terminal: Scroll margins
[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
55 /* Buffer sizes */
56 #define MAX_RESPONSE            11
57 #define MAX_ESCAPE              64
58
59 union utf8_char {
60         unsigned char byte[4];
61         uint32_t ch;
62 };
63
64 enum utf8_state {
65         utf8state_start,
66         utf8state_accept,
67         utf8state_reject,
68         utf8state_expect3,
69         utf8state_expect2,
70         utf8state_expect1
71 };
72
73 struct utf8_state_machine {
74         enum utf8_state state;
75         int len;
76         union utf8_char s;
77 };
78
79 static void
80 init_state_machine(struct utf8_state_machine *machine)
81 {
82         machine->state = utf8state_start;
83         machine->len = 0;
84         machine->s.ch = 0;
85 }
86
87 static enum utf8_state
88 utf8_next_char(struct utf8_state_machine *machine, char c)
89 {
90         switch(machine->state) {
91         case utf8state_start:
92         case utf8state_accept:
93         case utf8state_reject:
94                 machine->s.ch = 0;
95                 machine->len = 0;
96                 if(c == 0xC0 || c == 0xC1) {
97                         /* overlong encoding, reject */
98                         machine->state = utf8state_reject;
99                 } else if((c & 0x80) == 0) {
100                         /* single byte, accept */
101                         machine->s.byte[machine->len++] = c;
102                         machine->state = utf8state_accept;
103                 } else if((c & 0xC0) == 0x80) {
104                         /* parser out of sync, ignore byte */
105                         machine->state = utf8state_start;
106                 } else if((c & 0xE0) == 0xC0) {
107                         /* start of two byte sequence */
108                         machine->s.byte[machine->len++] = c;
109                         machine->state = utf8state_expect1;
110                 } else if((c & 0xF0) == 0xE0) {
111                         /* start of three byte sequence */
112                         machine->s.byte[machine->len++] = c;
113                         machine->state = utf8state_expect2;
114                 } else if((c & 0xF8) == 0xF0) {
115                         /* start of four byte sequence */
116                         machine->s.byte[machine->len++] = c;
117                         machine->state = utf8state_expect3;
118                 } else {
119                         /* overlong encoding, reject */
120                         machine->state = utf8state_reject;
121                 }
122                 break;
123         case utf8state_expect3:
124                 machine->s.byte[machine->len++] = c;
125                 if((c & 0xC0) == 0x80) {
126                         /* all good, continue */
127                         machine->state = utf8state_expect2;
128                 } else {
129                         /* missing extra byte, reject */
130                         machine->state = utf8state_reject;
131                 }
132                 break;
133         case utf8state_expect2:
134                 machine->s.byte[machine->len++] = c;
135                 if((c & 0xC0) == 0x80) {
136                         /* all good, continue */
137                         machine->state = utf8state_expect1;
138                 } else {
139                         /* missing extra byte, reject */
140                         machine->state = utf8state_reject;
141                 }
142                 break;
143         case utf8state_expect1:
144                 machine->s.byte[machine->len++] = c;
145                 if((c & 0xC0) == 0x80) {
146                         /* all good, accept */
147                         machine->state = utf8state_accept;
148                 } else {
149                         /* missing extra byte, reject */
150                         machine->state = utf8state_reject;
151                 }
152                 break;
153         default:
154                 machine->state = utf8state_reject;
155                 break;
156         }
157         
158         return machine->state;
159 }
160
161 struct terminal_color { double r, g, b, a; };
162 struct attr {
163         unsigned char fg, bg;
164         char a;        /* attributes format:
165                         * 76543210
166                         *     ilub */
167         char r;        /* reserved */
168 };
169 struct color_scheme {
170         struct terminal_color palette[16];
171         struct terminal_color border;
172         struct attr default_attr;
173 };
174
175 static void
176 attr_init(struct attr *data_attr, struct attr attr, int n)
177 {
178         int i;
179         for (i = 0; i < n; i++) {
180                 data_attr[i] = attr;
181         }
182 }
183
184 struct terminal {
185         struct window *window;
186         struct display *display;
187         union utf8_char *data;
188         struct attr *data_attr;
189         struct attr curr_attr;
190         char origin_mode;
191         union utf8_char last_char;
192         int margin_top, margin_bottom;
193         int data_pitch, attr_pitch;  /* The width in bytes of a line */
194         int width, height, start, row, column;
195         int fd, master;
196         GIOChannel *channel;
197         uint32_t modifiers;
198         char escape[MAX_ESCAPE];
199         int escape_length;
200         int state;
201         int qmark_flag;
202         struct utf8_state_machine state_machine;
203         int margin;
204         int fullscreen;
205         int focused;
206         struct color_scheme *color_scheme;
207         struct terminal_color color_table[256];
208         cairo_font_extents_t extents;
209         cairo_font_face_t *font_normal, *font_bold;
210 };
211
212 static void
213 terminal_init(struct terminal *terminal)
214 {
215         terminal->curr_attr = terminal->color_scheme->default_attr;
216         terminal->origin_mode = 0;
217
218         terminal->row = 0;
219         terminal->column = 0;
220 }
221
222 static void
223 init_color_table(struct terminal *terminal)
224 {
225         int c, r;
226         struct terminal_color *color_table = terminal->color_table;
227
228         for (c = 0; c < 256; c ++) {
229                 if (c < 16) {
230                         color_table[c] = terminal->color_scheme->palette[c];
231                 } else if (c < 232) {
232                         r = c - 16;
233                         color_table[c].b = ((double)(r % 6) / 6.0); r /= 6;
234                         color_table[c].g = ((double)(r % 6) / 6.0); r /= 6;
235                         color_table[c].r = ((double)(r % 6) / 6.0);
236                         color_table[c].a = 1.0;
237                 } else {
238                         r = (c - 232) * 10 + 8;
239                         color_table[c].r = ((double) r) / 256.0;
240                         color_table[c].g = color_table[c].r;
241                         color_table[c].b = color_table[c].r;
242                         color_table[c].a = 1.0;
243                 }
244         }
245 }
246
247 static union utf8_char *
248 terminal_get_row(struct terminal *terminal, int row)
249 {
250         int index;
251
252         index = (row + terminal->start) % terminal->height;
253
254         return &terminal->data[index * terminal->width];
255 }
256
257 static struct attr*
258 terminal_get_attr_row(struct terminal *terminal, int row) {
259         int index;
260
261         index = (row + terminal->start) % terminal->height;
262
263         return &terminal->data_attr[index * terminal->width];
264 }
265
266 static struct attr
267 terminal_get_attr(struct terminal *terminal, int row, int col) {
268         return terminal_get_attr_row(terminal, row)[col];
269 }
270
271 static void
272 terminal_scroll_buffer(struct terminal *terminal, int d)
273 {
274         int i;
275
276         d = d % (terminal->height + 1);
277         terminal->start = (terminal->start + d) % terminal->height;
278         if (terminal->start < 0) terminal->start = terminal->height + terminal->start;
279         if(d < 0) {
280                 d = 0 - d;
281                 for(i = 0; i < d; i++) {
282                         memset(terminal_get_row(terminal, i), 0, terminal->data_pitch);
283                         attr_init(terminal_get_attr_row(terminal, i),
284                             terminal->curr_attr, terminal->width);
285                 }
286         } else {
287                 for(i = terminal->height - d; i < terminal->height; i++) {
288                         memset(terminal_get_row(terminal, i), 0, terminal->data_pitch);
289                         attr_init(terminal_get_attr_row(terminal, i),
290                             terminal->curr_attr, terminal->width);
291                 }
292         }
293 }
294
295 static void
296 terminal_scroll_window(struct terminal *terminal, int d)
297 {
298         int i;
299         int window_height;
300         int from_row, to_row;
301         struct attr *dup_attr;
302         
303         // scrolling range is inclusive
304         window_height = terminal->margin_bottom - terminal->margin_top + 1;
305         d = d % (window_height + 1);
306         if(d < 0) {
307                 d = 0 - d;
308                 to_row = terminal->margin_bottom;
309                 from_row = terminal->margin_bottom - d;
310                 
311                 for (i = 0; i < (window_height - d); i++) {
312                         memcpy(terminal_get_row(terminal, to_row - i),
313                                terminal_get_row(terminal, from_row - i),
314                                terminal->data_pitch);
315                         memcpy(terminal_get_attr_row(terminal, to_row - i),
316                                terminal_get_attr_row(terminal, from_row - i),
317                                terminal->attr_pitch);
318                 }
319                 dup_attr = terminal_get_attr_row(terminal, terminal->margin_top);
320                 for (i = terminal->margin_top; i < (terminal->margin_top + d); i++) {
321                         memset(terminal_get_row(terminal, i), 0, terminal->data_pitch);
322                         if (i > terminal->margin_top) {
323                                 memcpy(terminal_get_attr_row(terminal, i),
324                                        dup_attr, terminal->attr_pitch);
325                         }
326                 }
327         } else {
328                 to_row = terminal->margin_top;
329                 from_row = terminal->margin_top + d;
330                 
331                 for (i = 0; i < (window_height - d); i++) {
332                         memcpy(terminal_get_row(terminal, to_row + i),
333                                terminal_get_row(terminal, from_row + i),
334                                terminal->data_pitch);
335                         memcpy(terminal_get_attr_row(terminal, to_row + i),
336                                terminal_get_attr_row(terminal, from_row + i),
337                                terminal->attr_pitch);
338                 }
339                 dup_attr = terminal_get_attr_row(terminal, terminal->margin_bottom);
340                 for (i = terminal->margin_bottom - d + 1; i <= terminal->margin_bottom; i++) {
341                         memset(terminal_get_row(terminal, i), 0, terminal->data_pitch);
342                         if (i < terminal->margin_bottom) {
343                                 memcpy(terminal_get_attr_row(terminal, i),
344                                        dup_attr, terminal->attr_pitch);
345                         }
346                 }
347         }
348 }
349
350 static void
351 terminal_scroll(struct terminal *terminal, int d)
352 {
353         if(terminal->margin_top == 0 && terminal->margin_bottom == terminal->height - 1)
354                 terminal_scroll_buffer(terminal, d);
355         else
356                 terminal_scroll_window(terminal, d);
357 }
358
359 static void
360 terminal_resize(struct terminal *terminal, int width, int height)
361 {
362         size_t size;
363         union utf8_char *data;
364         struct attr *data_attr;
365         int data_pitch, attr_pitch;
366         int i, l, total_rows, start;
367         struct rectangle rectangle;
368         struct winsize ws;
369
370         if (terminal->width == width && terminal->height == height)
371                 return;
372
373         data_pitch = width * sizeof(union utf8_char);
374         size = data_pitch * height;
375         data = malloc(size);
376         attr_pitch = width * sizeof(struct attr);
377         data_attr = malloc(attr_pitch * height);
378         memset(data, 0, size);
379         attr_init(data_attr, terminal->curr_attr, width * height);
380         if (terminal->data && terminal->data_attr) {
381                 if (width > terminal->width)
382                         l = terminal->width;
383                 else
384                         l = width;
385
386                 if (terminal->height > height) {
387                         total_rows = height;
388                         start = terminal->height - height;
389                 } else {
390                         total_rows = terminal->height;
391                         start = 0;
392                 }
393
394                 for (i = 0; i < total_rows; i++) {
395                         memcpy(&data[width * i],
396                                terminal_get_row(terminal, i),
397                                l * sizeof(union utf8_char));
398                         memcpy(&data_attr[width * i],
399                                terminal_get_attr_row(terminal, i),
400                                l * sizeof(struct attr));
401                 }
402
403                 free(terminal->data);
404                 free(terminal->data_attr);
405         }
406
407         terminal->data_pitch = data_pitch;
408         terminal->attr_pitch = attr_pitch;
409         terminal->width = width;
410         terminal->height = height;
411         if(terminal->margin_bottom >= terminal->height)
412                 terminal->margin_bottom = terminal->height - 1;
413         terminal->data = data;
414         terminal->data_attr = data_attr;
415
416         if (terminal->row >= terminal->height)
417                 terminal->row = terminal->height - 1;
418         if (terminal->column >= terminal->width)
419                 terminal->column = terminal->width - 1;
420         terminal->start = 0;
421         
422         if (!terminal->fullscreen) {
423                 rectangle.width = terminal->width *
424                         terminal->extents.max_x_advance + 2 * terminal->margin;
425                 rectangle.height = terminal->height *
426                         terminal->extents.height + 2 * terminal->margin;
427                 window_set_child_size(terminal->window, &rectangle);
428         }
429
430         /* Update the window size */
431         ws.ws_row = terminal->height;
432         ws.ws_col = terminal->width;
433         window_get_child_rectangle(terminal->window, &rectangle);
434         ws.ws_xpixel = rectangle.width;
435         ws.ws_ypixel = rectangle.height;
436         ioctl(terminal->master, TIOCSWINSZ, &ws);
437 }
438
439 struct color_scheme DEFAULT_COLORS = {
440         {
441                 {0,    0,    0,    1}, /* black */
442                 {0.66, 0,    0,    1}, /* red */
443                 {0  ,  0.66, 0,    1}, /* green */
444                 {0.66, 0.33, 0,    1}, /* orange (nicer than muddy yellow) */
445                 {0  ,  0  ,  0.66, 1}, /* blue */
446                 {0.66, 0  ,  0.66, 1}, /* magenta */
447                 {0,    0.66, 0.66, 1}, /* cyan */
448                 {0.66, 0.66, 0.66, 1}, /* light grey */
449                 {0.22, 0.33, 0.33, 1}, /* dark grey */
450                 {1,    0.33, 0.33, 1}, /* high red */
451                 {0.33, 1,    0.33, 1}, /* high green */
452                 {1,    1,    0.33, 1}, /* high yellow */
453                 {0.33, 0.33, 1,    1}, /* high blue */
454                 {1,    0.33, 1,    1}, /* high magenta */
455                 {0.33, 1,    1,    1}, /* high cyan */
456                 {1,    1,    1,    1}  /* white */
457         },
458         {0, 0, 0, 1},                  /* black border */
459         {7, 0, 0, }                    /* bg:black (0), fg:light gray (7)  */
460 };
461
462 static void
463 terminal_draw_contents(struct terminal *terminal)
464 {
465         struct rectangle rectangle;
466         cairo_t *cr;
467         cairo_font_extents_t extents;
468         int top_margin, side_margin;
469         int row, col;
470         struct attr attr;
471         union utf8_char *p_row;
472         struct utf8_chars {
473                 union utf8_char c;
474                 char null;
475         } toShow;
476         int foreground, background, bold, underline;
477         int text_x, text_y;
478         cairo_surface_t *surface;
479         double d;
480
481         toShow.null = 0;
482
483         window_get_child_rectangle(terminal->window, &rectangle);
484
485         surface = display_create_surface(terminal->display, &rectangle);
486         cr = cairo_create(surface);
487         cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
488         cairo_set_source_rgba(cr,
489                               terminal->color_scheme->border.r,
490                               terminal->color_scheme->border.g,
491                               terminal->color_scheme->border.b,
492                               terminal->color_scheme->border.a);
493         cairo_paint(cr);
494         cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
495
496         cairo_set_font_face(cr, terminal->font_normal);
497         cairo_set_font_size(cr, 14);
498
499         cairo_font_extents(cr, &extents);
500         side_margin = (rectangle.width - terminal->width * extents.max_x_advance) / 2;
501         top_margin = (rectangle.height - terminal->height * extents.height) / 2;
502
503         cairo_set_line_width(cr, 1.0);
504
505         for (row = 0; row < terminal->height; row++) {
506                 p_row = terminal_get_row(terminal, row);
507                 for (col = 0; col < terminal->width; col++) {
508                         /* get the attributes for this character cell */
509                         attr = terminal_get_attr(terminal, row, col);
510                         if ((attr.a & ATTRMASK_INVERSE) ||
511                                 (terminal->focused &&
512                                 terminal->row == row && terminal->column == col))
513                         {
514                                 foreground = attr.bg;
515                                 background = attr.fg;
516                         } else {
517                                 foreground = attr.fg;
518                                 background = attr.bg;
519                         }
520                         bold = attr.a & (ATTRMASK_BOLD | ATTRMASK_BLINK);
521                         underline = attr.a & ATTRMASK_UNDERLINE;
522
523                         /* paint the background */
524                         cairo_set_source_rgba(cr,
525                                               terminal->color_table[background].r,
526                                               terminal->color_table[background].g,
527                                               terminal->color_table[background].b,
528                                               terminal->color_table[background].a);
529                         cairo_move_to(cr, side_margin + (col * extents.max_x_advance),
530                               top_margin + (row * extents.height));
531                         cairo_rel_line_to(cr, extents.max_x_advance, 0);
532                         cairo_rel_line_to(cr, 0, extents.height);
533                         cairo_rel_line_to(cr, -extents.max_x_advance, 0);
534                         cairo_close_path(cr);
535                         cairo_fill(cr);
536
537                         /* paint the foreground */
538                         if (bold)
539                                 cairo_set_font_face(cr, terminal->font_bold);
540                         else
541                                 cairo_set_font_face(cr, terminal->font_normal);
542                         cairo_set_source_rgba(cr,
543                                               terminal->color_table[foreground].r,
544                                               terminal->color_table[foreground].g,
545                                               terminal->color_table[foreground].b,
546                                               terminal->color_table[foreground].a);
547
548                         text_x = side_margin + col * extents.max_x_advance;
549                         text_y = top_margin + extents.ascent + row * extents.height;
550                         if (underline) {
551                                 cairo_move_to(cr, text_x, text_y + 2);
552                                 cairo_line_to(cr, text_x + extents.max_x_advance, text_y + 2);
553                                 cairo_stroke(cr);
554                         }
555                         cairo_move_to(cr, text_x, text_y);
556                         
557                         toShow.c = p_row[col];
558                         cairo_show_text(cr, (char *) toShow.c.byte);
559                 }
560         }
561
562         if (!terminal->focused) {
563                 d = 0.5;
564
565                 cairo_set_line_width(cr, 1);
566                 cairo_move_to(cr, side_margin + terminal->column * extents.max_x_advance + d,
567                               top_margin + terminal->row * extents.height + d);
568                 cairo_rel_line_to(cr, extents.max_x_advance - 2 * d, 0);
569                 cairo_rel_line_to(cr, 0, extents.height - 2 * d);
570                 cairo_rel_line_to(cr, -extents.max_x_advance + 2 * d, 0);
571                 cairo_close_path(cr);
572
573                 cairo_stroke(cr);
574         }
575
576         cairo_destroy(cr);
577
578         window_copy_surface(terminal->window,
579                             &rectangle,
580                             surface);
581
582         cairo_surface_destroy(surface);
583 }
584
585 static void
586 terminal_draw(struct terminal *terminal)
587 {
588         struct rectangle rectangle;
589         int32_t width, height;
590
591         window_get_child_rectangle(terminal->window, &rectangle);
592
593         width = (rectangle.width - 2 * terminal->margin) /
594                 (int32_t) terminal->extents.max_x_advance;
595         height = (rectangle.height - 2 * terminal->margin) /
596                 (int32_t) terminal->extents.height;
597         terminal_resize(terminal, width, height);
598
599         window_draw(terminal->window);
600         terminal_draw_contents(terminal);
601         window_flush(terminal->window);
602 }
603
604 static void
605 redraw_handler(struct window *window, void *data)
606 {
607         struct terminal *terminal = data;
608
609         terminal_draw(terminal);
610 }
611
612 #define STATE_NORMAL 0
613 #define STATE_ESCAPE 1
614 #define STATE_ESCAPE_SPECIAL 2
615 #define STATE_ESCAPE_CSI  3
616
617 static void
618 terminal_data(struct terminal *terminal, const char *data, size_t length);
619
620 static void
621 handle_char(struct terminal *terminal, union utf8_char utf8);
622
623 static void
624 handle_sgr(struct terminal *terminal, int code);
625
626 static void
627 handle_term_parameter(struct terminal *terminal, int code, int sr)
628 {
629         if (terminal->qmark_flag) {
630                 switch(code) {
631                 case 6:  /* DECOM */
632                         terminal->origin_mode = sr;
633                         if (terminal->origin_mode)
634                                 terminal->row = terminal->margin_top;
635                         else
636                                 terminal->row = 0;
637                         terminal->column = 0;
638                         break;
639                 default:
640                         fprintf(stderr, "Unknown parameter: ?%d\n", code);
641                         break;
642                 }
643         } else {
644                 switch(code) {
645                 default:
646                         fprintf(stderr, "Unknown parameter: %d\n", code);
647                         break;
648                 }
649         }
650 }
651
652 static void
653 handle_escape(struct terminal *terminal)
654 {
655         union utf8_char *row;
656         struct attr *attr_row;
657         char *p;
658         int i, count, top, bottom;
659         int args[10], set[10] = { 0, };
660
661         terminal->escape[terminal->escape_length++] = '\0';
662         i = 0;
663         p = &terminal->escape[2];
664         while ((isdigit(*p) || *p == ';') && i < 10) {
665                 if (*p == ';') {
666                         if (!set[i]) {
667                                 args[i] = 0;
668                                 set[i] = 1;
669                         }
670                         p++;
671                         i++;
672                 } else {
673                         args[i] = strtol(p, &p, 10);
674                         set[i] = 1;
675                 }
676         }
677         
678         switch (*p) {
679         case 'A':
680                 count = set[0] ? args[0] : 1;
681                 if (terminal->row - count >= 0)
682                         terminal->row -= count;
683                 else
684                         terminal->row = 0;
685                 break;
686         case 'B':
687                 count = set[0] ? args[0] : 1;
688                 if (terminal->row + count < terminal->height)
689                         terminal->row += count;
690                 else
691                         terminal->row = terminal->height;
692                 break;
693         case 'C':
694                 count = set[0] ? args[0] : 1;
695                 if (terminal->column + count < terminal->width)
696                         terminal->column += count;
697                 else
698                         terminal->column = terminal->width;
699                 break;
700         case 'D':
701                 count = set[0] ? args[0] : 1;
702                 if (terminal->column - count >= 0)
703                         terminal->column -= count;
704                 else
705                         terminal->column = 0;
706                 break;
707         case 'J':
708                 row = terminal_get_row(terminal, terminal->row);
709                 attr_row = terminal_get_attr_row(terminal, terminal->row);
710                 memset(&row[terminal->column], 0, (terminal->width - terminal->column) * sizeof(union utf8_char));
711                 attr_init(&attr_row[terminal->column], terminal->curr_attr, (terminal->width - terminal->column));
712                 for (i = terminal->row + 1; i < terminal->height; i++) {
713                         memset(terminal_get_row(terminal, i), 0, terminal->width * sizeof(union utf8_char));
714                         attr_init(terminal_get_attr_row(terminal, i), terminal->curr_attr, terminal->width);
715                 }
716                 break;
717         case 'G':
718                 if (set[0])
719                         terminal->column = args[0] - 1;
720                 break;
721         case 'H':
722         case 'f':
723                 terminal->row = set[0] ? args[0] - 1 : 0;
724                 terminal->column = set[1] ? args[1] - 1 : 0;
725                 break;
726         case 'K':
727                 row = terminal_get_row(terminal, terminal->row);
728                 attr_row = terminal_get_attr_row(terminal, terminal->row);
729                 memset(&row[terminal->column], 0, (terminal->width - terminal->column) * sizeof(union utf8_char));
730                 attr_init(&attr_row[terminal->column], terminal->curr_attr, (terminal->width - terminal->column));
731                 break;
732         case 'S':    /* SU */
733                 terminal_scroll(terminal, set[0] ? args[0] : 1);
734                 break;
735         case 'T':    /* SD */
736                 terminal_scroll(terminal, 0 - (set[0] ? args[0] : 1));
737                 break;
738         case 'h':    /* SM */
739                 for(i = 0; i < 10 && set[i]; i++) {
740                         handle_term_parameter(terminal, args[i], 1);
741                 }
742                 break;
743         case 'l':    /* RM */
744                 for(i = 0; i < 10 && set[i]; i++) {
745                         handle_term_parameter(terminal, args[i], 0);
746                 }
747                 break;
748         case 'm':    /* SGR */
749                 if (set[0] && set[1] && set[2] && args[1] == 5) {
750                         if (args[0] == 38) {
751                                 handle_sgr(terminal, args[2] + 256);
752                                 break;
753                         } else if (args[0] == 48) {
754                                 handle_sgr(terminal, args[2] + 512);
755                                 break;
756                         }
757                 }
758                 for(i = 0; i < 10; i++) {
759                         if(set[i]) {
760                                 handle_sgr(terminal, args[i]);
761                         } else if(i == 0) {
762                                 handle_sgr(terminal, 0);
763                                 break;
764                         } else {
765                                 break;
766                         }
767                 }
768                 break;
769         case 'r':
770                 if(!set[0]) {
771                         terminal->margin_top = 0;
772                         terminal->margin_bottom = terminal->height-1;
773                         terminal->row = 0;
774                         terminal->column = 0;
775                 } else {
776                         top = (set[0] ? args[0] : 1) - 1;
777                         top = top < 0 ? 0 :
778                               (top >= terminal->height ? terminal->height - 1 : top);
779                         bottom = (set[1] ? args[1] : 1) - 1;
780                         bottom = bottom < 0 ? 0 :
781                                  (bottom >= terminal->height ? terminal->height - 1 : bottom);
782                         if(bottom > top) {
783                                 terminal->margin_top = top;
784                                 terminal->margin_bottom = bottom;
785                         } else {
786                                 terminal->margin_top = 0;
787                                 terminal->margin_bottom = terminal->height-1;
788                         }
789                         if(terminal->origin_mode)
790                                 terminal->row = terminal->margin_top;
791                         else
792                                 terminal->row = 0;
793                         terminal->column = 0;
794                 }
795                 break;
796         default:
797                 fprintf(stderr, "Unknown CSI escape: %c\n", *p);
798                 break;
799         }       
800 }
801
802 static void
803 handle_non_csi_escape(struct terminal *terminal, char code)
804 {
805         switch(code) {
806         case 'M':    /* RI */
807                 terminal->row -= 1;
808                 if(terminal->row < terminal->margin_top) {
809                         terminal->row = terminal->margin_top;
810                         terminal_scroll(terminal, -1);
811                 }
812                 break;
813         case 'E':    /* NEL */
814                 terminal->column = 0;
815                 // fallthrough
816         case 'D':    /* IND */
817                 terminal->row += 1;
818                 if(terminal->row > terminal->margin_bottom) {
819                         terminal->row = terminal->margin_bottom;
820                         terminal_scroll(terminal, +1);
821                 }
822                 break;
823         default:
824                 fprintf(stderr, "Unknown escape code: %c\n", code);
825                 break;
826         }
827 }
828
829 static void
830 handle_special_escape(struct terminal *terminal, char special, char code)
831 {
832 }
833
834 static void
835 handle_sgr(struct terminal *terminal, int code)
836 {
837         switch(code) {
838         case 0:
839                 terminal->curr_attr = terminal->color_scheme->default_attr;
840                 break;
841         case 1:
842                 terminal->curr_attr.a |= ATTRMASK_BOLD;
843                 if (terminal->curr_attr.fg < 8)
844                         terminal->curr_attr.fg += 8;
845                 break;
846         case 4:
847                 terminal->curr_attr.a |= ATTRMASK_UNDERLINE;
848                 break;
849         case 5:
850                 terminal->curr_attr.a |= ATTRMASK_BLINK;
851                 break;
852         case 2:
853         case 21:
854         case 22:
855                 terminal->curr_attr.a &= ~ATTRMASK_BOLD;
856                 if (terminal->curr_attr.fg < 16 && terminal->curr_attr.fg >= 8)
857                         terminal->curr_attr.fg -= 8;
858                 break;
859         case 24:
860                 terminal->curr_attr.a &= ~ATTRMASK_UNDERLINE;
861                 break;
862         case 25:
863                 terminal->curr_attr.a &= ~ATTRMASK_BLINK;
864                 break;
865         case 7:
866         case 26:
867                 terminal->curr_attr.a |= ATTRMASK_INVERSE;
868                 break;
869         case 27:
870                 terminal->curr_attr.a &= ~ATTRMASK_INVERSE;
871                 break;
872         case 39:
873                 terminal->curr_attr.fg = terminal->color_scheme->default_attr.fg;
874                 break;
875         case 49:
876                 terminal->curr_attr.bg = terminal->color_scheme->default_attr.bg;
877                 break;
878         default:
879                 if(code >= 30 && code <= 37) {
880                         terminal->curr_attr.fg = code - 30;
881                         if (terminal->curr_attr.a & ATTRMASK_BOLD)
882                                 terminal->curr_attr.fg += 8;
883                 } else if(code >= 40 && code <= 47) {
884                         terminal->curr_attr.bg = code - 40;
885                 } else if(code >= 256 && code < 512) {
886                         terminal->curr_attr.fg = code - 256;
887                 } else if(code >= 512 && code < 768) {
888                         terminal->curr_attr.bg = code - 512;
889                 } else {
890                         fprintf(stderr, "Unknown SGR code: %d\n", code);
891                 }
892                 break;
893         }
894 }
895
896 /* Returns 1 if c was special, otherwise 0 */
897 static int
898 handle_special_char(struct terminal *terminal, char c)
899 {
900         union utf8_char *row;
901         struct attr *attr_row;
902         
903         row = terminal_get_row(terminal, terminal->row);
904         attr_row = terminal_get_attr_row(terminal, terminal->row);
905         
906         switch(c) {
907         case '\r':
908                 terminal->column = 0;
909                 break;
910         case '\n':
911                 terminal->column = 0;
912                 /* fallthrough */
913         case '\v':
914         case '\f':
915                 terminal->row++;
916                 if(terminal->row > terminal->margin_bottom) {
917                         terminal->row = terminal->margin_bottom;
918                         terminal_scroll(terminal, +1);
919                 }
920
921                 break;
922         case '\t':
923                 memset(&row[terminal->column], ' ', (-terminal->column & 7) * sizeof(union utf8_char));
924                 attr_init(&attr_row[terminal->column], terminal->curr_attr, -terminal->column & 7);
925                 terminal->column = (terminal->column + 7) & ~7;
926                 break;
927         case '\b':
928                 if (terminal->column > 0)
929                         terminal->column--;
930                 break;
931         case '\a':
932                 /* Bell */
933                 break;
934         default:
935                 return 0;
936         }
937         
938         return 1;
939 }
940
941 static void
942 handle_char(struct terminal *terminal, union utf8_char utf8)
943 {
944         union utf8_char *row;
945         struct attr *attr_row;
946         
947         if (handle_special_char(terminal, utf8.byte[0])) return;
948         
949         /* There are a whole lot of non-characters, control codes,
950          * and formatting codes that should probably be ignored,
951          * for example: */
952         if (strncmp((char*) utf8.byte, "\xEF\xBB\xBF", 3) == 0) {
953                 /* BOM, ignore */
954                 return;
955         } 
956         
957         /* Some of these non-characters should be translated, e.g.: */
958         if (utf8.byte[0] < 32) {
959                 utf8.byte[0] = utf8.byte[0] + 64;
960         }
961         
962         /* handle right margin effects */
963         if (terminal->column >= terminal->width) {
964                 terminal->column--;
965         }
966         
967         row = terminal_get_row(terminal, terminal->row);
968         attr_row = terminal_get_attr_row(terminal, terminal->row);
969         
970         row[terminal->column] = utf8;
971         attr_row[terminal->column++] = terminal->curr_attr;
972
973         if (utf8.ch != terminal->last_char.ch)
974                 terminal->last_char = utf8;
975 }
976
977 static void
978 terminal_data(struct terminal *terminal, const char *data, size_t length)
979 {
980         int i;
981         union utf8_char utf8;
982         enum utf8_state parser_state;
983
984         for (i = 0; i < length; i++) {
985                 parser_state =
986                         utf8_next_char(&terminal->state_machine, data[i]);
987                 switch(parser_state) {
988                 case utf8state_accept:
989                         utf8.ch = terminal->state_machine.s.ch;
990                         break;
991                 case utf8state_reject:
992                         /* the unicode replacement character */
993                         utf8.byte[0] = 0xEF;
994                         utf8.byte[1] = 0xBF;
995                         utf8.byte[2] = 0xBD;
996                         utf8.byte[3] = 0x00;
997                         break;
998                 default:
999                         continue;
1000                 }
1001
1002                 /* assume escape codes never use non-ASCII characters */
1003                 if (terminal->state == STATE_ESCAPE) {
1004                         terminal->escape[terminal->escape_length++] = utf8.byte[0];
1005                         if (utf8.byte[0] == '[') {
1006                                 terminal->state = STATE_ESCAPE_CSI;
1007                                 continue;
1008                         } else if (utf8.byte[0] == '#' || utf8.byte[0] == '(' ||
1009                                 utf8.byte[0] == ')')
1010                         {
1011                                 terminal->state = STATE_ESCAPE_SPECIAL;
1012                                 continue;
1013                         } else {
1014                                 terminal->state = STATE_NORMAL;
1015                                 handle_non_csi_escape(terminal, utf8.byte[0]);
1016                                 continue;
1017                         }
1018                 } else if (terminal->state == STATE_ESCAPE_SPECIAL) {
1019                         terminal->escape[terminal->escape_length++] = utf8.byte[0];
1020                         terminal->state = STATE_NORMAL;
1021                         if (isdigit(utf8.byte[0]) || isalpha(utf8.byte[0])) {
1022                                 handle_special_escape(terminal, terminal->escape[1],
1023                                                       utf8.byte[0]);
1024                                 continue;
1025                         }
1026                 } else if (terminal->state == STATE_ESCAPE_CSI) {
1027                         if (handle_special_char(terminal, utf8.byte[0]) != 0) {
1028                                 /* do nothing */
1029                         } else if (utf8.byte[0] == '?') {
1030                                 terminal->qmark_flag = 1;
1031                         } else {
1032                                 /* Don't overflow the buffer */
1033                                 if (terminal->escape_length < MAX_ESCAPE)
1034                                         terminal->escape[terminal->escape_length++] = utf8.byte[0];
1035                                 if (terminal->escape_length >= MAX_ESCAPE)
1036                                         terminal->state = STATE_NORMAL;
1037                         }
1038                         
1039                         if (isalpha(utf8.byte[0]) || utf8.byte[0] == '@' ||
1040                                 utf8.byte[0] == '`')
1041                         {
1042                                 terminal->state = STATE_NORMAL;
1043                                 handle_escape(terminal);
1044                                 continue;
1045                         } else {
1046                                 continue;
1047                         }
1048                 }
1049
1050                 /* this is valid, because ASCII characters are never used to
1051                  * introduce a multibyte sequence in UTF-8 */
1052                 if (utf8.byte[0] == '\e') {
1053                         terminal->state = STATE_ESCAPE;
1054                         terminal->escape[0] = '\e';
1055                         terminal->escape_length = 1;
1056                         terminal->qmark_flag = 0;
1057                 } else {
1058                         handle_char(terminal, utf8);
1059                 } /* if */
1060         } /* for */
1061
1062         window_schedule_redraw(terminal->window);
1063 }
1064
1065 static void
1066 key_handler(struct window *window, uint32_t key, uint32_t sym,
1067             uint32_t state, uint32_t modifiers, void *data)
1068 {
1069         struct terminal *terminal = data;
1070         char ch[2];
1071         int len = 0;
1072
1073         switch (sym) {
1074         case XK_F11:
1075                 if (!state)
1076                         break;
1077                 terminal->fullscreen ^= 1;
1078                 window_set_fullscreen(window, terminal->fullscreen);
1079                 window_schedule_redraw(terminal->window);
1080                 break;
1081
1082         case XK_Delete:
1083                 sym = 0x04;
1084         case XK_BackSpace:
1085         case XK_Tab:
1086         case XK_Linefeed:
1087         case XK_Clear:
1088         case XK_Return:
1089         case XK_Pause:
1090         case XK_Scroll_Lock:
1091         case XK_Sys_Req:
1092         case XK_Escape:
1093                 ch[len++] = sym & 0x7f;
1094                 break;
1095
1096         case XK_Shift_L:
1097         case XK_Shift_R:
1098         case XK_Control_L:
1099         case XK_Control_R:
1100         case XK_Alt_L:
1101         case XK_Alt_R:
1102                 break;
1103
1104         default:
1105                 if (modifiers & WINDOW_MODIFIER_CONTROL)
1106                         sym = sym & 0x1f;
1107                 else if (modifiers & WINDOW_MODIFIER_ALT)
1108                         ch[len++] = 0x1b;
1109                 if (sym < 256)
1110                         ch[len++] = sym;
1111                 break;
1112         }
1113
1114         if (state && len > 0)
1115                 write(terminal->master, ch, len);
1116 }
1117
1118 static void
1119 keyboard_focus_handler(struct window *window,
1120                        struct input *device, void *data)
1121 {
1122         struct terminal *terminal = data;
1123
1124         terminal->focused = (device != NULL);
1125         window_schedule_redraw(terminal->window);
1126 }
1127
1128 static struct terminal *
1129 terminal_create(struct display *display, int fullscreen)
1130 {
1131         struct terminal *terminal;
1132         cairo_surface_t *surface;
1133         cairo_t *cr;
1134
1135         terminal = malloc(sizeof *terminal);
1136         if (terminal == NULL)
1137                 return terminal;
1138
1139         memset(terminal, 0, sizeof *terminal);
1140         terminal->fullscreen = fullscreen;
1141         terminal->color_scheme = &DEFAULT_COLORS;
1142         terminal_init(terminal);
1143         terminal->margin_top = 0;
1144         terminal->margin_bottom = 10000;  /* much too large, will be trimmed down
1145                                            * by terminal_resize */
1146         terminal->window = window_create(display, "Wayland Terminal",
1147                                          500, 400);
1148
1149         init_state_machine(&terminal->state_machine);
1150         init_color_table(terminal);
1151
1152         terminal->display = display;
1153         terminal->margin = 5;
1154
1155         window_set_fullscreen(terminal->window, terminal->fullscreen);
1156         window_set_user_data(terminal->window, terminal);
1157         window_set_redraw_handler(terminal->window, redraw_handler);
1158
1159         window_set_key_handler(terminal->window, key_handler);
1160         window_set_keyboard_focus_handler(terminal->window,
1161                                           keyboard_focus_handler);
1162
1163         surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 0, 0);
1164         cr = cairo_create(surface);
1165         terminal->font_bold = cairo_toy_font_face_create ("mono",
1166                               CAIRO_FONT_SLANT_NORMAL,
1167                               CAIRO_FONT_WEIGHT_BOLD);
1168         cairo_font_face_reference(terminal->font_bold);
1169         terminal->font_normal = cairo_toy_font_face_create ("mono",
1170                                 CAIRO_FONT_SLANT_NORMAL,
1171                                 CAIRO_FONT_WEIGHT_NORMAL);
1172         cairo_font_face_reference(terminal->font_normal);
1173         cairo_set_font_face(cr, terminal->font_normal);
1174         cairo_set_font_size(cr, 14);
1175         cairo_font_extents(cr, &terminal->extents);
1176         cairo_destroy(cr);
1177         cairo_surface_destroy(surface);
1178
1179         terminal_resize(terminal, 80, 24);
1180         terminal_draw(terminal);
1181
1182         return terminal;
1183 }
1184
1185 static gboolean
1186 io_handler(GIOChannel   *source,
1187            GIOCondition  condition,
1188            gpointer      data)
1189 {
1190         struct terminal *terminal = data;
1191         gchar buffer[256];
1192         gsize bytes_read;
1193         GError *error = NULL;
1194
1195         g_io_channel_read_chars(source, buffer, sizeof buffer,
1196                                 &bytes_read, &error);
1197
1198         terminal_data(terminal, buffer, bytes_read);
1199
1200         return TRUE;
1201 }
1202
1203 static int
1204 terminal_run(struct terminal *terminal, const char *path)
1205 {
1206         int master;
1207         pid_t pid;
1208
1209         pid = forkpty(&master, NULL, NULL, NULL);
1210         if (pid == 0) {
1211                 setenv("TERM", "vt100", 1);
1212                 if (execl(path, path, NULL)) {
1213                         printf("exec failed: %m\n");
1214                         exit(EXIT_FAILURE);
1215                 }
1216         } else if (pid < 0) {
1217                 fprintf(stderr, "failed to fork and create pty (%m).\n");
1218                 return -1;
1219         }
1220
1221         terminal->master = master;
1222         terminal->channel = g_io_channel_unix_new(master);
1223         fcntl(master, F_SETFL, O_NONBLOCK);
1224         g_io_add_watch(terminal->channel, G_IO_IN,
1225                        io_handler, terminal);
1226
1227         return 0;
1228 }
1229
1230 static const GOptionEntry option_entries[] = {
1231         { "fullscreen", 'f', 0, G_OPTION_ARG_NONE,
1232           &option_fullscreen, "Run in fullscreen mode" },
1233         { NULL }
1234 };
1235
1236 int main(int argc, char *argv[])
1237 {
1238         struct display *d;
1239         struct terminal *terminal;
1240
1241         d = display_create(&argc, &argv, option_entries);
1242         if (d == NULL) {
1243                 fprintf(stderr, "failed to create display: %m\n");
1244                 return -1;
1245         }
1246
1247         terminal = terminal_create(d, option_fullscreen);
1248         if (terminal_run(terminal, "/bin/bash"))
1249                 exit(EXIT_FAILURE);
1250
1251         display_run(d);
1252
1253         return 0;
1254 }