]> git.ipfire.org Git - thirdparty/newt.git/blob - newt.c
Restore cursor position after changing help line
[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 <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 trashScreen = 0;
42 extern int needResize;
43
44 static const char * const defaultHelpLine =
45 " <Tab>/<Alt-Tab> between elements | <Space> selects | <F12> next screen"
46 ;
47
48 const struct newtColors newtDefaultColorPalette = {
49 "white", "blue", /* root fg, bg */
50 "black", "lightgray", /* border fg, bg */
51 "black", "lightgray", /* window fg, bg */
52 "white", "black", /* shadow fg, bg */
53 "red", "lightgray", /* title fg, bg */
54 "lightgray", "red", /* button fg, bg */
55 "red", "lightgray", /* active button fg, bg */
56 "lightgray", "blue", /* checkbox fg, bg */
57 "lightgray", "red", /* active checkbox fg, bg */
58 "lightgray", "blue", /* entry box fg, bg */
59 "blue", "lightgray", /* label fg, bg */
60 "black", "lightgray", /* listbox fg, bg */
61 "lightgray", "blue", /* active listbox fg, bg */
62 "black", "lightgray", /* textbox fg, bg */
63 "lightgray", "red", /* active textbox fg, bg */
64 "white", "blue", /* help line */
65 "lightgray", "blue", /* root text */
66 "blue", /* scale full */
67 "red", /* scale empty */
68 "blue", "lightgray", /* disabled entry fg, bg */
69 "black", "lightgray", /* compact button fg, bg */
70 "lightgray", "red", /* active & sel listbox */
71 "black", "brown" /* selected listbox */
72 };
73
74 static const struct keymap keymap[] = {
75 { "\033OA", NEWT_KEY_UP, "ku" },
76 { "\020", NEWT_KEY_UP, NULL }, /* emacs ^P */
77 { "\033OB", NEWT_KEY_DOWN, "kd" },
78 { "\016", NEWT_KEY_DOWN, NULL }, /* emacs ^N */
79 { "\033OC", NEWT_KEY_RIGHT, "kr" },
80 { "\006", NEWT_KEY_RIGHT, NULL }, /* emacs ^F */
81 { "\033OD", NEWT_KEY_LEFT, "kl" },
82 { "\002", NEWT_KEY_LEFT, NULL }, /* emacs ^B */
83 { "\033OH", NEWT_KEY_HOME, "kh" },
84 { "\033[1~", NEWT_KEY_HOME, NULL },
85 { "\001", NEWT_KEY_HOME, NULL }, /* emacs ^A */
86 { "\033Ow", NEWT_KEY_END, "kH" },
87 { "\033[4~", NEWT_KEY_END, "@7" },
88 { "\005", NEWT_KEY_END, NULL }, /* emacs ^E */
89
90 { "\033[3~", NEWT_KEY_DELETE, "kD" },
91 { "\004", NEWT_KEY_DELETE, NULL }, /* emacs ^D */
92 { "\033[2~", NEWT_KEY_INSERT, "kI" },
93
94 { "\033\t", NEWT_KEY_UNTAB, "kB" },
95 { "\033[Z", NEWT_KEY_UNTAB, NULL },
96
97 { "\033[5~", NEWT_KEY_PGUP, "kP" },
98 { "\033[6~", NEWT_KEY_PGDN, "kN" },
99 { "\033V", NEWT_KEY_PGUP, NULL },
100 { "\033v", NEWT_KEY_PGUP, NULL },
101 { "\026", NEWT_KEY_PGDN, NULL },
102
103 { "\033[[A", NEWT_KEY_F1, NULL },
104 { "\033[[B", NEWT_KEY_F2, NULL },
105 { "\033[[C", NEWT_KEY_F3, NULL },
106 { "\033[[D", NEWT_KEY_F4, NULL },
107 { "\033[[E", NEWT_KEY_F5, NULL },
108
109 { "\033OP", NEWT_KEY_F1, NULL },
110 { "\033OQ", NEWT_KEY_F2, NULL },
111 { "\033OR", NEWT_KEY_F3, NULL },
112 { "\033OS", NEWT_KEY_F4, NULL },
113
114 { "\033[11~", NEWT_KEY_F1, "k1" },
115 { "\033[12~", NEWT_KEY_F2, "k2" },
116 { "\033[13~", NEWT_KEY_F3, "k3" },
117 { "\033[14~", NEWT_KEY_F4, "k4" },
118 { "\033[15~", NEWT_KEY_F5, "k5" },
119 { "\033[17~", NEWT_KEY_F6, "k6" },
120 { "\033[18~", NEWT_KEY_F7, "k7" },
121 { "\033[19~", NEWT_KEY_F8, "k8" },
122 { "\033[20~", NEWT_KEY_F9, "k9" },
123 { "\033[21~", NEWT_KEY_F10, "k;" },
124 { "\033[23~", NEWT_KEY_F11, "F1" },
125 { "\033[24~", NEWT_KEY_F12, "F2" },
126 { "\033", NEWT_KEY_ESCAPE, "@2" },
127 { "\033", NEWT_KEY_ESCAPE, "@9" },
128
129 { "\177", NEWT_KEY_BKSPC, NULL },
130 { "\010", NEWT_KEY_BKSPC, NULL },
131
132 { 0 }, /* LEAVE this one */
133 };
134 static void initKeymap();
135 static void freeKeymap();
136
137 static const char ident[] = // ident friendly
138 "$Version: Newt windowing library v" VERSION " $"
139 "$Copyright: (C) 1996-2003 Red Hat, Inc. Written by Erik Troan $"
140 "$License: Lesser GNU Public License. $";
141
142 static newtSuspendCallback suspendCallback = NULL;
143 static void * suspendCallbackData = NULL;
144
145 void newtSetSuspendCallback(newtSuspendCallback cb, void * data) {
146 suspendCallback = cb;
147 suspendCallbackData = data;
148 }
149
150 static void handleSigwinch(int signum) {
151 needResize = 1;
152 }
153
154 static int getkeyInterruptHook(void) {
155 return -1;
156 }
157
158 int _newt_wstrlen(const char *str, int len) {
159 mbstate_t ps;
160 wchar_t tmp;
161 int nchars = 0;
162
163 if (!str) return 0;
164 if (!len) return 0;
165 if (len < 0) len = strlen(str);
166 memset(&ps,0,sizeof(mbstate_t));
167 while (len > 0) {
168 int x,y;
169
170 x = mbrtowc(&tmp,str,len,&ps);
171 if (x >0) {
172 str += x;
173 len -= x;
174 y = wcwidth(tmp);
175 if (y>0)
176 nchars+=y;
177 } else break;
178 }
179 return nchars;
180 }
181
182 /** Trim a string to fit
183 * @param title - string. NULL will be inserted if necessary
184 * @param chrs - available space. (character cells)
185 */
186 void trim_string(char *title, int chrs)
187 {
188 char *p = title;
189 int ln;
190 int x = 0,y = 0;
191 wchar_t tmp;
192 mbstate_t ps;
193
194 memset(&ps, 0, sizeof(ps));
195 ln = strlen(title);
196
197 while (*p) {
198 x = mbrtowc(&tmp, p, ln, &ps);
199 if (x < 0) { // error
200 *p = '\0';
201 return;
202 }
203 y = wcwidth(tmp);
204 if (y > chrs) {
205 *p = '\0';
206 return;
207 } else {
208 p += x;
209 ln -= x;
210 chrs -= y;
211 }
212 }
213 }
214
215 static int getkey() {
216 int c;
217
218 while ((c = SLang_getkey()) == '\xC') { /* if Ctrl-L redraw whole screen */
219 SLsmg_touch_lines(0, SLtt_Screen_Rows);
220 SLsmg_refresh();
221 }
222 return c;
223
224 }
225
226 static void updateColorset(char *fg, char *bg, char **fg_p, char **bg_p)
227 {
228 if (*fg && fg_p)
229 *fg_p = fg;
230 if (*bg && bg_p)
231 *bg_p = bg;
232 }
233
234 /* parse color specifications (e.g. root=,black:border=red,blue)
235 * and update the palette
236 */
237 static void parseColors(char *s, struct newtColors *palette)
238 {
239 char *name, *str, *fg, *bg;
240
241 for (str = s; (s = strtok(str, ";:\n\r\t ")); str = NULL) {
242 name = s;
243 if (!(s = strchr(s, '=')) || !*s)
244 continue;
245 *s = '\0';
246 fg = ++s;
247 if (!(s = strchr(s, ',')) || !*s)
248 continue;
249 *s = '\0';
250 bg = ++s;
251
252 if (!strcmp(name, "root"))
253 updateColorset(fg, bg, &palette->rootFg, &palette->rootBg);
254 else if (!strcmp(name, "border"))
255 updateColorset(fg, bg, &palette->borderFg, &palette->borderBg);
256 else if (!strcmp(name, "window"))
257 updateColorset(fg, bg, &palette->windowFg, &palette->windowBg);
258 else if (!strcmp(name, "shadow"))
259 updateColorset(fg, bg, &palette->shadowFg, &palette->shadowBg);
260 else if (!strcmp(name, "title"))
261 updateColorset(fg, bg, &palette->titleFg, &palette->titleBg);
262 else if (!strcmp(name, "button"))
263 updateColorset(fg, bg, &palette->buttonFg, &palette->buttonBg);
264 else if (!strcmp(name, "actbutton"))
265 updateColorset(fg, bg, &palette->actButtonFg, &palette->actButtonBg);
266 else if (!strcmp(name, "checkbox"))
267 updateColorset(fg, bg, &palette->checkboxFg, &palette->checkboxBg);
268 else if (!strcmp(name, "actcheckbox"))
269 updateColorset(fg, bg, &palette->actCheckboxFg, &palette->actCheckboxBg);
270 else if (!strcmp(name, "entry"))
271 updateColorset(fg, bg, &palette->entryFg, &palette->entryBg);
272 else if (!strcmp(name, "label"))
273 updateColorset(fg, bg, &palette->labelFg, &palette->labelBg);
274 else if (!strcmp(name, "listbox"))
275 updateColorset(fg, bg, &palette->listboxFg, &palette->listboxBg);
276 else if (!strcmp(name, "actlistbox"))
277 updateColorset(fg, bg, &palette->actListboxFg, &palette->actListboxBg);
278 else if (!strcmp(name, "textbox"))
279 updateColorset(fg, bg, &palette->textboxFg, &palette->textboxBg);
280 else if (!strcmp(name, "acttextbox"))
281 updateColorset(fg, bg, &palette->actTextboxFg, &palette->actTextboxBg);
282 else if (!strcmp(name, "helpline"))
283 updateColorset(fg, bg, &palette->helpLineFg, &palette->helpLineBg);
284 else if (!strcmp(name, "roottext"))
285 updateColorset(fg, bg, &palette->rootTextFg, &palette->rootTextBg);
286 else if (!strcmp(name, "emptyscale"))
287 updateColorset(fg, bg, NULL, &palette->emptyScale);
288 else if (!strcmp(name, "fullscale"))
289 updateColorset(fg, bg, NULL, &palette->fullScale);
290 else if (!strcmp(name, "disentry"))
291 updateColorset(fg, bg, &palette->disabledEntryFg, &palette->disabledEntryBg);
292 else if (!strcmp(name, "compactbutton"))
293 updateColorset(fg, bg, &palette->compactButtonFg, &palette->compactButtonBg);
294 else if (!strcmp(name, "actsellistbox"))
295 updateColorset(fg, bg, &palette->actSelListboxFg, &palette->actSelListboxBg);
296 else if (!strcmp(name, "sellistbox"))
297 updateColorset(fg, bg, &palette->selListboxFg, &palette->selListboxBg);
298 }
299 }
300
301 static void initColors(void)
302 {
303 char *colors, *colors_file, buf[16384];
304 FILE *f;
305 struct newtColors palette;
306
307 palette = newtDefaultColorPalette;
308
309 colors_file = getenv("NEWT_COLORS_FILE");
310 #ifdef NEWT_COLORS_FILE
311 if (colors_file == NULL)
312 colors_file = NEWT_COLORS_FILE;
313 #endif
314
315 if ((colors = getenv("NEWT_COLORS"))) {
316 strncpy(buf, colors, sizeof (buf));
317 buf[sizeof (buf) - 1] = '\0';
318 parseColors(buf, &palette);
319 } else if (colors_file && *colors_file && (f = fopen(colors_file, "r"))) {
320 size_t r;
321 if ((r = fread(buf, 1, sizeof (buf) - 1, f)) > 0) {
322 buf[r] = '\0';
323 parseColors(buf, &palette);
324 }
325 fclose(f);
326 }
327
328 newtSetColors(palette);
329 }
330
331 void newtFlushInput(void) {
332 while (SLang_input_pending(0)) {
333 getkey();
334 }
335 }
336
337 /**
338 * @brief Refresh the screen
339 */
340 void newtRefresh(void) {
341 SLsmg_refresh();
342 }
343
344 void newtSuspend(void) {
345 SLtt_set_cursor_visibility (1);
346 SLsmg_suspend_smg();
347 SLang_reset_tty();
348 SLtt_set_cursor_visibility (cursorOn);
349 }
350
351 /**
352 * @brief Return after suspension.
353 * @return 0 on success.
354 */
355 int newtResume(void) {
356 SLsmg_resume_smg ();
357 SLsmg_refresh();
358 return SLang_init_tty(0, 0, 0);
359 }
360
361 void newtCls(void) {
362 SLsmg_set_color(NEWT_COLORSET_ROOT);
363 SLsmg_gotorc(0, 0);
364 SLsmg_erase_eos();
365
366 newtRefresh();
367 }
368
369 /**
370 * @brief Resize the screen
371 * @param redraw - boolean - should we redraw the screen?
372 */
373 void newtResizeScreen(int redraw) {
374 /* we can't redraw from scratch, just redisplay SLang screen */
375 SLtt_get_screen_size();
376 /* SLsmg_reinit_smg(); */
377 if (redraw) {
378 SLsmg_touch_lines(0, SLtt_Screen_Rows);
379 newtRefresh();
380 }
381 }
382
383 /**
384 * @brief Initialize the newt library
385 * @return int - 0 for success, else < 0
386 */
387 int newtInit(void) {
388 char * MonoValue, * MonoEnv = "NEWT_MONO";
389 const char *lang;
390 int ret;
391
392 if ((lang = getenv("LC_ALL")) == NULL)
393 if ((lang = getenv("LC_CTYPE")) == NULL)
394 if ((lang = getenv("LANG")) == NULL)
395 lang = "";
396 /* slang doesn't support multibyte encodings except UTF-8,
397 avoid character corruption by redrawing the screen */
398 if (strstr (lang, ".euc") != NULL)
399 trashScreen = 1;
400
401 (void) strlen(ident);
402
403 SLutf8_enable(-1);
404 SLtt_get_terminfo();
405 SLtt_get_screen_size();
406
407 MonoValue = getenv(MonoEnv);
408 if ( MonoValue != NULL )
409 SLtt_Use_Ansi_Colors = 0;
410
411 if ((ret = SLsmg_init_smg()) < 0)
412 return ret;
413 if ((ret = SLang_init_tty(0, 0, 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 = col = 0;
871
872 row = currentWindow->top - 1;
873 col = currentWindow->left - 2;
874 if (row < 0)
875 row = 0;
876 if (col < 0)
877 col = 0;
878 for (j = 0; j < currentWindow->height + 3; j++, row++) {
879 SLsmg_gotorc(row, col);
880 SLsmg_write_raw(currentWindow->buffer + n,
881 currentWindow->width + 5);
882 n += currentWindow->width + 5;
883 }
884
885 free(currentWindow->buffer);
886 free(currentWindow->title);
887
888 if (currentWindow == windowStack)
889 currentWindow = NULL;
890 else
891 currentWindow--;
892
893 SLsmg_set_char_set(0);
894
895 newtTrashScreen();
896 }
897
898 void newtGetWindowPos(int * x, int * y) {
899 if (currentWindow) {
900 *x = currentWindow->left;
901 *y = currentWindow->top;
902 } else
903 *x = *y = 0;
904 }
905
906 void newtGetrc(int * row, int * col) {
907 *row = cursorRow;
908 *col = cursorCol;
909
910 if (currentWindow) {
911 *row -= currentWindow->top;
912 *col -= currentWindow->left;
913 }
914 }
915
916 void newtGotorc(int newRow, int newCol) {
917 if (currentWindow) {
918 newRow += currentWindow->top;
919 newCol += currentWindow->left;
920 }
921
922 cursorRow = newRow;
923 cursorCol = newCol;
924 SLsmg_gotorc(cursorRow, cursorCol);
925 }
926
927 void newtDrawBox(int left, int top, int width, int height, int shadow) {
928 if (currentWindow) {
929 top += currentWindow->top;
930 left += currentWindow->left;
931 }
932
933 SLsmg_draw_box(top, left, height, width);
934
935 if (shadow) {
936 SLsmg_set_color(NEWT_COLORSET_SHADOW);
937 SLsmg_fill_region(top + height, left + 1, 1, width - 1, ' ');
938 SLsmg_fill_region(top + 1, left + width, height, 1, ' ');
939 }
940 }
941
942 void newtClearBox(int left, int top, int width, int height) {
943 if (currentWindow) {
944 top += currentWindow->top;
945 left += currentWindow->left;
946 }
947
948 SLsmg_fill_region(top, left, height, width, ' ');
949 }
950
951 static void initKeymap(void) {
952 const struct keymap * curr;
953 struct kmap_trie_entry *kmap_trie_escBrack, *kmap_trie_escO;
954
955 /* Here are some entries that will help in handling esc O foo and
956 esc [ foo as variants of each other. */
957 kmap_trie_root = calloc(3, sizeof (struct kmap_trie_entry));
958 kmap_trie_escBrack = kmap_trie_root + 1;
959 kmap_trie_escO = kmap_trie_root + 2;
960
961 kmap_trie_root->alloced = 1;
962 kmap_trie_root->c = '\033';
963 kmap_trie_root->contseq = kmap_trie_escBrack;
964
965 kmap_trie_escBrack->c = '[';
966 kmap_trie_escBrack->next = kmap_trie_escO;
967
968 kmap_trie_escO->c = 'O';
969
970 /* First bind built-in default bindings. They may be shadowed by
971 the termcap entries that get bound later. */
972 for (curr = keymap; curr->code; curr++) {
973 if (curr->str)
974 newtBindKey(curr->str,curr->code);
975 }
976
977 /* Then bind strings from termcap entries */
978 for (curr = keymap; curr->code; curr++) {
979 if (curr->tc) {
980 char *pc = SLtt_tgetstr(curr->tc);
981 if (pc) {
982 newtBindKey(pc,curr->code);
983 }
984 }
985 }
986
987 /* Finally, invent lowest-priority keybindings that correspond to
988 searching for esc-O-foo if esc-[-foo was not found and vice
989 versa. That is needed because of strong confusion among
990 different emulators of VTxxx terminals; some terminfo/termcap
991 descriptions are apparently written by people who were not
992 aware of the differences between "applicataion" and "terminal"
993 keypad modes. Or perhaps they were, but tried to make their
994 description work with a program that puts the keyboard in the
995 wrong emulation mode. In short, one needs this: */
996 kmap_trie_fallback(kmap_trie_escO->contseq, &kmap_trie_escBrack->contseq);
997 kmap_trie_fallback(kmap_trie_escBrack->contseq, &kmap_trie_escO->contseq);
998 }
999
1000 static void free_keys(struct kmap_trie_entry *kmap, struct kmap_trie_entry *parent, int prepare) {
1001 if (kmap == NULL)
1002 return;
1003
1004 free_keys(kmap->contseq, kmap, prepare);
1005 free_keys(kmap->next, kmap, prepare);
1006
1007 if (!kmap->alloced && kmap - parent == 1)
1008 return;
1009
1010 /* find first element in array */
1011 while (!kmap->alloced)
1012 kmap--;
1013
1014 kmap->alloced += prepare ? 1 : -1;
1015 if (!prepare && kmap->alloced == 1)
1016 free(kmap);
1017 }
1018
1019 static void freeKeymap() {
1020 free_keys(kmap_trie_root, NULL, 1);
1021 free_keys(kmap_trie_root, NULL, 0);
1022 kmap_trie_root = NULL;
1023 }
1024
1025 /**
1026 * @brief Delay for a specified number of usecs
1027 * @param int - number of usecs to wait for.
1028 */
1029 void newtDelay(unsigned int usecs) {
1030 usleep(usecs);
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 }