graphics.c revision 894e0ac8
1894e0ac8Smrg/* $XTermId: graphics.c,v 1.43 2014/05/28 22:27:07 tom Exp $ */
2e0a2b6dfSmrg
3e0a2b6dfSmrg/*
4894e0ac8Smrg * Copyright 2013,2014 by Ross Combs
5e0a2b6dfSmrg *
6e0a2b6dfSmrg *                         All Rights Reserved
7e0a2b6dfSmrg *
8e0a2b6dfSmrg * Permission is hereby granted, free of charge, to any person obtaining a
9e0a2b6dfSmrg * copy of this software and associated documentation files (the
10e0a2b6dfSmrg * "Software"), to deal in the Software without restriction, including
11e0a2b6dfSmrg * without limitation the rights to use, copy, modify, merge, publish,
12e0a2b6dfSmrg * distribute, sublicense, and/or sell copies of the Software, and to
13e0a2b6dfSmrg * permit persons to whom the Software is furnished to do so, subject to
14e0a2b6dfSmrg * the following conditions:
15e0a2b6dfSmrg *
16e0a2b6dfSmrg * The above copyright notice and this permission notice shall be included
17e0a2b6dfSmrg * in all copies or substantial portions of the Software.
18e0a2b6dfSmrg *
19e0a2b6dfSmrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
20e0a2b6dfSmrg * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21e0a2b6dfSmrg * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
22e0a2b6dfSmrg * IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT HOLDER(S) BE LIABLE FOR ANY
23e0a2b6dfSmrg * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
24e0a2b6dfSmrg * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
25e0a2b6dfSmrg * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26e0a2b6dfSmrg *
27e0a2b6dfSmrg * Except as contained in this notice, the name(s) of the above copyright
28e0a2b6dfSmrg * holders shall not be used in advertising or otherwise to promote the
29e0a2b6dfSmrg * sale, use or other dealings in this Software without prior written
30e0a2b6dfSmrg * authorization.
31e0a2b6dfSmrg */
32e0a2b6dfSmrg
33e0a2b6dfSmrg#include <xterm.h>
34e0a2b6dfSmrg
35e0a2b6dfSmrg#include <stdio.h>
36e0a2b6dfSmrg#include <ctype.h>
37e0a2b6dfSmrg#include <stdlib.h>
38e0a2b6dfSmrg
39e0a2b6dfSmrg#include <data.h>
40e0a2b6dfSmrg#include <ptyx.h>
41e0a2b6dfSmrg
42e0a2b6dfSmrg#include <assert.h>
43e0a2b6dfSmrg#include <graphics.h>
44e0a2b6dfSmrg
45894e0ac8Smrg#undef DUMP_BITMAP
46894e0ac8Smrg#undef DUMP_COLORS
47894e0ac8Smrg#undef DEBUG_PALETTE
48894e0ac8Smrg#undef DEBUG_PIXEL
49e0a2b6dfSmrg#undef DEBUG_REFRESH
50e0a2b6dfSmrg
51e0a2b6dfSmrg/* TODO:
52e0a2b6dfSmrg * ReGIS:
53894e0ac8Smrg * - shading with text
54894e0ac8Smrg * - polygon filling
55894e0ac8Smrg * - plane write control
56894e0ac8Smrg * - fix interpolated curves to more closely match implementation (identical despite direction and starting point)
57894e0ac8Smrg * - text
58894e0ac8Smrg * - input and output cursors
59894e0ac8Smrg * - mouse input
60894e0ac8Smrg * - stacks
61894e0ac8Smrg * - investigate second graphic page for ReGIS -- does it also apply to text and sixel graphics? are the contents preserved?
62894e0ac8Smrg * - font upload, italics, and other text attributes
63894e0ac8Smrg * - enter/leave during a command
64894e0ac8Smrg * - command display mode
65894e0ac8Smrg * - scrolling
66894e0ac8Smrg * - custom coordinate systems
67894e0ac8Smrg * - scaling/re-rasterization to fit screen
68894e0ac8Smrg * - macros
69e0a2b6dfSmrg * sixel:
70894e0ac8Smrg * - fix problem where new_row < 0 during sixel parsing (see FIXME)
71894e0ac8Smrg * VT55/VT105 waveform graphics
72894e0ac8Smrg * - everything
73894e0ac8Smrg * common:
74894e0ac8Smrg * - handle light/dark screen modes (CSI?5[hl])
75894e0ac8Smrg * - update text fg/bg color which overlaps images
76e0a2b6dfSmrg * - erase graphic when erasing screen
77894e0ac8Smrg * - handle graphic updates in scroll regions
78894e0ac8Smrg * - handle rectangular area copies (verify they work with graphics)
79e0a2b6dfSmrg * - maintain ordered list/array instead of qsort()
80894e0ac8Smrg * - erase text under graphic if bg not transparent to avoid flickering (or not: bad if the font changes or window resizes)
81894e0ac8Smrg * - erase graphics under graphic if same origin and bg not transparent to avoid flickering
82e0a2b6dfSmrg * - erase scrolled portions of all graphics on alt buffer
83e0a2b6dfSmrg * - delete graphic if scrolled past end of scrollback
84e0a2b6dfSmrg * - delete graphic if all pixels are transparent/erased
85894e0ac8Smrg * - dynamic memory allocation of graphics buffers, add configurable limits
86e0a2b6dfSmrg * - auto convert color graphics in VT330 mode
87894e0ac8Smrg * - posturize requested colors to match hardware palettes (e.g. four possible shades on VT240)
88894e0ac8Smrg * - color register report/restore
89894e0ac8Smrg * escape sequences:
90e0a2b6dfSmrg * - way to query font size without "window ops" (or make "window ops" permissions more fine grained)
91e0a2b6dfSmrg * - way to query and/or set the maximum number of color registers
92894e0ac8Smrg * - way to query and set the number of graphics pages
93894e0ac8Smrg * ReGIS extensions:
94894e0ac8Smrg * - gradients
95894e0ac8Smrg * - line width (RLogin has this and it is mentioned in docs for the DEC ReGIS to Postscript converter)
96894e0ac8Smrg * - F option for screen command (mentioned in docs for the DEC ReGIS to Postscript converter)
97894e0ac8Smrg * - transparency
98894e0ac8Smrg * - background color as stackable write control
99894e0ac8Smrg * - RGB triplets
100894e0ac8Smrg * - true color (virtual color registers created upon lookup)
101894e0ac8Smrg * - anti-aliasing
102e0a2b6dfSmrg */
103e0a2b6dfSmrg
104e0a2b6dfSmrg/* font sizes:
105e0a2b6dfSmrg * VT510:
106e0a2b6dfSmrg *   80 Columns 132 Columns Maximum Number of Lines
107e0a2b6dfSmrg *   10 x 16   6 x 16  26 lines + keyboard indicator line
108894e0ac8Smrg *   10 x 13   6 x 13  26 lines + keyboard indicator line
109e0a2b6dfSmrg *   10 x 10   6 x 10  42 lines + keyboard indicator line
110e0a2b6dfSmrg *   10 x 8    6 x 8   53 lines + keyboard indicator line
111e0a2b6dfSmrg*/
112e0a2b6dfSmrg
113894e0ac8Smrg#define FOR_EACH_SLOT(ii) for (ii = 0U; ii < MAX_GRAPHICS; ii++)
114894e0ac8Smrg
115894e0ac8Smrgstatic ColorRegister *shared_color_registers;
116894e0ac8Smrgstatic Graphic *displayed_graphics[MAX_GRAPHICS];
117894e0ac8Smrgstatic unsigned next_graphic_id = 0U;
118894e0ac8Smrg
119894e0ac8Smrgstatic ColorRegister *
120894e0ac8SmrgallocRegisters(void)
121e0a2b6dfSmrg{
122894e0ac8Smrg    return TypeCallocN(ColorRegister, MAX_COLOR_REGISTERS);
123894e0ac8Smrg}
124894e0ac8Smrg
125894e0ac8Smrgstatic Graphic *
126894e0ac8SmrgfreeGraphic(Graphic *obj)
127894e0ac8Smrg{
128894e0ac8Smrg    if (obj) {
129894e0ac8Smrg	if (obj->pixels)
130894e0ac8Smrg	    free(obj->pixels);
131894e0ac8Smrg	if (obj->private_color_registers)
132894e0ac8Smrg	    free(obj->private_color_registers);
133894e0ac8Smrg	free(obj);
134894e0ac8Smrg    }
135894e0ac8Smrg    return NULL;
136894e0ac8Smrg}
137894e0ac8Smrg
138894e0ac8Smrgstatic Graphic *
139894e0ac8SmrgallocGraphic(void)
140894e0ac8Smrg{
141894e0ac8Smrg    Graphic *result = TypeCalloc(Graphic);
142894e0ac8Smrg    if (result) {
143894e0ac8Smrg	if (!(result->pixels = TypeCallocN(RegisterNum, MAX_PIXELS))) {
144894e0ac8Smrg	    result = freeGraphic(result);
145894e0ac8Smrg	} else if (!(result->private_color_registers = allocRegisters())) {
146894e0ac8Smrg	    result = freeGraphic(result);
147e0a2b6dfSmrg	}
148e0a2b6dfSmrg    }
149894e0ac8Smrg    return result;
150894e0ac8Smrg}
151e0a2b6dfSmrg
152894e0ac8Smrgstatic Graphic *
153894e0ac8SmrggetActiveSlot(unsigned n)
154894e0ac8Smrg{
155894e0ac8Smrg    if (n < MAX_GRAPHICS &&
156894e0ac8Smrg	displayed_graphics[n] &&
157894e0ac8Smrg	displayed_graphics[n]->valid) {
158894e0ac8Smrg	return displayed_graphics[n];
159894e0ac8Smrg    }
160894e0ac8Smrg    return NULL;
161e0a2b6dfSmrg}
162e0a2b6dfSmrg
163894e0ac8Smrgstatic Graphic *
164894e0ac8SmrggetInactiveSlot(unsigned n)
165894e0ac8Smrg{
166894e0ac8Smrg    if (n < MAX_GRAPHICS &&
167894e0ac8Smrg	(!displayed_graphics[n] ||
168894e0ac8Smrg	 !displayed_graphics[n]->valid)) {
169894e0ac8Smrg	if (!displayed_graphics[n]) {
170894e0ac8Smrg	    displayed_graphics[n] = allocGraphic();
171894e0ac8Smrg	}
172894e0ac8Smrg	return displayed_graphics[n];
173894e0ac8Smrg    }
174894e0ac8Smrg    return NULL;
175894e0ac8Smrg}
176894e0ac8Smrg
177894e0ac8Smrgstatic ColorRegister *
178894e0ac8SmrggetSharedRegisters(void)
179894e0ac8Smrg{
180894e0ac8Smrg    if (!shared_color_registers)
181894e0ac8Smrg	shared_color_registers = allocRegisters();
182894e0ac8Smrg    return shared_color_registers;
183894e0ac8Smrg}
184e0a2b6dfSmrg
185e0a2b6dfSmrgstatic void
186894e0ac8SmrgdeactivateSlot(unsigned n)
187e0a2b6dfSmrg{
188894e0ac8Smrg    if (n < MAX_GRAPHICS) {
189894e0ac8Smrg	displayed_graphics[n] = freeGraphic(displayed_graphics[n]);
190894e0ac8Smrg    }
191894e0ac8Smrg}
192e0a2b6dfSmrg
193894e0ac8Smrgextern RegisterNum
194894e0ac8Smrgread_pixel(Graphic *graphic, int x, int y)
195894e0ac8Smrg{
196894e0ac8Smrg    if (x < 0 && x >= graphic->actual_width &&
197894e0ac8Smrg	y < 0 && y >= graphic->actual_height) {
198894e0ac8Smrg	return COLOR_HOLE;
199e0a2b6dfSmrg    }
200894e0ac8Smrg
201894e0ac8Smrg    return graphic->pixels[y * graphic->max_width + x];
202e0a2b6dfSmrg}
203e0a2b6dfSmrg
204894e0ac8Smrgvoid
205894e0ac8Smrgdraw_solid_pixel(Graphic *graphic, int x, int y, unsigned color)
206e0a2b6dfSmrg{
207894e0ac8Smrg    assert(color <= MAX_COLOR_REGISTERS);
208e0a2b6dfSmrg
209894e0ac8Smrg#ifdef DEBUG_PIXEL
210894e0ac8Smrg    TRACE(("drawing pixel at %d,%d color=%hu (hole=%hu, [%d,%d,%d])\n",
211894e0ac8Smrg	   x,
212894e0ac8Smrg	   y,
213e0a2b6dfSmrg	   color,
214894e0ac8Smrg	   COLOR_HOLE,
215e0a2b6dfSmrg	   ((color != COLOR_HOLE)
216894e0ac8Smrg	    ? (unsigned) graphic->color_registers[color].r : 0U),
217e0a2b6dfSmrg	   ((color != COLOR_HOLE)
218894e0ac8Smrg	    ? (unsigned) graphic->color_registers[color].g : 0U),
219e0a2b6dfSmrg	   ((color != COLOR_HOLE)
220894e0ac8Smrg	    ? (unsigned) graphic->color_registers[color].b : 0U)));
221894e0ac8Smrg#endif
222894e0ac8Smrg    if (x >= 0 && x < graphic->actual_width &&
223894e0ac8Smrg	y >= 0 && y < graphic->actual_height) {
224894e0ac8Smrg	graphic->pixels[y * graphic->max_width + x] = (RegisterNum) color;
225894e0ac8Smrg	if (color < MAX_COLOR_REGISTERS)
226894e0ac8Smrg	    graphic->color_registers_used[color] = 1;
227894e0ac8Smrg    } else {
228894e0ac8Smrg	TRACE(("pixel %d,%d out of bounds\n", x, y));
229894e0ac8Smrg    }
230894e0ac8Smrg}
231894e0ac8Smrg
232894e0ac8Smrgvoid
233894e0ac8Smrgdraw_solid_rectangle(Graphic *graphic, int x1, int y1, int x2, int y2, unsigned color)
234894e0ac8Smrg{
235894e0ac8Smrg    int x, y;
236894e0ac8Smrg    int tmp;
237894e0ac8Smrg
238894e0ac8Smrg    assert(color <= MAX_COLOR_REGISTERS);
239894e0ac8Smrg
240894e0ac8Smrg    if (x1 > x2) {
241894e0ac8Smrg	EXCHANGE(x1, x2, tmp);
242894e0ac8Smrg    }
243894e0ac8Smrg    if (y1 > y2) {
244894e0ac8Smrg	EXCHANGE(y1, y2, tmp);
245894e0ac8Smrg    }
246894e0ac8Smrg
247894e0ac8Smrg    for (y = y1; y <= y2; y++)
248894e0ac8Smrg	for (x = x1; x < x2; x++)
249894e0ac8Smrg	    draw_solid_pixel(graphic, x, y, color);
250894e0ac8Smrg}
251894e0ac8Smrg
252894e0ac8Smrgvoid
253894e0ac8Smrgdraw_solid_line(Graphic *graphic, int x1, int y1, int x2, int y2, unsigned color)
254894e0ac8Smrg{
255894e0ac8Smrg    int x, y;
256894e0ac8Smrg    int dx, dy;
257894e0ac8Smrg    int dir, diff;
258894e0ac8Smrg
259894e0ac8Smrg    assert(color <= MAX_COLOR_REGISTERS);
260894e0ac8Smrg
261894e0ac8Smrg    dx = abs(x1 - x2);
262894e0ac8Smrg    dy = abs(y1 - y2);
263894e0ac8Smrg
264894e0ac8Smrg    if (dx > dy) {
265894e0ac8Smrg	if (x1 > x2) {
266894e0ac8Smrg	    int tmp;
267894e0ac8Smrg	    EXCHANGE(x1, x2, tmp);
268894e0ac8Smrg	    EXCHANGE(y1, y2, tmp);
269894e0ac8Smrg	}
270894e0ac8Smrg	if (y1 < y2)
271894e0ac8Smrg	    dir = 1;
272894e0ac8Smrg	else if (y1 > y2)
273894e0ac8Smrg	    dir = -1;
274894e0ac8Smrg	else
275894e0ac8Smrg	    dir = 0;
276894e0ac8Smrg
277894e0ac8Smrg	diff = 0;
278894e0ac8Smrg	y = y1;
279894e0ac8Smrg	for (x = x1; x <= x2; x++) {
280894e0ac8Smrg	    if (diff >= dx) {
281894e0ac8Smrg		diff -= dx;
282894e0ac8Smrg		y += dir;
283e0a2b6dfSmrg	    }
284894e0ac8Smrg	    diff += dy;
285894e0ac8Smrg	    draw_solid_pixel(graphic, x, y, color);
286894e0ac8Smrg	}
287894e0ac8Smrg    } else {
288894e0ac8Smrg	if (y1 > y2) {
289894e0ac8Smrg	    int tmp;
290894e0ac8Smrg	    EXCHANGE(x1, x2, tmp);
291894e0ac8Smrg	    EXCHANGE(y1, y2, tmp);
292894e0ac8Smrg	}
293894e0ac8Smrg	if (x1 < x2)
294894e0ac8Smrg	    dir = 1;
295894e0ac8Smrg	else if (x1 > x2)
296894e0ac8Smrg	    dir = -1;
297894e0ac8Smrg	else
298894e0ac8Smrg	    dir = 0;
299894e0ac8Smrg
300894e0ac8Smrg	diff = 0;
301894e0ac8Smrg	x = x1;
302894e0ac8Smrg	for (y = y1; y <= y2; y++) {
303894e0ac8Smrg	    if (diff >= dy) {
304894e0ac8Smrg		diff -= dy;
305894e0ac8Smrg		x += dir;
306894e0ac8Smrg	    }
307894e0ac8Smrg	    diff += dx;
308894e0ac8Smrg	    draw_solid_pixel(graphic, x, y, color);
309e0a2b6dfSmrg	}
310e0a2b6dfSmrg    }
311e0a2b6dfSmrg}
312e0a2b6dfSmrg
313e0a2b6dfSmrgstatic void
314894e0ac8Smrgset_color_register(ColorRegister *color_registers,
315894e0ac8Smrg		   unsigned color,
316894e0ac8Smrg		   int r,
317894e0ac8Smrg		   int g,
318894e0ac8Smrg		   int b)
319e0a2b6dfSmrg{
320e0a2b6dfSmrg    ColorRegister *reg = &color_registers[color];
321e0a2b6dfSmrg    reg->r = (short) r;
322e0a2b6dfSmrg    reg->g = (short) g;
323e0a2b6dfSmrg    reg->b = (short) b;
324e0a2b6dfSmrg    reg->allocated = 0;
325e0a2b6dfSmrg}
326e0a2b6dfSmrg
327894e0ac8Smrg/* Graphics which don't use private colors will act as if they are using a
328894e0ac8Smrg * device-wide color palette.
329894e0ac8Smrg */
330894e0ac8Smrgstatic void
331894e0ac8Smrgset_shared_color_register(unsigned color, int r, int g, int b)
332894e0ac8Smrg{
333894e0ac8Smrg    Graphic *graphic;
334894e0ac8Smrg    unsigned ii;
335894e0ac8Smrg
336894e0ac8Smrg    assert(color < MAX_COLOR_REGISTERS);
337894e0ac8Smrg
338894e0ac8Smrg    set_color_register(getSharedRegisters(), color, r, g, b);
339894e0ac8Smrg
340894e0ac8Smrg    FOR_EACH_SLOT(ii) {
341894e0ac8Smrg	if (!(graphic = getActiveSlot(ii)))
342894e0ac8Smrg	    continue;
343894e0ac8Smrg	if (graphic->private_colors)
344894e0ac8Smrg	    continue;
345894e0ac8Smrg
346894e0ac8Smrg	if (graphic->color_registers_used[ii]) {
347894e0ac8Smrg	    graphic->dirty = 1;
348894e0ac8Smrg	}
349894e0ac8Smrg    }
350894e0ac8Smrg}
351894e0ac8Smrg
352894e0ac8Smrgvoid
353894e0ac8Smrgupdate_color_register(Graphic *graphic,
354894e0ac8Smrg		      unsigned color,
355894e0ac8Smrg		      int r,
356894e0ac8Smrg		      int g,
357894e0ac8Smrg		      int b)
358894e0ac8Smrg{
359894e0ac8Smrg    assert(color < MAX_COLOR_REGISTERS);
360894e0ac8Smrg
361894e0ac8Smrg    if (graphic->private_colors) {
362894e0ac8Smrg	set_color_register(graphic->private_color_registers,
363894e0ac8Smrg			   color, r, g, b);
364894e0ac8Smrg	if (graphic->color_registers_used[color]) {
365894e0ac8Smrg	    graphic->dirty = 1;
366894e0ac8Smrg	}
367894e0ac8Smrg	graphic->color_registers_used[color] = 1;
368894e0ac8Smrg    } else {
369894e0ac8Smrg	set_shared_color_register(color, r, g, b);
370894e0ac8Smrg    }
371894e0ac8Smrg}
372894e0ac8Smrg
373894e0ac8Smrg#define SQUARE(X) ( (X) * (X) )
374894e0ac8Smrg
375894e0ac8SmrgRegisterNum
376894e0ac8Smrgfind_color_register(ColorRegister const *color_registers, int r, int g, int b)
377894e0ac8Smrg{
378894e0ac8Smrg    unsigned i;
379894e0ac8Smrg    unsigned d;
380894e0ac8Smrg    unsigned closest_index;
381894e0ac8Smrg    unsigned closest_distance;
382894e0ac8Smrg
383894e0ac8Smrg    /* I have no idea what algorithm DEC used for this.
384894e0ac8Smrg     * The documentation warns that it is unpredictable, especially with values
385894e0ac8Smrg     * far away from any allocated color so it is probably a very simple
386894e0ac8Smrg     * hueristic rather than something fancy like finding the minimum distance
387894e0ac8Smrg     * in a linear perceptive color space.
388894e0ac8Smrg     */
389894e0ac8Smrg    closest_index = MAX_COLOR_REGISTERS;
390894e0ac8Smrg    closest_distance = 0U;
391894e0ac8Smrg    for (i = 0U; i < MAX_COLOR_REGISTERS; i++) {
392894e0ac8Smrg	d = (unsigned) (SQUARE(2 * (color_registers[i].r - r)) +
393894e0ac8Smrg			SQUARE(3 * (color_registers[i].g - g)) +
394894e0ac8Smrg			SQUARE(1 * (color_registers[i].b - b)));
395894e0ac8Smrg	if (closest_index == MAX_COLOR_REGISTERS || d < closest_distance) {
396894e0ac8Smrg	    closest_index = i;
397894e0ac8Smrg	    closest_distance = d;
398894e0ac8Smrg	}
399894e0ac8Smrg    }
400894e0ac8Smrg
401894e0ac8Smrg    TRACE(("found closest color register to %d,%d,%d: %u (distance %u value %d,%d,%d)\n",
402894e0ac8Smrg	   r, g, b,
403894e0ac8Smrg	   closest_index,
404894e0ac8Smrg	   closest_distance,
405894e0ac8Smrg	   color_registers[closest_index].r,
406894e0ac8Smrg	   color_registers[closest_index].g,
407894e0ac8Smrg	   color_registers[closest_index].b));
408894e0ac8Smrg    return (RegisterNum) closest_index;
409894e0ac8Smrg}
410894e0ac8Smrg
411e0a2b6dfSmrgstatic void
412e0a2b6dfSmrginit_color_registers(ColorRegister *color_registers, int terminal_id)
413e0a2b6dfSmrg{
414894e0ac8Smrg    TRACE(("setting initial colors for terminal %d\n", terminal_id));
415e0a2b6dfSmrg    {
416894e0ac8Smrg	unsigned i;
417e0a2b6dfSmrg
418e0a2b6dfSmrg	for (i = 0U; i < MAX_COLOR_REGISTERS; i++) {
419894e0ac8Smrg	    set_color_register(color_registers, (RegisterNum) i, 0, 0, 0);
420e0a2b6dfSmrg	}
421e0a2b6dfSmrg    }
422e0a2b6dfSmrg
423e0a2b6dfSmrg    /*
424e0a2b6dfSmrg     * default color registers:
425e0a2b6dfSmrg     *     (mono) (color)
426e0a2b6dfSmrg     * VK100/GIGI (fixed)
427e0a2b6dfSmrg     * VT125:
428e0a2b6dfSmrg     *   0: 0%      0%
429e0a2b6dfSmrg     *   1: 33%     blue
430e0a2b6dfSmrg     *   2: 66%     red
431e0a2b6dfSmrg     *   3: 100%    green
432e0a2b6dfSmrg     * VT240:
433e0a2b6dfSmrg     *   0: 0%      0%
434e0a2b6dfSmrg     *   1: 33%     blue
435e0a2b6dfSmrg     *   2: 66%     red
436e0a2b6dfSmrg     *   3: 100%    green
437e0a2b6dfSmrg     * VT241:
438e0a2b6dfSmrg     *   0: 0%      0%
439e0a2b6dfSmrg     *   1: 33%     blue
440e0a2b6dfSmrg     *   2: 66%     red
441e0a2b6dfSmrg     *   3: 100%    green
442e0a2b6dfSmrg     * VT330:
443e0a2b6dfSmrg     *   0: 0%      0%              (bg for light on dark mode)
444e0a2b6dfSmrg     *   1: 33%     blue (red?)
445e0a2b6dfSmrg     *   2: 66%     red (green?)
446e0a2b6dfSmrg     *   3: 100%    green (yellow?) (fg for light on dark mode)
447e0a2b6dfSmrg     * VT340:
448e0a2b6dfSmrg     *   0: 0%      0%              (bg for light on dark mode)
449e0a2b6dfSmrg     *   1: 14%     blue
450e0a2b6dfSmrg     *   2: 29%     red
451e0a2b6dfSmrg     *   3: 43%     green
452e0a2b6dfSmrg     *   4: 57%     magenta
453e0a2b6dfSmrg     *   5: 71%     cyan
454e0a2b6dfSmrg     *   6: 86%     yellow
455e0a2b6dfSmrg     *   7: 100%    50%             (fg for light on dark mode)
456e0a2b6dfSmrg     *   8: 0%      25%
457e0a2b6dfSmrg     *   9: 14%     gray-blue
458e0a2b6dfSmrg     *  10: 29%     gray-red
459e0a2b6dfSmrg     *  11: 43%     gray-green
460e0a2b6dfSmrg     *  12: 57%     gray-magenta
461e0a2b6dfSmrg     *  13: 71%     gray-cyan
462e0a2b6dfSmrg     *  14: 86%     gray-yellow
463894e0ac8Smrg     *  15: 100%    75%             ("white")
464894e0ac8Smrg     * VT382:
465894e0ac8Smrg     *   ? (FIXME: B&W only?)
466e0a2b6dfSmrg     * dxterm:
467e0a2b6dfSmrg     *  ?
468e0a2b6dfSmrg     */
469e0a2b6dfSmrg    switch (terminal_id) {
470e0a2b6dfSmrg    case 125:
471e0a2b6dfSmrg    case 241:
472894e0ac8Smrg	set_color_register(color_registers, 0, 0, 0, 0);
473894e0ac8Smrg	set_color_register(color_registers, 1, 0, 0, 100);
474894e0ac8Smrg	set_color_register(color_registers, 2, 0, 100, 0);
475894e0ac8Smrg	set_color_register(color_registers, 3, 100, 0, 0);
476e0a2b6dfSmrg	break;
477e0a2b6dfSmrg    case 240:
478e0a2b6dfSmrg    case 330:
479894e0ac8Smrg	set_color_register(color_registers, 0, 0, 0, 0);
480894e0ac8Smrg	set_color_register(color_registers, 1, 33, 33, 33);
481894e0ac8Smrg	set_color_register(color_registers, 2, 66, 66, 66);
482894e0ac8Smrg	set_color_register(color_registers, 3, 100, 100, 100);
483e0a2b6dfSmrg	break;
484e0a2b6dfSmrg    case 340:
485e0a2b6dfSmrg    default:
486894e0ac8Smrg	set_color_register(color_registers, 0, 0, 0, 0);
487894e0ac8Smrg	set_color_register(color_registers, 1, 20, 20, 80);
488894e0ac8Smrg	set_color_register(color_registers, 2, 80, 13, 13);
489894e0ac8Smrg	set_color_register(color_registers, 3, 20, 80, 20);
490894e0ac8Smrg	set_color_register(color_registers, 4, 80, 20, 80);
491894e0ac8Smrg	set_color_register(color_registers, 5, 20, 80, 80);
492894e0ac8Smrg	set_color_register(color_registers, 6, 80, 80, 20);
493894e0ac8Smrg	set_color_register(color_registers, 7, 53, 53, 53);
494894e0ac8Smrg	set_color_register(color_registers, 8, 26, 26, 26);
495894e0ac8Smrg	set_color_register(color_registers, 9, 33, 33, 60);
496894e0ac8Smrg	set_color_register(color_registers, 10, 60, 26, 26);
497894e0ac8Smrg	set_color_register(color_registers, 11, 33, 60, 33);
498894e0ac8Smrg	set_color_register(color_registers, 12, 60, 33, 60);
499894e0ac8Smrg	set_color_register(color_registers, 13, 33, 60, 60);
500894e0ac8Smrg	set_color_register(color_registers, 14, 60, 60, 33);
501894e0ac8Smrg	set_color_register(color_registers, 15, 80, 80, 80);
502894e0ac8Smrg	break;
503894e0ac8Smrg    case 382:			/* FIXME: verify */
504894e0ac8Smrg	set_color_register(color_registers, 0, 0, 0, 0);
505894e0ac8Smrg	set_color_register(color_registers, 1, 100, 100, 100);
506e0a2b6dfSmrg	break;
507e0a2b6dfSmrg    }
508e0a2b6dfSmrg
509894e0ac8Smrg#ifdef DEBUG_PALETTE
510894e0ac8Smrg    {
511894e0ac8Smrg	unsigned i;
512e0a2b6dfSmrg
513894e0ac8Smrg	for (i = 0U; i < MAX_COLOR_REGISTERS; i++) {
514894e0ac8Smrg	    printf("initial value for register %03u: %d,%d,%d\n",
515894e0ac8Smrg		   i,
516894e0ac8Smrg		   color_registers[i].r,
517894e0ac8Smrg		   color_registers[i].g,
518894e0ac8Smrg		   color_registers[i].b);
519894e0ac8Smrg	}
520894e0ac8Smrg    }
521894e0ac8Smrg#endif
522894e0ac8Smrg}
523e0a2b6dfSmrg
524894e0ac8Smrgunsigned
525894e0ac8Smrgget_color_register_count(TScreen const *screen)
526894e0ac8Smrg{
527894e0ac8Smrg    unsigned num_color_registers;
528e0a2b6dfSmrg
529894e0ac8Smrg    if (screen->numcolorregisters >= 0) {
530894e0ac8Smrg	num_color_registers = (unsigned) screen->numcolorregisters;
531894e0ac8Smrg    } else {
532894e0ac8Smrg	num_color_registers = 0U;
533894e0ac8Smrg    }
534e0a2b6dfSmrg
535894e0ac8Smrg    if (num_color_registers > 1U) {
536894e0ac8Smrg	if (num_color_registers > MAX_COLOR_REGISTERS)
537894e0ac8Smrg	    return MAX_COLOR_REGISTERS;
538894e0ac8Smrg	return num_color_registers;
539894e0ac8Smrg    }
540e0a2b6dfSmrg
541e0a2b6dfSmrg    /*
542e0a2b6dfSmrg     * color capabilities:
543e0a2b6dfSmrg     * VK100/GIGI  1 plane (12x1 pixel attribute blocks) colorspace is 8 fixed colors (black, white, red, green, blue, cyan, yellow, magenta)
544e0a2b6dfSmrg     * VT125       2 planes (4 registers) colorspace is (64?) (color), ? (grayscale)
545894e0ac8Smrg     * VT240       2 planes (4 registers) colorspace is 4 shades (grayscale)
546e0a2b6dfSmrg     * VT241       2 planes (4 registers) colorspace is ? (color), ? shades (grayscale)
547e0a2b6dfSmrg     * VT330       2 planes (4 registers) colorspace is 4 shades (grayscale)
548e0a2b6dfSmrg     * VT340       4 planes (16 registers) colorspace is r16g16b16 (color), 16 shades (grayscale)
549894e0ac8Smrg     * VT382       1 plane (two fixed colors: black and white)  FIXME: verify
550e0a2b6dfSmrg     * dxterm      ?
551e0a2b6dfSmrg     */
552894e0ac8Smrg    switch (screen->terminal_id) {
553e0a2b6dfSmrg    case 125:
554894e0ac8Smrg	return 4U;
555e0a2b6dfSmrg    case 240:
556894e0ac8Smrg	return 4U;
557e0a2b6dfSmrg    case 241:
558894e0ac8Smrg	return 4U;
559e0a2b6dfSmrg    case 330:
560894e0ac8Smrg	return 4U;
561e0a2b6dfSmrg    case 340:
562894e0ac8Smrg	return 16U;
563894e0ac8Smrg    case 382:
564894e0ac8Smrg	return 2U;
565e0a2b6dfSmrg    default:
566894e0ac8Smrg	/* unknown graphics model -- might as well be generous */
567894e0ac8Smrg	return MAX_COLOR_REGISTERS;
568e0a2b6dfSmrg    }
569894e0ac8Smrg}
570894e0ac8Smrg
571894e0ac8Smrgstatic void
572894e0ac8Smrginit_graphic(Graphic *graphic,
573894e0ac8Smrg	     unsigned type,
574894e0ac8Smrg	     int terminal_id,
575894e0ac8Smrg	     int charrow,
576894e0ac8Smrg	     int charcol,
577894e0ac8Smrg	     unsigned num_color_registers,
578894e0ac8Smrg	     int private_colors)
579894e0ac8Smrg{
580894e0ac8Smrg    unsigned i;
581894e0ac8Smrg
582894e0ac8Smrg    TRACE(("initializing graphic object\n"));
583894e0ac8Smrg
584894e0ac8Smrg    graphic->dirty = 1;
585894e0ac8Smrg    for (i = 0U; i < MAX_PIXELS; i++)
586894e0ac8Smrg	graphic->pixels[i] = COLOR_HOLE;
587894e0ac8Smrg    memset(graphic->color_registers_used, 0, sizeof(graphic->color_registers_used));
588e0a2b6dfSmrg
589e0a2b6dfSmrg    /*
590e0a2b6dfSmrg     * text and graphics interactions:
591e0a2b6dfSmrg     * VK100/GIGI                text writes on top of graphics buffer, color attribute shared with text
592e0a2b6dfSmrg     * VT240,VT241,VT330,VT340   text writes on top of graphics buffer
593894e0ac8Smrg     * VT382                     text writes on top of graphics buffer FIXME: verify
594e0a2b6dfSmrg     * VT125                     graphics buffer overlaid on top of text in B&W display, text not present in color display
595e0a2b6dfSmrg     */
596e0a2b6dfSmrg
597894e0ac8Smrg    /*
598894e0ac8Smrg     * dimensions (ReGIS logical, physical):
599894e0ac8Smrg     * VK100/GIGI  768x4??  768x240(status?)
600894e0ac8Smrg     * VT125       768x460  768x230(+10status) (1:2 aspect ratio, ReGIS halves vertical addresses through "odd y emulation")
601894e0ac8Smrg     * VT240       800x460  800x230(+10status) (1:2 aspect ratio, ReGIS halves vertical addresses through "odd y emulation")
602894e0ac8Smrg     * VT241       800x460  800x230(+10status) (1:2 aspect ratio, ReGIS halves vertical addresses through "odd y emulation")
603894e0ac8Smrg     * VT330       800x480  800x480(+?status)
604894e0ac8Smrg     * VT340       800x480  800x480(+?status)
605894e0ac8Smrg     * VT382       960x750  sixel only
606894e0ac8Smrg     * dxterm      ?x? ?x?  variable?
607894e0ac8Smrg     */
608894e0ac8Smrg    graphic->max_width = BUFFER_WIDTH;
609894e0ac8Smrg    graphic->max_height = BUFFER_HEIGHT;
610e0a2b6dfSmrg
611e0a2b6dfSmrg    graphic->actual_width = 0;
612e0a2b6dfSmrg    graphic->actual_height = 0;
613e0a2b6dfSmrg
614894e0ac8Smrg    graphic->pixw = 1;
615894e0ac8Smrg    graphic->pixh = 1;
616894e0ac8Smrg
617894e0ac8Smrg    graphic->valid_registers = num_color_registers;
618894e0ac8Smrg    TRACE(("%d color registers\n", graphic->valid_registers));
619894e0ac8Smrg
620e0a2b6dfSmrg    graphic->private_colors = private_colors;
621e0a2b6dfSmrg    if (graphic->private_colors) {
622894e0ac8Smrg	TRACE(("using private color registers\n"));
623e0a2b6dfSmrg	init_color_registers(graphic->private_color_registers, terminal_id);
624e0a2b6dfSmrg	graphic->color_registers = graphic->private_color_registers;
625e0a2b6dfSmrg    } else {
626894e0ac8Smrg	TRACE(("using shared color registers\n"));
627894e0ac8Smrg	graphic->color_registers = getSharedRegisters();
628e0a2b6dfSmrg    }
629e0a2b6dfSmrg
630894e0ac8Smrg    graphic->charrow = charrow;
631894e0ac8Smrg    graphic->charcol = charcol;
632894e0ac8Smrg    graphic->type = type;
633e0a2b6dfSmrg    graphic->valid = 0;
634e0a2b6dfSmrg}
635e0a2b6dfSmrg
636894e0ac8SmrgGraphic *
637894e0ac8Smrgget_new_graphic(XtermWidget xw, int charrow, int charcol, unsigned type)
638e0a2b6dfSmrg{
639e0a2b6dfSmrg    TScreen const *screen = TScreenOf(xw);
640e0a2b6dfSmrg    int bufferid = screen->whichBuf;
641e0a2b6dfSmrg    int terminal_id = screen->terminal_id;
642894e0ac8Smrg    Graphic *graphic;
643894e0ac8Smrg    unsigned ii;
644e0a2b6dfSmrg
645894e0ac8Smrg    FOR_EACH_SLOT(ii) {
646894e0ac8Smrg	if ((graphic = getInactiveSlot(ii))) {
647894e0ac8Smrg	    TRACE(("using fresh graphic index=%u id=%u\n", ii, next_graphic_id));
648e0a2b6dfSmrg	    break;
649894e0ac8Smrg	}
650e0a2b6dfSmrg    }
651e0a2b6dfSmrg
652894e0ac8Smrg    /* if none are free, recycle the graphic scrolled back the farthest */
653894e0ac8Smrg    if (!graphic) {
654e0a2b6dfSmrg	int min_charrow = 0;
655894e0ac8Smrg	Graphic *min_graphic = NULL;
656e0a2b6dfSmrg
657894e0ac8Smrg	FOR_EACH_SLOT(ii) {
658894e0ac8Smrg	    if (!(graphic = getActiveSlot(ii)))
659894e0ac8Smrg		continue;
660e0a2b6dfSmrg	    if (!min_graphic || graphic->charrow < min_charrow) {
661e0a2b6dfSmrg		min_charrow = graphic->charrow;
662e0a2b6dfSmrg		min_graphic = graphic;
663e0a2b6dfSmrg	    }
664e0a2b6dfSmrg	}
665894e0ac8Smrg	TRACE(("recycling old graphic index=%u id=%u\n", ii, next_graphic_id));
666e0a2b6dfSmrg	graphic = min_graphic;
667e0a2b6dfSmrg    }
668e0a2b6dfSmrg
669894e0ac8Smrg    if (graphic) {
670894e0ac8Smrg	unsigned num_color_registers;
671894e0ac8Smrg	num_color_registers = get_color_register_count(screen);
672894e0ac8Smrg	graphic->xw = xw;
673894e0ac8Smrg	graphic->bufferid = bufferid;
674894e0ac8Smrg	graphic->id = next_graphic_id++;
675894e0ac8Smrg	init_graphic(graphic,
676894e0ac8Smrg		     type,
677894e0ac8Smrg		     terminal_id,
678894e0ac8Smrg		     charrow,
679894e0ac8Smrg		     charcol,
680894e0ac8Smrg		     num_color_registers,
681894e0ac8Smrg		     screen->privatecolorregisters);
682894e0ac8Smrg    }
683e0a2b6dfSmrg    return graphic;
684e0a2b6dfSmrg}
685e0a2b6dfSmrg
686894e0ac8SmrgGraphic *
687894e0ac8Smrgget_new_or_matching_graphic(XtermWidget xw,
688894e0ac8Smrg			    int charrow,
689894e0ac8Smrg			    int charcol,
690894e0ac8Smrg			    int actual_width,
691894e0ac8Smrg			    int actual_height,
692894e0ac8Smrg			    unsigned type)
693e0a2b6dfSmrg{
694894e0ac8Smrg    TScreen const *screen = TScreenOf(xw);
695894e0ac8Smrg    int bufferid = screen->whichBuf;
696894e0ac8Smrg    Graphic *graphic;
697894e0ac8Smrg    unsigned ii;
698894e0ac8Smrg
699894e0ac8Smrg    FOR_EACH_SLOT(ii) {
700894e0ac8Smrg	if ((graphic = getActiveSlot(ii)) &&
701894e0ac8Smrg	    graphic->type == type &&
702894e0ac8Smrg	    graphic->bufferid == bufferid &&
703894e0ac8Smrg	    graphic->charrow == charrow &&
704894e0ac8Smrg	    graphic->charcol == charcol &&
705894e0ac8Smrg	    graphic->actual_width == actual_width &&
706894e0ac8Smrg	    graphic->actual_height == actual_height) {
707894e0ac8Smrg	    TRACE(("found existing graphic index=%u id=%u\n", ii, graphic->id));
708894e0ac8Smrg	    return graphic;
709e0a2b6dfSmrg	}
710e0a2b6dfSmrg    }
711e0a2b6dfSmrg
712894e0ac8Smrg    /* if no match get a new graphic */
713894e0ac8Smrg    if ((graphic = get_new_graphic(xw, charrow, charcol, type))) {
714894e0ac8Smrg	graphic->actual_width = actual_width;
715894e0ac8Smrg	graphic->actual_height = actual_height;
716e0a2b6dfSmrg    }
717894e0ac8Smrg    return graphic;
718e0a2b6dfSmrg}
719e0a2b6dfSmrg
720e0a2b6dfSmrg#define ScaleForXColor(s) (unsigned short) ((long)(s) * 65535 / 100)
721e0a2b6dfSmrg
722e0a2b6dfSmrgstatic Pixel
723894e0ac8Smrgcolor_register_to_xpixel(ColorRegister *reg, XtermWidget xw)
724e0a2b6dfSmrg{
725e0a2b6dfSmrg    if (!reg->allocated) {
726e0a2b6dfSmrg	XColor def;
727e0a2b6dfSmrg
728e0a2b6dfSmrg	def.red = ScaleForXColor(reg->r);
729e0a2b6dfSmrg	def.green = ScaleForXColor(reg->g);
730e0a2b6dfSmrg	def.blue = ScaleForXColor(reg->b);
731e0a2b6dfSmrg	def.flags = DoRed | DoGreen | DoBlue;
732e0a2b6dfSmrg	if (!allocateBestRGB(xw, &def)) {
733894e0ac8Smrg	    TRACE(("unable to allocate xcolor for color register\n"));
734e0a2b6dfSmrg	    return 0UL;
735e0a2b6dfSmrg	}
736e0a2b6dfSmrg	reg->pix = def.pixel;
737e0a2b6dfSmrg	reg->allocated = 1;
738e0a2b6dfSmrg    }
739e0a2b6dfSmrg
740e0a2b6dfSmrg    /* FIXME: with so many possible colors we need to determine
741e0a2b6dfSmrg     * when to free them to be nice to PseudoColor displays
742e0a2b6dfSmrg     */
743e0a2b6dfSmrg    return reg->pix;
744e0a2b6dfSmrg}
745e0a2b6dfSmrg
746e0a2b6dfSmrgstatic void
747894e0ac8Smrgrefresh_graphic(TScreen const *screen,
748894e0ac8Smrg		Graphic const *graphic,
749894e0ac8Smrg		int xbase,
750894e0ac8Smrg		int ybase,
751894e0ac8Smrg		int x,
752894e0ac8Smrg		int y,
753894e0ac8Smrg		int w,
754894e0ac8Smrg		int h)
755e0a2b6dfSmrg{
756e0a2b6dfSmrg    Display *display = screen->display;
757e0a2b6dfSmrg    Window vwindow = WhichVWin(screen)->window;
758e0a2b6dfSmrg    GC graphics_gc;
759e0a2b6dfSmrg    int r, c;
760e0a2b6dfSmrg    int pw, ph;
761894e0ac8Smrg    int rbase, cbase;
762e0a2b6dfSmrg    RegisterNum color;
763e0a2b6dfSmrg    RegisterNum old_fg;
764e0a2b6dfSmrg    XGCValues xgcv;
765e0a2b6dfSmrg    XtGCMask mask;
766e0a2b6dfSmrg    int holes, total;
767e0a2b6dfSmrg
768894e0ac8Smrg    TRACE(("refreshing graphic from %d,%d %dx%d (valid=%d, size=%dx%d, scale=%dx%d max=%dx%d) at base=%d,%d\n",
769e0a2b6dfSmrg	   x, y, w, h,
770e0a2b6dfSmrg	   graphic->valid,
771e0a2b6dfSmrg	   graphic->actual_width,
772e0a2b6dfSmrg	   graphic->actual_height,
773894e0ac8Smrg	   graphic->pixw,
774894e0ac8Smrg	   graphic->pixh,
775e0a2b6dfSmrg	   graphic->max_width,
776e0a2b6dfSmrg	   graphic->max_height,
777e0a2b6dfSmrg	   xbase, ybase));
778e0a2b6dfSmrg
779e0a2b6dfSmrg    memset(&xgcv, 0, sizeof(xgcv));
780e0a2b6dfSmrg    xgcv.foreground = 0UL;
781e0a2b6dfSmrg    xgcv.graphics_exposures = False;
782e0a2b6dfSmrg    mask = GCForeground | GCGraphicsExposures;
783e0a2b6dfSmrg    graphics_gc = XCreateGC(display, vwindow, mask, &xgcv);
784e0a2b6dfSmrg
785e0a2b6dfSmrg    pw = graphic->pixw;
786e0a2b6dfSmrg    ph = graphic->pixh;
787e0a2b6dfSmrg
788e0a2b6dfSmrg    TRACE(("refreshed graphic covers 0,0 to %d,%d\n",
789e0a2b6dfSmrg	   (graphic->actual_width - 1) * pw + pw - 1,
790e0a2b6dfSmrg	   (graphic->actual_height - 1) * ph + ph - 1));
791e0a2b6dfSmrg    TRACE(("refreshed area covers %d,%d to %d,%d\n",
792e0a2b6dfSmrg	   x, y,
793e0a2b6dfSmrg	   x + w - 1,
794e0a2b6dfSmrg	   y + h - 1));
795e0a2b6dfSmrg
796e0a2b6dfSmrg    old_fg = COLOR_HOLE;
797e0a2b6dfSmrg    holes = total = 0;
798894e0ac8Smrg    rbase = 0;
799894e0ac8Smrg    for (r = 0; r < graphic->actual_height; r++) {
800894e0ac8Smrg	int rtest = rbase;
801894e0ac8Smrg
802894e0ac8Smrg	rbase += ph;
803894e0ac8Smrg	if (rtest + ph - 1 < y)
804894e0ac8Smrg	    continue;
805894e0ac8Smrg	if (rtest > y + h - 1)
806894e0ac8Smrg	    continue;
807894e0ac8Smrg
808894e0ac8Smrg	cbase = 0;
809e0a2b6dfSmrg	for (c = 0; c < graphic->actual_width; c++) {
810894e0ac8Smrg	    int ctest = cbase;
811e0a2b6dfSmrg
812894e0ac8Smrg	    cbase += pw;
813894e0ac8Smrg	    if (ctest + pw - 1 < x)
814894e0ac8Smrg		continue;
815894e0ac8Smrg	    if (ctest > x + w - 1)
816894e0ac8Smrg		continue;
817e0a2b6dfSmrg
818e0a2b6dfSmrg	    total++;
819e0a2b6dfSmrg	    color = graphic->pixels[r * graphic->max_width + c];
820e0a2b6dfSmrg	    if (color == COLOR_HOLE) {
821e0a2b6dfSmrg		holes++;
822e0a2b6dfSmrg		continue;
823e0a2b6dfSmrg	    }
824e0a2b6dfSmrg
825e0a2b6dfSmrg	    if (color != old_fg) {
826e0a2b6dfSmrg		xgcv.foreground =
827894e0ac8Smrg		    color_register_to_xpixel(&graphic->color_registers[color],
828e0a2b6dfSmrg					     graphic->xw);
829e0a2b6dfSmrg		XChangeGC(display, graphics_gc, mask, &xgcv);
830e0a2b6dfSmrg		old_fg = color;
831e0a2b6dfSmrg	    }
832e0a2b6dfSmrg
833e0a2b6dfSmrg	    XFillRectangle(display, vwindow, graphics_gc,
834894e0ac8Smrg			   xbase + ctest,
835894e0ac8Smrg			   ybase + rtest,
836894e0ac8Smrg			   (unsigned) pw,
837894e0ac8Smrg			   (unsigned) ph);
838e0a2b6dfSmrg	}
839894e0ac8Smrg    }
840e0a2b6dfSmrg
841e0a2b6dfSmrg#ifdef DEBUG_REFRESH
842e0a2b6dfSmrg    {
843e0a2b6dfSmrg	XColor def;
844e0a2b6dfSmrg
845e0a2b6dfSmrg	def.red = (short) (1.0 * 65535.0);
846e0a2b6dfSmrg	def.green = (short) (0.1 * 65535.0);
847e0a2b6dfSmrg	def.blue = (short) (1.0 * 65535.0);
848e0a2b6dfSmrg	def.flags = DoRed | DoGreen | DoBlue;
849e0a2b6dfSmrg	if (allocateBestRGB(graphic->xw, &def)) {
850e0a2b6dfSmrg	    xgcv.foreground = def.pixel;
851e0a2b6dfSmrg	    XChangeGC(display, graphics_gc, mask, &xgcv);
852e0a2b6dfSmrg	}
853e0a2b6dfSmrg	XFillRectangle(display, vwindow, graphics_gc,
854e0a2b6dfSmrg		       xbase + 0,
855e0a2b6dfSmrg		       ybase + 0,
856e0a2b6dfSmrg		       (unsigned) pw, (unsigned) ph);
857e0a2b6dfSmrg	XFillRectangle(display, vwindow, graphics_gc,
858e0a2b6dfSmrg		       xbase + (graphic->actual_width - 1) * pw,
859e0a2b6dfSmrg		       ybase + (graphic->actual_height - 1) * ph,
860e0a2b6dfSmrg		       (unsigned) pw, (unsigned) ph);
861e0a2b6dfSmrg
862e0a2b6dfSmrg	def.red = (unsigned short) ((1.0 - 0.1 * (rand() / (double)
863e0a2b6dfSmrg						  RAND_MAX) * 65535.0));
864e0a2b6dfSmrg	def.green = (unsigned short) ((0.7 + 0.2 * (rand() / (double)
865e0a2b6dfSmrg						    RAND_MAX)) * 65535.0);
866e0a2b6dfSmrg	def.blue = (unsigned short) ((0.1 + 0.1 * (rand() / (double)
867e0a2b6dfSmrg						   RAND_MAX)) * 65535.0);
868e0a2b6dfSmrg	def.flags = DoRed | DoGreen | DoBlue;
869e0a2b6dfSmrg	if (allocateBestRGB(graphic->xw, &def)) {
870e0a2b6dfSmrg	    xgcv.foreground = def.pixel;
871e0a2b6dfSmrg	    XChangeGC(display, graphics_gc, mask, &xgcv);
872e0a2b6dfSmrg	}
873e0a2b6dfSmrg	XDrawLine(display, vwindow, graphics_gc,
874e0a2b6dfSmrg		  xbase + x + 0, ybase + y + 0,
875e0a2b6dfSmrg		  xbase + x + w - 1, ybase + y + 0);
876e0a2b6dfSmrg	XDrawLine(display, vwindow, graphics_gc,
877e0a2b6dfSmrg		  xbase + x + w - 1, ybase + y + 0,
878e0a2b6dfSmrg		  xbase + x + 0, ybase + y + h - 1);
879e0a2b6dfSmrg	XDrawLine(display, vwindow, graphics_gc,
880e0a2b6dfSmrg		  xbase + x + 0, ybase + y + h - 1,
881e0a2b6dfSmrg		  xbase + x + w - 1, ybase + y + h - 1);
882e0a2b6dfSmrg	XDrawLine(display, vwindow, graphics_gc,
883e0a2b6dfSmrg		  xbase + x + w - 1, ybase + y + h - 1,
884e0a2b6dfSmrg		  xbase + x + 0, ybase + y + 0);
885e0a2b6dfSmrg    }
886e0a2b6dfSmrg#endif
887e0a2b6dfSmrg    XFlush(display);
888894e0ac8Smrg    TRACE(("done refreshing graphic: %d of %d refreshed pixels were holes\n",
889e0a2b6dfSmrg	   holes, total));
890e0a2b6dfSmrg
891e0a2b6dfSmrg    XFreeGC(display, graphics_gc);
892e0a2b6dfSmrg}
893e0a2b6dfSmrg
894e0a2b6dfSmrg/*
895e0a2b6dfSmrg * Primary color hues:
896e0a2b6dfSmrg *  blue:    0 degrees
897e0a2b6dfSmrg *  red:   120 degrees
898e0a2b6dfSmrg *  green: 240 degrees
899e0a2b6dfSmrg */
900894e0ac8Smrgvoid
901e0a2b6dfSmrghls2rgb(int h, int l, int s, short *r, short *g, short *b)
902e0a2b6dfSmrg{
903e0a2b6dfSmrg    double hs = (h + 240) % 360;
904e0a2b6dfSmrg    double hv = hs / 360.0;
905e0a2b6dfSmrg    double lv = l / 100.0;
906e0a2b6dfSmrg    double sv = s / 100.0;
907894e0ac8Smrg    double c, x, m, c2;
908e0a2b6dfSmrg    double r1, g1, b1;
909e0a2b6dfSmrg    int hpi;
910e0a2b6dfSmrg
911e0a2b6dfSmrg    if (s == 0) {
912e0a2b6dfSmrg	*r = *g = *b = (short) l;
913e0a2b6dfSmrg	return;
914e0a2b6dfSmrg    }
915e0a2b6dfSmrg
916894e0ac8Smrg    if ((c2 = ((2.0 * lv) - 1.0)) < 0.0)
917894e0ac8Smrg	c2 = -c2;
918894e0ac8Smrg    c = (1.0 - c2) * sv;
919e0a2b6dfSmrg    hpi = (int) (hv * 6.0);
920e0a2b6dfSmrg    x = (hpi & 1) ? c : 0.0;
921e0a2b6dfSmrg    m = lv - 0.5 * c;
922e0a2b6dfSmrg
923e0a2b6dfSmrg    switch (hpi) {
924e0a2b6dfSmrg    case 0:
925e0a2b6dfSmrg	r1 = c;
926e0a2b6dfSmrg	g1 = x;
927e0a2b6dfSmrg	b1 = 0.0;
928e0a2b6dfSmrg	break;
929e0a2b6dfSmrg    case 1:
930e0a2b6dfSmrg	r1 = x;
931e0a2b6dfSmrg	g1 = c;
932e0a2b6dfSmrg	b1 = 0.0;
933e0a2b6dfSmrg	break;
934e0a2b6dfSmrg    case 2:
935e0a2b6dfSmrg	r1 = 0.0;
936e0a2b6dfSmrg	g1 = c;
937e0a2b6dfSmrg	b1 = x;
938e0a2b6dfSmrg	break;
939e0a2b6dfSmrg    case 3:
940e0a2b6dfSmrg	r1 = 0.0;
941e0a2b6dfSmrg	g1 = x;
942e0a2b6dfSmrg	b1 = c;
943e0a2b6dfSmrg	break;
944e0a2b6dfSmrg    case 4:
945e0a2b6dfSmrg	r1 = x;
946e0a2b6dfSmrg	g1 = 0.0;
947e0a2b6dfSmrg	b1 = c;
948e0a2b6dfSmrg	break;
949e0a2b6dfSmrg    case 5:
950e0a2b6dfSmrg	r1 = c;
951e0a2b6dfSmrg	g1 = 0.0;
952e0a2b6dfSmrg	b1 = x;
953e0a2b6dfSmrg	break;
954e0a2b6dfSmrg    default:
955894e0ac8Smrg	TRACE(("Bad HLS input: [%d,%d,%d], returning white\n", h, l, s));
956e0a2b6dfSmrg	*r = (short) 100;
957e0a2b6dfSmrg	*g = (short) 100;
958e0a2b6dfSmrg	*b = (short) 100;
959e0a2b6dfSmrg	return;
960e0a2b6dfSmrg    }
961e0a2b6dfSmrg
962e0a2b6dfSmrg    *r = (short) ((r1 + m) * 100.0 + 0.5);
963e0a2b6dfSmrg    *g = (short) ((g1 + m) * 100.0 + 0.5);
964e0a2b6dfSmrg    *b = (short) ((b1 + m) * 100.0 + 0.5);
965e0a2b6dfSmrg
966e0a2b6dfSmrg    if (*r < 0)
967e0a2b6dfSmrg	*r = 0;
968e0a2b6dfSmrg    else if (*r > 100)
969e0a2b6dfSmrg	*r = 100;
970e0a2b6dfSmrg    if (*g < 0)
971e0a2b6dfSmrg	*g = 0;
972e0a2b6dfSmrg    else if (*g > 100)
973e0a2b6dfSmrg	*g = 100;
974e0a2b6dfSmrg    if (*b < 0)
975e0a2b6dfSmrg	*b = 0;
976e0a2b6dfSmrg    else if (*b > 100)
977e0a2b6dfSmrg	*b = 100;
978e0a2b6dfSmrg}
979e0a2b6dfSmrg
980894e0ac8Smrgvoid
981894e0ac8Smrgdump_graphic(Graphic const *graphic)
982e0a2b6dfSmrg{
983894e0ac8Smrg#if defined(DUMP_COLORS) || defined(DUMP_BITMAP)
984894e0ac8Smrg    RegisterNum color;
985894e0ac8Smrg#endif
986894e0ac8Smrg#ifdef DUMP_BITMAP
987894e0ac8Smrg    int r, c;
988894e0ac8Smrg    ColorRegister const *reg;
989894e0ac8Smrg#endif
990e0a2b6dfSmrg
991894e0ac8Smrg    (void) graphic;
992e0a2b6dfSmrg
993894e0ac8Smrg    TRACE(("graphic stats: id=%u charrow=%d charcol=%d actual_width=%d actual_height=%d pixw=%d pixh=%d\n",
994894e0ac8Smrg	   graphic->id,
995894e0ac8Smrg	   graphic->charrow,
996894e0ac8Smrg	   graphic->charcol,
997894e0ac8Smrg	   graphic->actual_width,
998894e0ac8Smrg	   graphic->actual_height,
999894e0ac8Smrg	   graphic->pixw,
1000894e0ac8Smrg	   graphic->pixh));
1001e0a2b6dfSmrg
1002894e0ac8Smrg#ifdef DUMP_COLORS
1003894e0ac8Smrg    TRACE(("graphic colors:\n"));
1004894e0ac8Smrg    for (color = 0; color < graphic->valid_registers; color++) {
1005894e0ac8Smrg	TRACE(("%03u: %d,%d,%d\n",
1006894e0ac8Smrg	       color,
1007894e0ac8Smrg	       graphic->color_registers[color].r,
1008894e0ac8Smrg	       graphic->color_registers[color].g,
1009894e0ac8Smrg	       graphic->color_registers[color].b));
1010e0a2b6dfSmrg    }
1011e0a2b6dfSmrg#endif
1012e0a2b6dfSmrg
1013894e0ac8Smrg#ifdef DUMP_BITMAP
1014894e0ac8Smrg    TRACE(("graphic pixels:\n"));
1015894e0ac8Smrg    for (r = 0; r < graphic->actual_height; r++) {
1016894e0ac8Smrg	for (c = 0; c < graphic->actual_width; c++) {
1017894e0ac8Smrg	    color = graphic->pixels[r * graphic->max_width + c];
1018894e0ac8Smrg	    if (color == COLOR_HOLE) {
1019894e0ac8Smrg		TRACE(("?"));
1020e0a2b6dfSmrg	    } else {
1021894e0ac8Smrg		reg = &graphic->color_registers[color];
1022894e0ac8Smrg		if (reg->r + reg->g + reg->b > 200) {
1023894e0ac8Smrg		    TRACE(("#"));
1024894e0ac8Smrg		} else if (reg->r + reg->g + reg->b > 150) {
1025894e0ac8Smrg		    TRACE(("%%"));
1026894e0ac8Smrg		} else if (reg->r + reg->g + reg->b > 100) {
1027894e0ac8Smrg		    TRACE((":"));
1028894e0ac8Smrg		} else if (reg->r + reg->g + reg->b > 80) {
1029894e0ac8Smrg		    TRACE(("."));
1030894e0ac8Smrg		} else {
1031894e0ac8Smrg		    TRACE((" "));
1032e0a2b6dfSmrg		}
1033e0a2b6dfSmrg	    }
1034e0a2b6dfSmrg	}
1035894e0ac8Smrg	TRACE(("\n"));
1036e0a2b6dfSmrg    }
1037e0a2b6dfSmrg
1038894e0ac8Smrg    TRACE(("\n"));
1039894e0ac8Smrg#endif
1040e0a2b6dfSmrg}
1041e0a2b6dfSmrg
1042e0a2b6dfSmrg/* Erase the portion of any displayed graphic overlapping with a rectangle
1043e0a2b6dfSmrg * of the given size and location in pixels.
1044e0a2b6dfSmrg * This is used to allow text to "erase" graphics underneath it.
1045e0a2b6dfSmrg */
1046e0a2b6dfSmrgstatic void
1047894e0ac8Smrgerase_graphic(Graphic *graphic, int x, int y, int w, int h)
1048e0a2b6dfSmrg{
1049e0a2b6dfSmrg    RegisterNum hole = COLOR_HOLE;
1050e0a2b6dfSmrg    int pw, ph;
1051e0a2b6dfSmrg    int r, c;
1052894e0ac8Smrg    int rbase, cbase;
1053e0a2b6dfSmrg
1054e0a2b6dfSmrg    pw = graphic->pixw;
1055e0a2b6dfSmrg    ph = graphic->pixh;
1056e0a2b6dfSmrg
1057894e0ac8Smrg    TRACE(("erasing graphic %d,%d %dx%d\n", x, y, w, h));
1058e0a2b6dfSmrg
1059894e0ac8Smrg    rbase = 0;
1060e0a2b6dfSmrg    for (r = 0; r < graphic->actual_height; r++) {
1061894e0ac8Smrg	if (rbase + ph - 1 >= y
1062894e0ac8Smrg	    && rbase <= y + h - 1) {
1063894e0ac8Smrg	    cbase = 0;
1064894e0ac8Smrg	    for (c = 0; c < graphic->actual_width; c++) {
1065894e0ac8Smrg		if (cbase + pw - 1 >= x
1066894e0ac8Smrg		    && cbase <= x + w - 1) {
1067894e0ac8Smrg		    graphic->pixels[r * graphic->max_width + c] = hole;
1068894e0ac8Smrg		}
1069894e0ac8Smrg		cbase += pw;
1070894e0ac8Smrg	    }
1071e0a2b6dfSmrg	}
1072894e0ac8Smrg	rbase += ph;
1073e0a2b6dfSmrg    }
1074e0a2b6dfSmrg}
1075e0a2b6dfSmrg
1076e0a2b6dfSmrgstatic int
1077894e0ac8Smrgcompare_graphic_ids(const void *left, const void *right)
1078e0a2b6dfSmrg{
1079894e0ac8Smrg    const Graphic *l = *(const Graphic *const *) left;
1080894e0ac8Smrg    const Graphic *r = *(const Graphic *const *) right;
1081e0a2b6dfSmrg
1082e0a2b6dfSmrg    if (!l->valid || !r->valid)
1083e0a2b6dfSmrg	return 0;
1084e0a2b6dfSmrg    if (l->id < r->id)
1085e0a2b6dfSmrg	return -1;
1086e0a2b6dfSmrg    else
1087e0a2b6dfSmrg	return 1;
1088e0a2b6dfSmrg}
1089e0a2b6dfSmrg
1090894e0ac8Smrgvoid
1091e0a2b6dfSmrgrefresh_displayed_graphics(TScreen const *screen,
1092e0a2b6dfSmrg			   int leftcol,
1093e0a2b6dfSmrg			   int toprow,
1094e0a2b6dfSmrg			   int ncols,
1095e0a2b6dfSmrg			   int nrows)
1096e0a2b6dfSmrg{
1097894e0ac8Smrg    Graphic *ordered_graphics[MAX_GRAPHICS];
1098894e0ac8Smrg    Graphic *graphic;
1099894e0ac8Smrg    unsigned ii;
1100894e0ac8Smrg    unsigned jj = 0;
1101e0a2b6dfSmrg    int x, y, w, h;
1102e0a2b6dfSmrg    int xbase, ybase;
1103e0a2b6dfSmrg
1104894e0ac8Smrg    FOR_EACH_SLOT(ii) {
1105894e0ac8Smrg	if ((graphic = getActiveSlot(ii))) {
1106894e0ac8Smrg	    ordered_graphics[jj++] = graphic;
1107894e0ac8Smrg	}
1108894e0ac8Smrg    }
1109894e0ac8Smrg    if (jj > 1) {
1110894e0ac8Smrg	qsort(ordered_graphics,
1111894e0ac8Smrg	      (size_t) jj,
1112894e0ac8Smrg	      sizeof(ordered_graphics[0]),
1113894e0ac8Smrg	      compare_graphic_ids);
1114e0a2b6dfSmrg    }
1115e0a2b6dfSmrg
1116894e0ac8Smrg    for (ii = 0; ii < jj; ++ii) {
1117e0a2b6dfSmrg	graphic = ordered_graphics[ii];
1118894e0ac8Smrg	if (graphic->bufferid != screen->whichBuf)
1119e0a2b6dfSmrg	    continue;
1120e0a2b6dfSmrg
1121e0a2b6dfSmrg	x = (leftcol - graphic->charcol) * FontWidth(screen);
1122e0a2b6dfSmrg	y = (toprow - graphic->charrow) * FontHeight(screen);
1123e0a2b6dfSmrg	w = ncols * FontWidth(screen);
1124e0a2b6dfSmrg	h = nrows * FontHeight(screen);
1125e0a2b6dfSmrg
1126e0a2b6dfSmrg	xbase = (OriginX(screen)
1127e0a2b6dfSmrg		 + graphic->charcol * FontWidth(screen));
1128e0a2b6dfSmrg	ybase = (OriginY(screen)
1129e0a2b6dfSmrg		 + (graphic->charrow - screen->topline) * FontHeight(screen));
1130e0a2b6dfSmrg
1131e0a2b6dfSmrg	if (xbase + x + w + OriginX(screen) > FullWidth(screen))
1132e0a2b6dfSmrg	    w = FullWidth(screen) - (xbase + x + OriginX(screen));
1133e0a2b6dfSmrg	if (ybase + y + h + OriginY(screen) > FullHeight(screen))
1134e0a2b6dfSmrg	    h = FullHeight(screen) - (ybase + y + OriginY(screen));
1135e0a2b6dfSmrg	else if (ybase + y < OriginY(screen)) {
1136e0a2b6dfSmrg	    int diff = OriginY(screen) - (ybase + y);
1137e0a2b6dfSmrg	    y += diff;
1138e0a2b6dfSmrg	    h -= diff;
1139e0a2b6dfSmrg	}
1140e0a2b6dfSmrg
1141e0a2b6dfSmrg	TRACE(("graphics refresh: screen->topline=%d leftcol=%d toprow=%d nrows=%d ncols=%d x=%d y=%d w=%d h=%d xbase=%d ybase=%d\n",
1142e0a2b6dfSmrg	       screen->topline,
1143e0a2b6dfSmrg	       leftcol, toprow,
1144e0a2b6dfSmrg	       nrows, ncols,
1145e0a2b6dfSmrg	       x, y, w, h,
1146e0a2b6dfSmrg	       xbase, ybase));
1147894e0ac8Smrg	refresh_graphic(screen, graphic, xbase, ybase, x, y, w, h);
1148e0a2b6dfSmrg    }
1149e0a2b6dfSmrg}
1150e0a2b6dfSmrg
1151894e0ac8Smrgvoid
1152e0a2b6dfSmrgrefresh_modified_displayed_graphics(TScreen const *screen)
1153e0a2b6dfSmrg{
1154894e0ac8Smrg    Graphic *graphic;
1155894e0ac8Smrg    unsigned ii;
1156e0a2b6dfSmrg    int leftcol, toprow;
1157e0a2b6dfSmrg    int nrows, ncols;
1158e0a2b6dfSmrg    int x, y, w, h;
1159e0a2b6dfSmrg    int xbase, ybase;
1160e0a2b6dfSmrg
1161894e0ac8Smrg    FOR_EACH_SLOT(ii) {
1162894e0ac8Smrg	if (!(graphic = getActiveSlot(ii)))
1163894e0ac8Smrg	    continue;
1164894e0ac8Smrg	if (graphic->bufferid != screen->whichBuf)
1165894e0ac8Smrg	    continue;
1166894e0ac8Smrg	if (!graphic->dirty)
1167e0a2b6dfSmrg	    continue;
1168e0a2b6dfSmrg
1169e0a2b6dfSmrg	leftcol = graphic->charcol;
1170e0a2b6dfSmrg	toprow = graphic->charrow;
1171e0a2b6dfSmrg	nrows = (((graphic->actual_height * graphic->pixh)
1172e0a2b6dfSmrg		  + FontHeight(screen) - 1)
1173e0a2b6dfSmrg		 / FontHeight(screen));
1174e0a2b6dfSmrg	ncols = (((graphic->actual_width * graphic->pixw)
1175e0a2b6dfSmrg		  + FontWidth(screen) - 1)
1176e0a2b6dfSmrg		 / FontWidth(screen));
1177e0a2b6dfSmrg
1178e0a2b6dfSmrg	x = (leftcol - graphic->charcol) * FontWidth(screen);
1179e0a2b6dfSmrg	y = (toprow - graphic->charrow) * FontHeight(screen);
1180e0a2b6dfSmrg	w = ncols * FontWidth(screen);
1181e0a2b6dfSmrg	h = nrows * FontHeight(screen);
1182e0a2b6dfSmrg
1183e0a2b6dfSmrg	xbase = (OriginX(screen)
1184e0a2b6dfSmrg		 + graphic->charcol * FontWidth(screen));
1185e0a2b6dfSmrg	ybase = (OriginY(screen)
1186e0a2b6dfSmrg		 + (graphic->charrow - screen->topline) * FontHeight(screen));
1187e0a2b6dfSmrg
1188e0a2b6dfSmrg	if (xbase + x + w + OriginX(screen) > FullWidth(screen))
1189e0a2b6dfSmrg	    w = FullWidth(screen) - (xbase + x + OriginX(screen));
1190e0a2b6dfSmrg	if (ybase + y + h + OriginY(screen) > FullHeight(screen))
1191e0a2b6dfSmrg	    h = FullHeight(screen) - (ybase + y + OriginY(screen));
1192e0a2b6dfSmrg	else if (ybase + y < OriginY(screen)) {
1193e0a2b6dfSmrg	    int diff = OriginY(screen) - (ybase + y);
1194e0a2b6dfSmrg	    y += diff;
1195e0a2b6dfSmrg	    h -= diff;
1196e0a2b6dfSmrg	}
1197e0a2b6dfSmrg
1198e0a2b6dfSmrg	TRACE(("full graphics refresh: screen->topline=%d leftcol=%d toprow=%d nrows=%d ncols=%d x=%d y=%d w=%d h=%d xbase=%d ybase=%d\n",
1199e0a2b6dfSmrg	       screen->topline,
1200e0a2b6dfSmrg	       leftcol, toprow,
1201e0a2b6dfSmrg	       nrows, ncols,
1202e0a2b6dfSmrg	       x, y, w, h,
1203e0a2b6dfSmrg	       xbase, ybase));
1204894e0ac8Smrg	refresh_graphic(screen, graphic, xbase, ybase, x, y, w, h);
1205e0a2b6dfSmrg	graphic->dirty = 0;
1206e0a2b6dfSmrg    }
1207e0a2b6dfSmrg}
1208e0a2b6dfSmrg
1209894e0ac8Smrgvoid
1210e0a2b6dfSmrgscroll_displayed_graphics(int rows)
1211e0a2b6dfSmrg{
1212894e0ac8Smrg    Graphic *graphic;
1213894e0ac8Smrg    unsigned ii;
1214e0a2b6dfSmrg
1215e0a2b6dfSmrg    TRACE(("graphics scroll: moving all up %d rows\n", rows));
1216894e0ac8Smrg    /* FIXME: VT125 ReGIS graphics are fixed at the upper left of the display; need to verify */
1217894e0ac8Smrg
1218894e0ac8Smrg    FOR_EACH_SLOT(ii) {
1219894e0ac8Smrg	if (!(graphic = getActiveSlot(ii)))
1220e0a2b6dfSmrg	    continue;
1221e0a2b6dfSmrg
1222e0a2b6dfSmrg	graphic->charrow -= rows;
1223e0a2b6dfSmrg    }
1224e0a2b6dfSmrg}
1225e0a2b6dfSmrg
1226894e0ac8Smrgvoid
1227e0a2b6dfSmrgpixelarea_clear_displayed_graphics(TScreen const *screen,
1228e0a2b6dfSmrg				   int winx,
1229e0a2b6dfSmrg				   int winy,
1230e0a2b6dfSmrg				   int w,
1231e0a2b6dfSmrg				   int h)
1232e0a2b6dfSmrg{
1233894e0ac8Smrg    Graphic *graphic;
1234894e0ac8Smrg    unsigned ii;
1235e0a2b6dfSmrg    int x, y;
1236e0a2b6dfSmrg
1237894e0ac8Smrg    FOR_EACH_SLOT(ii) {
1238894e0ac8Smrg	if (!(graphic = getActiveSlot(ii)))
1239e0a2b6dfSmrg	    continue;
1240e0a2b6dfSmrg
1241e0a2b6dfSmrg	x = winx - graphic->charcol * FontWidth(screen);
1242e0a2b6dfSmrg	y = winy - graphic->charrow * FontHeight(screen);
1243e0a2b6dfSmrg
1244e0a2b6dfSmrg	TRACE(("pixelarea graphics erase: screen->topline=%d winx=%d winy=%d w=%d h=%d x=%d y=%d\n",
1245e0a2b6dfSmrg	       screen->topline,
1246e0a2b6dfSmrg	       winx, winy,
1247e0a2b6dfSmrg	       w, h,
1248e0a2b6dfSmrg	       x, y));
1249894e0ac8Smrg	erase_graphic(graphic, x, y, w, h);
1250e0a2b6dfSmrg    }
1251e0a2b6dfSmrg}
1252e0a2b6dfSmrg
1253894e0ac8Smrgvoid
1254e0a2b6dfSmrgchararea_clear_displayed_graphics(TScreen const *screen,
1255e0a2b6dfSmrg				  int leftcol,
1256e0a2b6dfSmrg				  int toprow,
1257e0a2b6dfSmrg				  int ncols,
1258e0a2b6dfSmrg				  int nrows)
1259e0a2b6dfSmrg{
1260e0a2b6dfSmrg    int x, y, w, h;
1261e0a2b6dfSmrg
1262e0a2b6dfSmrg    x = leftcol * FontWidth(screen);
1263e0a2b6dfSmrg    y = toprow * FontHeight(screen);
1264e0a2b6dfSmrg    w = ncols * FontWidth(screen);
1265e0a2b6dfSmrg    h = nrows * FontHeight(screen);
1266e0a2b6dfSmrg
1267e0a2b6dfSmrg    TRACE(("chararea clear graphics: screen->topline=%d leftcol=%d toprow=%d nrows=%d ncols=%d x=%d y=%d w=%d h=%d\n",
1268e0a2b6dfSmrg	   screen->topline,
1269e0a2b6dfSmrg	   leftcol, toprow,
1270e0a2b6dfSmrg	   nrows, ncols,
1271e0a2b6dfSmrg	   x, y, w, h));
1272e0a2b6dfSmrg    pixelarea_clear_displayed_graphics(screen, x, y, w, h);
1273e0a2b6dfSmrg}
1274e0a2b6dfSmrg
1275894e0ac8Smrgvoid
1276e0a2b6dfSmrgreset_displayed_graphics(TScreen const *screen)
1277e0a2b6dfSmrg{
1278894e0ac8Smrg    unsigned ii;
1279894e0ac8Smrg
1280894e0ac8Smrg    init_color_registers(getSharedRegisters(), screen->terminal_id);
1281894e0ac8Smrg
1282894e0ac8Smrg    TRACE(("resetting all graphics\n"));
1283894e0ac8Smrg    FOR_EACH_SLOT(ii) {
1284894e0ac8Smrg	deactivateSlot(ii);
1285894e0ac8Smrg    }
1286894e0ac8Smrg}
1287894e0ac8Smrg
1288894e0ac8Smrg#ifdef NO_LEAKS
1289894e0ac8Smrgvoid
1290894e0ac8Smrgnoleaks_graphics(void)
1291894e0ac8Smrg{
1292894e0ac8Smrg    unsigned ii;
1293894e0ac8Smrg
1294894e0ac8Smrg    FOR_EACH_SLOT(ii) {
1295894e0ac8Smrg	deactivateSlot(ii);
1296e0a2b6dfSmrg    }
1297e0a2b6dfSmrg}
1298894e0ac8Smrg#endif
1299