~cosme/ubuntu/oneiric/wayland/wayland

« back to all changes in this revision

Viewing changes to clients/terminal.c

  • Committer: Bazaar Package Importer
  • Author(s): Bryce Harrington
  • Date: 2011-02-23 18:13:23 UTC
  • mfrom: (1.1.1 upstream)
  • Revision ID: james.westby@ubuntu.com-20110223181323-xm62w5zi2adkscxq
Tags: 0.1~git20110214.e4762a6a-0ubuntu1
* Update to new git snapshot from 2011-02-14 up to commit e4762a6a. (This
  is the most recent upstream commit that doesn't require moving to a
  newer version of mesa.)
  (LP: #700986)
* copyright: Update to match current codebase, and reformat to follow
  dep5 style.

Show diffs side-by-side

added added

removed removed

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