Updated MPSL syntax definition.
[mp-5.x.git] / mpv_curses.c
blobc3818a41e1edea1147ae507064eb388d8c51549e
1 /*
3 Minimum Profit - Programmer Text Editor
5 Curses driver.
7 Copyright (C) 1991-2010 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 wattrset(cw, nc_attrs[last_attr]);
76 wbkgdset(cw, ' ' | nc_attrs[last_attr]);
80 static void nc_sigwinch(int s)
81 /* SIGWINCH signal handler */
83 mpdm_t v;
85 #ifdef NCURSES_VERSION
86 /* Make sure that window size changes... */
87 struct winsize ws;
89 int fd = open("/dev/tty", O_RDWR);
91 if (fd == -1)
92 return; /* This should never have to happen! */
94 if (ioctl(fd, TIOCGWINSZ, &ws) == 0)
95 resizeterm(ws.ws_row, ws.ws_col);
97 close(fd);
98 #else
99 /* restart curses */
100 /* ... */
101 #endif
103 /* invalidate main window */
104 clearok(stdscr, 1);
105 refresh();
107 /* re-set dimensions */
108 v = mpdm_hget_s(mp, L"window");
109 mpdm_hset_s(v, L"tx", MPDM_I(COLS));
110 mpdm_hset_s(v, L"ty", MPDM_I(LINES));
112 /* reattach */
113 signal(SIGWINCH, nc_sigwinch);
117 #ifdef CONFOPT_WGET_WCH
118 int wget_wch(WINDOW * w, wint_t * ch);
119 #endif
121 static wchar_t *nc_getwch(void)
122 /* gets a key as a wchar_t */
124 static wchar_t c[2];
126 #ifdef CONFOPT_WGET_WCH
128 /* set timer period */
129 if (timer_msecs > 0)
130 timeout(timer_msecs);
132 if (wget_wch(stdscr, (wint_t *) c) == -1)
133 c[0] = (wchar_t) - 1;
135 #else
136 char tmp[MB_CUR_MAX + 1];
137 int cc, n = 0;
139 /* read one byte */
140 cc = wgetch(cw);
141 if (has_key(cc)) {
142 c[0] = cc;
143 return c;
146 /* set to non-blocking */
147 nodelay(cw, 1);
149 /* read all possible following characters */
150 tmp[n++] = cc;
151 while (n < sizeof(tmp) - 1 && (cc = getch()) != ERR)
152 tmp[n++] = cc;
154 /* sets input as blocking */
155 nodelay(cw, 0);
157 tmp[n] = '\0';
158 mbstowcs(c, tmp, n);
159 #endif
161 c[1] = '\0';
162 return c;
166 #define ctrl(k) ((k) & 31)
168 static mpdm_t nc_getkey(mpdm_t args, mpdm_t ctxt)
169 /* reads a key and converts to an action */
171 static int shift = 0;
172 wchar_t *f = NULL;
173 mpdm_t k = NULL;
175 /* any pending key? return it */
176 if ((k = mp_pending_key()) != NULL)
177 return k;
179 f = nc_getwch();
181 if (f[0] == -1) {
182 mpdm_void(mpdm_exec(timer_func, NULL, NULL));
183 return NULL;
186 if (shift) {
187 switch (f[0]) {
188 case L'0':
189 f = L"f10";
190 break;
191 case L'1':
192 f = L"f1";
193 break;
194 case L'2':
195 f = L"f2";
196 break;
197 case L'3':
198 f = L"f3";
199 break;
200 case L'4':
201 f = L"f4";
202 break;
203 case L'5':
204 f = L"f5";
205 break;
206 case L'6':
207 f = L"f6";
208 break;
209 case L'7':
210 f = L"f7";
211 break;
212 case L'8':
213 f = L"f8";
214 break;
215 case L'9':
216 f = L"f9";
217 break;
218 case KEY_LEFT:
219 f = L"alt-cursor-left";
220 break;
221 case KEY_RIGHT:
222 f = L"alt-cursor-right";
223 break;
224 case KEY_DOWN:
225 f = L"alt-cursor-down";
226 break;
227 case KEY_UP:
228 f = L"alt-cursor-up";
229 break;
230 case KEY_END:
231 f = L"alt-end";
232 break;
233 case KEY_HOME:
234 f = L"alt-home";
235 break;
236 case L'\r':
237 f = L"alt-enter";
238 break;
239 case L'\e':
240 f = L"escape";
241 break;
242 case KEY_ENTER:
243 f = L"alt-enter";
244 break;
245 case L' ':
246 f = L"alt-space";
247 break;
248 case L'a':
249 f = L"alt-a";
250 break;
251 case L'b':
252 f = L"alt-b";
253 break;
254 case L'c':
255 f = L"alt-c";
256 break;
257 case L'd':
258 f = L"alt-d";
259 break;
260 case L'e':
261 f = L"alt-e";
262 break;
263 case L'f':
264 f = L"alt-f";
265 break;
266 case L'g':
267 f = L"alt-g";
268 break;
269 case L'h':
270 f = L"alt-h";
271 break;
272 case L'i':
273 f = L"alt-i";
274 break;
275 case L'j':
276 f = L"alt-j";
277 break;
278 case L'k':
279 f = L"alt-k";
280 break;
281 case L'l':
282 f = L"alt-l";
283 break;
284 case L'm':
285 f = L"alt-m";
286 break;
287 case L'n':
288 f = L"alt-n";
289 break;
290 case L'o':
291 f = L"alt-o";
292 break;
293 case L'p':
294 f = L"alt-p";
295 break;
296 case L'q':
297 f = L"alt-q";
298 break;
299 case L'r':
300 f = L"alt-r";
301 break;
302 case L's':
303 f = L"alt-s";
304 break;
305 case L't':
306 f = L"alt-t";
307 break;
308 case L'u':
309 f = L"alt-u";
310 break;
311 case L'v':
312 f = L"alt-v";
313 break;
314 case L'w':
315 f = L"alt-w";
316 break;
317 case L'x':
318 f = L"alt-x";
319 break;
320 case L'y':
321 f = L"alt-y";
322 break;
323 case L'z':
324 f = L"alt-z";
325 break;
326 case L'\'':
327 f = L"alt-'";
328 break;
329 case L',':
330 f = L"alt-,";
331 break;
332 case L'-':
333 f = L"alt--";
334 break;
335 case L'.':
336 f = L"alt-.";
337 break;
338 case L'/':
339 f = L"alt-/";
340 break;
341 case L'=':
342 f = L"alt-=";
343 break;
346 shift = 0;
348 else {
349 switch (f[0]) {
350 case KEY_LEFT:
351 f = L"cursor-left";
352 break;
353 case KEY_RIGHT:
354 f = L"cursor-right";
355 break;
356 case KEY_UP:
357 f = L"cursor-up";
358 break;
359 case KEY_DOWN:
360 f = L"cursor-down";
361 break;
362 case KEY_PPAGE:
363 f = L"page-up";
364 break;
365 case KEY_NPAGE:
366 f = L"page-down";
367 break;
368 case KEY_HOME:
369 f = L"home";
370 break;
371 case KEY_END:
372 f = L"end";
373 break;
374 case KEY_LL:
375 f = L"end";
376 break;
377 case KEY_IC:
378 f = L"insert";
379 break;
380 case KEY_DC:
381 f = L"delete";
382 break;
383 case 0x7f:
384 case KEY_BACKSPACE:
385 case L'\b':
386 f = L"backspace";
387 break;
388 case L'\r':
389 case KEY_ENTER:
390 f = L"enter";
391 break;
392 case L'\t':
393 f = L"tab";
394 break;
395 case KEY_BTAB:
396 f = L"shift-tab";
397 break;
398 case L' ':
399 f = L"space";
400 break;
401 case KEY_F(1):
402 f = L"f1";
403 break;
404 case KEY_F(2):
405 f = L"f2";
406 break;
407 case KEY_F(3):
408 f = L"f3";
409 break;
410 case KEY_F(4):
411 f = L"f4";
412 break;
413 case KEY_F(5):
414 f = L"f5";
415 break;
416 case KEY_F(6):
417 f = L"f6";
418 break;
419 case KEY_F(7):
420 f = L"f7";
421 break;
422 case KEY_F(8):
423 f = L"f8";
424 break;
425 case KEY_F(9):
426 f = L"f9";
427 break;
428 case KEY_F(10):
429 f = L"f10";
430 break;
431 case ctrl(' '):
432 f = L"ctrl-space";
433 break;
434 case ctrl('a'):
435 f = L"ctrl-a";
436 break;
437 case ctrl('b'):
438 f = L"ctrl-b";
439 break;
440 case ctrl('c'):
441 f = L"ctrl-c";
442 break;
443 case ctrl('d'):
444 f = L"ctrl-d";
445 break;
446 case ctrl('e'):
447 f = L"ctrl-e";
448 break;
449 case ctrl('f'):
450 f = L"ctrl-f";
451 break;
452 case ctrl('g'):
453 f = L"ctrl-g";
454 break;
455 case ctrl('j'):
456 f = L"ctrl-j";
457 break;
458 case ctrl('k'):
459 f = L"ctrl-k";
460 break;
461 case ctrl('l'):
462 f = L"ctrl-l";
463 break;
464 case ctrl('n'):
465 f = L"ctrl-n";
466 break;
467 case ctrl('o'):
468 f = L"ctrl-o";
469 break;
470 case ctrl('p'):
471 f = L"ctrl-p";
472 break;
473 case ctrl('q'):
474 f = L"ctrl-q";
475 break;
476 case ctrl('r'):
477 f = L"ctrl-r";
478 break;
479 case ctrl('s'):
480 f = L"ctrl-s";
481 break;
482 case ctrl('t'):
483 f = L"ctrl-t";
484 break;
485 case ctrl('u'):
486 f = L"ctrl-u";
487 break;
488 case ctrl('v'):
489 f = L"ctrl-v";
490 break;
491 case ctrl('w'):
492 f = L"ctrl-w";
493 break;
494 case ctrl('x'):
495 f = L"ctrl-x";
496 break;
497 case ctrl('y'):
498 f = L"ctrl-y";
499 break;
500 case ctrl('z'):
501 f = L"ctrl-z";
502 break;
503 case L'\e':
504 shift = 1;
505 f = NULL;
506 break;
510 if (f != NULL) {
511 mpdm_t t;
513 k = mpdm_ref(MPDM_S(f));
515 if ((t = mp_process_keyseq(k)) != k) {
516 mpdm_unref(k);
517 k = t;
519 else
520 mpdm_unrefnd(t);
523 return k;
527 static mpdm_t nc_addwstr(mpdm_t str)
528 /* draws a string */
530 wchar_t *wptr = mpdm_string(str);
532 #ifndef CONFOPT_ADDWSTR
533 char *cptr;
535 cptr = mpdm_wcstombs(wptr, NULL);
536 waddstr(cw, cptr);
537 free(cptr);
539 #else
540 waddwstr(cw, wptr);
541 #endif /* CONFOPT_ADDWSTR */
543 return NULL;
547 static mpdm_t nc_doc_draw(mpdm_t args, mpdm_t ctxt)
548 /* draws the document part */
550 mpdm_t d;
551 int n, m;
553 werase(cw);
555 d = mpdm_aget(args, 0);
556 d = mpdm_ref(mp_draw(d, 0));
558 for (n = 0; n < mpdm_size(d); n++) {
559 mpdm_t l = mpdm_aget(d, n);
561 wmove(cw, n, 0);
563 for (m = 0; m < mpdm_size(l); m++) {
564 int attr;
565 mpdm_t s;
567 /* get the attribute and the string */
568 attr = mpdm_ival(mpdm_aget(l, m++));
569 s = mpdm_aget(l, m);
571 wattrset(cw, nc_attrs[attr]);
572 nc_addwstr(s);
576 mpdm_unref(d);
578 return NULL;
582 static void build_colors(void)
583 /* builds the colors */
585 mpdm_t colors;
586 mpdm_t color_names;
587 mpdm_t l;
588 mpdm_t c;
589 int n, s;
591 #ifdef CONFOPT_TRANSPARENCY
592 use_default_colors();
594 #define DEFAULT_INK -1
595 #define DEFAULT_PAPER -1
597 #else /* CONFOPT_TRANSPARENCY */
599 #define DEFAULT_INK COLOR_BLACK
600 #define DEFAULT_PAPER COLOR_WHITE
602 #endif
604 /* gets the color definitions and attribute names */
605 colors = mpdm_hget_s(mp, L"colors");
606 color_names = mpdm_hget_s(mp, L"color_names");
607 l = mpdm_ref(mpdm_keys(colors));
608 s = mpdm_size(l);
610 /* redim the structures */
611 nc_attrs = realloc(nc_attrs, sizeof(int) * s);
613 /* loop the colors */
614 for (n = 0; n < s && (c = mpdm_aget(l, n)) != NULL; n++) {
615 mpdm_t d = mpdm_hget(colors, c);
616 mpdm_t v = mpdm_hget_s(d, L"text");
617 int cp, c0, c1;
619 /* store the 'normal' attribute */
620 if (wcscmp(mpdm_string(c), L"normal") == 0)
621 normal_attr = n;
623 /* store the attr */
624 mpdm_hset_s(d, L"attr", MPDM_I(n));
626 /* get color indexes */
627 if ((c0 = mpdm_seek(color_names, mpdm_aget(v, 0), 1)) == -1 ||
628 (c1 = mpdm_seek(color_names, mpdm_aget(v, 1), 1)) == -1)
629 continue;
631 init_pair(n + 1, c0 - 1, c1 - 1);
632 cp = COLOR_PAIR(n + 1);
634 /* flags */
635 v = mpdm_hget_s(d, L"flags");
636 if (mpdm_seek_s(v, L"reverse", 1) != -1)
637 cp |= A_REVERSE;
638 if (mpdm_seek_s(v, L"bright", 1) != -1)
639 cp |= A_BOLD;
640 if (mpdm_seek_s(v, L"underline", 1) != -1)
641 cp |= A_UNDERLINE;
643 nc_attrs[n] = cp;
646 /* set the background filler */
647 wbkgdset(cw, ' ' | nc_attrs[normal_attr]);
649 mpdm_unref(l);
653 /** driver functions **/
655 static mpdm_t ncursesw_drv_timer(mpdm_t a, mpdm_t ctxt)
657 mpdm_t func = mpdm_aget(a, 1);
659 timer_msecs = mpdm_ival(mpdm_aget(a, 0));
661 mpdm_ref(func);
662 mpdm_unref(timer_func);
663 timer_func = func;
665 return NULL;
669 static mpdm_t ncursesw_drv_shutdown(mpdm_t a, mpdm_t ctxt)
671 mpdm_t v;
673 endwin();
675 if ((v = mpdm_hget_s(mp, L"exit_message")) != NULL) {
676 mpdm_write_wcs(stdout, mpdm_string(v));
677 printf("\n");
680 return NULL;
684 /** TUI **/
686 static mpdm_t tui_addstr(mpdm_t a, mpdm_t ctxt)
687 /* TUI: add a string */
689 return nc_addwstr(mpdm_aget(a, 0));
693 static mpdm_t tui_move(mpdm_t a, mpdm_t ctxt)
694 /* TUI: move to a screen position */
696 /* curses' move() use y, x */
697 wmove(cw, mpdm_ival(mpdm_aget(a, 1)), mpdm_ival(mpdm_aget(a, 0)));
699 /* if third argument is not NULL, clear line */
700 if (mpdm_aget(a, 2) != NULL)
701 wclrtoeol(cw);
703 return NULL;
707 static mpdm_t tui_attr(mpdm_t a, mpdm_t ctxt)
708 /* TUI: set attribute for next string */
710 last_attr = mpdm_ival(mpdm_aget(a, 0));
712 set_attr();
714 return NULL;
718 static mpdm_t tui_refresh(mpdm_t a, mpdm_t ctxt)
719 /* TUI: refresh the screen */
721 wrefresh(cw);
722 return NULL;
726 static mpdm_t tui_getxy(mpdm_t a, mpdm_t ctxt)
727 /* TUI: returns the x and y cursor position */
729 mpdm_t v;
730 int x, y;
732 getyx(cw, y, x);
734 v = MPDM_A(2);
735 mpdm_ref(v);
737 mpdm_aset(v, MPDM_I(x), 0);
738 mpdm_aset(v, MPDM_I(y), 1);
740 mpdm_unrefnd(v);
742 return v;
746 static mpdm_t tui_openpanel(mpdm_t a, mpdm_t ctxt)
747 /* opens a panel (creates new window) */
749 n_stack++;
750 w_stack = realloc(w_stack, n_stack * sizeof(WINDOW *));
751 cw = w_stack[n_stack - 1] = newwin(mpdm_ival(mpdm_aget(a, 3)),
752 mpdm_ival(mpdm_aget(a, 2)),
753 mpdm_ival(mpdm_aget(a, 1)),
754 mpdm_ival(mpdm_aget(a, 0)));
756 set_attr();
757 wclrtobot(cw);
758 box(cw, 0, 0);
760 return NULL;
764 static mpdm_t tui_closepanel(mpdm_t a, mpdm_t ctxt)
765 /* closes a panel (deletes last window) */
767 n_stack--;
768 delwin(w_stack[n_stack]);
770 w_stack = realloc(w_stack, n_stack * sizeof(WINDOW *));
771 cw = n_stack == 0 ? stdscr : w_stack[n_stack - 1];
773 touchwin(cw);
774 wrefresh(cw);
776 return NULL;
780 static void register_functions(void)
782 mpdm_t drv;
783 mpdm_t tui;
785 drv = mpdm_hget_s(mp, L"drv");
786 mpdm_hset_s(drv, L"timer", MPDM_X(ncursesw_drv_timer));
787 mpdm_hset_s(drv, L"shutdown", MPDM_X(ncursesw_drv_shutdown));
789 tui = mpsl_eval(MPDM_LS(L"load('mp_tui.mpsl');"), NULL, NULL);
791 /* FIXME: if tui failed, a fatal error must be shown */
793 /* if((e = mpdm_hget_s(mpdm_root(), L"ERROR")) != NULL)
795 mpdm_write_wcs(stdout, mpdm_string(e));
796 printf("\n");
798 return(0);
801 /* execute tui */
802 mpdm_hset_s(tui, L"getkey", MPDM_X(nc_getkey));
803 mpdm_hset_s(tui, L"addstr", MPDM_X(tui_addstr));
804 mpdm_hset_s(tui, L"move", MPDM_X(tui_move));
805 mpdm_hset_s(tui, L"attr", MPDM_X(tui_attr));
806 mpdm_hset_s(tui, L"refresh", MPDM_X(tui_refresh));
807 mpdm_hset_s(tui, L"getxy", MPDM_X(tui_getxy));
808 mpdm_hset_s(tui, L"openpanel", MPDM_X(tui_openpanel));
809 mpdm_hset_s(tui, L"closepanel", MPDM_X(tui_closepanel));
810 mpdm_hset_s(tui, L"doc_draw", MPDM_X(nc_doc_draw));
814 static mpdm_t ncursesw_drv_startup(mpdm_t a)
816 mpdm_t v;
818 register_functions();
820 initscr();
821 start_color();
822 keypad(stdscr, TRUE);
823 nonl();
824 raw();
825 noecho();
827 build_colors();
829 v = mpdm_hget_s(mp, L"window");
830 mpdm_hset_s(v, L"tx", MPDM_I(COLS));
831 mpdm_hset_s(v, L"ty", MPDM_I(LINES));
833 signal(SIGWINCH, nc_sigwinch);
835 cw = stdscr;
837 return NULL;
841 int ncursesw_drv_detect(int *argc, char ***argv)
843 mpdm_t drv;
845 drv = mpdm_hget_s(mp, L"drv");
846 mpdm_hset_s(drv, L"id", MPDM_LS(L"curses"));
847 mpdm_hset_s(drv, L"startup", MPDM_X(ncursesw_drv_startup));
849 return 1;
852 #endif /* CONFOPT_CURSES */