Fixed ncurses window resize (Closes: #1170).
[mp-5.x.git] / mpv_curses.c
blob704b101b4ad0fd09c75826b7c5922799ce3a4d45
1 /*
3 Minimum Profit - Programmer Text Editor
5 Curses driver.
7 Copyright (C) 1991-2012 Angel Ortega <angel@triptico.com>
9 This program is free software; you can redistribute it and/or
10 modify it under the terms of the GNU General Public License
11 as published by the Free Software Foundation; either version 2
12 of the License, or (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23 http://www.triptico.com
27 #include "config.h"
29 #ifdef CONFOPT_CURSES
31 #include <stdio.h>
32 #include <wchar.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <signal.h>
36 #include <fcntl.h>
37 #include <unistd.h>
38 #include <sys/stat.h>
39 #include <sys/ioctl.h>
41 #include <ncursesw/ncurses.h>
43 #include "mpdm.h"
44 #include "mpsl.h"
46 #include "mp.h"
48 /** data **/
50 /* the curses attributes */
51 int *nc_attrs = NULL;
53 /* code for the 'normal' attribute */
54 static int normal_attr = 0;
56 /* current window */
57 static WINDOW *cw = NULL;
59 /* stack of windows */
60 static int n_stack = 0;
61 static WINDOW **w_stack = NULL;
63 /* last attr set */
64 static int last_attr = 0;
66 /* timer function */
67 static int timer_msecs = 0;
68 static mpdm_t timer_func = NULL;
70 /** code **/
72 static void set_attr(void)
73 /* set the current and fill attributes */
75 if (wattrset(cw, nc_attrs[last_attr]) == OK)
76 wbkgdset(cw, ' ' | nc_attrs[last_attr]);
80 #ifndef NCURSES_VERSION
82 static void nc_sigwinch(int s)
83 /* SIGWINCH signal handler */
85 mpdm_t v;
87 /* invalidate main window */
88 clearok(stdscr, 1);
89 refresh();
91 /* re-set dimensions */
92 v = mpdm_hget_s(mp, L"window");
93 mpdm_hset_s(v, L"tx", MPDM_I(COLS));
94 mpdm_hset_s(v, L"ty", MPDM_I(LINES));
96 /* reattach */
97 signal(SIGWINCH, nc_sigwinch);
100 #endif
103 #ifdef CONFOPT_WGET_WCH
104 int wget_wch(WINDOW * w, wint_t * ch);
105 #endif
107 static wchar_t *nc_getwch(void)
108 /* gets a key as a wchar_t */
110 static wchar_t c[2];
112 #ifdef CONFOPT_WGET_WCH
114 /* set timer period */
115 if (timer_msecs > 0)
116 timeout(timer_msecs);
118 if (wget_wch(stdscr, (wint_t *) c) == -1)
119 c[0] = (wchar_t) - 1;
121 #else
122 char tmp[MB_CUR_MAX + 1];
123 int cc, n = 0;
125 /* read one byte */
126 cc = wgetch(cw);
127 if (has_key(cc)) {
128 c[0] = cc;
129 return c;
132 /* set to non-blocking */
133 nodelay(cw, 1);
135 /* read all possible following characters */
136 tmp[n++] = cc;
137 while (n < sizeof(tmp) - 1 && (cc = getch()) != ERR)
138 tmp[n++] = cc;
140 /* sets input as blocking */
141 nodelay(cw, 0);
143 tmp[n] = '\0';
144 mbstowcs(c, tmp, n);
145 #endif
147 c[1] = '\0';
148 return c;
152 #define ctrl(k) ((k) & 31)
154 static mpdm_t nc_getkey(mpdm_t args, mpdm_t ctxt)
155 /* reads a key and converts to an action */
157 static int shift = 0;
158 wchar_t *f = NULL;
159 mpdm_t k = NULL;
161 /* any pending key? return it */
162 if ((k = mp_pending_key()) != NULL)
163 return k;
165 f = nc_getwch();
167 if (f[0] == -1) {
168 mpdm_void(mpdm_exec(timer_func, NULL, NULL));
169 return NULL;
172 if (shift) {
173 switch (f[0]) {
174 case L'0':
175 f = L"f10";
176 break;
177 case L'1':
178 f = L"f1";
179 break;
180 case L'2':
181 f = L"f2";
182 break;
183 case L'3':
184 f = L"f3";
185 break;
186 case L'4':
187 f = L"f4";
188 break;
189 case L'5':
190 f = L"f5";
191 break;
192 case L'6':
193 f = L"f6";
194 break;
195 case L'7':
196 f = L"f7";
197 break;
198 case L'8':
199 f = L"f8";
200 break;
201 case L'9':
202 f = L"f9";
203 break;
204 case KEY_LEFT:
205 f = L"alt-cursor-left";
206 break;
207 case KEY_RIGHT:
208 f = L"alt-cursor-right";
209 break;
210 case KEY_DOWN:
211 f = L"alt-cursor-down";
212 break;
213 case KEY_UP:
214 f = L"alt-cursor-up";
215 break;
216 case KEY_END:
217 f = L"alt-end";
218 break;
219 case KEY_HOME:
220 f = L"alt-home";
221 break;
222 case L'\r':
223 f = L"alt-enter";
224 break;
225 case L'\e':
226 f = L"escape";
227 break;
228 case KEY_ENTER:
229 f = L"alt-enter";
230 break;
231 case L' ':
232 f = L"alt-space";
233 break;
234 case L'a':
235 f = L"alt-a";
236 break;
237 case L'b':
238 f = L"alt-b";
239 break;
240 case L'c':
241 f = L"alt-c";
242 break;
243 case L'd':
244 f = L"alt-d";
245 break;
246 case L'e':
247 f = L"alt-e";
248 break;
249 case L'f':
250 f = L"alt-f";
251 break;
252 case L'g':
253 f = L"alt-g";
254 break;
255 case L'h':
256 f = L"alt-h";
257 break;
258 case L'i':
259 f = L"alt-i";
260 break;
261 case L'j':
262 f = L"alt-j";
263 break;
264 case L'k':
265 f = L"alt-k";
266 break;
267 case L'l':
268 f = L"alt-l";
269 break;
270 case L'm':
271 f = L"alt-m";
272 break;
273 case L'n':
274 f = L"alt-n";
275 break;
276 case L'o':
277 f = L"alt-o";
278 break;
279 case L'p':
280 f = L"alt-p";
281 break;
282 case L'q':
283 f = L"alt-q";
284 break;
285 case L'r':
286 f = L"alt-r";
287 break;
288 case L's':
289 f = L"alt-s";
290 break;
291 case L't':
292 f = L"alt-t";
293 break;
294 case L'u':
295 f = L"alt-u";
296 break;
297 case L'v':
298 f = L"alt-v";
299 break;
300 case L'w':
301 f = L"alt-w";
302 break;
303 case L'x':
304 f = L"alt-x";
305 break;
306 case L'y':
307 f = L"alt-y";
308 break;
309 case L'z':
310 f = L"alt-z";
311 break;
312 case L'\'':
313 f = L"alt-'";
314 break;
315 case L',':
316 f = L"alt-,";
317 break;
318 case L'-':
319 f = L"alt--";
320 break;
321 case L'.':
322 f = L"alt-.";
323 break;
324 case L'/':
325 f = L"alt-/";
326 break;
327 case L'=':
328 f = L"alt-=";
329 break;
332 shift = 0;
334 else {
335 switch (f[0]) {
336 case KEY_LEFT:
337 f = L"cursor-left";
338 break;
339 case KEY_RIGHT:
340 f = L"cursor-right";
341 break;
342 case KEY_UP:
343 f = L"cursor-up";
344 break;
345 case KEY_DOWN:
346 f = L"cursor-down";
347 break;
348 case KEY_PPAGE:
349 f = L"page-up";
350 break;
351 case KEY_NPAGE:
352 f = L"page-down";
353 break;
354 case KEY_HOME:
355 f = L"home";
356 break;
357 case KEY_END:
358 f = L"end";
359 break;
360 case KEY_LL:
361 f = L"end";
362 break;
363 case KEY_IC:
364 f = L"insert";
365 break;
366 case KEY_DC:
367 f = L"delete";
368 break;
369 case 0x7f:
370 case KEY_BACKSPACE:
371 case L'\b':
372 f = L"backspace";
373 break;
374 case L'\r':
375 case KEY_ENTER:
376 f = L"enter";
377 break;
378 case L'\t':
379 f = L"tab";
380 break;
381 case KEY_BTAB:
382 f = L"shift-tab";
383 break;
384 case L' ':
385 f = L"space";
386 break;
387 case KEY_F(1):
388 f = L"f1";
389 break;
390 case KEY_F(2):
391 f = L"f2";
392 break;
393 case KEY_F(3):
394 f = L"f3";
395 break;
396 case KEY_F(4):
397 f = L"f4";
398 break;
399 case KEY_F(5):
400 f = L"f5";
401 break;
402 case KEY_F(6):
403 f = L"f6";
404 break;
405 case KEY_F(7):
406 f = L"f7";
407 break;
408 case KEY_F(8):
409 f = L"f8";
410 break;
411 case KEY_F(9):
412 f = L"f9";
413 break;
414 case KEY_F(10):
415 f = L"f10";
416 break;
417 case ctrl(' '):
418 f = L"ctrl-space";
419 break;
420 case ctrl('a'):
421 f = L"ctrl-a";
422 break;
423 case ctrl('b'):
424 f = L"ctrl-b";
425 break;
426 case ctrl('c'):
427 f = L"ctrl-c";
428 break;
429 case ctrl('d'):
430 f = L"ctrl-d";
431 break;
432 case ctrl('e'):
433 f = L"ctrl-e";
434 break;
435 case ctrl('f'):
436 f = L"ctrl-f";
437 break;
438 case ctrl('g'):
439 f = L"ctrl-g";
440 break;
441 case ctrl('j'):
442 f = L"ctrl-j";
443 break;
444 case ctrl('k'):
445 f = L"ctrl-k";
446 break;
447 case ctrl('l'):
448 f = L"ctrl-l";
449 break;
450 case ctrl('n'):
451 f = L"ctrl-n";
452 break;
453 case ctrl('o'):
454 f = L"ctrl-o";
455 break;
456 case ctrl('p'):
457 f = L"ctrl-p";
458 break;
459 case ctrl('q'):
460 f = L"ctrl-q";
461 break;
462 case ctrl('r'):
463 f = L"ctrl-r";
464 break;
465 case ctrl('s'):
466 f = L"ctrl-s";
467 break;
468 case ctrl('t'):
469 f = L"ctrl-t";
470 break;
471 case ctrl('u'):
472 f = L"ctrl-u";
473 break;
474 case ctrl('v'):
475 f = L"ctrl-v";
476 break;
477 case ctrl('w'):
478 f = L"ctrl-w";
479 break;
480 case ctrl('x'):
481 f = L"ctrl-x";
482 break;
483 case ctrl('y'):
484 f = L"ctrl-y";
485 break;
486 case ctrl('z'):
487 f = L"ctrl-z";
488 break;
489 case KEY_RESIZE:
491 /* handle ncurses resizing */
492 f = NULL;
494 mpdm_t v = mpdm_hget_s(mp, L"window");
495 mpdm_hset_s(v, L"tx", MPDM_I(COLS));
496 mpdm_hset_s(v, L"ty", MPDM_I(LINES));
499 break;
500 case L'\e':
501 shift = 1;
502 f = NULL;
503 break;
507 if (f != NULL) {
508 mpdm_t t;
510 k = mpdm_ref(MPDM_S(f));
512 if ((t = mp_process_keyseq(k)) != k) {
513 mpdm_unref(k);
514 k = t;
516 else
517 mpdm_unrefnd(t);
520 return k;
524 static mpdm_t nc_addwstr(mpdm_t str)
525 /* draws a string */
527 wchar_t *wptr = mpdm_string(str);
529 #ifndef CONFOPT_ADDWSTR
530 char *cptr;
532 cptr = mpdm_wcstombs(wptr, NULL);
533 waddstr(cw, cptr);
534 free(cptr);
536 #else
537 waddwstr(cw, wptr);
538 #endif /* CONFOPT_ADDWSTR */
540 return NULL;
544 static mpdm_t nc_doc_draw(mpdm_t args, mpdm_t ctxt)
545 /* draws the document part */
547 mpdm_t d;
548 int n, m;
550 werase(cw);
552 d = mpdm_aget(args, 0);
553 d = mpdm_ref(mp_draw(d, 0));
555 for (n = 0; n < mpdm_size(d); n++) {
556 mpdm_t l = mpdm_aget(d, n);
558 wmove(cw, n, 0);
560 for (m = 0; m < mpdm_size(l); m++) {
561 int attr;
562 mpdm_t s;
564 /* get the attribute and the string */
565 attr = mpdm_ival(mpdm_aget(l, m++));
566 s = mpdm_aget(l, m);
568 if (wattrset(cw, nc_attrs[attr]) == OK)
569 nc_addwstr(s);
573 mpdm_unref(d);
575 return NULL;
579 static void build_colors(void)
580 /* builds the colors */
582 mpdm_t colors;
583 mpdm_t color_names;
584 mpdm_t l;
585 mpdm_t c;
586 int n, s;
588 #ifdef CONFOPT_TRANSPARENCY
589 use_default_colors();
591 #define DEFAULT_INK -1
592 #define DEFAULT_PAPER -1
594 #else /* CONFOPT_TRANSPARENCY */
596 #define DEFAULT_INK COLOR_BLACK
597 #define DEFAULT_PAPER COLOR_WHITE
599 #endif
601 /* gets the color definitions and attribute names */
602 colors = mpdm_hget_s(mp, L"colors");
603 color_names = mpdm_hget_s(mp, L"color_names");
604 l = mpdm_ref(mpdm_keys(colors));
605 s = mpdm_size(l);
607 /* redim the structures */
608 nc_attrs = realloc(nc_attrs, sizeof(int) * s);
610 /* loop the colors */
611 for (n = 0; n < s && (c = mpdm_aget(l, n)) != NULL; n++) {
612 mpdm_t d = mpdm_hget(colors, c);
613 mpdm_t v = mpdm_hget_s(d, L"text");
614 int cp, c0, c1;
616 /* store the 'normal' attribute */
617 if (wcscmp(mpdm_string(c), L"normal") == 0)
618 normal_attr = n;
620 /* store the attr */
621 mpdm_hset_s(d, L"attr", MPDM_I(n));
623 /* get color indexes */
624 if ((c0 = mpdm_seek(color_names, mpdm_aget(v, 0), 1)) == -1 ||
625 (c1 = mpdm_seek(color_names, mpdm_aget(v, 1), 1)) == -1)
626 continue;
628 init_pair(n + 1, c0 - 1, c1 - 1);
629 cp = COLOR_PAIR(n + 1);
631 /* flags */
632 v = mpdm_hget_s(d, L"flags");
633 if (mpdm_seek_s(v, L"reverse", 1) != -1)
634 cp |= A_REVERSE;
635 if (mpdm_seek_s(v, L"bright", 1) != -1)
636 cp |= A_BOLD;
637 if (mpdm_seek_s(v, L"underline", 1) != -1)
638 cp |= A_UNDERLINE;
640 nc_attrs[n] = cp;
643 /* set the background filler */
644 wbkgdset(cw, ' ' | nc_attrs[normal_attr]);
646 mpdm_unref(l);
650 /** driver functions **/
652 static mpdm_t ncursesw_drv_timer(mpdm_t a, mpdm_t ctxt)
654 mpdm_t func = mpdm_aget(a, 1);
656 timer_msecs = mpdm_ival(mpdm_aget(a, 0));
658 mpdm_ref(func);
659 mpdm_unref(timer_func);
660 timer_func = func;
662 return NULL;
666 static mpdm_t ncursesw_drv_shutdown(mpdm_t a, mpdm_t ctxt)
668 mpdm_t v;
670 endwin();
672 if ((v = mpdm_hget_s(mp, L"exit_message")) != NULL) {
673 mpdm_write_wcs(stdout, mpdm_string(v));
674 printf("\n");
677 return NULL;
681 /** TUI **/
683 static mpdm_t tui_addstr(mpdm_t a, mpdm_t ctxt)
684 /* TUI: add a string */
686 return nc_addwstr(mpdm_aget(a, 0));
690 static mpdm_t tui_move(mpdm_t a, mpdm_t ctxt)
691 /* TUI: move to a screen position */
693 /* curses' move() use y, x */
694 wmove(cw, mpdm_ival(mpdm_aget(a, 1)), mpdm_ival(mpdm_aget(a, 0)));
696 /* if third argument is not NULL, clear line */
697 if (mpdm_aget(a, 2) != NULL)
698 wclrtoeol(cw);
700 return NULL;
704 static mpdm_t tui_attr(mpdm_t a, mpdm_t ctxt)
705 /* TUI: set attribute for next string */
707 last_attr = mpdm_ival(mpdm_aget(a, 0));
709 set_attr();
711 return NULL;
715 static mpdm_t tui_refresh(mpdm_t a, mpdm_t ctxt)
716 /* TUI: refresh the screen */
718 wrefresh(cw);
719 return NULL;
723 static mpdm_t tui_getxy(mpdm_t a, mpdm_t ctxt)
724 /* TUI: returns the x and y cursor position */
726 mpdm_t v;
727 int x, y;
729 getyx(cw, y, x);
731 v = MPDM_A(2);
732 mpdm_ref(v);
734 mpdm_aset(v, MPDM_I(x), 0);
735 mpdm_aset(v, MPDM_I(y), 1);
737 mpdm_unrefnd(v);
739 return v;
743 static mpdm_t tui_openpanel(mpdm_t a, mpdm_t ctxt)
744 /* opens a panel (creates new window) */
746 n_stack++;
747 w_stack = realloc(w_stack, n_stack * sizeof(WINDOW *));
748 cw = w_stack[n_stack - 1] = newwin(mpdm_ival(mpdm_aget(a, 3)),
749 mpdm_ival(mpdm_aget(a, 2)),
750 mpdm_ival(mpdm_aget(a, 1)),
751 mpdm_ival(mpdm_aget(a, 0)));
753 set_attr();
754 wclrtobot(cw);
755 box(cw, 0, 0);
757 return NULL;
761 static mpdm_t tui_closepanel(mpdm_t a, mpdm_t ctxt)
762 /* closes a panel (deletes last window) */
764 n_stack--;
765 delwin(w_stack[n_stack]);
767 w_stack = realloc(w_stack, n_stack * sizeof(WINDOW *));
768 cw = n_stack == 0 ? stdscr : w_stack[n_stack - 1];
770 touchwin(cw);
771 wrefresh(cw);
773 return NULL;
777 static void register_functions(void)
779 mpdm_t drv;
780 mpdm_t tui;
782 drv = mpdm_hget_s(mp, L"drv");
783 mpdm_hset_s(drv, L"timer", MPDM_X(ncursesw_drv_timer));
784 mpdm_hset_s(drv, L"shutdown", MPDM_X(ncursesw_drv_shutdown));
786 tui = mpsl_eval(MPDM_LS(L"load('mp_tui.mpsl');"), NULL, NULL);
788 /* FIXME: if tui failed, a fatal error must be shown */
790 /* if((e = mpdm_hget_s(mpdm_root(), L"ERROR")) != NULL)
792 mpdm_write_wcs(stdout, mpdm_string(e));
793 printf("\n");
795 return(0);
798 /* execute tui */
799 mpdm_hset_s(tui, L"getkey", MPDM_X(nc_getkey));
800 mpdm_hset_s(tui, L"addstr", MPDM_X(tui_addstr));
801 mpdm_hset_s(tui, L"move", MPDM_X(tui_move));
802 mpdm_hset_s(tui, L"attr", MPDM_X(tui_attr));
803 mpdm_hset_s(tui, L"refresh", MPDM_X(tui_refresh));
804 mpdm_hset_s(tui, L"getxy", MPDM_X(tui_getxy));
805 mpdm_hset_s(tui, L"openpanel", MPDM_X(tui_openpanel));
806 mpdm_hset_s(tui, L"closepanel", MPDM_X(tui_closepanel));
807 mpdm_hset_s(tui, L"doc_draw", MPDM_X(nc_doc_draw));
811 static mpdm_t ncursesw_drv_startup(mpdm_t a)
813 mpdm_t v;
815 register_functions();
817 initscr();
818 start_color();
819 keypad(stdscr, TRUE);
820 nonl();
821 raw();
822 noecho();
824 build_colors();
826 v = mpdm_hget_s(mp, L"window");
827 mpdm_hset_s(v, L"tx", MPDM_I(COLS));
828 mpdm_hset_s(v, L"ty", MPDM_I(LINES));
830 #ifndef NCURSES_VERSION
831 signal(SIGWINCH, nc_sigwinch);
832 #endif
834 cw = stdscr;
836 return NULL;
840 int ncursesw_drv_detect(int *argc, char ***argv)
842 mpdm_t drv;
844 drv = mpdm_hget_s(mp, L"drv");
845 mpdm_hset_s(drv, L"id", MPDM_LS(L"curses"));
846 mpdm_hset_s(drv, L"startup", MPDM_X(ncursesw_drv_startup));
848 return 1;
851 #endif /* CONFOPT_CURSES */