eloop: move prefix to "ev_" instead of "kmscon_"
[platform/upstream/kmscon.git] / src / terminal.c
1 /*
2  * kmscon - Terminal
3  *
4  * Copyright (c) 2011 David Herrmann <dh.herrmann@googlemail.com>
5  * Copyright (c) 2011 University of Tuebingen
6  *
7  * Permission is hereby granted, free of charge, to any person obtaining
8  * a copy of this software and associated documentation files
9  * (the "Software"), to deal in the Software without restriction, including
10  * without limitation the rights to use, copy, modify, merge, publish,
11  * distribute, sublicense, and/or sell copies of the Software, and to
12  * permit persons to whom the Software is furnished to do so, subject to
13  * the following conditions:
14  *
15  * The above copyright notice and this permission notice shall be included
16  * in all copies or substantial portions of the Software.
17  *
18  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
19  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
21  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
22  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
23  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
24  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25  */
26
27 /*
28  * Terminal
29  * A terminal gets assigned an input stream and several output objects and then
30  * runs a fully functional terminal emulation on it.
31  */
32
33 #include <errno.h>
34 #include <stdlib.h>
35 #include <string.h>
36
37 #include "console.h"
38 #include "eloop.h"
39 #include "font.h"
40 #include "input.h"
41 #include "log.h"
42 #include "output.h"
43 #include "pty.h"
44 #include "terminal.h"
45 #include "unicode.h"
46 #include "vte.h"
47
48 struct term_out {
49         struct term_out *next;
50         struct kmscon_output *output;
51 };
52
53 struct kmscon_terminal {
54         unsigned long ref;
55         struct ev_eloop *eloop;
56         struct kmscon_compositor *comp;
57
58         struct term_out *outputs;
59         unsigned int max_height;
60
61         struct kmscon_console *console;
62         struct ev_idle *redraw;
63         struct kmscon_vte *vte;
64         struct kmscon_pty *pty;
65
66         kmscon_terminal_closed_cb closed_cb;
67         void *closed_data;
68 };
69
70 static void draw_all(struct ev_idle *idle, void *data)
71 {
72         struct kmscon_terminal *term = data;
73         struct term_out *iter;
74         struct kmscon_output *output;
75         struct kmscon_context *ctx;
76         int ret;
77
78         ctx = kmscon_compositor_get_context(term->comp);
79         ev_eloop_rm_idle(idle);
80
81         iter = term->outputs;
82         for (; iter; iter = iter->next) {
83                 output = iter->output;
84                 if (!kmscon_output_is_awake(output))
85                         continue;
86
87                 ret = kmscon_output_use(output);
88                 if (ret)
89                         continue;
90
91                 kmscon_context_clear(ctx);
92                 kmscon_console_map(term->console);
93                 kmscon_output_swap(output);
94         }
95 }
96
97 static void schedule_redraw(struct kmscon_terminal *term)
98 {
99         int ret;
100
101         if (!term || !term->eloop)
102                 return;
103
104         ret = ev_eloop_add_idle(term->eloop, term->redraw, draw_all, term);
105         if (ret && ret != -EALREADY)
106                 log_warn("terminal: cannot schedule redraw\n");
107 }
108
109 static void pty_input(struct kmscon_pty *pty, const char *u8, size_t len,
110                                                                 void *data)
111 {
112         struct kmscon_terminal *term = data;
113
114         if (!len) {
115                 if (term->closed_cb)
116                         term->closed_cb(term, term->closed_data);
117         } else {
118                 kmscon_vte_input(term->vte, u8, len);
119                 schedule_redraw(term);
120         }
121 }
122
123 int kmscon_terminal_new(struct kmscon_terminal **out,
124                 struct ev_eloop *loop, struct kmscon_font_factory *ff,
125                 struct kmscon_compositor *comp, struct kmscon_symbol_table *st)
126 {
127         struct kmscon_terminal *term;
128         int ret;
129
130         if (!out)
131                 return -EINVAL;
132
133         log_debug("terminal: new terminal object\n");
134
135         term = malloc(sizeof(*term));
136         if (!term)
137                 return -ENOMEM;
138
139         memset(term, 0, sizeof(*term));
140         term->ref = 1;
141         term->eloop = loop;
142         term->comp = comp;
143
144         ret = ev_idle_new(&term->redraw);
145         if (ret)
146                 goto err_free;
147
148         ret = kmscon_console_new(&term->console, ff, comp);
149         if (ret)
150                 goto err_idle;
151
152         ret = kmscon_vte_new(&term->vte, st);
153         if (ret)
154                 goto err_con;
155         kmscon_vte_bind(term->vte, term->console);
156
157         ret = kmscon_pty_new(&term->pty, term->eloop, pty_input, term);
158         if (ret)
159                 goto err_vte;
160
161         ev_eloop_ref(term->eloop);
162         kmscon_compositor_ref(term->comp);
163         *out = term;
164
165         return 0;
166
167 err_vte:
168         kmscon_vte_unref(term->vte);
169 err_con:
170         kmscon_console_unref(term->console);
171 err_idle:
172         ev_idle_unref(term->redraw);
173 err_free:
174         free(term);
175         return ret;
176 }
177
178 void kmscon_terminal_ref(struct kmscon_terminal *term)
179 {
180         if (!term)
181                 return;
182
183         term->ref++;
184 }
185
186 void kmscon_terminal_unref(struct kmscon_terminal *term)
187 {
188         if (!term || !term->ref)
189                 return;
190
191         if (--term->ref)
192                 return;
193
194         kmscon_terminal_close(term);
195         kmscon_terminal_rm_all_outputs(term);
196         kmscon_pty_unref(term->pty);
197         kmscon_vte_unref(term->vte);
198         kmscon_console_unref(term->console);
199         ev_idle_unref(term->redraw);
200         kmscon_compositor_unref(term->comp);
201         ev_eloop_unref(term->eloop);
202         free(term);
203         log_debug("terminal: destroying terminal object\n");
204 }
205
206 int kmscon_terminal_open(struct kmscon_terminal *term,
207                         kmscon_terminal_closed_cb closed_cb, void *data)
208 {
209         int ret;
210         unsigned short width, height;
211
212         if (!term)
213                 return -EINVAL;
214
215         width = kmscon_console_get_width(term->console);
216         height = kmscon_console_get_height(term->console);
217         ret = kmscon_pty_open(term->pty, width, height);
218         if (ret)
219                 return ret;
220
221         term->closed_cb = closed_cb;
222         term->closed_data = data;
223         return 0;
224 }
225
226 void kmscon_terminal_close(struct kmscon_terminal *term)
227 {
228         if (!term)
229                 return;
230
231         kmscon_pty_close(term->pty);
232         term->closed_data = NULL;
233         term->closed_cb = NULL;
234 }
235
236 int kmscon_terminal_add_output(struct kmscon_terminal *term,
237                                                 struct kmscon_output *output)
238 {
239         struct term_out *out;
240         unsigned int height;
241         struct kmscon_mode *mode;
242
243         if (!term || !output)
244                 return -EINVAL;
245
246         mode = kmscon_output_get_current(output);
247         if (!mode) {
248                 log_warn("terminal: invalid output added to terminal\n");
249                 return -EINVAL;
250         }
251
252         out = malloc(sizeof(*out));
253         if (!out)
254                 return -ENOMEM;
255
256         memset(out, 0, sizeof(*out));
257         kmscon_output_ref(output);
258         out->output = output;
259         out->next = term->outputs;
260         term->outputs = out;
261
262         height = kmscon_mode_get_height(mode);
263         if (term->max_height < height) {
264                 term->max_height = height;
265                 kmscon_console_resize(term->console, 0, 0, term->max_height);
266         }
267
268         schedule_redraw(term);
269
270         return 0;
271 }
272
273 void kmscon_terminal_rm_all_outputs(struct kmscon_terminal *term)
274 {
275         struct term_out *tmp;
276
277         if (!term)
278                 return;
279
280         while (term->outputs) {
281                 tmp = term->outputs;
282                 term->outputs = tmp->next;
283                 kmscon_output_unref(tmp->output);
284                 free(tmp);
285         }
286 }
287
288 int kmscon_terminal_input(struct kmscon_terminal *term,
289                                         const struct kmscon_input_event *ev)
290 {
291         int ret;
292         const char *u8;
293         size_t len;
294
295         if (!term || !ev)
296                 return -EINVAL;
297
298         ret = kmscon_vte_handle_keyboard(term->vte, ev, &u8, &len);
299         switch (ret) {
300                 case KMSCON_VTE_SEND:
301                         ret = kmscon_pty_write(term->pty, u8, len);
302                         if (ret)
303                                 return ret;
304                         break;
305                 case KMSCON_VTE_DROP:
306                 default:
307                         break;
308         }
309
310         return 0;
311 }