font: move font handling into new subsystem
[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 <GL/gl.h>
35 #include <GL/glext.h>
36 #include <stdlib.h>
37 #include <string.h>
38
39 #include "console.h"
40 #include "eloop.h"
41 #include "font.h"
42 #include "log.h"
43 #include "terminal.h"
44 #include "unicode.h"
45 #include "vte.h"
46
47 struct term_out {
48         struct term_out *next;
49         struct kmscon_output *output;
50 };
51
52 struct kmscon_terminal {
53         unsigned long ref;
54         struct kmscon_eloop *eloop;
55
56         struct term_out *outputs;
57         unsigned int max_height;
58
59         struct kmscon_console *console;
60         struct kmscon_idle *redraw;
61         struct kmscon_vte *vte;
62 };
63
64 static void draw_all(struct kmscon_idle *idle, void *data)
65 {
66         struct kmscon_terminal *term = data;
67         struct term_out *iter;
68         struct kmscon_output *output;
69         int ret;
70
71         kmscon_eloop_rm_idle(idle);
72         kmscon_console_draw(term->console);
73
74         iter = term->outputs;
75         for (; iter; iter = iter->next) {
76                 output = iter->output;
77                 if (!kmscon_output_is_awake(output))
78                         continue;
79
80                 ret = kmscon_output_use(output);
81                 if (ret)
82                         continue;
83
84                 glClearColor(0.0, 0.0, 0.0, 1.0);
85                 glClear(GL_COLOR_BUFFER_BIT);
86
87                 kmscon_console_map(term->console);
88                 kmscon_output_swap(output);
89         }
90 }
91
92 static void schedule_redraw(struct kmscon_terminal *term)
93 {
94         int ret;
95
96         if (!term || !term->eloop)
97                 return;
98
99         ret = kmscon_eloop_add_idle(term->eloop, term->redraw, draw_all, term);
100         if (ret && ret != -EALREADY)
101                 log_warning("terminal: cannot schedule redraw\n");
102 }
103
104 static const char help_text[] =
105 "terminal subsystem - KMS based console test\n"
106 "This is some default text to test the drawing operations.\n\n";
107
108 static void print_help(struct kmscon_terminal *term)
109 {
110         unsigned int i, len;
111         kmscon_symbol_t ch;
112
113         len = sizeof(help_text) - 1;
114         for (i = 0; i < len; ++i) {
115                 ch = kmscon_symbol_make(help_text[i]);
116                 kmscon_terminal_input(term, ch);
117         }
118 }
119
120 int kmscon_terminal_new(struct kmscon_terminal **out,
121                                         struct kmscon_font_factory *ff)
122 {
123         struct kmscon_terminal *term;
124         int ret;
125
126         if (!out)
127                 return -EINVAL;
128
129         log_debug("terminal: new terminal object\n");
130
131         term = malloc(sizeof(*term));
132         if (!term)
133                 return -ENOMEM;
134
135         memset(term, 0, sizeof(*term));
136         term->ref = 1;
137
138         ret = kmscon_idle_new(&term->redraw);
139         if (ret)
140                 goto err_free;
141
142         ret = kmscon_console_new(&term->console, ff);
143         if (ret)
144                 goto err_idle;
145
146         ret = kmscon_vte_new(&term->vte);
147         if (ret)
148                 goto err_con;
149         kmscon_vte_bind(term->vte, term->console);
150         print_help(term);
151
152         *out = term;
153         return 0;
154
155 err_con:
156         kmscon_console_unref(term->console);
157 err_idle:
158         kmscon_idle_unref(term->redraw);
159 err_free:
160         free(term);
161         return ret;
162 }
163
164 void kmscon_terminal_ref(struct kmscon_terminal *term)
165 {
166         if (!term)
167                 return;
168
169         term->ref++;
170 }
171
172 void kmscon_terminal_unref(struct kmscon_terminal *term)
173 {
174         if (!term || !term->ref)
175                 return;
176
177         if (--term->ref)
178                 return;
179
180         kmscon_terminal_rm_all_outputs(term);
181         kmscon_vte_unref(term->vte);
182         kmscon_console_unref(term->console);
183         kmscon_terminal_disconnect_eloop(term);
184         free(term);
185         log_debug("terminal: destroying terminal object\n");
186 }
187
188 int kmscon_terminal_connect_eloop(struct kmscon_terminal *term,
189                                                 struct kmscon_eloop *eloop)
190 {
191         if (!term || !eloop)
192                 return -EINVAL;
193
194         if (term->eloop)
195                 return -EALREADY;
196
197         kmscon_eloop_ref(eloop);
198         term->eloop = eloop;
199
200         return 0;
201 }
202
203 void kmscon_terminal_disconnect_eloop(struct kmscon_terminal *term)
204 {
205         if (!term)
206                 return;
207
208         kmscon_eloop_unref(term->eloop);
209         term->eloop = NULL;
210 }
211
212 int kmscon_terminal_add_output(struct kmscon_terminal *term,
213                                                 struct kmscon_output *output)
214 {
215         struct term_out *out;
216         unsigned int height;
217         struct kmscon_mode *mode;
218
219         if (!term || !output)
220                 return -EINVAL;
221
222         mode = kmscon_output_get_current(output);
223         if (!mode) {
224                 log_warning("terminal: invalid output added to terminal\n");
225                 return -EINVAL;
226         }
227
228         out = malloc(sizeof(*out));
229         if (!out)
230                 return -ENOMEM;
231
232         memset(out, 0, sizeof(*out));
233         kmscon_output_ref(output);
234         out->output = output;
235         out->next = term->outputs;
236         term->outputs = out;
237
238         height = kmscon_mode_get_height(mode);
239         if (term->max_height < height) {
240                 term->max_height = height;
241                 kmscon_console_resize(term->console, 0, 0, term->max_height);
242         }
243
244         schedule_redraw(term);
245
246         return 0;
247 }
248
249 void kmscon_terminal_rm_all_outputs(struct kmscon_terminal *term)
250 {
251         struct term_out *tmp;
252
253         if (!term)
254                 return;
255
256         while (term->outputs) {
257                 tmp = term->outputs;
258                 term->outputs = tmp->next;
259                 kmscon_output_unref(tmp->output);
260                 free(tmp);
261         }
262 }
263
264 void kmscon_terminal_input(struct kmscon_terminal *term, kmscon_symbol_t ch)
265 {
266         kmscon_vte_input(term->vte, ch);
267         schedule_redraw(term);
268 }