1 /*
2  *	HT Editor
3  *	display.cc
4  *
5  *	Copyright (C) 1999-2004 Stefan Weyergraf (stefan@weyergraf.de)
6  *
7  *	This program is free software; you can redistribute it and/or modify
8  *	it under the terms of the GNU General Public License version 2 as
9  *	published by the Free Software Foundation.
10  *
11  *	This program is distributed in the hope that it will be useful,
12  *	but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *	GNU General Public License for more details.
15  *
16  *	You should have received a copy of the GNU General Public License
17  *	along with this program; if not, write to the Free Software
18  *	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19  */
20 
21 #include <new>
22 #include <cstdarg>
23 #include <cstdlib>
24 
25 #include "display.h"
26 #include "snprintf.h"
27 
28 #if 0
29 static vc gInverseColors[8] = {
30         VC_WHITE, VC_YELLOW, VC_MAGENTA, VC_RED,
31 	VC_CYAN, VC_GREEN, VC_BLUE, VC_BLACK
32 };
33 #define VC_GET_INVERSE(vc) gInverseColor[(vc)&7]
34 #else
35 #define VC_GET_INVERSE(vc) (7-((vc)&7))
36 #endif
37 
mixSingleColor(vc base,vc layer)38 inline vc mixSingleColor(vc base, vc layer)
39 {
40 	if (VC_GET_BASECOLOR(base) > 7) return layer;
41 
42 	switch (VC_GET_BASECOLOR(layer)) {
43 	case VC_TRANSPARENT_EXCLUSIVE_DOM:
44 	case VC_TRANSPARENT_EXCLUSIVE:
45 	case VC_TRANSPARENT:	return base;
46 
47 	case VC_DARKEN:		return VC_GET_BASECOLOR(base);
48 	case VC_LIGHTEN:	return VC_LIGHT(base);
49 
50 	case VC_MONOCHROME: {
51 		switch (base) {
52 		case VC_CYAN:
53 		case VC_MAGENTA:
54 		case VC_YELLOW:
55 		case VC_WHITE:
56 			return VC_WHITE;
57 		case VC_RED:
58 		case VC_BLACK:
59 		case VC_BLUE:
60 		case VC_GREEN:
61 		default:
62 			return VC_BLACK;
63 		}
64 	}
65 	case VC_INVERSE:	return VC_GET_INVERSE(VC_GET_BASECOLOR(base));
66 	}
67 
68 	return layer;
69 }
70 
mixColors(vcp base,vcp layer)71 vcp mixColors(vcp base, vcp layer)
72 {
73 	vc fg = mixSingleColor(VCP_FOREGROUND(base), VCP_FOREGROUND(layer));
74 	vc bg = mixSingleColor(VCP_BACKGROUND(base), VCP_BACKGROUND(layer));
75 
76 	if (fg == bg) {
77 		if (VC_GET_BASECOLOR(VCP_FOREGROUND(layer)) == VC_TRANSPARENT_EXCLUSIVE
78 		 || VC_GET_BASECOLOR(VCP_BACKGROUND(layer)) == VC_TRANSPARENT_EXCLUSIVE_DOM) {
79 			int fglight = VC_GET_LIGHT(fg);
80 			fg = VC_GET_INVERSE(VC_GET_BASECOLOR(bg)) | fglight;
81 			fg = VC_BLACK;
82 		} else if (VC_GET_BASECOLOR(VCP_BACKGROUND(layer)) == VC_TRANSPARENT_EXCLUSIVE
83 		 || VC_GET_BASECOLOR(VCP_FOREGROUND(layer)) == VC_TRANSPARENT_EXCLUSIVE_DOM) {
84 			int bglight = VC_GET_LIGHT(bg);
85 			bg = VC_GET_INVERSE(VC_GET_BASECOLOR(fg)) | bglight;
86 		}
87 	}
88 	return VCP(fg, bg);
89 }
90 
91 /*
92  *	Display
93  */
assign(int x,int y,int w,int h)94 void Display::assign(int x, int y, int w, int h)
95 {
96 	Bounds b(x, y, w, h);
97 	setBounds(b);
98 }
99 
fillAll(vcp color,char chr,Codepage cp)100 void Display::fillAll(vcp color, char chr, Codepage cp)
101 {
102 	fill(0, 0, w, h, color, chr, cp);
103 }
104 
move(int deltax,int deltay)105 void Display::move(int deltax, int deltay)
106 {
107 	Bounds b(*this);
108 	b.move(deltax, deltay);
109 	setBounds(b);
110 }
111 
resize(int deltaw,int deltah)112 void Display::resize(int deltaw, int deltah)
113 {
114 	Bounds b(*this);
115 	b.resize(deltaw, deltah);
116 	setBounds(b);
117 }
118 
nprintf(int x,int y,vcp color,int maxstrlen,Codepage cp,const char * format,...)119 int Display::nprintf(int x, int y, vcp color, int maxstrlen, Codepage cp, const char *format, ...)
120 {
121 	char buf[512];
122 	va_list ap;
123 	va_start(ap, format);
124 	ht_vsnprintf(buf, MIN((int)sizeof buf, maxstrlen), format, ap);
125 	va_end(ap);
126 	return print(x, y, color, buf, cp);
127 }
128 
print(int x,int y,vc color,const char * str,Codepage cp)129 int Display::print(int x, int y, vc color, const char *str, Codepage cp)
130 {
131 	return nprint(x, y, color, str, 0x7fffffff, cp);
132 }
133 
printW(int x,int y,vcp color,const AbstractChar * widestr)134 int Display::printW(int x, int y, vcp color, const AbstractChar *widestr)
135 {
136 	const AbstractChar *owidestr = widestr;
137 	// FIXME: speed ?
138 	while (widestr->codepage != CP_INVALID) {
139 		if (!printChar(x++, y, color, widestr->chr, widestr->codepage)) break;
140 		widestr++;
141 	}
142 	return widestr-owidestr;
143 }
144 
nprintW(int x,int y,vcp color,const AbstractChar * widestr,int maxstrlen)145 int Display::nprintW(int x, int y, vcp color, const AbstractChar *widestr, int maxstrlen)
146 {
147 	const AbstractChar *owidestr = widestr;
148 	// FIXME: speed ?
149 	while (widestr->codepage != CP_INVALID && maxstrlen--) {
150 		if (!printChar(x++, y, color, widestr->chr, widestr->codepage)) break;
151 		widestr++;
152 	}
153 	return widestr-owidestr;
154 }
155 
printChar(int x,int y,vcp color,char chr,Codepage cp)156 int Display::printChar(int x, int y, vcp color, char chr, Codepage cp)
157 {
158 	// FIXME: speed ?
159 	fill(x, y, 1, 1, color, chr, cp);
160 	return 1;
161 }
162 
printf(int x,int y,vcp color,Codepage cp,const char * format,...)163 int Display::printf(int x, int y, vcp color, Codepage cp, const char *format, ...)
164 {
165 	char buf[512];
166 	va_list ap;
167 	va_start(ap, format);
168 	ht_vsnprintf(buf, sizeof buf, format, ap);
169 	va_end(ap);
170 	return print(x, y, color, buf, cp);
171 }
172 
setBounds(const Bounds & b)173 void Display::setBounds(const Bounds &b)
174 {
175 	Bounds::assign(b.x, b.y, b.w, b.h);
176 }
177 
178 /* graphical extension */
179 #if 0
180 void Display::line(int px1, int py1, int px2, int py2, uint color)
181 {
182 }
183 
184 void Display::putPixel(int px, int py, uint color)
185 {
186 }
187 
188 void Display::textToPixelCoord(int tx, int ty, int &px, int &py) const
189 {
190 	px = tx;
191 	py = ty;
192 }
193 
194 void Display::pixelToTextCoord(int px, int py, int &tx, int &ty) const
195 {
196 	tx = px;
197 	ty = py;
198 }
199 #endif
200 
201 /*
202  *	NullDisplay
203  */
NullRDisplay(const Bounds & b)204 NullRDisplay::NullRDisplay(const Bounds &b)
205 : RDisplay(b)
206 {
207      setCursor(0, 0, CURSOR_OFF);
208 }
209 
fill(int x,int y,int w,int h,vcp color,char chr,Codepage cp)210 void NullRDisplay::fill(int x, int y, int w, int h, vcp color, char chr, Codepage cp)
211 {
212 }
213 
getCursor(int & x,int & y) const214 void NullRDisplay::getCursor(int &x, int &y) const
215 {
216 	x = cursorx;
217 	y = cursory;
218 }
219 
getCursorMode() const220 CursorMode NullRDisplay::getCursorMode() const
221 {
222 	return cursorMode;
223 }
224 
nprint(int ix,int iy,vcp color,const char * str,int maxstrlen,Codepage cp)225 int NullRDisplay::nprint(int ix, int iy, vcp color, const char *str, int maxstrlen, Codepage cp)
226 {
227 	int i = 0;
228 	// FIXME: more efficient impl
229 	if (y < h) {
230 		while ((ix+i < w) && (*str) && (i<maxstrlen)) {
231 			i++;
232 		}
233 	}
234 	return i;
235 }
236 
read(uint & rawchar,vcp & color,int x,int y) const237 bool NullRDisplay::read(uint &rawchar, vcp &color, int x, int y) const
238 {
239 	if ((x >= w) || (y >= h)) return false;
240 	return true;
241 }
242 
setCursor(int x,int y,CursorMode mode)243 void NullRDisplay::setCursor(int x, int y, CursorMode mode)
244 {
245 	cursorx = x;
246 	cursory = y;
247 	setCursorMode(mode);
248 }
249 
setCursorMode(CursorMode mode)250 void NullRDisplay::setCursorMode(CursorMode mode)
251 {
252 	cursorMode = mode;
253 }
254 
255 /*
256  *	BufferedRDisplay
257  */
BufferedRDisplay(const Bounds & b)258 BufferedRDisplay::BufferedRDisplay(const Bounds &b)
259 : RDisplay(b)
260 {
261 	buf = NULL;
262 	setBounds(b);
263 	setCursor(0, 0, CURSOR_OFF);
264 }
265 
~BufferedRDisplay()266 BufferedRDisplay::~BufferedRDisplay()
267 {
268 	free(buf);
269 }
270 
fill(int x,int y,int w,int h,vcp color,char chr,Codepage cp)271 void BufferedRDisplay::fill(int x, int y, int w, int h, vcp color, char chr, Codepage cp)
272 {
273 	uint rawchar = mapCharToSystemCP(chr, cp);
274 	bool transparent = (cp == CP_GRAPHICAL && chr == GC_TRANSPARENT);
275 	if (y < 0) {
276 		h += y;
277 		y = 0;
278 	}
279 	if (x < 0) {
280 		w += x;
281 		x = 0;
282 	}
283 	if (x+w > this->w) {
284 		w = this->w - x;
285 	}
286 	if (y+h > this->h) {
287 		h = this->h - y;
288 	}
289 	for (int iy = y; iy < y+h; iy++) {
290 		ColoredChar *b = buf+x+ iy * this->w;
291 		for (int ix = x; ix < x+w; ix++) {
292 			if (!transparent) b->rawchar = rawchar;
293 			b->color = mixColors(b->color, color);
294 			b++;
295 		}
296 	}
297 }
298 
getCursor(int & x,int & y) const299 void BufferedRDisplay::getCursor(int &x, int &y) const
300 {
301 	x = cursorx;
302 	y = cursory;
303 }
304 
getCursorMode() const305 CursorMode BufferedRDisplay::getCursorMode() const
306 {
307 	return cursorMode;
308 }
309 
nprint(int ix,int iy,vcp color,const char * str,int maxstrlen,Codepage cp)310 int BufferedRDisplay::nprint(int ix, int iy, vcp color, const char *str, int maxstrlen, Codepage cp)
311 {
312 	int i = 0;
313 	ColoredChar *b = buf+ix+ iy * w;
314 	if (iy < h) {
315 		while (ix+i < w && str[i] && i < maxstrlen) {
316 			bool transparent = (cp == CP_GRAPHICAL && str[i] == GC_TRANSPARENT);
317 			if (!transparent) b->rawchar = mapCharToSystemCP(str[i], cp);
318 			b->color = mixColors(b->color, color);
319 			i++;
320 			b++;
321 		}
322 	}
323 	return i;
324 }
325 
read(uint & rawchar,vcp & color,int x,int y) const326 bool BufferedRDisplay::read(uint &rawchar, vcp &color, int x, int y) const
327 {
328 	if (!buf) return false;
329 	if ((x >= w) || (y >= h)) return false;
330 	ColoredChar *b = buf + x + y*w;
331 	rawchar = b->rawchar;
332 	color = b->color;
333 	return true;
334 }
335 
setBounds(const Bounds & b)336 void BufferedRDisplay::setBounds(const Bounds &b)
337 {
338 	Bounds oldb = *(Bounds*)this;
339 	RDisplay::setBounds(b);
340 	ColoredChar *bufnew;
341 	if (w * h) {
342 		bufnew = ht_malloc(sizeof *buf * w * h);
343 		if (!bufnew) throw std::bad_alloc();
344 		ColoredChar *bb = bufnew;
345 		for (int iy = 0; iy < h; iy++) {
346 			for (int ix = 0; ix < w; ix++) {
347 				if ((ix < oldb.w) && (iy < oldb.h) && buf) {
348 					*bb = buf[ix+iy*oldb.w];
349 				} else {
350 					bb->rawchar = ' ';
351 					bb->color = VCP(VC_TRANSPARENT, VC_TRANSPARENT);
352 				}
353 				bb++;
354 			}
355 		}
356 	} else bufnew = NULL;
357 	free(buf);
358 	buf = bufnew;
359 }
360 
setCursor(int x,int y,CursorMode mode)361 void BufferedRDisplay::setCursor(int x, int y, CursorMode mode)
362 {
363 	cursorx = x;
364 	cursory = y;
365 	setCursorMode(mode);
366 }
367 
setCursorMode(CursorMode mode)368 void BufferedRDisplay::setCursorMode(CursorMode mode)
369 {
370 	cursorMode = mode;
371 }
372 
373 /*
374  *	SystemDisplay
375  */
SystemDisplay()376 SystemDisplay::SystemDisplay()
377 {
378 }
379 
380 /*
381  *	SystemRDisplay
382  */
SystemRDisplay(SystemDisplay * System_display,const Bounds & b)383 SystemRDisplay::SystemRDisplay(SystemDisplay *System_display, const Bounds &b)
384 : RDisplay(b)
385 {
386 	system_display = System_display;
387 }
388 
~SystemRDisplay()389 SystemRDisplay::~SystemRDisplay()
390 {
391 }
392 
fill(int x,int y,int w,int h,vcp color,char chr,Codepage cp)393 void SystemRDisplay::fill(int x, int y, int w, int h, vcp color, char chr, Codepage cp)
394 {
395 	x += this->x;
396 	y += this->y;
397 	system_display->fill(x, y, w, h, color, chr, cp);
398 }
399 
getCursor(int & x,int & y) const400 void SystemRDisplay::getCursor(int &x, int &y) const
401 {
402 	system_display->getCursor(x, y);
403 	x -= this->x;
404 	y -= this->y;
405 }
406 
getCursorMode() const407 CursorMode SystemRDisplay::getCursorMode() const
408 {
409 	return system_display->getCursorMode();
410 }
411 
nprint(int x,int y,vcp color,const char * str,int maxstrlen,Codepage cp)412 int SystemRDisplay::nprint(int x, int y, vcp color, const char *str, int maxstrlen, Codepage cp)
413 {
414 	x += this->x;
415 	y += this->y;
416 	return system_display->nprint(x, y, color, str, maxstrlen, cp);
417 }
418 
read(uint & rawchar,vcp & color,int x,int y) const419 bool SystemRDisplay::read(uint &rawchar, vcp &color, int x, int y) const
420 {
421 	x += this->x;
422 	y += this->y;
423 	return system_display->read(rawchar, color, x, y);
424 }
425 
setBounds(const Bounds & b)426 void SystemRDisplay::setBounds(const Bounds &b)
427 {
428 	RDisplay::setBounds(b);
429 }
430 
setCursor(int x,int y,CursorMode mode)431 void SystemRDisplay::setCursor(int x, int y, CursorMode mode)
432 {
433 	x += this->x;
434 	y += this->y;
435 	system_display->setCursor(x, y, mode);
436 }
437 
setCursorMode(CursorMode mode)438 void SystemRDisplay::setCursorMode(CursorMode mode)
439 {
440 	system_display->setCursorMode(mode);
441 }
442