aff870a0a9dc9423980d99a152d2dcad03ba5d8b
[platform/upstream/kmscon.git] / src / console.c
1 /*
2  * kmscon - Console Management
3  * Written 2011 by David Herrmann <dh.herrmann@googlemail.com>
4  */
5
6 /*
7  * Console Management
8  * This provides the console drawing and manipulation functions. It does not
9  * provide the terminal emulation. It is just an abstraction layer to draw text
10  * to a framebuffer as used by terminals and consoles.
11  */
12
13 /*
14  * TODO: Avoid using this hack and instead retrieve GL extension
15  * pointers dynamically on initialization.
16  */
17 #define GL_GLEXT_PROTOTYPES
18
19 #include <errno.h>
20 #include <stdlib.h>
21 #include <string.h>
22
23 #include <cairo.h>
24 #include <GL/gl.h>
25 #include <GL/glext.h>
26
27 #include "console.h"
28
29 struct kmscon_cell {
30         struct kmscon_char *ch;
31 };
32
33 struct kmscon_console {
34         size_t ref;
35
36         GLuint tex;
37         uint32_t res_x;
38         uint32_t res_y;
39
40         cairo_t *cr;
41         cairo_surface_t *surf;
42         unsigned char *surf_buf;
43
44         uint32_t lines_x;
45         uint32_t lines_y;
46         struct kmscon_cell *cells;
47
48         struct kmscon_font *font;
49 };
50
51 int kmscon_console_new(struct kmscon_console **out)
52 {
53         struct kmscon_console *con;
54         int ret;
55
56         if (!out)
57                 return -EINVAL;
58
59         con = malloc(sizeof(*con));
60         if (!con)
61                 return -ENOMEM;
62
63         memset(con, 0, sizeof(*con));
64         con->ref = 1;
65
66         ret = kmscon_console_resize(con, 80, 24);
67         if (ret)
68                 goto err_free;
69
70         glGenTextures(1, &con->tex);
71
72         *out = con;
73         return 0;
74
75 err_free:
76         free(con);
77         return ret;
78 }
79
80 void kmscon_console_ref(struct kmscon_console *con)
81 {
82         if (!con)
83                 return;
84
85         ++con->ref;
86 }
87
88 static void console_free_cells(struct kmscon_console *con)
89 {
90         uint32_t i, size;
91
92         if (con->cells) {
93                 size = con->lines_x * con->lines_y;
94
95                 for (i = 0; i < size; ++i)
96                         kmscon_char_free(con->cells[i].ch);
97
98                 free(con->cells);
99         }
100 }
101
102 /*
103  * Drops one reference. If this is the last reference, the whole console is
104  * freed and the associated render-images are destroyed.
105  */
106 void kmscon_console_unref(struct kmscon_console *con)
107 {
108         if (!con || !con->ref)
109                 return;
110
111         if (--con->ref)
112                 return;
113
114         if (con->cr) {
115                 cairo_destroy(con->cr);
116                 cairo_surface_destroy(con->surf);
117                 free(con->surf_buf);
118         }
119
120         kmscon_font_unref(con->font);
121         console_free_cells(con);
122         glDeleteTextures(1, &con->tex);
123         free(con);
124 }
125
126 /*
127  * This resets the resolution used for drawing operations. It is recommended to
128  * set this to the size of your framebuffer, howevr, you can set this to
129  * anything except 0.
130  * This image-resolution is used internally to render the console fonts. The
131  * kmscon_console_map() function can map this image to any framebuffer size you
132  * want. Therefore, this screen resolution is just a performance and quality
133  * hint.
134  * This function must be called before drawing the console, though. Returns 0 on
135  * success, -EINVAL if con, x or y is 0/NULL and -ENOMEM on out-of-mem errors.
136  */
137 int kmscon_console_set_res(struct kmscon_console *con, uint32_t x, uint32_t y)
138 {
139         unsigned char *buf;
140         cairo_t *cr;
141         cairo_surface_t *surface;
142         int stride, ret;
143         cairo_format_t format = CAIRO_FORMAT_ARGB32;
144
145         if (!con || !x || !y)
146                 return -EINVAL;
147
148         stride = cairo_format_stride_for_width(format, x);
149
150         buf = malloc(stride * y);
151         if (!buf)
152                 return -ENOMEM;
153
154         surface = cairo_image_surface_create_for_data(buf, format, x, y,
155                                                                 stride);
156         if (cairo_surface_status(surface) != CAIRO_STATUS_SUCCESS) {
157                 ret = -ENOMEM;
158                 goto err_free;
159         }
160
161         cr = cairo_create(surface);
162         if (cairo_status(cr) != CAIRO_STATUS_SUCCESS) {
163                 ret = -EFAULT;
164                 goto err_cairo;
165         }
166
167         if (con->cr) {
168                 cairo_destroy(con->cr);
169                 cairo_surface_destroy(con->surf);
170                 free(con->surf_buf);
171         }
172
173         con->res_x = x;
174         con->res_y = y;
175         con->surf_buf = buf;
176         con->surf = surface;
177         con->cr = cr;
178
179         glBindTexture(GL_TEXTURE_RECTANGLE, con->tex);
180         glTexImage2D(GL_TEXTURE_RECTANGLE, 0, GL_RGBA, con->res_x, con->res_y,
181                                 0, GL_BGRA, GL_UNSIGNED_BYTE, con->surf_buf);
182         glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
183
184         return 0;
185
186 err_cairo:
187         cairo_destroy(cr);
188 err_free:
189         cairo_surface_destroy(surface);
190         free(buf);
191         return ret;
192 }
193
194 /*
195  * This redraws the console. It does not clip/copy the image onto any
196  * framebuffer. You must use kmscon_console_map() to do this.
197  * This allows to draw the console once and then map it onto multiple
198  * framebuffers so it is displayed on multiple monitors with different screen
199  * resolutions.
200  * You must have called kmscon_console_set_res() before.
201  */
202 void kmscon_console_draw(struct kmscon_console *con)
203 {
204         if (!con || !con->cr)
205                 return;
206
207         cairo_save(con->cr);
208
209         cairo_set_operator(con->cr, CAIRO_OPERATOR_OVER);
210         cairo_scale(con->cr, con->res_x, con->res_y);
211         cairo_set_source_rgba(con->cr, 0.0, 0.0, 0.0, 0.0);
212         cairo_paint(con->cr);
213
214         // TODO: draw console here
215
216         cairo_restore(con->cr);
217 }
218
219 /*
220  * This maps the console onto the current GL framebuffer. It expects the
221  * framebuffer to have 0/0 in the middle, -1/-1 in the upper left and 1/1 in the
222  * lower right (default GL settings).
223  * This does not clear the screen, nor does it paint the background. Instead the
224  * background is transparent and blended on top of the framebuffer.
225  *
226  * You must have called kmscon_console_draw() before, otherwise this will map an
227  * empty image onto the screen.
228  */
229 void kmscon_console_map(struct kmscon_console *con)
230 {
231         if (!con || !con->cr)
232                 return;
233
234         glEnable(GL_BLEND);
235         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
236         glEnable(GL_TEXTURE_RECTANGLE);
237         glBindTexture(GL_TEXTURE_RECTANGLE, con->tex);
238
239         glBegin(GL_QUADS);
240                 glColor4f(0.0f, 0.0f, 0.0f, 0.0f);
241
242                 glTexCoord2f(0.0f, 0.0f);
243                 glVertex2f(-1.0f, -1.0f);
244
245                 glTexCoord2f(con->res_x, 0.0f);
246                 glVertex2f(1.0f, -1.0f);
247
248                 glTexCoord2f(con->res_x, con->res_y);
249                 glVertex2f(1.0f, 1.0f);
250
251                 glTexCoord2f(0.0f, con->res_y);
252                 glVertex2f(-1.0f, 1.0f);
253         glEnd();
254 }
255
256 /*
257  * Resize console. x/y must not be 0.
258  * This resizes the whole console buffer and recreates all cells. It tries to
259  * preserve as many content from the previous buffer as possible.
260  */
261 int kmscon_console_resize(struct kmscon_console *con, uint32_t x, uint32_t y)
262 {
263         struct kmscon_cell *cells;
264         struct kmscon_font *font;
265         uint32_t size, i, j;
266         int ret;
267
268         size = x * y;
269         if (!con || !size || size < x || size < y)
270                 return -EINVAL;
271
272         ret = kmscon_font_new(&font);
273         if (ret)
274                 return ret;
275
276         cells = malloc(sizeof(*cells) * size);
277         if (!cells) {
278                 ret = -ENOMEM;
279                 goto err_font;
280         }
281
282         memset(cells, 0, sizeof(*cells) * size);
283
284         for (i = 0; i < size; ++i) {
285                 ret = kmscon_char_new(&cells[i].ch);
286                 if (ret) {
287                         for (j = 0; j < i; ++j)
288                                 kmscon_char_free(cells[j].ch);
289                         goto err_free;
290                 }
291         }
292
293         kmscon_font_unref(con->font);
294         con->font = font;
295
296         console_free_cells(con);
297         con->lines_x = x;
298         con->lines_y = y;
299         con->cells = cells;
300
301         return 0;
302
303 err_free:
304         free(cells);
305 err_font:
306         kmscon_font_unref(font);
307         return ret;
308 }