Add cells to console objects
[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
49 int kmscon_console_new(struct kmscon_console **out)
50 {
51         struct kmscon_console *con;
52
53         if (!out)
54                 return -EINVAL;
55
56         con = malloc(sizeof(*con));
57         if (!con)
58                 return -ENOMEM;
59
60         memset(con, 0, sizeof(*con));
61         con->ref = 1;
62
63         glGenTextures(1, &con->tex);
64
65         *out = con;
66         return 0;
67 }
68
69 void kmscon_console_ref(struct kmscon_console *con)
70 {
71         if (!con)
72                 return;
73
74         ++con->ref;
75 }
76
77 static void console_free_cells(struct kmscon_console *con)
78 {
79         uint32_t i, size;
80
81         if (con->cells) {
82                 size = con->lines_x * con->lines_y;
83
84                 for (i = 0; i < size; ++i)
85                         kmscon_char_free(con->cells[i].ch);
86
87                 free(con->cells);
88         }
89 }
90
91 /*
92  * Drops one reference. If this is the last reference, the whole console is
93  * freed and the associated render-images are destroyed.
94  */
95 void kmscon_console_unref(struct kmscon_console *con)
96 {
97         if (!con || !con->ref)
98                 return;
99
100         if (--con->ref)
101                 return;
102
103         if (con->cr) {
104                 cairo_destroy(con->cr);
105                 cairo_surface_destroy(con->surf);
106                 free(con->surf_buf);
107         }
108
109         console_free_cells(con);
110         glDeleteTextures(1, &con->tex);
111         free(con);
112 }
113
114 /*
115  * This resets the resolution used for drawing operations. It is recommended to
116  * set this to the size of your framebuffer, howevr, you can set this to
117  * anything except 0.
118  * This image-resolution is used internally to render the console fonts. The
119  * kmscon_console_map() function can map this image to any framebuffer size you
120  * want. Therefore, this screen resolution is just a performance and quality
121  * hint.
122  * This function must be called before drawing the console, though. Returns 0 on
123  * success, -EINVAL if con, x or y is 0/NULL and -ENOMEM on out-of-mem errors.
124  */
125 int kmscon_console_set_res(struct kmscon_console *con, uint32_t x, uint32_t y)
126 {
127         unsigned char *buf;
128         cairo_t *cr;
129         cairo_surface_t *surface;
130         int stride, ret;
131         cairo_format_t format = CAIRO_FORMAT_ARGB32;
132
133         if (!con || !x || !y)
134                 return -EINVAL;
135
136         stride = cairo_format_stride_for_width(format, x);
137
138         buf = malloc(stride * y);
139         if (!buf)
140                 return -ENOMEM;
141
142         surface = cairo_image_surface_create_for_data(buf, format, x, y,
143                                                                 stride);
144         if (cairo_surface_status(surface) != CAIRO_STATUS_SUCCESS) {
145                 ret = -ENOMEM;
146                 goto err_free;
147         }
148
149         cr = cairo_create(surface);
150         if (cairo_status(cr) != CAIRO_STATUS_SUCCESS) {
151                 ret = -EFAULT;
152                 goto err_cairo;
153         }
154
155         if (con->cr) {
156                 cairo_destroy(con->cr);
157                 cairo_surface_destroy(con->surf);
158                 free(con->surf_buf);
159         }
160
161         con->res_x = x;
162         con->res_y = y;
163         con->surf_buf = buf;
164         con->surf = surface;
165         con->cr = cr;
166
167         glBindTexture(GL_TEXTURE_RECTANGLE, con->tex);
168         glTexImage2D(GL_TEXTURE_RECTANGLE, 0, GL_RGBA, con->res_x, con->res_y,
169                                 0, GL_BGRA, GL_UNSIGNED_BYTE, con->surf_buf);
170         glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
171
172         return 0;
173
174 err_cairo:
175         cairo_destroy(cr);
176 err_free:
177         cairo_surface_destroy(surface);
178         free(buf);
179         return ret;
180 }
181
182 /*
183  * This redraws the console. It does not clip/copy the image onto any
184  * framebuffer. You must use kmscon_console_map() to do this.
185  * This allows to draw the console once and then map it onto multiple
186  * framebuffers so it is displayed on multiple monitors with different screen
187  * resolutions.
188  * You must have called kmscon_console_set_res() before.
189  */
190 void kmscon_console_draw(struct kmscon_console *con)
191 {
192         if (!con || !con->cr)
193                 return;
194
195         cairo_save(con->cr);
196
197         cairo_set_operator(con->cr, CAIRO_OPERATOR_OVER);
198         cairo_scale(con->cr, con->res_x, con->res_y);
199         cairo_set_source_rgba(con->cr, 0.0, 0.0, 0.0, 0.0);
200         cairo_paint(con->cr);
201
202         // TODO: draw console here
203
204         cairo_restore(con->cr);
205 }
206
207 /*
208  * This maps the console onto the current GL framebuffer. It expects the
209  * framebuffer to have 0/0 in the middle, -1/-1 in the upper left and 1/1 in the
210  * lower right (default GL settings).
211  * This does not clear the screen, nor does it paint the background. Instead the
212  * background is transparent and blended on top of the framebuffer.
213  *
214  * You must have called kmscon_console_draw() before, otherwise this will map an
215  * empty image onto the screen.
216  */
217 void kmscon_console_map(struct kmscon_console *con)
218 {
219         if (!con || !con->cr)
220                 return;
221
222         glEnable(GL_BLEND);
223         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
224         glEnable(GL_TEXTURE_RECTANGLE);
225         glBindTexture(GL_TEXTURE_RECTANGLE, con->tex);
226
227         glBegin(GL_QUADS);
228                 glColor4f(0.0f, 0.0f, 0.0f, 0.0f);
229
230                 glTexCoord2f(0.0f, 0.0f);
231                 glVertex2f(-1.0f, -1.0f);
232
233                 glTexCoord2f(con->res_x, 0.0f);
234                 glVertex2f(1.0f, -1.0f);
235
236                 glTexCoord2f(con->res_x, con->res_y);
237                 glVertex2f(1.0f, 1.0f);
238
239                 glTexCoord2f(0.0f, con->res_y);
240                 glVertex2f(-1.0f, 1.0f);
241         glEnd();
242 }
243
244 /*
245  * Resize console. x/y must not be 0.
246  * This resizes the whole console buffer and recreates all cells. It tries to
247  * preserve as many content from the previous buffer as possible.
248  */
249 int kmscon_console_resize(struct kmscon_console *con, uint32_t x, uint32_t y)
250 {
251         struct kmscon_cell *cells;
252         uint32_t size, i, j;
253         int ret;
254
255         size = x * y;
256         if (!con || !size || size < x || size < y)
257                 return -EINVAL;
258
259         cells = malloc(sizeof(*cells) * size);
260         if (!cells)
261                 return -ENOMEM;
262
263         memset(cells, 0, sizeof(*cells) * size);
264
265         for (i = 0; i < size; ++i) {
266                 ret = kmscon_char_new(&cells[i].ch);
267                 if (ret) {
268                         for (j = 0; j < i; ++j)
269                                 kmscon_char_free(cells[j].ch);
270                         goto err_free;
271                 }
272         }
273
274         console_free_cells(con);
275         con->lines_x = x;
276         con->lines_y = y;
277         con->cells = cells;
278
279         return 0;
280
281 err_free:
282         free(cells);
283         return ret;
284 }