The mp.copy() function also allows to directly copy arrays or scalars to the clipboard.
[mp-5.x.git] / mpv_curses.c
blobb3b45afc7d4f65c6ce804a6afd00e803e8a331be
1 /*
3 Minimum Profit - Programmer Text Editor
5 Curses driver.
7 Copyright (C) 1991-2009 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)
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_exec(timer_func, NULL);
183 return NULL;
186 if (shift) {
187 switch (f[0]) {
188 case L'0': f = L"f10"; break;
189 case L'1': f = L"f1"; break;
190 case L'2': f = L"f2"; break;
191 case L'3': f = L"f3"; break;
192 case L'4': f = L"f4"; break;
193 case L'5': f = L"f5"; break;
194 case L'6': f = L"f6"; break;
195 case L'7': f = L"f7"; break;
196 case L'8': f = L"f8"; break;
197 case L'9': f = L"f9"; break;
198 case KEY_LEFT: f = L"ctrl-cursor-left"; break;
199 case KEY_RIGHT: f = L"ctrl-cursor-right"; break;
200 case KEY_DOWN: f = L"ctrl-cursor-down"; break;
201 case KEY_UP: f = L"ctrl-cursor-up"; break;
202 case KEY_END: f = L"ctrl-end"; break;
203 case KEY_HOME: f = L"ctrl-home"; break;
204 case L'\r': f = L"ctrl-enter"; break;
205 case L'\e': f = L"escape"; break;
206 case KEY_ENTER: f = L"ctrl-enter"; break;
207 case L' ': f = L"ctrl-space"; break;
208 case L'a': f = L"ctrl-a"; break;
209 case L'b': f = L"ctrl-b"; break;
210 case L'c': f = L"ctrl-c"; break;
211 case L'd': f = L"ctrl-d"; break;
212 case L'e': f = L"ctrl-e"; break;
213 case L'f': f = L"ctrl-f"; break;
214 case L'g': f = L"ctrl-g"; break;
215 case L'h': f = L"ctrl-h"; break;
216 case L'i': f = L"ctrl-i"; break;
217 case L'j': f = L"ctrl-j"; break;
218 case L'k': f = L"ctrl-k"; break;
219 case L'l': f = L"ctrl-l"; break;
220 case L'm': f = L"ctrl-m"; break;
221 case L'n': f = L"ctrl-n"; break;
222 case L'o': f = L"ctrl-o"; break;
223 case L'p': f = L"ctrl-p"; break;
224 case L'q': f = L"ctrl-q"; break;
225 case L'r': f = L"ctrl-r"; break;
226 case L's': f = L"ctrl-s"; break;
227 case L't': f = L"ctrl-t"; break;
228 case L'u': f = L"ctrl-u"; break;
229 case L'v': f = L"ctrl-v"; break;
230 case L'w': f = L"ctrl-w"; break;
231 case L'x': f = L"ctrl-x"; break;
232 case L'y': f = L"ctrl-y"; break;
233 case L'z': f = L"ctrl-z"; break;
236 shift = 0;
238 else {
239 switch (f[0]) {
240 case KEY_LEFT: f = L"cursor-left"; break;
241 case KEY_RIGHT: f = L"cursor-right"; break;
242 case KEY_UP: f = L"cursor-up"; break;
243 case KEY_DOWN: f = L"cursor-down"; break;
244 case KEY_PPAGE: f = L"page-up"; break;
245 case KEY_NPAGE: f = L"page-down"; break;
246 case KEY_HOME: f = L"home"; break;
247 case KEY_END: f = L"end"; break;
248 case KEY_LL: f = L"end"; break;
249 case KEY_IC: f = L"insert"; break;
250 case KEY_DC: f = L"delete"; break;
251 case 0x7f:
252 case KEY_BACKSPACE:
253 case L'\b': f = L"backspace"; break;
254 case L'\r':
255 case KEY_ENTER: f = L"enter"; break;
256 case L'\t': f = L"tab"; break;
257 case KEY_BTAB: f = L"shift-tab"; break;
258 case L' ': f = L"space"; break;
259 case KEY_F(1): f = L"f1"; break;
260 case KEY_F(2): f = L"f2"; break;
261 case KEY_F(3): f = L"f3"; break;
262 case KEY_F(4): f = L"f4"; break;
263 case KEY_F(5): f = L"f5"; break;
264 case KEY_F(6): f = L"f6"; break;
265 case KEY_F(7): f = L"f7"; break;
266 case KEY_F(8): f = L"f8"; break;
267 case KEY_F(9): f = L"f9"; break;
268 case KEY_F(10): f = L"f10"; break;
269 case ctrl(' '): f = L"ctrl-space"; break;
270 case ctrl('a'): f = L"ctrl-a"; break;
271 case ctrl('b'): f = L"ctrl-b"; break;
272 case ctrl('c'): f = L"ctrl-c"; break;
273 case ctrl('d'): f = L"ctrl-d"; break;
274 case ctrl('e'): f = L"ctrl-e"; break;
275 case ctrl('f'): f = L"ctrl-f"; break;
276 case ctrl('g'): f = L"ctrl-g"; break;
277 case ctrl('j'): f = L"ctrl-j"; break;
278 case ctrl('k'): f = L"ctrl-k"; break;
279 case ctrl('l'): f = L"ctrl-l"; break;
280 case ctrl('n'): f = L"ctrl-n"; break;
281 case ctrl('o'): f = L"ctrl-o"; break;
282 case ctrl('p'): f = L"ctrl-p"; break;
283 case ctrl('q'): f = L"ctrl-q"; break;
284 case ctrl('r'): f = L"ctrl-r"; break;
285 case ctrl('s'): f = L"ctrl-s"; break;
286 case ctrl('t'): f = L"ctrl-t"; break;
287 case ctrl('u'): f = L"ctrl-u"; break;
288 case ctrl('v'): f = L"ctrl-v"; break;
289 case ctrl('w'): f = L"ctrl-w"; break;
290 case ctrl('x'): f = L"ctrl-x"; break;
291 case ctrl('y'): f = L"ctrl-y"; break;
292 case ctrl('z'): f = L"ctrl-z"; break;
293 case L'\e': shift = 1; f = NULL; break;
297 /* no known key? do nothing */
298 if(f == NULL)
299 return NULL;
301 return mp_process_keyseq(MPDM_S(f));
305 static mpdm_t nc_addwstr(mpdm_t str)
306 /* draws a string */
308 wchar_t * wptr = mpdm_string(str);
310 #ifndef CONFOPT_ADDWSTR
311 char * cptr;
313 cptr = mpdm_wcstombs(wptr, NULL);
314 waddstr(cw, cptr);
315 free(cptr);
317 #else
318 waddwstr(cw, wptr);
319 #endif /* CONFOPT_ADDWSTR */
321 return NULL;
325 static void draw_status(void)
326 /* draws the status bar */
328 mpdm_t t;
330 t = mp_build_status_line();
332 /* move to the last line, clear it and draw there */
333 wmove(cw, LINES - 1, 0);
334 wattrset(cw, nc_attrs[normal_attr]);
335 wclrtoeol(cw);
336 nc_addwstr(t);
338 /* draw the menu key hint, right-aligned */
339 if ((t = mpdm_hget_s(mp, L"menu_key_hint")) == NULL) {
340 t = MPDM_LS(L"ctrl-a: ");
341 t = mpdm_strcat(t, mpdm_gettext(MPDM_LS(L"Menu")));
342 mpdm_hset_s(mp, L"menu_key_hint", t);
345 wmove(cw, LINES - 1, COLS - mpdm_size(t));
346 nc_addwstr(t);
350 static void nc_draw(mpdm_t doc)
351 /* driver drawing function for cursesw */
353 mpdm_t d;
354 int n, m;
356 werase(cw);
358 d = mp_draw(doc, 0);
360 for (n = 0; n < mpdm_size(d); n++) {
361 mpdm_t l = mpdm_aget(d, n);
363 wmove(cw, n, 0);
365 for (m = 0; m < mpdm_size(l); m++) {
366 int attr;
367 mpdm_t s;
369 /* get the attribute and the string */
370 attr = mpdm_ival(mpdm_aget(l, m++));
371 s = mpdm_aget(l, m);
373 wattrset(cw, nc_attrs[attr]);
374 nc_addwstr(s);
378 draw_status();
380 wrefresh(cw);
384 static void build_colors(void)
385 /* builds the colors */
387 mpdm_t colors;
388 mpdm_t color_names;
389 mpdm_t l;
390 mpdm_t c;
391 int n, s;
393 #ifdef CONFOPT_TRANSPARENCY
394 use_default_colors();
396 #define DEFAULT_INK -1
397 #define DEFAULT_PAPER -1
399 #else /* CONFOPT_TRANSPARENCY */
401 #define DEFAULT_INK COLOR_BLACK
402 #define DEFAULT_PAPER COLOR_WHITE
404 #endif
406 /* gets the color definitions and attribute names */
407 colors = mpdm_hget_s(mp, L"colors");
408 color_names = mpdm_hget_s(mp, L"color_names");
409 l = mpdm_keys(colors);
410 s = mpdm_size(l);
412 /* redim the structures */
413 nc_attrs = realloc(nc_attrs, sizeof(int) * s);
415 /* loop the colors */
416 for (n = 0; n < s && (c = mpdm_aget(l, n)) != NULL; n++) {
417 mpdm_t d = mpdm_hget(colors, c);
418 mpdm_t v = mpdm_hget_s(d, L"text");
419 int cp, c0, c1;
421 /* store the 'normal' attribute */
422 if (wcscmp(mpdm_string(c), L"normal") == 0)
423 normal_attr = n;
425 /* store the attr */
426 mpdm_hset_s(d, L"attr", MPDM_I(n));
428 /* get color indexes */
429 if ((c0 = mpdm_seek(color_names, mpdm_aget(v, 0), 1)) == -1 ||
430 (c1 = mpdm_seek(color_names, mpdm_aget(v, 1), 1)) == -1)
431 continue;
433 init_pair(n + 1, c0 - 1, c1 - 1);
434 cp = COLOR_PAIR(n + 1);
436 /* flags */
437 v = mpdm_hget_s(d, L"flags");
438 if (mpdm_seek_s(v, L"reverse", 1) != -1)
439 cp |= A_REVERSE;
440 if (mpdm_seek_s(v, L"bright", 1) != -1)
441 cp |= A_BOLD;
442 if (mpdm_seek_s(v, L"underline", 1) != -1)
443 cp |= A_UNDERLINE;
445 nc_attrs[n] = cp;
448 /* set the background filler */
449 wbkgdset(cw, ' ' | nc_attrs[normal_attr]);
453 /** driver functions **/
455 static mpdm_t ncursesw_drv_timer(mpdm_t a)
457 mpdm_t func = mpdm_aget(a, 1);
458 mpdm_t r;
460 timer_msecs = mpdm_ival(mpdm_aget(a, 0));
462 r = mpdm_unref(timer_func);
463 timer_func = mpdm_ref(func);
465 return r;
469 static mpdm_t ncursesw_drv_main_loop(mpdm_t a)
470 /* curses driver main loop */
472 while (! mp_exit_requested) {
473 /* get current document and draw it */
474 nc_draw(mp_active());
476 /* get key and process it */
477 mp_process_event(nc_getkey(NULL));
480 return NULL;
484 static mpdm_t ncursesw_drv_shutdown(mpdm_t a)
486 mpdm_t v;
488 endwin();
490 if ((v = mpdm_hget_s(mp, L"exit_message")) != NULL) {
491 mpdm_write_wcs(stdout, mpdm_string(v));
492 printf("\n");
495 return NULL;
499 /** TUI **/
501 static mpdm_t tui_addstr(mpdm_t a)
502 /* TUI: add a string */
504 return nc_addwstr(mpdm_aget(a, 0));
508 static mpdm_t tui_move(mpdm_t a)
509 /* TUI: move to a screen position */
511 /* curses' move() use y, x */
512 wmove(cw, mpdm_ival(mpdm_aget(a, 1)), mpdm_ival(mpdm_aget(a, 0)));
514 /* if third argument is not NULL, clear line */
515 if (mpdm_aget(a, 2) != NULL)
516 wclrtoeol(cw);
518 return NULL;
522 static mpdm_t tui_attr(mpdm_t a)
523 /* TUI: set attribute for next string */
525 last_attr = mpdm_ival(mpdm_aget(a, 0));
527 set_attr();
529 return NULL;
533 static mpdm_t tui_refresh(mpdm_t a)
534 /* TUI: refresh the screen */
536 wrefresh(cw);
537 return NULL;
541 static mpdm_t tui_getxy(mpdm_t a)
542 /* TUI: returns the x and y cursor position */
544 mpdm_t v;
545 int x, y;
547 getyx(cw, y, x);
549 v = MPDM_A(2);
550 mpdm_aset(v, MPDM_I(x), 0);
551 mpdm_aset(v, MPDM_I(y), 1);
553 return v;
557 static mpdm_t tui_openpanel(mpdm_t a)
558 /* opens a panel (creates new window) */
560 n_stack++;
561 w_stack = realloc(w_stack, n_stack * sizeof(WINDOW *));
562 cw = w_stack[n_stack - 1] = newwin(mpdm_ival(mpdm_aget(a, 3)),
563 mpdm_ival(mpdm_aget(a, 2)),
564 mpdm_ival(mpdm_aget(a, 1)),
565 mpdm_ival(mpdm_aget(a, 0)));
567 set_attr();
568 wclrtobot(cw);
569 box(cw, 0, 0);
571 return NULL;
575 static mpdm_t tui_closepanel(mpdm_t a)
576 /* closes a panel (deletes last window) */
578 n_stack--;
579 delwin(w_stack[n_stack]);
581 w_stack = realloc(w_stack, n_stack * sizeof(WINDOW *));
582 cw = n_stack == 0 ? stdscr : w_stack[n_stack - 1];
584 touchwin(cw);
585 wrefresh(cw);
587 return NULL;
591 static void register_functions(void)
593 mpdm_t drv;
594 mpdm_t tui;
596 drv = mpdm_hget_s(mp, L"drv");
597 mpdm_hset_s(drv, L"timer", MPDM_X(ncursesw_drv_timer));
598 mpdm_hset_s(drv, L"main_loop", MPDM_X(ncursesw_drv_main_loop));
599 mpdm_hset_s(drv, L"shutdown", MPDM_X(ncursesw_drv_shutdown));
601 tui = mpsl_eval(MPDM_LS(L"load('mp_tui.mpsl');"), NULL);
603 /* FIXME: if tui failed, a fatal error must be shown */
605 /* if((e = mpdm_hget_s(mpdm_root(), L"ERROR")) != NULL)
607 mpdm_write_wcs(stdout, mpdm_string(e));
608 printf("\n");
610 return(0);
613 /* execute tui */
614 mpdm_hset_s(tui, L"getkey", MPDM_X(nc_getkey));
615 mpdm_hset_s(tui, L"addstr", MPDM_X(tui_addstr));
616 mpdm_hset_s(tui, L"move", MPDM_X(tui_move));
617 mpdm_hset_s(tui, L"attr", MPDM_X(tui_attr));
618 mpdm_hset_s(tui, L"refresh", MPDM_X(tui_refresh));
619 mpdm_hset_s(tui, L"getxy", MPDM_X(tui_getxy));
620 mpdm_hset_s(tui, L"openpanel", MPDM_X(tui_openpanel));
621 mpdm_hset_s(tui, L"closepanel", MPDM_X(tui_closepanel));
625 static mpdm_t ncursesw_drv_startup(mpdm_t a)
627 mpdm_t v;
629 register_functions();
631 initscr();
632 start_color();
633 keypad(stdscr, TRUE);
634 nonl();
635 raw();
636 noecho();
638 build_colors();
640 v = mpdm_hget_s(mp, L"window");
641 mpdm_hset_s(v, L"tx", MPDM_I(COLS));
642 mpdm_hset_s(v, L"ty", MPDM_I(LINES));
644 signal(SIGWINCH, nc_sigwinch);
646 cw = stdscr;
648 return NULL;
652 int ncursesw_drv_detect(int * argc, char *** argv)
654 mpdm_t drv;
656 drv = mpdm_hget_s(mp, L"drv");
657 mpdm_hset_s(drv, L"id", MPDM_LS(L"curses"));
658 mpdm_hset_s(drv, L"startup", MPDM_X(ncursesw_drv_startup));
660 return 1;
663 #endif /* CONFOPT_CURSES */