1 #include "config.h"
2
3 #include <slang.h>
4 #include <stdio.h>
5 #include <stdlib.h>
6 #include <string.h>
7 #include <sys/signal.h>
8 #include <sys/time.h>
9 #include <sys/types.h>
10 #include <termios.h>
11 #include <unistd.h>
12 #include <wchar.h>
13
14 #ifdef HAVE_ALLOCA_H
15 #include <alloca.h>
16 #endif
17
18 #include "newt.h"
19 #include "newt_pr.h"
20
21 struct Window {
22 int height, width, top, left;
23 SLsmg_Char_Type * buffer;
24 char * title;
25 };
26
27 struct keymap {
28 char * str;
29 int code;
30 char * tc;
31 };
32
33 static struct Window windowStack[20];
34 static struct Window * currentWindow = NULL;
35
36 static char * helplineStack[20];
37 static char ** currentHelpline = NULL;
38
39 static int cursorRow, cursorCol;
40 static int cursorOn = 1;
41 static int noFlowCtrl = 0;
42 static int trashScreen = 0;
43 extern int needResize;
44
45 static const char * const defaultHelpLine =
46 " <Tab>/<Alt-Tab> between elements | <Space> selects | <F12> next screen"
47 ;
48
49 const struct newtColors newtDefaultColorPalette = {
50 "white", "blue", /* root fg, bg */
51 "black", "lightgray", /* border fg, bg */
52 "black", "lightgray", /* window fg, bg */
53 "white", "black", /* shadow fg, bg */
54 "red", "lightgray", /* title fg, bg */
55 "lightgray", "red", /* button fg, bg */
56 "red", "lightgray", /* active button fg, bg */
57 "lightgray", "blue", /* checkbox fg, bg */
58 "lightgray", "red", /* active checkbox fg, bg */
59 "lightgray", "blue", /* entry box fg, bg */
60 "blue", "lightgray", /* label fg, bg */
61 "black", "lightgray", /* listbox fg, bg */
62 "lightgray", "blue", /* active listbox fg, bg */
63 "black", "lightgray", /* textbox fg, bg */
64 "lightgray", "red", /* active textbox fg, bg */
65 "white", "blue", /* help line */
66 "lightgray", "blue", /* root text */
67 "blue", /* scale full */
68 "red", /* scale empty */
69 "blue", "lightgray", /* disabled entry fg, bg */
70 "black", "lightgray", /* compact button fg, bg */
71 "lightgray", "red", /* active & sel listbox */
72 "black", "brown" /* selected listbox */
73 };
74
75 static const struct keymap keymap[] = {
76 { "\033OA", NEWT_KEY_UP, "ku" },
77 { "\020", NEWT_KEY_UP, NULL }, /* emacs ^P */
78 { "\033OB", NEWT_KEY_DOWN, "kd" },
79 { "\016", NEWT_KEY_DOWN, NULL }, /* emacs ^N */
80 { "\033OC", NEWT_KEY_RIGHT, "kr" },
81 { "\006", NEWT_KEY_RIGHT, NULL }, /* emacs ^F */
82 { "\033OD", NEWT_KEY_LEFT, "kl" },
83 { "\002", NEWT_KEY_LEFT, NULL }, /* emacs ^B */
84 { "\033OH", NEWT_KEY_HOME, "kh" },
85 { "\033[1~", NEWT_KEY_HOME, NULL },
86 { "\001", NEWT_KEY_HOME, NULL }, /* emacs ^A */
87 { "\033Ow", NEWT_KEY_END, "kH" },
88 { "\033[4~", NEWT_KEY_END, "@7" },
89 { "\005", NEWT_KEY_END, NULL }, /* emacs ^E */
90
91 { "\033[3~", NEWT_KEY_DELETE, "kD" },
92 { "\004", NEWT_KEY_DELETE, NULL }, /* emacs ^D */
93 { "\033[2~", NEWT_KEY_INSERT, "kI" },
94
95 { "\033\t", NEWT_KEY_UNTAB, "kB" },
96 { "\033[Z", NEWT_KEY_UNTAB, NULL },
97
98 { "\033[5~", NEWT_KEY_PGUP, "kP" },
99 { "\033[6~", NEWT_KEY_PGDN, "kN" },
100 { "\033V", NEWT_KEY_PGUP, NULL },
101 { "\033v", NEWT_KEY_PGUP, NULL },
102 { "\033[G", NEWT_KEY_PGDN, NULL },
103 { "\033[I", NEWT_KEY_PGUP, NULL },
104 { "\026", NEWT_KEY_PGDN, NULL },
105
106 { "\033[[A", NEWT_KEY_F1, NULL },
107 { "\033[[B", NEWT_KEY_F2, NULL },
108 { "\033[[C", NEWT_KEY_F3, NULL },
109 { "\033[[D", NEWT_KEY_F4, NULL },
110 { "\033[[E", NEWT_KEY_F5, NULL },
111
112 { "\033OP", NEWT_KEY_F1, NULL },
113 { "\033OQ", NEWT_KEY_F2, NULL },
114 { "\033OR", NEWT_KEY_F3, NULL },
115 { "\033OS", NEWT_KEY_F4, NULL },
116
117 { "\033[11~", NEWT_KEY_F1, "k1" },
118 { "\033[12~", NEWT_KEY_F2, "k2" },
119 { "\033[13~", NEWT_KEY_F3, "k3" },
120 { "\033[14~", NEWT_KEY_F4, "k4" },
121 { "\033[15~", NEWT_KEY_F5, "k5" },
122 { "\033[17~", NEWT_KEY_F6, "k6" },
123 { "\033[18~", NEWT_KEY_F7, "k7" },
124 { "\033[19~", NEWT_KEY_F8, "k8" },
125 { "\033[20~", NEWT_KEY_F9, "k9" },
126 { "\033[21~", NEWT_KEY_F10, "k;" },
127 { "\033[23~", NEWT_KEY_F11, "F1" },
128 { "\033[24~", NEWT_KEY_F12, "F2" },
129 { "\033", NEWT_KEY_ESCAPE, "@2" },
130 { "\033", NEWT_KEY_ESCAPE, "@9" },
131
132 { "\177", NEWT_KEY_BKSPC, NULL },
133 { "\010", NEWT_KEY_BKSPC, NULL },
134
135 { 0 }, /* LEAVE this one */
136 };
137 static void initKeymap();
138 static void freeKeymap();
139
140 static const char ident[] = // ident friendly
141 "$Version: Newt windowing library v" VERSION " $"
142 "$Copyright: (C) 1996-2003 Red Hat, Inc. Written by Erik Troan $"
143 "$License: Lesser GNU Public License. $";
144
145 static newtSuspendCallback suspendCallback = NULL;
146 static void * suspendCallbackData = NULL;
147
newtSetSuspendCallback(newtSuspendCallback cb,void * data)148 void newtSetSuspendCallback(newtSuspendCallback cb, void * data) {
149 suspendCallback = cb;
150 suspendCallbackData = data;
151 }
152
handleSigwinch(int signum)153 static void handleSigwinch(int signum) {
154 needResize = 1;
155 }
156
getkeyInterruptHook(void)157 static int getkeyInterruptHook(void) {
158 return -1;
159 }
160
_newt_wstrlen(const char * str,int len)161 int _newt_wstrlen(const char *str, int len) {
162 mbstate_t ps;
163 wchar_t tmp;
164 int nchars = 0;
165
166 if (!str) return 0;
167 if (!len) return 0;
168 if (len < 0) len = strlen(str);
169 memset(&ps,0,sizeof(mbstate_t));
170 while (len > 0) {
171 int x,y;
172
173 x = mbrtowc(&tmp,str,len,&ps);
174 if (x >0) {
175 str += x;
176 len -= x;
177 y = wcwidth(tmp);
178 if (y>0)
179 nchars+=y;
180 } else break;
181 }
182 return nchars;
183 }
184
185 /** Trim a string to fit
186 * @param title - string. NULL will be inserted if necessary
187 * @param chrs - available space. (character cells)
188 */
trim_string(char * title,int chrs)189 void trim_string(char *title, int chrs)
190 {
191 char *p = title;
192 int ln;
193 int x = 0,y = 0;
194 wchar_t tmp;
195 mbstate_t ps;
196
197 memset(&ps, 0, sizeof(ps));
198 ln = strlen(title);
199
200 while (*p) {
201 x = mbrtowc(&tmp, p, ln, &ps);
202 if (x < 0) { // error
203 *p = '\0';
204 return;
205 }
206 y = wcwidth(tmp);
207 if (y > chrs) {
208 *p = '\0';
209 return;
210 } else {
211 p += x;
212 ln -= x;
213 chrs -= y;
214 }
215 }
216 }
217
getkey()218 static int getkey() {
219 int c;
220
221 while ((c = SLang_getkey()) == '\xC') { /* if Ctrl-L redraw whole screen */
222 SLsmg_touch_lines(0, SLtt_Screen_Rows);
223 SLsmg_refresh();
224 }
225 return c;
226
227 }
228
updateColorset(char * fg,char * bg,char ** fg_p,char ** bg_p)229 static void updateColorset(char *fg, char *bg, char **fg_p, char **bg_p)
230 {
231 if (*fg && fg_p)
232 *fg_p = fg;
233 if (*bg && bg_p)
234 *bg_p = bg;
235 }
236
237 /* parse color specifications (e.g. root=,black:border=red,blue)
238 * and update the palette
239 */
parseColors(char * s,struct newtColors * palette)240 static void parseColors(char *s, struct newtColors *palette)
241 {
242 char *name, *str, *fg, *bg;
243
244 for (str = s; (s = strtok(str, ";:\n\r\t ")); str = NULL) {
245 name = s;
246 if (!(s = strchr(s, '=')) || !*s)
247 continue;
248 *s = '\0';
249 fg = ++s;
250 if (!(s = strchr(s, ',')) || !*s)
251 continue;
252 *s = '\0';
253 bg = ++s;
254
255 if (!strcmp(name, "root"))
256 updateColorset(fg, bg, &palette->rootFg, &palette->rootBg);
257 else if (!strcmp(name, "border"))
258 updateColorset(fg, bg, &palette->borderFg, &palette->borderBg);
259 else if (!strcmp(name, "window"))
260 updateColorset(fg, bg, &palette->windowFg, &palette->windowBg);
261 else if (!strcmp(name, "shadow"))
262 updateColorset(fg, bg, &palette->shadowFg, &palette->shadowBg);
263 else if (!strcmp(name, "title"))
264 updateColorset(fg, bg, &palette->titleFg, &palette->titleBg);
265 else if (!strcmp(name, "button"))
266 updateColorset(fg, bg, &palette->buttonFg, &palette->buttonBg);
267 else if (!strcmp(name, "actbutton"))
268 updateColorset(fg, bg, &palette->actButtonFg, &palette->actButtonBg);
269 else if (!strcmp(name, "checkbox"))
270 updateColorset(fg, bg, &palette->checkboxFg, &palette->checkboxBg);
271 else if (!strcmp(name, "actcheckbox"))
272 updateColorset(fg, bg, &palette->actCheckboxFg, &palette->actCheckboxBg);
273 else if (!strcmp(name, "entry"))
274 updateColorset(fg, bg, &palette->entryFg, &palette->entryBg);
275 else if (!strcmp(name, "label"))
276 updateColorset(fg, bg, &palette->labelFg, &palette->labelBg);
277 else if (!strcmp(name, "listbox"))
278 updateColorset(fg, bg, &palette->listboxFg, &palette->listboxBg);
279 else if (!strcmp(name, "actlistbox"))
280 updateColorset(fg, bg, &palette->actListboxFg, &palette->actListboxBg);
281 else if (!strcmp(name, "textbox"))
282 updateColorset(fg, bg, &palette->textboxFg, &palette->textboxBg);
283 else if (!strcmp(name, "acttextbox"))
284 updateColorset(fg, bg, &palette->actTextboxFg, &palette->actTextboxBg);
285 else if (!strcmp(name, "helpline"))
286 updateColorset(fg, bg, &palette->helpLineFg, &palette->helpLineBg);
287 else if (!strcmp(name, "roottext"))
288 updateColorset(fg, bg, &palette->rootTextFg, &palette->rootTextBg);
289 else if (!strcmp(name, "emptyscale"))
290 updateColorset(fg, bg, NULL, &palette->emptyScale);
291 else if (!strcmp(name, "fullscale"))
292 updateColorset(fg, bg, NULL, &palette->fullScale);
293 else if (!strcmp(name, "disentry"))
294 updateColorset(fg, bg, &palette->disabledEntryFg, &palette->disabledEntryBg);
295 else if (!strcmp(name, "compactbutton"))
296 updateColorset(fg, bg, &palette->compactButtonFg, &palette->compactButtonBg);
297 else if (!strcmp(name, "actsellistbox"))
298 updateColorset(fg, bg, &palette->actSelListboxFg, &palette->actSelListboxBg);
299 else if (!strcmp(name, "sellistbox"))
300 updateColorset(fg, bg, &palette->selListboxFg, &palette->selListboxBg);
301 }
302 }
303
initColors(void)304 static void initColors(void)
305 {
306 char *colors, *colors_file, buf[16384];
307 FILE *f;
308 struct newtColors palette;
309
310 palette = newtDefaultColorPalette;
311
312 colors_file = getenv("NEWT_COLORS_FILE");
313 #ifdef NEWT_COLORS_FILE
314 if (colors_file == NULL)
315 colors_file = NEWT_COLORS_FILE;
316 #endif
317
318 if ((colors = getenv("NEWT_COLORS"))) {
319 strncpy(buf, colors, sizeof (buf));
320 buf[sizeof (buf) - 1] = '\0';
321 parseColors(buf, &palette);
322 } else if (colors_file && *colors_file && (f = fopen(colors_file, "r"))) {
323 size_t r;
324 if ((r = fread(buf, 1, sizeof (buf) - 1, f)) > 0) {
325 buf[r] = '\0';
326 parseColors(buf, &palette);
327 }
328 fclose(f);
329 }
330
331 newtSetColors(palette);
332 }
333
newtFlushInput(void)334 void newtFlushInput(void) {
335 while (SLang_input_pending(0)) {
336 getkey();
337 }
338 }
339
340 /**
341 * @brief Refresh the screen
342 */
newtRefresh(void)343 void newtRefresh(void) {
344 SLsmg_refresh();
345 }
346
newtSuspend(void)347 void newtSuspend(void) {
348 SLtt_set_cursor_visibility (1);
349 SLsmg_suspend_smg();
350 SLang_reset_tty();
351 SLtt_set_cursor_visibility (cursorOn);
352 }
353
354 /**
355 * @brief Return after suspension.
356 * @return 0 on success.
357 */
newtResume(void)358 int newtResume(void) {
359 SLsmg_resume_smg ();
360 SLsmg_refresh();
361 return SLang_init_tty(0, noFlowCtrl, 0);
362 }
363
newtCls(void)364 void newtCls(void) {
365 SLsmg_set_color(NEWT_COLORSET_ROOT);
366 SLsmg_gotorc(0, 0);
367 SLsmg_erase_eos();
368
369 newtRefresh();
370 }
371
372 /**
373 * @brief Resize the screen
374 * @param redraw - boolean - should we redraw the screen?
375 */
newtResizeScreen(int redraw)376 void newtResizeScreen(int redraw) {
377 /* we can't redraw from scratch, just redisplay SLang screen */
378 SLtt_get_screen_size();
379 /* SLsmg_reinit_smg(); */
380 if (redraw) {
381 SLsmg_touch_lines(0, SLtt_Screen_Rows);
382 newtRefresh();
383 }
384 }
385
386 /**
387 * @brief Initialize the newt library
388 * @return int - 0 for success, else < 0
389 */
newtInit(void)390 int newtInit(void) {
391 char * MonoValue, * MonoEnv = "NEWT_MONO";
392 char * NoFlowCtrlValue, * NoFlowCtrlEnv = "NEWT_NOFLOWCTRL";
393 const char *lang;
394 int ret;
395
396 if ((lang = getenv("LC_ALL")) == NULL)
397 if ((lang = getenv("LC_CTYPE")) == NULL)
398 if ((lang = getenv("LANG")) == NULL)
399 lang = "";
400 /* slang doesn't support multibyte encodings except UTF-8,
401 avoid character corruption by redrawing the screen */
402 if (strstr (lang, ".euc") != NULL)
403 trashScreen = 1;
404
405 (void) strlen(ident);
406
407 SLutf8_enable(-1);
408 SLtt_get_terminfo();
409 SLtt_get_screen_size();
410
411 MonoValue = getenv(MonoEnv);
412 if ( MonoValue != NULL )
413 SLtt_Use_Ansi_Colors = 0;
414
415 NoFlowCtrlValue = getenv(NoFlowCtrlEnv);
416 if ( NoFlowCtrlValue != NULL )
417 noFlowCtrl = 1;
418
419 if ((ret = SLsmg_init_smg()) < 0)
420 return ret;
421 if ((ret = SLang_init_tty(0, noFlowCtrl, 0)) < 0)
422 return ret;
423
424 initColors();
425 newtCursorOff();
426 initKeymap();
427
428 SLsignal_intr(SIGWINCH, handleSigwinch);
429 SLang_getkey_intr_hook = getkeyInterruptHook;
430
431 return 0;
432 }
433
434 /**
435 * @brief Closedown the newt library, tidying screen.
436 * @returns int , 0. (no errors reported)
437 */
newtFinished(void)438 int newtFinished(void) {
439 if (currentWindow) {
440 for (; currentWindow >= windowStack; currentWindow--) {
441 free(currentWindow->buffer);
442 free(currentWindow->title);
443 }
444 currentWindow = NULL;
445 }
446
447 if (currentHelpline) {
448 for (; currentHelpline >= helplineStack; currentHelpline--)
449 free(*currentHelpline);
450 currentHelpline = NULL;
451 }
452
453 freeKeymap();
454
455 SLsmg_gotorc(SLtt_Screen_Rows - 1, 0);
456 newtCursorOn();
457 SLsmg_refresh();
458 SLsmg_reset_smg();
459 SLang_reset_tty();
460
461 return 0;
462 }
463
464 /**
465 * @brief Set the colors used.
466 * @param colors - newtColor struct used.
467 */
newtSetColors(struct newtColors colors)468 void newtSetColors(struct newtColors colors) {
469 if (!SLtt_Use_Ansi_Colors) {
470 int i;
471
472 for (i = 2; i < 25; i++)
473 SLtt_set_mono(i, NULL, 0);
474
475 SLtt_set_mono(NEWT_COLORSET_SELLISTBOX, NULL, SLTT_BOLD_MASK);
476
477 SLtt_set_mono(NEWT_COLORSET_ACTBUTTON, NULL, SLTT_REV_MASK);
478 SLtt_set_mono(NEWT_COLORSET_ACTCHECKBOX, NULL, SLTT_REV_MASK);
479 SLtt_set_mono(NEWT_COLORSET_ACTLISTBOX, NULL, SLTT_REV_MASK);
480 SLtt_set_mono(NEWT_COLORSET_ACTTEXTBOX, NULL, SLTT_REV_MASK);
481
482 SLtt_set_mono(NEWT_COLORSET_ACTSELLISTBOX, NULL, SLTT_REV_MASK | SLTT_BOLD_MASK);
483
484 SLtt_set_mono(NEWT_COLORSET_DISENTRY, NULL, 0); // FIXME
485 SLtt_set_mono(NEWT_COLORSET_FULLSCALE, NULL, SLTT_ULINE_MASK | SLTT_REV_MASK);
486 SLtt_set_mono(NEWT_COLORSET_EMPTYSCALE, NULL, SLTT_ULINE_MASK);
487 return;
488 }
489 SLtt_set_color(NEWT_COLORSET_ROOT, "", colors.rootFg, colors.rootBg);
490 SLtt_set_color(NEWT_COLORSET_BORDER, "", colors.borderFg, colors.borderBg);
491 SLtt_set_color(NEWT_COLORSET_WINDOW, "", colors.windowFg, colors.windowBg);
492 SLtt_set_color(NEWT_COLORSET_SHADOW, "", colors.shadowFg, colors.shadowBg);
493 SLtt_set_color(NEWT_COLORSET_TITLE, "", colors.titleFg, colors.titleBg);
494 SLtt_set_color(NEWT_COLORSET_BUTTON, "", colors.buttonFg, colors.buttonBg);
495 SLtt_set_color(NEWT_COLORSET_ACTBUTTON, "", colors.actButtonFg,
496 colors.actButtonBg);
497 SLtt_set_color(NEWT_COLORSET_CHECKBOX, "", colors.checkboxFg,
498 colors.checkboxBg);
499 SLtt_set_color(NEWT_COLORSET_ACTCHECKBOX, "", colors.actCheckboxFg,
500 colors.actCheckboxBg);
501 SLtt_set_color(NEWT_COLORSET_ENTRY, "", colors.entryFg, colors.entryBg);
502 SLtt_set_color(NEWT_COLORSET_LABEL, "", colors.labelFg, colors.labelBg);
503 SLtt_set_color(NEWT_COLORSET_LISTBOX, "", colors.listboxFg,
504 colors.listboxBg);
505 SLtt_set_color(NEWT_COLORSET_ACTLISTBOX, "", colors.actListboxFg,
506 colors.actListboxBg);
507 SLtt_set_color(NEWT_COLORSET_TEXTBOX, "", colors.textboxFg,
508 colors.textboxBg);
509 SLtt_set_color(NEWT_COLORSET_ACTTEXTBOX, "", colors.actTextboxFg,
510 colors.actTextboxBg);
511 SLtt_set_color(NEWT_COLORSET_HELPLINE, "", colors.helpLineFg,
512 colors.helpLineBg);
513 SLtt_set_color(NEWT_COLORSET_ROOTTEXT, "", colors.rootTextFg,
514 colors.rootTextBg);
515
516 SLtt_set_color(NEWT_COLORSET_EMPTYSCALE, "", "white",
517 colors.emptyScale);
518 SLtt_set_color(NEWT_COLORSET_FULLSCALE, "", "white",
519 colors.fullScale);
520 SLtt_set_color(NEWT_COLORSET_DISENTRY, "", colors.disabledEntryFg,
521 colors.disabledEntryBg);
522
523 SLtt_set_color(NEWT_COLORSET_COMPACTBUTTON, "", colors.compactButtonFg,
524 colors.compactButtonBg);
525
526 SLtt_set_color(NEWT_COLORSET_ACTSELLISTBOX, "", colors.actSelListboxFg,
527 colors.actSelListboxBg);
528 SLtt_set_color(NEWT_COLORSET_SELLISTBOX, "", colors.selListboxFg,
529 colors.selListboxBg);
530 }
531
newtSetColor(int colorset,char * fg,char * bg)532 void newtSetColor(int colorset, char *fg, char *bg) {
533 if (colorset < NEWT_COLORSET_ROOT ||
534 (colorset > NEWT_COLORSET_SELLISTBOX && colorset < NEWT_COLORSET_CUSTOM(0)) ||
535 !SLtt_Use_Ansi_Colors)
536 return;
537
538 SLtt_set_color(colorset, "", fg, bg);
539 }
540
541 /* Keymap handling - rewritten by Henning Makholm <henning@makholm.net>,
542 * November 2003.
543 */
544
545 struct kmap_trie_entry {
546 char alloced; /* alloced/not first element in array */
547 char c ; /* character got from terminal */
548 int code; /* newt key, or 0 if c does not make a complete sequence */
549 struct kmap_trie_entry *contseq; /* sub-trie for character following c */
550 struct kmap_trie_entry *next; /* try this if char received != c */
551 };
552
553 static struct kmap_trie_entry *kmap_trie_root = NULL;
554 static int keyreader_buf_len = 10 ;
555 static unsigned char default_keyreader_buf[10];
556 static unsigned char *keyreader_buf = default_keyreader_buf;
557
558 #if 0 /* for testing of the keymap manipulation code */
559 static void dumpkeys_recursive(struct kmap_trie_entry *curr, int i, FILE *f) {
560 int j, ps ;
561 char seen[256]={0};
562 if( curr && i >= keyreader_buf_len ) {
563 fprintf(f,"ARGH! Too long sequence!\n") ;
564 return ;
565 }
566 for(;curr;curr=curr->next) {
567 keyreader_buf[i] = curr->c ;
568 ps = seen[(unsigned char)curr->c]++ ;
569 if( ps || curr->code || (!curr->code && !curr->contseq) ) {
570 for(j=0;j<=i;j++) {
571 if( keyreader_buf[j] > 32 && keyreader_buf[j]<127 &&
572 keyreader_buf[j] != '^' && keyreader_buf[j] != '\\' )
573 fprintf(f,"%c",keyreader_buf[j]);
574 else if( keyreader_buf[j] > 0 && keyreader_buf[j]<=32 )
575 fprintf(f,"^%c",keyreader_buf[j] + 0x40);
576 else
577 fprintf(f,"\\%03o",
578 (unsigned)(unsigned char)keyreader_buf[j]);
579 }
580 if( curr->code )
581 fprintf(f,": 0x%X\n",curr->code);
582 else
583 fprintf(f,": (just keymap)\n");
584 }
585 dumpkeys_recursive(curr->contseq,i+1,f);
586 }
587 }
588 static void dump_keymap(void) {
589 FILE *f = fopen("newt.keydump","wt");
590 if (f) {
591 dumpkeys_recursive(kmap_trie_root, 0, f);
592 fclose(f);
593 }
594 }
595 #endif
596
597 /* newtBindKey may overwrite a binding that is there already */
newtBindKey(char * keyseq,int meaning)598 static void newtBindKey(char *keyseq, int meaning) {
599 struct kmap_trie_entry *root = kmap_trie_root ;
600 struct kmap_trie_entry **curptr = &root ;
601
602 /* Try to make sure the common matching buffer is long enough. */
603 if( strlen(keyseq) > keyreader_buf_len ) {
604 int i = strlen(keyseq)+10;
605 unsigned char *newbuf = malloc(i);
606 if (newbuf) {
607 if (keyreader_buf != default_keyreader_buf)
608 free(keyreader_buf);
609 keyreader_buf = newbuf;
610 keyreader_buf_len = i;
611 }
612 }
613
614 if (*keyseq == 0) return; /* binding the empty sequence is meaningless */
615
616 while(1) {
617 while ((*curptr) && (*curptr)->c != *keyseq)
618 curptr = &(*curptr)->next;
619 if ((*curptr)==0) {
620 struct kmap_trie_entry* fresh
621 = calloc(strlen(keyseq),sizeof(struct kmap_trie_entry));
622 if (fresh == 0) return; /* despair! */
623 fresh->alloced = 1;
624 *curptr = fresh;
625 while (keyseq[1]) {
626 fresh->contseq = fresh+1;
627 (fresh++)->c = *(keyseq++);
628 }
629 fresh->c = *keyseq;
630 fresh->code = meaning;
631 return;
632 }
633 if (keyseq[1]==0) {
634 (*curptr)->code = meaning;
635 return;
636 } else {
637 curptr = &(*curptr)->contseq;
638 keyseq++;
639 }
640 }
641 }
642
643 /* This function recursively inserts all entries in the "to" trie into
644 corresponding positions in the "from" trie, except positions that
645 are already defined in the "from" trie. */
kmap_trie_fallback(struct kmap_trie_entry * to,struct kmap_trie_entry ** from)646 static void kmap_trie_fallback(struct kmap_trie_entry *to,
647 struct kmap_trie_entry **from) {
648 if (*from == NULL)
649 *from = to ;
650 if (*from == to)
651 return ;
652 for (;to!=NULL;to=to->next) {
653 struct kmap_trie_entry **fromcopy = from ;
654 while ((*fromcopy) && (*fromcopy)->c != to->c)
655 fromcopy = &(*fromcopy)->next ;
656 if (*fromcopy) {
657 if ((*fromcopy)->code == 0)
658 (*fromcopy)->code = to->code;
659 kmap_trie_fallback(to->contseq, &(*fromcopy)->contseq);
660 } else {
661 *fromcopy = malloc(sizeof(struct kmap_trie_entry));
662 if (*fromcopy) {
663 **fromcopy = *to ;
664 (*fromcopy)->alloced = 1;
665 (*fromcopy)->next = 0 ;
666 }
667 }
668 }
669 }
670
newtGetKey(void)671 int newtGetKey(void) {
672 int key, lastcode, errors = 0;
673 unsigned char *chptr = keyreader_buf, *lastmatch;
674 struct kmap_trie_entry *curr = kmap_trie_root;
675
676 do {
677 key = getkey();
678 if (key == SLANG_GETKEY_ERROR) {
679 if (needResize) {
680 needResize = 0;
681 return NEWT_KEY_RESIZE;
682 }
683
684 /* Ignore other signals, but assume that stdin disappeared (the
685 * parent terminal was proably closed) if the error persists.
686 */
687 if (errors++ > 10)
688 return NEWT_KEY_ERROR;
689
690 continue;
691 }
692
693 if (key == NEWT_KEY_SUSPEND && suspendCallback)
694 suspendCallback(suspendCallbackData);
695 } while (key == NEWT_KEY_SUSPEND || key == SLANG_GETKEY_ERROR);
696
697 /* Read more characters, matching against the trie as we go */
698 lastcode = *chptr = key;
699 lastmatch = chptr ;
700 while(1) {
701 while (curr->c != key) {
702 curr = curr->next ;
703 if (curr==NULL) goto break2levels;
704 }
705 if (curr->code) {
706 lastcode = curr->code;
707 lastmatch = chptr;
708 }
709 curr = curr->contseq;
710 if (curr==NULL) break;
711
712 if (SLang_input_pending(5) <= 0)
713 break;
714
715 if (chptr==keyreader_buf+keyreader_buf_len-1) break;
716 *++chptr = key = getkey();
717 }
718 break2levels:
719
720 /* The last time the trie matched was at position lastmatch. Back
721 * up if we have read too many characters. */
722 while (chptr > lastmatch)
723 SLang_ungetkey(*chptr--);
724
725 return lastcode;
726 }
727
728 /**
729 * @brief Wait for a keystroke
730 */
newtWaitForKey(void)731 void newtWaitForKey(void) {
732 newtRefresh();
733
734 getkey();
735 newtClearKeyBuffer();
736 }
737
738 /**
739 * @brief Clear the keybuffer
740 */
newtClearKeyBuffer(void)741 void newtClearKeyBuffer(void) {
742 while (SLang_input_pending(1)) {
743 getkey();
744 }
745 }
746
747 /**
748 * Open a new window.
749 * @param left. int Size; _not_ including border
750 * @param top: int size, _not_ including border
751 * @param width unsigned int
752 * @param height unsigned int
753 * @param title - title string
754 * @return zero on success
755 */
newtOpenWindow(int left,int top,unsigned int width,unsigned int height,const char * title)756 int newtOpenWindow(int left, int top,
757 unsigned int width, unsigned int height,
758 const char * title) {
759 int j, row, col;
760 int n;
761 int i;
762
763 newtFlushInput();
764
765 if (currentWindow && currentWindow - windowStack + 1
766 >= sizeof (windowStack) / sizeof (struct Window))
767 return 1;
768
769 if (!currentWindow) {
770 currentWindow = windowStack;
771 } else {
772 currentWindow++;
773 }
774
775 currentWindow->left = left;
776 currentWindow->top = top;
777 currentWindow->width = width;
778 currentWindow->height = height;
779 currentWindow->title = title ? strdup(title) : NULL;
780
781 currentWindow->buffer = malloc(sizeof(SLsmg_Char_Type) * (width + 5) * (height + 3));
782
783 row = top - 1;
784 col = left - 2;
785 /* clip to the current screen bounds - msw */
786 if (row < 0)
787 row = 0;
788 if (col < 0)
789 col = 0;
790 if (left + width > SLtt_Screen_Cols)
791 width = SLtt_Screen_Cols - left;
792 if (top + height > SLtt_Screen_Rows)
793 height = SLtt_Screen_Rows - top;
794 n = 0;
795 for (j = 0; j < height + 3; j++, row++) {
796 SLsmg_gotorc(row, col);
797 SLsmg_read_raw(currentWindow->buffer + n,
798 currentWindow->width + 5);
799 n += currentWindow->width + 5;
800 }
801
802 newtTrashScreen();
803
804 SLsmg_set_color(NEWT_COLORSET_BORDER);
805 SLsmg_set_char_set(1);
806 SLsmg_draw_box(top - 1, left - 1, height + 2, width + 2);
807 SLsmg_set_char_set(0);
808
809 if (currentWindow->title) {
810 trim_string (currentWindow->title, width-4);
811 i = wstrlen(currentWindow->title,-1) + 4;
812 i = ((width - i) / 2) + left;
813 SLsmg_gotorc(top - 1, i);
814 SLsmg_set_char_set(1);
815 SLsmg_write_char(SLSMG_RTEE_CHAR);
816 SLsmg_set_char_set(0);
817 SLsmg_write_char(' ');
818 SLsmg_set_color(NEWT_COLORSET_TITLE);
819 SLsmg_write_string((char *)currentWindow->title);
820 SLsmg_set_color(NEWT_COLORSET_BORDER);
821 SLsmg_write_char(' ');
822 SLsmg_set_char_set(1);
823 SLsmg_write_char(SLSMG_LTEE_CHAR);
824 SLsmg_set_char_set(0);
825 }
826
827 SLsmg_set_color(NEWT_COLORSET_WINDOW);
828 SLsmg_fill_region(top, left, height, width, ' ');
829
830 SLsmg_set_color(NEWT_COLORSET_SHADOW);
831 SLsmg_fill_region(top + height + 1, left, 1, width + 2, ' ');
832 SLsmg_fill_region(top, left + width + 1, height + 1, 1, ' ');
833
834 for (i = top; i < (top + height + 1); i++) {
835 SLsmg_gotorc(i, left + width + 1);
836 SLsmg_write_string(" ");
837 }
838
839 return 0;
840 }
841
842 /**
843 * @brief Draw a centered window.
844 * @param width - width in char cells
845 * @param height - no. of char cells.
846 * @param title - fixed title
847 * @returns zero on success
848 */
newtCenteredWindow(unsigned int width,unsigned int height,const char * title)849 int newtCenteredWindow(unsigned int width,unsigned int height,
850 const char * title) {
851 int top, left;
852
853 top = (int)(SLtt_Screen_Rows - height) / 2;
854
855 /* I don't know why, but this seems to look better */
856 if ((SLtt_Screen_Rows % 2) && (top % 2)) top--;
857
858 left = (int)(SLtt_Screen_Cols - width) / 2;
859
860 return newtOpenWindow(left, top, width, height, title);
861 }
862
863 /**
864 * @brief Remove the top window
865 */
newtPopWindow(void)866 void newtPopWindow(void) {
867 newtPopWindowNoRefresh();
868 newtRefresh();
869 }
870
newtPopWindowNoRefresh(void)871 void newtPopWindowNoRefresh(void) {
872 int j, row, col;
873 int n = 0;
874
875 if (currentWindow == NULL)
876 return;
877
878 row = col = 0;
879
880 row = currentWindow->top - 1;
881 col = currentWindow->left - 2;
882 if (row < 0)
883 row = 0;
884 if (col < 0)
885 col = 0;
886 for (j = 0; j < currentWindow->height + 3; j++, row++) {
887 SLsmg_gotorc(row, col);
888 SLsmg_write_raw(currentWindow->buffer + n,
889 currentWindow->width + 5);
890 n += currentWindow->width + 5;
891 }
892
893 free(currentWindow->buffer);
894 free(currentWindow->title);
895
896 if (currentWindow == windowStack)
897 currentWindow = NULL;
898 else
899 currentWindow--;
900
901 SLsmg_set_char_set(0);
902
903 newtTrashScreen();
904 }
905
newtGetWindowPos(int * x,int * y)906 void newtGetWindowPos(int * x, int * y) {
907 if (currentWindow) {
908 *x = currentWindow->left;
909 *y = currentWindow->top;
910 } else
911 *x = *y = 0;
912 }
913
newtGetrc(int * row,int * col)914 void newtGetrc(int * row, int * col) {
915 *row = cursorRow;
916 *col = cursorCol;
917
918 if (currentWindow) {
919 *row -= currentWindow->top;
920 *col -= currentWindow->left;
921 }
922 }
923
newtGotorc(int newRow,int newCol)924 void newtGotorc(int newRow, int newCol) {
925 if (currentWindow) {
926 newRow += currentWindow->top;
927 newCol += currentWindow->left;
928 }
929
930 cursorRow = newRow;
931 cursorCol = newCol;
932 SLsmg_gotorc(cursorRow, cursorCol);
933 }
934
newtDrawBox(int left,int top,int width,int height,int shadow)935 void newtDrawBox(int left, int top, int width, int height, int shadow) {
936 if (currentWindow) {
937 top += currentWindow->top;
938 left += currentWindow->left;
939 }
940
941 SLsmg_draw_box(top, left, height, width);
942
943 if (shadow) {
944 SLsmg_set_color(NEWT_COLORSET_SHADOW);
945 SLsmg_fill_region(top + height, left + 1, 1, width - 1, ' ');
946 SLsmg_fill_region(top + 1, left + width, height, 1, ' ');
947 }
948 }
949
newtClearBox(int left,int top,int width,int height)950 void newtClearBox(int left, int top, int width, int height) {
951 if (currentWindow) {
952 top += currentWindow->top;
953 left += currentWindow->left;
954 }
955
956 SLsmg_fill_region(top, left, height, width, ' ');
957 }
958
initKeymap(void)959 static void initKeymap(void) {
960 const struct keymap * curr;
961 struct kmap_trie_entry *kmap_trie_escBrack, *kmap_trie_escO;
962
963 /* Here are some entries that will help in handling esc O foo and
964 esc [ foo as variants of each other. */
965 kmap_trie_root = calloc(3, sizeof (struct kmap_trie_entry));
966 kmap_trie_escBrack = kmap_trie_root + 1;
967 kmap_trie_escO = kmap_trie_root + 2;
968
969 kmap_trie_root->alloced = 1;
970 kmap_trie_root->c = '\033';
971 kmap_trie_root->contseq = kmap_trie_escBrack;
972
973 kmap_trie_escBrack->c = '[';
974 kmap_trie_escBrack->next = kmap_trie_escO;
975
976 kmap_trie_escO->c = 'O';
977
978 /* First bind built-in default bindings. They may be shadowed by
979 the termcap entries that get bound later. */
980 for (curr = keymap; curr->code; curr++) {
981 if (curr->str)
982 newtBindKey(curr->str,curr->code);
983 }
984
985 /* Then bind strings from termcap entries */
986 for (curr = keymap; curr->code; curr++) {
987 if (curr->tc) {
988 char *pc = SLtt_tgetstr(curr->tc);
989 if (pc) {
990 newtBindKey(pc,curr->code);
991 }
992 }
993 }
994
995 /* Finally, invent lowest-priority keybindings that correspond to
996 searching for esc-O-foo if esc-[-foo was not found and vice
997 versa. That is needed because of strong confusion among
998 different emulators of VTxxx terminals; some terminfo/termcap
999 descriptions are apparently written by people who were not
1000 aware of the differences between "applicataion" and "terminal"
1001 keypad modes. Or perhaps they were, but tried to make their
1002 description work with a program that puts the keyboard in the
1003 wrong emulation mode. In short, one needs this: */
1004 kmap_trie_fallback(kmap_trie_escO->contseq, &kmap_trie_escBrack->contseq);
1005 kmap_trie_fallback(kmap_trie_escBrack->contseq, &kmap_trie_escO->contseq);
1006 }
1007
free_keys(struct kmap_trie_entry * kmap,struct kmap_trie_entry * parent,int prepare)1008 static void free_keys(struct kmap_trie_entry *kmap, struct kmap_trie_entry *parent, int prepare) {
1009 if (kmap == NULL)
1010 return;
1011
1012 free_keys(kmap->contseq, kmap, prepare);
1013 free_keys(kmap->next, kmap, prepare);
1014
1015 if (!kmap->alloced && kmap - parent == 1)
1016 return;
1017
1018 /* find first element in array */
1019 while (!kmap->alloced)
1020 kmap--;
1021
1022 kmap->alloced += prepare ? 1 : -1;
1023 if (!prepare && kmap->alloced == 1)
1024 free(kmap);
1025 }
1026
freeKeymap()1027 static void freeKeymap() {
1028 free_keys(kmap_trie_root, NULL, 1);
1029 free_keys(kmap_trie_root, NULL, 0);
1030 kmap_trie_root = NULL;
1031 }
1032
1033 /**
1034 * @brief Delay for a specified number of usecs
1035 * @param int - number of usecs to wait for.
1036 */
newtDelay(unsigned int usecs)1037 void newtDelay(unsigned int usecs) {
1038 usleep(usecs);
1039 }
1040
newtDefaultEventHandler(newtComponent c,struct event ev)1041 struct eventResult newtDefaultEventHandler(newtComponent c,
1042 struct event ev) {
1043 struct eventResult er;
1044
1045 er.result = ER_IGNORED;
1046 return er;
1047 }
1048
newtRedrawHelpLine(void)1049 void newtRedrawHelpLine(void) {
1050 char * buf;
1051
1052 SLsmg_set_color(NEWT_COLORSET_HELPLINE);
1053
1054 if (currentHelpline) {
1055 /* buffer size needs to be wide enough to hold all the multibyte
1056 currentHelpline + all the single byte ' ' to fill the line */
1057 int wlen = wstrlen(*currentHelpline, -1);
1058 int len;
1059
1060 if (wlen > SLtt_Screen_Cols)
1061 wlen = SLtt_Screen_Cols;
1062 len = strlen(*currentHelpline) + (SLtt_Screen_Cols - wlen);
1063 buf = alloca(len + 1);
1064 memset(buf, ' ', len);
1065 memcpy(buf, *currentHelpline, strlen(*currentHelpline));
1066 buf[len] = '\0';
1067 } else {
1068 buf = alloca(SLtt_Screen_Cols + 1);
1069 memset(buf, ' ', SLtt_Screen_Cols);
1070 buf[SLtt_Screen_Cols] = '\0';
1071 }
1072 SLsmg_gotorc(SLtt_Screen_Rows - 1, 0);
1073 SLsmg_write_string(buf);
1074 SLsmg_gotorc(cursorRow, cursorCol);
1075 }
1076
newtPushHelpLine(const char * text)1077 void newtPushHelpLine(const char * text) {
1078 if (currentHelpline && currentHelpline - helplineStack + 1
1079 >= sizeof (helplineStack) / sizeof (char *))
1080 return;
1081
1082 if (!text)
1083 text = defaultHelpLine;
1084
1085 if (currentHelpline)
1086 (*(++currentHelpline)) = strdup(text);
1087 else {
1088 currentHelpline = helplineStack;
1089 *currentHelpline = strdup(text);
1090 }
1091
1092 newtRedrawHelpLine();
1093 }
1094
newtPopHelpLine(void)1095 void newtPopHelpLine(void) {
1096 if (!currentHelpline) return;
1097
1098 free(*currentHelpline);
1099 if (currentHelpline == helplineStack)
1100 currentHelpline = NULL;
1101 else
1102 currentHelpline--;
1103
1104 newtRedrawHelpLine();
1105 }
1106
newtDrawRootText(int col,int row,const char * text)1107 void newtDrawRootText(int col, int row, const char * text) {
1108 SLsmg_set_color(NEWT_COLORSET_ROOTTEXT);
1109
1110 if (col < 0) {
1111 col = SLtt_Screen_Cols + col;
1112 }
1113
1114 if (row < 0) {
1115 row = SLtt_Screen_Rows + row;
1116 }
1117
1118 SLsmg_gotorc(row, col);
1119 SLsmg_write_string((char *)text);
1120 }
1121
newtSetFlags(int oldFlags,int newFlags,enum newtFlagsSense sense)1122 int newtSetFlags(int oldFlags, int newFlags, enum newtFlagsSense sense) {
1123 switch (sense) {
1124 case NEWT_FLAGS_SET:
1125 return oldFlags | newFlags;
1126
1127 case NEWT_FLAGS_RESET:
1128 return oldFlags & (~newFlags);
1129
1130 case NEWT_FLAGS_TOGGLE:
1131 return oldFlags ^ newFlags;
1132
1133 default:
1134 return oldFlags;
1135 }
1136 }
1137
newtBell(void)1138 void newtBell(void)
1139 {
1140 SLtt_beep();
1141 }
1142
newtGetScreenSize(int * cols,int * rows)1143 void newtGetScreenSize(int * cols, int * rows) {
1144 if (rows) *rows = SLtt_Screen_Rows;
1145 if (cols) *cols = SLtt_Screen_Cols;
1146 }
1147
newtDefaultPlaceHandler(newtComponent c,int newLeft,int newTop)1148 void newtDefaultPlaceHandler(newtComponent c, int newLeft, int newTop) {
1149 c->left = newLeft;
1150 c->top = newTop;
1151 }
1152
newtDefaultMappedHandler(newtComponent c,int isMapped)1153 void newtDefaultMappedHandler(newtComponent c, int isMapped) {
1154 c->isMapped = isMapped;
1155 }
1156
newtCursorOff(void)1157 void newtCursorOff(void) {
1158 cursorOn = 0;
1159 SLtt_set_cursor_visibility (cursorOn);
1160 }
1161
newtCursorOn(void)1162 void newtCursorOn(void) {
1163 cursorOn = 1;
1164 SLtt_set_cursor_visibility (cursorOn);
1165 }
1166
newtTrashScreen(void)1167 void newtTrashScreen(void) {
1168 if (trashScreen)
1169 SLsmg_touch_lines(0, SLtt_Screen_Rows);
1170 }
1171
newtComponentGetPosition(newtComponent co,int * left,int * top)1172 void newtComponentGetPosition(newtComponent co, int * left, int * top) {
1173 if (left) *left = co->left;
1174 if (top) *top = co->top;
1175 }
1176
newtComponentGetSize(newtComponent co,int * width,int * height)1177 void newtComponentGetSize(newtComponent co, int * width, int * height) {
1178 if (width) *width = co->width;
1179 if (height) *height = co->height;
1180 }
1181