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