Drop libdrm CFLAGS where no longer necessary.
[profile/ivi/wayland.git] / terminal.c
1 /*
2  * Copyright © 2008 Kristian Høgsberg
3  *
4  * Permission to use, copy, modify, distribute, and sell this software and its
5  * documentation for any purpose is hereby granted without fee, provided that
6  * the above copyright notice appear in all copies and that both that copyright
7  * notice and this permission notice appear in supporting documentation, and
8  * that the name of the copyright holders not be used in advertising or
9  * publicity pertaining to distribution of the software without specific,
10  * written prior permission.  The copyright holders make no representations
11  * about the suitability of this software for any purpose.  It is provided "as
12  * is" without express or implied warranty.
13  *
14  * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
15  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
16  * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
17  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
18  * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
19  * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
20  * OF THIS SOFTWARE.
21  */
22
23 #include <stdint.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <fcntl.h>
28 #include <unistd.h>
29 #include <math.h>
30 #include <time.h>
31 #include <pty.h>
32 #include <ctype.h>
33 #include <cairo.h>
34 #include <glib.h>
35 #include <linux/input.h>
36 #include <cairo-drm.h>
37
38 #include <GL/gl.h>
39 #include <eagle.h>
40
41 #include "wayland-client.h"
42 #include "wayland-glib.h"
43
44 #include "window.h"
45
46 static int option_fullscreen;
47 static const char gem_device[] = "/dev/dri/card0";
48 static const char socket_name[] = "\0wayland";
49
50 #define MOD_SHIFT       0x01
51 #define MOD_ALT         0x02
52 #define MOD_CTRL        0x04
53
54 struct terminal {
55         struct window *window;
56         struct wl_display *display;
57         struct wl_compositor *compositor;
58         int redraw_scheduled, redraw_pending;
59         char *data;
60         int width, height, start, row, column;
61         int fd, master;
62         cairo_surface_t *surface;
63         GIOChannel *channel;
64         uint32_t modifiers;
65         char escape[64];
66         int escape_length;
67         int state;
68         int margin;
69         int fullscreen;
70 };
71
72 static char *
73 terminal_get_row(struct terminal *terminal, int row)
74 {
75         int index;
76
77         index = (row + terminal->start) % terminal->height;
78
79         return &terminal->data[index * (terminal->width + 1)];
80 }
81
82 static void
83 terminal_resize(struct terminal *terminal, int width, int height)
84 {
85         size_t size;
86         char *data;
87         int i, l, total_rows, start;
88
89         if (terminal->width == width && terminal->height == height)
90                 return;
91
92         size = (width + 1) * height;
93         data = malloc(size);
94         memset(data, 0, size);
95         if (terminal->data) {
96                 if (width > terminal->width)
97                         l = terminal->width;
98                 else
99                         l = width;
100
101                 if (terminal->height > height) {
102                         total_rows = height;
103                         start = terminal->height - height;
104                 } else {
105                         total_rows = terminal->height;
106                         start = 0;
107                 }
108
109                 for (i = 0; i < total_rows; i++)
110                         memcpy(data + (width + 1) * i,
111                                terminal_get_row(terminal, i), l);
112
113                 free(terminal->data);
114         }
115
116         terminal->width = width;
117         terminal->height = height;
118         terminal->data = data;
119
120         if (terminal->row >= terminal->height)
121                 terminal->row = terminal->height - 1;
122         if (terminal->column >= terminal->width)
123                 terminal->column = terminal->width - 1;
124         terminal->start = 0;
125 }
126
127 static void
128 terminal_draw_contents(struct terminal *terminal)
129 {
130         struct rectangle rectangle;
131         cairo_t *cr;
132         cairo_font_extents_t extents;
133         int i, top_margin, side_margin;
134
135         window_get_child_rectangle(terminal->window, &rectangle);
136
137         terminal->surface =
138                 window_create_surface(terminal->window, &rectangle);
139         cr = cairo_create(terminal->surface);
140         cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
141         cairo_set_source_rgba(cr, 0, 0, 0, 0.9);
142         cairo_paint(cr);
143         cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
144         cairo_set_source_rgba(cr, 0, 0.7, 0, 1);
145
146         cairo_select_font_face (cr, "mono",
147                                 CAIRO_FONT_SLANT_NORMAL,
148                                 CAIRO_FONT_WEIGHT_NORMAL);
149         cairo_set_font_size(cr, 14);
150
151         cairo_font_extents(cr, &extents);
152         side_margin = (rectangle.width - terminal->width * extents.max_x_advance) / 2;
153         top_margin = (rectangle.height - terminal->height * extents.height) / 2;
154
155         for (i = 0; i < terminal->height; i++) {
156                 cairo_move_to(cr, side_margin,
157                               top_margin + extents.ascent + extents.height * i);
158                 cairo_show_text(cr, terminal_get_row(terminal, i));
159         }
160         cairo_destroy(cr);
161
162         window_copy_surface(terminal->window,
163                             &rectangle,
164                             terminal->surface);
165 }
166
167 static void
168 terminal_draw(struct terminal *terminal)
169 {
170         struct rectangle rectangle;
171         cairo_surface_t *surface;
172         cairo_font_extents_t extents;
173         cairo_t *cr;
174         int32_t width, height;
175
176         window_get_child_rectangle(terminal->window, &rectangle);
177
178         surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 0, 0);
179         cr = cairo_create(surface);
180         cairo_select_font_face (cr, "mono",
181                                 CAIRO_FONT_SLANT_NORMAL,
182                                 CAIRO_FONT_WEIGHT_NORMAL);
183         cairo_set_font_size(cr, 14);
184         cairo_font_extents(cr, &extents);
185         cairo_destroy(cr);
186         cairo_surface_destroy(surface);
187
188         width = (rectangle.width - 2 * terminal->margin) / (int32_t) extents.max_x_advance;
189         height = (rectangle.height - 2 * terminal->margin) / (int32_t) extents.height;
190         terminal_resize(terminal, width, height);
191
192         if (!terminal->fullscreen) {
193                 rectangle.width = terminal->width * extents.max_x_advance + 2 * terminal->margin;
194                 rectangle.height = terminal->height * extents.height + 2 * terminal->margin;
195                 window_set_child_size(terminal->window, &rectangle);
196         }
197
198         window_draw(terminal->window);
199         terminal_draw_contents(terminal);
200         wl_compositor_commit(terminal->compositor, 0);
201 }
202
203 static gboolean
204 idle_redraw(void *data)
205 {
206         struct terminal *terminal = data;
207
208         terminal_draw(terminal);
209
210         return FALSE;
211 }
212
213 #define STATE_NORMAL 0
214 #define STATE_ESCAPE 1
215
216 static void
217 terminal_data(struct terminal *terminal, const char *data, size_t length);
218
219 static void
220 terminal_schedule_redraw(struct terminal *terminal)
221 {
222         if (!terminal->redraw_scheduled) {
223                 g_idle_add(idle_redraw, terminal);
224                 terminal->redraw_scheduled = 1;
225         } else {
226                 terminal->redraw_pending = 1;
227         }
228 }
229
230 static void
231 terminal_data(struct terminal *terminal, const char *data, size_t length);
232
233 static void
234 handle_escape(struct terminal *terminal)
235 {
236         char *row, *p;
237         int i, count;
238         int args[10], set[10] = { 0, };
239
240         terminal->escape[terminal->escape_length++] = '\0';
241         i = 0;
242         p = &terminal->escape[2];
243         while ((isdigit(*p) || *p == ';') && i < 10) {
244                 if (*p == ';') {
245                         p++;
246                         i++;
247                 } else {
248                         args[i] = strtol(p, &p, 10);
249                         set[i] = 1;
250                 }
251         }
252         
253         switch (*p) {
254         case 'A':
255                 count = set[0] ? args[0] : 1;
256                 if (terminal->row - count >= 0)
257                         terminal->row -= count;
258                 else
259                         terminal->row = 0;
260                 break;
261         case 'B':
262                 count = set[0] ? args[0] : 1;
263                 if (terminal->row + count < terminal->height)
264                         terminal->row += count;
265                 else
266                         terminal->row = terminal->height;
267                 break;
268         case 'C':
269                 count = set[0] ? args[0] : 1;
270                 if (terminal->column + count < terminal->width)
271                         terminal->column += count;
272                 else
273                         terminal->column = terminal->width;
274                 break;
275         case 'D':
276                 count = set[0] ? args[0] : 1;
277                 if (terminal->column - count >= 0)
278                         terminal->column -= count;
279                 else
280                         terminal->column = 0;
281                 break;
282         case 'J':
283                 row = terminal_get_row(terminal, terminal->row);
284                 memset(&row[terminal->column], 0, terminal->width - terminal->column);
285                 for (i = terminal->row + 1; i < terminal->height; i++)
286                         memset(terminal_get_row(terminal, i), 0, terminal->width);
287                 break;
288         case 'G':
289                 if (set[0])
290                         terminal->column = args[0] - 1;
291                 break;
292         case 'H':
293         case 'f':
294                 terminal->row = set[0] ? args[0] - 1 : 0;
295                 terminal->column = set[1] ? args[1] - 1 : 0;
296                 break;
297         case 'K':
298                 row = terminal_get_row(terminal, terminal->row);
299                 memset(&row[terminal->column], 0, terminal->width - terminal->column);
300                 break;
301         case 'm':
302                 /* color, blink, bold etc*/
303                 break;
304         case '?':
305                 if (strcmp(p, "?25l") == 0) {
306                         /* hide cursor */
307                 } else if (strcmp(p, "?25h") == 0) {
308                         /* show cursor */
309                 }
310                 break;
311         default:
312                 terminal_data(terminal,
313                               terminal->escape + 1,
314                               terminal->escape_length - 2);
315                 break;
316         }       
317 }
318
319 static void
320 terminal_data(struct terminal *terminal, const char *data, size_t length)
321 {
322         int i;
323         char *row;
324
325         for (i = 0; i < length; i++) {
326                 row = terminal_get_row(terminal, terminal->row);
327
328                 if (terminal->state == STATE_ESCAPE) {
329                         terminal->escape[terminal->escape_length++] = data[i];
330                         if (terminal->escape_length == 2 && data[i] != '[') {
331                                 /* Bad escape sequence. */
332                                 terminal->state = STATE_NORMAL;
333                                 goto cancel_escape;
334                         }
335
336                         if (isalpha(data[i])) {
337                                 terminal->state = STATE_NORMAL;
338                                 handle_escape(terminal);
339                         } 
340                         continue;
341                 }
342
343         cancel_escape:
344                 switch (data[i]) {
345                 case '\r':
346                         terminal->column = 0;
347                         break;
348                 case '\n':
349                         terminal->column = 0;
350                         if (terminal->row + 1 < terminal->height) {
351                                 terminal->row++;
352                         } else {
353                                 terminal->start++;
354                                 if (terminal->start == terminal->height)
355                                         terminal->start = 0;
356                                 memset(terminal_get_row(terminal, terminal->row),
357                                                         0, terminal->width);
358                         }
359
360                         break;
361                 case '\t':
362                         memset(&row[terminal->column], ' ', -terminal->column & 7);
363                         terminal->column = (terminal->column + 7) & ~7;
364                         break;
365                 case '\e':
366                         terminal->state = STATE_ESCAPE;
367                         terminal->escape[0] = '\e';
368                         terminal->escape_length = 1;
369                         break;
370                 case '\b':
371                         if (terminal->column > 0)
372                                 terminal->column--;
373                         break;
374                 case '\a':
375                         /* Bell */
376                         break;
377                 default:
378                         if (terminal->column < terminal->width)
379                                 row[terminal->column++] = data[i] < 32 ? data[i] + 64 : data[i];
380                         break;
381                 }
382         }
383
384         terminal_schedule_redraw(terminal);
385 }
386
387 static void
388 resize_handler(struct window *window, void *data)
389 {
390         struct terminal *terminal = data;
391
392         terminal_schedule_redraw(terminal);
393 }
394
395 static void
396 handle_acknowledge(void *data,
397                    struct wl_compositor *compositor,
398                    uint32_t key, uint32_t frame)
399 {
400         struct terminal *terminal = data;
401
402         terminal->redraw_scheduled = 0;
403         if (key == 0)
404                 cairo_surface_destroy(terminal->surface);
405
406         if (terminal->redraw_pending) {
407                 terminal->redraw_pending = 0;
408                 terminal_schedule_redraw(terminal);
409         }
410 }
411
412 static void
413 handle_frame(void *data,
414              struct wl_compositor *compositor,
415              uint32_t frame, uint32_t timestamp)
416 {
417 }
418
419 static const struct wl_compositor_listener compositor_listener = {
420         handle_acknowledge,
421         handle_frame,
422 };
423
424 struct key {
425         int code[4];
426 } evdev_keymap[] = {
427         { { 0, 0 } },           /* 0 */
428         { { 0x1b, 0x1b } },
429         { { '1', '!' } },
430         { { '2', '@' } },
431         { { '3', '#' } },
432         { { '4', '$' } },
433         { { '5', '%' } },
434         { { '6', '^' } },
435         { { '7', '&' } },
436         { { '8', '*' } },
437         { { '9', '(' } },
438         { { '0', ')' } },
439         { { '-', '_' } },
440         { { '=', '+' } },
441         { { '\b', '\b' } },
442         { { '\t', '\t' } },
443
444         { { 'q', 'Q', 0x11 } },         /* 16 */
445         { { 'w', 'W', 0x17 } },
446         { { 'e', 'E', 0x05 } },
447         { { 'r', 'R', 0x12 } },
448         { { 't', 'T', 0x14 } },
449         { { 'y', 'Y', 0x19 } },
450         { { 'u', 'U', 0x15 } },
451         { { 'i', 'I', 0x09 } },
452         { { 'o', 'O', 0x0f } },
453         { { 'p', 'P', 0x10 } },
454         { { '[', '{', 0x1b } },
455         { { ']', '}', 0x1d } },
456         { { '\n', '\n' } },
457         { { 0, 0 } },
458         { { 'a', 'A', 0x01} },
459         { { 's', 'S', 0x13 } },
460
461         { { 'd', 'D', 0x04 } },         /* 32 */
462         { { 'f', 'F', 0x06 } },
463         { { 'g', 'G', 0x07 } },
464         { { 'h', 'H', 0x08 } },
465         { { 'j', 'J', 0x0a } },
466         { { 'k', 'K', 0x0b } },
467         { { 'l', 'L', 0x0c } },
468         { { ';', ':' } },
469         { { '\'', '"' } },
470         { { '`', '~' } },
471         { { 0, 0 } },
472         { { '\\', '|', 0x1c } },
473         { { 'z', 'Z', 0x1a } },
474         { { 'x', 'X', 0x18 } },
475         { { 'c', 'C', 0x03 } },
476         { { 'v', 'V', 0x16 } },
477
478         { { 'b', 'B', 0x02 } },         /* 48 */
479         { { 'n', 'N', 0x0e } },
480         { { 'm', 'M', 0x0d } },
481         { { ',', '<' } },
482         { { '.', '>' } },
483         { { '/', '?' } },
484         { { 0, 0 } },
485         { { '*', '*' } },
486         { { 0, 0 } },
487         { { ' ', ' ' } },
488         { { 0, 0 } }
489
490         /* 59 */
491 };
492
493 #define ARRAY_LENGTH(a) (sizeof (a) / sizeof (a)[0])
494
495 static void
496 key_handler(struct window *window, uint32_t key, uint32_t state, void *data)
497 {
498         struct terminal *terminal = data;
499         uint32_t mod = 0;
500         char c;
501
502         switch (key) {
503         case KEY_LEFTSHIFT:
504         case KEY_RIGHTSHIFT:
505                 mod = MOD_SHIFT;
506                 break;
507         case KEY_LEFTCTRL:
508         case KEY_RIGHTCTRL:
509                 mod = MOD_CTRL;
510                 break;
511         case KEY_LEFTALT:
512         case KEY_RIGHTALT:
513                 mod = MOD_ALT;
514                 break;
515         case KEY_F11:
516                 if (!state)
517                         break;
518                 terminal->fullscreen ^= 1;
519                 window_set_fullscreen(window, terminal->fullscreen);
520                 terminal_schedule_redraw(terminal);
521                 break;
522         default:
523                 if (key < ARRAY_LENGTH(evdev_keymap)) {
524                         if (terminal->modifiers & MOD_CTRL)
525                                 c = evdev_keymap[key].code[2];
526                         else if (terminal->modifiers & MOD_SHIFT)
527                                 c = evdev_keymap[key].code[1];
528                         else
529                                 c = evdev_keymap[key].code[0];
530                         if (state && c)
531                                 write(terminal->master, &c, 1);
532                 }
533                 break;
534         }
535
536         if (state)
537                 terminal->modifiers |= mod;
538         else
539                 terminal->modifiers &= ~mod;
540 }
541
542 static struct terminal *
543 terminal_create(struct wl_display *display, int fd, int fullscreen)
544 {
545         struct terminal *terminal;
546
547         terminal = malloc(sizeof *terminal);
548         if (terminal == NULL)
549                 return terminal;
550
551         memset(terminal, 0, sizeof *terminal);
552         terminal->fullscreen = fullscreen;
553         terminal->window = window_create(display, fd, "Wayland Terminal",
554                                          500, 100, 500, 400);
555         terminal->display = display;
556         terminal->redraw_scheduled = 1;
557         terminal->margin = 5;
558
559         terminal->compositor = wl_display_get_compositor(display);
560         window_set_fullscreen(terminal->window, terminal->fullscreen);
561         window_set_resize_handler(terminal->window, resize_handler, terminal);
562
563         wl_compositor_add_listener(terminal->compositor,
564                                    &compositor_listener, terminal);
565
566         window_set_key_handler(terminal->window, key_handler, terminal);
567
568         terminal_draw(terminal);
569
570         return terminal;
571 }
572
573 static gboolean
574 io_handler(GIOChannel   *source,
575            GIOCondition  condition,
576            gpointer      data)
577 {
578         struct terminal *terminal = data;
579         gchar buffer[256];
580         gsize bytes_read;
581         GError *error = NULL;
582
583         g_io_channel_read_chars(source, buffer, sizeof buffer,
584                                 &bytes_read, &error);
585
586         terminal_data(terminal, buffer, bytes_read);
587
588         return TRUE;
589 }
590
591 static int
592 terminal_run(struct terminal *terminal, const char *path)
593 {
594         int master;
595         pid_t pid;
596
597         pid = forkpty(&master, NULL, NULL, NULL);
598         if (pid == 0) {
599                 close(master);
600                 setenv("TERM", "vt100", 1);
601                 if (execl(path, path, NULL)) {
602                         printf("exec failed: %m\n");
603                         exit(EXIT_FAILURE);
604                 }
605         } else if (pid < 0) {
606                 fprintf(stderr, "failed to fork and create pty (%m).\n");
607                 return -1;
608         }
609
610         terminal->master = master;
611         terminal->channel = g_io_channel_unix_new(master);
612         fcntl(master, F_SETFL, O_NONBLOCK);
613         g_io_add_watch(terminal->channel, G_IO_IN,
614                        io_handler, terminal);
615
616         return 0;
617 }
618
619 static const GOptionEntry option_entries[] = {
620         { "fullscreen", 'f', 0, G_OPTION_ARG_NONE,
621           &option_fullscreen, "Run in fullscreen mode" },
622         { NULL }
623 };
624
625 int main(int argc, char *argv[])
626 {
627         struct wl_display *display;
628         int fd;
629         GMainLoop *loop;
630         GSource *source;
631         struct terminal *terminal;
632         GOptionContext *context;
633         GError *error;
634
635         context = g_option_context_new(NULL);
636         g_option_context_add_main_entries(context, option_entries, "Wayland Terminal");
637         if (!g_option_context_parse(context, &argc, &argv, &error)) {
638                 fprintf(stderr, "option parsing failed: %s\n", error->message);
639                 exit(EXIT_FAILURE);
640         }
641
642         fd = open(gem_device, O_RDWR);
643         if (fd < 0) {
644                 fprintf(stderr, "drm open failed: %m\n");
645                 return -1;
646         }
647
648         display = wl_display_create(socket_name, sizeof socket_name);
649         if (display == NULL) {
650                 fprintf(stderr, "failed to create display: %m\n");
651                 return -1;
652         }
653
654         loop = g_main_loop_new(NULL, FALSE);
655         source = wl_glib_source_new(display);
656         g_source_attach(source, NULL);
657
658         terminal = terminal_create(display, fd, option_fullscreen);
659         if (terminal_run(terminal, "/bin/bash"))
660                 exit(EXIT_FAILURE);
661
662         g_main_loop_run(loop);
663
664         return 0;
665 }