wlt: add --term, --login, --palette and --sb-size
[platform/upstream/kmscon.git] / src / wlt_terminal.c
1 /*
2  * wlt - Terminals
3  *
4  * Copyright (c) 2012 David Herrmann <dh.herrmann@googlemail.com>
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining
7  * a copy of this software and associated documentation files
8  * (the "Software"), to deal in the Software without restriction, including
9  * without limitation the rights to use, copy, modify, merge, publish,
10  * distribute, sublicense, and/or sell copies of the Software, and to
11  * permit persons to whom the Software is furnished to do so, subject to
12  * the following conditions:
13  *
14  * The above copyright notice and this permission notice shall be included
15  * in all copies or substantial portions of the Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
18  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
20  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
21  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
22  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
23  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24  */
25
26 /*
27  * Wayland Terminal console helpers
28  */
29
30 #include <errno.h>
31 #include <stdbool.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <wayland-client.h>
35 #include <xkbcommon/xkbcommon.h>
36 #include "conf.h"
37 #include "eloop.h"
38 #include "log.h"
39 #include "pty.h"
40 #include "shl_misc.h"
41 #include "text.h"
42 #include "tsm_unicode.h"
43 #include "tsm_screen.h"
44 #include "tsm_vte.h"
45 #include "wlt_main.h"
46 #include "wlt_terminal.h"
47 #include "wlt_toolkit.h"
48
49 #define LOG_SUBSYSTEM "wlt_terminal"
50
51 struct wlt_terminal {
52         struct ev_eloop *eloop;
53         struct wlt_window *wnd;
54         struct wlt_widget *widget;
55         struct wlt_shm_buffer buffer;
56
57         struct tsm_screen *scr;
58         struct tsm_vte *vte;
59         struct kmscon_pty *pty;
60         struct ev_fd *pty_fd;
61         bool pty_open;
62
63         struct kmscon_font *font_normal;
64         unsigned int cols;
65         unsigned int rows;
66
67         wlt_terminal_cb cb;
68         void *data;
69 };
70
71 static int draw_cell(struct tsm_screen *scr,
72                      uint32_t id, const uint32_t *ch, size_t len,
73                      unsigned int posx, unsigned int posy,
74                      const struct tsm_screen_attr *attr, void *data)
75 {
76         struct wlt_terminal *term = data;
77         const struct kmscon_glyph *glyph;
78         int ret;
79         unsigned int x, y, tmp, width, height, i, r, g, b;
80         uint8_t *dst, *src;
81         const struct uterm_video_buffer *buf;
82         unsigned int fr, fg, fb, br, bg, bb;
83         uint32_t val;
84
85         if (!len)
86                 ret = kmscon_font_render_empty(term->font_normal, &glyph);
87         else
88                 ret = kmscon_font_render(term->font_normal, id, ch, len,
89                                          &glyph);
90
91         if (ret) {
92                 ret = kmscon_font_render_inval(term->font_normal, &glyph);
93                 if (ret)
94                         return ret;
95         }
96
97         buf = &glyph->buf;
98         x = posx * term->font_normal->attr.width;
99         y = posy * term->font_normal->attr.height;
100
101         if (attr->inverse) {
102                 fr = attr->br;
103                 fg = attr->bg;
104                 fb = attr->bb;
105                 br = attr->fr;
106                 bg = attr->fg;
107                 bb = attr->fb;
108         } else {
109                 fr = attr->fr;
110                 fg = attr->fg;
111                 fb = attr->fb;
112                 br = attr->br;
113                 bg = attr->bg;
114                 bb = attr->bb;
115         }
116
117         tmp = x + buf->width;
118         if (tmp < x || x >= term->buffer.width)
119                 return 0;
120         if (tmp > term->buffer.width)
121                 width = term->buffer.width - x;
122         else
123                 width = buf->width;
124
125         tmp = y + buf->height;
126         if (tmp < y || y >= term->buffer.height)
127                 return 0;
128         if (tmp > term->buffer.height)
129                 height = term->buffer.height - y;
130         else
131                 height = buf->height;
132
133         dst = term->buffer.data;
134         dst = &dst[y * term->buffer.stride + x * 4];
135         src = buf->data;
136
137         /* Division by 256 instead of 255 increases
138          * speed by like 20% on slower machines.
139          * Downside is, full white is 254/254/254
140          * instead of 255/255/255. */
141         while (height--) {
142                 for (i = 0; i < width; ++i) {
143                         if (src[i] == 0) {
144                                 r = br;
145                                 g = bg;
146                                 b = bb;
147                         } else if (src[i] == 255) {
148                                 r = fr;
149                                 g = fg;
150                                 b = fb;
151                         } else {
152                                 r = fr * src[i] +
153                                     br * (255 - src[i]);
154                                 r /= 256;
155                                 g = fg * src[i] +
156                                     bg * (255 - src[i]);
157                                 g /= 256;
158                                 b = fb * src[i] +
159                                     bb * (255 - src[i]);
160                                 b /= 256;
161                         }
162                         val = (0xff << 24) | (r << 16) | (g << 8) | b;
163                         ((uint32_t*)dst)[i] = val;
164                 }
165                 dst += term->buffer.stride;
166                 src += buf->stride;
167         }
168
169         return 0;
170 }
171
172 static void widget_redraw(struct wlt_widget *widget, void *data)
173 {
174         struct wlt_terminal *term = data;
175
176         tsm_screen_draw(term->scr, NULL, draw_cell, NULL, term);
177 }
178
179 static void widget_resize(struct wlt_widget *widget, struct wlt_rect *alloc,
180                           void *data)
181 {
182         struct wlt_terminal *term = data;
183         int ret;
184
185         wlt_window_get_buffer(term->wnd, alloc, &term->buffer);
186
187         /* don't allow children */
188         alloc->width = 0;
189         alloc->height = 0;
190
191         term->cols = term->buffer.width / term->font_normal->attr.width;
192         if (term->cols < 1)
193                 term->cols = 1;
194         term->rows = term->buffer.height / term->font_normal->attr.height;
195         if (term->rows < 1)
196                 term->rows = 1;
197
198         ret = tsm_screen_resize(term->scr, term->cols, term->rows);
199         if (ret)
200                 log_error("cannot resize TSM screen: %d", ret);
201         kmscon_pty_resize(term->pty, term->cols, term->rows);
202 }
203
204 static void widget_prepare_resize(struct wlt_widget *widget,
205                                   unsigned int width, unsigned int height,
206                                   unsigned int *min_width,
207                                   unsigned int *min_height,
208                                   unsigned int *new_width,
209                                   unsigned int *new_height,
210                                   void *data)
211 {
212         struct wlt_terminal *term = data;
213         unsigned int w, h;
214
215         if (*new_width >= width) {
216                 *new_width += term->font_normal->attr.width;
217         } else {
218                 w = width - *new_width;
219                 w /= term->font_normal->attr.width;
220                 if (!w)
221                         w = 1;
222                 w *= term->font_normal->attr.width;
223                 *new_width += w;
224         }
225
226         if (*new_width < *min_width) {
227                 w = *min_width - *new_width;
228                 w /= term->font_normal->attr.width;
229                 w += 1;
230                 w *= term->font_normal->attr.width;
231                 *new_width += w;
232         }
233
234         if (*new_height >= height) {
235                 *new_height += term->font_normal->attr.height;
236         } else {
237                 h = height - *new_height;
238                 h /= term->font_normal->attr.height;
239                 if (!h)
240                         h = 1;
241                 h *= term->font_normal->attr.height;
242                 *new_height += h;
243         }
244
245         if (*new_height < *min_height) {
246                 h = *min_height - *new_height;
247                 h /= term->font_normal->attr.height;
248                 h += 1;
249                 h *= term->font_normal->attr.height;
250                 *new_height += h;
251         }
252 }
253
254 static void widget_key(struct wlt_widget *widget, unsigned int mask,
255                        uint32_t sym, uint32_t state, void *data)
256 {
257         struct wlt_terminal *term = data;
258         uint32_t ucs4;
259
260         if (state != WL_KEYBOARD_KEY_STATE_PRESSED)
261                 return;
262
263         ucs4 = xkb_keysym_to_utf32(sym) ? : TSM_VTE_INVALID;
264
265         if (SHL_HAS_BITS(mask, wlt_conf.grab_scroll_up->mods) &&
266             sym == wlt_conf.grab_scroll_up->keysym) {
267                 tsm_screen_sb_up(term->scr, 1);
268                 wlt_window_schedule_redraw(term->wnd);
269                 return;
270         }
271         if (SHL_HAS_BITS(mask, wlt_conf.grab_scroll_down->mods) &&
272             sym == wlt_conf.grab_scroll_down->keysym) {
273                 tsm_screen_sb_down(term->scr, 1);
274                 wlt_window_schedule_redraw(term->wnd);
275                 return;
276         }
277         if (SHL_HAS_BITS(mask, wlt_conf.grab_page_up->mods) &&
278             sym == wlt_conf.grab_page_up->keysym) {
279                 tsm_screen_sb_page_up(term->scr, 1);
280                 wlt_window_schedule_redraw(term->wnd);
281                 return;
282         }
283         if (SHL_HAS_BITS(mask, wlt_conf.grab_page_down->mods) &&
284             sym == wlt_conf.grab_page_down->keysym) {
285                 tsm_screen_sb_page_down(term->scr, 1);
286                 wlt_window_schedule_redraw(term->wnd);
287                 return;
288         }
289
290         if (tsm_vte_handle_keyboard(term->vte, sym, mask, ucs4))
291                 wlt_window_schedule_redraw(term->wnd);
292 }
293
294 static void vte_event(struct tsm_vte *vte, const char *u8, size_t len,
295                       void *data)
296 {
297         struct wlt_terminal *term = data;
298
299         kmscon_pty_write(term->pty, u8, len);
300 }
301
302 static void pty_input(struct kmscon_pty *pty, const char *u8, size_t len,
303                       void *data)
304 {
305         struct wlt_terminal *term = data;
306
307         if (!len) {
308                 term->pty_open = false;
309                 if (term->cb)
310                         term->cb(term, WLT_TERMINAL_HUP, term->data);
311         } else {
312                 tsm_vte_input(term->vte, u8, len);
313                 wlt_window_schedule_redraw(term->wnd);
314         }
315 }
316
317 static void pty_event(struct ev_fd *fd, int mask, void *data)
318 {
319         struct wlt_terminal *term = data;
320
321         kmscon_pty_dispatch(term->pty);
322 }
323
324 static void widget_destroy(struct wlt_widget *widget, void *data)
325 {
326         struct wlt_terminal *term = data;
327
328         tsm_vte_unref(term->vte);
329         tsm_screen_unref(term->scr);
330         free(term);
331 }
332
333 int wlt_terminal_new(struct wlt_terminal **out, struct wlt_window *wnd)
334 {
335         struct wlt_terminal *term;
336         int ret;
337         struct kmscon_font_attr attr = { "", 0, 20, false, false, 0, 0 };
338
339         if (!out || !wnd)
340                 return -EINVAL;
341
342         term = malloc(sizeof(*term));
343         if (!term)
344                 return -ENOMEM;
345         memset(term, 0, sizeof(*term));
346         term->wnd = wnd;
347         term->eloop = wlt_window_get_eloop(wnd);
348         term->cols = 80;
349         term->rows = 24;
350
351         attr.ppi = wlt_conf.font_ppi;
352         attr.points = wlt_conf.font_size;
353         strncpy(attr.name, wlt_conf.font_name, KMSCON_FONT_MAX_NAME - 1);
354         attr.name[KMSCON_FONT_MAX_NAME - 1] = 0;
355
356         ret = kmscon_font_find(&term->font_normal, &attr, wlt_conf.font_engine);
357         if (ret) {
358                 log_error("cannot create font");
359                 goto err_free;
360         }
361
362         ret = tsm_screen_new(&term->scr, log_llog);
363         if (ret) {
364                 log_error("cannot create tsm-screen object");
365                 goto err_font;
366         }
367         tsm_screen_set_max_sb(term->scr, wlt_conf.sb_size);
368
369         ret = tsm_vte_new(&term->vte, term->scr, vte_event, term, log_llog);
370         if (ret) {
371                 log_error("cannot create tsm-vte object");
372                 goto err_scr;
373         }
374         tsm_vte_set_palette(term->vte, wlt_conf.palette);
375
376         ret = kmscon_pty_new(&term->pty, pty_input, term);
377         if (ret) {
378                 log_error("cannot create pty object");
379                 goto err_vte;
380         }
381         kmscon_pty_set_term(term->pty, "xterm-256color");
382
383         ret = kmscon_pty_set_term(term->pty, wlt_conf.term);
384         if (ret)
385                 goto err_pty;
386
387         ret = kmscon_pty_set_argv(term->pty, wlt_conf.argv);
388         if (ret)
389                 goto err_pty;
390
391         ret = ev_eloop_new_fd(term->eloop, &term->pty_fd,
392                               kmscon_pty_get_fd(term->pty),
393                               EV_READABLE, pty_event, term);
394         if (ret)
395                 goto err_pty;
396
397         ret = wlt_window_create_widget(wnd, &term->widget, term);
398         if (ret) {
399                 log_error("cannot create terminal widget");
400                 goto err_pty_fd;
401         }
402
403         wlt_widget_set_destroy_cb(term->widget, widget_destroy);
404         wlt_widget_set_redraw_cb(term->widget, widget_redraw);
405         wlt_widget_set_resize_cb(term->widget, widget_prepare_resize,
406                                  widget_resize);
407         wlt_widget_set_keyboard_cb(term->widget, widget_key);
408         *out = term;
409         return 0;
410
411 err_pty_fd:
412         ev_eloop_rm_fd(term->pty_fd);
413 err_pty:
414         kmscon_pty_unref(term->pty);
415 err_vte:
416         tsm_vte_unref(term->vte);
417 err_scr:
418         tsm_screen_unref(term->scr);
419 err_font:
420         kmscon_font_unref(term->font_normal);
421 err_free:
422         free(term);
423         return ret;
424 }
425
426 void wlt_terminal_destroy(struct wlt_terminal *term)
427 {
428         if (!term)
429                 return;
430
431         wlt_widget_destroy(term->widget);
432 }
433
434 int wlt_terminal_open(struct wlt_terminal *term, wlt_terminal_cb cb,
435                       void *data)
436 {
437         int ret;
438
439         if (!term)
440                 return -EINVAL;
441
442         if (term->pty_open)
443                 return -EALREADY;
444
445         term->cb = cb;
446         term->data = data;
447
448         kmscon_pty_close(term->pty);
449         ret = kmscon_pty_open(term->pty, term->cols, term->rows);
450         if (ret)
451                 return ret;
452
453         term->pty_open = true;
454         return 0;
455 }