terminal: fix up horizontal scan-line characters
[profile/ivi/weston.git] / clients / terminal.c
1 /*
2  * Copyright © 2008 Kristian Høgsberg
3  *
4  * Permission to use, copy, modify, distribute, and sell this software and its
5  * documentation for any purpose is hereby granted without fee, provided that
6  * the above copyright notice appear in all copies and that both that copyright
7  * notice and this permission notice appear in supporting documentation, and
8  * that the name of the copyright holders not be used in advertising or
9  * publicity pertaining to distribution of the software without specific,
10  * written prior permission.  The copyright holders make no representations
11  * about the suitability of this software for any purpose.  It is provided "as
12  * is" without express or implied warranty.
13  *
14  * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
15  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
16  * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
17  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
18  * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
19  * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
20  * OF THIS SOFTWARE.
21  */
22
23 #include <stdint.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <fcntl.h>
28 #include <unistd.h>
29 #include <math.h>
30 #include <time.h>
31 #include <pty.h>
32 #include <ctype.h>
33 #include <cairo.h>
34 #include <sys/epoll.h>
35
36 #include <wayland-client.h>
37
38 #include "window.h"
39
40 static int option_fullscreen;
41
42 #define MOD_SHIFT       0x01
43 #define MOD_ALT         0x02
44 #define MOD_CTRL        0x04
45
46 #define ATTRMASK_BOLD           0x01
47 #define ATTRMASK_UNDERLINE      0x02
48 #define ATTRMASK_BLINK          0x04
49 #define ATTRMASK_INVERSE        0x08
50 #define ATTRMASK_CONCEALED      0x10
51
52 /* Buffer sizes */
53 #define MAX_RESPONSE            256
54 #define MAX_ESCAPE              255
55
56 /* Terminal modes */
57 #define MODE_SHOW_CURSOR        0x00000001
58 #define MODE_INVERSE            0x00000002
59 #define MODE_AUTOWRAP           0x00000004
60 #define MODE_AUTOREPEAT         0x00000008
61 #define MODE_LF_NEWLINE         0x00000010
62 #define MODE_IRM                0x00000020
63 #define MODE_DELETE_SENDS_DEL   0x00000040
64 #define MODE_ALT_SENDS_ESC      0x00000080
65
66 union utf8_char {
67         unsigned char byte[4];
68         uint32_t ch;
69 };
70
71 enum utf8_state {
72         utf8state_start,
73         utf8state_accept,
74         utf8state_reject,
75         utf8state_expect3,
76         utf8state_expect2,
77         utf8state_expect1
78 };
79
80 struct utf8_state_machine {
81         enum utf8_state state;
82         int len;
83         union utf8_char s;
84 };
85
86 static void
87 init_state_machine(struct utf8_state_machine *machine)
88 {
89         machine->state = utf8state_start;
90         machine->len = 0;
91         machine->s.ch = 0;
92 }
93
94 static enum utf8_state
95 utf8_next_char(struct utf8_state_machine *machine, unsigned char c)
96 {
97         switch(machine->state) {
98         case utf8state_start:
99         case utf8state_accept:
100         case utf8state_reject:
101                 machine->s.ch = 0;
102                 machine->len = 0;
103                 if(c == 0xC0 || c == 0xC1) {
104                         /* overlong encoding, reject */
105                         machine->state = utf8state_reject;
106                 } else if((c & 0x80) == 0) {
107                         /* single byte, accept */
108                         machine->s.byte[machine->len++] = c;
109                         machine->state = utf8state_accept;
110                 } else if((c & 0xC0) == 0x80) {
111                         /* parser out of sync, ignore byte */
112                         machine->state = utf8state_start;
113                 } else if((c & 0xE0) == 0xC0) {
114                         /* start of two byte sequence */
115                         machine->s.byte[machine->len++] = c;
116                         machine->state = utf8state_expect1;
117                 } else if((c & 0xF0) == 0xE0) {
118                         /* start of three byte sequence */
119                         machine->s.byte[machine->len++] = c;
120                         machine->state = utf8state_expect2;
121                 } else if((c & 0xF8) == 0xF0) {
122                         /* start of four byte sequence */
123                         machine->s.byte[machine->len++] = c;
124                         machine->state = utf8state_expect3;
125                 } else {
126                         /* overlong encoding, reject */
127                         machine->state = utf8state_reject;
128                 }
129                 break;
130         case utf8state_expect3:
131                 machine->s.byte[machine->len++] = c;
132                 if((c & 0xC0) == 0x80) {
133                         /* all good, continue */
134                         machine->state = utf8state_expect2;
135                 } else {
136                         /* missing extra byte, reject */
137                         machine->state = utf8state_reject;
138                 }
139                 break;
140         case utf8state_expect2:
141                 machine->s.byte[machine->len++] = c;
142                 if((c & 0xC0) == 0x80) {
143                         /* all good, continue */
144                         machine->state = utf8state_expect1;
145                 } else {
146                         /* missing extra byte, reject */
147                         machine->state = utf8state_reject;
148                 }
149                 break;
150         case utf8state_expect1:
151                 machine->s.byte[machine->len++] = c;
152                 if((c & 0xC0) == 0x80) {
153                         /* all good, accept */
154                         machine->state = utf8state_accept;
155                 } else {
156                         /* missing extra byte, reject */
157                         machine->state = utf8state_reject;
158                 }
159                 break;
160         default:
161                 machine->state = utf8state_reject;
162                 break;
163         }
164         
165         return machine->state;
166 }
167
168 struct char_sub {
169         union utf8_char match;
170         union utf8_char replace;
171 };
172 /* Set last char_sub match to NULL char */
173 typedef struct char_sub *character_set;
174
175 struct char_sub CS_US[] = {
176         {{{0, }}, {{0, }}}
177 };
178 static struct char_sub CS_UK[] = {
179         {{{'#', 0, }}, {{0xC2, 0xA3, 0, }}},
180         {{{0, }}, {{0, }}}
181 };
182 static struct char_sub CS_SPECIAL[] = {
183         {{{'`', 0, }}, {{0xE2, 0x99, 0xA6, 0}}}, /* diamond */
184         {{{'a', 0, }}, {{0xE2, 0x96, 0x92, 0}}}, /* 50% cell */
185         {{{'b', 0, }}, {{0xE2, 0x90, 0x89, 0}}}, /* HT */
186         {{{'c', 0, }}, {{0xE2, 0x90, 0x8C, 0}}}, /* FF */
187         {{{'d', 0, }}, {{0xE2, 0x90, 0x8D, 0}}}, /* CR */
188         {{{'e', 0, }}, {{0xE2, 0x90, 0x8A, 0}}}, /* LF */
189         {{{'f', 0, }}, {{0xC2, 0xB0, 0, }}}, /* Degree */
190         {{{'g', 0, }}, {{0xC2, 0xB1, 0, }}}, /* Plus/Minus */
191         {{{'h', 0, }}, {{0xE2, 0x90, 0xA4, 0}}}, /* NL */
192         {{{'i', 0, }}, {{0xE2, 0x90, 0x8B, 0}}}, /* VT */
193         {{{'j', 0, }}, {{0xE2, 0x94, 0x98, 0}}}, /* CN_RB */
194         {{{'k', 0, }}, {{0xE2, 0x94, 0x90, 0}}}, /* CN_RT */
195         {{{'l', 0, }}, {{0xE2, 0x94, 0x8C, 0}}}, /* CN_LT */
196         {{{'m', 0, }}, {{0xE2, 0x94, 0x94, 0}}}, /* CN_RB */
197         {{{'n', 0, }}, {{0xE2, 0x94, 0xBC, 0}}}, /* CROSS */
198         {{{'o', 0, }}, {{0xE2, 0x8E, 0xBA, 0}}}, /* Horiz. Scan Line 1 */
199         {{{'p', 0, }}, {{0xE2, 0x8E, 0xBB, 0}}}, /* Horiz. Scan Line 3 */
200         {{{'q', 0, }}, {{0xE2, 0x94, 0x80, 0}}}, /* Horiz. Scan Line 5 */
201         {{{'r', 0, }}, {{0xE2, 0x8E, 0xBC, 0}}}, /* Horiz. Scan Line 7 */
202         {{{'s', 0, }}, {{0xE2, 0x8E, 0xBD, 0}}}, /* Horiz. Scan Line 9 */
203         {{{'t', 0, }}, {{0xE2, 0x94, 0x9C, 0}}}, /* TR */
204         {{{'u', 0, }}, {{0xE2, 0x94, 0xA4, 0}}}, /* TL */
205         {{{'v', 0, }}, {{0xE2, 0x94, 0xB4, 0}}}, /* TU */
206         {{{'w', 0, }}, {{0xE2, 0x94, 0xAC, 0}}}, /* TD */
207         {{{'x', 0, }}, {{0xE2, 0x94, 0x82, 0}}}, /* V */
208         {{{'y', 0, }}, {{0xE2, 0x89, 0xA4, 0}}}, /* LE */
209         {{{'z', 0, }}, {{0xE2, 0x89, 0xA5, 0}}}, /* GE */
210         {{{'{', 0, }}, {{0xCF, 0x80, 0, }}}, /* PI */
211         {{{'|', 0, }}, {{0xE2, 0x89, 0xA0, 0}}}, /* NEQ */
212         {{{'}', 0, }}, {{0xC2, 0xA3, 0, }}}, /* POUND */
213         {{{'~', 0, }}, {{0xE2, 0x8B, 0x85, 0}}}, /* DOT */
214         {{{0, }}, {{0, }}}
215 };
216
217 static void
218 apply_char_set(character_set cs, union utf8_char *utf8)
219 {
220         int i = 0;
221         
222         while (cs[i].match.byte[0]) {
223                 if ((*utf8).ch == cs[i].match.ch) {
224                         *utf8 = cs[i].replace;
225                         break;
226                 }
227                 i++;
228         }
229 }
230
231 struct key_map {
232         int sym;
233         int num;
234         char escape;
235         char code;
236 };
237 /* Set last key_sub sym to NULL */
238 typedef struct key_map *keyboard_mode;
239
240 static struct key_map KM_NORMAL[] = {
241         { XKB_KEY_Left,  1, '[', 'D' },
242         { XKB_KEY_Right, 1, '[', 'C' },
243         { XKB_KEY_Up,    1, '[', 'A' },
244         { XKB_KEY_Down,  1, '[', 'B' },
245         { XKB_KEY_Home,  1, '[', 'H' },
246         { XKB_KEY_End,   1, '[', 'F' },
247         { 0, 0, 0, 0 }
248 };
249 static struct key_map KM_APPLICATION[] = {
250         { XKB_KEY_Left,          1, 'O', 'D' },
251         { XKB_KEY_Right,         1, 'O', 'C' },
252         { XKB_KEY_Up,            1, 'O', 'A' },
253         { XKB_KEY_Down,          1, 'O', 'B' },
254         { XKB_KEY_Home,          1, 'O', 'H' },
255         { XKB_KEY_End,           1, 'O', 'F' },
256         { XKB_KEY_KP_Enter,      1, 'O', 'M' },
257         { XKB_KEY_KP_Multiply,   1, 'O', 'j' },
258         { XKB_KEY_KP_Add,        1, 'O', 'k' },
259         { XKB_KEY_KP_Separator,  1, 'O', 'l' },
260         { XKB_KEY_KP_Subtract,   1, 'O', 'm' },
261         { XKB_KEY_KP_Divide,     1, 'O', 'o' },
262         { 0, 0, 0, 0 }
263 };
264
265 static int
266 function_key_response(char escape, int num, uint32_t modifiers,
267                       char code, char *response)
268 {
269         int mod_num = 0;
270         int len;
271
272         if (modifiers & MOD_SHIFT_MASK) mod_num   |= 1;
273         if (modifiers & MOD_ALT_MASK) mod_num    |= 2;
274         if (modifiers & MOD_CONTROL_MASK) mod_num |= 4;
275
276         if (mod_num != 0)
277                 len = snprintf(response, MAX_RESPONSE, "\e[%d;%d%c",
278                                num, mod_num + 1, code);
279         else if (code != '~')
280                 len = snprintf(response, MAX_RESPONSE, "\e%c%c",
281                                escape, code);
282         else
283                 len = snprintf(response, MAX_RESPONSE, "\e%c%d%c",
284                                escape, num, code);
285
286         if (len >= MAX_RESPONSE)        return MAX_RESPONSE - 1;
287         else                            return len;
288 }
289
290 /* returns the number of bytes written into response,
291  * which must have room for MAX_RESPONSE bytes */
292 static int
293 apply_key_map(keyboard_mode mode, int sym, uint32_t modifiers, char *response)
294 {
295         struct key_map map;
296         int len = 0;
297         int i = 0;
298         
299         while (mode[i].sym) {
300                 map = mode[i++];
301                 if (sym == map.sym) {
302                         len = function_key_response(map.escape, map.num,
303                                                     modifiers, map.code,
304                                                     response);
305                         break;
306                 }
307         }
308         
309         return len;
310 }
311
312 struct terminal_color { double r, g, b, a; };
313 struct attr {
314         unsigned char fg, bg;
315         char a;        /* attributes format:
316                         * 76543210
317                         *    cilub */
318         char s;        /* in selection */
319 };
320 struct color_scheme {
321         struct terminal_color palette[16];
322         char border;
323         struct attr default_attr;
324 };
325
326 static void
327 attr_init(struct attr *data_attr, struct attr attr, int n)
328 {
329         int i;
330         for (i = 0; i < n; i++) {
331                 data_attr[i] = attr;
332         }
333 }
334
335 enum escape_state {
336         escape_state_normal = 0,
337         escape_state_escape,
338         escape_state_dcs,
339         escape_state_csi,
340         escape_state_osc,
341         escape_state_inner_escape,
342         escape_state_ignore,
343         escape_state_special
344 };
345
346 #define ESC_FLAG_WHAT   0x01
347 #define ESC_FLAG_GT     0x02
348 #define ESC_FLAG_BANG   0x04
349 #define ESC_FLAG_CASH   0x08
350 #define ESC_FLAG_SQUOTE 0x10
351 #define ESC_FLAG_DQUOTE 0x20
352 #define ESC_FLAG_SPACE  0x40
353
354 struct terminal {
355         struct window *window;
356         struct widget *widget;
357         struct display *display;
358         union utf8_char *data;
359         struct task io_task;
360         char *tab_ruler;
361         struct attr *data_attr;
362         struct attr curr_attr;
363         uint32_t mode;
364         char origin_mode;
365         char saved_origin_mode;
366         struct attr saved_attr;
367         union utf8_char last_char;
368         int margin_top, margin_bottom;
369         character_set cs, g0, g1;
370         character_set saved_cs, saved_g0, saved_g1;
371         keyboard_mode key_mode;
372         int data_pitch, attr_pitch;  /* The width in bytes of a line */
373         int width, height, start, row, column;
374         int saved_row, saved_column;
375         int fd, master;
376         uint32_t modifiers;
377         char escape[MAX_ESCAPE+1];
378         int escape_length;
379         enum escape_state state;
380         enum escape_state outer_state;
381         int escape_flags;
382         struct utf8_state_machine state_machine;
383         int margin;
384         int fullscreen;
385         int focused;
386         struct color_scheme *color_scheme;
387         struct terminal_color color_table[256];
388         cairo_font_extents_t extents;
389         cairo_scaled_font_t *font_normal, *font_bold;
390
391         struct wl_data_source *selection;
392         int32_t dragging;
393         int selection_start_x, selection_start_y;
394         int selection_end_x, selection_end_y;
395 };
396
397 /* Create default tab stops, every 8 characters */
398 static void
399 terminal_init_tabs(struct terminal *terminal)
400 {
401         int i = 0;
402         
403         while (i < terminal->width) {
404                 if (i % 8 == 0)
405                         terminal->tab_ruler[i] = 1;
406                 else
407                         terminal->tab_ruler[i] = 0;
408                 i++;
409         }
410 }
411
412 static void
413 terminal_init(struct terminal *terminal)
414 {
415         terminal->curr_attr = terminal->color_scheme->default_attr;
416         terminal->origin_mode = 0;
417         terminal->mode = MODE_SHOW_CURSOR |
418                          MODE_AUTOREPEAT |
419                          MODE_ALT_SENDS_ESC |
420                          MODE_AUTOWRAP;
421
422         terminal->row = 0;
423         terminal->column = 0;
424
425         terminal->g0 = CS_US;
426         terminal->g1 = CS_US;
427         terminal->cs = terminal->g0;
428         terminal->key_mode = KM_NORMAL;
429
430         terminal->saved_g0 = terminal->g0;
431         terminal->saved_g1 = terminal->g1;
432         terminal->saved_cs = terminal->cs;
433
434         terminal->saved_attr = terminal->curr_attr;
435         terminal->saved_origin_mode = terminal->origin_mode;
436         terminal->saved_row = terminal->row;
437         terminal->saved_column = terminal->column;
438
439         if (terminal->tab_ruler != NULL) terminal_init_tabs(terminal);
440 }
441
442 static void
443 init_color_table(struct terminal *terminal)
444 {
445         int c, r;
446         struct terminal_color *color_table = terminal->color_table;
447
448         for (c = 0; c < 256; c ++) {
449                 if (c < 16) {
450                         color_table[c] = terminal->color_scheme->palette[c];
451                 } else if (c < 232) {
452                         r = c - 16;
453                         color_table[c].b = ((double)(r % 6) / 6.0); r /= 6;
454                         color_table[c].g = ((double)(r % 6) / 6.0); r /= 6;
455                         color_table[c].r = ((double)(r % 6) / 6.0);
456                         color_table[c].a = 1.0;
457                 } else {
458                         r = (c - 232) * 10 + 8;
459                         color_table[c].r = ((double) r) / 256.0;
460                         color_table[c].g = color_table[c].r;
461                         color_table[c].b = color_table[c].r;
462                         color_table[c].a = 1.0;
463                 }
464         }
465 }
466
467 static union utf8_char *
468 terminal_get_row(struct terminal *terminal, int row)
469 {
470         int index;
471
472         index = (row + terminal->start) % terminal->height;
473
474         return &terminal->data[index * terminal->width];
475 }
476
477 static struct attr*
478 terminal_get_attr_row(struct terminal *terminal, int row)
479 {
480         int index;
481
482         index = (row + terminal->start) % terminal->height;
483
484         return &terminal->data_attr[index * terminal->width];
485 }
486
487 union decoded_attr {
488         struct attr attr;
489         uint32_t key;
490 };
491
492 static int
493 terminal_compare_position(struct terminal *terminal,
494                           int x, int y, int32_t ref_row, int32_t ref_col)
495 {
496         struct rectangle allocation;
497         int top_margin, side_margin, col, row, ref_x;
498
499         widget_get_allocation(terminal->widget, &allocation);
500         side_margin = allocation.x + (allocation.width - terminal->width * terminal->extents.max_x_advance) / 2;
501         top_margin = allocation.y + (allocation.height - terminal->height * terminal->extents.height) / 2;
502
503         col = (x - side_margin) / terminal->extents.max_x_advance;
504         row = (y - top_margin) / terminal->extents.height;
505
506         ref_x = side_margin + ref_col * terminal->extents.max_x_advance +
507                 terminal->extents.max_x_advance / 2;
508
509         if (row < ref_row)
510                 return -1;
511         if (row == ref_row) {
512                 if (col < ref_col)
513                         return -1;
514                 if (col == ref_col && x < ref_x)
515                         return -1;
516         }
517
518         return 1;
519 }
520
521 static void
522 terminal_decode_attr(struct terminal *terminal, int row, int col,
523                      union decoded_attr *decoded)
524 {
525         struct attr attr;
526         int foreground, background, tmp;
527         int start_cmp, end_cmp;
528
529         start_cmp =
530                 terminal_compare_position(terminal,
531                                           terminal->selection_start_x,
532                                           terminal->selection_start_y,
533                                           row, col);
534         end_cmp =
535                 terminal_compare_position(terminal,
536                                           terminal->selection_end_x,
537                                           terminal->selection_end_y,
538                                           row, col);
539         decoded->attr.s = 0;
540         if (start_cmp < 0 && end_cmp > 0)
541                 decoded->attr.s = 1;
542         else if (end_cmp < 0 && start_cmp > 0)
543                 decoded->attr.s = 1;
544
545         /* get the attributes for this character cell */
546         attr = terminal_get_attr_row(terminal, row)[col];
547         if ((attr.a & ATTRMASK_INVERSE) ||
548             decoded->attr.s ||
549             ((terminal->mode & MODE_SHOW_CURSOR) &&
550              terminal->focused && terminal->row == row &&
551              terminal->column == col)) {
552                 foreground = attr.bg;
553                 background = attr.fg;
554                 if (attr.a & ATTRMASK_BOLD) {
555                         if (foreground <= 16) foreground |= 0x08;
556                         if (background <= 16) background &= 0x07;
557                 }
558         } else {
559                 foreground = attr.fg;
560                 background = attr.bg;
561         }
562
563         if (terminal->mode & MODE_INVERSE) {
564                 tmp = foreground;
565                 foreground = background;
566                 background = tmp;
567                 if (attr.a & ATTRMASK_BOLD) {
568                         if (foreground <= 16) foreground |= 0x08;
569                         if (background <= 16) background &= 0x07;
570                 }
571         }
572
573         decoded->attr.fg = foreground;
574         decoded->attr.bg = background;
575         decoded->attr.a = attr.a;
576 }
577
578
579 static void
580 terminal_scroll_buffer(struct terminal *terminal, int d)
581 {
582         int i;
583
584         d = d % (terminal->height + 1);
585         terminal->start = (terminal->start + d) % terminal->height;
586         if (terminal->start < 0) terminal->start = terminal->height + terminal->start;
587         if(d < 0) {
588                 d = 0 - d;
589                 for(i = 0; i < d; i++) {
590                         memset(terminal_get_row(terminal, i), 0, terminal->data_pitch);
591                         attr_init(terminal_get_attr_row(terminal, i),
592                             terminal->curr_attr, terminal->width);
593                 }
594         } else {
595                 for(i = terminal->height - d; i < terminal->height; i++) {
596                         memset(terminal_get_row(terminal, i), 0, terminal->data_pitch);
597                         attr_init(terminal_get_attr_row(terminal, i),
598                             terminal->curr_attr, terminal->width);
599                 }
600         }
601 }
602
603 static void
604 terminal_scroll_window(struct terminal *terminal, int d)
605 {
606         int i;
607         int window_height;
608         int from_row, to_row;
609         
610         // scrolling range is inclusive
611         window_height = terminal->margin_bottom - terminal->margin_top + 1;
612         d = d % (window_height + 1);
613         if(d < 0) {
614                 d = 0 - d;
615                 to_row = terminal->margin_bottom;
616                 from_row = terminal->margin_bottom - d;
617                 
618                 for (i = 0; i < (window_height - d); i++) {
619                         memcpy(terminal_get_row(terminal, to_row - i),
620                                terminal_get_row(terminal, from_row - i),
621                                terminal->data_pitch);
622                         memcpy(terminal_get_attr_row(terminal, to_row - i),
623                                terminal_get_attr_row(terminal, from_row - i),
624                                terminal->attr_pitch);
625                 }
626                 for (i = terminal->margin_top; i < (terminal->margin_top + d); i++) {
627                         memset(terminal_get_row(terminal, i), 0, terminal->data_pitch);
628                         attr_init(terminal_get_attr_row(terminal, i),
629                                 terminal->curr_attr, terminal->width);
630                 }
631         } else {
632                 to_row = terminal->margin_top;
633                 from_row = terminal->margin_top + d;
634                 
635                 for (i = 0; i < (window_height - d); i++) {
636                         memcpy(terminal_get_row(terminal, to_row + i),
637                                terminal_get_row(terminal, from_row + i),
638                                terminal->data_pitch);
639                         memcpy(terminal_get_attr_row(terminal, to_row + i),
640                                terminal_get_attr_row(terminal, from_row + i),
641                                terminal->attr_pitch);
642                 }
643                 for (i = terminal->margin_bottom - d + 1; i <= terminal->margin_bottom; i++) {
644                         memset(terminal_get_row(terminal, i), 0, terminal->data_pitch);
645                         attr_init(terminal_get_attr_row(terminal, i),
646                                 terminal->curr_attr, terminal->width);
647                 }
648         }
649 }
650
651 static void
652 terminal_scroll(struct terminal *terminal, int d)
653 {
654         if(terminal->margin_top == 0 && terminal->margin_bottom == terminal->height - 1)
655                 terminal_scroll_buffer(terminal, d);
656         else
657                 terminal_scroll_window(terminal, d);
658 }
659
660 static void
661 terminal_shift_line(struct terminal *terminal, int d)
662 {
663         union utf8_char *row;
664         struct attr *attr_row;
665         
666         row = terminal_get_row(terminal, terminal->row);
667         attr_row = terminal_get_attr_row(terminal, terminal->row);
668
669         if ((terminal->width + d) <= terminal->column)
670                 d = terminal->column + 1 - terminal->width;
671         if ((terminal->column + d) >= terminal->width)
672                 d = terminal->width - terminal->column - 1;
673         
674         if (d < 0) {
675                 d = 0 - d;
676                 memmove(&row[terminal->column],
677                         &row[terminal->column + d],
678                         (terminal->width - terminal->column - d) * sizeof(union utf8_char));
679                 memmove(&attr_row[terminal->column], &attr_row[terminal->column + d],
680                         (terminal->width - terminal->column - d) * sizeof(struct attr));
681                 memset(&row[terminal->width - d], 0, d * sizeof(union utf8_char));
682                 attr_init(&attr_row[terminal->width - d], terminal->curr_attr, d);
683         } else {
684                 memmove(&row[terminal->column + d], &row[terminal->column],
685                         (terminal->width - terminal->column - d) * sizeof(union utf8_char));
686                 memmove(&attr_row[terminal->column + d], &attr_row[terminal->column],
687                         (terminal->width - terminal->column - d) * sizeof(struct attr));
688                 memset(&row[terminal->column], 0, d * sizeof(union utf8_char));
689                 attr_init(&attr_row[terminal->column], terminal->curr_attr, d);
690         }
691 }
692
693 static void
694 terminal_resize_cells(struct terminal *terminal, int width, int height)
695 {
696         size_t size;
697         union utf8_char *data;
698         struct attr *data_attr;
699         char *tab_ruler;
700         int data_pitch, attr_pitch;
701         int i, l, total_rows;
702         struct rectangle allocation;
703         struct winsize ws;
704
705         if (width < 1)
706                 width = 1;
707         if (height < 1)
708                 height = 1;
709         if (terminal->width == width && terminal->height == height)
710                 return;
711
712         data_pitch = width * sizeof(union utf8_char);
713         size = data_pitch * height;
714         data = malloc(size);
715         attr_pitch = width * sizeof(struct attr);
716         data_attr = malloc(attr_pitch * height);
717         tab_ruler = malloc(width);
718         memset(data, 0, size);
719         memset(tab_ruler, 0, width);
720         attr_init(data_attr, terminal->curr_attr, width * height);
721         if (terminal->data && terminal->data_attr) {
722                 if (width > terminal->width)
723                         l = terminal->width;
724                 else
725                         l = width;
726
727                 if (terminal->height > height) {
728                         total_rows = height;
729                 } else {
730                         total_rows = terminal->height;
731                 }
732
733                 for (i = 0; i < total_rows; i++) {
734                         memcpy(&data[width * i],
735                                terminal_get_row(terminal, i),
736                                l * sizeof(union utf8_char));
737                         memcpy(&data_attr[width * i],
738                                terminal_get_attr_row(terminal, i),
739                                l * sizeof(struct attr));
740                 }
741
742                 free(terminal->data);
743                 free(terminal->data_attr);
744                 free(terminal->tab_ruler);
745         }
746
747         terminal->data_pitch = data_pitch;
748         terminal->attr_pitch = attr_pitch;
749         terminal->margin_bottom =
750                 height - (terminal->height - terminal->margin_bottom);
751         terminal->width = width;
752         terminal->height = height;
753         terminal->data = data;
754         terminal->data_attr = data_attr;
755         terminal->tab_ruler = tab_ruler;
756         terminal_init_tabs(terminal);
757
758         /* Update the window size */
759         ws.ws_row = terminal->height;
760         ws.ws_col = terminal->width;
761         widget_get_allocation(terminal->widget, &allocation);
762         ws.ws_xpixel = allocation.width;
763         ws.ws_ypixel = allocation.height;
764         ioctl(terminal->master, TIOCSWINSZ, &ws);
765 }
766
767 static void
768 resize_handler(struct widget *widget,
769                int32_t width, int32_t height, void *data)
770 {
771         struct terminal *terminal = data;
772         int32_t columns, rows, m;
773
774         m = 2 * terminal->margin;
775         columns = (width - m) / (int32_t) terminal->extents.max_x_advance;
776         rows = (height - m) / (int32_t) terminal->extents.height;
777
778         if (!terminal->fullscreen) {
779                 width = columns * terminal->extents.max_x_advance + m;
780                 height = rows * terminal->extents.height + m;
781                 widget_set_size(terminal->widget, width, height);
782         }
783
784         terminal_resize_cells(terminal, columns, rows);
785 }
786
787 static void
788 terminal_resize(struct terminal *terminal, int columns, int rows)
789 {
790         int32_t width, height, m;
791
792         if (terminal->fullscreen)
793                 return;
794
795         m = 2 * terminal->margin;
796         width = columns * terminal->extents.max_x_advance + m;
797         height = rows * terminal->extents.height + m;
798         widget_schedule_resize(terminal->widget, width, height);
799 }
800
801 struct color_scheme DEFAULT_COLORS = {
802         {
803                 {0,    0,    0,    1}, /* black */
804                 {0.66, 0,    0,    1}, /* red */
805                 {0  ,  0.66, 0,    1}, /* green */
806                 {0.66, 0.33, 0,    1}, /* orange (nicer than muddy yellow) */
807                 {0  ,  0  ,  0.66, 1}, /* blue */
808                 {0.66, 0  ,  0.66, 1}, /* magenta */
809                 {0,    0.66, 0.66, 1}, /* cyan */
810                 {0.66, 0.66, 0.66, 1}, /* light grey */
811                 {0.22, 0.33, 0.33, 1}, /* dark grey */
812                 {1,    0.33, 0.33, 1}, /* high red */
813                 {0.33, 1,    0.33, 1}, /* high green */
814                 {1,    1,    0.33, 1}, /* high yellow */
815                 {0.33, 0.33, 1,    1}, /* high blue */
816                 {1,    0.33, 1,    1}, /* high magenta */
817                 {0.33, 1,    1,    1}, /* high cyan */
818                 {1,    1,    1,    1}  /* white */
819         },
820         0,                             /* black border */
821         {7, 0, 0, }                    /* bg:black (0), fg:light gray (7)  */
822 };
823
824 static void
825 terminal_set_color(struct terminal *terminal, cairo_t *cr, int index)
826 {
827         cairo_set_source_rgba(cr,
828                               terminal->color_table[index].r,
829                               terminal->color_table[index].g,
830                               terminal->color_table[index].b,
831                               terminal->color_table[index].a);
832 }
833
834 static void
835 terminal_send_selection(struct terminal *terminal, int fd)
836 {
837         int row, col;
838         union utf8_char *p_row;
839         union decoded_attr attr;
840         FILE *fp;
841         int len;
842
843         fp = fdopen(fd, "w");
844         for (row = 0; row < terminal->height; row++) {
845                 p_row = terminal_get_row(terminal, row);
846                 for (col = 0; col < terminal->width; col++) {
847                         /* get the attributes for this character cell */
848                         terminal_decode_attr(terminal, row, col, &attr);
849                         if (!attr.attr.s)
850                                 continue;
851                         len = strnlen((char *) p_row[col].byte, 4);
852                         fwrite(p_row[col].byte, 1, len, fp);
853                 }
854         }
855         fclose(fp);
856 }
857
858 struct glyph_run {
859         struct terminal *terminal;
860         cairo_t *cr;
861         unsigned int count;
862         union decoded_attr attr;
863         cairo_glyph_t glyphs[256], *g;
864 };
865
866 static void
867 glyph_run_init(struct glyph_run *run, struct terminal *terminal, cairo_t *cr)
868 {
869         run->terminal = terminal;
870         run->cr = cr;
871         run->g = run->glyphs;
872         run->count = 0;
873         run->attr.key = 0;
874 }
875
876 static void
877 glyph_run_flush(struct glyph_run *run, union decoded_attr attr)
878 {
879         cairo_scaled_font_t *font;
880
881         if (run->count > ARRAY_LENGTH(run->glyphs) - 10 ||
882             (attr.key != run->attr.key)) {
883                 if (run->attr.attr.a & (ATTRMASK_BOLD | ATTRMASK_BLINK))
884                         font = run->terminal->font_bold;
885                 else
886                         font = run->terminal->font_normal;
887                 cairo_set_scaled_font(run->cr, font);
888                 terminal_set_color(run->terminal, run->cr,
889                                    run->attr.attr.fg);
890
891                 if (!(run->attr.attr.a & ATTRMASK_CONCEALED))
892                         cairo_show_glyphs (run->cr, run->glyphs, run->count);
893                 run->g = run->glyphs;
894                 run->count = 0;
895         }
896         run->attr = attr;
897 }
898
899 static void
900 glyph_run_add(struct glyph_run *run, int x, int y, union utf8_char *c)
901 {
902         int num_glyphs;
903         cairo_scaled_font_t *font;
904
905         num_glyphs = ARRAY_LENGTH(run->glyphs) - run->count;
906
907         if (run->attr.attr.a & (ATTRMASK_BOLD | ATTRMASK_BLINK))
908                 font = run->terminal->font_bold;
909         else
910                 font = run->terminal->font_normal;
911
912         cairo_move_to(run->cr, x, y);
913         cairo_scaled_font_text_to_glyphs (font, x, y,
914                                           (char *) c->byte, 4,
915                                           &run->g, &num_glyphs,
916                                           NULL, NULL, NULL);
917         run->g += num_glyphs;
918         run->count += num_glyphs;
919 }
920
921
922 static void
923 redraw_handler(struct widget *widget, void *data)
924 {
925         struct terminal *terminal = data;
926         struct rectangle allocation;
927         cairo_t *cr;
928         int top_margin, side_margin;
929         int row, col;
930         union utf8_char *p_row;
931         union decoded_attr attr;
932         int text_x, text_y;
933         cairo_surface_t *surface;
934         double d;
935         struct glyph_run run;
936         cairo_font_extents_t extents;
937
938         surface = window_get_surface(terminal->window);
939         widget_get_allocation(terminal->widget, &allocation);
940         cr = cairo_create(surface);
941         cairo_rectangle(cr, allocation.x, allocation.y,
942                         allocation.width, allocation.height);
943         cairo_clip(cr);
944         cairo_push_group(cr);
945
946         cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
947         terminal_set_color(terminal, cr, terminal->color_scheme->border);
948         cairo_paint(cr);
949
950         cairo_set_scaled_font(cr, terminal->font_normal);
951
952         extents = terminal->extents;
953         side_margin = (allocation.width - terminal->width * extents.max_x_advance) / 2;
954         top_margin = (allocation.height - terminal->height * extents.height) / 2;
955
956         cairo_set_line_width(cr, 1.0);
957         cairo_translate(cr, allocation.x + side_margin,
958                         allocation.y + top_margin);
959         /* paint the background */
960         for (row = 0; row < terminal->height; row++) {
961                 for (col = 0; col < terminal->width; col++) {
962                         /* get the attributes for this character cell */
963                         terminal_decode_attr(terminal, row, col, &attr);
964
965                         if (attr.attr.bg == terminal->color_scheme->border)
966                                 continue;
967
968                         terminal_set_color(terminal, cr, attr.attr.bg);
969                         cairo_move_to(cr, col * extents.max_x_advance,
970                                       row * extents.height);
971                         cairo_rel_line_to(cr, extents.max_x_advance, 0);
972                         cairo_rel_line_to(cr, 0, extents.height);
973                         cairo_rel_line_to(cr, -extents.max_x_advance, 0);
974                         cairo_close_path(cr);
975                         cairo_fill(cr);
976                 }
977         }
978
979         cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
980
981         /* paint the foreground */
982         glyph_run_init(&run, terminal, cr);
983         for (row = 0; row < terminal->height; row++) {
984                 p_row = terminal_get_row(terminal, row);
985                 for (col = 0; col < terminal->width; col++) {
986                         /* get the attributes for this character cell */
987                         terminal_decode_attr(terminal, row, col, &attr);
988
989                         glyph_run_flush(&run, attr);
990
991                         text_x = col * extents.max_x_advance;
992                         text_y = extents.ascent + row * extents.height;
993                         if (attr.attr.a & ATTRMASK_UNDERLINE) {
994                                 terminal_set_color(terminal, cr, attr.attr.fg);
995                                 cairo_move_to(cr, text_x, (double)text_y + 1.5);
996                                 cairo_line_to(cr, text_x + extents.max_x_advance, (double) text_y + 1.5);
997                                 cairo_stroke(cr);
998                         }
999
1000                         glyph_run_add(&run, text_x, text_y, &p_row[col]);
1001                 }
1002         }
1003
1004         attr.key = ~0;
1005         glyph_run_flush(&run, attr);
1006
1007         if ((terminal->mode & MODE_SHOW_CURSOR) && !terminal->focused) {
1008                 d = 0.5;
1009
1010                 cairo_set_line_width(cr, 1);
1011                 cairo_move_to(cr, terminal->column * extents.max_x_advance + d,
1012                               terminal->row * extents.height + d);
1013                 cairo_rel_line_to(cr, extents.max_x_advance - 2 * d, 0);
1014                 cairo_rel_line_to(cr, 0, extents.height - 2 * d);
1015                 cairo_rel_line_to(cr, -extents.max_x_advance + 2 * d, 0);
1016                 cairo_close_path(cr);
1017
1018                 cairo_stroke(cr);
1019         }
1020
1021         cairo_pop_group_to_source(cr);
1022         cairo_paint(cr);
1023         cairo_destroy(cr);
1024         cairo_surface_destroy(surface);
1025 }
1026
1027 static void
1028 terminal_write(struct terminal *terminal, const char *data, size_t length)
1029 {
1030         if (write(terminal->master, data, length) < 0)
1031                 abort();
1032 }
1033
1034 static void
1035 terminal_data(struct terminal *terminal, const char *data, size_t length);
1036
1037 static void
1038 handle_char(struct terminal *terminal, union utf8_char utf8);
1039
1040 static void
1041 handle_sgr(struct terminal *terminal, int code);
1042
1043 static void
1044 handle_term_parameter(struct terminal *terminal, int code, int sr)
1045 {
1046         int i;
1047
1048         if (terminal->escape_flags & ESC_FLAG_WHAT) {
1049                 switch(code) {
1050                 case 1:  /* DECCKM */
1051                         if (sr) terminal->key_mode = KM_APPLICATION;
1052                         else    terminal->key_mode = KM_NORMAL;
1053                         break;
1054                 case 2:  /* DECANM */
1055                         /* No VT52 support yet */
1056                         terminal->g0 = CS_US;
1057                         terminal->g1 = CS_US;
1058                         terminal->cs = terminal->g0;
1059                         break;
1060                 case 3:  /* DECCOLM */
1061                         if (sr)
1062                                 terminal_resize(terminal, 132, 24);
1063                         else
1064                                 terminal_resize(terminal, 80, 24);
1065                         
1066                         /* set columns, but also home cursor and clear screen */
1067                         terminal->row = 0; terminal->column = 0;
1068                         for (i = 0; i < terminal->height; i++) {
1069                                 memset(terminal_get_row(terminal, i),
1070                                     0, terminal->data_pitch);
1071                                 attr_init(terminal_get_attr_row(terminal, i),
1072                                     terminal->curr_attr, terminal->width);
1073                         }
1074                         break;
1075                 case 5:  /* DECSCNM */
1076                         if (sr) terminal->mode |=  MODE_INVERSE;
1077                         else    terminal->mode &= ~MODE_INVERSE;
1078                         break;
1079                 case 6:  /* DECOM */
1080                         terminal->origin_mode = sr;
1081                         if (terminal->origin_mode)
1082                                 terminal->row = terminal->margin_top;
1083                         else
1084                                 terminal->row = 0;
1085                         terminal->column = 0;
1086                         break;
1087                 case 7:  /* DECAWM */
1088                         if (sr) terminal->mode |=  MODE_AUTOWRAP;
1089                         else    terminal->mode &= ~MODE_AUTOWRAP;
1090                         break;
1091                 case 8:  /* DECARM */
1092                         if (sr) terminal->mode |=  MODE_AUTOREPEAT;
1093                         else    terminal->mode &= ~MODE_AUTOREPEAT;
1094                         break;
1095                 case 25:
1096                         if (sr) terminal->mode |=  MODE_SHOW_CURSOR;
1097                         else    terminal->mode &= ~MODE_SHOW_CURSOR;
1098                         break;
1099                 case 1037:   /* deleteSendsDel */
1100                         if (sr) terminal->mode |=  MODE_DELETE_SENDS_DEL;
1101                         else    terminal->mode &= ~MODE_DELETE_SENDS_DEL;
1102                         break;
1103                 case 1039:   /* altSendsEscape */
1104                         if (sr) terminal->mode |=  MODE_ALT_SENDS_ESC;
1105                         else    terminal->mode &= ~MODE_ALT_SENDS_ESC;
1106                         break;
1107                 default:
1108                         fprintf(stderr, "Unknown parameter: ?%d\n", code);
1109                         break;
1110                 }
1111         } else {
1112                 switch(code) {
1113                 case 4:  /* IRM */
1114                         if (sr) terminal->mode |=  MODE_IRM;
1115                         else    terminal->mode &= ~MODE_IRM;
1116                         break;
1117                 case 20: /* LNM */
1118                         if (sr) terminal->mode |=  MODE_LF_NEWLINE;
1119                         else    terminal->mode &= ~MODE_LF_NEWLINE;
1120                         break;
1121                 default:
1122                         fprintf(stderr, "Unknown parameter: %d\n", code);
1123                         break;
1124                 }
1125         }
1126 }
1127
1128 static void
1129 handle_dcs(struct terminal *terminal)
1130 {
1131 }
1132
1133 static void
1134 handle_osc(struct terminal *terminal)
1135 {
1136         char *p;
1137         int code;
1138
1139         terminal->escape[terminal->escape_length++] = '\0';
1140         p = &terminal->escape[2];
1141         code = strtol(p, &p, 10);
1142         if (*p == ';') p++;
1143
1144         switch (code) {
1145         case 0: /* Icon name and window title */
1146         case 1: /* Icon label */
1147         case 2: /* Window title*/
1148                 window_set_title(terminal->window, p);
1149                 break;
1150         default:
1151                 fprintf(stderr, "Unknown OSC escape code %d\n", code);
1152                 break;
1153         }
1154 }
1155
1156 static void
1157 handle_escape(struct terminal *terminal)
1158 {
1159         union utf8_char *row;
1160         struct attr *attr_row;
1161         char *p;
1162         int i, count, x, y, top, bottom;
1163         int args[10], set[10] = { 0, };
1164         char response[MAX_RESPONSE] = {0, };
1165         struct rectangle allocation;
1166
1167         terminal->escape[terminal->escape_length++] = '\0';
1168         i = 0;
1169         p = &terminal->escape[2];
1170         while ((isdigit(*p) || *p == ';') && i < 10) {
1171                 if (*p == ';') {
1172                         if (!set[i]) {
1173                                 args[i] = 0;
1174                                 set[i] = 1;
1175                         }
1176                         p++;
1177                         i++;
1178                 } else {
1179                         args[i] = strtol(p, &p, 10);
1180                         set[i] = 1;
1181                 }
1182         }
1183         
1184         switch (*p) {
1185         case '@':    /* ICH */
1186                 count = set[0] ? args[0] : 1;
1187                 if (count == 0) count = 1;
1188                 terminal_shift_line(terminal, count);
1189                 break;
1190         case 'A':    /* CUU */
1191                 count = set[0] ? args[0] : 1;
1192                 if (count == 0) count = 1;
1193                 if (terminal->row - count >= terminal->margin_top)
1194                         terminal->row -= count;
1195                 else
1196                         terminal->row = terminal->margin_top;
1197                 break;
1198         case 'B':    /* CUD */
1199                 count = set[0] ? args[0] : 1;
1200                 if (count == 0) count = 1;
1201                 if (terminal->row + count <= terminal->margin_bottom)
1202                         terminal->row += count;
1203                 else
1204                         terminal->row = terminal->margin_bottom;
1205                 break;
1206         case 'C':    /* CUF */
1207                 count = set[0] ? args[0] : 1;
1208                 if (count == 0) count = 1;
1209                 if ((terminal->column + count) < terminal->width)
1210                         terminal->column += count;
1211                 else
1212                         terminal->column = terminal->width - 1;
1213                 break;
1214         case 'D':    /* CUB */
1215                 count = set[0] ? args[0] : 1;
1216                 if (count == 0) count = 1;
1217                 if ((terminal->column - count) >= 0)
1218                         terminal->column -= count;
1219                 else
1220                         terminal->column = 0;
1221                 break;
1222         case 'E':    /* CNL */
1223                 count = set[0] ? args[0] : 1;
1224                 if (terminal->row + count <= terminal->margin_bottom)
1225                         terminal->row += count;
1226                 else
1227                         terminal->row = terminal->margin_bottom;
1228                 terminal->column = 0;
1229                 break;
1230         case 'F':    /* CPL */
1231                 count = set[0] ? args[0] : 1;
1232                 if (terminal->row - count >= terminal->margin_top)
1233                         terminal->row -= count;
1234                 else
1235                         terminal->row = terminal->margin_top;
1236                 terminal->column = 0;
1237                 break;
1238         case 'G':    /* CHA */
1239                 y = set[0] ? args[0] : 1;
1240                 y = y <= 0 ? 1 : y > terminal->width ? terminal->width : y;
1241                 
1242                 terminal->column = y - 1;
1243                 break;
1244         case 'f':    /* HVP */
1245         case 'H':    /* CUP */
1246                 x = (set[1] ? args[1] : 1) - 1;
1247                 x = x < 0 ? 0 :
1248                     (x >= terminal->width ? terminal->width - 1 : x);
1249                 
1250                 y = (set[0] ? args[0] : 1) - 1;
1251                 if (terminal->origin_mode) {
1252                         y += terminal->margin_top;
1253                         y = y < terminal->margin_top ? terminal->margin_top :
1254                             (y > terminal->margin_bottom ? terminal->margin_bottom : y);
1255                 } else {
1256                         y = y < 0 ? 0 :
1257                             (y >= terminal->height ? terminal->height - 1 : y);
1258                 }
1259                 
1260                 terminal->row = y;
1261                 terminal->column = x;
1262                 break;
1263         case 'I':    /* CHT */
1264                 count = set[0] ? args[0] : 1;
1265                 if (count == 0) count = 1;
1266                 while (count > 0 && terminal->column < terminal->width) {
1267                         if (terminal->tab_ruler[terminal->column]) count--;
1268                         terminal->column++;
1269                 }
1270                 terminal->column--;
1271                 break;
1272         case 'J':    /* ED */
1273                 row = terminal_get_row(terminal, terminal->row);
1274                 attr_row = terminal_get_attr_row(terminal, terminal->row);
1275                 if (!set[0] || args[0] == 0 || args[0] > 2) {
1276                         memset(&row[terminal->column],
1277                                0, (terminal->width - terminal->column) * sizeof(union utf8_char));
1278                         attr_init(&attr_row[terminal->column],
1279                                terminal->curr_attr, terminal->width - terminal->column);
1280                         for (i = terminal->row + 1; i < terminal->height; i++) {
1281                                 memset(terminal_get_row(terminal, i),
1282                                     0, terminal->data_pitch);
1283                                 attr_init(terminal_get_attr_row(terminal, i),
1284                                     terminal->curr_attr, terminal->width);
1285                         }
1286                 } else if (args[0] == 1) {
1287                         memset(row, 0, (terminal->column+1) * sizeof(union utf8_char));
1288                         attr_init(attr_row, terminal->curr_attr, terminal->column+1);
1289                         for (i = 0; i < terminal->row; i++) {
1290                                 memset(terminal_get_row(terminal, i),
1291                                     0, terminal->data_pitch);
1292                                 attr_init(terminal_get_attr_row(terminal, i),
1293                                     terminal->curr_attr, terminal->width);
1294                         }
1295                 } else if (args[0] == 2) {
1296                         for (i = 0; i < terminal->height; i++) {
1297                                 memset(terminal_get_row(terminal, i),
1298                                     0, terminal->data_pitch);
1299                                 attr_init(terminal_get_attr_row(terminal, i),
1300                                     terminal->curr_attr, terminal->width);
1301                         }
1302                 }
1303                 break;
1304         case 'K':    /* EL */
1305                 row = terminal_get_row(terminal, terminal->row);
1306                 attr_row = terminal_get_attr_row(terminal, terminal->row);
1307                 if (!set[0] || args[0] == 0 || args[0] > 2) {
1308                         memset(&row[terminal->column], 0,
1309                             (terminal->width - terminal->column) * sizeof(union utf8_char));
1310                         attr_init(&attr_row[terminal->column], terminal->curr_attr,
1311                             terminal->width - terminal->column);
1312                 } else if (args[0] == 1) {
1313                         memset(row, 0, (terminal->column+1) * sizeof(union utf8_char));
1314                         attr_init(attr_row, terminal->curr_attr, terminal->column+1);
1315                 } else if (args[0] == 2) {
1316                         memset(row, 0, terminal->data_pitch);
1317                         attr_init(attr_row, terminal->curr_attr, terminal->width);
1318                 }
1319                 break;
1320         case 'L':    /* IL */
1321                 count = set[0] ? args[0] : 1;
1322                 if (count == 0) count = 1;
1323                 if (terminal->row >= terminal->margin_top &&
1324                         terminal->row < terminal->margin_bottom)
1325                 {
1326                         top = terminal->margin_top;
1327                         terminal->margin_top = terminal->row;
1328                         terminal_scroll(terminal, 0 - count);
1329                         terminal->margin_top = top;
1330                 } else if (terminal->row == terminal->margin_bottom) {
1331                         memset(terminal_get_row(terminal, terminal->row),
1332                                0, terminal->data_pitch);
1333                         attr_init(terminal_get_attr_row(terminal, terminal->row),
1334                                 terminal->curr_attr, terminal->width);
1335                 }
1336                 break;
1337         case 'M':    /* DL */
1338                 count = set[0] ? args[0] : 1;
1339                 if (count == 0) count = 1;
1340                 if (terminal->row >= terminal->margin_top &&
1341                         terminal->row < terminal->margin_bottom)
1342                 {
1343                         top = terminal->margin_top;
1344                         terminal->margin_top = terminal->row;
1345                         terminal_scroll(terminal, count);
1346                         terminal->margin_top = top;
1347                 } else if (terminal->row == terminal->margin_bottom) {
1348                         memset(terminal_get_row(terminal, terminal->row),
1349                                0, terminal->data_pitch);
1350                 }
1351                 break;
1352         case 'P':    /* DCH */
1353                 count = set[0] ? args[0] : 1;
1354                 if (count == 0) count = 1;
1355                 terminal_shift_line(terminal, 0 - count);
1356                 break;
1357         case 'S':    /* SU */
1358                 terminal_scroll(terminal, set[0] ? args[0] : 1);
1359                 break;
1360         case 'T':    /* SD */
1361                 terminal_scroll(terminal, 0 - (set[0] ? args[0] : 1));
1362                 break;
1363         case 'X':    /* ECH */
1364                 count = set[0] ? args[0] : 1;
1365                 if (count == 0) count = 1;
1366                 if ((terminal->column + count) > terminal->width)
1367                         count = terminal->width - terminal->column;
1368                 row = terminal_get_row(terminal, terminal->row);
1369                 attr_row = terminal_get_attr_row(terminal, terminal->row);
1370                 memset(&row[terminal->column], 0, count * sizeof(union utf8_char));
1371                 attr_init(&attr_row[terminal->column], terminal->curr_attr, count);
1372                 break;
1373         case 'Z':    /* CBT */
1374                 count = set[0] ? args[0] : 1;
1375                 if (count == 0) count = 1;
1376                 while (count > 0 && terminal->column >= 0) {
1377                         if (terminal->tab_ruler[terminal->column]) count--;
1378                         terminal->column--;
1379                 }
1380                 terminal->column++;
1381                 break;
1382         case '`':    /* HPA */
1383                 y = set[0] ? args[0] : 1;
1384                 y = y <= 0 ? 1 : y > terminal->width ? terminal->width : y;
1385                 
1386                 terminal->column = y - 1;
1387                 break;
1388         case 'b':    /* REP */
1389                 count = set[0] ? args[0] : 1;
1390                 if (count == 0) count = 1;
1391                 if (terminal->last_char.byte[0])
1392                         for (i = 0; i < count; i++)
1393                                 handle_char(terminal, terminal->last_char);
1394                 terminal->last_char.byte[0] = 0;
1395                 break;
1396         case 'c':    /* Primary DA */
1397                 terminal_write(terminal, "\e[?6c", 5);
1398                 break;
1399         case 'd':    /* VPA */
1400                 x = set[0] ? args[0] : 1;
1401                 x = x <= 0 ? 1 : x > terminal->height ? terminal->height : x;
1402                 
1403                 terminal->row = x - 1;
1404                 break;
1405         case 'g':    /* TBC */
1406                 if (!set[0] || args[0] == 0) {
1407                         terminal->tab_ruler[terminal->column] = 0;
1408                 } else if (args[0] == 3) {
1409                         memset(terminal->tab_ruler, 0, terminal->width);
1410                 }
1411                 break;
1412         case 'h':    /* SM */
1413                 for(i = 0; i < 10 && set[i]; i++) {
1414                         handle_term_parameter(terminal, args[i], 1);
1415                 }
1416                 break;
1417         case 'l':    /* RM */
1418                 for(i = 0; i < 10 && set[i]; i++) {
1419                         handle_term_parameter(terminal, args[i], 0);
1420                 }
1421                 break;
1422         case 'm':    /* SGR */
1423                 for(i = 0; i < 10; i++) {
1424                         if (i <= 7 && set[i] && set[i + 1] &&
1425                                 set[i + 2] && args[i + 1] == 5)
1426                         {
1427                                 if (args[i] == 38) {
1428                                         handle_sgr(terminal, args[i + 2] + 256);
1429                                         break;
1430                                 } else if (args[i] == 48) {
1431                                         handle_sgr(terminal, args[i + 2] + 512);
1432                                         break;
1433                                 }
1434                         }
1435                         if(set[i]) {
1436                                 handle_sgr(terminal, args[i]);
1437                         } else if(i == 0) {
1438                                 handle_sgr(terminal, 0);
1439                                 break;
1440                         } else {
1441                                 break;
1442                         }
1443                 }
1444                 break;
1445         case 'n':    /* DSR */
1446                 i = set[0] ? args[0] : 0;
1447                 if (i == 0 || i == 5) {
1448                         terminal_write(terminal, "\e[0n", 4);
1449                 } else if (i == 6) {
1450                         snprintf(response, MAX_RESPONSE, "\e[%d;%dR",
1451                                  terminal->origin_mode ?
1452                                      terminal->row+terminal->margin_top : terminal->row+1,
1453                                  terminal->column+1);
1454                         terminal_write(terminal, response, strlen(response));
1455                 }
1456                 break;
1457         case 'r':
1458                 if(!set[0]) {
1459                         terminal->margin_top = 0;
1460                         terminal->margin_bottom = terminal->height-1;
1461                         terminal->row = 0;
1462                         terminal->column = 0;
1463                 } else {
1464                         top = (set[0] ? args[0] : 1) - 1;
1465                         top = top < 0 ? 0 :
1466                               (top >= terminal->height ? terminal->height - 1 : top);
1467                         bottom = (set[1] ? args[1] : 1) - 1;
1468                         bottom = bottom < 0 ? 0 :
1469                                  (bottom >= terminal->height ? terminal->height - 1 : bottom);
1470                         if(bottom > top) {
1471                                 terminal->margin_top = top;
1472                                 terminal->margin_bottom = bottom;
1473                         } else {
1474                                 terminal->margin_top = 0;
1475                                 terminal->margin_bottom = terminal->height-1;
1476                         }
1477                         if(terminal->origin_mode)
1478                                 terminal->row = terminal->margin_top;
1479                         else
1480                                 terminal->row = 0;
1481                         terminal->column = 0;
1482                 }
1483                 break;
1484         case 's':
1485                 terminal->saved_row = terminal->row;
1486                 terminal->saved_column = terminal->column;
1487                 break;
1488         case 't':    /* windowOps */
1489                 if (!set[0]) break;
1490                 switch (args[0]) {
1491                 case 4:  /* resize px */
1492                         if (set[1] && set[2]) {
1493                                 widget_schedule_resize(terminal->widget,
1494                                                        args[2], args[1]);
1495                         }
1496                         break;
1497                 case 8:  /* resize ch */
1498                         if (set[1] && set[2]) {
1499                                 terminal_resize(terminal, args[2], args[1]);
1500                         }
1501                         break;
1502                 case 13: /* report position */
1503                         widget_get_allocation(terminal->widget, &allocation);
1504                         snprintf(response, MAX_RESPONSE, "\e[3;%d;%dt",
1505                                  allocation.x, allocation.y);
1506                         terminal_write(terminal, response, strlen(response));
1507                         break;
1508                 case 14: /* report px */
1509                         widget_get_allocation(terminal->widget, &allocation);
1510                         snprintf(response, MAX_RESPONSE, "\e[4;%d;%dt",
1511                                  allocation.height, allocation.width);
1512                         terminal_write(terminal, response, strlen(response));
1513                         break;
1514                 case 18: /* report ch */
1515                         snprintf(response, MAX_RESPONSE, "\e[9;%d;%dt",
1516                                  terminal->height, terminal->width);
1517                         terminal_write(terminal, response, strlen(response));
1518                         break;
1519                 case 21: /* report title */
1520                         snprintf(response, MAX_RESPONSE, "\e]l%s\e\\",
1521                                  window_get_title(terminal->window));
1522                         terminal_write(terminal, response, strlen(response));
1523                         break;
1524                 default:
1525                         if (args[0] >= 24)
1526                                 terminal_resize(terminal, terminal->width, args[0]);
1527                         else
1528                                 fprintf(stderr, "Unimplemented windowOp %d\n", args[0]);
1529                         break;
1530                 }
1531         case 'u':
1532                 terminal->row = terminal->saved_row;
1533                 terminal->column = terminal->saved_column;
1534                 break;
1535         default:
1536                 fprintf(stderr, "Unknown CSI escape: %c\n", *p);
1537                 break;
1538         }       
1539 }
1540
1541 static void
1542 handle_non_csi_escape(struct terminal *terminal, char code)
1543 {
1544         switch(code) {
1545         case 'M':    /* RI */
1546                 terminal->row -= 1;
1547                 if(terminal->row < terminal->margin_top) {
1548                         terminal->row = terminal->margin_top;
1549                         terminal_scroll(terminal, -1);
1550                 }
1551                 break;
1552         case 'E':    /* NEL */
1553                 terminal->column = 0;
1554                 // fallthrough
1555         case 'D':    /* IND */
1556                 terminal->row += 1;
1557                 if(terminal->row > terminal->margin_bottom) {
1558                         terminal->row = terminal->margin_bottom;
1559                         terminal_scroll(terminal, +1);
1560                 }
1561                 break;
1562         case 'c':    /* RIS */
1563                 terminal_init(terminal);
1564                 break;
1565         case 'H':    /* HTS */
1566                 terminal->tab_ruler[terminal->column] = 1;
1567                 break;
1568         case '7':    /* DECSC */
1569                 terminal->saved_row = terminal->row;
1570                 terminal->saved_column = terminal->column;
1571                 terminal->saved_attr = terminal->curr_attr;
1572                 terminal->saved_origin_mode = terminal->origin_mode;
1573                 terminal->saved_cs = terminal->cs;
1574                 terminal->saved_g0 = terminal->g0;
1575                 terminal->saved_g1 = terminal->g1;
1576                 break;
1577         case '8':    /* DECRC */
1578                 terminal->row = terminal->saved_row;
1579                 terminal->column = terminal->saved_column;
1580                 terminal->curr_attr = terminal->saved_attr;
1581                 terminal->origin_mode = terminal->saved_origin_mode;
1582                 terminal->cs = terminal->saved_cs;
1583                 terminal->g0 = terminal->saved_g0;
1584                 terminal->g1 = terminal->saved_g1;
1585                 break;
1586         case '=':    /* DECPAM */
1587                 terminal->key_mode = KM_APPLICATION;
1588                 break;
1589         case '>':    /* DECPNM */
1590                 terminal->key_mode = KM_NORMAL;
1591                 break;
1592         default:
1593                 fprintf(stderr, "Unknown escape code: %c\n", code);
1594                 break;
1595         }
1596 }
1597
1598 static void
1599 handle_special_escape(struct terminal *terminal, char special, char code)
1600 {
1601         int i, numChars;
1602
1603         if (special == '#') {
1604                 switch(code) {
1605                 case '8':
1606                         /* fill with 'E', no cheap way to do this */
1607                         memset(terminal->data, 0, terminal->data_pitch * terminal->height);
1608                         numChars = terminal->width * terminal->height;
1609                         for(i = 0; i < numChars; i++) {
1610                                 terminal->data[i].byte[0] = 'E';
1611                         }
1612                         break;
1613                 default:
1614                         fprintf(stderr, "Unknown HASH escape #%c\n", code);
1615                         break;
1616                 }
1617         } else if (special == '(' || special == ')') {
1618                 switch(code) {
1619                 case '0':
1620                         if (special == '(')
1621                                 terminal->g0 = CS_SPECIAL;
1622                         else
1623                                 terminal->g1 = CS_SPECIAL;
1624                         break;
1625                 case 'A':
1626                         if (special == '(')
1627                                 terminal->g0 = CS_UK;
1628                         else
1629                                 terminal->g1 = CS_UK;
1630                         break;
1631                 case 'B':
1632                         if (special == '(')
1633                                 terminal->g0 = CS_US;
1634                         else
1635                                 terminal->g1 = CS_US;
1636                         break;
1637                 default:
1638                         fprintf(stderr, "Unknown character set %c\n", code);
1639                         break;
1640                 }
1641         } else {
1642                 fprintf(stderr, "Unknown special escape %c%c\n", special, code);
1643         }
1644 }
1645
1646 static void
1647 handle_sgr(struct terminal *terminal, int code)
1648 {
1649         switch(code) {
1650         case 0:
1651                 terminal->curr_attr = terminal->color_scheme->default_attr;
1652                 break;
1653         case 1:
1654                 terminal->curr_attr.a |= ATTRMASK_BOLD;
1655                 if (terminal->curr_attr.fg < 8)
1656                         terminal->curr_attr.fg += 8;
1657                 break;
1658         case 4:
1659                 terminal->curr_attr.a |= ATTRMASK_UNDERLINE;
1660                 break;
1661         case 5:
1662                 terminal->curr_attr.a |= ATTRMASK_BLINK;
1663                 break;
1664         case 8:
1665                 terminal->curr_attr.a |= ATTRMASK_CONCEALED;
1666                 break;
1667         case 2:
1668         case 21:
1669         case 22:
1670                 terminal->curr_attr.a &= ~ATTRMASK_BOLD;
1671                 if (terminal->curr_attr.fg < 16 && terminal->curr_attr.fg >= 8)
1672                         terminal->curr_attr.fg -= 8;
1673                 break;
1674         case 24:
1675                 terminal->curr_attr.a &= ~ATTRMASK_UNDERLINE;
1676                 break;
1677         case 25:
1678                 terminal->curr_attr.a &= ~ATTRMASK_BLINK;
1679                 break;
1680         case 7:
1681         case 26:
1682                 terminal->curr_attr.a |= ATTRMASK_INVERSE;
1683                 break;
1684         case 27:
1685                 terminal->curr_attr.a &= ~ATTRMASK_INVERSE;
1686                 break;
1687         case 28:
1688                 terminal->curr_attr.a &= ~ATTRMASK_CONCEALED;
1689                 break;
1690         case 39:
1691                 terminal->curr_attr.fg = terminal->color_scheme->default_attr.fg;
1692                 break;
1693         case 49:
1694                 terminal->curr_attr.bg = terminal->color_scheme->default_attr.bg;
1695                 break;
1696         default:
1697                 if(code >= 30 && code <= 37) {
1698                         terminal->curr_attr.fg = code - 30;
1699                         if (terminal->curr_attr.a & ATTRMASK_BOLD)
1700                                 terminal->curr_attr.fg += 8;
1701                 } else if(code >= 40 && code <= 47) {
1702                         terminal->curr_attr.bg = code - 40;
1703                 } else if (code >= 90 && code <= 97) {
1704                         terminal->curr_attr.fg = code - 90 + 8;
1705                 } else if (code >= 100 && code <= 107) {
1706                         terminal->curr_attr.bg = code - 100 + 8;
1707                 } else if(code >= 256 && code < 512) {
1708                         terminal->curr_attr.fg = code - 256;
1709                 } else if(code >= 512 && code < 768) {
1710                         terminal->curr_attr.bg = code - 512;
1711                 } else {
1712                         fprintf(stderr, "Unknown SGR code: %d\n", code);
1713                 }
1714                 break;
1715         }
1716 }
1717
1718 /* Returns 1 if c was special, otherwise 0 */
1719 static int
1720 handle_special_char(struct terminal *terminal, char c)
1721 {
1722         union utf8_char *row;
1723         struct attr *attr_row;
1724         
1725         row = terminal_get_row(terminal, terminal->row);
1726         attr_row = terminal_get_attr_row(terminal, terminal->row);
1727         
1728         switch(c) {
1729         case '\r':
1730                 terminal->column = 0;
1731                 break;
1732         case '\n':
1733                 if (terminal->mode & MODE_LF_NEWLINE) {
1734                         terminal->column = 0;
1735                 }
1736                 /* fallthrough */
1737         case '\v':
1738         case '\f':
1739                 terminal->row++;
1740                 if(terminal->row > terminal->margin_bottom) {
1741                         terminal->row = terminal->margin_bottom;
1742                         terminal_scroll(terminal, +1);
1743                 }
1744
1745                 break;
1746         case '\t':
1747                 while (terminal->column < terminal->width) {
1748                         if (terminal->tab_ruler[terminal->column]) break;
1749                         if (terminal->mode & MODE_IRM)
1750                                 terminal_shift_line(terminal, +1);
1751                         row[terminal->column].byte[0] = ' ';
1752                         row[terminal->column].byte[1] = '\0';
1753                         attr_row[terminal->column] = terminal->curr_attr;
1754                         terminal->column++;
1755                 }
1756                 if (terminal->column >= terminal->width) {
1757                         terminal->column = terminal->width - 1;
1758                 }
1759
1760                 break;
1761         case '\b':
1762                 if (terminal->column >= terminal->width) {
1763                         terminal->column = terminal->width - 2;
1764                 } else if (terminal->column > 0) {
1765                         terminal->column--;
1766                 } else if (terminal->mode & MODE_AUTOWRAP) {
1767                         terminal->column = terminal->width - 1;
1768                         terminal->row -= 1;
1769                         if (terminal->row < terminal->margin_top) {
1770                                 terminal->row = terminal->margin_top;
1771                                 terminal_scroll(terminal, -1);
1772                         }
1773                 }
1774
1775                 break;
1776         case '\a':
1777                 /* Bell */
1778                 break;
1779         case '\x0E': /* SO */
1780                 terminal->cs = terminal->g1;
1781                 break;
1782         case '\x0F': /* SI */
1783                 terminal->cs = terminal->g0;
1784                 break;
1785         default:
1786                 return 0;
1787         }
1788         
1789         return 1;
1790 }
1791
1792 static void
1793 handle_char(struct terminal *terminal, union utf8_char utf8)
1794 {
1795         union utf8_char *row;
1796         struct attr *attr_row;
1797         
1798         if (handle_special_char(terminal, utf8.byte[0])) return;
1799
1800         apply_char_set(terminal->cs, &utf8);
1801         
1802         /* There are a whole lot of non-characters, control codes,
1803          * and formatting codes that should probably be ignored,
1804          * for example: */
1805         if (strncmp((char*) utf8.byte, "\xEF\xBB\xBF", 3) == 0) {
1806                 /* BOM, ignore */
1807                 return;
1808         } 
1809         
1810         /* Some of these non-characters should be translated, e.g.: */
1811         if (utf8.byte[0] < 32) {
1812                 utf8.byte[0] = utf8.byte[0] + 64;
1813         }
1814         
1815         /* handle right margin effects */
1816         if (terminal->column >= terminal->width) {
1817                 if (terminal->mode & MODE_AUTOWRAP) {
1818                         terminal->column = 0;
1819                         terminal->row += 1;
1820                         if (terminal->row > terminal->margin_bottom) {
1821                                 terminal->row = terminal->margin_bottom;
1822                                 terminal_scroll(terminal, +1);
1823                         }
1824                 } else {
1825                         terminal->column--;
1826                 }
1827         }
1828         
1829         row = terminal_get_row(terminal, terminal->row);
1830         attr_row = terminal_get_attr_row(terminal, terminal->row);
1831         
1832         if (terminal->mode & MODE_IRM)
1833                 terminal_shift_line(terminal, +1);
1834         row[terminal->column] = utf8;
1835         attr_row[terminal->column++] = terminal->curr_attr;
1836
1837         if (utf8.ch != terminal->last_char.ch)
1838                 terminal->last_char = utf8;
1839 }
1840
1841 static void
1842 escape_append_utf8(struct terminal *terminal, union utf8_char utf8)
1843 {
1844         int len, i;
1845
1846         if ((utf8.byte[0] & 0x80) == 0x00)       len = 1;
1847         else if ((utf8.byte[0] & 0xE0) == 0xC0)  len = 2;
1848         else if ((utf8.byte[0] & 0xF0) == 0xE0)  len = 3;
1849         else if ((utf8.byte[0] & 0xF8) == 0xF0)  len = 4;
1850         else                                     len = 1;  /* Invalid, cannot happen */
1851
1852         if (terminal->escape_length + len <= MAX_ESCAPE) {
1853                 for (i = 0; i < len; i++)
1854                         terminal->escape[terminal->escape_length + i] = utf8.byte[i];
1855                 terminal->escape_length += len;
1856         } else if (terminal->escape_length < MAX_ESCAPE) {
1857                 terminal->escape[terminal->escape_length++] = 0;
1858         }
1859 }
1860
1861 static void
1862 terminal_data(struct terminal *terminal, const char *data, size_t length)
1863 {
1864         unsigned int i;
1865         union utf8_char utf8;
1866         enum utf8_state parser_state;
1867
1868         for (i = 0; i < length; i++) {
1869                 parser_state =
1870                         utf8_next_char(&terminal->state_machine, data[i]);
1871                 switch(parser_state) {
1872                 case utf8state_accept:
1873                         utf8.ch = terminal->state_machine.s.ch;
1874                         break;
1875                 case utf8state_reject:
1876                         /* the unicode replacement character */
1877                         utf8.byte[0] = 0xEF;
1878                         utf8.byte[1] = 0xBF;
1879                         utf8.byte[2] = 0xBD;
1880                         utf8.byte[3] = 0x00;
1881                         break;
1882                 default:
1883                         continue;
1884                 }
1885
1886                 /* assume escape codes never use non-ASCII characters */
1887                 switch (terminal->state) {
1888                 case escape_state_escape:
1889                         escape_append_utf8(terminal, utf8);
1890                         switch (utf8.byte[0]) {
1891                         case 'P':  /* DCS */
1892                                 terminal->state = escape_state_dcs;
1893                                 break;
1894                         case '[':  /* CSI */
1895                                 terminal->state = escape_state_csi;
1896                                 break;
1897                         case ']':  /* OSC */
1898                                 terminal->state = escape_state_osc;
1899                                 break;
1900                         case '#':
1901                         case '(':
1902                         case ')':  /* special */
1903                                 terminal->state = escape_state_special;
1904                                 break;
1905                         case '^':  /* PM (not implemented) */
1906                         case '_':  /* APC (not implemented) */
1907                                 terminal->state = escape_state_ignore;
1908                                 break;
1909                         default:
1910                                 terminal->state = escape_state_normal;
1911                                 handle_non_csi_escape(terminal, utf8.byte[0]);
1912                                 break;
1913                         }
1914                         continue;
1915                 case escape_state_csi:
1916                         if (handle_special_char(terminal, utf8.byte[0]) != 0) {
1917                                 /* do nothing */
1918                         } else if (utf8.byte[0] == '?') {
1919                                 terminal->escape_flags |= ESC_FLAG_WHAT;
1920                         } else if (utf8.byte[0] == '>') {
1921                                 terminal->escape_flags |= ESC_FLAG_GT;
1922                         } else if (utf8.byte[0] == '!') {
1923                                 terminal->escape_flags |= ESC_FLAG_BANG;
1924                         } else if (utf8.byte[0] == '$') {
1925                                 terminal->escape_flags |= ESC_FLAG_CASH;
1926                         } else if (utf8.byte[0] == '\'') {
1927                                 terminal->escape_flags |= ESC_FLAG_SQUOTE;
1928                         } else if (utf8.byte[0] == '"') {
1929                                 terminal->escape_flags |= ESC_FLAG_DQUOTE;
1930                         } else if (utf8.byte[0] == ' ') {
1931                                 terminal->escape_flags |= ESC_FLAG_SPACE;
1932                         } else {
1933                                 escape_append_utf8(terminal, utf8);
1934                                 if (terminal->escape_length >= MAX_ESCAPE)
1935                                         terminal->state = escape_state_normal;
1936                         }
1937                         
1938                         if (isalpha(utf8.byte[0]) || utf8.byte[0] == '@' ||
1939                                 utf8.byte[0] == '`')
1940                         {
1941                                 terminal->state = escape_state_normal;
1942                                 handle_escape(terminal);
1943                         } else {
1944                         }
1945                         continue;
1946                 case escape_state_inner_escape:
1947                         if (utf8.byte[0] == '\\') {
1948                                 terminal->state = escape_state_normal;
1949                                 if (terminal->outer_state == escape_state_dcs) {
1950                                         handle_dcs(terminal);
1951                                 } else if (terminal->outer_state == escape_state_osc) {
1952                                         handle_osc(terminal);
1953                                 }
1954                         } else if (utf8.byte[0] == '\e') {
1955                                 terminal->state = terminal->outer_state;
1956                                 escape_append_utf8(terminal, utf8);
1957                                 if (terminal->escape_length >= MAX_ESCAPE)
1958                                         terminal->state = escape_state_normal;
1959                         } else {
1960                                 terminal->state = terminal->outer_state;
1961                                 if (terminal->escape_length < MAX_ESCAPE)
1962                                         terminal->escape[terminal->escape_length++] = '\e';
1963                                 escape_append_utf8(terminal, utf8);
1964                                 if (terminal->escape_length >= MAX_ESCAPE)
1965                                         terminal->state = escape_state_normal;
1966                         }
1967                         continue;
1968                 case escape_state_dcs:
1969                 case escape_state_osc:
1970                 case escape_state_ignore:
1971                         if (utf8.byte[0] == '\e') {
1972                                 terminal->outer_state = terminal->state;
1973                                 terminal->state = escape_state_inner_escape;
1974                         } else if (utf8.byte[0] == '\a' && terminal->state == escape_state_osc) {
1975                                 terminal->state = escape_state_normal;
1976                                 handle_osc(terminal);
1977                         } else {
1978                                 escape_append_utf8(terminal, utf8);
1979                                 if (terminal->escape_length >= MAX_ESCAPE)
1980                                         terminal->state = escape_state_normal;
1981                         }
1982                         continue;
1983                 case escape_state_special:
1984                         escape_append_utf8(terminal, utf8);
1985                         terminal->state = escape_state_normal;
1986                         if (isdigit(utf8.byte[0]) || isalpha(utf8.byte[0])) {
1987                                 handle_special_escape(terminal, terminal->escape[1],
1988                                                       utf8.byte[0]);
1989                         }
1990                         continue;
1991                 default:
1992                         break;
1993                 }
1994
1995                 /* this is valid, because ASCII characters are never used to
1996                  * introduce a multibyte sequence in UTF-8 */
1997                 if (utf8.byte[0] == '\e') {
1998                         terminal->state = escape_state_escape;
1999                         terminal->outer_state = escape_state_normal;
2000                         terminal->escape[0] = '\e';
2001                         terminal->escape_length = 1;
2002                         terminal->escape_flags = 0;
2003                 } else {
2004                         handle_char(terminal, utf8);
2005                 } /* if */
2006         } /* for */
2007
2008         window_schedule_redraw(terminal->window);
2009 }
2010
2011 static void
2012 data_source_target(void *data,
2013                    struct wl_data_source *source, const char *mime_type)
2014 {
2015         fprintf(stderr, "data_source_target, %s\n", mime_type);
2016 }
2017
2018 static void
2019 data_source_send(void *data,
2020                  struct wl_data_source *source,
2021                  const char *mime_type, int32_t fd)
2022 {
2023         struct terminal *terminal = data;
2024
2025         terminal_send_selection(terminal, fd);
2026 }
2027
2028 static void
2029 data_source_cancelled(void *data, struct wl_data_source *source)
2030 {
2031         wl_data_source_destroy(source);
2032 }
2033
2034 static const struct wl_data_source_listener data_source_listener = {
2035         data_source_target,
2036         data_source_send,
2037         data_source_cancelled
2038 };
2039
2040 static int
2041 handle_bound_key(struct terminal *terminal,
2042                  struct input *input, uint32_t sym, uint32_t time)
2043 {
2044         switch (sym) {
2045         case XKB_KEY_X:
2046                 /* Cut selection; terminal doesn't do cut, fall
2047                  * through to copy. */
2048         case XKB_KEY_C:
2049                 terminal->selection =
2050                         display_create_data_source(terminal->display);
2051                 wl_data_source_offer(terminal->selection,
2052                                      "text/plain;charset=utf-8");
2053                 wl_data_source_add_listener(terminal->selection,
2054                                             &data_source_listener, terminal);
2055                 input_set_selection(input, terminal->selection, time);
2056                 return 1;
2057         case XKB_KEY_V:
2058                 input_receive_selection_data_to_fd(input,
2059                                                    "text/plain;charset=utf-8",
2060                                                    terminal->master);
2061
2062                 return 1;
2063         default:
2064                 return 0;
2065         }
2066 }
2067
2068 static void
2069 key_handler(struct window *window, struct input *input, uint32_t time,
2070             uint32_t key, uint32_t sym, uint32_t state, void *data)
2071 {
2072         struct terminal *terminal = data;
2073         char ch[MAX_RESPONSE];
2074         uint32_t modifiers;
2075         int len = 0;
2076
2077         modifiers = input_get_modifiers(input);
2078         if ((modifiers & MOD_CONTROL_MASK) &&
2079             (modifiers & MOD_SHIFT_MASK) &&
2080             state && handle_bound_key(terminal, input, sym, time))
2081                 return;
2082
2083         switch (sym) {
2084         case XKB_KEY_F11:
2085                 if (!state)
2086                         break;
2087                 terminal->fullscreen ^= 1;
2088                 window_set_fullscreen(window, terminal->fullscreen);
2089                 break;
2090
2091         case XKB_KEY_BackSpace:
2092         case XKB_KEY_Tab:
2093         case XKB_KEY_Linefeed:
2094         case XKB_KEY_Clear:
2095         case XKB_KEY_Pause:
2096         case XKB_KEY_Scroll_Lock:
2097         case XKB_KEY_Sys_Req:
2098         case XKB_KEY_Escape:
2099                 ch[len++] = sym & 0x7f;
2100                 break;
2101
2102         case XKB_KEY_Return:
2103                 if (terminal->mode & MODE_LF_NEWLINE) {
2104                         ch[len++] = 0x0D;
2105                         ch[len++] = 0x0A;
2106                 } else {
2107                         ch[len++] = 0x0D;
2108                 }
2109                 break;
2110
2111         case XKB_KEY_Shift_L:
2112         case XKB_KEY_Shift_R:
2113         case XKB_KEY_Control_L:
2114         case XKB_KEY_Control_R:
2115         case XKB_KEY_Alt_L:
2116         case XKB_KEY_Alt_R:
2117                 break;
2118
2119         case XKB_KEY_Insert:
2120                 len = function_key_response('[', 2, modifiers, '~', ch);
2121                 break;
2122         case XKB_KEY_Delete:
2123                 if (terminal->mode & MODE_DELETE_SENDS_DEL) {
2124                         ch[len++] = '\x04';
2125                 } else {
2126                         len = function_key_response('[', 3, modifiers, '~', ch);
2127                 }
2128                 break;
2129         case XKB_KEY_Page_Up:
2130                 len = function_key_response('[', 5, modifiers, '~', ch);
2131                 break;
2132         case XKB_KEY_Page_Down:
2133                 len = function_key_response('[', 6, modifiers, '~', ch);
2134                 break;
2135         case XKB_KEY_F1:
2136                 len = function_key_response('O', 1, modifiers, 'P', ch);
2137                 break;
2138         case XKB_KEY_F2:
2139                 len = function_key_response('O', 1, modifiers, 'Q', ch);
2140                 break;
2141         case XKB_KEY_F3:
2142                 len = function_key_response('O', 1, modifiers, 'R', ch);
2143                 break;
2144         case XKB_KEY_F4:
2145                 len = function_key_response('O', 1, modifiers, 'S', ch);
2146                 break;
2147         case XKB_KEY_F5:
2148                 len = function_key_response('[', 15, modifiers, '~', ch);
2149                 break;
2150         case XKB_KEY_F6:
2151                 len = function_key_response('[', 17, modifiers, '~', ch);
2152                 break;
2153         case XKB_KEY_F7:
2154                 len = function_key_response('[', 18, modifiers, '~', ch);
2155                 break;
2156         case XKB_KEY_F8:
2157                 len = function_key_response('[', 19, modifiers, '~', ch);
2158                 break;
2159         case XKB_KEY_F9:
2160                 len = function_key_response('[', 20, modifiers, '~', ch);
2161                 break;
2162         case XKB_KEY_F10:
2163                 len = function_key_response('[', 21, modifiers, '~', ch);
2164                 break;
2165         case XKB_KEY_F12:
2166                 len = function_key_response('[', 24, modifiers, '~', ch);
2167                 break;
2168         default:
2169                 /* Handle special keys with alternate mappings */
2170                 len = apply_key_map(terminal->key_mode, sym, modifiers, ch);
2171                 if (len != 0) break;
2172                 
2173                 if (modifiers & MOD_CONTROL_MASK) {
2174                         if (sym >= '3' && sym <= '7')
2175                                 sym = (sym & 0x1f) + 8;
2176
2177                         if (!((sym >= '!' && sym <= '/') ||
2178                                 (sym >= '8' && sym <= '?') ||
2179                                 (sym >= '0' && sym <= '2'))) sym = sym & 0x1f;
2180                         else if (sym == '2') sym = 0x00;
2181                         else if (sym == '/') sym = 0x1F;
2182                         else if (sym == '8' || sym == '?') sym = 0x7F;
2183                 } else if ((terminal->mode & MODE_ALT_SENDS_ESC) && 
2184                            (modifiers & MOD_ALT_MASK))
2185                 {
2186                         ch[len++] = 0x1b;
2187                 } else if (modifiers & MOD_ALT_MASK) {
2188                         sym = sym | 0x80;
2189                 }
2190
2191                 if (sym < 256)
2192                         ch[len++] = sym;
2193                 break;
2194         }
2195
2196         if (state && len > 0)
2197                 terminal_write(terminal, ch, len);
2198 }
2199
2200 static void
2201 keyboard_focus_handler(struct window *window,
2202                        struct input *device, void *data)
2203 {
2204         struct terminal *terminal = data;
2205
2206         terminal->focused = (device != NULL);
2207         window_schedule_redraw(terminal->window);
2208 }
2209
2210 static void
2211 button_handler(struct widget *widget,
2212                struct input *input, uint32_t time,
2213                uint32_t button, uint32_t state, void *data)
2214 {
2215         struct terminal *terminal = data;
2216
2217         switch (button) {
2218         case 272:
2219                 if (state) {
2220                         terminal->dragging = 1;
2221                         input_get_position(input,
2222                                            &terminal->selection_start_x,
2223                                            &terminal->selection_start_y);
2224                         terminal->selection_end_x = terminal->selection_start_x;
2225                         terminal->selection_end_y = terminal->selection_start_y;
2226                         widget_schedule_redraw(widget);
2227                 } else {
2228                         terminal->dragging = 0;
2229                 }
2230                 break;
2231         }
2232 }
2233
2234 static int
2235 motion_handler(struct widget *widget,
2236                struct input *input, uint32_t time,
2237                float x, float y, void *data)
2238 {
2239         struct terminal *terminal = data;
2240
2241         if (terminal->dragging) {
2242                 input_get_position(input,
2243                                    &terminal->selection_end_x,
2244                                    &terminal->selection_end_y);
2245                 widget_schedule_redraw(widget);
2246         }
2247
2248         return CURSOR_IBEAM;
2249 }
2250
2251 static struct terminal *
2252 terminal_create(struct display *display, int fullscreen)
2253 {
2254         struct terminal *terminal;
2255         cairo_surface_t *surface;
2256         cairo_t *cr;
2257
2258         terminal = malloc(sizeof *terminal);
2259         if (terminal == NULL)
2260                 return terminal;
2261
2262         memset(terminal, 0, sizeof *terminal);
2263         terminal->fullscreen = fullscreen;
2264         terminal->color_scheme = &DEFAULT_COLORS;
2265         terminal_init(terminal);
2266         terminal->margin_top = 0;
2267         terminal->margin_bottom = -1;
2268         terminal->window = window_create(display);
2269         terminal->widget = frame_create(terminal->window, terminal);
2270         window_set_title(terminal->window, "Wayland Terminal");
2271         widget_set_transparent(terminal->widget, 0);
2272
2273         init_state_machine(&terminal->state_machine);
2274         init_color_table(terminal);
2275
2276         terminal->display = display;
2277         terminal->margin = 5;
2278
2279         window_set_user_data(terminal->window, terminal);
2280         window_set_key_handler(terminal->window, key_handler);
2281         window_set_keyboard_focus_handler(terminal->window,
2282                                           keyboard_focus_handler);
2283         widget_set_redraw_handler(terminal->widget, redraw_handler);
2284         widget_set_resize_handler(terminal->widget, resize_handler);
2285         widget_set_button_handler(terminal->widget, button_handler);
2286         widget_set_motion_handler(terminal->widget, motion_handler);
2287
2288         surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 0, 0);
2289         cr = cairo_create(surface);
2290         cairo_set_font_size(cr, 14);
2291         cairo_select_font_face (cr, "mono",
2292                                 CAIRO_FONT_SLANT_NORMAL,
2293                                 CAIRO_FONT_WEIGHT_BOLD);
2294         terminal->font_bold = cairo_get_scaled_font (cr);
2295         cairo_scaled_font_reference(terminal->font_bold);
2296
2297         cairo_select_font_face (cr, "mono",
2298                                 CAIRO_FONT_SLANT_NORMAL,
2299                                 CAIRO_FONT_WEIGHT_NORMAL);
2300         terminal->font_normal = cairo_get_scaled_font (cr);
2301         cairo_scaled_font_reference(terminal->font_normal);
2302
2303         cairo_font_extents(cr, &terminal->extents);
2304         cairo_destroy(cr);
2305         cairo_surface_destroy(surface);
2306
2307         window_schedule_resize(terminal->window, 500, 400);
2308
2309         return terminal;
2310 }
2311
2312 static void
2313 io_handler(struct task *task, uint32_t events)
2314 {
2315         struct terminal *terminal =
2316                 container_of(task, struct terminal, io_task);
2317         char buffer[256];
2318         int len;
2319
2320         if (events & EPOLLHUP)
2321                 exit(0);
2322
2323         len = read(terminal->master, buffer, sizeof buffer);
2324         if (len < 0)
2325                 exit(0);
2326
2327         terminal_data(terminal, buffer, len);
2328 }
2329
2330 static int
2331 terminal_run(struct terminal *terminal, const char *path)
2332 {
2333         int master;
2334         pid_t pid;
2335
2336         pid = forkpty(&master, NULL, NULL, NULL);
2337         if (pid == 0) {
2338                 setenv("TERM", "xterm-256color", 1);
2339                 setenv("COLORTERM", "xterm-256color", 1);
2340                 if (execl(path, path, NULL)) {
2341                         printf("exec failed: %m\n");
2342                         exit(EXIT_FAILURE);
2343                 }
2344         } else if (pid < 0) {
2345                 fprintf(stderr, "failed to fork and create pty (%m).\n");
2346                 return -1;
2347         }
2348
2349         terminal->master = master;
2350         fcntl(master, F_SETFL, O_NONBLOCK);
2351         terminal->io_task.run = io_handler;
2352         display_watch_fd(terminal->display, terminal->master,
2353                          EPOLLIN | EPOLLHUP, &terminal->io_task);
2354
2355         window_set_fullscreen(terminal->window, terminal->fullscreen);
2356         if (!terminal->fullscreen)
2357                 terminal_resize(terminal, 80, 24);
2358
2359         return 0;
2360 }
2361
2362 static const struct weston_option terminal_options[] = {
2363         { WESTON_OPTION_BOOLEAN, "fullscreen", 'f', &option_fullscreen },
2364 };
2365
2366 int main(int argc, char *argv[])
2367 {
2368         struct display *d;
2369         struct terminal *terminal;
2370         const char *shell;
2371
2372         argc = parse_options(terminal_options,
2373                              ARRAY_LENGTH(terminal_options), argc, argv);
2374
2375         d = display_create(argc, argv);
2376         if (d == NULL) {
2377                 fprintf(stderr, "failed to create display: %m\n");
2378                 return -1;
2379         }
2380
2381         shell = getenv("SHELL");
2382         if (!shell)
2383                 shell = "/bin/bash";
2384
2385         terminal = terminal_create(d, option_fullscreen);
2386         if (terminal_run(terminal, shell))
2387                 exit(EXIT_FAILURE);
2388
2389         display_run(d);
2390
2391         return 0;
2392 }