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