1 /*
2  *	HT Editor
3  *	sysdisplay.cc - screen access functions for POSIX
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 "config.h"
22 #include CURSES_HDR
23 
24 #include <sys/ioctl.h>
25 #include <signal.h>
26 #include <stdlib.h>
27 #include <unistd.h>
28 #include <string.h>
29 #include <locale.h>
30 
31 #include "io/display.h"
32 #include "io/types.h"
33 
34 extern struct sigaction old_SIGTRAP;
35 
mapToGraphical(char chr)36 uint mapToGraphical(char chr)
37 {
38 	switch (chr) {
39 	case GC_1VLINE:
40 		return ACS_VLINE;
41 	case GC_1HLINE:
42 		return ACS_HLINE;
43 	case GC_1CORNER0:
44 		return ACS_URCORNER;
45 	case GC_1CORNER1:
46 		return ACS_LRCORNER;
47 	case GC_1CORNER2:
48 		return ACS_LLCORNER;
49 	case GC_1CORNER3:
50 		return ACS_ULCORNER;
51 	/* for all 'T's: ACS nomenclature differs from ours */
52 	case GC_1UTEE:
53 		return ACS_BTEE;
54 	case GC_1LTEE:
55 		return ACS_RTEE;
56 	case GC_1DTEE:
57 		return ACS_TTEE;
58 	case GC_1RTEE:
59 		return ACS_LTEE;
60 	case GC_1CROSS:
61 		return ACS_PLUS;
62 	case GC_2VLINE:
63 		return ACS_VLINE;
64 	case GC_2HLINE:
65 		return ACS_HLINE;
66 	case GC_2CORNER0:
67 		return ACS_URCORNER;
68 	case GC_2CORNER1:
69 		return ACS_LRCORNER;
70 	case GC_2CORNER2:
71 		return ACS_LLCORNER;
72 	case GC_2CORNER3:
73 		return ACS_ULCORNER;
74 	case GC_LOW:
75 		return ACS_CKBOARD;
76 	case GC_MEDIUM:
77 		return ACS_CKBOARD;
78 	case GC_HIGH:
79 		return ACS_CKBOARD;
80 	case GC_FULL:
81 		return ACS_BLOCK;
82 	case GC_ARROW_UP:
83 		return ACS_UARROW;
84 	case GC_ARROW_DOWN:
85 		return ACS_DARROW;
86 	case GC_ARROW_LEFT:
87 		return (byte)'<';
88 	case GC_ARROW_RIGHT:
89 		return (byte)'>';
90 	case GC_SMALL_ARROW_UP:
91 		return ACS_UARROW;
92 	case GC_SMALL_ARROW_DOWN:
93 		return ACS_DARROW;
94 	case GC_FILLED_CIRCLE:
95 		return ACS_BULLET;
96 	case GC_FILLED_QUAD:
97 		return 'x';
98 	case GC_TRANSPARENT:
99 		return 0;
100 	case GC_FILLED_UPPER:
101 		return '^';
102 	case GC_FILLED_LOWER:
103 		return '_';
104 	}
105 	return '?';
106 }
107 
108 #if 0
109 #define CHAR_LINEV ACS_VLINE
110 #define CHAR_LINEH ACS_HLINE
111 #define CHAR_LINEV_DBL ACS_VLINE
112 #define CHAR_LINEH_DBL ACS_HLINE
113 #define CHAR_BORDERTL ACS_LTEE
114 #define CHAR_BORDERTR ACS_RTEE
115 #define CHAR_BORDERTU ACS_TTEE
116 #define CHAR_BORDERTD ACS_BTEE
117 #define CHAR_BORDERTL_DBL ACS_LTEE
118 #define CHAR_BORDERTR_DBL ACS_RTEE
119 #define CHAR_BORDERTU_DBL ACS_TTEE
120 #define CHAR_BORDERTD_DBL ACS_BTEE
121 #define CHAR_CORNERUL ACS_ULCORNER
122 #define CHAR_CORNERLL ACS_LLCORNER
123 #define CHAR_CORNERUR ACS_URCORNER
124 #define CHAR_CORNERLR ACS_LRCORNER
125 #define CHAR_CORNERUL_DBL ACS_ULCORNER
126 #define CHAR_CORNERLL_DBL ACS_LLCORNER
127 #define CHAR_CORNERUR_DBL ACS_URCORNER
128 #define CHAR_CORNERLR_DBL ACS_LRCORNER
129 #define CHAR_FILLED_L ACS_CKBOARD	/* low */
130 #define CHAR_FILLED_M ACS_CKBOARD	/* medium */
131 #define CHAR_FILLED_H ACS_CKBOARD	/* high */
132 #define CHAR_FILLED_F ACS_BLOCK		/* full */
133 #define CHAR_FILLED_HU '#'
134 #define CHAR_FILLED_HL ' '
135 #define CHAR_QUAD_SMALL 'x'
136 #define CHAR_ARROW_UP ACS_UARROW
137 #define CHAR_ARROW_DOWN ACS_DARROW
138 #define CHAR_ARROWBIG_UP ACS_UARROW
139 #define CHAR_ARROWBIG_DOWN ACS_DARROW
140 #define CHAR_ARROWBIG_RIGHT '>'
141 #define CHAR_ARROWBIG_LEFT '<'
142 #define CHAR_RADIO ACS_BULLET
143 #endif
144 
mapCharToSystemCP(char chr,Codepage cp)145 uint mapCharToSystemCP(char chr, Codepage cp)
146 {
147 	switch (cp) {
148 	case CP_DEVICE: return (byte)chr;
149 //	case CP_WINDOWS: return (byte)chr;
150 	case CP_GRAPHICAL: return mapToGraphical(chr);
151 	default: break;
152 	}
153 	return (byte)chr;
154 }
155 
156 /*
157  *	Class CursesSystemDisplay
158  */
159 
160 struct CursesChar {
161 	uint rawchar;
162 	vcp color;
163 };
164 
165 class CursesSystemDisplay: public SystemDisplay {
166 protected:
167 	CursorMode cursor_mode;
168 	int cursorx, cursory;
169 
170 	CursesChar *buf;
171 	WINDOW *win;
172 	SCREEN *terminal;
173 	bool use_colors, use_high_colors, is_xterm;
174 
175 	short colormap[64];
176 
177 			void 		cursorBold();
178 			void 		cursorHide();
179 			void 		cursorNormal();
180 			void		putChar(CursesChar *dest, uint rawchar, vcp vc);
181 public:
182 					CursesSystemDisplay(const char *title);
183 	virtual				~CursesSystemDisplay();
184 	/* extends Display */
185 	virtual	void			copyFromDisplay(const Display &display, int x, int y, const Bounds &clipping);
186 	virtual	void			fill(int x, int y, int w, int h, vcp color, char chr, Codepage cp = CP_DEVICE);
187 	virtual	void			getCursor(int &x, int &y) const;
188 	virtual	CursorMode		getCursorMode() const;
189 	virtual	int			nprint(int x, int y, vcp color, const char *str, int maxstrlen, Codepage cp = CP_DEVICE);
190 	virtual	bool			read(uint &rawchar, vcp &color, int x, int y) const;
191 	virtual	void			setBounds(const Bounds &b);
192 	virtual	void			setCursor(int x, int y, CursorMode mode = CURSOR_NORMAL);
193 	virtual	void			setCursorMode(CursorMode mode = CURSOR_NORMAL);
194 	/* extends SystemDisplay */
195 	virtual	void			show();
196 	/* debug */
197 		void			doShowCursor();
198 		void			term_on();
199 		void			term_off();
200 };
201 
202 CursesSystemDisplay *gDisplay = NULL;
203 
204 extern struct sigaction old_SIGWINCH;	// from sysinit.cc
205 
206 // dont work, dont know why...
SIGTRAP_sigaction(int i,siginfo_t * info,void * v)207 void SIGTRAP_sigaction(int i, siginfo_t *info, void *v)
208 {
209 	// set cursor for gdb
210 	if (gDisplay) gDisplay->doShowCursor();
211 	old_SIGTRAP.sa_sigaction(i, info, v);
212 }
213 
214 sig_atomic_t gWinChFlag = 0;
215 
SIGWINCH_sigaction(int i,siginfo_t * info,void * v)216 void SIGWINCH_sigaction(int i, siginfo_t *info, void *v)
217 {
218 	// FIXME: really, really, really possible??? (or just Valgrind's fault)
219 //	if (!info) return;
220 	gWinChFlag = true;
221 	if (old_SIGWINCH.sa_sigaction) old_SIGWINCH.sa_sigaction(i, info, v);
222 }
223 
sys_get_winch_flag()224 bool sys_get_winch_flag()
225 {
226 	return gWinChFlag;
227 }
228 
sys_set_winch_flag(bool f)229 void sys_set_winch_flag(bool f)
230 {
231 	gWinChFlag = f;
232 }
233 
sys_get_screen_size(int & w,int & h)234 bool sys_get_screen_size(int &w, int &h)
235 {
236 #if defined TIOCGWINSZ && !defined SCO_FLAVOR
237 	struct winsize winsz;
238 
239 	winsz.ws_col = winsz.ws_row = 0;
240 	/* Ioctl on the STDIN_FILENO */
241 	ioctl(0, TIOCGWINSZ, &winsz);
242 	if (winsz.ws_col && winsz.ws_row) {
243 	        w = winsz.ws_col;
244 		h = winsz.ws_row;
245     		resizeterm(winsz.ws_row, winsz.ws_col);
246 	        clearok(stdscr, TRUE);
247 	        return true;
248         }
249 #endif /* TIOCGWINSZ && !SCO_FLAVOR */
250 	return false;
251 }
252 
sys_set_screen_size(int w,int h)253 bool sys_set_screen_size(int w, int h)
254 {
255 #if defined TIOCSWINSZ && !defined SCO_FLAVOR
256         struct winsize tty_size;
257 
258 	tty_size.ws_row = h;
259 	tty_size.ws_col = w;
260 	tty_size.ws_xpixel = tty_size.ws_ypixel = 0;
261 
262 	return ioctl(0, TIOCSWINSZ, &tty_size) == 0;
263 #endif
264 	return false;
265 }
266 
CursesSystemDisplay(const char * title)267 CursesSystemDisplay::CursesSystemDisplay(const char *title)
268 : SystemDisplay()
269 {
270 	buf = NULL;
271 
272 	cursorx = 0;
273 	cursory = 0;
274 
275 	terminal = NULL;
276 
277 	term_on();
278 	show();
279 }
280 
~CursesSystemDisplay()281 CursesSystemDisplay::~CursesSystemDisplay()
282 {
283 	term_off();
284 	free(buf);
285 }
286 
term_off()287 void CursesSystemDisplay::term_off()
288 {
289 //	if (!terminal) return;
290 	::erase();
291 	::refresh();
292 	::endwin();
293 //	::delscreen(terminal);
294 	terminal = NULL;
295 }
296 
term_on()297 void CursesSystemDisplay::term_on()
298 {
299 	if (terminal) return;
300 	int colors[8] = { COLOR_BLACK, COLOR_BLUE, COLOR_GREEN, COLOR_CYAN, COLOR_RED, COLOR_MAGENTA, COLOR_YELLOW, COLOR_WHITE };
301 
302 	setCursor(0, 0, CURSOR_OFF);
303 
304 	::setlocale(LC_ALL, "");
305 
306 //	terminal = ::newterm(NULL, stdout, stdin);
307 	win = ::initscr();
308 //	win = stdscr;
309 
310 //	::setlocale(LC_ALL, "C");
311 
312 	use_colors = false;
313 	use_high_colors = false;
314 	if (::has_colors()) {
315 		use_colors = true;
316 		char *term = getenv("TERM");
317 		bool bold_support = false;
318 		attr_t attrs;
319 		short cur_color = 1;
320 		::start_color();
321 		/* FIXME: Does this work ???: test if the WA_BOLD attr can be set */
322 		::attr_on(WA_BOLD, 0);
323 		attrs = WA_NORMAL;
324 		attr_get(&attrs, &cur_color, 0);
325 		bold_support = (attrs==WA_BOLD);
326 		::attr_off(WA_BOLD, 0);
327 
328 		is_xterm = (term && strcmp(term, "linux") && strcmp(term, "console"));
329 		if (!is_xterm && !bold_support) {
330 			fprintf(stderr, "warning: terminal is of type '%s' (non-x-terminal) but bold_test fails!", term);
331 		}
332 		if (bold_support) {
333 			use_high_colors = true;
334 		} else {
335 			fprintf(stderr, "warning: terminal only supports 8 foreground colors!");
336 		}
337 		for (int fg=0; fg < 8; fg++) {
338 			for (int bg=0; bg < 8; bg++) {
339 				colormap[fg+bg*8] = fg+bg*8;
340 				::init_pair(fg+bg*8, colors[fg], colors[bg]);
341 			}
342 		}
343 		colormap[7] = 0;
344 		colormap[0] = 7;
345 		::init_pair(7, COLOR_BLACK, COLOR_BLACK);
346 	} else {
347 		fprintf(stderr, "warning: terminal lacks color support!");
348 	}
349 	::wtimeout(win, 1);
350 	::meta(win, 1);
351 	::keypad(win, 1);
352 	::nodelay(win, 1);
353 	::noecho();
354 	::cbreak();
355 	::set_escdelay(500);
356 
357 	assign(0, 0, getmaxx(win), getmaxy(win));
358 }
359 
copyFromDisplay(const Display & d,int x,int y,const Bounds & clipping)360 void CursesSystemDisplay::copyFromDisplay(const Display &d, int x, int y, const Bounds &clipping)
361 {
362 	CursorMode cm = d.getCursorMode();
363 	int cx, cy;
364 	d.getCursor(cx,cy);
365 	cx += d.x;
366 	cy += d.y;
367 //	setCursor(cx, cy, cm);
368 	for (int iy = 0; iy < d.h; iy++) {
369 		CursesChar *k = buf+x+(iy+y)*w;
370 		if (y+iy >= clipping.y+clipping.h) break;
371 		if (y+iy >= clipping.y)
372 		for (int ix=0; ix < d.w; ix++) {
373 			uint rawchar;
374 			vcp color;
375 			d.read(rawchar, color, ix, iy);
376 			if (x+ix < clipping.x+clipping.w && x+ix >= clipping.x)
377 				putChar(k, rawchar, color);
378 			k++;
379 		}
380 	}
381 }
382 
fill(int x,int y,int w,int h,vcp color,char chr,Codepage cp)383 void CursesSystemDisplay::fill(int x, int y, int w, int h, vcp color, char chr, Codepage cp)
384 {
385 	uint rawchar = mapCharToSystemCP(chr, cp);
386 	for (int i=0; i<h; i++) {
387 		CursesChar *b = buf+x+(y+i)*this->w;
388 		if (y+i >= this->h) break;
389 		if (y+i >= 0)
390 		for (int j=0; j<w; j++) {
391 			if (x+j >= this->w) break;
392 			if (x+j >= 0) putChar(b, rawchar, color);
393 			b++;
394 		}
395 	}
396 }
397 
getCursor(int & x,int & y) const398 void CursesSystemDisplay::getCursor(int &x, int &y) const
399 {
400 	x = cursorx;
401 	y = cursory;
402 }
403 
getCursorMode() const404 CursorMode CursesSystemDisplay::getCursorMode() const
405 {
406 	return cursor_mode;
407 }
408 
nprint(int x,int y,vcp color,const char * str,int maxstrlen,Codepage cp)409 int CursesSystemDisplay::nprint(int x, int y, vcp color, const char *str, int maxstrlen, Codepage cp)
410 {
411 	int n = 0;
412 	CursesChar *b=buf+x+y*w;
413 	while (n < maxstrlen && *str && x+n < w) {
414 		uint rawchar = mapCharToSystemCP(*str, cp);
415 		if (x+n>=0) putChar(b, rawchar, color);
416 		b++;
417 		str++;
418 		n++;
419 	}
420 	return n;
421 }
422 
read(uint & rawchar,vcp & color,int x,int y) const423 bool CursesSystemDisplay::read(uint &rawchar, vcp &color, int x, int y) const
424 {
425 	CursesChar *b = buf+x+y*w;
426 	rawchar = b->rawchar;
427 	color = b->color;
428 	return true;
429 }
430 
setBounds(const Bounds & b)431 void CursesSystemDisplay::setBounds(const Bounds &b)
432 {
433 	SystemDisplay::setBounds(b);
434 	free(buf);
435 	buf = ht_malloc(sizeof *buf * w * h);
436 	memset(buf, 0, sizeof *buf * w * h);
437 	fill(x, y, w, h, VCP(VC_WHITE, VC_BLACK), ' ');
438 }
439 
setCursor(int x,int y,CursorMode mode)440 void CursesSystemDisplay::setCursor(int x, int y, CursorMode mode)
441 {
442 	cursorx = x;
443 	cursory = y;
444 	setCursorMode(mode);
445 }
446 
setCursorMode(CursorMode mode)447 void CursesSystemDisplay::setCursorMode(CursorMode mode)
448 {
449 	cursor_mode = mode;
450 }
451 
show()452 void CursesSystemDisplay::show()
453 {
454 	CursesChar *ch = buf;
455 	vcp c = -1;
456 	for (int iy=0; iy < h; iy++) {
457 		::move(iy+y, x);
458 		for (int ix=0; ix < w; ix++) {
459 			if (use_colors && ch->color != c) {
460 				vc fg = VCP_FOREGROUND(ch->color);
461 				vc bg = VCP_BACKGROUND(ch->color);
462 				if (use_high_colors) {
463 					if (0 && is_xterm && fg == VC_LIGHT(VC_BLACK)) {
464 						/* some terminals can't display dark grey (=light black), so we take light grey instead... */
465 						attrset(A_NORMAL);
466 						fg = VC_WHITE;
467 					} else {
468 						if (VC_GET_LIGHT(fg)) attrset(A_BOLD); else attrset(A_NORMAL);
469 					}
470 				}
471 /*				fg = VC_GET_BASECOLOR(fg);
472 				bg = VC_GET_BASECOLOR(bg);*/
473 				fg &= 7;
474 				bg &= 7;
475 				color_set(colormap[bg*8+fg], 0);
476 			}
477 			uint chr = (uint)ch->rawchar;
478 			if ((chr >= 0x20 && chr < 0x7f) || chr > 0xff) {
479 				addch(chr);
480 			} else {
481 				attrset(A_BOLD);
482 				color_set(colormap[VC_RED*8+VC_WHITE], 0);
483 				addch('?');
484 			}
485 			ch++;
486 		}
487 	}
488 
489 	::curs_set(0);
490 
491 	::refresh();
492 
493 	::move(cursory, cursorx);
494 	switch (cursor_mode) {
495 	case CURSOR_OFF:
496 		::curs_set(0); break;
497 	case CURSOR_NORMAL:
498 		::curs_set(1); break;
499 	case CURSOR_BOLD:
500 		if (::curs_set(2) == ERR) ::curs_set(1);
501 		break;
502 	}
503 	::refresh();
504 }
505 
doShowCursor()506 void CursesSystemDisplay::doShowCursor()
507 {
508 /*	::move(0,0);
509 	curs_set(1);
510 	refresh();*/
511 }
512 
putChar(CursesChar * dest,uint rawchar,vcp vc)513 void CursesSystemDisplay::putChar(CursesChar *dest, uint rawchar, vcp vc)
514 {
515 	if (dest >= buf+w*h || dest < buf) return;
516 	if (rawchar) dest->rawchar = rawchar;
517 	dest->color = mixColors(dest->color, vc);
518 }
519 
520 static int sysdisplay_count = 0;
521 
allocSystemDisplay(const char * title)522 SystemDisplay *allocSystemDisplay(const char *title)
523 {
524 	if (sysdisplay_count) return NULL;
525 	sysdisplay_count++;
526 	gDisplay = new CursesSystemDisplay(title);
527 	return gDisplay;
528 }
529 
530 /*
531 void sys_display_enter()
532 {
533 	gDisplay->term_on();
534 }
535 
536 void sys_display_leave()
537 {
538 	gDisplay->term_off();
539 }
540 */
541