uvtd: seat: implement session IDs
[platform/upstream/kmscon.git] / src / text_pixman.c
1 /*
2  * kmscon - Pixman Text Renderer Backend
3  *
4  * Copyright (c) 2012-2013 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  * Pixman based text renderer
28  */
29
30 #include <errno.h>
31 #include <pixman.h>
32 #include <stdbool.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include "shl_hashtable.h"
36 #include "shl_log.h"
37 #include "text.h"
38 #include "uterm_video.h"
39
40 #define LOG_SUBSYSTEM "text_pixman"
41
42 struct tp_glyph {
43         const struct kmscon_glyph *glyph;
44         pixman_image_t *surf;
45         uint8_t *data;
46 };
47
48 struct tp_pixman {
49         pixman_image_t *white;
50         struct shl_hashtable *glyphs;
51         struct shl_hashtable *bold_glyphs;
52
53         struct uterm_video_buffer buf[2];
54         pixman_image_t *surf[2];
55         unsigned int format[2];
56
57         bool new_stride;
58         bool use_indirect;
59         uint8_t *data[2];
60         struct uterm_video_buffer vbuf;
61
62         /* cache */
63         unsigned int cur;
64         unsigned int c_bpp;
65         uint32_t *c_data;
66         unsigned int c_stride;
67 };
68
69 static int tp_init(struct kmscon_text *txt)
70 {
71         struct tp_pixman *tp;
72
73         tp = malloc(sizeof(*tp));
74         if (!tp)
75                 return -ENOMEM;
76
77         txt->data = tp;
78         return 0;
79 }
80
81 static void tp_destroy(struct kmscon_text *txt)
82 {
83         struct tp_pixman *tp = txt->data;
84
85         free(tp);
86 }
87
88 static void free_glyph(void *data)
89 {
90         struct tp_glyph *glyph = data;
91
92         pixman_image_unref(glyph->surf);
93         free(glyph->data);
94         free(glyph);
95 }
96
97 static unsigned int format_u2p(unsigned int f)
98 {
99         switch (f) {
100         case UTERM_FORMAT_XRGB32:
101                 return PIXMAN_x8r8g8b8;
102         case UTERM_FORMAT_RGB16:
103                 return PIXMAN_r5g6b5;
104         case UTERM_FORMAT_GREY:
105                 return PIXMAN_a8;
106         default:
107                 return 0;
108         }
109 }
110
111 static int alloc_indirect(struct kmscon_text *txt,
112                           unsigned int w, unsigned int h)
113 {
114         struct tp_pixman *tp = txt->data;
115         unsigned int s, i, format;
116         int ret;
117
118         log_info("using blitting engine");
119
120         format = format_u2p(UTERM_FORMAT_XRGB32);
121         s = w * 4;
122
123         tp->data[0] = malloc(s * h);
124         tp->data[1] = malloc(s * h);
125         if (!tp->data[0] || !tp->data[1]) {
126                 log_error("cannot allocate memory for render-buffer");
127                 ret = -ENOMEM;
128                 goto err_free;
129         }
130
131         for (i = 0; i < 2; ++i) {
132                 tp->format[i] = format;
133                 tp->surf[i] = pixman_image_create_bits(format, w, h,
134                                                        (void*)tp->data[i], s);
135                 if (!tp->surf[i]) {
136                         log_error("cannot create pixman surfaces");
137                         goto err_pixman;
138                 }
139         }
140
141         tp->vbuf.width = w;
142         tp->vbuf.height = h;
143         tp->vbuf.stride = s;
144         tp->vbuf.format = UTERM_FORMAT_XRGB32;
145         tp->use_indirect = true;
146         return 0;
147
148 err_pixman:
149         if (tp->surf[1])
150                 pixman_image_unref(tp->surf[1]);
151         tp->surf[1] = NULL;
152         if (tp->surf[0])
153                 pixman_image_unref(tp->surf[0]);
154         tp->surf[0] = NULL;
155 err_free:
156         free(tp->data[1]);
157         free(tp->data[0]);
158         tp->data[1] = NULL;
159         tp->data[0] = NULL;
160         return ret;
161 }
162
163 static int tp_set(struct kmscon_text *txt)
164 {
165         struct tp_pixman *tp = txt->data;
166         int ret;
167         unsigned int w, h;
168         struct uterm_mode *m;
169         pixman_color_t white;
170
171         memset(tp, 0, sizeof(*tp));
172         m = uterm_display_get_current(txt->disp);
173         w = uterm_mode_get_width(m);
174         h = uterm_mode_get_height(m);
175
176         white.red = 0xffff;
177         white.green = 0xffff;
178         white.blue = 0xffff;
179         white.red = 0xffff;
180
181         tp->white = pixman_image_create_solid_fill(&white);
182         if (!tp->white) {
183                 log_error("cannot create pixman solid color buffer");
184                 return -ENOMEM;
185         }
186
187         ret = shl_hashtable_new(&tp->glyphs, shl_direct_hash,
188                                 shl_direct_equal, NULL,
189                                 free_glyph);
190         if (ret)
191                 goto err_white;
192
193         ret = shl_hashtable_new(&tp->bold_glyphs, shl_direct_hash,
194                                 shl_direct_equal, NULL,
195                                 free_glyph);
196         if (ret)
197                 goto err_htable;
198
199         /*
200          * TODO: It is actually faster to use a local shadow buffer and then
201          * blit all data to the framebuffer afterwards. Reads seem to be
202          * horribly slow on some mmap'ed framebuffers. However, that's not true
203          * for all so we actually don't know which to use here.
204          */
205         ret = uterm_display_get_buffers(txt->disp, tp->buf,
206                                         UTERM_FORMAT_XRGB32);
207         if (ret) {
208                 log_warning("cannot get buffers for display %p",
209                             txt->disp);
210                 ret = alloc_indirect(txt, w, h);
211                 if (ret)
212                         goto err_htable_bold;
213         } else {
214                 tp->format[0] = format_u2p(tp->buf[0].format);
215                 tp->surf[0] = pixman_image_create_bits_no_clear(tp->format[0],
216                                         tp->buf[0].width, tp->buf[0].height,
217                                         (void*)tp->buf[0].data,
218                                         tp->buf[0].stride);
219                 tp->format[1] = format_u2p(tp->buf[1].format);
220                 tp->surf[1] = pixman_image_create_bits_no_clear(tp->format[1],
221                                         tp->buf[1].width, tp->buf[1].height,
222                                         (void*)tp->buf[1].data,
223                                         tp->buf[1].stride);
224                 if (!tp->surf[0] || !tp->surf[1]) {
225                         log_error("cannot create pixman surfaces");
226                         goto err_ctx;
227                 }
228         }
229
230         txt->cols = w / txt->font->attr.width;
231         txt->rows = h / txt->font->attr.height;
232
233         return 0;
234
235 err_ctx:
236         if (tp->surf[1])
237                 pixman_image_unref(tp->surf[1]);
238         if (tp->surf[0])
239                 pixman_image_unref(tp->surf[0]);
240         free(tp->data[1]);
241         free(tp->data[0]);
242 err_htable_bold:
243         shl_hashtable_free(tp->bold_glyphs);
244 err_htable:
245         shl_hashtable_free(tp->glyphs);
246 err_white:
247         pixman_image_unref(tp->white);
248         return ret;
249 }
250
251 static void tp_unset(struct kmscon_text *txt)
252 {
253         struct tp_pixman *tp = txt->data;
254
255         pixman_image_unref(tp->surf[1]);
256         pixman_image_unref(tp->surf[0]);
257         free(tp->data[1]);
258         free(tp->data[0]);
259         shl_hashtable_free(tp->bold_glyphs);
260         shl_hashtable_free(tp->glyphs);
261         pixman_image_unref(tp->white);
262 }
263
264 static int find_glyph(struct kmscon_text *txt, struct tp_glyph **out,
265                       uint32_t id, const uint32_t *ch, size_t len, bool bold)
266 {
267         struct tp_pixman *tp = txt->data;
268         struct tp_glyph *glyph;
269         struct shl_hashtable *gtable;
270         struct kmscon_font *font;
271         const struct uterm_video_buffer *buf;
272         uint8_t *dst, *src;
273         unsigned int format, i;
274         int ret, stride;
275         bool res;
276
277         if (bold) {
278                 gtable = tp->bold_glyphs;
279                 font = txt->bold_font;
280         } else {
281                 gtable = tp->glyphs;
282                 font = txt->font;
283         }
284
285         res = shl_hashtable_find(gtable, (void**)&glyph,
286                                  (void*)(unsigned long)id);
287         if (res) {
288                 *out = glyph;
289                 return 0;
290         }
291
292         glyph = malloc(sizeof(*glyph));
293         if (!glyph)
294                 return -ENOMEM;
295         memset(glyph, 0, sizeof(*glyph));
296
297         if (!len)
298                 ret = kmscon_font_render_empty(font, &glyph->glyph);
299         else
300                 ret = kmscon_font_render(font, id, ch, len, &glyph->glyph);
301
302         if (ret) {
303                 ret = kmscon_font_render_inval(font, &glyph->glyph);
304                 if (ret)
305                         goto err_free;
306         }
307
308         buf = &glyph->glyph->buf;
309         stride = buf->stride;
310         format = format_u2p(buf->format);
311         glyph->surf = pixman_image_create_bits_no_clear(format,
312                                                         buf->width,
313                                                         buf->height,
314                                                         (void*)buf->data,
315                                                         buf->stride);
316         if (!glyph->surf) {
317                 stride = (buf->stride + 3) & ~0x3;
318                 if (!tp->new_stride) {
319                         tp->new_stride = true;
320                         log_debug("wrong stride, copy buffer (%d => %d)",
321                           buf->stride, stride);
322                 }
323
324                 glyph->data = malloc(stride * buf->height);
325                 if (!glyph->data) {
326                         log_error("cannot allocate memory for glyph storage");
327                         ret = -ENOMEM;
328                         goto err_free;
329                 }
330
331                 src = buf->data;
332                 dst = glyph->data;
333                 for (i = 0; i < buf->height; ++i) {
334                         memcpy(dst, src, buf->width);
335                         dst += stride;
336                         src += buf->stride;
337                 }
338
339                 glyph->surf = pixman_image_create_bits_no_clear(format,
340                                                                 buf->width,
341                                                                 buf->height,
342                                                                 (void*)
343                                                                 glyph->data,
344                                                                 stride);
345         }
346         if (!glyph->surf) {
347                 log_error("cannot create pixman-glyph: %d %p %d %d %d %d",
348                           ret, glyph->data ? glyph->data : buf->data, format,
349                           buf->width, buf->height, stride);
350                 ret = -EFAULT;
351                 goto err_free;
352         }
353
354         ret = shl_hashtable_insert(gtable, (void*)(long)id, glyph);
355         if (ret)
356                 goto err_pixman;
357
358         *out = glyph;
359         return 0;
360
361 err_pixman:
362         pixman_image_unref(glyph->surf);
363 err_free:
364         free(glyph);
365         return ret;
366 }
367
368 static int tp_prepare(struct kmscon_text *txt)
369 {
370         struct tp_pixman *tp = txt->data;
371         int ret;
372         pixman_image_t *img;
373
374         ret = uterm_display_use(txt->disp, NULL);
375         if (ret < 0) {
376                 log_error("cannot use display %p", txt->disp);
377                 return ret;
378         }
379
380         tp->cur = ret;
381         img = tp->surf[tp->cur];
382         tp->c_bpp = PIXMAN_FORMAT_BPP(tp->format[tp->cur]);
383         tp->c_data = pixman_image_get_data(img);
384         tp->c_stride = pixman_image_get_stride(img);
385
386         return 0;
387 }
388
389 static int tp_draw(struct kmscon_text *txt,
390                    uint32_t id, const uint32_t *ch, size_t len,
391                    unsigned int width,
392                    unsigned int posx, unsigned int posy,
393                    const struct tsm_screen_attr *attr)
394 {
395         struct tp_pixman *tp = txt->data;
396         struct tp_glyph *glyph;
397         int ret;
398         uint32_t bc;
399         pixman_color_t fc;
400         pixman_image_t *col;
401
402         if (!width)
403                 return 0;
404
405         ret = find_glyph(txt, &glyph, id, ch, len, attr->bold);
406         if (ret)
407                 return ret;
408
409         if (attr->inverse) {
410                 bc = (attr->fr << 16) | (attr->fg << 8) | (attr->fb);
411                 fc.red = attr->br << 8;
412                 fc.green = attr->bg << 8;
413                 fc.blue = attr->bb << 8;
414                 fc.alpha = 0xffff;
415         } else {
416                 bc = (attr->br << 16) | (attr->bg << 8) | (attr->bb);
417                 fc.red = attr->fr << 8;
418                 fc.green = attr->fg << 8;
419                 fc.blue = attr->fb << 8;
420                 fc.alpha = 0xffff;
421         }
422
423         /* TODO: We _really_ should fix pixman to allow something like
424          * pixman_image_set_solid_fill(img, &fc) to avoid allocating a pixman
425          * image for each glyph here.
426          * libc malloc() is pretty fast, but this still costs us a lot of
427          * rendering performance. */
428         if (!fc.red && !fc.green && !fc.blue) {
429                 col = tp->white;
430                 pixman_image_ref(col);
431         } else {
432                 col = pixman_image_create_solid_fill(&fc);
433                 if (!col) {
434                         log_error("cannot create pixman color image");
435                         return -ENOMEM;
436                 }
437         }
438
439         if (!bc) {
440                 pixman_image_composite(PIXMAN_OP_SRC,
441                                        col,
442                                        glyph->surf,
443                                        tp->surf[tp->cur],
444                                        0, 0, 0, 0,
445                                        posx * txt->font->attr.width,
446                                        posy * txt->font->attr.height,
447                                        txt->font->attr.width,
448                                        txt->font->attr.height);
449         } else {
450                 pixman_fill(tp->c_data, tp->c_stride / 4, tp->c_bpp,
451                             posx * txt->font->attr.width,
452                             posy * txt->font->attr.height,
453                             txt->font->attr.width,
454                             txt->font->attr.height,
455                             bc);
456
457                 pixman_image_composite(PIXMAN_OP_OVER,
458                                        col,
459                                        glyph->surf,
460                                        tp->surf[tp->cur],
461                                        0, 0, 0, 0,
462                                        posx * txt->font->attr.width,
463                                        posy * txt->font->attr.height,
464                                        txt->font->attr.width,
465                                        txt->font->attr.height);
466         }
467
468         pixman_image_unref(col);
469
470         return 0;
471 }
472
473 static int tp_render(struct kmscon_text *txt)
474 {
475         struct tp_pixman *tp = txt->data;
476         int ret;
477
478         if (!tp->use_indirect)
479                 return 0;
480
481         tp->vbuf.data = tp->data[tp->cur];
482         ret = uterm_display_blit(txt->disp, &tp->vbuf, 0, 0);
483         if (ret) {
484                 log_error("cannot blit back-buffer to display: %d", ret);
485                 return ret;
486         }
487
488         return 0;
489 }
490
491 struct kmscon_text_ops kmscon_text_pixman_ops = {
492         .name = "pixman",
493         .owner = NULL,
494         .init = tp_init,
495         .destroy = tp_destroy,
496         .set = tp_set,
497         .unset = tp_unset,
498         .prepare = tp_prepare,
499         .draw = tp_draw,
500         .render = tp_render,
501         .abort = NULL,
502 };