blob: 88864c317af8939e3e5b456b0fb501d8a9a0cc1a [file] [log] [blame]
Kristian Høgsberg0c4457f2008-12-07 19:59:11 -05001/*
2 * Copyright © 2008 Kristian Høgsberg
3 *
4 * Permission to use, copy, modify, distribute, and sell this software and its
5 * documentation for any purpose is hereby granted without fee, provided that
6 * the above copyright notice appear in all copies and that both that copyright
7 * notice and this permission notice appear in supporting documentation, and
8 * that the name of the copyright holders not be used in advertising or
9 * publicity pertaining to distribution of the software without specific,
10 * written prior permission. The copyright holders make no representations
11 * about the suitability of this software for any purpose. It is provided "as
12 * is" without express or implied warranty.
13 *
14 * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
15 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
16 * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
17 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
18 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
19 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
20 * OF THIS SOFTWARE.
21 */
22
23#include <stdint.h>
24#include <stdio.h>
25#include <stdlib.h>
26#include <string.h>
27#include <fcntl.h>
28#include <unistd.h>
29#include <math.h>
30#include <time.h>
Kristian Høgsberg269d6e32008-12-07 23:17:31 -050031#include <pty.h>
Kristian Høgsbergf04e8382008-12-08 00:07:49 -050032#include <ctype.h>
Kristian Høgsberg0c4457f2008-12-07 19:59:11 -050033#include <cairo.h>
34#include <glib.h>
35
Kristian Høgsberg94adf6c2010-06-25 16:50:05 -040036#include <X11/keysym.h>
37
Kristian Høgsberg12308a42009-09-28 13:08:50 -040038#include "wayland-util.h"
Kristian Høgsberg0c4457f2008-12-07 19:59:11 -050039#include "wayland-client.h"
40#include "wayland-glib.h"
41
Kristian Høgsberg0c4457f2008-12-07 19:59:11 -050042#include "window.h"
43
Kristian Høgsberg0395f302008-12-22 12:14:50 -050044static int option_fullscreen;
Kristian Høgsberg0c4457f2008-12-07 19:59:11 -050045
Kristian Høgsberg6e83d582008-12-08 00:01:36 -050046#define MOD_SHIFT 0x01
47#define MOD_ALT 0x02
48#define MOD_CTRL 0x04
49
Callum Lowcay30eeae52011-01-07 19:46:55 +000050#define ATTRMASK_BOLD 0x01
51#define ATTRMASK_UNDERLINE 0x02
52#define ATTRMASK_BLINK 0x04
53#define ATTRMASK_INVERSE 0x08
54
Callum Lowcayb8609ad2011-01-07 19:46:57 +000055/* Buffer sizes */
56#define MAX_RESPONSE 11
57#define MAX_ESCAPE 64
58
Callum Lowcay8e57dd52011-01-07 19:46:59 +000059/* Terminal modes */
60#define MODE_SHOW_CURSOR 0x00000001
61#define MODE_INVERSE 0x00000002
62#define MODE_AUTOWRAP 0x00000004
63#define MODE_AUTOREPEAT 0x00000008
64#define MODE_LF_NEWLINE 0x00000010
Callum Lowcay69e96582011-01-07 19:47:00 +000065#define MODE_IRM 0x00000020
Callum Lowcay8e57dd52011-01-07 19:46:59 +000066
Callum Lowcay15bdc5d2011-01-07 19:46:54 +000067union utf8_char {
68 unsigned char byte[4];
69 uint32_t ch;
70};
71
72enum utf8_state {
73 utf8state_start,
74 utf8state_accept,
75 utf8state_reject,
76 utf8state_expect3,
77 utf8state_expect2,
78 utf8state_expect1
79};
80
81struct utf8_state_machine {
82 enum utf8_state state;
83 int len;
84 union utf8_char s;
85};
86
87static void
88init_state_machine(struct utf8_state_machine *machine)
89{
90 machine->state = utf8state_start;
91 machine->len = 0;
92 machine->s.ch = 0;
93}
94
95static enum utf8_state
96utf8_next_char(struct utf8_state_machine *machine, char c)
97{
98 switch(machine->state) {
99 case utf8state_start:
100 case utf8state_accept:
101 case utf8state_reject:
102 machine->s.ch = 0;
103 machine->len = 0;
104 if(c == 0xC0 || c == 0xC1) {
105 /* overlong encoding, reject */
106 machine->state = utf8state_reject;
107 } else if((c & 0x80) == 0) {
108 /* single byte, accept */
109 machine->s.byte[machine->len++] = c;
110 machine->state = utf8state_accept;
111 } else if((c & 0xC0) == 0x80) {
112 /* parser out of sync, ignore byte */
113 machine->state = utf8state_start;
114 } else if((c & 0xE0) == 0xC0) {
115 /* start of two byte sequence */
116 machine->s.byte[machine->len++] = c;
117 machine->state = utf8state_expect1;
118 } else if((c & 0xF0) == 0xE0) {
119 /* start of three byte sequence */
120 machine->s.byte[machine->len++] = c;
121 machine->state = utf8state_expect2;
122 } else if((c & 0xF8) == 0xF0) {
123 /* start of four byte sequence */
124 machine->s.byte[machine->len++] = c;
125 machine->state = utf8state_expect3;
126 } else {
127 /* overlong encoding, reject */
128 machine->state = utf8state_reject;
129 }
130 break;
131 case utf8state_expect3:
132 machine->s.byte[machine->len++] = c;
133 if((c & 0xC0) == 0x80) {
134 /* all good, continue */
135 machine->state = utf8state_expect2;
136 } else {
137 /* missing extra byte, reject */
138 machine->state = utf8state_reject;
139 }
140 break;
141 case utf8state_expect2:
142 machine->s.byte[machine->len++] = c;
143 if((c & 0xC0) == 0x80) {
144 /* all good, continue */
145 machine->state = utf8state_expect1;
146 } else {
147 /* missing extra byte, reject */
148 machine->state = utf8state_reject;
149 }
150 break;
151 case utf8state_expect1:
152 machine->s.byte[machine->len++] = c;
153 if((c & 0xC0) == 0x80) {
154 /* all good, accept */
155 machine->state = utf8state_accept;
156 } else {
157 /* missing extra byte, reject */
158 machine->state = utf8state_reject;
159 }
160 break;
161 default:
162 machine->state = utf8state_reject;
163 break;
164 }
165
166 return machine->state;
167}
168
Callum Lowcay30eeae52011-01-07 19:46:55 +0000169struct terminal_color { double r, g, b, a; };
170struct attr {
171 unsigned char fg, bg;
172 char a; /* attributes format:
173 * 76543210
174 * ilub */
175 char r; /* reserved */
176};
177struct color_scheme {
178 struct terminal_color palette[16];
179 struct terminal_color border;
180 struct attr default_attr;
181};
182
183static void
184attr_init(struct attr *data_attr, struct attr attr, int n)
185{
186 int i;
187 for (i = 0; i < n; i++) {
188 data_attr[i] = attr;
189 }
190}
191
Kristian Høgsberg0c4457f2008-12-07 19:59:11 -0500192struct terminal {
193 struct window *window;
Kristian Høgsberg43c28ee2009-01-26 23:42:46 -0500194 struct display *display;
Callum Lowcay15bdc5d2011-01-07 19:46:54 +0000195 union utf8_char *data;
Callum Lowcay8e57dd52011-01-07 19:46:59 +0000196 char *tab_ruler;
Callum Lowcay30eeae52011-01-07 19:46:55 +0000197 struct attr *data_attr;
198 struct attr curr_attr;
Callum Lowcay8e57dd52011-01-07 19:46:59 +0000199 uint32_t mode;
Callum Lowcaybbeac602011-01-07 19:46:58 +0000200 char origin_mode;
Callum Lowcay8e57dd52011-01-07 19:46:59 +0000201 char saved_origin_mode;
202 struct attr saved_attr;
Callum Lowcay15bdc5d2011-01-07 19:46:54 +0000203 union utf8_char last_char;
Callum Lowcaybbeac602011-01-07 19:46:58 +0000204 int margin_top, margin_bottom;
Callum Lowcay30eeae52011-01-07 19:46:55 +0000205 int data_pitch, attr_pitch; /* The width in bytes of a line */
Kristian Høgsbergdbd54642008-12-08 22:22:25 -0500206 int width, height, start, row, column;
Callum Lowcay8e57dd52011-01-07 19:46:59 +0000207 int saved_row, saved_column;
Kristian Høgsberg6e83d582008-12-08 00:01:36 -0500208 int fd, master;
Kristian Høgsberg269d6e32008-12-07 23:17:31 -0500209 GIOChannel *channel;
Kristian Høgsberg6e83d582008-12-08 00:01:36 -0500210 uint32_t modifiers;
Callum Lowcayb8609ad2011-01-07 19:46:57 +0000211 char escape[MAX_ESCAPE];
Kristian Høgsberg17809b12008-12-08 12:20:40 -0500212 int escape_length;
Kristian Høgsbergf04e8382008-12-08 00:07:49 -0500213 int state;
Callum Lowcayb8609ad2011-01-07 19:46:57 +0000214 int qmark_flag;
Callum Lowcay15bdc5d2011-01-07 19:46:54 +0000215 struct utf8_state_machine state_machine;
Kristian Høgsberg1584c572008-12-08 12:59:37 -0500216 int margin;
Kristian Høgsberg0395f302008-12-22 12:14:50 -0500217 int fullscreen;
Kristian Høgsberg3c248cc2009-02-22 23:01:35 -0500218 int focused;
Kristian Høgsberg12308a42009-09-28 13:08:50 -0400219 struct color_scheme *color_scheme;
Callum Lowcay30eeae52011-01-07 19:46:55 +0000220 struct terminal_color color_table[256];
Kristian Høgsberg09531622010-06-14 23:22:15 -0400221 cairo_font_extents_t extents;
Callum Lowcay30eeae52011-01-07 19:46:55 +0000222 cairo_font_face_t *font_normal, *font_bold;
Kristian Høgsberg0c4457f2008-12-07 19:59:11 -0500223};
224
Callum Lowcay8e57dd52011-01-07 19:46:59 +0000225/* Create default tab stops, every 8 characters */
226static void
227terminal_init_tabs(struct terminal *terminal)
228{
229 int i = 0;
230
231 while (i < terminal->width) {
232 if (i % 8 == 0)
233 terminal->tab_ruler[i] = 1;
234 else
235 terminal->tab_ruler[i] = 0;
236 i++;
237 }
238}
239
Callum Lowcay30eeae52011-01-07 19:46:55 +0000240static void
241terminal_init(struct terminal *terminal)
242{
243 terminal->curr_attr = terminal->color_scheme->default_attr;
Callum Lowcaybbeac602011-01-07 19:46:58 +0000244 terminal->origin_mode = 0;
Callum Lowcay8e57dd52011-01-07 19:46:59 +0000245 terminal->mode = MODE_SHOW_CURSOR |
246 MODE_AUTOREPEAT |
247 MODE_AUTOWRAP;
Callum Lowcay30eeae52011-01-07 19:46:55 +0000248
249 terminal->row = 0;
250 terminal->column = 0;
Callum Lowcay8e57dd52011-01-07 19:46:59 +0000251
252 terminal->saved_attr = terminal->curr_attr;
253 terminal->saved_origin_mode = terminal->origin_mode;
254 terminal->saved_row = terminal->row;
255 terminal->saved_column = terminal->column;
256
257 if (terminal->tab_ruler != NULL) terminal_init_tabs(terminal);
Callum Lowcay30eeae52011-01-07 19:46:55 +0000258}
259
260static void
261init_color_table(struct terminal *terminal)
262{
263 int c, r;
264 struct terminal_color *color_table = terminal->color_table;
265
266 for (c = 0; c < 256; c ++) {
267 if (c < 16) {
268 color_table[c] = terminal->color_scheme->palette[c];
269 } else if (c < 232) {
270 r = c - 16;
271 color_table[c].b = ((double)(r % 6) / 6.0); r /= 6;
272 color_table[c].g = ((double)(r % 6) / 6.0); r /= 6;
273 color_table[c].r = ((double)(r % 6) / 6.0);
274 color_table[c].a = 1.0;
275 } else {
276 r = (c - 232) * 10 + 8;
277 color_table[c].r = ((double) r) / 256.0;
278 color_table[c].g = color_table[c].r;
279 color_table[c].b = color_table[c].r;
280 color_table[c].a = 1.0;
281 }
282 }
283}
284
Callum Lowcay15bdc5d2011-01-07 19:46:54 +0000285static union utf8_char *
Kristian Høgsbergdbd54642008-12-08 22:22:25 -0500286terminal_get_row(struct terminal *terminal, int row)
287{
288 int index;
289
290 index = (row + terminal->start) % terminal->height;
291
Callum Lowcay15bdc5d2011-01-07 19:46:54 +0000292 return &terminal->data[index * terminal->width];
Kristian Høgsbergdbd54642008-12-08 22:22:25 -0500293}
294
Callum Lowcay30eeae52011-01-07 19:46:55 +0000295static struct attr*
296terminal_get_attr_row(struct terminal *terminal, int row) {
297 int index;
298
299 index = (row + terminal->start) % terminal->height;
300
301 return &terminal->data_attr[index * terminal->width];
302}
303
304static struct attr
305terminal_get_attr(struct terminal *terminal, int row, int col) {
306 return terminal_get_attr_row(terminal, row)[col];
307}
308
Kristian Høgsberg44e3c5e2008-12-07 21:51:58 -0500309static void
Callum Lowcaybbeac602011-01-07 19:46:58 +0000310terminal_scroll_buffer(struct terminal *terminal, int d)
311{
312 int i;
313
314 d = d % (terminal->height + 1);
315 terminal->start = (terminal->start + d) % terminal->height;
316 if (terminal->start < 0) terminal->start = terminal->height + terminal->start;
317 if(d < 0) {
318 d = 0 - d;
319 for(i = 0; i < d; i++) {
320 memset(terminal_get_row(terminal, i), 0, terminal->data_pitch);
321 attr_init(terminal_get_attr_row(terminal, i),
322 terminal->curr_attr, terminal->width);
323 }
324 } else {
325 for(i = terminal->height - d; i < terminal->height; i++) {
326 memset(terminal_get_row(terminal, i), 0, terminal->data_pitch);
327 attr_init(terminal_get_attr_row(terminal, i),
328 terminal->curr_attr, terminal->width);
329 }
330 }
331}
332
333static void
334terminal_scroll_window(struct terminal *terminal, int d)
335{
336 int i;
337 int window_height;
338 int from_row, to_row;
339 struct attr *dup_attr;
340
341 // scrolling range is inclusive
342 window_height = terminal->margin_bottom - terminal->margin_top + 1;
343 d = d % (window_height + 1);
344 if(d < 0) {
345 d = 0 - d;
346 to_row = terminal->margin_bottom;
347 from_row = terminal->margin_bottom - d;
348
349 for (i = 0; i < (window_height - d); i++) {
350 memcpy(terminal_get_row(terminal, to_row - i),
351 terminal_get_row(terminal, from_row - i),
352 terminal->data_pitch);
353 memcpy(terminal_get_attr_row(terminal, to_row - i),
354 terminal_get_attr_row(terminal, from_row - i),
355 terminal->attr_pitch);
356 }
357 dup_attr = terminal_get_attr_row(terminal, terminal->margin_top);
358 for (i = terminal->margin_top; i < (terminal->margin_top + d); i++) {
359 memset(terminal_get_row(terminal, i), 0, terminal->data_pitch);
360 if (i > terminal->margin_top) {
361 memcpy(terminal_get_attr_row(terminal, i),
362 dup_attr, terminal->attr_pitch);
363 }
364 }
365 } else {
366 to_row = terminal->margin_top;
367 from_row = terminal->margin_top + d;
368
369 for (i = 0; i < (window_height - d); i++) {
370 memcpy(terminal_get_row(terminal, to_row + i),
371 terminal_get_row(terminal, from_row + i),
372 terminal->data_pitch);
373 memcpy(terminal_get_attr_row(terminal, to_row + i),
374 terminal_get_attr_row(terminal, from_row + i),
375 terminal->attr_pitch);
376 }
377 dup_attr = terminal_get_attr_row(terminal, terminal->margin_bottom);
378 for (i = terminal->margin_bottom - d + 1; i <= terminal->margin_bottom; i++) {
379 memset(terminal_get_row(terminal, i), 0, terminal->data_pitch);
380 if (i < terminal->margin_bottom) {
381 memcpy(terminal_get_attr_row(terminal, i),
382 dup_attr, terminal->attr_pitch);
383 }
384 }
385 }
386}
387
388static void
389terminal_scroll(struct terminal *terminal, int d)
390{
391 if(terminal->margin_top == 0 && terminal->margin_bottom == terminal->height - 1)
392 terminal_scroll_buffer(terminal, d);
393 else
394 terminal_scroll_window(terminal, d);
395}
396
397static void
Callum Lowcay69e96582011-01-07 19:47:00 +0000398terminal_shift_line(struct terminal *terminal, int d)
399{
400 union utf8_char *row;
401 struct attr *attr_row, attr;
402
403 row = terminal_get_row(terminal, terminal->row);
404 attr_row = terminal_get_attr_row(terminal, terminal->row);
405
406 if ((terminal->width + d) <= terminal->column)
407 d = terminal->column + 1 - terminal->width;
408 if ((terminal->column + d) >= terminal->width)
409 d = terminal->width - terminal->column - 1;
410
411 if (d < 0) {
412 d = 0 - d;
413 memmove(&row[terminal->column],
414 &row[terminal->column + d],
415 (terminal->width - terminal->column - d) * sizeof(union utf8_char));
416 attr = attr_row[terminal->width - 1];
417 memmove(&attr_row[terminal->column], &attr_row[terminal->column + d],
418 (terminal->width - terminal->column - d) * sizeof(struct attr));
419 memset(&row[terminal->width - d], 0, d * sizeof(union utf8_char));
420 attr_init(&attr_row[terminal->width - d], terminal->curr_attr, d);
421 } else {
422 memmove(&row[terminal->column + d], &row[terminal->column],
423 (terminal->width - terminal->column - d) * sizeof(union utf8_char));
424 memmove(&attr_row[terminal->column + d], &attr_row[terminal->column],
425 (terminal->width - terminal->column - d) * sizeof(struct attr));
426 memset(&row[terminal->column], 0, d * sizeof(union utf8_char));
427 attr_init(&attr_row[terminal->column], terminal->curr_attr, d);
428 }
429}
430
431static void
Kristian Høgsberg22106762008-12-08 13:50:07 -0500432terminal_resize(struct terminal *terminal, int width, int height)
433{
434 size_t size;
Callum Lowcay15bdc5d2011-01-07 19:46:54 +0000435 union utf8_char *data;
Callum Lowcay30eeae52011-01-07 19:46:55 +0000436 struct attr *data_attr;
Callum Lowcay8e57dd52011-01-07 19:46:59 +0000437 char *tab_ruler;
Callum Lowcay30eeae52011-01-07 19:46:55 +0000438 int data_pitch, attr_pitch;
Kristian Høgsbergdbd54642008-12-08 22:22:25 -0500439 int i, l, total_rows, start;
Callum Lowcaya0ee21c2011-01-07 19:46:56 +0000440 struct rectangle rectangle;
441 struct winsize ws;
Kristian Høgsberg22106762008-12-08 13:50:07 -0500442
443 if (terminal->width == width && terminal->height == height)
444 return;
445
Callum Lowcay15bdc5d2011-01-07 19:46:54 +0000446 data_pitch = width * sizeof(union utf8_char);
447 size = data_pitch * height;
Kristian Høgsberg22106762008-12-08 13:50:07 -0500448 data = malloc(size);
Callum Lowcay30eeae52011-01-07 19:46:55 +0000449 attr_pitch = width * sizeof(struct attr);
450 data_attr = malloc(attr_pitch * height);
Callum Lowcay8e57dd52011-01-07 19:46:59 +0000451 tab_ruler = malloc(width);
Kristian Høgsberg22106762008-12-08 13:50:07 -0500452 memset(data, 0, size);
Callum Lowcay8e57dd52011-01-07 19:46:59 +0000453 memset(tab_ruler, 0, width);
Callum Lowcay30eeae52011-01-07 19:46:55 +0000454 attr_init(data_attr, terminal->curr_attr, width * height);
455 if (terminal->data && terminal->data_attr) {
Kristian Høgsberg22106762008-12-08 13:50:07 -0500456 if (width > terminal->width)
457 l = terminal->width;
458 else
459 l = width;
460
Kristian Høgsbergdbd54642008-12-08 22:22:25 -0500461 if (terminal->height > height) {
Kristian Høgsberg22106762008-12-08 13:50:07 -0500462 total_rows = height;
Kristian Høgsbergdbd54642008-12-08 22:22:25 -0500463 start = terminal->height - height;
Kristian Høgsberg22106762008-12-08 13:50:07 -0500464 } else {
Kristian Høgsbergdbd54642008-12-08 22:22:25 -0500465 total_rows = terminal->height;
466 start = 0;
Kristian Høgsberg22106762008-12-08 13:50:07 -0500467 }
468
Callum Lowcay15bdc5d2011-01-07 19:46:54 +0000469 for (i = 0; i < total_rows; i++) {
470 memcpy(&data[width * i],
471 terminal_get_row(terminal, i),
472 l * sizeof(union utf8_char));
Callum Lowcay30eeae52011-01-07 19:46:55 +0000473 memcpy(&data_attr[width * i],
474 terminal_get_attr_row(terminal, i),
475 l * sizeof(struct attr));
Callum Lowcay15bdc5d2011-01-07 19:46:54 +0000476 }
Kristian Høgsberg22106762008-12-08 13:50:07 -0500477
478 free(terminal->data);
Callum Lowcay30eeae52011-01-07 19:46:55 +0000479 free(terminal->data_attr);
Callum Lowcay8e57dd52011-01-07 19:46:59 +0000480 free(terminal->tab_ruler);
Kristian Høgsberg22106762008-12-08 13:50:07 -0500481 }
482
Callum Lowcay15bdc5d2011-01-07 19:46:54 +0000483 terminal->data_pitch = data_pitch;
Callum Lowcay30eeae52011-01-07 19:46:55 +0000484 terminal->attr_pitch = attr_pitch;
Kristian Høgsberg22106762008-12-08 13:50:07 -0500485 terminal->width = width;
486 terminal->height = height;
Callum Lowcaybbeac602011-01-07 19:46:58 +0000487 if(terminal->margin_bottom >= terminal->height)
488 terminal->margin_bottom = terminal->height - 1;
Kristian Høgsberg22106762008-12-08 13:50:07 -0500489 terminal->data = data;
Callum Lowcay30eeae52011-01-07 19:46:55 +0000490 terminal->data_attr = data_attr;
Callum Lowcay8e57dd52011-01-07 19:46:59 +0000491 terminal->tab_ruler = tab_ruler;
492 terminal_init_tabs(terminal);
Kristian Høgsberg22106762008-12-08 13:50:07 -0500493
Kristian Høgsbergdbd54642008-12-08 22:22:25 -0500494 if (terminal->row >= terminal->height)
495 terminal->row = terminal->height - 1;
Kristian Høgsberg22106762008-12-08 13:50:07 -0500496 if (terminal->column >= terminal->width)
497 terminal->column = terminal->width - 1;
Kristian Høgsbergdbd54642008-12-08 22:22:25 -0500498 terminal->start = 0;
Callum Lowcaya0ee21c2011-01-07 19:46:56 +0000499
500 if (!terminal->fullscreen) {
501 rectangle.width = terminal->width *
502 terminal->extents.max_x_advance + 2 * terminal->margin;
503 rectangle.height = terminal->height *
504 terminal->extents.height + 2 * terminal->margin;
505 window_set_child_size(terminal->window, &rectangle);
506 }
507
508 /* Update the window size */
509 ws.ws_row = terminal->height;
510 ws.ws_col = terminal->width;
511 window_get_child_rectangle(terminal->window, &rectangle);
512 ws.ws_xpixel = rectangle.width;
513 ws.ws_ypixel = rectangle.height;
514 ioctl(terminal->master, TIOCSWINSZ, &ws);
Kristian Høgsberg22106762008-12-08 13:50:07 -0500515}
516
Callum Lowcay30eeae52011-01-07 19:46:55 +0000517struct color_scheme DEFAULT_COLORS = {
518 {
519 {0, 0, 0, 1}, /* black */
520 {0.66, 0, 0, 1}, /* red */
521 {0 , 0.66, 0, 1}, /* green */
522 {0.66, 0.33, 0, 1}, /* orange (nicer than muddy yellow) */
523 {0 , 0 , 0.66, 1}, /* blue */
524 {0.66, 0 , 0.66, 1}, /* magenta */
525 {0, 0.66, 0.66, 1}, /* cyan */
526 {0.66, 0.66, 0.66, 1}, /* light grey */
527 {0.22, 0.33, 0.33, 1}, /* dark grey */
528 {1, 0.33, 0.33, 1}, /* high red */
529 {0.33, 1, 0.33, 1}, /* high green */
530 {1, 1, 0.33, 1}, /* high yellow */
531 {0.33, 0.33, 1, 1}, /* high blue */
532 {1, 0.33, 1, 1}, /* high magenta */
533 {0.33, 1, 1, 1}, /* high cyan */
534 {1, 1, 1, 1} /* white */
535 },
536 {0, 0, 0, 1}, /* black border */
537 {7, 0, 0, } /* bg:black (0), fg:light gray (7) */
538};
Kristian Høgsberg12308a42009-09-28 13:08:50 -0400539
Kristian Høgsberg22106762008-12-08 13:50:07 -0500540static void
Kristian Høgsberg44e3c5e2008-12-07 21:51:58 -0500541terminal_draw_contents(struct terminal *terminal)
542{
543 struct rectangle rectangle;
Kristian Høgsberg44e3c5e2008-12-07 21:51:58 -0500544 cairo_t *cr;
545 cairo_font_extents_t extents;
Callum Lowcay15bdc5d2011-01-07 19:46:54 +0000546 int top_margin, side_margin;
547 int row, col;
Callum Lowcay30eeae52011-01-07 19:46:55 +0000548 struct attr attr;
Callum Lowcay15bdc5d2011-01-07 19:46:54 +0000549 union utf8_char *p_row;
550 struct utf8_chars {
551 union utf8_char c;
552 char null;
553 } toShow;
Callum Lowcay8e57dd52011-01-07 19:46:59 +0000554 int foreground, background, bold, underline, tmp;
Callum Lowcay15bdc5d2011-01-07 19:46:54 +0000555 int text_x, text_y;
Kristian Høgsberg2aac3022009-12-21 10:04:53 -0500556 cairo_surface_t *surface;
Kristian Høgsberg3c248cc2009-02-22 23:01:35 -0500557 double d;
Kristian Høgsberg44e3c5e2008-12-07 21:51:58 -0500558
Callum Lowcay15bdc5d2011-01-07 19:46:54 +0000559 toShow.null = 0;
560
Kristian Høgsberg44e3c5e2008-12-07 21:51:58 -0500561 window_get_child_rectangle(terminal->window, &rectangle);
562
Kristian Høgsbergdcb71b62010-06-15 17:16:35 -0400563 surface = display_create_surface(terminal->display, &rectangle);
Kristian Høgsberg2aac3022009-12-21 10:04:53 -0500564 cr = cairo_create(surface);
Kristian Høgsberg44e3c5e2008-12-07 21:51:58 -0500565 cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
Kristian Høgsberg12308a42009-09-28 13:08:50 -0400566 cairo_set_source_rgba(cr,
Callum Lowcay30eeae52011-01-07 19:46:55 +0000567 terminal->color_scheme->border.r,
568 terminal->color_scheme->border.g,
569 terminal->color_scheme->border.b,
570 terminal->color_scheme->border.a);
Kristian Høgsberg44e3c5e2008-12-07 21:51:58 -0500571 cairo_paint(cr);
572 cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
Kristian Høgsberg44e3c5e2008-12-07 21:51:58 -0500573
Callum Lowcay30eeae52011-01-07 19:46:55 +0000574 cairo_set_font_face(cr, terminal->font_normal);
Kristian Høgsberg269d6e32008-12-07 23:17:31 -0500575 cairo_set_font_size(cr, 14);
Kristian Høgsberg44e3c5e2008-12-07 21:51:58 -0500576
577 cairo_font_extents(cr, &extents);
Kristian Høgsberg0395f302008-12-22 12:14:50 -0500578 side_margin = (rectangle.width - terminal->width * extents.max_x_advance) / 2;
579 top_margin = (rectangle.height - terminal->height * extents.height) / 2;
580
Callum Lowcay30eeae52011-01-07 19:46:55 +0000581 cairo_set_line_width(cr, 1.0);
582
Callum Lowcay15bdc5d2011-01-07 19:46:54 +0000583 for (row = 0; row < terminal->height; row++) {
584 p_row = terminal_get_row(terminal, row);
585 for (col = 0; col < terminal->width; col++) {
Callum Lowcay30eeae52011-01-07 19:46:55 +0000586 /* get the attributes for this character cell */
587 attr = terminal_get_attr(terminal, row, col);
588 if ((attr.a & ATTRMASK_INVERSE) ||
Callum Lowcay8e57dd52011-01-07 19:46:59 +0000589 ((terminal->mode & MODE_SHOW_CURSOR) &&
590 terminal->focused && terminal->row == row &&
591 terminal->column == col))
Callum Lowcay30eeae52011-01-07 19:46:55 +0000592 {
593 foreground = attr.bg;
594 background = attr.fg;
595 } else {
596 foreground = attr.fg;
597 background = attr.bg;
598 }
Callum Lowcay8e57dd52011-01-07 19:46:59 +0000599 if (terminal->mode & MODE_INVERSE) {
600 tmp = foreground;
601 foreground = background;
602 background = tmp;
603 }
Callum Lowcay30eeae52011-01-07 19:46:55 +0000604 bold = attr.a & (ATTRMASK_BOLD | ATTRMASK_BLINK);
605 underline = attr.a & ATTRMASK_UNDERLINE;
606
607 /* paint the background */
608 cairo_set_source_rgba(cr,
609 terminal->color_table[background].r,
610 terminal->color_table[background].g,
611 terminal->color_table[background].b,
612 terminal->color_table[background].a);
613 cairo_move_to(cr, side_margin + (col * extents.max_x_advance),
614 top_margin + (row * extents.height));
615 cairo_rel_line_to(cr, extents.max_x_advance, 0);
616 cairo_rel_line_to(cr, 0, extents.height);
617 cairo_rel_line_to(cr, -extents.max_x_advance, 0);
618 cairo_close_path(cr);
619 cairo_fill(cr);
620
Callum Lowcay15bdc5d2011-01-07 19:46:54 +0000621 /* paint the foreground */
Callum Lowcay30eeae52011-01-07 19:46:55 +0000622 if (bold)
623 cairo_set_font_face(cr, terminal->font_bold);
624 else
625 cairo_set_font_face(cr, terminal->font_normal);
626 cairo_set_source_rgba(cr,
627 terminal->color_table[foreground].r,
628 terminal->color_table[foreground].g,
629 terminal->color_table[foreground].b,
630 terminal->color_table[foreground].a);
631
Callum Lowcay15bdc5d2011-01-07 19:46:54 +0000632 text_x = side_margin + col * extents.max_x_advance;
633 text_y = top_margin + extents.ascent + row * extents.height;
Callum Lowcay30eeae52011-01-07 19:46:55 +0000634 if (underline) {
635 cairo_move_to(cr, text_x, text_y + 2);
636 cairo_line_to(cr, text_x + extents.max_x_advance, text_y + 2);
637 cairo_stroke(cr);
638 }
Callum Lowcay15bdc5d2011-01-07 19:46:54 +0000639 cairo_move_to(cr, text_x, text_y);
640
641 toShow.c = p_row[col];
642 cairo_show_text(cr, (char *) toShow.c.byte);
643 }
Kristian Høgsberg44e3c5e2008-12-07 21:51:58 -0500644 }
Kristian Høgsbergb0b82e22009-02-21 15:42:25 -0500645
Callum Lowcay8e57dd52011-01-07 19:46:59 +0000646 if ((terminal->mode & MODE_SHOW_CURSOR) && !terminal->focused) {
Callum Lowcay30eeae52011-01-07 19:46:55 +0000647 d = 0.5;
Kristian Høgsberg3c248cc2009-02-22 23:01:35 -0500648
Callum Lowcay30eeae52011-01-07 19:46:55 +0000649 cairo_set_line_width(cr, 1);
650 cairo_move_to(cr, side_margin + terminal->column * extents.max_x_advance + d,
651 top_margin + terminal->row * extents.height + d);
652 cairo_rel_line_to(cr, extents.max_x_advance - 2 * d, 0);
653 cairo_rel_line_to(cr, 0, extents.height - 2 * d);
654 cairo_rel_line_to(cr, -extents.max_x_advance + 2 * d, 0);
655 cairo_close_path(cr);
Kristian Høgsberg3c248cc2009-02-22 23:01:35 -0500656
Kristian Høgsberg3c248cc2009-02-22 23:01:35 -0500657 cairo_stroke(cr);
Callum Lowcay30eeae52011-01-07 19:46:55 +0000658 }
Kristian Høgsbergb0b82e22009-02-21 15:42:25 -0500659
Kristian Høgsberg44e3c5e2008-12-07 21:51:58 -0500660 cairo_destroy(cr);
661
Kristian Høgsberg0ac16f02009-01-15 11:37:43 -0500662 window_copy_surface(terminal->window,
663 &rectangle,
Kristian Høgsberg2aac3022009-12-21 10:04:53 -0500664 surface);
665
666 cairo_surface_destroy(surface);
Kristian Høgsberg44e3c5e2008-12-07 21:51:58 -0500667}
668
669static void
670terminal_draw(struct terminal *terminal)
671{
Kristian Høgsberg22106762008-12-08 13:50:07 -0500672 struct rectangle rectangle;
Kristian Høgsberg0395f302008-12-22 12:14:50 -0500673 int32_t width, height;
Kristian Høgsberg22106762008-12-08 13:50:07 -0500674
675 window_get_child_rectangle(terminal->window, &rectangle);
676
Kristian Høgsberg09531622010-06-14 23:22:15 -0400677 width = (rectangle.width - 2 * terminal->margin) /
678 (int32_t) terminal->extents.max_x_advance;
679 height = (rectangle.height - 2 * terminal->margin) /
680 (int32_t) terminal->extents.height;
Kristian Høgsberg22106762008-12-08 13:50:07 -0500681 terminal_resize(terminal, width, height);
682
Kristian Høgsberg44e3c5e2008-12-07 21:51:58 -0500683 window_draw(terminal->window);
684 terminal_draw_contents(terminal);
Kristian Høgsberg9d69f8e2010-09-03 14:46:38 -0400685 window_flush(terminal->window);
Kristian Høgsberg44e3c5e2008-12-07 21:51:58 -0500686}
687
Kristian Høgsberg80d746f2010-06-14 23:52:50 -0400688static void
689redraw_handler(struct window *window, void *data)
Kristian Høgsberg0c4457f2008-12-07 19:59:11 -0500690{
691 struct terminal *terminal = data;
692
Kristian Høgsberg44e3c5e2008-12-07 21:51:58 -0500693 terminal_draw(terminal);
Kristian Høgsberg0c4457f2008-12-07 19:59:11 -0500694}
695
Kristian Høgsbergf04e8382008-12-08 00:07:49 -0500696#define STATE_NORMAL 0
Kristian Høgsberg17809b12008-12-08 12:20:40 -0500697#define STATE_ESCAPE 1
Callum Lowcayb8609ad2011-01-07 19:46:57 +0000698#define STATE_ESCAPE_SPECIAL 2
699#define STATE_ESCAPE_CSI 3
Kristian Høgsberg17809b12008-12-08 12:20:40 -0500700
701static void
702terminal_data(struct terminal *terminal, const char *data, size_t length);
Kristian Høgsbergf04e8382008-12-08 00:07:49 -0500703
Kristian Høgsberg0c4457f2008-12-07 19:59:11 -0500704static void
Callum Lowcayb8609ad2011-01-07 19:46:57 +0000705handle_char(struct terminal *terminal, union utf8_char utf8);
706
707static void
Callum Lowcay30eeae52011-01-07 19:46:55 +0000708handle_sgr(struct terminal *terminal, int code);
709
710static void
Callum Lowcaybbeac602011-01-07 19:46:58 +0000711handle_term_parameter(struct terminal *terminal, int code, int sr)
712{
Callum Lowcay8e57dd52011-01-07 19:46:59 +0000713 int i;
714
Callum Lowcaybbeac602011-01-07 19:46:58 +0000715 if (terminal->qmark_flag) {
716 switch(code) {
Callum Lowcay8e57dd52011-01-07 19:46:59 +0000717 case 3: /* DECCOLM */
718 if (sr)
719 terminal_resize(terminal, 132, 24);
720 else
721 terminal_resize(terminal, 80, 24);
722
723 /* set columns, but also home cursor and clear screen */
724 terminal->row = 0; terminal->column = 0;
725 for (i = 0; i < terminal->height; i++) {
726 memset(terminal_get_row(terminal, i),
727 0, terminal->data_pitch);
728 attr_init(terminal_get_attr_row(terminal, i),
729 terminal->curr_attr, terminal->width);
730 }
731 break;
732 case 5: /* DECSCNM */
733 if (sr) terminal->mode |= MODE_INVERSE;
734 else terminal->mode &= ~MODE_INVERSE;
735 break;
Callum Lowcaybbeac602011-01-07 19:46:58 +0000736 case 6: /* DECOM */
737 terminal->origin_mode = sr;
738 if (terminal->origin_mode)
739 terminal->row = terminal->margin_top;
740 else
741 terminal->row = 0;
742 terminal->column = 0;
743 break;
Callum Lowcay8e57dd52011-01-07 19:46:59 +0000744 case 7: /* DECAWM */
745 if (sr) terminal->mode |= MODE_AUTOWRAP;
746 else terminal->mode &= ~MODE_AUTOWRAP;
747 break;
748 case 8: /* DECARM */
749 if (sr) terminal->mode |= MODE_AUTOREPEAT;
750 else terminal->mode &= ~MODE_AUTOREPEAT;
751 break;
752 case 25:
753 if (sr) terminal->mode |= MODE_SHOW_CURSOR;
754 else terminal->mode &= ~MODE_SHOW_CURSOR;
755 break;
Callum Lowcaybbeac602011-01-07 19:46:58 +0000756 default:
757 fprintf(stderr, "Unknown parameter: ?%d\n", code);
758 break;
759 }
760 } else {
761 switch(code) {
Callum Lowcay69e96582011-01-07 19:47:00 +0000762 case 4: /* IRM */
763 if (sr) terminal->mode |= MODE_IRM;
764 else terminal->mode &= ~MODE_IRM;
765 break;
Callum Lowcay8e57dd52011-01-07 19:46:59 +0000766 case 20: /* LNM */
767 if (sr) terminal->mode |= MODE_LF_NEWLINE;
768 else terminal->mode &= ~MODE_LF_NEWLINE;
769 break;
Callum Lowcaybbeac602011-01-07 19:46:58 +0000770 default:
771 fprintf(stderr, "Unknown parameter: %d\n", code);
772 break;
773 }
774 }
775}
776
777static void
Kristian Høgsberg17809b12008-12-08 12:20:40 -0500778handle_escape(struct terminal *terminal)
779{
Callum Lowcay15bdc5d2011-01-07 19:46:54 +0000780 union utf8_char *row;
Callum Lowcay30eeae52011-01-07 19:46:55 +0000781 struct attr *attr_row;
Callum Lowcay15bdc5d2011-01-07 19:46:54 +0000782 char *p;
Callum Lowcay8e57dd52011-01-07 19:46:59 +0000783 int i, count, x, y, top, bottom;
Kristian Høgsbergdbd54642008-12-08 22:22:25 -0500784 int args[10], set[10] = { 0, };
Callum Lowcay8e57dd52011-01-07 19:46:59 +0000785 char response[MAX_RESPONSE] = {0, };
Kristian Høgsberg17809b12008-12-08 12:20:40 -0500786
787 terminal->escape[terminal->escape_length++] = '\0';
Kristian Høgsbergdbd54642008-12-08 22:22:25 -0500788 i = 0;
789 p = &terminal->escape[2];
790 while ((isdigit(*p) || *p == ';') && i < 10) {
791 if (*p == ';') {
Callum Lowcay30eeae52011-01-07 19:46:55 +0000792 if (!set[i]) {
793 args[i] = 0;
794 set[i] = 1;
795 }
Kristian Høgsbergdbd54642008-12-08 22:22:25 -0500796 p++;
797 i++;
798 } else {
799 args[i] = strtol(p, &p, 10);
800 set[i] = 1;
Kristian Høgsberg17809b12008-12-08 12:20:40 -0500801 }
Kristian Høgsberg17809b12008-12-08 12:20:40 -0500802 }
Kristian Høgsbergdbd54642008-12-08 22:22:25 -0500803
804 switch (*p) {
Callum Lowcay69e96582011-01-07 19:47:00 +0000805 case '@': /* ICH */
806 count = set[0] ? args[0] : 1;
807 if (count == 0) count = 1;
808 terminal_shift_line(terminal, count);
809 break;
Callum Lowcay8e57dd52011-01-07 19:46:59 +0000810 case 'A': /* CUU */
Kristian Høgsbergdbd54642008-12-08 22:22:25 -0500811 count = set[0] ? args[0] : 1;
Callum Lowcay8e57dd52011-01-07 19:46:59 +0000812 if (count == 0) count = 1;
813 if (terminal->row - count >= terminal->margin_top)
Kristian Høgsbergdbd54642008-12-08 22:22:25 -0500814 terminal->row -= count;
815 else
Callum Lowcay8e57dd52011-01-07 19:46:59 +0000816 terminal->row = terminal->margin_top;
Kristian Høgsbergdbd54642008-12-08 22:22:25 -0500817 break;
Callum Lowcay8e57dd52011-01-07 19:46:59 +0000818 case 'B': /* CUD */
Kristian Høgsbergdbd54642008-12-08 22:22:25 -0500819 count = set[0] ? args[0] : 1;
Callum Lowcay8e57dd52011-01-07 19:46:59 +0000820 if (count == 0) count = 1;
821 if (terminal->row + count <= terminal->margin_bottom)
Kristian Høgsbergdbd54642008-12-08 22:22:25 -0500822 terminal->row += count;
823 else
Callum Lowcay8e57dd52011-01-07 19:46:59 +0000824 terminal->row = terminal->margin_bottom;
Kristian Høgsbergdbd54642008-12-08 22:22:25 -0500825 break;
Callum Lowcay8e57dd52011-01-07 19:46:59 +0000826 case 'C': /* CUF */
Kristian Høgsbergdbd54642008-12-08 22:22:25 -0500827 count = set[0] ? args[0] : 1;
Callum Lowcay8e57dd52011-01-07 19:46:59 +0000828 if (count == 0) count = 1;
829 if ((terminal->column + count) < terminal->width)
Kristian Høgsbergdbd54642008-12-08 22:22:25 -0500830 terminal->column += count;
831 else
Callum Lowcay8e57dd52011-01-07 19:46:59 +0000832 terminal->column = terminal->width - 1;
Kristian Høgsbergdbd54642008-12-08 22:22:25 -0500833 break;
Callum Lowcay8e57dd52011-01-07 19:46:59 +0000834 case 'D': /* CUB */
Kristian Høgsbergdbd54642008-12-08 22:22:25 -0500835 count = set[0] ? args[0] : 1;
Callum Lowcay8e57dd52011-01-07 19:46:59 +0000836 if (count == 0) count = 1;
837 if ((terminal->column - count) >= 0)
Kristian Høgsbergdbd54642008-12-08 22:22:25 -0500838 terminal->column -= count;
839 else
840 terminal->column = 0;
841 break;
Callum Lowcay8e57dd52011-01-07 19:46:59 +0000842 case 'E': /* CNL */
843 count = set[0] ? args[0] : 1;
844 if (terminal->row + count <= terminal->margin_bottom)
845 terminal->row += count;
846 else
847 terminal->row = terminal->margin_bottom;
848 terminal->column = 0;
849 break;
850 case 'F': /* CPL */
851 count = set[0] ? args[0] : 1;
852 if (terminal->row - count >= terminal->margin_top)
853 terminal->row -= count;
854 else
855 terminal->row = terminal->margin_top;
856 terminal->column = 0;
857 break;
858 case 'G': /* CHA */
859 y = set[0] ? args[0] : 1;
860 y = y <= 0 ? 1 : y > terminal->width ? terminal->width : y;
861
862 terminal->column = y - 1;
863 break;
864 case 'f': /* HVP */
865 case 'H': /* CUP */
866 x = (set[1] ? args[1] : 1) - 1;
867 x = x < 0 ? 0 :
868 (x >= terminal->width ? terminal->width - 1 : x);
869
870 y = (set[0] ? args[0] : 1) - 1;
871 if (terminal->origin_mode) {
872 y += terminal->margin_top;
873 y = y < terminal->margin_top ? terminal->margin_top :
874 (y > terminal->margin_bottom ? terminal->margin_bottom : y);
875 } else {
876 y = y < 0 ? 0 :
877 (y >= terminal->height ? terminal->height - 1 : y);
878 }
879
880 terminal->row = y;
881 terminal->column = x;
882 break;
883 case 'I': /* CHT */
884 count = set[0] ? args[0] : 1;
885 if (count == 0) count = 1;
886 while (count > 0 && terminal->column < terminal->width) {
887 if (terminal->tab_ruler[terminal->column]) count--;
888 terminal->column++;
889 }
890 terminal->column--;
891 break;
892 case 'J': /* ED */
Kristian Høgsbergdbd54642008-12-08 22:22:25 -0500893 row = terminal_get_row(terminal, terminal->row);
Callum Lowcay30eeae52011-01-07 19:46:55 +0000894 attr_row = terminal_get_attr_row(terminal, terminal->row);
Callum Lowcay8e57dd52011-01-07 19:46:59 +0000895 if (!set[0] || args[0] == 0 || args[0] > 2) {
896 memset(&row[terminal->column],
897 0, (terminal->width - terminal->column) * sizeof(union utf8_char));
898 attr_init(&attr_row[terminal->column],
899 terminal->curr_attr, terminal->width - terminal->column);
900 for (i = terminal->row + 1; i < terminal->height; i++) {
901 memset(terminal_get_row(terminal, i),
902 0, terminal->data_pitch);
903 attr_init(terminal_get_attr_row(terminal, i),
904 terminal->curr_attr, terminal->width);
905 }
906 } else if (args[0] == 1) {
907 memset(row, 0, (terminal->column+1) * sizeof(union utf8_char));
908 attr_init(attr_row, terminal->curr_attr, terminal->column+1);
909 for (i = 0; i < terminal->row; i++) {
910 memset(terminal_get_row(terminal, i),
911 0, terminal->data_pitch);
912 attr_init(terminal_get_attr_row(terminal, i),
913 terminal->curr_attr, terminal->width);
914 }
915 } else if (args[0] == 2) {
916 for (i = 0; i < terminal->height; i++) {
917 memset(terminal_get_row(terminal, i),
918 0, terminal->data_pitch);
919 attr_init(terminal_get_attr_row(terminal, i),
920 terminal->curr_attr, terminal->width);
921 }
Callum Lowcay30eeae52011-01-07 19:46:55 +0000922 }
Kristian Høgsbergdbd54642008-12-08 22:22:25 -0500923 break;
Callum Lowcay8e57dd52011-01-07 19:46:59 +0000924 case 'K': /* EL */
Kristian Høgsbergdbd54642008-12-08 22:22:25 -0500925 row = terminal_get_row(terminal, terminal->row);
Callum Lowcay30eeae52011-01-07 19:46:55 +0000926 attr_row = terminal_get_attr_row(terminal, terminal->row);
Callum Lowcay8e57dd52011-01-07 19:46:59 +0000927 if (!set[0] || args[0] == 0 || args[0] > 2) {
928 memset(&row[terminal->column], 0,
929 (terminal->width - terminal->column) * sizeof(union utf8_char));
930 attr_init(&attr_row[terminal->column], terminal->curr_attr,
931 terminal->width - terminal->column);
932 } else if (args[0] == 1) {
933 memset(row, 0, (terminal->column+1) * sizeof(union utf8_char));
934 attr_init(attr_row, terminal->curr_attr, terminal->column+1);
935 } else if (args[0] == 2) {
936 memset(row, 0, terminal->data_pitch);
937 attr_init(attr_row, terminal->curr_attr, terminal->width);
938 }
Kristian Høgsbergdbd54642008-12-08 22:22:25 -0500939 break;
Callum Lowcay69e96582011-01-07 19:47:00 +0000940 case 'L': /* IL */
941 count = set[0] ? args[0] : 1;
942 if (count == 0) count = 1;
943 if (terminal->row >= terminal->margin_top &&
944 terminal->row < terminal->margin_bottom)
945 {
946 top = terminal->margin_top;
947 terminal->margin_top = terminal->row;
948 terminal_scroll(terminal, 0 - count);
949 terminal->margin_top = top;
950 } else if (terminal->row == terminal->margin_bottom) {
951 memset(terminal_get_row(terminal, terminal->row),
952 0, terminal->data_pitch);
953 attr_init(terminal_get_attr_row(terminal, terminal->row),
954 terminal->curr_attr, terminal->width);
955 }
956 break;
957 case 'M': /* DL */
958 count = set[0] ? args[0] : 1;
959 if (count == 0) count = 1;
960 if (terminal->row >= terminal->margin_top &&
961 terminal->row < terminal->margin_bottom)
962 {
963 top = terminal->margin_top;
964 terminal->margin_top = terminal->row;
965 terminal_scroll(terminal, count);
966 terminal->margin_top = top;
967 } else if (terminal->row == terminal->margin_bottom) {
968 memset(terminal_get_row(terminal, terminal->row),
969 0, terminal->data_pitch);
970 }
971 break;
972 case 'P': /* DCH */
973 count = set[0] ? args[0] : 1;
974 if (count == 0) count = 1;
975 terminal_shift_line(terminal, 0 - count);
976 break;
Callum Lowcaybbeac602011-01-07 19:46:58 +0000977 case 'S': /* SU */
978 terminal_scroll(terminal, set[0] ? args[0] : 1);
979 break;
980 case 'T': /* SD */
981 terminal_scroll(terminal, 0 - (set[0] ? args[0] : 1));
982 break;
Callum Lowcay69e96582011-01-07 19:47:00 +0000983 case 'X': /* ECH */
984 count = set[0] ? args[0] : 1;
985 if (count == 0) count = 1;
986 if ((terminal->column + count) > terminal->width)
987 count = terminal->width - terminal->column;
988 row = terminal_get_row(terminal, terminal->row);
989 attr_row = terminal_get_attr_row(terminal, terminal->row);
990 memset(&row[terminal->column], 0, count * sizeof(union utf8_char));
991 attr_init(&attr_row[terminal->column], terminal->curr_attr, count);
992 break;
Callum Lowcay8e57dd52011-01-07 19:46:59 +0000993 case 'Z': /* CBT */
994 count = set[0] ? args[0] : 1;
995 if (count == 0) count = 1;
996 while (count > 0 && terminal->column >= 0) {
997 if (terminal->tab_ruler[terminal->column]) count--;
998 terminal->column--;
999 }
1000 terminal->column++;
1001 break;
1002 case '`': /* HPA */
1003 y = set[0] ? args[0] : 1;
1004 y = y <= 0 ? 1 : y > terminal->width ? terminal->width : y;
1005
1006 terminal->column = y - 1;
1007 break;
1008 case 'b': /* REP */
1009 count = set[0] ? args[0] : 1;
1010 if (count == 0) count = 1;
1011 if (terminal->last_char.byte[0])
1012 for (i = 0; i < count; i++)
1013 handle_char(terminal, terminal->last_char);
1014 terminal->last_char.byte[0] = 0;
1015 break;
1016 case 'c': /* Primary DA */
Callum Lowcay69e96582011-01-07 19:47:00 +00001017 write(terminal->master, "\e[?6c", 5);
Callum Lowcay8e57dd52011-01-07 19:46:59 +00001018 sleep(1);
1019 break;
1020 case 'd': /* VPA */
1021 x = set[0] ? args[0] : 1;
1022 x = x <= 0 ? 1 : x > terminal->height ? terminal->height : x;
1023
1024 terminal->row = x - 1;
1025 break;
1026 case 'g': /* TBC */
1027 if (!set[0] || args[0] == 0) {
1028 terminal->tab_ruler[terminal->column] = 0;
1029 } else if (args[0] == 3) {
1030 memset(terminal->tab_ruler, 0, terminal->width);
1031 }
1032 break;
Callum Lowcaybbeac602011-01-07 19:46:58 +00001033 case 'h': /* SM */
1034 for(i = 0; i < 10 && set[i]; i++) {
1035 handle_term_parameter(terminal, args[i], 1);
1036 }
1037 break;
1038 case 'l': /* RM */
1039 for(i = 0; i < 10 && set[i]; i++) {
1040 handle_term_parameter(terminal, args[i], 0);
1041 }
1042 break;
Callum Lowcay30eeae52011-01-07 19:46:55 +00001043 case 'm': /* SGR */
1044 if (set[0] && set[1] && set[2] && args[1] == 5) {
1045 if (args[0] == 38) {
1046 handle_sgr(terminal, args[2] + 256);
1047 break;
1048 } else if (args[0] == 48) {
1049 handle_sgr(terminal, args[2] + 512);
1050 break;
1051 }
1052 }
1053 for(i = 0; i < 10; i++) {
1054 if(set[i]) {
1055 handle_sgr(terminal, args[i]);
1056 } else if(i == 0) {
1057 handle_sgr(terminal, 0);
1058 break;
1059 } else {
1060 break;
1061 }
1062 }
Kristian Høgsbergdbd54642008-12-08 22:22:25 -05001063 break;
Callum Lowcay8e57dd52011-01-07 19:46:59 +00001064 case 'n': /* DSR */
1065 i = set[0] ? args[0] : 0;
1066 if (i == 0 || i == 5) {
1067 write(terminal->master, "\e[0n", 4);
1068 } else if (i == 6) {
1069 snprintf(response, MAX_RESPONSE, "\e[%d;%dR",
1070 terminal->origin_mode ?
1071 terminal->row+terminal->margin_top : terminal->row+1,
1072 terminal->column+1);
1073 write(terminal->master, response, strlen(response));
1074 }
1075 sleep(1); /* is this required? why? */
1076 break;
Callum Lowcaybbeac602011-01-07 19:46:58 +00001077 case 'r':
1078 if(!set[0]) {
1079 terminal->margin_top = 0;
1080 terminal->margin_bottom = terminal->height-1;
1081 terminal->row = 0;
1082 terminal->column = 0;
1083 } else {
1084 top = (set[0] ? args[0] : 1) - 1;
1085 top = top < 0 ? 0 :
1086 (top >= terminal->height ? terminal->height - 1 : top);
1087 bottom = (set[1] ? args[1] : 1) - 1;
1088 bottom = bottom < 0 ? 0 :
1089 (bottom >= terminal->height ? terminal->height - 1 : bottom);
1090 if(bottom > top) {
1091 terminal->margin_top = top;
1092 terminal->margin_bottom = bottom;
1093 } else {
1094 terminal->margin_top = 0;
1095 terminal->margin_bottom = terminal->height-1;
1096 }
1097 if(terminal->origin_mode)
1098 terminal->row = terminal->margin_top;
1099 else
1100 terminal->row = 0;
1101 terminal->column = 0;
Kristian Høgsbergdbd54642008-12-08 22:22:25 -05001102 }
1103 break;
Callum Lowcay8e57dd52011-01-07 19:46:59 +00001104 case 's':
1105 terminal->saved_row = terminal->row;
1106 terminal->saved_column = terminal->column;
1107 break;
1108 case 'u':
1109 terminal->row = terminal->saved_row;
1110 terminal->column = terminal->saved_column;
1111 break;
Kristian Høgsbergdbd54642008-12-08 22:22:25 -05001112 default:
Callum Lowcayb8609ad2011-01-07 19:46:57 +00001113 fprintf(stderr, "Unknown CSI escape: %c\n", *p);
Kristian Høgsbergdbd54642008-12-08 22:22:25 -05001114 break;
1115 }
Kristian Høgsberg17809b12008-12-08 12:20:40 -05001116}
1117
1118static void
Callum Lowcayb8609ad2011-01-07 19:46:57 +00001119handle_non_csi_escape(struct terminal *terminal, char code)
1120{
Callum Lowcaybbeac602011-01-07 19:46:58 +00001121 switch(code) {
1122 case 'M': /* RI */
1123 terminal->row -= 1;
1124 if(terminal->row < terminal->margin_top) {
1125 terminal->row = terminal->margin_top;
1126 terminal_scroll(terminal, -1);
1127 }
1128 break;
1129 case 'E': /* NEL */
1130 terminal->column = 0;
1131 // fallthrough
1132 case 'D': /* IND */
1133 terminal->row += 1;
1134 if(terminal->row > terminal->margin_bottom) {
1135 terminal->row = terminal->margin_bottom;
1136 terminal_scroll(terminal, +1);
1137 }
1138 break;
Callum Lowcay8e57dd52011-01-07 19:46:59 +00001139 case 'c': /* RIS */
1140 terminal_init(terminal);
1141 break;
1142 case 'H': /* HTS */
1143 terminal->tab_ruler[terminal->column] = 1;
1144 break;
1145 case '7': /* DECSC */
1146 terminal->saved_row = terminal->row;
1147 terminal->saved_column = terminal->column;
1148 terminal->saved_attr = terminal->curr_attr;
1149 terminal->saved_origin_mode = terminal->origin_mode;
1150 break;
1151 case '8': /* DECRC */
1152 terminal->row = terminal->saved_row;
1153 terminal->column = terminal->saved_column;
1154 terminal->curr_attr = terminal->saved_attr;
1155 terminal->origin_mode = terminal->saved_origin_mode;
1156 break;
Callum Lowcaybbeac602011-01-07 19:46:58 +00001157 default:
1158 fprintf(stderr, "Unknown escape code: %c\n", code);
1159 break;
1160 }
Callum Lowcayb8609ad2011-01-07 19:46:57 +00001161}
1162
1163static void
1164handle_special_escape(struct terminal *terminal, char special, char code)
1165{
Callum Lowcay8e57dd52011-01-07 19:46:59 +00001166 int i, numChars;
1167
1168 if (special == '#') {
1169 switch(code) {
1170 case '8':
1171 /* fill with 'E', no cheap way to do this */
1172 memset(terminal->data, 0, terminal->data_pitch * terminal->height);
1173 numChars = terminal->width * terminal->height;
1174 for(i = 0; i < numChars; i++) {
1175 terminal->data[i].byte[0] = 'E';
1176 }
1177 break;
1178 default:
1179 fprintf(stderr, "Unknown HASH escape #%c\n", code);
1180 break;
1181 }
1182 } else {
1183 fprintf(stderr, "Unknown special escape %c%c\n", special, code);
1184 }
Callum Lowcayb8609ad2011-01-07 19:46:57 +00001185}
1186
1187static void
Callum Lowcay30eeae52011-01-07 19:46:55 +00001188handle_sgr(struct terminal *terminal, int code)
1189{
1190 switch(code) {
1191 case 0:
1192 terminal->curr_attr = terminal->color_scheme->default_attr;
1193 break;
1194 case 1:
1195 terminal->curr_attr.a |= ATTRMASK_BOLD;
1196 if (terminal->curr_attr.fg < 8)
1197 terminal->curr_attr.fg += 8;
1198 break;
1199 case 4:
1200 terminal->curr_attr.a |= ATTRMASK_UNDERLINE;
1201 break;
1202 case 5:
1203 terminal->curr_attr.a |= ATTRMASK_BLINK;
1204 break;
1205 case 2:
1206 case 21:
1207 case 22:
1208 terminal->curr_attr.a &= ~ATTRMASK_BOLD;
1209 if (terminal->curr_attr.fg < 16 && terminal->curr_attr.fg >= 8)
1210 terminal->curr_attr.fg -= 8;
1211 break;
1212 case 24:
1213 terminal->curr_attr.a &= ~ATTRMASK_UNDERLINE;
1214 break;
1215 case 25:
1216 terminal->curr_attr.a &= ~ATTRMASK_BLINK;
1217 break;
1218 case 7:
1219 case 26:
1220 terminal->curr_attr.a |= ATTRMASK_INVERSE;
1221 break;
1222 case 27:
1223 terminal->curr_attr.a &= ~ATTRMASK_INVERSE;
1224 break;
1225 case 39:
1226 terminal->curr_attr.fg = terminal->color_scheme->default_attr.fg;
1227 break;
1228 case 49:
1229 terminal->curr_attr.bg = terminal->color_scheme->default_attr.bg;
1230 break;
1231 default:
1232 if(code >= 30 && code <= 37) {
1233 terminal->curr_attr.fg = code - 30;
1234 if (terminal->curr_attr.a & ATTRMASK_BOLD)
1235 terminal->curr_attr.fg += 8;
1236 } else if(code >= 40 && code <= 47) {
1237 terminal->curr_attr.bg = code - 40;
1238 } else if(code >= 256 && code < 512) {
1239 terminal->curr_attr.fg = code - 256;
1240 } else if(code >= 512 && code < 768) {
1241 terminal->curr_attr.bg = code - 512;
1242 } else {
1243 fprintf(stderr, "Unknown SGR code: %d\n", code);
1244 }
1245 break;
1246 }
1247}
1248
Callum Lowcayb8609ad2011-01-07 19:46:57 +00001249/* Returns 1 if c was special, otherwise 0 */
1250static int
1251handle_special_char(struct terminal *terminal, char c)
1252{
1253 union utf8_char *row;
1254 struct attr *attr_row;
1255
1256 row = terminal_get_row(terminal, terminal->row);
1257 attr_row = terminal_get_attr_row(terminal, terminal->row);
1258
1259 switch(c) {
1260 case '\r':
1261 terminal->column = 0;
1262 break;
1263 case '\n':
Callum Lowcay8e57dd52011-01-07 19:46:59 +00001264 if (terminal->mode & MODE_LF_NEWLINE) {
1265 terminal->column = 0;
1266 }
Callum Lowcayb8609ad2011-01-07 19:46:57 +00001267 /* fallthrough */
1268 case '\v':
1269 case '\f':
Callum Lowcaybbeac602011-01-07 19:46:58 +00001270 terminal->row++;
1271 if(terminal->row > terminal->margin_bottom) {
1272 terminal->row = terminal->margin_bottom;
1273 terminal_scroll(terminal, +1);
Callum Lowcayb8609ad2011-01-07 19:46:57 +00001274 }
1275
1276 break;
1277 case '\t':
Callum Lowcay8e57dd52011-01-07 19:46:59 +00001278 while (terminal->column < terminal->width) {
1279 if (terminal->tab_ruler[terminal->column]) break;
Callum Lowcay69e96582011-01-07 19:47:00 +00001280 if (terminal->mode & MODE_IRM)
1281 terminal_shift_line(terminal, +1);
Callum Lowcay8e57dd52011-01-07 19:46:59 +00001282 row[terminal->column].byte[0] = ' ';
1283 row[terminal->column].byte[1] = '\0';
1284 attr_row[terminal->column] = terminal->curr_attr;
1285 terminal->column++;
1286 }
1287 if (terminal->column >= terminal->width) {
1288 terminal->column = terminal->width - 1;
1289 }
1290
Callum Lowcayb8609ad2011-01-07 19:46:57 +00001291 break;
1292 case '\b':
Callum Lowcay8e57dd52011-01-07 19:46:59 +00001293 if (terminal->column >= terminal->width) {
1294 terminal->column = terminal->width - 2;
1295 } else if (terminal->column > 0) {
Callum Lowcayb8609ad2011-01-07 19:46:57 +00001296 terminal->column--;
Callum Lowcay8e57dd52011-01-07 19:46:59 +00001297 } else if (terminal->mode & MODE_AUTOWRAP) {
1298 terminal->column = terminal->width - 1;
1299 terminal->row -= 1;
1300 if (terminal->row < terminal->margin_top) {
1301 terminal->row = terminal->margin_top;
1302 terminal_scroll(terminal, -1);
1303 }
1304 }
1305
Callum Lowcayb8609ad2011-01-07 19:46:57 +00001306 break;
1307 case '\a':
1308 /* Bell */
1309 break;
1310 default:
1311 return 0;
1312 }
1313
1314 return 1;
1315}
1316
1317static void
1318handle_char(struct terminal *terminal, union utf8_char utf8)
1319{
1320 union utf8_char *row;
1321 struct attr *attr_row;
1322
1323 if (handle_special_char(terminal, utf8.byte[0])) return;
1324
1325 /* There are a whole lot of non-characters, control codes,
1326 * and formatting codes that should probably be ignored,
1327 * for example: */
1328 if (strncmp((char*) utf8.byte, "\xEF\xBB\xBF", 3) == 0) {
1329 /* BOM, ignore */
1330 return;
1331 }
1332
1333 /* Some of these non-characters should be translated, e.g.: */
1334 if (utf8.byte[0] < 32) {
1335 utf8.byte[0] = utf8.byte[0] + 64;
1336 }
1337
1338 /* handle right margin effects */
1339 if (terminal->column >= terminal->width) {
Callum Lowcay8e57dd52011-01-07 19:46:59 +00001340 if (terminal->mode & MODE_AUTOWRAP) {
1341 terminal->column = 0;
1342 terminal->row += 1;
1343 if (terminal->row > terminal->margin_bottom) {
1344 terminal->row = terminal->margin_bottom;
1345 terminal_scroll(terminal, +1);
1346 }
1347 } else {
1348 terminal->column--;
1349 }
1350 }
Callum Lowcayb8609ad2011-01-07 19:46:57 +00001351
1352 row = terminal_get_row(terminal, terminal->row);
1353 attr_row = terminal_get_attr_row(terminal, terminal->row);
1354
Callum Lowcay69e96582011-01-07 19:47:00 +00001355 if (terminal->mode & MODE_IRM)
1356 terminal_shift_line(terminal, +1);
Callum Lowcayb8609ad2011-01-07 19:46:57 +00001357 row[terminal->column] = utf8;
1358 attr_row[terminal->column++] = terminal->curr_attr;
1359
1360 if (utf8.ch != terminal->last_char.ch)
1361 terminal->last_char = utf8;
1362}
1363
Callum Lowcay30eeae52011-01-07 19:46:55 +00001364static void
Kristian Høgsberg269d6e32008-12-07 23:17:31 -05001365terminal_data(struct terminal *terminal, const char *data, size_t length)
1366{
1367 int i;
Callum Lowcay15bdc5d2011-01-07 19:46:54 +00001368 union utf8_char utf8;
1369 enum utf8_state parser_state;
Kristian Høgsberg269d6e32008-12-07 23:17:31 -05001370
1371 for (i = 0; i < length; i++) {
Callum Lowcay15bdc5d2011-01-07 19:46:54 +00001372 parser_state =
1373 utf8_next_char(&terminal->state_machine, data[i]);
1374 switch(parser_state) {
1375 case utf8state_accept:
1376 utf8.ch = terminal->state_machine.s.ch;
1377 break;
1378 case utf8state_reject:
1379 /* the unicode replacement character */
1380 utf8.byte[0] = 0xEF;
1381 utf8.byte[1] = 0xBF;
1382 utf8.byte[2] = 0xBD;
1383 utf8.byte[3] = 0x00;
1384 break;
1385 default:
1386 continue;
1387 }
1388
Callum Lowcayb8609ad2011-01-07 19:46:57 +00001389 /* assume escape codes never use non-ASCII characters */
Kristian Høgsberg17809b12008-12-08 12:20:40 -05001390 if (terminal->state == STATE_ESCAPE) {
Callum Lowcay15bdc5d2011-01-07 19:46:54 +00001391 terminal->escape[terminal->escape_length++] = utf8.byte[0];
Callum Lowcayb8609ad2011-01-07 19:46:57 +00001392 if (utf8.byte[0] == '[') {
1393 terminal->state = STATE_ESCAPE_CSI;
1394 continue;
1395 } else if (utf8.byte[0] == '#' || utf8.byte[0] == '(' ||
1396 utf8.byte[0] == ')')
1397 {
1398 terminal->state = STATE_ESCAPE_SPECIAL;
1399 continue;
1400 } else {
Kristian Høgsbergf04e8382008-12-08 00:07:49 -05001401 terminal->state = STATE_NORMAL;
Callum Lowcayb8609ad2011-01-07 19:46:57 +00001402 handle_non_csi_escape(terminal, utf8.byte[0]);
1403 continue;
Kristian Høgsberg17809b12008-12-08 12:20:40 -05001404 }
Callum Lowcayb8609ad2011-01-07 19:46:57 +00001405 } else if (terminal->state == STATE_ESCAPE_SPECIAL) {
1406 terminal->escape[terminal->escape_length++] = utf8.byte[0];
1407 terminal->state = STATE_NORMAL;
1408 if (isdigit(utf8.byte[0]) || isalpha(utf8.byte[0])) {
1409 handle_special_escape(terminal, terminal->escape[1],
1410 utf8.byte[0]);
1411 continue;
1412 }
1413 } else if (terminal->state == STATE_ESCAPE_CSI) {
1414 if (handle_special_char(terminal, utf8.byte[0]) != 0) {
1415 /* do nothing */
1416 } else if (utf8.byte[0] == '?') {
1417 terminal->qmark_flag = 1;
1418 } else {
1419 /* Don't overflow the buffer */
1420 if (terminal->escape_length < MAX_ESCAPE)
1421 terminal->escape[terminal->escape_length++] = utf8.byte[0];
1422 if (terminal->escape_length >= MAX_ESCAPE)
1423 terminal->state = STATE_NORMAL;
1424 }
1425
1426 if (isalpha(utf8.byte[0]) || utf8.byte[0] == '@' ||
1427 utf8.byte[0] == '`')
1428 {
Kristian Høgsberg17809b12008-12-08 12:20:40 -05001429 terminal->state = STATE_NORMAL;
1430 handle_escape(terminal);
Callum Lowcayb8609ad2011-01-07 19:46:57 +00001431 continue;
1432 } else {
1433 continue;
1434 }
Kristian Høgsbergf04e8382008-12-08 00:07:49 -05001435 }
1436
Callum Lowcayb8609ad2011-01-07 19:46:57 +00001437 /* this is valid, because ASCII characters are never used to
1438 * introduce a multibyte sequence in UTF-8 */
1439 if (utf8.byte[0] == '\e') {
Kristian Høgsberg17809b12008-12-08 12:20:40 -05001440 terminal->state = STATE_ESCAPE;
1441 terminal->escape[0] = '\e';
1442 terminal->escape_length = 1;
Callum Lowcayb8609ad2011-01-07 19:46:57 +00001443 terminal->qmark_flag = 0;
1444 } else {
1445 handle_char(terminal, utf8);
1446 } /* if */
1447 } /* for */
Kristian Høgsberg721f09f2008-12-08 11:13:26 -05001448
Kristian Høgsberg80d746f2010-06-14 23:52:50 -04001449 window_schedule_redraw(terminal->window);
Kristian Høgsberg6e83d582008-12-08 00:01:36 -05001450}
1451
1452static void
Kristian Høgsberg94adf6c2010-06-25 16:50:05 -04001453key_handler(struct window *window, uint32_t key, uint32_t sym,
Kristian Høgsberg55444912009-02-21 14:31:09 -05001454 uint32_t state, uint32_t modifiers, void *data)
Kristian Høgsberg6e83d582008-12-08 00:01:36 -05001455{
1456 struct terminal *terminal = data;
Callum Lowcay8e57dd52011-01-07 19:46:59 +00001457 char ch[MAX_RESPONSE];
Kristian Høgsberg94adf6c2010-06-25 16:50:05 -04001458 int len = 0;
Kristian Høgsberg6e83d582008-12-08 00:01:36 -05001459
Kristian Høgsberg94adf6c2010-06-25 16:50:05 -04001460 switch (sym) {
1461 case XK_F11:
Kristian Høgsberg0395f302008-12-22 12:14:50 -05001462 if (!state)
1463 break;
1464 terminal->fullscreen ^= 1;
1465 window_set_fullscreen(window, terminal->fullscreen);
Kristian Høgsberg80d746f2010-06-14 23:52:50 -04001466 window_schedule_redraw(terminal->window);
Kristian Høgsberg0395f302008-12-22 12:14:50 -05001467 break;
Kristian Høgsberg94adf6c2010-06-25 16:50:05 -04001468
1469 case XK_Delete:
1470 sym = 0x04;
1471 case XK_BackSpace:
1472 case XK_Tab:
1473 case XK_Linefeed:
1474 case XK_Clear:
Kristian Høgsberg94adf6c2010-06-25 16:50:05 -04001475 case XK_Pause:
1476 case XK_Scroll_Lock:
1477 case XK_Sys_Req:
1478 case XK_Escape:
1479 ch[len++] = sym & 0x7f;
1480 break;
1481
Callum Lowcay8e57dd52011-01-07 19:46:59 +00001482 case XK_Return:
1483 if (terminal->mode & MODE_LF_NEWLINE) {
1484 ch[len++] = 0x0D;
1485 ch[len++] = 0x0A;
1486 } else {
1487 ch[len++] = 0x0D;
1488 }
1489 break;
1490
Kristian Høgsberg94adf6c2010-06-25 16:50:05 -04001491 case XK_Shift_L:
1492 case XK_Shift_R:
1493 case XK_Control_L:
1494 case XK_Control_R:
1495 case XK_Alt_L:
1496 case XK_Alt_R:
1497 break;
1498
Kristian Høgsberg6e83d582008-12-08 00:01:36 -05001499 default:
Kristian Høgsberg94adf6c2010-06-25 16:50:05 -04001500 if (modifiers & WINDOW_MODIFIER_CONTROL)
1501 sym = sym & 0x1f;
1502 else if (modifiers & WINDOW_MODIFIER_ALT)
1503 ch[len++] = 0x1b;
1504 if (sym < 256)
1505 ch[len++] = sym;
Kristian Høgsberg6e83d582008-12-08 00:01:36 -05001506 break;
1507 }
Kristian Høgsberg94adf6c2010-06-25 16:50:05 -04001508
1509 if (state && len > 0)
1510 write(terminal->master, ch, len);
Kristian Høgsberg6e83d582008-12-08 00:01:36 -05001511}
1512
Kristian Høgsberg3c248cc2009-02-22 23:01:35 -05001513static void
1514keyboard_focus_handler(struct window *window,
Kristian Høgsberg43788b12010-07-28 23:50:12 -04001515 struct input *device, void *data)
Kristian Høgsberg3c248cc2009-02-22 23:01:35 -05001516{
1517 struct terminal *terminal = data;
1518
1519 terminal->focused = (device != NULL);
Kristian Høgsberg80d746f2010-06-14 23:52:50 -04001520 window_schedule_redraw(terminal->window);
Kristian Høgsberg3c248cc2009-02-22 23:01:35 -05001521}
1522
Kristian Høgsberg0c4457f2008-12-07 19:59:11 -05001523static struct terminal *
Kristian Høgsberg43c28ee2009-01-26 23:42:46 -05001524terminal_create(struct display *display, int fullscreen)
Kristian Høgsberg0c4457f2008-12-07 19:59:11 -05001525{
1526 struct terminal *terminal;
Kristian Høgsberg09531622010-06-14 23:22:15 -04001527 cairo_surface_t *surface;
1528 cairo_t *cr;
Kristian Høgsberg0c4457f2008-12-07 19:59:11 -05001529
1530 terminal = malloc(sizeof *terminal);
1531 if (terminal == NULL)
1532 return terminal;
1533
Kristian Høgsberg269d6e32008-12-07 23:17:31 -05001534 memset(terminal, 0, sizeof *terminal);
Kristian Høgsberg0395f302008-12-22 12:14:50 -05001535 terminal->fullscreen = fullscreen;
Callum Lowcay30eeae52011-01-07 19:46:55 +00001536 terminal->color_scheme = &DEFAULT_COLORS;
1537 terminal_init(terminal);
Callum Lowcaybbeac602011-01-07 19:46:58 +00001538 terminal->margin_top = 0;
1539 terminal->margin_bottom = 10000; /* much too large, will be trimmed down
1540 * by terminal_resize */
Kristian Høgsberg43c28ee2009-01-26 23:42:46 -05001541 terminal->window = window_create(display, "Wayland Terminal",
Kristian Høgsberg82da52b2010-12-17 09:53:12 -05001542 500, 400);
Callum Lowcay15bdc5d2011-01-07 19:46:54 +00001543
1544 init_state_machine(&terminal->state_machine);
Callum Lowcay30eeae52011-01-07 19:46:55 +00001545 init_color_table(terminal);
Callum Lowcay15bdc5d2011-01-07 19:46:54 +00001546
Kristian Høgsberg0c4457f2008-12-07 19:59:11 -05001547 terminal->display = display;
Kristian Høgsberg1584c572008-12-08 12:59:37 -05001548 terminal->margin = 5;
Kristian Høgsberg44e3c5e2008-12-07 21:51:58 -05001549
Kristian Høgsberg0395f302008-12-22 12:14:50 -05001550 window_set_fullscreen(terminal->window, terminal->fullscreen);
Kristian Høgsbergc8c37342010-06-25 11:19:22 -04001551 window_set_user_data(terminal->window, terminal);
1552 window_set_redraw_handler(terminal->window, redraw_handler);
Kristian Høgsberg94448c02008-12-30 11:03:33 -05001553
Kristian Høgsbergc8c37342010-06-25 11:19:22 -04001554 window_set_key_handler(terminal->window, key_handler);
Kristian Høgsberg3c248cc2009-02-22 23:01:35 -05001555 window_set_keyboard_focus_handler(terminal->window,
Kristian Høgsbergc8c37342010-06-25 11:19:22 -04001556 keyboard_focus_handler);
Kristian Høgsberg0c4457f2008-12-07 19:59:11 -05001557
Kristian Høgsberg09531622010-06-14 23:22:15 -04001558 surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 0, 0);
1559 cr = cairo_create(surface);
Callum Lowcay30eeae52011-01-07 19:46:55 +00001560 terminal->font_bold = cairo_toy_font_face_create ("mono",
1561 CAIRO_FONT_SLANT_NORMAL,
1562 CAIRO_FONT_WEIGHT_BOLD);
1563 cairo_font_face_reference(terminal->font_bold);
1564 terminal->font_normal = cairo_toy_font_face_create ("mono",
1565 CAIRO_FONT_SLANT_NORMAL,
1566 CAIRO_FONT_WEIGHT_NORMAL);
1567 cairo_font_face_reference(terminal->font_normal);
1568 cairo_set_font_face(cr, terminal->font_normal);
Kristian Høgsberg09531622010-06-14 23:22:15 -04001569 cairo_set_font_size(cr, 14);
1570 cairo_font_extents(cr, &terminal->extents);
1571 cairo_destroy(cr);
1572 cairo_surface_destroy(surface);
1573
Callum Lowcaya0ee21c2011-01-07 19:46:56 +00001574 terminal_resize(terminal, 80, 24);
Kristian Høgsberg22106762008-12-08 13:50:07 -05001575 terminal_draw(terminal);
1576
Kristian Høgsberg0c4457f2008-12-07 19:59:11 -05001577 return terminal;
1578}
1579
Kristian Høgsberg269d6e32008-12-07 23:17:31 -05001580static gboolean
1581io_handler(GIOChannel *source,
1582 GIOCondition condition,
1583 gpointer data)
1584{
1585 struct terminal *terminal = data;
1586 gchar buffer[256];
1587 gsize bytes_read;
1588 GError *error = NULL;
1589
1590 g_io_channel_read_chars(source, buffer, sizeof buffer,
1591 &bytes_read, &error);
Kristian Høgsberg269d6e32008-12-07 23:17:31 -05001592
1593 terminal_data(terminal, buffer, bytes_read);
1594
Kristian Høgsberg269d6e32008-12-07 23:17:31 -05001595 return TRUE;
1596}
1597
1598static int
1599terminal_run(struct terminal *terminal, const char *path)
1600{
Kristian Høgsbergf0c7b202008-12-12 13:39:03 -05001601 int master;
Kristian Høgsberg269d6e32008-12-07 23:17:31 -05001602 pid_t pid;
1603
1604 pid = forkpty(&master, NULL, NULL, NULL);
1605 if (pid == 0) {
Callum Lowcay69e96582011-01-07 19:47:00 +00001606 setenv("TERM", "vt102", 1);
Kristian Høgsberg269d6e32008-12-07 23:17:31 -05001607 if (execl(path, path, NULL)) {
1608 printf("exec failed: %m\n");
1609 exit(EXIT_FAILURE);
1610 }
Kristian Høgsbergf0c7b202008-12-12 13:39:03 -05001611 } else if (pid < 0) {
1612 fprintf(stderr, "failed to fork and create pty (%m).\n");
1613 return -1;
Kristian Høgsberg269d6e32008-12-07 23:17:31 -05001614 }
1615
Kristian Høgsberg6e83d582008-12-08 00:01:36 -05001616 terminal->master = master;
Kristian Høgsberg269d6e32008-12-07 23:17:31 -05001617 terminal->channel = g_io_channel_unix_new(master);
1618 fcntl(master, F_SETFL, O_NONBLOCK);
1619 g_io_add_watch(terminal->channel, G_IO_IN,
1620 io_handler, terminal);
1621
1622 return 0;
1623}
1624
Kristian Høgsberg0395f302008-12-22 12:14:50 -05001625static const GOptionEntry option_entries[] = {
1626 { "fullscreen", 'f', 0, G_OPTION_ARG_NONE,
1627 &option_fullscreen, "Run in fullscreen mode" },
1628 { NULL }
1629};
1630
Kristian Høgsberg0c4457f2008-12-07 19:59:11 -05001631int main(int argc, char *argv[])
1632{
Kristian Høgsberg43c28ee2009-01-26 23:42:46 -05001633 struct display *d;
Kristian Høgsberg0c4457f2008-12-07 19:59:11 -05001634 struct terminal *terminal;
Kristian Høgsberg0395f302008-12-22 12:14:50 -05001635
Kristian Høgsberg7824d812010-06-08 14:59:44 -04001636 d = display_create(&argc, &argv, option_entries);
Yuval Fledele9f5e362010-11-22 21:34:19 +02001637 if (d == NULL) {
1638 fprintf(stderr, "failed to create display: %m\n");
1639 return -1;
1640 }
Kristian Høgsberg0c4457f2008-12-07 19:59:11 -05001641
Kristian Høgsberg43c28ee2009-01-26 23:42:46 -05001642 terminal = terminal_create(d, option_fullscreen);
Kristian Høgsbergf0c7b202008-12-12 13:39:03 -05001643 if (terminal_run(terminal, "/bin/bash"))
1644 exit(EXIT_FAILURE);
Kristian Høgsberg0c4457f2008-12-07 19:59:11 -05001645
Kristian Høgsberg7824d812010-06-08 14:59:44 -04001646 display_run(d);
Kristian Høgsberg0c4457f2008-12-07 19:59:11 -05001647
1648 return 0;
1649}