]> git.ipfire.org Git - thirdparty/newt.git/blob - newt.c
sync with 0.52.0
[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 #if defined(HAVE_FRIBIDI_FRIBIDI_H) && defined(HAVE_DLFCN_H)
22 #include <fribidi/fribidi.h>
23 #include <dlfcn.h>
24
25 /* No sense in enabling shaping if we don't have BIDI support. */
26 typedef struct
27 {
28 int is_shaped;
29 wchar_t isolated;
30 wchar_t initial;
31 wchar_t medial;
32 wchar_t final;
33 }
34 arabic_char_node;
35
36 #define ARABIC_BASE 0x621
37 #define ARABIC_END 0x64A
38
39 static const arabic_char_node arabic_shaping_table[] = {
40 /* 0x621 */ { TRUE , 0xFE80, 0x0000, 0x0000, 0x0000},
41 /* 0x622 */ { TRUE , 0xFE81, 0x0000, 0x0000, 0xFE82},
42 /* 0x623 */ { TRUE , 0xFE83, 0x0000, 0x0000, 0xFE84},
43 /* 0x624 */ { TRUE , 0xFE85, 0x0000, 0x0000, 0xFE86},
44 /* 0x625 */ { TRUE , 0xFE87, 0x0000, 0x0000, 0xFE88},
45 /* 0x626 */ { TRUE , 0xFE89, 0xFE8B, 0xFE8C, 0xFE8A},
46 /* 0x627 */ { TRUE , 0xFE8D, 0x0000, 0x0000, 0xFE8E},
47 /* 0x628 */ { TRUE , 0xFE8F, 0xFE91, 0xFE92, 0xFE90},
48 /* 0x629 */ { TRUE , 0xFE93, 0x0000, 0x0000, 0xFE94},
49 /* 0x62A */ { TRUE , 0xFE95, 0xFE97, 0xFE98, 0xFE96},
50 /* 0x62B */ { TRUE , 0xFE99, 0xFE9B, 0xFE9C, 0xFE9A},
51 /* 0x62C */ { TRUE , 0xFE9D, 0xFE9F, 0xFEA0, 0xFE9E},
52 /* 0x62D */ { TRUE , 0xFEA1, 0xFEA3, 0xFEA4, 0xFEA2},
53 /* 0x62E */ { TRUE , 0xFEA5, 0xFEA7, 0xFEA8, 0xFEA6},
54 /* 0x62F */ { TRUE , 0xFEA9, 0x0000, 0x0000, 0xFEAA},
55 /* 0x630 */ { TRUE , 0xFEAB, 0x0000, 0x0000, 0xFEAC},
56 /* 0x631 */ { TRUE , 0xFEAD, 0x0000, 0x0000, 0xFEAE},
57 /* 0x632 */ { TRUE , 0xFEAF, 0x0000, 0x0000, 0xFEB0},
58 /* 0x633 */ { TRUE , 0xFEB1, 0xFEB3, 0xFEB4, 0xFEB2},
59 /* 0x634 */ { TRUE , 0xFEB5, 0xFEB7, 0xFEB8, 0xFEB6},
60 /* 0x635 */ { TRUE , 0xFEB9, 0xFEBB, 0xFEBC, 0xFEBA},
61 /* 0x636 */ { TRUE , 0xFEBD, 0xFEBF, 0xFEC0, 0xFEBE},
62 /* 0x637 */ { TRUE , 0xFEC1, 0xFEC3, 0xFEC4, 0xFEC2},
63 /* 0x638 */ { TRUE , 0xFEC5, 0xFEC7, 0xFEC8, 0xFEC6},
64 /* 0x639 */ { TRUE , 0xFEC9, 0xFECB, 0xFECC, 0xFECA},
65 /* 0x63A */ { TRUE , 0xFECD, 0xFECF, 0xFED0, 0xFECE},
66 /* 0x63B */ { FALSE, 0x0000, 0x0000, 0x0000, 0x0000},
67 /* 0x63C */ { FALSE, 0x0000, 0x0000, 0x0000, 0x0000},
68 /* 0x63D */ { FALSE, 0x0000, 0x0000, 0x0000, 0x0000},
69 /* 0x63E */ { FALSE, 0x0000, 0x0000, 0x0000, 0x0000},
70 /* 0x63F */ { FALSE, 0x0000, 0x0000, 0x0000, 0x0000},
71 /* 0x640 */ { TRUE , 0x0640, 0x0640, 0x0640, 0x0640},
72 /* 0x641 */ { TRUE , 0xFED1, 0xFED3, 0xFED4, 0xFED2},
73 /* 0x642 */ { TRUE , 0xFED5, 0xFED7, 0xFED8, 0xFED6},
74 /* 0x643 */ { TRUE , 0xFED9, 0xFEDB, 0xFEDC, 0xFEDA},
75 /* 0x644 */ { TRUE , 0xFEDD, 0xFEDF, 0xFEE0, 0xFEDE},
76 /* 0x645 */ { TRUE , 0xFEE1, 0xFEE3, 0xFEE4, 0xFEE2},
77 /* 0x646 */ { TRUE , 0xFEE5, 0xFEE7, 0xFEE8, 0xFEE6},
78 /* 0x647 */ { TRUE , 0xFEE9, 0xFEEB, 0xFEEC, 0xFEEA},
79 /* 0x648 */ { TRUE , 0xFEED, 0x0000, 0x0000, 0xFEEE},
80 /* 0x649 */ { TRUE , 0xFEEF, 0x0000, 0x0000, 0xFEF0},
81 /* 0x64A */ { TRUE , 0xFEF1, 0xFEF3, 0xFEF4, 0xFEF2}
82 };
83
84 typedef struct {
85 wchar_t c;
86 arabic_char_node ar_node;
87 }
88 extra_char_node;
89
90 #define EXTRA_BASE 0x67E
91 #define EXTRA_END 0x6CC
92 static const extra_char_node extra_shaping_table[] = {
93 {0x067E, {TRUE, 0xFB56, 0xFB58, 0xFB59, 0xFB57}},
94 {0x0686, {TRUE, 0xFB7A, 0xFB7C, 0xFB7D, 0xFB7B}},
95 {0x0698, {TRUE, 0xFB8A, 0x0000, 0x0000, 0xFB8B}},
96 {0x06A9, {TRUE, 0xFB8E, 0xFB90, 0xFB91, 0xFB8F}},
97 {0x06AF, {TRUE, 0xFB92, 0xFB94, 0xFB95, 0xFB93}},
98 {0x06CC, {TRUE, 0xFBFC, 0xFBFE, 0xFBFF, 0xFBFD}},
99 {0x0000, {FALSE, 0x0000, 0x0000, 0x0000, 0x0000}},
100 };
101
102 static const arabic_char_node *get_char_node(wchar_t w)
103 {
104 if (w >= ARABIC_BASE && w <= ARABIC_END)
105 return &arabic_shaping_table[w - ARABIC_BASE];
106 else if (w >= EXTRA_BASE && w <= EXTRA_END) {
107 const extra_char_node *node = extra_shaping_table;
108
109 while (node->c) {
110 if (node->c == w)
111 return &node->ar_node;
112 node++;
113 }
114 return NULL;
115 }
116 return NULL;
117 }
118
119 static int do_shaping(wchar_t *buf, int len) {
120 int i,j;
121
122 wchar_t *newbuf;
123
124 if (len < 1)
125 return 0;
126
127 newbuf = (wchar_t *)malloc(sizeof(wchar_t)*len);
128
129 for (i = 0, j = 0; i < len; i++, j++) {
130 int have_previous = FALSE, have_next = FALSE;
131 const arabic_char_node *node, *node1;
132 int prev, next;
133
134 if (buf[i] == L'\0')
135 break;
136
137 /* If it is non-joiner, ignore it */
138 if (buf[i] == 0x200C) {
139 j--;
140 continue;
141 }
142
143 newbuf[j] = buf[i];
144
145 /* If it's not in our range, skip it. */
146 node = get_char_node(buf[i]);
147 if (!node)
148 {
149 continue;
150 }
151
152 /* The character wasn't included in the unicode shaping table. */
153 if (!node->is_shaped)
154 {
155 continue;
156 }
157
158 for (prev = i - 1; prev >= 0; prev--)
159 if (wcwidth(buf[prev]) || buf[prev] == 0x200C)
160 break;
161
162 if (prev >= 0 && (node1 = get_char_node(buf[prev]))
163 && ( node1->initial || node1->medial))
164 {
165 have_previous = TRUE;
166 }
167
168 for (next = i + 1; next < len; next++)
169 if (wcwidth(buf[next]) || buf[next] == 0x200C)
170 break;
171
172 if (next < len && (node1 = get_char_node(buf[next]))
173 && (node1->medial || node1->final))
174 {
175 have_next = TRUE;
176 }
177
178 /*
179 * FIXME: do not make ligature if there are combining
180 * characters between two parts.
181 */
182 if (buf[i] == 0x644 && have_next && next == i + 1)
183 {
184 switch (buf[next])
185 {
186 case 0x622:
187 newbuf[j] = 0xFEF5 + (have_previous ? 1 : 0);
188 i++;
189 continue;
190 case 0x623:
191 newbuf[j] = 0xFEF7 + (have_previous ? 1 : 0);
192 i++;
193 continue;
194 case 0x625:
195 newbuf[j] = 0xFEF9 + (have_previous ? 1 : 0);
196 i++;
197 continue;
198 case 0x627:
199 newbuf[j] = 0xFEFB + (have_previous ? 1 : 0);
200 i++;
201 continue;
202 default:
203 break;
204 }
205 }
206
207 /** Medial **/
208 if (have_previous && have_next && node->medial)
209 {
210 newbuf[j] = node->medial;
211 }
212
213 /** Final **/
214 else if (have_previous && node->final)
215 {
216 newbuf[j] = node->final;
217 }
218
219 /** Initial **/
220 else if (have_next && node->initial)
221 {
222 newbuf[j] = node->initial;
223 }
224
225 /** Isolated **/
226 else if (node->isolated)
227 {
228 newbuf[j] = node->isolated;
229 }
230 }
231 for (i = 0; i < len && i < j; i++) {
232 buf[i] = newbuf[i];
233 }
234 while (i < len) {
235 buf[i++] = L'\0';
236 }
237
238 free(newbuf);
239 return 0;
240 }
241
242 /* Converts bidi wchar text {in} to visual wchar text which is displayable
243 * in text mode. Uses {base_dir} as default base direction.
244 * Returns malloc'ed converted text or NULL in case of error or if {need_out} is
245 * not set. Modifies {base_dir} to reflect actual direction.
246 */
247 static wchar_t* wchar_to_textmod_visual(wchar_t *in,unsigned int len,FriBidiCharType *base_dir, int need_out)
248 {
249 FriBidiChar *out = NULL;
250 static void *handle = NULL;
251
252 fribidi_boolean (*func_ptr) (FriBidiChar *, FriBidiStrIndex,
253 FriBidiCharType *, FriBidiChar *,
254 FriBidiStrIndex *, FriBidiStrIndex *,
255 FriBidiLevel *);
256
257 if (!handle)
258 handle = dlopen("/usr/lib/libfribidi.so.0", RTLD_LAZY | RTLD_GLOBAL);
259 if (!handle)
260 handle = dlopen("/lib/libfribidi.so.0", RTLD_LAZY | RTLD_GLOBAL);
261 if (!handle)
262 return NULL;
263
264 func_ptr = dlsym(handle, "fribidi_log2vis");
265 if (!func_ptr) {
266 dlclose(handle);
267 handle = NULL;
268 return NULL;
269 }
270
271 if (need_out) {
272 out = (FriBidiChar *)malloc(sizeof(FriBidiChar)*(len+1));
273 if(!out)
274 {
275 dlclose(handle);
276 handle = NULL;
277 return (wchar_t *) out;
278 }
279
280 do_shaping(in, len);
281 len = wcsnlen(in, len);
282 }
283 (*func_ptr)(in, len, base_dir, out, NULL, NULL, NULL);
284
285 return (wchar_t *) out;
286 }
287
288 /*
289 * Converts text given in {str} from logical order to visual order.
290 * Uses {dir} as base direction ('N', 'R', 'L').
291 * Returns malloc'ed converted string. Modifies {dir} to reflect actual
292 * direction.
293 */
294 static char *_newt_log2vis(const char *str, char *dir)
295 {
296 wchar_t *wcs;
297 char *rstr = NULL;
298 int len = strlen(str);
299 int ret;
300 FriBidiCharType basedir;
301
302 switch (*dir)
303 {
304 case 'R': basedir = FRIBIDI_TYPE_R;
305 break;
306 case 'L': basedir = FRIBIDI_TYPE_L;
307 break;
308 default: basedir = FRIBIDI_TYPE_ON;
309 break;
310 }
311
312 if (len) {
313 wchar_t *owcs;
314 int newlen;
315
316 wcs = malloc(sizeof(*wcs) * (len + 1));
317 if (!wcs)
318 return NULL;
319 ret = mbstowcs(wcs, str, len + 1);
320 if (ret < 0) {
321 free(wcs);
322 return NULL;
323 }
324 owcs = wchar_to_textmod_visual(wcs, ret, &basedir, 1);
325 if (FRIBIDI_DIR_TO_LEVEL(basedir))
326 *dir = 'R';
327 else
328 *dir = 'L';
329
330 free(wcs);
331 if (!owcs)
332 return NULL;
333
334 newlen = wcstombs(NULL, owcs, 0);
335 if (newlen < 0) {
336 free(owcs);
337 return NULL;
338 }
339 rstr = malloc(newlen + 1);
340 if (!rstr) {
341 free(owcs);
342 return NULL;
343 }
344 ret = wcstombs(rstr, owcs, newlen + 1);
345 free(owcs);
346 if (ret < 0) {
347 free(rstr);
348 return NULL;
349 }
350 }
351 return rstr;
352 }
353
354 /* Returns base direction of text given in {str}.
355 */
356 char get_text_direction(const char *str)
357 {
358 int len = strlen(str);
359 char dir = 'N';
360 wchar_t *wcs;
361 int ret;
362
363 FriBidiCharType basedir = FRIBIDI_TYPE_ON;
364
365 if (len) {
366 wcs = malloc(sizeof(*wcs) * (len + 1));
367 if (!wcs)
368 return dir;
369 ret = mbstowcs(wcs, str, len + 1);
370 if (ret < 0) {
371 free(wcs);
372 return dir;
373 }
374 wchar_to_textmod_visual(wcs, ret, &basedir, 1);
375 free(wcs);
376 if (FRIBIDI_DIR_TO_LEVEL(basedir))
377 dir = 'R';
378 else
379 dir = 'L';
380 }
381 return dir;
382 }
383
384 /* If width of string {str} is less then {width} adds
385 * final spaces to make it {width} position wide.
386 * Returns malloc'ed padded string or NULL in case of errors
387 * or if string does not need padding.
388 */
389 static char *pad_line(const char *str, int width)
390 {
391 int len = strlen(str);
392 int w = _newt_wstrlen(str, len);
393
394 if (w < width) {
395 char *newstr = malloc(len + 1 + (width - w));
396 if (!newstr)
397 return NULL;
398 memcpy(newstr, str, len);
399 memset(newstr + len, ' ', width - w);
400 newstr[len+width-w] = '\0';
401 return newstr;
402 }
403 return NULL;
404 }
405
406 /*
407 * Writes string {str}. Uses {dir} as default direction.
408 * Returns direction of the string in {dir}.
409 */
410 void write_string_int(const char *str, char *dir)
411 {
412 char dummy;
413 char *tmpstr;
414
415 if (!dir) {
416 dummy = 'N';
417 dir = &dummy;
418 }
419
420 tmpstr = _newt_log2vis(str, dir);
421 if (tmpstr)
422 str = tmpstr;
423 SLsmg_write_string(str);
424 if (tmpstr)
425 free(tmpstr);
426 }
427
428 /* Writes at most {n} positions of the string {str}.
429 * Adds final (logical) spaces if string width is less than {n}.
430 * Uses {dir} as default direction.
431 * Returns direction of the string in {dir}
432 */
433 void write_nstring_int(const char *str, int n, char *dir)
434 {
435 char dummy;
436 char *tmpstr, *tmpstr1;
437
438 if (!dir) {
439 dummy = 'N';
440 dir = &dummy;
441 }
442
443 tmpstr1 = pad_line(str, n);
444 if (tmpstr1)
445 str = tmpstr1;
446 tmpstr = _newt_log2vis(str, dir);
447 if (tmpstr) {
448 free(tmpstr1);
449 str = tmpstr;
450 }
451 SLsmg_write_nstring(str, n);
452 if (tmpstr)
453 free(tmpstr);
454 else
455 free(tmpstr1);
456 }
457 #else
458 void write_string_int(const char *str, char *dir)
459 {
460 SLsmg_write_string(str);
461 }
462
463 void write_nstring_int(const char *str, int w, char *dir)
464 {
465 SLsmg_write_nstring(str, w);
466 }
467
468 char get_text_direction(const char *str)
469 {
470 return 'L';
471 }
472 #endif
473
474 struct Window {
475 int height, width, top, left;
476 SLsmg_Char_Type * buffer;
477 char * title;
478 };
479
480 struct keymap {
481 char * str;
482 int code;
483 char * tc;
484 };
485
486 static struct Window windowStack[20];
487 static struct Window * currentWindow = NULL;
488
489 static char * helplineStack[20];
490 static char ** currentHelpline = NULL;
491
492 static int cursorRow, cursorCol;
493 static int needResize = 0;
494 static int cursorOn = 1;
495 static int trashScreen = 0;
496
497 static const char * defaultHelpLine =
498 " <Tab>/<Alt-Tab> between elements | <Space> selects | <F12> next screen"
499 ;
500
501 const struct newtColors newtDefaultColorPalette = {
502 "white", "blue", /* root fg, bg */
503 "black", "lightgray", /* border fg, bg */
504 "black", "lightgray", /* window fg, bg */
505 "white", "black", /* shadow fg, bg */
506 "red", "lightgray", /* title fg, bg */
507 "lightgray", "red", /* button fg, bg */
508 "red", "lightgray", /* active button fg, bg */
509 "yellow", "blue", /* checkbox fg, bg */
510 "blue", "brown", /* active checkbox fg, bg */
511 "yellow", "blue", /* entry box fg, bg */
512 "blue", "lightgray", /* label fg, bg */
513 "black", "lightgray", /* listbox fg, bg */
514 "yellow", "blue", /* active listbox fg, bg */
515 "black", "lightgray", /* textbox fg, bg */
516 "lightgray", "black", /* active textbox fg, bg */
517 "white", "blue", /* help line */
518 "yellow", "blue", /* root text */
519 "blue", /* scale full */
520 "red", /* scale empty */
521 "blue", "lightgray", /* disabled entry fg, bg */
522 "black", "lightgray", /* compact button fg, bg */
523 "yellow", "red", /* active & sel listbox */
524 "black", "brown" /* selected listbox */
525 };
526
527 static const struct keymap keymap[] = {
528 { "\033OA", NEWT_KEY_UP, "ku" },
529 { "\020", NEWT_KEY_UP, NULL }, /* emacs ^P */
530 { "\033OB", NEWT_KEY_DOWN, "kd" },
531 { "\016", NEWT_KEY_DOWN, NULL }, /* emacs ^N */
532 { "\033OC", NEWT_KEY_RIGHT, "kr" },
533 { "\006", NEWT_KEY_RIGHT, NULL }, /* emacs ^F */
534 { "\033OD", NEWT_KEY_LEFT, "kl" },
535 { "\002", NEWT_KEY_LEFT, NULL }, /* emacs ^B */
536 { "\033OH", NEWT_KEY_HOME, "kh" },
537 { "\033[1~", NEWT_KEY_HOME, NULL },
538 { "\001", NEWT_KEY_HOME, NULL }, /* emacs ^A */
539 { "\033Ow", NEWT_KEY_END, "kH" },
540 { "\033[4~", NEWT_KEY_END, "@7" },
541 { "\005", NEWT_KEY_END, NULL }, /* emacs ^E */
542
543 { "\033[3~", NEWT_KEY_DELETE, "kD" },
544 { "\004", NEWT_KEY_DELETE, NULL }, /* emacs ^D */
545 { "\033[2~", NEWT_KEY_INSERT, "kI" },
546
547 { "\033\t", NEWT_KEY_UNTAB, "kB" },
548
549 { "\033[5~", NEWT_KEY_PGUP, "kP" },
550 { "\033[6~", NEWT_KEY_PGDN, "kN" },
551 { "\033V", NEWT_KEY_PGUP, NULL },
552 { "\033v", NEWT_KEY_PGUP, NULL },
553 { "\026", NEWT_KEY_PGDN, NULL },
554
555 { "\033[[A", NEWT_KEY_F1, NULL },
556 { "\033[[B", NEWT_KEY_F2, NULL },
557 { "\033[[C", NEWT_KEY_F3, NULL },
558 { "\033[[D", NEWT_KEY_F4, NULL },
559 { "\033[[E", NEWT_KEY_F5, NULL },
560
561 { "\033OP", NEWT_KEY_F1, NULL },
562 { "\033OQ", NEWT_KEY_F2, NULL },
563 { "\033OR", NEWT_KEY_F3, NULL },
564 { "\033OS", NEWT_KEY_F4, NULL },
565
566 { "\033[11~", NEWT_KEY_F1, "k1" },
567 { "\033[12~", NEWT_KEY_F2, "k2" },
568 { "\033[13~", NEWT_KEY_F3, "k3" },
569 { "\033[14~", NEWT_KEY_F4, "k4" },
570 { "\033[15~", NEWT_KEY_F5, "k5" },
571 { "\033[17~", NEWT_KEY_F6, "k6" },
572 { "\033[18~", NEWT_KEY_F7, "k7" },
573 { "\033[19~", NEWT_KEY_F8, "k8" },
574 { "\033[20~", NEWT_KEY_F9, "k9" },
575 { "\033[21~", NEWT_KEY_F10, "k;" },
576 { "\033[23~", NEWT_KEY_F11, "F1" },
577 { "\033[24~", NEWT_KEY_F12, "F2" },
578 { "\033", NEWT_KEY_ESCAPE, "@2" },
579 { "\033", NEWT_KEY_ESCAPE, "@9" },
580
581 { "\177", NEWT_KEY_BKSPC, NULL },
582 { "\010", NEWT_KEY_BKSPC, NULL },
583
584 { 0 }, /* LEAVE this one */
585 };
586 static void initKeymap();
587
588 static const char ident[] = // ident friendly
589 "$Version: Newt windowing library v" VERSION " $"
590 "$Copyright: (C) 1996-2003 Red Hat, Inc. Written by Erik Troan $"
591 "$License: Lesser GNU Public License. $";
592
593 static newtSuspendCallback suspendCallback = NULL;
594 static void * suspendCallbackData = NULL;
595
596 void newtSetSuspendCallback(newtSuspendCallback cb, void * data) {
597 suspendCallback = cb;
598 suspendCallbackData = data;
599 }
600
601 static void handleSigwinch(int signum) {
602 needResize = 1;
603 }
604
605 static int getkeyInterruptHook(void) {
606 return -1;
607 }
608
609 int _newt_wstrlen(const char *str, int len) {
610 mbstate_t ps;
611 wchar_t tmp;
612 int nchars = 0;
613
614 if (!str) return 0;
615 if (!len) return 0;
616 if (len < 0) len = strlen(str);
617 memset(&ps,0,sizeof(mbstate_t));
618 while (len > 0) {
619 int x,y;
620
621 x = mbrtowc(&tmp,str,len,&ps);
622 if (x >0) {
623 str += x;
624 len -= x;
625 y = wcwidth(tmp);
626 if (y>0)
627 nchars+=y;
628 } else break;
629 }
630 return nchars;
631 }
632
633 /** Trim a string to fit
634 * @param title - string. NULL will be inserted if necessary
635 * @param chrs - available space. (character cells)
636 */
637 void trim_string(char *title, int chrs)
638 {
639 char *p = title;
640 int ln = chrs;
641 int x = 0,y = 0;
642 wchar_t tmp;
643 mbstate_t ps;
644
645 memset(&ps, 0, sizeof(ps));
646
647 while (*p) {
648 x = mbrtowc(&tmp, p, ln, &ps);
649 if (x < 0) { // error
650 *p = '\0';
651 return;
652 }
653 y = wcwidth(tmp);
654 if (y > ln) {
655 *p = '\0';
656 return;
657 } else {
658 p += x;
659 ln -= y;
660 }
661 }
662 }
663
664 static int getkey() {
665 int c;
666
667 while ((c = SLang_getkey()) == '\xC') { /* if Ctrl-L redraw whole screen */
668 SLsmg_touch_lines (0, SLtt_Screen_Rows - 1);
669 SLsmg_refresh();
670 }
671 return c;
672
673 }
674
675 void newtFlushInput(void) {
676 while (SLang_input_pending(0)) {
677 getkey();
678 }
679 }
680
681 /**
682 * @brief Refresh the screen
683 */
684 void newtRefresh(void) {
685 SLsmg_refresh();
686 }
687
688 void newtSuspend(void) {
689 SLtt_set_cursor_visibility (1);
690 SLsmg_suspend_smg();
691 SLang_reset_tty();
692 SLtt_set_cursor_visibility (cursorOn);
693 }
694
695 /**
696 * @brief Return after suspension.
697 * @return 0 on success.
698 */
699 int newtResume(void) {
700 SLsmg_resume_smg ();
701 SLsmg_refresh();
702 return SLang_init_tty(0, 0, 0);
703 }
704
705 void newtCls(void) {
706 SLsmg_set_color(NEWT_COLORSET_ROOT);
707 SLsmg_gotorc(0, 0);
708 SLsmg_erase_eos();
709
710 newtRefresh();
711 }
712
713 /**
714 * @brief Resize the screen
715 * @param redraw - boolean - should we redraw the screen?
716 */
717 void newtResizeScreen(int redraw) {
718 SLtt_get_screen_size();
719 SLsmg_reinit_smg();
720 if (redraw) {
721 SLsmg_touch_lines (0, SLtt_Screen_Rows - 1);
722 newtRefresh();
723 }
724 }
725
726 /**
727 * @brief Initialize the newt library
728 * @return int - 0 for success, else < 0
729 */
730 int newtInit(void) {
731 char * MonoValue, * MonoEnv = "NEWT_MONO";
732 const char *lang;
733 int ret;
734
735 if ((lang = getenv("LC_ALL")) == NULL)
736 if ((lang = getenv("LC_CTYPE")) == NULL)
737 if ((lang = getenv("LANG")) == NULL)
738 lang = "";
739 if (strstr (lang, ".euc") != NULL)
740 trashScreen = 1;
741
742 (void) strlen(ident);
743
744 SLtt_get_terminfo();
745 SLtt_get_screen_size();
746 // there is no such function in slang?!
747 // SLutf8_enable(-1); /* init. utf8 according to locale */
748
749 MonoValue = getenv(MonoEnv);
750 if ( MonoValue == NULL ) {
751 SLtt_Use_Ansi_Colors = 1;
752 } else {
753 SLtt_Use_Ansi_Colors = 0;
754 }
755
756 if ((ret = SLsmg_init_smg()) < 0)
757 return ret;
758 if ((ret = SLang_init_tty(0, 0, 0)) < 0)
759 return ret;
760
761 newtSetColors(newtDefaultColorPalette);
762 newtCursorOff();
763 initKeymap();
764
765 /*memset(&sa, 0, sizeof(sa));
766 sa.sa_handler = handleSigwinch;
767 sigaction(SIGWINCH, &sa, NULL);*/
768
769 SLsignal_intr(SIGWINCH, handleSigwinch);
770 SLang_getkey_intr_hook = getkeyInterruptHook;
771
772 return 0;
773 }
774
775 /**
776 * @brief Closedown the newt library, tidying screen.
777 * @returns int , 0. (no errors reported)
778 */
779 int newtFinished(void) {
780 SLsmg_gotorc(SLtt_Screen_Rows - 1, 0);
781 newtCursorOn();
782 SLsmg_refresh();
783 SLsmg_reset_smg();
784 SLang_reset_tty();
785
786 return 0;
787 }
788
789 /**
790 * @brief Set the colors used.
791 * @param colors - newtColor struct used.
792 */
793 void newtSetColors(struct newtColors colors) {
794 if (!SLtt_Use_Ansi_Colors) {
795 int i;
796
797 for (i = 2; i < 25; i++)
798 SLtt_set_mono(i, NULL, 0);
799
800 SLtt_set_mono(NEWT_COLORSET_SELLISTBOX, NULL, SLTT_BOLD_MASK);
801
802 SLtt_set_mono(NEWT_COLORSET_ACTBUTTON, NULL, SLTT_REV_MASK);
803 SLtt_set_mono(NEWT_COLORSET_ACTCHECKBOX, NULL, SLTT_REV_MASK);
804 SLtt_set_mono(NEWT_COLORSET_ACTLISTBOX, NULL, SLTT_REV_MASK);
805 SLtt_set_mono(NEWT_COLORSET_ACTTEXTBOX, NULL, SLTT_REV_MASK);
806
807 SLtt_set_mono(NEWT_COLORSET_ACTSELLISTBOX, NULL, SLTT_REV_MASK | SLTT_BOLD_MASK);
808
809 SLtt_set_mono(NEWT_COLORSET_DISENTRY, NULL, 0); // FIXME
810 SLtt_set_mono(NEWT_COLORSET_FULLSCALE, NULL, SLTT_ULINE_MASK | SLTT_REV_MASK);
811 SLtt_set_mono(NEWT_COLORSET_EMPTYSCALE, NULL, SLTT_ULINE_MASK);
812 return;
813 }
814 SLtt_set_color(NEWT_COLORSET_ROOT, "", colors.rootFg, colors.rootBg);
815 SLtt_set_color(NEWT_COLORSET_BORDER, "", colors.borderFg, colors.borderBg);
816 SLtt_set_color(NEWT_COLORSET_WINDOW, "", colors.windowFg, colors.windowBg);
817 SLtt_set_color(NEWT_COLORSET_SHADOW, "", colors.shadowFg, colors.shadowBg);
818 SLtt_set_color(NEWT_COLORSET_TITLE, "", colors.titleFg, colors.titleBg);
819 SLtt_set_color(NEWT_COLORSET_BUTTON, "", colors.buttonFg, colors.buttonBg);
820 SLtt_set_color(NEWT_COLORSET_ACTBUTTON, "", colors.actButtonFg,
821 colors.actButtonBg);
822 SLtt_set_color(NEWT_COLORSET_CHECKBOX, "", colors.checkboxFg,
823 colors.checkboxBg);
824 SLtt_set_color(NEWT_COLORSET_ACTCHECKBOX, "", colors.actCheckboxFg,
825 colors.actCheckboxBg);
826 SLtt_set_color(NEWT_COLORSET_ENTRY, "", colors.entryFg, colors.entryBg);
827 SLtt_set_color(NEWT_COLORSET_LABEL, "", colors.labelFg, colors.labelBg);
828 SLtt_set_color(NEWT_COLORSET_LISTBOX, "", colors.listboxFg,
829 colors.listboxBg);
830 SLtt_set_color(NEWT_COLORSET_ACTLISTBOX, "", colors.actListboxFg,
831 colors.actListboxBg);
832 SLtt_set_color(NEWT_COLORSET_TEXTBOX, "", colors.textboxFg,
833 colors.textboxBg);
834 SLtt_set_color(NEWT_COLORSET_ACTTEXTBOX, "", colors.actTextboxFg,
835 colors.actTextboxBg);
836 SLtt_set_color(NEWT_COLORSET_HELPLINE, "", colors.helpLineFg,
837 colors.helpLineBg);
838 SLtt_set_color(NEWT_COLORSET_ROOTTEXT, "", colors.rootTextFg,
839 colors.rootTextBg);
840
841 SLtt_set_color(NEWT_COLORSET_EMPTYSCALE, "", "white",
842 colors.emptyScale);
843 SLtt_set_color(NEWT_COLORSET_FULLSCALE, "", "white",
844 colors.fullScale);
845 SLtt_set_color(NEWT_COLORSET_DISENTRY, "", colors.disabledEntryFg,
846 colors.disabledEntryBg);
847
848 SLtt_set_color(NEWT_COLORSET_COMPACTBUTTON, "", colors.compactButtonFg,
849 colors.compactButtonBg);
850
851 SLtt_set_color(NEWT_COLORSET_ACTSELLISTBOX, "", colors.actSelListboxFg,
852 colors.actSelListboxBg);
853 SLtt_set_color(NEWT_COLORSET_SELLISTBOX, "", colors.selListboxFg,
854 colors.selListboxBg);
855 }
856
857 /* Keymap handling - rewritten by Henning Makholm <henning@makholm.net>,
858 * November 2003.
859 */
860
861 struct kmap_trie_entry {
862 char c ; /* character got from terminal */
863 int code; /* newt key, or 0 if c does not make a complete sequence */
864 struct kmap_trie_entry *contseq; /* sub-trie for character following c */
865 struct kmap_trie_entry *next; /* try this if char received != c */
866 };
867 /* Here are some static entries that will help in handling esc O foo and
868 esc [ foo as variants of each other: */
869 static struct kmap_trie_entry
870 kmap_trie_escO = { 'O', 0, 0, 0 },
871 kmap_trie_escBrack = { '[', 0, 0, &kmap_trie_escO },
872 kmap_trie_root = { '\033', 0, &kmap_trie_escBrack, 0 };
873 static int keyreader_buf_len = 10 ;
874 static unsigned char default_keyreader_buf[10];
875 static unsigned char *keyreader_buf = default_keyreader_buf;
876
877 #if 0 /* for testing of the keymap manipulation code */
878 static void dumpkeys_recursive(struct kmap_trie_entry *curr, int i, FILE *f) {
879 int j, ps ;
880 char seen[256]={0};
881 if( curr && i >= keyreader_buf_len ) {
882 fprintf(f,"ARGH! Too long sequence!\n") ;
883 return ;
884 }
885 for(;curr;curr=curr->next) {
886 keyreader_buf[i] = curr->c ;
887 ps = seen[(unsigned char)curr->c]++ ;
888 if( ps || curr->code || (!curr->code && !curr->contseq) ) {
889 for(j=0;j<=i;j++) {
890 if( keyreader_buf[j] > 32 && keyreader_buf[j]<127 &&
891 keyreader_buf[j] != '^' && keyreader_buf[j] != '\\' )
892 fprintf(f,"%c",keyreader_buf[j]);
893 else if( keyreader_buf[j] > 0 && keyreader_buf[j]<=32 )
894 fprintf(f,"^%c",keyreader_buf[j] + 0x40);
895 else
896 fprintf(f,"\\%03o",
897 (unsigned)(unsigned char)keyreader_buf[j]);
898 }
899 if( curr->code )
900 fprintf(f,": 0x%X\n",curr->code);
901 else
902 fprintf(f,": (just keymap)\n");
903 }
904 dumpkeys_recursive(curr->contseq,i+1,f);
905 }
906 }
907 static void dump_keymap(void) {
908 FILE *f = fopen("newt.keydump","wt");
909 if (f) {
910 dumpkeys_recursive(&kmap_trie_root,0,f);
911 fclose(f);
912 }
913 }
914 #endif
915
916 /* newtBindKey may overwrite a binding that is there already */
917 static void newtBindKey(char *keyseq, int meaning) {
918 struct kmap_trie_entry *root = &kmap_trie_root ;
919 struct kmap_trie_entry **curptr = &root ;
920
921 /* Try to make sure the common matching buffer is long enough. */
922 if( strlen(keyseq) > keyreader_buf_len ) {
923 int i = strlen(keyseq)+10;
924 unsigned char *newbuf = malloc(i);
925 if (newbuf) {
926 if (keyreader_buf != default_keyreader_buf)
927 free(keyreader_buf);
928 keyreader_buf = newbuf;
929 keyreader_buf_len = i;
930 }
931 }
932
933 if (*keyseq == 0) return; /* binding the empty sequence is meaningless */
934
935 while(1) {
936 while ((*curptr) && (*curptr)->c != *keyseq)
937 curptr = &(*curptr)->next;
938 if ((*curptr)==0) {
939 struct kmap_trie_entry* fresh
940 = calloc(strlen(keyseq),sizeof(struct kmap_trie_entry));
941 if (fresh == 0) return; /* despair! */
942 *curptr = fresh;
943 while (keyseq[1]) {
944 fresh->contseq = fresh+1;
945 (fresh++)->c = *(keyseq++);
946 }
947 fresh->c = *keyseq;
948 fresh->code = meaning;
949 return;
950 }
951 if (keyseq[1]==0) {
952 (*curptr)->code = meaning;
953 return;
954 } else {
955 curptr = &(*curptr)->contseq;
956 keyseq++;
957 }
958 }
959 }
960
961 /* This function recursively inserts all entries in the "to" trie into
962 corresponding positions in the "from" trie, except positions that
963 are already defined in the "from" trie. */
964 static void kmap_trie_fallback(struct kmap_trie_entry *to,
965 struct kmap_trie_entry **from) {
966 if (*from == NULL)
967 *from = to ;
968 if (*from == to)
969 return ;
970 for (;to!=NULL;to=to->next) {
971 struct kmap_trie_entry **fromcopy = from ;
972 while ((*fromcopy) && (*fromcopy)->c != to->c)
973 fromcopy = &(*fromcopy)->next ;
974 if (*fromcopy) {
975 if ((*fromcopy)->code == 0)
976 (*fromcopy)->code = to->code;
977 kmap_trie_fallback(to->contseq, &(*fromcopy)->contseq);
978 } else {
979 *fromcopy = malloc(sizeof(struct kmap_trie_entry));
980 if (*fromcopy) {
981 **fromcopy = *to ;
982 (*fromcopy)->next = 0 ;
983 }
984 }
985 }
986 }
987
988 int newtGetKey(void) {
989 int key;
990 unsigned char *chptr = keyreader_buf, *lastmatch;
991 int lastcode;
992 struct kmap_trie_entry *curr = &kmap_trie_root;
993
994 do {
995 key = getkey();
996 if (key == SLANG_GETKEY_ERROR) {
997 /* Either garbage was read, or stdin disappeared
998 * (the parent terminal was proably closed)
999 * if the latter, die.
1000 */
1001 if (feof(stdin))
1002 exit(1);
1003 if (needResize) {
1004 needResize = 0;
1005 return NEWT_KEY_RESIZE;
1006 }
1007
1008 /* ignore other signals */
1009 continue;
1010 }
1011
1012 if (key == NEWT_KEY_SUSPEND && suspendCallback)
1013 suspendCallback(suspendCallbackData);
1014 } while (key == NEWT_KEY_SUSPEND);
1015
1016 /* Read more characters, matching against the trie as we go */
1017 lastcode = *chptr = key;
1018 lastmatch = chptr ;
1019 while(1) {
1020 while (curr->c != key) {
1021 curr = curr->next ;
1022 if (curr==NULL) goto break2levels;
1023 }
1024 if (curr->code) {
1025 lastcode = curr->code;
1026 lastmatch = chptr;
1027 }
1028 curr = curr->contseq;
1029 if (curr==NULL) break;
1030
1031 if (SLang_input_pending(5) <= 0)
1032 break;
1033
1034 if (chptr==keyreader_buf+keyreader_buf_len-1) break;
1035 *++chptr = key = getkey();
1036 }
1037 break2levels:
1038
1039 /* The last time the trie matched was at position lastmatch. Back
1040 * up if we have read too many characters. */
1041 while (chptr > lastmatch)
1042 SLang_ungetkey(*chptr--);
1043
1044 return lastcode;
1045 }
1046
1047 /**
1048 * @brief Wait for a keystroke
1049 */
1050 void newtWaitForKey(void) {
1051 newtRefresh();
1052
1053 getkey();
1054 newtClearKeyBuffer();
1055 }
1056
1057 /**
1058 * @brief Clear the keybuffer
1059 */
1060 void newtClearKeyBuffer(void) {
1061 while (SLang_input_pending(1)) {
1062 getkey();
1063 }
1064 }
1065
1066 /**
1067 * Open a new window.
1068 * @param left. unsigned int Size; _not_ including border
1069 * @param top: unsigned int size, _not_ including border
1070 * @param width unsigned int
1071 * @param height unsigned int
1072 * @param title - title string
1073 * @return zero on success (currently no errors reported)
1074 */
1075 int newtOpenWindow(unsigned int left, unsigned int top,
1076 unsigned int width, unsigned int height,
1077 const char * title) {
1078 int j, row, col;
1079 int n;
1080 int i;
1081
1082 newtFlushInput();
1083
1084 if (!currentWindow) {
1085 currentWindow = windowStack;
1086 } else {
1087 currentWindow++;
1088 }
1089
1090 currentWindow->left = left;
1091 currentWindow->top = top;
1092 currentWindow->width = width;
1093 currentWindow->height = height;
1094 currentWindow->title = title ? strdup(title) : NULL;
1095
1096 currentWindow->buffer = malloc(sizeof(SLsmg_Char_Type) * (width + 3) * (height + 3));
1097
1098 row = top - 1;
1099 col = left - 1;
1100 /* clip to the current screen bounds - msw */
1101 if (row < 0)
1102 row = 0;
1103 if (col < 0)
1104 col = 0;
1105 if (left + width > SLtt_Screen_Cols)
1106 width = SLtt_Screen_Cols - left;
1107 if (top + height > SLtt_Screen_Rows)
1108 height = SLtt_Screen_Rows - top;
1109 n = 0;
1110 for (j = 0; j < height + 3; j++, row++) {
1111 SLsmg_gotorc(row, col);
1112 SLsmg_read_raw(currentWindow->buffer + n,
1113 currentWindow->width + 3);
1114 n += currentWindow->width + 3;
1115 }
1116
1117 newtTrashScreen();
1118
1119 SLsmg_set_color(NEWT_COLORSET_BORDER);
1120 SLsmg_set_char_set(1);
1121 SLsmg_draw_box(top - 1, left - 1, height + 2, width + 2);
1122 SLsmg_set_char_set(0);
1123
1124 if (currentWindow->title) {
1125 trim_string (currentWindow->title, width-4);
1126 i = wstrlen(currentWindow->title,-1) + 4;
1127 i = ((width - i) / 2) + left;
1128 SLsmg_gotorc(top - 1, i);
1129 SLsmg_set_char_set(1);
1130 SLsmg_write_char(SLSMG_RTEE_CHAR);
1131 SLsmg_set_char_set(0);
1132 SLsmg_write_char(' ');
1133 SLsmg_set_color(NEWT_COLORSET_TITLE);
1134 write_string_int((char *)currentWindow->title, NULL);
1135 SLsmg_set_color(NEWT_COLORSET_BORDER);
1136 SLsmg_write_char(' ');
1137 SLsmg_set_char_set(1);
1138 SLsmg_write_char(SLSMG_LTEE_CHAR);
1139 SLsmg_set_char_set(0);
1140 }
1141
1142 SLsmg_set_color(NEWT_COLORSET_WINDOW);
1143 SLsmg_fill_region(top, left, height, width, ' ');
1144
1145 SLsmg_set_color(NEWT_COLORSET_SHADOW);
1146 SLsmg_fill_region(top + height + 1, left, 1, width + 2, ' ');
1147 SLsmg_fill_region(top, left + width + 1, height + 1, 1, ' ');
1148
1149 for (i = top; i < (top + height + 1); i++) {
1150 SLsmg_gotorc(i, left + width + 1);
1151 SLsmg_write_string(" ");
1152 }
1153
1154 return 0;
1155 }
1156
1157 /**
1158 * @brief Draw a centered window.
1159 * @param width - width in char cells
1160 * @param height - no. of char cells.
1161 * @param title - fixed title
1162 * @returns 0. No errors reported
1163 */
1164 int newtCenteredWindow(unsigned int width,unsigned int height,
1165 const char * title) {
1166 unsigned int top, left;
1167
1168 top = (SLtt_Screen_Rows - height) / 2;
1169
1170 /* I don't know why, but this seems to look better */
1171 if ((SLtt_Screen_Rows % 2) && (top % 2)) top--;
1172
1173 left = (SLtt_Screen_Cols - width) / 2;
1174
1175 newtOpenWindow(left, top, width, height, title);
1176
1177 return 0;
1178 }
1179
1180 /**
1181 * @brief Remove the top window
1182 */
1183 void newtPopWindow(void) {
1184 int j, row, col;
1185 int n = 0;
1186
1187 row = col = 0;
1188
1189 row = currentWindow->top - 1;
1190 col = currentWindow->left - 1;
1191 if (row < 0)
1192 row = 0;
1193 if (col < 0)
1194 col = 0;
1195 for (j = 0; j < currentWindow->height + 3; j++, row++) {
1196 SLsmg_gotorc(row, col);
1197 SLsmg_write_raw(currentWindow->buffer + n,
1198 currentWindow->width + 3);
1199 n += currentWindow->width + 3;
1200 }
1201
1202 free(currentWindow->buffer);
1203 free(currentWindow->title);
1204
1205 if (currentWindow == windowStack)
1206 currentWindow = NULL;
1207 else
1208 currentWindow--;
1209
1210 SLsmg_set_char_set(0);
1211
1212 newtTrashScreen();
1213
1214 newtRefresh();
1215 }
1216
1217 void newtGetWindowPos(int * x, int * y) {
1218 if (currentWindow) {
1219 *x = currentWindow->left;
1220 *y = currentWindow->top;
1221 } else
1222 *x = *y = 0;
1223 }
1224
1225 void newtGetrc(int * row, int * col) {
1226 *row = cursorRow;
1227 *col = cursorCol;
1228 }
1229
1230 void newtGotorc(int newRow, int newCol) {
1231 if (currentWindow) {
1232 newRow += currentWindow->top;
1233 newCol += currentWindow->left;
1234 }
1235
1236 cursorRow = newRow;
1237 cursorCol = newCol;
1238 SLsmg_gotorc(cursorRow, cursorCol);
1239 }
1240
1241 void newtDrawBox(int left, int top, int width, int height, int shadow) {
1242 if (currentWindow) {
1243 top += currentWindow->top;
1244 left += currentWindow->left;
1245 }
1246
1247 SLsmg_draw_box(top, left, height, width);
1248
1249 if (shadow) {
1250 SLsmg_set_color(NEWT_COLORSET_SHADOW);
1251 SLsmg_fill_region(top + height, left + 1, 1, width - 1, ' ');
1252 SLsmg_fill_region(top + 1, left + width, height, 1, ' ');
1253 }
1254 }
1255
1256 void newtClearBox(int left, int top, int width, int height) {
1257 if (currentWindow) {
1258 top += currentWindow->top;
1259 left += currentWindow->left;
1260 }
1261
1262 SLsmg_fill_region(top, left, height, width, ' ');
1263 }
1264
1265 static void initKeymap(void) {
1266 const struct keymap * curr;
1267
1268 /* First bind built-in default bindings. They may be shadowed by
1269 the termcap entries that get bound later. */
1270 for (curr = keymap; curr->code; curr++) {
1271 if (curr->str)
1272 newtBindKey(curr->str,curr->code);
1273 }
1274
1275 /* Then bind strings from termcap entries */
1276 for (curr = keymap; curr->code; curr++) {
1277 if (curr->tc) {
1278 char *pc = SLtt_tgetstr(curr->tc);
1279 if (pc) {
1280 newtBindKey(pc,curr->code);
1281 }
1282 }
1283 }
1284
1285 /* Finally, invent lowest-priority keybindings that correspond to
1286 searching for esc-O-foo if esc-[-foo was not found and vice
1287 versa. That is needed because of strong confusion among
1288 different emulators of VTxxx terminals; some terminfo/termcap
1289 descriptions are apparently written by people who were not
1290 aware of the differences between "applicataion" and "terminal"
1291 keypad modes. Or perhaps they were, but tried to make their
1292 description work with a program that puts the keyboard in the
1293 wrong emulation mode. In short, one needs this: */
1294 kmap_trie_fallback(kmap_trie_escO.contseq, &kmap_trie_escBrack.contseq);
1295 kmap_trie_fallback(kmap_trie_escBrack.contseq, &kmap_trie_escO.contseq);
1296 }
1297
1298 /**
1299 * @brief Delay for a specified number of usecs
1300 * @param int - number of usecs to wait for.
1301 */
1302 void newtDelay(unsigned int usecs) {
1303 fd_set set;
1304 struct timeval tv;
1305
1306 FD_ZERO(&set);
1307
1308 tv.tv_sec = usecs / 1000000;
1309 tv.tv_usec = usecs % 1000000;
1310
1311 select(0, &set, &set, &set, &tv);
1312 }
1313
1314 struct eventResult newtDefaultEventHandler(newtComponent c,
1315 struct event ev) {
1316 struct eventResult er;
1317
1318 er.result = ER_IGNORED;
1319 return er;
1320 }
1321
1322 void newtRedrawHelpLine(void) {
1323 char * buf;
1324
1325 SLsmg_set_color(NEWT_COLORSET_HELPLINE);
1326
1327 if (currentHelpline) {
1328 /* buffer size needs to be wide enough to hold all the multibyte
1329 currentHelpline + all the single byte ' ' to fill the line */
1330 int wlen = wstrlen(*currentHelpline, -1);
1331 int len;
1332
1333 if (wlen > SLtt_Screen_Cols)
1334 wlen = SLtt_Screen_Cols;
1335 len = strlen(*currentHelpline) + (SLtt_Screen_Cols - wlen);
1336 buf = alloca(len + 1);
1337 memset(buf, ' ', len);
1338 memcpy(buf, *currentHelpline, strlen(*currentHelpline));
1339 buf[len] = '\0';
1340 } else {
1341 buf = alloca(SLtt_Screen_Cols + 1);
1342 memset(buf, ' ', SLtt_Screen_Cols);
1343 buf[SLtt_Screen_Cols] = '\0';
1344 }
1345 SLsmg_gotorc(SLtt_Screen_Rows - 1, 0);
1346 write_string_int(buf, NULL);
1347 }
1348
1349 void newtPushHelpLine(const char * text) {
1350 if (!text)
1351 text = defaultHelpLine;
1352
1353 if (currentHelpline)
1354 (*(++currentHelpline)) = strdup(text);
1355 else {
1356 currentHelpline = helplineStack;
1357 *currentHelpline = strdup(text);
1358 }
1359
1360 newtRedrawHelpLine();
1361 }
1362
1363 void newtPopHelpLine(void) {
1364 if (!currentHelpline) return;
1365
1366 free(*currentHelpline);
1367 if (currentHelpline == helplineStack)
1368 currentHelpline = NULL;
1369 else
1370 currentHelpline--;
1371
1372 newtRedrawHelpLine();
1373 }
1374
1375 void newtDrawRootText(int col, int row, const char * text) {
1376 SLsmg_set_color(NEWT_COLORSET_ROOTTEXT);
1377
1378 if (col < 0) {
1379 col = SLtt_Screen_Cols + col;
1380 }
1381
1382 if (row < 0) {
1383 row = SLtt_Screen_Rows + row;
1384 }
1385
1386 SLsmg_gotorc(row, col);
1387 write_string_int((char *)text, NULL);
1388 }
1389
1390 int newtSetFlags(int oldFlags, int newFlags, enum newtFlagsSense sense) {
1391 switch (sense) {
1392 case NEWT_FLAGS_SET:
1393 return oldFlags | newFlags;
1394
1395 case NEWT_FLAGS_RESET:
1396 return oldFlags & (~newFlags);
1397
1398 case NEWT_FLAGS_TOGGLE:
1399 return oldFlags ^ newFlags;
1400
1401 default:
1402 return oldFlags;
1403 }
1404 }
1405
1406 void newtBell(void)
1407 {
1408 SLtt_beep();
1409 }
1410
1411 void newtGetScreenSize(int * cols, int * rows) {
1412 if (rows) *rows = SLtt_Screen_Rows;
1413 if (cols) *cols = SLtt_Screen_Cols;
1414 }
1415
1416 void newtDefaultPlaceHandler(newtComponent c, int newLeft, int newTop) {
1417 c->left = newLeft;
1418 c->top = newTop;
1419 }
1420
1421 void newtDefaultMappedHandler(newtComponent c, int isMapped) {
1422 c->isMapped = isMapped;
1423 }
1424
1425 void newtCursorOff(void) {
1426 cursorOn = 0;
1427 SLtt_set_cursor_visibility (cursorOn);
1428 }
1429
1430 void newtCursorOn(void) {
1431 cursorOn = 1;
1432 SLtt_set_cursor_visibility (cursorOn);
1433 }
1434
1435 void newtTrashScreen(void) {
1436 if (trashScreen)
1437 SLsmg_touch_lines (0, SLtt_Screen_Rows - 1);
1438 }
1439