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