]> git.ipfire.org Git - thirdparty/newt.git/blob - newt.c
don't flicker, SLsmg_touch_screen does a clear
[thirdparty/newt.git] / newt.c
1 #include <slang.h>
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <string.h>
5 #include <sys/signal.h>
6 #include <sys/time.h>
7 #include <sys/types.h>
8 #include <termios.h>
9 #include <unistd.h>
10
11 #include "newt.h"
12 #include "newt_pr.h"
13
14 struct Window {
15 int height, width, top, left;
16 short * buffer;
17 char * title;
18 };
19
20 struct keymap {
21 char * str;
22 int code;
23 char * tc;
24 };
25
26 static struct Window windowStack[20];
27 static struct Window * currentWindow = NULL;
28
29 static char * helplineStack[20];
30 static char ** currentHelpline = NULL;
31
32 static int cursorRow, cursorCol;
33 static int needResize;
34 static int cursorOn = 1;
35 static int trashScreen = 0;
36
37 static const char * defaultHelpLine =
38 " <Tab>/<Alt-Tab> between elements | <Space> selects | <F12> next screen"
39 ;
40
41 const struct newtColors newtDefaultColorPalette = {
42 "white", "blue", /* root fg, bg */
43 "black", "lightgray", /* border fg, bg */
44 "black", "lightgray", /* window fg, bg */
45 "white", "black", /* shadow fg, bg */
46 "red", "lightgray", /* title fg, bg */
47 "lightgray", "red", /* button fg, bg */
48 "red", "lightgray", /* active button fg, bg */
49 "yellow", "blue", /* checkbox fg, bg */
50 "blue", "brown", /* active checkbox fg, bg */
51 "yellow", "blue", /* entry box fg, bg */
52 "blue", "lightgray", /* label fg, bg */
53 "black", "lightgray", /* listbox fg, bg */
54 "yellow", "blue", /* active listbox fg, bg */
55 "black", "lightgray", /* textbox fg, bg */
56 "lightgray", "black", /* active textbox fg, bg */
57 "white", "blue", /* help line */
58 "yellow", "blue", /* root text */
59 "blue", /* scale full */
60 "red", /* scale empty */
61 "blue", "lightgray", /* disabled entry fg, bg */
62 "white", "blue", /* compact button fg, bg */
63 "yellow", "red", /* active & sel listbox */
64 "black", "brown" /* selected listbox */
65 };
66
67 static const struct keymap keymap[] = {
68 { "\033OA", NEWT_KEY_UP, "kh" },
69 { "\033[A", NEWT_KEY_UP, "ku" },
70 { "\033OB", NEWT_KEY_DOWN, "kd" },
71 { "\033[B", NEWT_KEY_DOWN, "kd" },
72 { "\033[C", NEWT_KEY_RIGHT, "kr" },
73 { "\033OC", NEWT_KEY_RIGHT, "kr" },
74 { "\033[D", NEWT_KEY_LEFT, "kl" },
75 { "\033OD", NEWT_KEY_LEFT, "kl" },
76 { "\033[H", NEWT_KEY_HOME, "kh" },
77 { "\033[1~", NEWT_KEY_HOME, "kh" },
78 { "\033Ow", NEWT_KEY_END, "kH" },
79 { "\033[4~", NEWT_KEY_END, "kH" },
80
81 { "\033[3~", NEWT_KEY_DELETE, "kl" },
82 { "\033[2~", NEWT_KEY_INSERT, NULL },
83
84 { "\033\t", NEWT_KEY_UNTAB, NULL },
85
86 { "\033[5~", NEWT_KEY_PGUP, NULL },
87 { "\033[6~", NEWT_KEY_PGDN, NULL },
88 { "\033V", NEWT_KEY_PGUP, "kH" },
89 { "\033v", NEWT_KEY_PGUP, "kH" },
90
91 { "\033[[A", NEWT_KEY_F1, NULL },
92 { "\033[[B", NEWT_KEY_F2, NULL },
93 { "\033[[C", NEWT_KEY_F3, NULL },
94 { "\033[[D", NEWT_KEY_F4, NULL },
95 { "\033[[E", NEWT_KEY_F5, NULL },
96
97 { "\033OP", NEWT_KEY_F1, NULL },
98 { "\033OQ", NEWT_KEY_F2, NULL },
99 { "\033OR", NEWT_KEY_F3, NULL },
100 { "\033OS", NEWT_KEY_F4, NULL },
101
102 { "\033[11~", NEWT_KEY_F1, NULL },
103 { "\033[12~", NEWT_KEY_F2, NULL },
104 { "\033[13~", NEWT_KEY_F3, NULL },
105 { "\033[14~", NEWT_KEY_F4, NULL },
106 { "\033[15~", NEWT_KEY_F5, NULL },
107 { "\033[17~", NEWT_KEY_F6, NULL },
108 { "\033[18~", NEWT_KEY_F7, NULL },
109 { "\033[19~", NEWT_KEY_F8, NULL },
110 { "\033[20~", NEWT_KEY_F9, NULL },
111 { "\033[21~", NEWT_KEY_F10, NULL },
112 { "\033[23~", NEWT_KEY_F11, NULL },
113 { "\033[24~", NEWT_KEY_F12, NULL },
114
115 { NULL, 0, NULL }, /* LEAVE this one */
116 };
117 static char keyPrefix = '\033';
118
119 static const char * version = "Newt windowing library version " VERSION
120 " - (C) 1996-2000 Red Hat Software. "
121 "Redistributable under the term of the Library "
122 "GNU Public License. "
123 "Written by Erik Troan\n";
124
125 static newtSuspendCallback suspendCallback = NULL;
126 static void * suspendCallbackData = NULL;
127
128 void newtSetSuspendCallback(newtSuspendCallback cb, void * data) {
129 suspendCallback = cb;
130 suspendCallbackData = data;
131 }
132
133 static void handleSigwinch(int signum) {
134 needResize = 1;
135 }
136
137 static int getkeyInterruptHook(void) {
138 return -1;
139 }
140
141 void newtFlushInput(void) {
142 while (SLang_input_pending(0)) {
143 SLang_getkey();
144 }
145 }
146
147 void newtRefresh(void) {
148 SLsmg_refresh();
149 }
150
151 void newtSuspend(void) {
152 SLtt_set_cursor_visibility (1);
153 SLsmg_suspend_smg();
154 SLang_reset_tty();
155 SLtt_set_cursor_visibility (cursorOn);
156 }
157
158 void newtResume(void) {
159 SLsmg_resume_smg ();
160 SLsmg_refresh();
161 SLang_init_tty(0, 0, 0);
162 }
163
164 void newtCls(void) {
165 SLsmg_set_color(NEWT_COLORSET_ROOT);
166 SLsmg_gotorc(0, 0);
167 SLsmg_erase_eos();
168
169 newtRefresh();
170 }
171
172 #if defined(THIS_DOESNT_WORK)
173 void newtResizeScreen(int redraw) {
174 newtPushHelpLine("");
175
176 SLtt_get_screen_size();
177 SLang_init_tty(0, 0, 0);
178
179 SLsmg_touch_lines (0, SLtt_Screen_Rows - 1);
180
181 /* I don't know why I need this */
182 SLsmg_refresh();
183
184 newtPopHelpLine();
185
186 if (redraw)
187 SLsmg_refresh();
188 }
189 #endif
190
191 int newtInit(void) {
192 char * MonoValue, * MonoEnv = "NEWT_MONO", * lang;
193
194 lang = getenv ("LANG");
195 if (lang && !strcasecmp (lang, "ja_JP.eucJP"))
196 trashScreen = 1;
197
198 /* use the version variable just to be sure it gets included */
199 strlen(version);
200
201 SLtt_get_terminfo();
202 SLtt_get_screen_size();
203
204 MonoValue = getenv(MonoEnv);
205 if ( MonoValue == NULL ) {
206 SLtt_Use_Ansi_Colors = 1;
207 } else {
208 SLtt_Use_Ansi_Colors = 0;
209 }
210
211 SLsmg_init_smg();
212 SLang_init_tty(0, 0, 0);
213
214 newtSetColors(newtDefaultColorPalette);
215 newtCursorOff();
216 /*initKeymap();*/
217
218 /*memset(&sa, 0, sizeof(sa));
219 sa.sa_handler = handleSigwinch;
220 sigaction(SIGWINCH, &sa, NULL);*/
221
222 SLsignal_intr(SIGWINCH, handleSigwinch);
223 SLang_getkey_intr_hook = getkeyInterruptHook;
224
225
226
227 return 0;
228 }
229
230 int newtFinished(void) {
231 SLsmg_gotorc(SLtt_Screen_Rows - 1, 0);
232 newtCursorOn();
233 SLsmg_refresh();
234 SLsmg_reset_smg();
235 SLang_reset_tty();
236
237 return 0;
238 }
239
240 void newtSetColors(struct newtColors colors) {
241 SLtt_set_color(NEWT_COLORSET_ROOT, "", colors.rootFg, colors.rootBg);
242 SLtt_set_color(NEWT_COLORSET_BORDER, "", colors.borderFg, colors.borderBg);
243 SLtt_set_color(NEWT_COLORSET_WINDOW, "", colors.windowFg, colors.windowBg);
244 SLtt_set_color(NEWT_COLORSET_SHADOW, "", colors.shadowFg, colors.shadowBg);
245 SLtt_set_color(NEWT_COLORSET_TITLE, "", colors.titleFg, colors.titleBg);
246 SLtt_set_color(NEWT_COLORSET_BUTTON, "", colors.buttonFg, colors.buttonBg);
247 SLtt_set_color(NEWT_COLORSET_ACTBUTTON, "", colors.actButtonFg,
248 colors.actButtonBg);
249 SLtt_set_color(NEWT_COLORSET_CHECKBOX, "", colors.checkboxFg,
250 colors.checkboxBg);
251 SLtt_set_color(NEWT_COLORSET_ACTCHECKBOX, "", colors.actCheckboxFg,
252 colors.actCheckboxBg);
253 SLtt_set_color(NEWT_COLORSET_ENTRY, "", colors.entryFg, colors.entryBg);
254 SLtt_set_color(NEWT_COLORSET_LABEL, "", colors.labelFg, colors.labelBg);
255 SLtt_set_color(NEWT_COLORSET_LISTBOX, "", colors.listboxFg,
256 colors.listboxBg);
257 SLtt_set_color(NEWT_COLORSET_ACTLISTBOX, "", colors.actListboxFg,
258 colors.actListboxBg);
259 SLtt_set_color(NEWT_COLORSET_TEXTBOX, "", colors.textboxFg,
260 colors.textboxBg);
261 SLtt_set_color(NEWT_COLORSET_ACTTEXTBOX, "", colors.actTextboxFg,
262 colors.actTextboxBg);
263 SLtt_set_color(NEWT_COLORSET_HELPLINE, "", colors.helpLineFg,
264 colors.helpLineBg);
265 SLtt_set_color(NEWT_COLORSET_ROOTTEXT, "", colors.rootTextFg,
266 colors.rootTextBg);
267
268 SLtt_set_color(NEWT_COLORSET_EMPTYSCALE, "", "black",
269 colors.emptyScale);
270 SLtt_set_color(NEWT_COLORSET_FULLSCALE, "", "black",
271 colors.fullScale);
272 SLtt_set_color(NEWT_COLORSET_DISENTRY, "", colors.disabledEntryFg,
273 colors.disabledEntryBg);
274
275 SLtt_set_color(NEWT_COLORSET_COMPACTBUTTON, "", colors.compactButtonFg,
276 colors.compactButtonBg);
277
278 SLtt_set_color(NEWT_COLORSET_ACTSELLISTBOX, "", colors.actSelListboxFg,
279 colors.actSelListboxBg);
280 SLtt_set_color(NEWT_COLORSET_SELLISTBOX, "", colors.selListboxFg,
281 colors.selListboxBg);
282 }
283
284 int newtGetKey(void) {
285 int key;
286 char buf[10], * chptr = buf;
287 const struct keymap * curr;
288
289 do {
290 key = SLang_getkey();
291 if (key == 0xFFFF) {
292 if (needResize)
293 return NEWT_KEY_RESIZE;
294
295 /* ignore other signals */
296 continue;
297 }
298
299 if (key == NEWT_KEY_SUSPEND && suspendCallback)
300 suspendCallback(suspendCallbackData);
301 } while (key == NEWT_KEY_SUSPEND);
302
303 switch (key) {
304 case 'v' | 0x80:
305 case 'V' | 0x80:
306 return NEWT_KEY_PGUP;
307
308 case 22:
309 return NEWT_KEY_PGDN;
310
311 return NEWT_KEY_BKSPC;
312 case 0x7f:
313 return NEWT_KEY_BKSPC;
314
315 case 0x08:
316 return NEWT_KEY_BKSPC;
317
318 default:
319 if (key != keyPrefix) return key;
320 }
321
322 memset(buf, 0, sizeof(buf));
323
324 *chptr++ = key;
325 while (SLang_input_pending(5)) {
326 key = SLang_getkey();
327 if (key == keyPrefix) {
328 /* he hit unknown keys too many times -- start over */
329 memset(buf, 0, sizeof(buf));
330 chptr = buf;
331 }
332
333 *chptr++ = key;
334
335 /* this search should use bsearch(), but when we only look through
336 a list of 20 (or so) keymappings, it's probably faster just to
337 do a inline linear search */
338
339 for (curr = keymap; curr->code; curr++) {
340 if (curr->str) {
341 if (!strcmp(curr->str, buf))
342 return curr->code;
343 }
344 }
345 }
346
347 for (curr = keymap; curr->code; curr++) {
348 if (curr->str) {
349 if (!strcmp(curr->str, buf))
350 return curr->code;
351 }
352 }
353
354 /* Looks like we were a bit overzealous in reading characters. Return
355 just the first character, and put everything else back in the buffer
356 for later */
357
358 chptr--;
359 while (chptr > buf)
360 SLang_ungetkey(*chptr--);
361
362 return *chptr;
363 }
364
365 void newtWaitForKey(void) {
366 newtRefresh();
367
368 SLang_getkey();
369 newtClearKeyBuffer();
370 }
371
372 void newtClearKeyBuffer(void) {
373 while (SLang_input_pending(1)) {
374 SLang_getkey();
375 }
376 }
377
378 int newtOpenWindow(int left, int top, int width, int height,
379 const char * title) {
380 int j, row, col;
381 int n;
382 int i;
383
384 newtFlushInput();
385
386 if (!currentWindow) {
387 currentWindow = windowStack;
388 } else {
389 currentWindow++;
390 }
391
392 currentWindow->left = left;
393 currentWindow->top = top;
394 currentWindow->width = width;
395 currentWindow->height = height;
396 currentWindow->title = title ? strdup(title) : NULL;
397
398 currentWindow->buffer = malloc(sizeof(short) * (width + 3) * (height + 3));
399
400 row = top - 1;
401 col = left - 1;
402 /* clip to the current screen bounds - msw */
403 if (row < 0)
404 row = 0;
405 if (col < 0)
406 col = 0;
407 if (left + width > SLtt_Screen_Cols)
408 width = SLtt_Screen_Cols - left;
409 if (top + height > SLtt_Screen_Rows)
410 height = SLtt_Screen_Rows - top;
411 n = 0;
412 for (j = 0; j < height + 3; j++, row++) {
413 SLsmg_gotorc(row, col);
414 SLsmg_read_raw(currentWindow->buffer + n,
415 currentWindow->width + 3);
416 n += currentWindow->width + 3;
417 }
418
419 newtTrashScreen();
420
421 SLsmg_set_color(NEWT_COLORSET_BORDER);
422 SLsmg_draw_box(top - 1, left - 1, height + 2, width + 2);
423
424 if (currentWindow->title) {
425 i = strlen(currentWindow->title) + 4;
426 i = ((width - i) / 2) + left;
427 SLsmg_gotorc(top - 1, i);
428 SLsmg_set_char_set(1);
429 SLsmg_write_char(SLSMG_RTEE_CHAR);
430 SLsmg_set_char_set(0);
431 SLsmg_write_char(' ');
432 SLsmg_set_color(NEWT_COLORSET_TITLE);
433 SLsmg_write_string((char *)currentWindow->title);
434 SLsmg_set_color(NEWT_COLORSET_BORDER);
435 SLsmg_write_char(' ');
436 SLsmg_set_char_set(1);
437 SLsmg_write_char(SLSMG_LTEE_CHAR);
438 SLsmg_set_char_set(0);
439 }
440
441 SLsmg_set_color(NEWT_COLORSET_WINDOW);
442 SLsmg_fill_region(top, left, height, width, ' ');
443
444 SLsmg_set_color(NEWT_COLORSET_SHADOW);
445 SLsmg_fill_region(top + height + 1, left, 1, width + 2, ' ');
446 SLsmg_fill_region(top, left + width + 1, height + 1, 1, ' ');
447
448 for (i = top; i < (top + height + 1); i++) {
449 SLsmg_gotorc(i, left + width + 1);
450 SLsmg_write_string(" ");
451 }
452
453 return 0;
454 }
455
456 int newtCenteredWindow(int width, int height, const char * title) {
457 int top, left;
458
459 top = (SLtt_Screen_Rows - height) / 2;
460
461 /* I don't know why, but this seems to look better */
462 if ((SLtt_Screen_Rows % 2) && (top % 2)) top--;
463
464 left = (SLtt_Screen_Cols - width) / 2;
465
466 newtOpenWindow(left, top, width, height, title);
467
468 return 0;
469 }
470
471 void newtPopWindow(void) {
472 int j, row, col;
473 int n = 0;
474
475 row = col = 0;
476
477 row = currentWindow->top - 1;
478 col = currentWindow->left - 1;
479 if (row < 0)
480 row = 0;
481 if (col < 0)
482 col = 0;
483 for (j = 0; j < currentWindow->height + 3; j++, row++) {
484 SLsmg_gotorc(row, col);
485 SLsmg_write_raw(currentWindow->buffer + n,
486 currentWindow->width + 3);
487 n += currentWindow->width + 3;
488 }
489
490 free(currentWindow->buffer);
491 free(currentWindow->title);
492
493 if (currentWindow == windowStack)
494 currentWindow = NULL;
495 else
496 currentWindow--;
497
498 SLsmg_set_char_set(0);
499
500 newtTrashScreen();
501
502 newtRefresh();
503 }
504
505 void newtGetWindowPos(int * x, int * y) {
506 if (currentWindow) {
507 *x = currentWindow->left;
508 *y = currentWindow->top;
509 } else
510 *x = *y = 0;
511 }
512
513 void newtGetrc(int * row, int * col) {
514 *row = cursorRow;
515 *col = cursorCol;
516 }
517
518 void newtGotorc(int newRow, int newCol) {
519 if (currentWindow) {
520 newRow += currentWindow->top;
521 newCol += currentWindow->left;
522 }
523
524 cursorRow = newRow;
525 cursorCol = newCol;
526 SLsmg_gotorc(cursorRow, cursorCol);
527 }
528
529 void newtDrawBox(int left, int top, int width, int height, int shadow) {
530 if (currentWindow) {
531 top += currentWindow->top;
532 left += currentWindow->left;
533 }
534
535 SLsmg_draw_box(top, left, height, width);
536
537 if (shadow) {
538 SLsmg_set_color(NEWT_COLORSET_SHADOW);
539 SLsmg_fill_region(top + height, left + 1, 1, width - 1, ' ');
540 SLsmg_fill_region(top + 1, left + width, height, 1, ' ');
541 }
542 }
543
544 void newtClearBox(int left, int top, int width, int height) {
545 if (currentWindow) {
546 top += currentWindow->top;
547 left += currentWindow->left;
548 }
549
550 SLsmg_fill_region(top, left, height, width, ' ');
551 }
552
553 #if 0
554 /* This doesn't seem to work quite right. I don't know why not, but when
555 I rsh from an rxvt into a box and run this code, the machine returns
556 console key's (\033[B) rather then xterm ones (\033OB). */
557 static void initKeymap(void) {
558 struct keymap * curr;
559
560 for (curr = keymap; curr->code; curr++) {
561 if (!curr->str)
562 curr->str = SLtt_tgetstr(curr->tc);
563 }
564
565 /* Newt's keymap handling is a bit broken. It assumes that any extended
566 keystrokes begin with ESC. If you're using a homebrek terminal you
567 will probably need to fix this, or just yell at me and I'll be so
568 ashamed of myself for doing it this way I'll fix it */
569
570 keyPrefix = 0x1b; /* ESC */
571 }
572 #endif
573
574 void newtDelay(int usecs) {
575 fd_set set;
576 struct timeval tv;
577
578 FD_ZERO(&set);
579
580 tv.tv_sec = usecs / 1000000;
581 tv.tv_usec = usecs % 1000000;
582
583 select(0, &set, &set, &set, &tv);
584 }
585
586 struct eventResult newtDefaultEventHandler(newtComponent c,
587 struct event ev) {
588 struct eventResult er;
589
590 er.result = ER_IGNORED;
591 return er;
592 }
593
594 void newtRedrawHelpLine(void) {
595 char * buf;
596
597 SLsmg_set_color(NEWT_COLORSET_HELPLINE);
598
599 buf = alloca(SLtt_Screen_Cols + 1);
600 memset(buf, ' ', SLtt_Screen_Cols);
601 buf[SLtt_Screen_Cols] = '\0';
602
603 if (currentHelpline) {
604 int len = strlen(*currentHelpline);
605 if (SLtt_Screen_Cols < len)
606 len = SLtt_Screen_Cols;
607 memcpy(buf, *currentHelpline, len);
608 }
609 SLsmg_gotorc(SLtt_Screen_Rows - 1, 0);
610 SLsmg_write_string(buf);
611 }
612
613 void newtPushHelpLine(const char * text) {
614 if (!text)
615 text = defaultHelpLine;
616
617 if (currentHelpline)
618 (*(++currentHelpline)) = strdup(text);
619 else {
620 currentHelpline = helplineStack;
621 *currentHelpline = strdup(text);
622 }
623
624 newtRedrawHelpLine();
625 }
626
627 void newtPopHelpLine(void) {
628 if (!currentHelpline) return;
629
630 free(*currentHelpline);
631 if (currentHelpline == helplineStack)
632 currentHelpline = NULL;
633 else
634 currentHelpline--;
635
636 newtRedrawHelpLine();
637 }
638
639 void newtDrawRootText(int col, int row, const char * text) {
640 SLsmg_set_color(NEWT_COLORSET_ROOTTEXT);
641
642 if (col < 0) {
643 col = SLtt_Screen_Cols + col;
644 }
645
646 if (row < 0) {
647 row = SLtt_Screen_Rows + row;
648 }
649
650 SLsmg_gotorc(row, col);
651 SLsmg_write_string((char *)text);
652 }
653
654 int newtSetFlags(int oldFlags, int newFlags, enum newtFlagsSense sense) {
655 switch (sense) {
656 case NEWT_FLAGS_SET:
657 return oldFlags | newFlags;
658
659 case NEWT_FLAGS_RESET:
660 return oldFlags & (~newFlags);
661
662 case NEWT_FLAGS_TOGGLE:
663 return oldFlags ^ newFlags;
664
665 default:
666 return oldFlags;
667 }
668 }
669
670 void newtBell(void)
671 {
672 SLtt_beep();
673 }
674
675 void newtGetScreenSize(int * cols, int * rows) {
676 if (rows) *rows = SLtt_Screen_Rows;
677 if (cols) *cols = SLtt_Screen_Cols;
678 }
679
680 void newtDefaultPlaceHandler(newtComponent c, int newLeft, int newTop) {
681 c->left = newLeft;
682 c->top = newTop;
683 }
684
685 void newtDefaultMappedHandler(newtComponent c, int isMapped) {
686 c->isMapped = isMapped;
687 }
688
689 void newtCursorOff(void) {
690 cursorOn = 0;
691 SLtt_set_cursor_visibility (cursorOn);
692 }
693
694 void newtCursorOn(void) {
695 cursorOn = 1;
696 SLtt_set_cursor_visibility (cursorOn);
697 }
698
699 void newtTrashScreen(void) {
700 if (trashScreen)
701 SLsmg_touch_lines (0, SLtt_Screen_Rows - 1);
702 }
703