terminal: Escape sequences with string parameters
[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 <glib.h>
35
36 #include <X11/keysym.h>
37
38 #include "wayland-util.h"
39 #include "wayland-client.h"
40 #include "wayland-glib.h"
41
42 #include "window.h"
43
44 static int option_fullscreen;
45
46 #define MOD_SHIFT       0x01
47 #define MOD_ALT         0x02
48 #define MOD_CTRL        0x04
49
50 #define ATTRMASK_BOLD           0x01
51 #define ATTRMASK_UNDERLINE      0x02
52 #define ATTRMASK_BLINK          0x04
53 #define ATTRMASK_INVERSE        0x08
54 #define ATTRMASK_CONCEALED      0x10
55
56 /* Buffer sizes */
57 #define MAX_RESPONSE            11
58 #define MAX_ESCAPE              64
59
60 /* Terminal modes */
61 #define MODE_SHOW_CURSOR        0x00000001
62 #define MODE_INVERSE            0x00000002
63 #define MODE_AUTOWRAP           0x00000004
64 #define MODE_AUTOREPEAT         0x00000008
65 #define MODE_LF_NEWLINE         0x00000010
66 #define MODE_IRM                0x00000020
67 #define MODE_DELETE_SENDS_DEL   0x00000040
68 #define MODE_ALT_SENDS_ESC      0x00000080
69
70 union utf8_char {
71         unsigned char byte[4];
72         uint32_t ch;
73 };
74
75 enum utf8_state {
76         utf8state_start,
77         utf8state_accept,
78         utf8state_reject,
79         utf8state_expect3,
80         utf8state_expect2,
81         utf8state_expect1
82 };
83
84 struct utf8_state_machine {
85         enum utf8_state state;
86         int len;
87         union utf8_char s;
88 };
89
90 static void
91 init_state_machine(struct utf8_state_machine *machine)
92 {
93         machine->state = utf8state_start;
94         machine->len = 0;
95         machine->s.ch = 0;
96 }
97
98 static enum utf8_state
99 utf8_next_char(struct utf8_state_machine *machine, char c)
100 {
101         switch(machine->state) {
102         case utf8state_start:
103         case utf8state_accept:
104         case utf8state_reject:
105                 machine->s.ch = 0;
106                 machine->len = 0;
107                 if(c == 0xC0 || c == 0xC1) {
108                         /* overlong encoding, reject */
109                         machine->state = utf8state_reject;
110                 } else if((c & 0x80) == 0) {
111                         /* single byte, accept */
112                         machine->s.byte[machine->len++] = c;
113                         machine->state = utf8state_accept;
114                 } else if((c & 0xC0) == 0x80) {
115                         /* parser out of sync, ignore byte */
116                         machine->state = utf8state_start;
117                 } else if((c & 0xE0) == 0xC0) {
118                         /* start of two byte sequence */
119                         machine->s.byte[machine->len++] = c;
120                         machine->state = utf8state_expect1;
121                 } else if((c & 0xF0) == 0xE0) {
122                         /* start of three byte sequence */
123                         machine->s.byte[machine->len++] = c;
124                         machine->state = utf8state_expect2;
125                 } else if((c & 0xF8) == 0xF0) {
126                         /* start of four byte sequence */
127                         machine->s.byte[machine->len++] = c;
128                         machine->state = utf8state_expect3;
129                 } else {
130                         /* overlong encoding, reject */
131                         machine->state = utf8state_reject;
132                 }
133                 break;
134         case utf8state_expect3:
135                 machine->s.byte[machine->len++] = c;
136                 if((c & 0xC0) == 0x80) {
137                         /* all good, continue */
138                         machine->state = utf8state_expect2;
139                 } else {
140                         /* missing extra byte, reject */
141                         machine->state = utf8state_reject;
142                 }
143                 break;
144         case utf8state_expect2:
145                 machine->s.byte[machine->len++] = c;
146                 if((c & 0xC0) == 0x80) {
147                         /* all good, continue */
148                         machine->state = utf8state_expect1;
149                 } else {
150                         /* missing extra byte, reject */
151                         machine->state = utf8state_reject;
152                 }
153                 break;
154         case utf8state_expect1:
155                 machine->s.byte[machine->len++] = c;
156                 if((c & 0xC0) == 0x80) {
157                         /* all good, accept */
158                         machine->state = utf8state_accept;
159                 } else {
160                         /* missing extra byte, reject */
161                         machine->state = utf8state_reject;
162                 }
163                 break;
164         default:
165                 machine->state = utf8state_reject;
166                 break;
167         }
168         
169         return machine->state;
170 }
171
172 struct char_sub {
173         union utf8_char match;
174         union utf8_char replace;
175 };
176 /* Set last char_sub match to NULL char */
177 typedef struct char_sub *character_set;
178
179 struct char_sub CS_US[] = {
180         {{{0, }}, {{0, }}}
181 };
182 static struct char_sub CS_UK[] = {
183         {{{'#', 0, }}, {{0xC2, 0xA3, 0, }}},
184         {{{0, }}, {{0, }}}
185 };
186 static struct char_sub CS_SPECIAL[] = {
187         {{{'`', 0, }}, {{0xE2, 0x99, 0xA6, 0}}}, /* diamond */
188         {{{'a', 0, }}, {{0xE2, 0x96, 0x92, 0}}}, /* 50% cell */
189         {{{'b', 0, }}, {{0xE2, 0x90, 0x89, 0}}}, /* HT */
190         {{{'c', 0, }}, {{0xE2, 0x90, 0x8C, 0}}}, /* FF */
191         {{{'d', 0, }}, {{0xE2, 0x90, 0x8D, 0}}}, /* CR */
192         {{{'e', 0, }}, {{0xE2, 0x90, 0x8A, 0}}}, /* LF */
193         {{{'f', 0, }}, {{0xC2, 0xB0, 0, }}}, /* Degree */
194         {{{'g', 0, }}, {{0xC2, 0xB1, 0, }}}, /* Plus/Minus */
195         {{{'h', 0, }}, {{0xE2, 0x90, 0xA4, 0}}}, /* NL */
196         {{{'i', 0, }}, {{0xE2, 0x90, 0x8B, 0}}}, /* VT */
197         {{{'j', 0, }}, {{0xE2, 0x94, 0x98, 0}}}, /* CN_RB */
198         {{{'k', 0, }}, {{0xE2, 0x94, 0x90, 0}}}, /* CN_RT */
199         {{{'l', 0, }}, {{0xE2, 0x94, 0x8C, 0}}}, /* CN_LT */
200         {{{'m', 0, }}, {{0xE2, 0x94, 0x94, 0}}}, /* CN_RB */
201         {{{'n', 0, }}, {{0xE2, 0x94, 0xBC, 0}}}, /* CROSS */
202         {{{'o', 0, }}, {{0xE2, 0x94, 0x80, 0}}}, /* H */
203         {{{'p', 0, }}, {{0xE2, 0x94, 0x80, 0}}}, /* H */
204         {{{'q', 0, }}, {{0xE2, 0x94, 0x80, 0}}}, /* H */
205         {{{'r', 0, }}, {{0xE2, 0x94, 0x80, 0}}}, /* H */
206         {{{'s', 0, }}, {{0xE2, 0x94, 0x80, 0}}}, /* H */
207         {{{'t', 0, }}, {{0xE2, 0x94, 0x9C, 0}}}, /* TR */
208         {{{'u', 0, }}, {{0xE2, 0x94, 0xA4, 0}}}, /* TL */
209         {{{'v', 0, }}, {{0xE2, 0x94, 0xB4, 0}}}, /* TU */
210         {{{'w', 0, }}, {{0xE2, 0x94, 0xAC, 0}}}, /* TD */
211         {{{'x', 0, }}, {{0xE2, 0x94, 0x82, 0}}}, /* V */
212         {{{'y', 0, }}, {{0xE2, 0x89, 0xA4, 0}}}, /* LE */
213         {{{'z', 0, }}, {{0xE2, 0x89, 0xA5, 0}}}, /* GE */
214         {{{'{', 0, }}, {{0xCF, 0x80, 0, }}}, /* PI */
215         {{{'|', 0, }}, {{0xE2, 0x89, 0xA0, 0}}}, /* NEQ */
216         {{{'}', 0, }}, {{0xC2, 0xA3, 0, }}}, /* POUND */
217         {{{'~', 0, }}, {{0xE2, 0x8B, 0x85, 0}}}, /* DOT */
218         {{{0, }}, {{0, }}}
219 };
220
221 static void
222 apply_char_set(character_set cs, union utf8_char *utf8)
223 {
224         int i = 0;
225         
226         while (cs[i].match.byte[0]) {
227                 if ((*utf8).ch == cs[i].match.ch) {
228                         *utf8 = cs[i].replace;
229                         break;
230                 }
231                 i++;
232         }
233 }
234
235 struct key_map {
236         int sym;
237         int num;
238         char escape;
239         char code;
240 };
241 /* Set last key_sub sym to NULL */
242 typedef struct key_map *keyboard_mode;
243
244 static struct key_map KM_NORMAL[] = {
245         {XK_Left,  1, '[', 'D'},
246         {XK_Right, 1, '[', 'C'},
247         {XK_Up,    1, '[', 'A'},
248         {XK_Down,  1, '[', 'B'},
249         {XK_Home,  1, '[', 'H'},
250         {XK_End,   1, '[', 'F'},
251         {0, 0, 0, 0}
252 };
253 static struct key_map KM_APPLICATION[] = {
254         {XK_Left,          1, 'O', 'D'},
255         {XK_Right,         1, 'O', 'C'},
256         {XK_Up,            1, 'O', 'A'},
257         {XK_Down,          1, 'O', 'B'},
258         {XK_Home,          1, 'O', 'H'},
259         {XK_End,           1, 'O', 'F'},
260         {XK_KP_Enter,      1, 'O', 'M'},
261         {XK_KP_Multiply,   1, 'O', 'j'},
262         {XK_KP_Add,        1, 'O', 'k'},
263         {XK_KP_Separator,  1, 'O', 'l'},
264         {XK_KP_Subtract,   1, 'O', 'm'},
265         {XK_KP_Divide,     1, 'O', 'o'},
266         {0, 0, 0, 0}
267 };
268
269 static int
270 function_key_response(char escape, int num, uint32_t modifiers,
271                       char code, char *response)
272 {
273         int mod_num = 0;
274         int len;
275
276         if (modifiers & WINDOW_MODIFIER_SHIFT) mod_num   |= 1;
277         if (modifiers & WINDOW_MODIFIER_ALT) mod_num     |= 2;
278         if (modifiers & WINDOW_MODIFIER_CONTROL) mod_num |= 4;
279
280         if (mod_num != 0)
281                 len = snprintf(response, MAX_RESPONSE, "\e[%d;%d%c",
282                                num, mod_num + 1, code);
283         else if (code != '~')
284                 len = snprintf(response, MAX_RESPONSE, "\e%c%c",
285                                escape, code);
286         else
287                 len = snprintf(response, MAX_RESPONSE, "\e%c%d%c",
288                                escape, num, code);
289
290         if (len >= MAX_RESPONSE)        return MAX_RESPONSE - 1;
291         else                            return len;
292 }
293
294 /* returns the number of bytes written into response,
295  * which must have room for MAX_RESPONSE bytes */
296 static int
297 apply_key_map(keyboard_mode mode, int sym, uint32_t modifiers, char *response)
298 {
299         struct key_map map;
300         int len = 0;
301         int i = 0;
302         
303         while (mode[i].sym) {
304                 map = mode[i++];
305                 if (sym == map.sym) {
306                         len = function_key_response(map.escape, map.num,
307                                                     modifiers, map.code,
308                                                     response);
309                         break;
310                 }
311         }
312         
313         return len;
314 }
315
316 struct terminal_color { double r, g, b, a; };
317 struct attr {
318         unsigned char fg, bg;
319         char a;        /* attributes format:
320                         * 76543210
321                         *    cilub */
322         char r;        /* reserved */
323 };
324 struct color_scheme {
325         struct terminal_color palette[16];
326         struct terminal_color border;
327         struct attr default_attr;
328 };
329
330 static void
331 attr_init(struct attr *data_attr, struct attr attr, int n)
332 {
333         int i;
334         for (i = 0; i < n; i++) {
335                 data_attr[i] = attr;
336         }
337 }
338
339 enum escape_state {
340         escape_state_normal = 0,
341         escape_state_escape,
342         escape_state_dcs,
343         escape_state_csi,
344         escape_state_osc,
345         escape_state_inner_escape,
346         escape_state_ignore,
347         escape_state_special
348 };
349
350 #define ESC_FLAG_WHAT   0x01
351 #define ESC_FLAG_GT     0x02
352 #define ESC_FLAG_BANG   0x04
353 #define ESC_FLAG_CASH   0x08
354 #define ESC_FLAG_SQUOTE 0x10
355 #define ESC_FLAG_DQUOTE 0x20
356 #define ESC_FLAG_SPACE  0x40
357
358 struct terminal {
359         struct window *window;
360         struct display *display;
361         union utf8_char *data;
362         char *tab_ruler;
363         struct attr *data_attr;
364         struct attr curr_attr;
365         uint32_t mode;
366         char origin_mode;
367         char saved_origin_mode;
368         struct attr saved_attr;
369         union utf8_char last_char;
370         int margin_top, margin_bottom;
371         character_set cs, g0, g1;
372         character_set saved_cs, saved_g0, saved_g1;
373         keyboard_mode key_mode;
374         int data_pitch, attr_pitch;  /* The width in bytes of a line */
375         int width, height, start, row, column;
376         int saved_row, saved_column;
377         int fd, master;
378         GIOChannel *channel;
379         uint32_t modifiers;
380         char escape[MAX_ESCAPE];
381         int escape_length;
382         enum escape_state state;
383         enum escape_state outer_state;
384         int escape_flags;
385         struct utf8_state_machine state_machine;
386         int margin;
387         int fullscreen;
388         int focused;
389         struct color_scheme *color_scheme;
390         struct terminal_color color_table[256];
391         cairo_font_extents_t extents;
392         cairo_font_face_t *font_normal, *font_bold;
393 };
394
395 /* Create default tab stops, every 8 characters */
396 static void
397 terminal_init_tabs(struct terminal *terminal)
398 {
399         int i = 0;
400         
401         while (i < terminal->width) {
402                 if (i % 8 == 0)
403                         terminal->tab_ruler[i] = 1;
404                 else
405                         terminal->tab_ruler[i] = 0;
406                 i++;
407         }
408 }
409
410 static void
411 terminal_init(struct terminal *terminal)
412 {
413         terminal->curr_attr = terminal->color_scheme->default_attr;
414         terminal->origin_mode = 0;
415         terminal->mode = MODE_SHOW_CURSOR |
416                          MODE_AUTOREPEAT |
417                          MODE_ALT_SENDS_ESC |
418                          MODE_AUTOWRAP;
419
420         terminal->row = 0;
421         terminal->column = 0;
422
423         terminal->g0 = CS_US;
424         terminal->g1 = CS_US;
425         terminal->cs = terminal->g0;
426         terminal->key_mode = KM_NORMAL;
427
428         terminal->saved_g0 = terminal->g0;
429         terminal->saved_g1 = terminal->g1;
430         terminal->saved_cs = terminal->cs;
431
432         terminal->saved_attr = terminal->curr_attr;
433         terminal->saved_origin_mode = terminal->origin_mode;
434         terminal->saved_row = terminal->row;
435         terminal->saved_column = terminal->column;
436
437         if (terminal->tab_ruler != NULL) terminal_init_tabs(terminal);
438 }
439
440 static void
441 init_color_table(struct terminal *terminal)
442 {
443         int c, r;
444         struct terminal_color *color_table = terminal->color_table;
445
446         for (c = 0; c < 256; c ++) {
447                 if (c < 16) {
448                         color_table[c] = terminal->color_scheme->palette[c];
449                 } else if (c < 232) {
450                         r = c - 16;
451                         color_table[c].b = ((double)(r % 6) / 6.0); r /= 6;
452                         color_table[c].g = ((double)(r % 6) / 6.0); r /= 6;
453                         color_table[c].r = ((double)(r % 6) / 6.0);
454                         color_table[c].a = 1.0;
455                 } else {
456                         r = (c - 232) * 10 + 8;
457                         color_table[c].r = ((double) r) / 256.0;
458                         color_table[c].g = color_table[c].r;
459                         color_table[c].b = color_table[c].r;
460                         color_table[c].a = 1.0;
461                 }
462         }
463 }
464
465 static union utf8_char *
466 terminal_get_row(struct terminal *terminal, int row)
467 {
468         int index;
469
470         index = (row + terminal->start) % terminal->height;
471
472         return &terminal->data[index * terminal->width];
473 }
474
475 static struct attr*
476 terminal_get_attr_row(struct terminal *terminal, int row) {
477         int index;
478
479         index = (row + terminal->start) % terminal->height;
480
481         return &terminal->data_attr[index * terminal->width];
482 }
483
484 static struct attr
485 terminal_get_attr(struct terminal *terminal, int row, int col) {
486         return terminal_get_attr_row(terminal, row)[col];
487 }
488
489 static void
490 terminal_scroll_buffer(struct terminal *terminal, int d)
491 {
492         int i;
493
494         d = d % (terminal->height + 1);
495         terminal->start = (terminal->start + d) % terminal->height;
496         if (terminal->start < 0) terminal->start = terminal->height + terminal->start;
497         if(d < 0) {
498                 d = 0 - d;
499                 for(i = 0; i < d; i++) {
500                         memset(terminal_get_row(terminal, i), 0, terminal->data_pitch);
501                         attr_init(terminal_get_attr_row(terminal, i),
502                             terminal->curr_attr, terminal->width);
503                 }
504         } else {
505                 for(i = terminal->height - d; i < terminal->height; i++) {
506                         memset(terminal_get_row(terminal, i), 0, terminal->data_pitch);
507                         attr_init(terminal_get_attr_row(terminal, i),
508                             terminal->curr_attr, terminal->width);
509                 }
510         }
511 }
512
513 static void
514 terminal_scroll_window(struct terminal *terminal, int d)
515 {
516         int i;
517         int window_height;
518         int from_row, to_row;
519         
520         // scrolling range is inclusive
521         window_height = terminal->margin_bottom - terminal->margin_top + 1;
522         d = d % (window_height + 1);
523         if(d < 0) {
524                 d = 0 - d;
525                 to_row = terminal->margin_bottom;
526                 from_row = terminal->margin_bottom - d;
527                 
528                 for (i = 0; i < (window_height - d); i++) {
529                         memcpy(terminal_get_row(terminal, to_row - i),
530                                terminal_get_row(terminal, from_row - i),
531                                terminal->data_pitch);
532                         memcpy(terminal_get_attr_row(terminal, to_row - i),
533                                terminal_get_attr_row(terminal, from_row - i),
534                                terminal->attr_pitch);
535                 }
536                 for (i = terminal->margin_top; i < (terminal->margin_top + d); i++) {
537                         memset(terminal_get_row(terminal, i), 0, terminal->data_pitch);
538                         attr_init(terminal_get_attr_row(terminal, i),
539                                 terminal->curr_attr, terminal->width);
540                 }
541         } else {
542                 to_row = terminal->margin_top;
543                 from_row = terminal->margin_top + d;
544                 
545                 for (i = 0; i < (window_height - d); i++) {
546                         memcpy(terminal_get_row(terminal, to_row + i),
547                                terminal_get_row(terminal, from_row + i),
548                                terminal->data_pitch);
549                         memcpy(terminal_get_attr_row(terminal, to_row + i),
550                                terminal_get_attr_row(terminal, from_row + i),
551                                terminal->attr_pitch);
552                 }
553                 for (i = terminal->margin_bottom - d + 1; i <= terminal->margin_bottom; i++) {
554                         memset(terminal_get_row(terminal, i), 0, terminal->data_pitch);
555                         attr_init(terminal_get_attr_row(terminal, i),
556                                 terminal->curr_attr, terminal->width);
557                 }
558         }
559 }
560
561 static void
562 terminal_scroll(struct terminal *terminal, int d)
563 {
564         if(terminal->margin_top == 0 && terminal->margin_bottom == terminal->height - 1)
565                 terminal_scroll_buffer(terminal, d);
566         else
567                 terminal_scroll_window(terminal, d);
568 }
569
570 static void
571 terminal_shift_line(struct terminal *terminal, int d)
572 {
573         union utf8_char *row;
574         struct attr *attr_row, attr;
575         
576         row = terminal_get_row(terminal, terminal->row);
577         attr_row = terminal_get_attr_row(terminal, terminal->row);
578
579         if ((terminal->width + d) <= terminal->column)
580                 d = terminal->column + 1 - terminal->width;
581         if ((terminal->column + d) >= terminal->width)
582                 d = terminal->width - terminal->column - 1;
583         
584         if (d < 0) {
585                 d = 0 - d;
586                 memmove(&row[terminal->column],
587                         &row[terminal->column + d],
588                         (terminal->width - terminal->column - d) * sizeof(union utf8_char));
589                 attr = attr_row[terminal->width - 1];
590                 memmove(&attr_row[terminal->column], &attr_row[terminal->column + d],
591                         (terminal->width - terminal->column - d) * sizeof(struct attr));
592                 memset(&row[terminal->width - d], 0, d * sizeof(union utf8_char));
593                 attr_init(&attr_row[terminal->width - d], terminal->curr_attr, d);
594         } else {
595                 memmove(&row[terminal->column + d], &row[terminal->column],
596                         (terminal->width - terminal->column - d) * sizeof(union utf8_char));
597                 memmove(&attr_row[terminal->column + d], &attr_row[terminal->column],
598                         (terminal->width - terminal->column - d) * sizeof(struct attr));
599                 memset(&row[terminal->column], 0, d * sizeof(union utf8_char));
600                 attr_init(&attr_row[terminal->column], terminal->curr_attr, d);
601         }
602 }
603
604 static void
605 terminal_resize(struct terminal *terminal, int width, int height)
606 {
607         size_t size;
608         union utf8_char *data;
609         struct attr *data_attr;
610         char *tab_ruler;
611         int data_pitch, attr_pitch;
612         int i, l, total_rows, start;
613         struct rectangle rectangle;
614         struct winsize ws;
615
616         if (terminal->width == width && terminal->height == height)
617                 return;
618
619         data_pitch = width * sizeof(union utf8_char);
620         size = data_pitch * height;
621         data = malloc(size);
622         attr_pitch = width * sizeof(struct attr);
623         data_attr = malloc(attr_pitch * height);
624         tab_ruler = malloc(width);
625         memset(data, 0, size);
626         memset(tab_ruler, 0, width);
627         attr_init(data_attr, terminal->curr_attr, width * height);
628         if (terminal->data && terminal->data_attr) {
629                 if (width > terminal->width)
630                         l = terminal->width;
631                 else
632                         l = width;
633
634                 if (terminal->height > height) {
635                         total_rows = height;
636                         start = terminal->height - height;
637                 } else {
638                         total_rows = terminal->height;
639                         start = 0;
640                 }
641
642                 for (i = 0; i < total_rows; i++) {
643                         memcpy(&data[width * i],
644                                terminal_get_row(terminal, i),
645                                l * sizeof(union utf8_char));
646                         memcpy(&data_attr[width * i],
647                                terminal_get_attr_row(terminal, i),
648                                l * sizeof(struct attr));
649                 }
650
651                 free(terminal->data);
652                 free(terminal->data_attr);
653                 free(terminal->tab_ruler);
654         }
655
656         terminal->data_pitch = data_pitch;
657         terminal->attr_pitch = attr_pitch;
658         terminal->margin_bottom =
659                 height - (terminal->height - terminal->margin_bottom);
660         terminal->width = width;
661         terminal->height = height;
662         terminal->data = data;
663         terminal->data_attr = data_attr;
664         terminal->tab_ruler = tab_ruler;
665         terminal_init_tabs(terminal);
666
667         if (terminal->row >= terminal->height)
668                 terminal->row = terminal->height - 1;
669         if (terminal->column >= terminal->width)
670                 terminal->column = terminal->width - 1;
671         terminal->start = 0;
672         
673         if (!terminal->fullscreen) {
674                 rectangle.width = terminal->width *
675                         terminal->extents.max_x_advance + 2 * terminal->margin;
676                 rectangle.height = terminal->height *
677                         terminal->extents.height + 2 * terminal->margin;
678                 window_set_child_size(terminal->window, &rectangle);
679         }
680
681         /* Update the window size */
682         ws.ws_row = terminal->height;
683         ws.ws_col = terminal->width;
684         window_get_child_rectangle(terminal->window, &rectangle);
685         ws.ws_xpixel = rectangle.width;
686         ws.ws_ypixel = rectangle.height;
687         ioctl(terminal->master, TIOCSWINSZ, &ws);
688 }
689
690 struct color_scheme DEFAULT_COLORS = {
691         {
692                 {0,    0,    0,    1}, /* black */
693                 {0.66, 0,    0,    1}, /* red */
694                 {0  ,  0.66, 0,    1}, /* green */
695                 {0.66, 0.33, 0,    1}, /* orange (nicer than muddy yellow) */
696                 {0  ,  0  ,  0.66, 1}, /* blue */
697                 {0.66, 0  ,  0.66, 1}, /* magenta */
698                 {0,    0.66, 0.66, 1}, /* cyan */
699                 {0.66, 0.66, 0.66, 1}, /* light grey */
700                 {0.22, 0.33, 0.33, 1}, /* dark grey */
701                 {1,    0.33, 0.33, 1}, /* high red */
702                 {0.33, 1,    0.33, 1}, /* high green */
703                 {1,    1,    0.33, 1}, /* high yellow */
704                 {0.33, 0.33, 1,    1}, /* high blue */
705                 {1,    0.33, 1,    1}, /* high magenta */
706                 {0.33, 1,    1,    1}, /* high cyan */
707                 {1,    1,    1,    1}  /* white */
708         },
709         {0, 0, 0, 1},                  /* black border */
710         {7, 0, 0, }                    /* bg:black (0), fg:light gray (7)  */
711 };
712
713 static void
714 terminal_draw_contents(struct terminal *terminal)
715 {
716         struct rectangle rectangle;
717         cairo_t *cr;
718         cairo_font_extents_t extents;
719         int top_margin, side_margin;
720         int row, col;
721         struct attr attr;
722         union utf8_char *p_row;
723         struct utf8_chars {
724                 union utf8_char c;
725                 char null;
726         } toShow;
727         int foreground, background, bold, underline, concealed, tmp;
728         int text_x, text_y;
729         cairo_surface_t *surface;
730         double d;
731
732         toShow.null = 0;
733
734         window_get_child_rectangle(terminal->window, &rectangle);
735
736         surface = display_create_surface(terminal->display, &rectangle);
737         cr = cairo_create(surface);
738         cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
739         cairo_set_source_rgba(cr,
740                               terminal->color_scheme->border.r,
741                               terminal->color_scheme->border.g,
742                               terminal->color_scheme->border.b,
743                               terminal->color_scheme->border.a);
744         cairo_paint(cr);
745         cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
746
747         cairo_set_font_face(cr, terminal->font_normal);
748         cairo_set_font_size(cr, 14);
749
750         cairo_font_extents(cr, &extents);
751         side_margin = (rectangle.width - terminal->width * extents.max_x_advance) / 2;
752         top_margin = (rectangle.height - terminal->height * extents.height) / 2;
753
754         cairo_set_line_width(cr, 1.0);
755
756         for (row = 0; row < terminal->height; row++) {
757                 p_row = terminal_get_row(terminal, row);
758                 for (col = 0; col < terminal->width; col++) {
759                         /* get the attributes for this character cell */
760                         attr = terminal_get_attr(terminal, row, col);
761                         if ((attr.a & ATTRMASK_INVERSE) ||
762                                 ((terminal->mode & MODE_SHOW_CURSOR) &&
763                                 terminal->focused && terminal->row == row &&
764                                 terminal->column == col))
765                         {
766                                 foreground = attr.bg;
767                                 background = attr.fg;
768                                 if (attr.a & ATTRMASK_BOLD) {
769                                         if (foreground <= 16) foreground |= 0x08;
770                                         if (background <= 16) background &= 0x07;
771                                 }
772                         } else {
773                                 foreground = attr.fg;
774                                 background = attr.bg;
775                         }
776                         if (terminal->mode & MODE_INVERSE) {
777                                 tmp = foreground;
778                                 foreground = background;
779                                 background = tmp;
780                                 if (attr.a & ATTRMASK_BOLD) {
781                                         if (foreground <= 16) foreground |= 0x08;
782                                         if (background <= 16) background &= 0x07;
783                                 }
784                         }
785                         bold = attr.a & (ATTRMASK_BOLD | ATTRMASK_BLINK);
786                         underline = attr.a & ATTRMASK_UNDERLINE;
787                         concealed = attr.a & ATTRMASK_CONCEALED;
788
789                         /* paint the background */
790                         cairo_set_source_rgba(cr,
791                                               terminal->color_table[background].r,
792                                               terminal->color_table[background].g,
793                                               terminal->color_table[background].b,
794                                               terminal->color_table[background].a);
795                         cairo_move_to(cr, side_margin + (col * extents.max_x_advance),
796                               top_margin + (row * extents.height));
797                         cairo_rel_line_to(cr, extents.max_x_advance, 0);
798                         cairo_rel_line_to(cr, 0, extents.height);
799                         cairo_rel_line_to(cr, -extents.max_x_advance, 0);
800                         cairo_close_path(cr);
801                         cairo_fill(cr);
802
803                         /* paint the foreground */
804                         if (concealed) continue;
805                         if (bold)
806                                 cairo_set_font_face(cr, terminal->font_bold);
807                         else
808                                 cairo_set_font_face(cr, terminal->font_normal);
809                         cairo_set_source_rgba(cr,
810                                               terminal->color_table[foreground].r,
811                                               terminal->color_table[foreground].g,
812                                               terminal->color_table[foreground].b,
813                                               terminal->color_table[foreground].a);
814
815                         text_x = side_margin + col * extents.max_x_advance;
816                         text_y = top_margin + extents.ascent + row * extents.height;
817                         if (underline) {
818                                 cairo_move_to(cr, text_x, (double)text_y + 1.5);
819                                 cairo_line_to(cr, text_x + extents.max_x_advance, (double) text_y + 1.5);
820                                 cairo_stroke(cr);
821                         }
822                         cairo_move_to(cr, text_x, text_y);
823                         
824                         toShow.c = p_row[col];
825                         cairo_show_text(cr, (char *) toShow.c.byte);
826                 }
827         }
828
829         if ((terminal->mode & MODE_SHOW_CURSOR) && !terminal->focused) {
830                 d = 0.5;
831
832                 cairo_set_line_width(cr, 1);
833                 cairo_move_to(cr, side_margin + terminal->column * extents.max_x_advance + d,
834                               top_margin + terminal->row * extents.height + d);
835                 cairo_rel_line_to(cr, extents.max_x_advance - 2 * d, 0);
836                 cairo_rel_line_to(cr, 0, extents.height - 2 * d);
837                 cairo_rel_line_to(cr, -extents.max_x_advance + 2 * d, 0);
838                 cairo_close_path(cr);
839
840                 cairo_stroke(cr);
841         }
842
843         cairo_destroy(cr);
844
845         window_copy_surface(terminal->window,
846                             &rectangle,
847                             surface);
848
849         cairo_surface_destroy(surface);
850 }
851
852 static void
853 terminal_draw(struct terminal *terminal)
854 {
855         struct rectangle rectangle;
856         int32_t width, height;
857
858         window_get_child_rectangle(terminal->window, &rectangle);
859
860         width = (rectangle.width - 2 * terminal->margin) /
861                 (int32_t) terminal->extents.max_x_advance;
862         height = (rectangle.height - 2 * terminal->margin) /
863                 (int32_t) terminal->extents.height;
864
865         if (width < 0 || height < 0)
866                 return;
867
868         terminal_resize(terminal, width, height);
869
870         window_draw(terminal->window);
871         terminal_draw_contents(terminal);
872         window_flush(terminal->window);
873 }
874
875 static void
876 redraw_handler(struct window *window, void *data)
877 {
878         struct terminal *terminal = data;
879
880         terminal_draw(terminal);
881 }
882
883 static void
884 terminal_data(struct terminal *terminal, const char *data, size_t length);
885
886 static void
887 handle_char(struct terminal *terminal, union utf8_char utf8);
888
889 static void
890 handle_sgr(struct terminal *terminal, int code);
891
892 static void
893 handle_term_parameter(struct terminal *terminal, int code, int sr)
894 {
895         int i;
896
897         if (terminal->escape_flags & ESC_FLAG_WHAT) {
898                 switch(code) {
899                 case 1:  /* DECCKM */
900                         if (sr) terminal->key_mode = KM_APPLICATION;
901                         else    terminal->key_mode = KM_NORMAL;
902                         break;
903                 case 2:  /* DECANM */
904                         /* No VT52 support yet */
905                         terminal->g0 = CS_US;
906                         terminal->g1 = CS_US;
907                         terminal->cs = terminal->g0;
908                         break;
909                 case 3:  /* DECCOLM */
910                         if (sr)
911                                 terminal_resize(terminal, 132, 24);
912                         else
913                                 terminal_resize(terminal, 80, 24);
914                         
915                         /* set columns, but also home cursor and clear screen */
916                         terminal->row = 0; terminal->column = 0;
917                         for (i = 0; i < terminal->height; i++) {
918                                 memset(terminal_get_row(terminal, i),
919                                     0, terminal->data_pitch);
920                                 attr_init(terminal_get_attr_row(terminal, i),
921                                     terminal->curr_attr, terminal->width);
922                         }
923                         break;
924                 case 5:  /* DECSCNM */
925                         if (sr) terminal->mode |=  MODE_INVERSE;
926                         else    terminal->mode &= ~MODE_INVERSE;
927                         break;
928                 case 6:  /* DECOM */
929                         terminal->origin_mode = sr;
930                         if (terminal->origin_mode)
931                                 terminal->row = terminal->margin_top;
932                         else
933                                 terminal->row = 0;
934                         terminal->column = 0;
935                         break;
936                 case 7:  /* DECAWM */
937                         if (sr) terminal->mode |=  MODE_AUTOWRAP;
938                         else    terminal->mode &= ~MODE_AUTOWRAP;
939                         break;
940                 case 8:  /* DECARM */
941                         if (sr) terminal->mode |=  MODE_AUTOREPEAT;
942                         else    terminal->mode &= ~MODE_AUTOREPEAT;
943                         break;
944                 case 25:
945                         if (sr) terminal->mode |=  MODE_SHOW_CURSOR;
946                         else    terminal->mode &= ~MODE_SHOW_CURSOR;
947                         break;
948                 case 1037:   /* deleteSendsDel */
949                         if (sr) terminal->mode |=  MODE_DELETE_SENDS_DEL;
950                         else    terminal->mode &= ~MODE_DELETE_SENDS_DEL;
951                         break;
952                 case 1039:   /* altSendsEscape */
953                         if (sr) terminal->mode |=  MODE_ALT_SENDS_ESC;
954                         else    terminal->mode &= ~MODE_ALT_SENDS_ESC;
955                         break;
956                 default:
957                         fprintf(stderr, "Unknown parameter: ?%d\n", code);
958                         break;
959                 }
960         } else {
961                 switch(code) {
962                 case 4:  /* IRM */
963                         if (sr) terminal->mode |=  MODE_IRM;
964                         else    terminal->mode &= ~MODE_IRM;
965                         break;
966                 case 20: /* LNM */
967                         if (sr) terminal->mode |=  MODE_LF_NEWLINE;
968                         else    terminal->mode &= ~MODE_LF_NEWLINE;
969                         break;
970                 default:
971                         fprintf(stderr, "Unknown parameter: %d\n", code);
972                         break;
973                 }
974         }
975 }
976
977 static void
978 handle_dcs(struct terminal *terminal)
979 {
980 }
981
982 static void
983 handle_osc(struct terminal *terminal)
984 {
985 }
986
987 static void
988 handle_escape(struct terminal *terminal)
989 {
990         union utf8_char *row;
991         struct attr *attr_row;
992         char *p;
993         int i, count, x, y, top, bottom;
994         int args[10], set[10] = { 0, };
995         char response[MAX_RESPONSE] = {0, };
996
997         terminal->escape[terminal->escape_length++] = '\0';
998         i = 0;
999         p = &terminal->escape[2];
1000         while ((isdigit(*p) || *p == ';') && i < 10) {
1001                 if (*p == ';') {
1002                         if (!set[i]) {
1003                                 args[i] = 0;
1004                                 set[i] = 1;
1005                         }
1006                         p++;
1007                         i++;
1008                 } else {
1009                         args[i] = strtol(p, &p, 10);
1010                         set[i] = 1;
1011                 }
1012         }
1013         
1014         switch (*p) {
1015         case '@':    /* ICH */
1016                 count = set[0] ? args[0] : 1;
1017                 if (count == 0) count = 1;
1018                 terminal_shift_line(terminal, count);
1019                 break;
1020         case 'A':    /* CUU */
1021                 count = set[0] ? args[0] : 1;
1022                 if (count == 0) count = 1;
1023                 if (terminal->row - count >= terminal->margin_top)
1024                         terminal->row -= count;
1025                 else
1026                         terminal->row = terminal->margin_top;
1027                 break;
1028         case 'B':    /* CUD */
1029                 count = set[0] ? args[0] : 1;
1030                 if (count == 0) count = 1;
1031                 if (terminal->row + count <= terminal->margin_bottom)
1032                         terminal->row += count;
1033                 else
1034                         terminal->row = terminal->margin_bottom;
1035                 break;
1036         case 'C':    /* CUF */
1037                 count = set[0] ? args[0] : 1;
1038                 if (count == 0) count = 1;
1039                 if ((terminal->column + count) < terminal->width)
1040                         terminal->column += count;
1041                 else
1042                         terminal->column = terminal->width - 1;
1043                 break;
1044         case 'D':    /* CUB */
1045                 count = set[0] ? args[0] : 1;
1046                 if (count == 0) count = 1;
1047                 if ((terminal->column - count) >= 0)
1048                         terminal->column -= count;
1049                 else
1050                         terminal->column = 0;
1051                 break;
1052         case 'E':    /* CNL */
1053                 count = set[0] ? args[0] : 1;
1054                 if (terminal->row + count <= terminal->margin_bottom)
1055                         terminal->row += count;
1056                 else
1057                         terminal->row = terminal->margin_bottom;
1058                 terminal->column = 0;
1059                 break;
1060         case 'F':    /* CPL */
1061                 count = set[0] ? args[0] : 1;
1062                 if (terminal->row - count >= terminal->margin_top)
1063                         terminal->row -= count;
1064                 else
1065                         terminal->row = terminal->margin_top;
1066                 terminal->column = 0;
1067                 break;
1068         case 'G':    /* CHA */
1069                 y = set[0] ? args[0] : 1;
1070                 y = y <= 0 ? 1 : y > terminal->width ? terminal->width : y;
1071                 
1072                 terminal->column = y - 1;
1073                 break;
1074         case 'f':    /* HVP */
1075         case 'H':    /* CUP */
1076                 x = (set[1] ? args[1] : 1) - 1;
1077                 x = x < 0 ? 0 :
1078                     (x >= terminal->width ? terminal->width - 1 : x);
1079                 
1080                 y = (set[0] ? args[0] : 1) - 1;
1081                 if (terminal->origin_mode) {
1082                         y += terminal->margin_top;
1083                         y = y < terminal->margin_top ? terminal->margin_top :
1084                             (y > terminal->margin_bottom ? terminal->margin_bottom : y);
1085                 } else {
1086                         y = y < 0 ? 0 :
1087                             (y >= terminal->height ? terminal->height - 1 : y);
1088                 }
1089                 
1090                 terminal->row = y;
1091                 terminal->column = x;
1092                 break;
1093         case 'I':    /* CHT */
1094                 count = set[0] ? args[0] : 1;
1095                 if (count == 0) count = 1;
1096                 while (count > 0 && terminal->column < terminal->width) {
1097                         if (terminal->tab_ruler[terminal->column]) count--;
1098                         terminal->column++;
1099                 }
1100                 terminal->column--;
1101                 break;
1102         case 'J':    /* ED */
1103                 row = terminal_get_row(terminal, terminal->row);
1104                 attr_row = terminal_get_attr_row(terminal, terminal->row);
1105                 if (!set[0] || args[0] == 0 || args[0] > 2) {
1106                         memset(&row[terminal->column],
1107                                0, (terminal->width - terminal->column) * sizeof(union utf8_char));
1108                         attr_init(&attr_row[terminal->column],
1109                                terminal->curr_attr, terminal->width - terminal->column);
1110                         for (i = terminal->row + 1; i < terminal->height; i++) {
1111                                 memset(terminal_get_row(terminal, i),
1112                                     0, terminal->data_pitch);
1113                                 attr_init(terminal_get_attr_row(terminal, i),
1114                                     terminal->curr_attr, terminal->width);
1115                         }
1116                 } else if (args[0] == 1) {
1117                         memset(row, 0, (terminal->column+1) * sizeof(union utf8_char));
1118                         attr_init(attr_row, terminal->curr_attr, terminal->column+1);
1119                         for (i = 0; i < terminal->row; i++) {
1120                                 memset(terminal_get_row(terminal, i),
1121                                     0, terminal->data_pitch);
1122                                 attr_init(terminal_get_attr_row(terminal, i),
1123                                     terminal->curr_attr, terminal->width);
1124                         }
1125                 } else if (args[0] == 2) {
1126                         for (i = 0; i < terminal->height; i++) {
1127                                 memset(terminal_get_row(terminal, i),
1128                                     0, terminal->data_pitch);
1129                                 attr_init(terminal_get_attr_row(terminal, i),
1130                                     terminal->curr_attr, terminal->width);
1131                         }
1132                 }
1133                 break;
1134         case 'K':    /* EL */
1135                 row = terminal_get_row(terminal, terminal->row);
1136                 attr_row = terminal_get_attr_row(terminal, terminal->row);
1137                 if (!set[0] || args[0] == 0 || args[0] > 2) {
1138                         memset(&row[terminal->column], 0,
1139                             (terminal->width - terminal->column) * sizeof(union utf8_char));
1140                         attr_init(&attr_row[terminal->column], terminal->curr_attr,
1141                             terminal->width - terminal->column);
1142                 } else if (args[0] == 1) {
1143                         memset(row, 0, (terminal->column+1) * sizeof(union utf8_char));
1144                         attr_init(attr_row, terminal->curr_attr, terminal->column+1);
1145                 } else if (args[0] == 2) {
1146                         memset(row, 0, terminal->data_pitch);
1147                         attr_init(attr_row, terminal->curr_attr, terminal->width);
1148                 }
1149                 break;
1150         case 'L':    /* IL */
1151                 count = set[0] ? args[0] : 1;
1152                 if (count == 0) count = 1;
1153                 if (terminal->row >= terminal->margin_top &&
1154                         terminal->row < terminal->margin_bottom)
1155                 {
1156                         top = terminal->margin_top;
1157                         terminal->margin_top = terminal->row;
1158                         terminal_scroll(terminal, 0 - count);
1159                         terminal->margin_top = top;
1160                 } else if (terminal->row == terminal->margin_bottom) {
1161                         memset(terminal_get_row(terminal, terminal->row),
1162                                0, terminal->data_pitch);
1163                         attr_init(terminal_get_attr_row(terminal, terminal->row),
1164                                 terminal->curr_attr, terminal->width);
1165                 }
1166                 break;
1167         case 'M':    /* DL */
1168                 count = set[0] ? args[0] : 1;
1169                 if (count == 0) count = 1;
1170                 if (terminal->row >= terminal->margin_top &&
1171                         terminal->row < terminal->margin_bottom)
1172                 {
1173                         top = terminal->margin_top;
1174                         terminal->margin_top = terminal->row;
1175                         terminal_scroll(terminal, count);
1176                         terminal->margin_top = top;
1177                 } else if (terminal->row == terminal->margin_bottom) {
1178                         memset(terminal_get_row(terminal, terminal->row),
1179                                0, terminal->data_pitch);
1180                 }
1181                 break;
1182         case 'P':    /* DCH */
1183                 count = set[0] ? args[0] : 1;
1184                 if (count == 0) count = 1;
1185                 terminal_shift_line(terminal, 0 - count);
1186                 break;
1187         case 'S':    /* SU */
1188                 terminal_scroll(terminal, set[0] ? args[0] : 1);
1189                 break;
1190         case 'T':    /* SD */
1191                 terminal_scroll(terminal, 0 - (set[0] ? args[0] : 1));
1192                 break;
1193         case 'X':    /* ECH */
1194                 count = set[0] ? args[0] : 1;
1195                 if (count == 0) count = 1;
1196                 if ((terminal->column + count) > terminal->width)
1197                         count = terminal->width - terminal->column;
1198                 row = terminal_get_row(terminal, terminal->row);
1199                 attr_row = terminal_get_attr_row(terminal, terminal->row);
1200                 memset(&row[terminal->column], 0, count * sizeof(union utf8_char));
1201                 attr_init(&attr_row[terminal->column], terminal->curr_attr, count);
1202                 break;
1203         case 'Z':    /* CBT */
1204                 count = set[0] ? args[0] : 1;
1205                 if (count == 0) count = 1;
1206                 while (count > 0 && terminal->column >= 0) {
1207                         if (terminal->tab_ruler[terminal->column]) count--;
1208                         terminal->column--;
1209                 }
1210                 terminal->column++;
1211                 break;
1212         case '`':    /* HPA */
1213                 y = set[0] ? args[0] : 1;
1214                 y = y <= 0 ? 1 : y > terminal->width ? terminal->width : y;
1215                 
1216                 terminal->column = y - 1;
1217                 break;
1218         case 'b':    /* REP */
1219                 count = set[0] ? args[0] : 1;
1220                 if (count == 0) count = 1;
1221                 if (terminal->last_char.byte[0])
1222                         for (i = 0; i < count; i++)
1223                                 handle_char(terminal, terminal->last_char);
1224                 terminal->last_char.byte[0] = 0;
1225                 break;
1226         case 'c':    /* Primary DA */
1227                 write(terminal->master, "\e[?6c", 5);
1228                 break;
1229         case 'd':    /* VPA */
1230                 x = set[0] ? args[0] : 1;
1231                 x = x <= 0 ? 1 : x > terminal->height ? terminal->height : x;
1232                 
1233                 terminal->row = x - 1;
1234                 break;
1235         case 'g':    /* TBC */
1236                 if (!set[0] || args[0] == 0) {
1237                         terminal->tab_ruler[terminal->column] = 0;
1238                 } else if (args[0] == 3) {
1239                         memset(terminal->tab_ruler, 0, terminal->width);
1240                 }
1241                 break;
1242         case 'h':    /* SM */
1243                 for(i = 0; i < 10 && set[i]; i++) {
1244                         handle_term_parameter(terminal, args[i], 1);
1245                 }
1246                 break;
1247         case 'l':    /* RM */
1248                 for(i = 0; i < 10 && set[i]; i++) {
1249                         handle_term_parameter(terminal, args[i], 0);
1250                 }
1251                 break;
1252         case 'm':    /* SGR */
1253                 for(i = 0; i < 10; i++) {
1254                         if (i <= 7 && set[i] && set[i + 1] &&
1255                                 set[i + 2] && args[i + 1] == 5)
1256                         {
1257                                 if (args[i] == 38) {
1258                                         handle_sgr(terminal, args[i + 2] + 256);
1259                                         break;
1260                                 } else if (args[i] == 48) {
1261                                         handle_sgr(terminal, args[i + 2] + 512);
1262                                         break;
1263                                 }
1264                         }
1265                         if(set[i]) {
1266                                 handle_sgr(terminal, args[i]);
1267                         } else if(i == 0) {
1268                                 handle_sgr(terminal, 0);
1269                                 break;
1270                         } else {
1271                                 break;
1272                         }
1273                 }
1274                 break;
1275         case 'n':    /* DSR */
1276                 i = set[0] ? args[0] : 0;
1277                 if (i == 0 || i == 5) {
1278                         write(terminal->master, "\e[0n", 4);
1279                 } else if (i == 6) {
1280                         snprintf(response, MAX_RESPONSE, "\e[%d;%dR",
1281                                  terminal->origin_mode ?
1282                                      terminal->row+terminal->margin_top : terminal->row+1,
1283                                  terminal->column+1);
1284                         write(terminal->master, response, strlen(response));
1285                 }
1286                 break;
1287         case 'r':
1288                 if(!set[0]) {
1289                         terminal->margin_top = 0;
1290                         terminal->margin_bottom = terminal->height-1;
1291                         terminal->row = 0;
1292                         terminal->column = 0;
1293                 } else {
1294                         top = (set[0] ? args[0] : 1) - 1;
1295                         top = top < 0 ? 0 :
1296                               (top >= terminal->height ? terminal->height - 1 : top);
1297                         bottom = (set[1] ? args[1] : 1) - 1;
1298                         bottom = bottom < 0 ? 0 :
1299                                  (bottom >= terminal->height ? terminal->height - 1 : bottom);
1300                         if(bottom > top) {
1301                                 terminal->margin_top = top;
1302                                 terminal->margin_bottom = bottom;
1303                         } else {
1304                                 terminal->margin_top = 0;
1305                                 terminal->margin_bottom = terminal->height-1;
1306                         }
1307                         if(terminal->origin_mode)
1308                                 terminal->row = terminal->margin_top;
1309                         else
1310                                 terminal->row = 0;
1311                         terminal->column = 0;
1312                 }
1313                 break;
1314         case 's':
1315                 terminal->saved_row = terminal->row;
1316                 terminal->saved_column = terminal->column;
1317                 break;
1318         case 'u':
1319                 terminal->row = terminal->saved_row;
1320                 terminal->column = terminal->saved_column;
1321                 break;
1322         default:
1323                 fprintf(stderr, "Unknown CSI escape: %c\n", *p);
1324                 break;
1325         }       
1326 }
1327
1328 static void
1329 handle_non_csi_escape(struct terminal *terminal, char code)
1330 {
1331         switch(code) {
1332         case 'M':    /* RI */
1333                 terminal->row -= 1;
1334                 if(terminal->row < terminal->margin_top) {
1335                         terminal->row = terminal->margin_top;
1336                         terminal_scroll(terminal, -1);
1337                 }
1338                 break;
1339         case 'E':    /* NEL */
1340                 terminal->column = 0;
1341                 // fallthrough
1342         case 'D':    /* IND */
1343                 terminal->row += 1;
1344                 if(terminal->row > terminal->margin_bottom) {
1345                         terminal->row = terminal->margin_bottom;
1346                         terminal_scroll(terminal, +1);
1347                 }
1348                 break;
1349         case 'c':    /* RIS */
1350                 terminal_init(terminal);
1351                 break;
1352         case 'H':    /* HTS */
1353                 terminal->tab_ruler[terminal->column] = 1;
1354                 break;
1355         case '7':    /* DECSC */
1356                 terminal->saved_row = terminal->row;
1357                 terminal->saved_column = terminal->column;
1358                 terminal->saved_attr = terminal->curr_attr;
1359                 terminal->saved_origin_mode = terminal->origin_mode;
1360                 terminal->saved_cs = terminal->cs;
1361                 terminal->saved_g0 = terminal->g0;
1362                 terminal->saved_g1 = terminal->g1;
1363                 break;
1364         case '8':    /* DECRC */
1365                 terminal->row = terminal->saved_row;
1366                 terminal->column = terminal->saved_column;
1367                 terminal->curr_attr = terminal->saved_attr;
1368                 terminal->origin_mode = terminal->saved_origin_mode;
1369                 terminal->cs = terminal->saved_cs;
1370                 terminal->g0 = terminal->saved_g0;
1371                 terminal->g1 = terminal->saved_g1;
1372                 break;
1373         case '=':    /* DECPAM */
1374                 terminal->key_mode = KM_APPLICATION;
1375                 break;
1376         case '>':    /* DECPNM */
1377                 terminal->key_mode = KM_NORMAL;
1378                 break;
1379         default:
1380                 fprintf(stderr, "Unknown escape code: %c\n", code);
1381                 break;
1382         }
1383 }
1384
1385 static void
1386 handle_special_escape(struct terminal *terminal, char special, char code)
1387 {
1388         int i, numChars;
1389
1390         if (special == '#') {
1391                 switch(code) {
1392                 case '8':
1393                         /* fill with 'E', no cheap way to do this */
1394                         memset(terminal->data, 0, terminal->data_pitch * terminal->height);
1395                         numChars = terminal->width * terminal->height;
1396                         for(i = 0; i < numChars; i++) {
1397                                 terminal->data[i].byte[0] = 'E';
1398                         }
1399                         break;
1400                 default:
1401                         fprintf(stderr, "Unknown HASH escape #%c\n", code);
1402                         break;
1403                 }
1404         } else if (special == '(' || special == ')') {
1405                 switch(code) {
1406                 case '0':
1407                         if (special == '(')
1408                                 terminal->g0 = CS_SPECIAL;
1409                         else
1410                                 terminal->g1 = CS_SPECIAL;
1411                         break;
1412                 case 'A':
1413                         if (special == '(')
1414                                 terminal->g0 = CS_UK;
1415                         else
1416                                 terminal->g1 = CS_UK;
1417                         break;
1418                 case 'B':
1419                         if (special == '(')
1420                                 terminal->g0 = CS_US;
1421                         else
1422                                 terminal->g1 = CS_US;
1423                         break;
1424                 default:
1425                         fprintf(stderr, "Unknown character set %c\n", code);
1426                         break;
1427                 }
1428         } else {
1429                 fprintf(stderr, "Unknown special escape %c%c\n", special, code);
1430         }
1431 }
1432
1433 static void
1434 handle_sgr(struct terminal *terminal, int code)
1435 {
1436         switch(code) {
1437         case 0:
1438                 terminal->curr_attr = terminal->color_scheme->default_attr;
1439                 break;
1440         case 1:
1441                 terminal->curr_attr.a |= ATTRMASK_BOLD;
1442                 if (terminal->curr_attr.fg < 8)
1443                         terminal->curr_attr.fg += 8;
1444                 break;
1445         case 4:
1446                 terminal->curr_attr.a |= ATTRMASK_UNDERLINE;
1447                 break;
1448         case 5:
1449                 terminal->curr_attr.a |= ATTRMASK_BLINK;
1450                 break;
1451         case 8:
1452                 terminal->curr_attr.a |= ATTRMASK_CONCEALED;
1453                 break;
1454         case 2:
1455         case 21:
1456         case 22:
1457                 terminal->curr_attr.a &= ~ATTRMASK_BOLD;
1458                 if (terminal->curr_attr.fg < 16 && terminal->curr_attr.fg >= 8)
1459                         terminal->curr_attr.fg -= 8;
1460                 break;
1461         case 24:
1462                 terminal->curr_attr.a &= ~ATTRMASK_UNDERLINE;
1463                 break;
1464         case 25:
1465                 terminal->curr_attr.a &= ~ATTRMASK_BLINK;
1466                 break;
1467         case 7:
1468         case 26:
1469                 terminal->curr_attr.a |= ATTRMASK_INVERSE;
1470                 break;
1471         case 27:
1472                 terminal->curr_attr.a &= ~ATTRMASK_INVERSE;
1473                 break;
1474         case 28:
1475                 terminal->curr_attr.a &= ~ATTRMASK_CONCEALED;
1476                 break;
1477         case 39:
1478                 terminal->curr_attr.fg = terminal->color_scheme->default_attr.fg;
1479                 break;
1480         case 49:
1481                 terminal->curr_attr.bg = terminal->color_scheme->default_attr.bg;
1482                 break;
1483         default:
1484                 if(code >= 30 && code <= 37) {
1485                         terminal->curr_attr.fg = code - 30;
1486                         if (terminal->curr_attr.a & ATTRMASK_BOLD)
1487                                 terminal->curr_attr.fg += 8;
1488                 } else if(code >= 40 && code <= 47) {
1489                         terminal->curr_attr.bg = code - 40;
1490                 } else if (code >= 90 && code <= 97) {
1491                         terminal->curr_attr.fg = code - 90 + 8;
1492                 } else if (code >= 100 && code <= 107) {
1493                         terminal->curr_attr.bg = code - 100 + 8;
1494                 } else if(code >= 256 && code < 512) {
1495                         terminal->curr_attr.fg = code - 256;
1496                 } else if(code >= 512 && code < 768) {
1497                         terminal->curr_attr.bg = code - 512;
1498                 } else {
1499                         fprintf(stderr, "Unknown SGR code: %d\n", code);
1500                 }
1501                 break;
1502         }
1503 }
1504
1505 /* Returns 1 if c was special, otherwise 0 */
1506 static int
1507 handle_special_char(struct terminal *terminal, char c)
1508 {
1509         union utf8_char *row;
1510         struct attr *attr_row;
1511         
1512         row = terminal_get_row(terminal, terminal->row);
1513         attr_row = terminal_get_attr_row(terminal, terminal->row);
1514         
1515         switch(c) {
1516         case '\r':
1517                 terminal->column = 0;
1518                 break;
1519         case '\n':
1520                 if (terminal->mode & MODE_LF_NEWLINE) {
1521                         terminal->column = 0;
1522                 }
1523                 /* fallthrough */
1524         case '\v':
1525         case '\f':
1526                 terminal->row++;
1527                 if(terminal->row > terminal->margin_bottom) {
1528                         terminal->row = terminal->margin_bottom;
1529                         terminal_scroll(terminal, +1);
1530                 }
1531
1532                 break;
1533         case '\t':
1534                 while (terminal->column < terminal->width) {
1535                         if (terminal->tab_ruler[terminal->column]) break;
1536                         if (terminal->mode & MODE_IRM)
1537                                 terminal_shift_line(terminal, +1);
1538                         row[terminal->column].byte[0] = ' ';
1539                         row[terminal->column].byte[1] = '\0';
1540                         attr_row[terminal->column] = terminal->curr_attr;
1541                         terminal->column++;
1542                 }
1543                 if (terminal->column >= terminal->width) {
1544                         terminal->column = terminal->width - 1;
1545                 }
1546
1547                 break;
1548         case '\b':
1549                 if (terminal->column >= terminal->width) {
1550                         terminal->column = terminal->width - 2;
1551                 } else if (terminal->column > 0) {
1552                         terminal->column--;
1553                 } else if (terminal->mode & MODE_AUTOWRAP) {
1554                         terminal->column = terminal->width - 1;
1555                         terminal->row -= 1;
1556                         if (terminal->row < terminal->margin_top) {
1557                                 terminal->row = terminal->margin_top;
1558                                 terminal_scroll(terminal, -1);
1559                         }
1560                 }
1561
1562                 break;
1563         case '\a':
1564                 /* Bell */
1565                 break;
1566         case '\x0E': /* SO */
1567                 terminal->cs = terminal->g1;
1568                 break;
1569         case '\x0F': /* SI */
1570                 terminal->cs = terminal->g0;
1571                 break;
1572         default:
1573                 return 0;
1574         }
1575         
1576         return 1;
1577 }
1578
1579 static void
1580 handle_char(struct terminal *terminal, union utf8_char utf8)
1581 {
1582         union utf8_char *row;
1583         struct attr *attr_row;
1584         
1585         if (handle_special_char(terminal, utf8.byte[0])) return;
1586
1587         apply_char_set(terminal->cs, &utf8);
1588         
1589         /* There are a whole lot of non-characters, control codes,
1590          * and formatting codes that should probably be ignored,
1591          * for example: */
1592         if (strncmp((char*) utf8.byte, "\xEF\xBB\xBF", 3) == 0) {
1593                 /* BOM, ignore */
1594                 return;
1595         } 
1596         
1597         /* Some of these non-characters should be translated, e.g.: */
1598         if (utf8.byte[0] < 32) {
1599                 utf8.byte[0] = utf8.byte[0] + 64;
1600         }
1601         
1602         /* handle right margin effects */
1603         if (terminal->column >= terminal->width) {
1604                 if (terminal->mode & MODE_AUTOWRAP) {
1605                         terminal->column = 0;
1606                         terminal->row += 1;
1607                         if (terminal->row > terminal->margin_bottom) {
1608                                 terminal->row = terminal->margin_bottom;
1609                                 terminal_scroll(terminal, +1);
1610                         }
1611                 } else {
1612                         terminal->column--;
1613                 }
1614         }
1615         
1616         row = terminal_get_row(terminal, terminal->row);
1617         attr_row = terminal_get_attr_row(terminal, terminal->row);
1618         
1619         if (terminal->mode & MODE_IRM)
1620                 terminal_shift_line(terminal, +1);
1621         row[terminal->column] = utf8;
1622         attr_row[terminal->column++] = terminal->curr_attr;
1623
1624         if (utf8.ch != terminal->last_char.ch)
1625                 terminal->last_char = utf8;
1626 }
1627
1628 static void
1629 escape_append_utf8(struct terminal *terminal, union utf8_char utf8)
1630 {
1631         int len, i;
1632
1633         if ((utf8.byte[0] & 0x80) == 0x00)       len = 1;
1634         else if ((utf8.byte[0] & 0xE0) == 0xC0)  len = 2;
1635         else if ((utf8.byte[0] & 0xF0) == 0xE0)  len = 3;
1636         else if ((utf8.byte[0] & 0xF8) == 0xF0)  len = 4;
1637         else                                     len = 1;  /* Invalid, cannot happen */
1638
1639         if (terminal->escape_length + len <= MAX_ESCAPE) {
1640                 for (i = 0; i < len; i++)
1641                         terminal->escape[terminal->escape_length + i] = utf8.byte[i];
1642                 terminal->escape_length += len;
1643         } else if (terminal->escape_length < MAX_ESCAPE) {
1644                 terminal->escape[terminal->escape_length++] = 0;
1645         }
1646 }
1647
1648 static void
1649 terminal_data(struct terminal *terminal, const char *data, size_t length)
1650 {
1651         int i;
1652         union utf8_char utf8;
1653         enum utf8_state parser_state;
1654
1655         for (i = 0; i < length; i++) {
1656                 parser_state =
1657                         utf8_next_char(&terminal->state_machine, data[i]);
1658                 switch(parser_state) {
1659                 case utf8state_accept:
1660                         utf8.ch = terminal->state_machine.s.ch;
1661                         break;
1662                 case utf8state_reject:
1663                         /* the unicode replacement character */
1664                         utf8.byte[0] = 0xEF;
1665                         utf8.byte[1] = 0xBF;
1666                         utf8.byte[2] = 0xBD;
1667                         utf8.byte[3] = 0x00;
1668                         break;
1669                 default:
1670                         continue;
1671                 }
1672
1673                 /* assume escape codes never use non-ASCII characters */
1674                 switch (terminal->state) {
1675                 case escape_state_escape:
1676                         escape_append_utf8(terminal, utf8);
1677                         switch (utf8.byte[0]) {
1678                         case 'P':  /* DCS */
1679                                 terminal->state = escape_state_dcs;
1680                                 break;
1681                         case '[':  /* CSI */
1682                                 terminal->state = escape_state_csi;
1683                                 break;
1684                         case ']':  /* OSC */
1685                                 terminal->state = escape_state_osc;
1686                                 break;
1687                         case '#':
1688                         case '(':
1689                         case ')':  /* special */
1690                                 terminal->state = escape_state_special;
1691                                 break;
1692                         case '^':  /* PM (not implemented) */
1693                         case '_':  /* APC (not implemented) */
1694                                 terminal->state = escape_state_ignore;
1695                                 break;
1696                         default:
1697                                 terminal->state = escape_state_normal;
1698                                 handle_non_csi_escape(terminal, utf8.byte[0]);
1699                                 break;
1700                         }
1701                         continue;
1702                 case escape_state_csi:
1703                         if (handle_special_char(terminal, utf8.byte[0]) != 0) {
1704                                 /* do nothing */
1705                         } else if (utf8.byte[0] == '?') {
1706                                 terminal->escape_flags |= ESC_FLAG_WHAT;
1707                         } else if (utf8.byte[0] == '>') {
1708                                 terminal->escape_flags |= ESC_FLAG_GT;
1709                         } else if (utf8.byte[0] == '!') {
1710                                 terminal->escape_flags |= ESC_FLAG_BANG;
1711                         } else if (utf8.byte[0] == '$') {
1712                                 terminal->escape_flags |= ESC_FLAG_CASH;
1713                         } else if (utf8.byte[0] == '\'') {
1714                                 terminal->escape_flags |= ESC_FLAG_SQUOTE;
1715                         } else if (utf8.byte[0] == '"') {
1716                                 terminal->escape_flags |= ESC_FLAG_DQUOTE;
1717                         } else if (utf8.byte[0] == ' ') {
1718                                 terminal->escape_flags |= ESC_FLAG_SPACE;
1719                         } else {
1720                                 escape_append_utf8(terminal, utf8);
1721                                 if (terminal->escape_length >= MAX_ESCAPE)
1722                                         terminal->state = escape_state_normal;
1723                         }
1724                         
1725                         if (isalpha(utf8.byte[0]) || utf8.byte[0] == '@' ||
1726                                 utf8.byte[0] == '`')
1727                         {
1728                                 terminal->state = escape_state_normal;
1729                                 handle_escape(terminal);
1730                         } else {
1731                         }
1732                         continue;
1733                 case escape_state_inner_escape:
1734                         if (utf8.byte[0] == '\\') {
1735                                 terminal->state = escape_state_normal;
1736                                 if (terminal->outer_state == escape_state_dcs) {
1737                                         handle_dcs(terminal);
1738                                 } else if (terminal->outer_state == escape_state_osc) {
1739                                         handle_osc(terminal);
1740                                 }
1741                         } else if (utf8.byte[0] == '\e') {
1742                                 terminal->state = terminal->outer_state;
1743                                 escape_append_utf8(terminal, utf8);
1744                                 if (terminal->escape_length >= MAX_ESCAPE)
1745                                         terminal->state = escape_state_normal;
1746                         } else {
1747                                 terminal->state = terminal->outer_state;
1748                                 if (terminal->escape_length < MAX_ESCAPE)
1749                                         terminal->escape[terminal->escape_length++] = '\e';
1750                                 escape_append_utf8(terminal, utf8);
1751                                 if (terminal->escape_length >= MAX_ESCAPE)
1752                                         terminal->state = escape_state_normal;
1753                         }
1754                         continue;
1755                 case escape_state_dcs:
1756                 case escape_state_osc:
1757                 case escape_state_ignore:
1758                         if (utf8.byte[0] == '\e') {
1759                                 terminal->outer_state = terminal->state;
1760                                 terminal->state = escape_state_inner_escape;
1761                         } else if (utf8.byte[0] == '\a' && terminal->state == escape_state_osc) {
1762                                 terminal->state = escape_state_normal;
1763                                 handle_osc(terminal);
1764                         } else {
1765                                 escape_append_utf8(terminal, utf8);
1766                                 if (terminal->escape_length >= MAX_ESCAPE)
1767                                         terminal->state = escape_state_normal;
1768                         }
1769                         continue;
1770                 case escape_state_special:
1771                         escape_append_utf8(terminal, utf8);
1772                         terminal->state = escape_state_normal;
1773                         if (isdigit(utf8.byte[0]) || isalpha(utf8.byte[0])) {
1774                                 handle_special_escape(terminal, terminal->escape[1],
1775                                                       utf8.byte[0]);
1776                         }
1777                         continue;
1778                 default:
1779                         break;
1780                 }
1781
1782                 /* this is valid, because ASCII characters are never used to
1783                  * introduce a multibyte sequence in UTF-8 */
1784                 if (utf8.byte[0] == '\e') {
1785                         terminal->state = escape_state_escape;
1786                         terminal->outer_state = escape_state_normal;
1787                         terminal->escape[0] = '\e';
1788                         terminal->escape_length = 1;
1789                         terminal->escape_flags = 0;
1790                 } else {
1791                         handle_char(terminal, utf8);
1792                 } /* if */
1793         } /* for */
1794
1795         window_schedule_redraw(terminal->window);
1796 }
1797
1798 static void
1799 key_handler(struct window *window, uint32_t key, uint32_t sym,
1800             uint32_t state, uint32_t modifiers, void *data)
1801 {
1802         struct terminal *terminal = data;
1803         char ch[MAX_RESPONSE];
1804         int len = 0;
1805
1806         switch (sym) {
1807         case XK_F11:
1808                 if (!state)
1809                         break;
1810                 terminal->fullscreen ^= 1;
1811                 window_set_fullscreen(window, terminal->fullscreen);
1812                 window_schedule_redraw(terminal->window);
1813                 break;
1814
1815         case XK_BackSpace:
1816         case XK_Tab:
1817         case XK_Linefeed:
1818         case XK_Clear:
1819         case XK_Pause:
1820         case XK_Scroll_Lock:
1821         case XK_Sys_Req:
1822         case XK_Escape:
1823                 ch[len++] = sym & 0x7f;
1824                 break;
1825
1826         case XK_Return:
1827                 if (terminal->mode & MODE_LF_NEWLINE) {
1828                         ch[len++] = 0x0D;
1829                         ch[len++] = 0x0A;
1830                 } else {
1831                         ch[len++] = 0x0D;
1832                 }
1833                 break;
1834
1835         case XK_Shift_L:
1836         case XK_Shift_R:
1837         case XK_Control_L:
1838         case XK_Control_R:
1839         case XK_Alt_L:
1840         case XK_Alt_R:
1841                 break;
1842
1843         case XK_Insert:
1844                 len = function_key_response('[', 2, modifiers, '~', ch);
1845                 break;
1846         case XK_Delete:
1847                 if (terminal->mode & MODE_DELETE_SENDS_DEL) {
1848                         ch[len++] = '\x04';
1849                 } else {
1850                         len = function_key_response('[', 3, modifiers, '~', ch);
1851                 }
1852                 break;
1853         case XK_Page_Up:
1854                 len = function_key_response('[', 5, modifiers, '~', ch);
1855                 break;
1856         case XK_Page_Down:
1857                 len = function_key_response('[', 6, modifiers, '~', ch);
1858                 break;
1859         case XK_F1:
1860                 len = function_key_response('O', 1, modifiers, 'P', ch);
1861                 break;
1862         case XK_F2:
1863                 len = function_key_response('O', 1, modifiers, 'Q', ch);
1864                 break;
1865         case XK_F3:
1866                 len = function_key_response('O', 1, modifiers, 'R', ch);
1867                 break;
1868         case XK_F4:
1869                 len = function_key_response('O', 1, modifiers, 'S', ch);
1870                 break;
1871         case XK_F5:
1872                 len = function_key_response('[', 15, modifiers, '~', ch);
1873                 break;
1874         case XK_F6:
1875                 len = function_key_response('[', 17, modifiers, '~', ch);
1876                 break;
1877         case XK_F7:
1878                 len = function_key_response('[', 18, modifiers, '~', ch);
1879                 break;
1880         case XK_F8:
1881                 len = function_key_response('[', 19, modifiers, '~', ch);
1882                 break;
1883         case XK_F9:
1884                 len = function_key_response('[', 20, modifiers, '~', ch);
1885                 break;
1886         case XK_F10:
1887                 len = function_key_response('[', 21, modifiers, '~', ch);
1888                 break;
1889         case XK_F12:
1890                 len = function_key_response('[', 24, modifiers, '~', ch);
1891                 break;
1892         default:
1893                 /* Handle special keys with alternate mappings */
1894                 len = apply_key_map(terminal->key_mode, sym, modifiers, ch);
1895                 if (len != 0) break;
1896                 
1897                 if (modifiers & WINDOW_MODIFIER_CONTROL) {
1898                         if (sym >= '3' && sym <= '7')
1899                                 sym = (sym & 0x1f) + 8;
1900
1901                         if (!((sym >= '!' && sym <= '/') ||
1902                                 (sym >= '8' && sym <= '?') ||
1903                                 (sym >= '0' && sym <= '2'))) sym = sym & 0x1f;
1904                         else if (sym == '2') sym = 0x00;
1905                         else if (sym == '/') sym = 0x1F;
1906                         else if (sym == '8' || sym == '?') sym = 0x7F;
1907                 } else if ((terminal->mode & MODE_ALT_SENDS_ESC) && 
1908                         (modifiers & WINDOW_MODIFIER_ALT))
1909                 {
1910                         ch[len++] = 0x1b;
1911                 } else if (modifiers & WINDOW_MODIFIER_ALT) {
1912                         sym = sym | 0x80;
1913                 }
1914
1915                 if (sym < 256)
1916                         ch[len++] = sym;
1917                 break;
1918         }
1919
1920         if (state && len > 0)
1921                 write(terminal->master, ch, len);
1922 }
1923
1924 static void
1925 keyboard_focus_handler(struct window *window,
1926                        struct input *device, void *data)
1927 {
1928         struct terminal *terminal = data;
1929
1930         terminal->focused = (device != NULL);
1931         window_schedule_redraw(terminal->window);
1932 }
1933
1934 static struct terminal *
1935 terminal_create(struct display *display, int fullscreen)
1936 {
1937         struct terminal *terminal;
1938         cairo_surface_t *surface;
1939         cairo_t *cr;
1940
1941         terminal = malloc(sizeof *terminal);
1942         if (terminal == NULL)
1943                 return terminal;
1944
1945         memset(terminal, 0, sizeof *terminal);
1946         terminal->fullscreen = fullscreen;
1947         terminal->color_scheme = &DEFAULT_COLORS;
1948         terminal_init(terminal);
1949         terminal->margin_top = 0;
1950         terminal->margin_bottom = -1;
1951         terminal->window = window_create(display, "Wayland Terminal",
1952                                          500, 400);
1953
1954         init_state_machine(&terminal->state_machine);
1955         init_color_table(terminal);
1956
1957         terminal->display = display;
1958         terminal->margin = 5;
1959
1960         window_set_fullscreen(terminal->window, terminal->fullscreen);
1961         window_set_user_data(terminal->window, terminal);
1962         window_set_redraw_handler(terminal->window, redraw_handler);
1963
1964         window_set_key_handler(terminal->window, key_handler);
1965         window_set_keyboard_focus_handler(terminal->window,
1966                                           keyboard_focus_handler);
1967
1968         surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 0, 0);
1969         cr = cairo_create(surface);
1970         terminal->font_bold = cairo_toy_font_face_create ("mono",
1971                               CAIRO_FONT_SLANT_NORMAL,
1972                               CAIRO_FONT_WEIGHT_BOLD);
1973         cairo_font_face_reference(terminal->font_bold);
1974         terminal->font_normal = cairo_toy_font_face_create ("mono",
1975                                 CAIRO_FONT_SLANT_NORMAL,
1976                                 CAIRO_FONT_WEIGHT_NORMAL);
1977         cairo_font_face_reference(terminal->font_normal);
1978         cairo_set_font_face(cr, terminal->font_normal);
1979         cairo_set_font_size(cr, 14);
1980         cairo_font_extents(cr, &terminal->extents);
1981         cairo_destroy(cr);
1982         cairo_surface_destroy(surface);
1983
1984         terminal_resize(terminal, 80, 24);
1985         terminal_draw(terminal);
1986
1987         return terminal;
1988 }
1989
1990 static gboolean
1991 io_handler(GIOChannel   *source,
1992            GIOCondition  condition,
1993            gpointer      data)
1994 {
1995         struct terminal *terminal = data;
1996         gchar buffer[256];
1997         gsize bytes_read;
1998         GError *error = NULL;
1999
2000         g_io_channel_read_chars(source, buffer, sizeof buffer,
2001                                 &bytes_read, &error);
2002
2003         terminal_data(terminal, buffer, bytes_read);
2004
2005         return TRUE;
2006 }
2007
2008 static int
2009 terminal_run(struct terminal *terminal, const char *path)
2010 {
2011         int master;
2012         pid_t pid;
2013
2014         pid = forkpty(&master, NULL, NULL, NULL);
2015         if (pid == 0) {
2016                 setenv("TERM", "xterm-256color", 1);
2017                 setenv("COLORTERM", "xterm-256color", 1);
2018                 if (execl(path, path, NULL)) {
2019                         printf("exec failed: %m\n");
2020                         exit(EXIT_FAILURE);
2021                 }
2022         } else if (pid < 0) {
2023                 fprintf(stderr, "failed to fork and create pty (%m).\n");
2024                 return -1;
2025         }
2026
2027         terminal->master = master;
2028         terminal->channel = g_io_channel_unix_new(master);
2029         fcntl(master, F_SETFL, O_NONBLOCK);
2030         g_io_add_watch(terminal->channel, G_IO_IN,
2031                        io_handler, terminal);
2032
2033         return 0;
2034 }
2035
2036 static const GOptionEntry option_entries[] = {
2037         { "fullscreen", 'f', 0, G_OPTION_ARG_NONE,
2038           &option_fullscreen, "Run in fullscreen mode" },
2039         { NULL }
2040 };
2041
2042 int main(int argc, char *argv[])
2043 {
2044         struct display *d;
2045         struct terminal *terminal;
2046
2047         d = display_create(&argc, &argv, option_entries);
2048         if (d == NULL) {
2049                 fprintf(stderr, "failed to create display: %m\n");
2050                 return -1;
2051         }
2052
2053         terminal = terminal_create(d, option_fullscreen);
2054         if (terminal_run(terminal, "/bin/bash"))
2055                 exit(EXIT_FAILURE);
2056
2057         display_run(d);
2058
2059         return 0;
2060 }