b2db07c92401b82f0f1f061515256dc110e05cf2
[platform/upstream/kmscon.git] / src / text.c
1 /*
2  * kmscon - Text Renderer
3  *
4  * Copyright (c) 2012 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  * SECTION:text
28  * @short_description: Text Renderer
29  * @include: text.h
30  *
31  * TODO
32  */
33
34 #include <errno.h>
35 #include <pthread.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include "log.h"
39 #include "static_misc.h"
40 #include "text.h"
41 #include "uterm.h"
42
43 #define LOG_SUBSYSTEM "text"
44
45 struct text_backend {
46         struct kmscon_dlist list;
47         const struct kmscon_text_ops *ops;
48 };
49
50 static pthread_mutex_t text_mutex = PTHREAD_MUTEX_INITIALIZER;
51 static struct kmscon_dlist text__list = KMSCON_DLIST_INIT(text__list);
52
53 static void text_lock()
54 {
55         pthread_mutex_lock(&text_mutex);
56 }
57
58 static void text_unlock()
59 {
60         pthread_mutex_unlock(&text_mutex);
61 }
62
63 /**
64  * kmscon_text_register:
65  * @ops: Text operations and name for new backend
66  *
67  * This register a new text backend with operations set to @ops. The name
68  * @ops->name must be valid.
69  *
70  * The first font that is registered automatically becomes the default and
71  * fallback. So make sure you register a safe fallback as first backend.
72  * If this is unregistered, the next in the list becomes the default
73  * and fallback.
74  *
75  * Returns: 0 on success, negative error code on failure
76  */
77 int kmscon_text_register(const struct kmscon_text_ops *ops)
78 {
79         struct kmscon_dlist *iter;
80         struct text_backend *be;
81         int ret;
82
83         if (!ops || !ops->name)
84                 return -EINVAL;
85
86         log_debug("register text backend %s", ops->name);
87
88         text_lock();
89
90         kmscon_dlist_for_each(iter, &text__list) {
91                 be = kmscon_dlist_entry(iter, struct text_backend, list);
92                 if (!strcmp(be->ops->name, ops->name)) {
93                         log_error("registering already available backend %s",
94                                   ops->name);
95                         ret = -EALREADY;
96                         goto out_unlock;
97                 }
98         }
99
100         be = malloc(sizeof(*be));
101         if (!be) {
102                 log_error("cannot allocate memory for backend");
103                 ret = -ENOMEM;
104                 goto out_unlock;
105         }
106
107         memset(be, 0, sizeof(*be));
108         be->ops = ops;
109         kmscon_dlist_link(&text__list, &be->list);
110
111         ret = 0;
112
113 out_unlock:
114         text_unlock();
115         return ret;
116 }
117
118 /**
119  * kmscon_text_unregister:
120  * @name: Name of backend
121  *
122  * This unregisters the text-backend that is registered with name @name. If
123  * @name is not found, a warning is printed but nothing else is done.
124  */
125 void kmscon_text_unregister(const char *name)
126 {
127         struct kmscon_dlist *iter;
128         struct text_backend *be;
129
130         if (!name)
131                 return;
132
133         log_debug("unregister backend %s", name);
134
135         text_lock();
136
137         kmscon_dlist_for_each(iter, &text__list) {
138                 be = kmscon_dlist_entry(iter, struct text_backend, list);
139                 if (strcmp(name, be->ops->name))
140                         continue;
141
142                 kmscon_dlist_unlink(&be->list);
143                 break;
144         }
145
146         if (iter == &text__list)
147                 be = NULL;
148
149         text_unlock();
150
151         if (!be) {
152                 log_error("cannot unregister backend %s: not found", name);
153         } else {
154                 free(be);
155         }
156 }
157
158 /**
159  * kmscon_text_new:
160  * @out: A pointer to the new text-renderer is stored here
161  * @backend: Backend to use or NULL for default backend
162  *
163  * Returns: 0 on success, error code on failure
164  */
165 int kmscon_text_new(struct kmscon_text **out,
166                     const char *backend)
167 {
168         struct kmscon_text *text;
169         struct kmscon_dlist *iter;
170         struct text_backend *be, *def;
171         int ret;
172
173         if (!out)
174                 return -EINVAL;
175
176         text_lock();
177
178         if (kmscon_dlist_empty(&text__list)) {
179                 log_error("no text backend available");
180                 ret = -EFAULT;
181         } else {
182                 ret = 0;
183                 def = kmscon_dlist_entry(text__list.prev,
184                                          struct text_backend,
185                                          list);
186                 if (!backend) {
187                         be = def;
188                 } else {
189                         kmscon_dlist_for_each(iter, &text__list) {
190                                 be = kmscon_dlist_entry(iter,
191                                                         struct text_backend,
192                                                         list);
193                                 if (!strcmp(backend, be->ops->name))
194                                         break;
195                         }
196                         if (iter == &text__list) {
197                                 log_warning("requested backend %s not found",
198                                             backend);
199                                 be = def;
200                         }
201                 }
202         }
203
204         if (ret)
205                 goto out_unlock;
206
207         text = malloc(sizeof(*text));
208         if (!text) {
209                 log_error("cannot allocate memory for new text-renderer");
210                 ret = -ENOMEM;
211                 goto out_unlock;
212         }
213         memset(text, 0, sizeof(*text));
214         text->ref = 1;
215         text->ops = be->ops;
216
217         if (text->ops->init)
218                 ret = text->ops->init(text);
219         else
220                 ret = 0;
221
222         if (ret) {
223                 if (be == def) {
224                         log_error("default backend %s cannot create renderer",
225                                   text->ops->name);
226                         goto err_free;
227                 }
228
229                 log_warning("backend %s cannot create renderer; trying default backend %s",
230                             be->ops->name, def->ops->name);
231
232                 memset(text, 0, sizeof(*text));
233                 text->ref = 1;
234                 text->ops = def->ops;
235
236                 ret = text->ops->init(text);
237                 if (ret) {
238                         log_error("default backend %s cannot create renderer",
239                                   text->ops->name);
240                         goto err_free;
241                 }
242         }
243
244         log_debug("using: be: %s", text->ops->name);
245         *out = text;
246         ret = 0;
247         goto out_unlock;
248
249 err_free:
250         free(text);
251 out_unlock:
252         text_unlock();
253         return ret;
254 }
255
256 /**
257  * kmscon_text_ref:
258  * @text: Valid text-renderer object
259  *
260  * This increases the reference count of @text by one.
261  */
262 void kmscon_text_ref(struct kmscon_text *text)
263 {
264         if (!text || !text->ref)
265                 return;
266
267         ++text->ref;
268 }
269
270 /**
271  * kmscon_text_unref:
272  * @text: Valid text-renderer object
273  *
274  * This decreases the reference count of @text by one. If it drops to zero, the
275  * object is freed.
276  */
277 void kmscon_text_unref(struct kmscon_text *text)
278 {
279         if (!text || !text->ref || --text->ref)
280                 return;
281
282         log_debug("freeing text renderer");
283         kmscon_text_unset(text);
284
285         text_lock();
286
287         if (text->ops->destroy)
288                 text->ops->destroy(text);
289         free(text);
290
291         text_unlock();
292 }
293
294 /**
295  * kmscon_text_set:
296  * @txt: Valid text-renderer object
297  * @font: font object
298  * @screen: screen object
299  *
300  * This makes the text-renderer @txt use the font @font and screen @screen. You
301  * can drop your reference to both after calling this.
302  * This calls kmscon_text_unset() first to remove all previous associations.
303  * None of the arguments can be NULL!
304  * If this function fails then you must assume that no font/screen will be set
305  * and the object is invalid.
306  *
307  * Returns: 0 on success, negative error code on failure.
308  */
309 int kmscon_text_set(struct kmscon_text *txt,
310                     struct kmscon_font *font,
311                     struct uterm_screen *screen)
312 {
313         int ret;
314
315         if (!txt || !font || !screen)
316                 return -EINVAL;
317
318         kmscon_text_unset(txt);
319
320         txt->font = font;
321         txt->screen = screen;
322
323         if (txt->ops->set) {
324                 ret = txt->ops->set(txt);
325                 if (ret) {
326                         txt->font = NULL;
327                         txt->screen = NULL;
328                         return ret;
329                 }
330         }
331
332         kmscon_font_ref(txt->font);
333         uterm_screen_ref(txt->screen);
334
335         return 0;
336 }
337
338 /**
339  * kmscon_text_unset():
340  * @txt: text renderer
341  *
342  * This redos kmscon_text_set() by dropping the internal references to the font
343  * and screen and invalidating the object. You need to call kmscon_text_set()
344  * again to make use of this text renderer.
345  * This is automatically called when the text renderer is destroyed.
346  */
347 void kmscon_text_unset(struct kmscon_text *txt)
348 {
349         if (!txt || !txt->screen || !txt->font)
350                 return;
351
352         if (txt->ops->unset)
353                 txt->ops->unset(txt);
354
355         kmscon_font_unref(txt->font);
356         uterm_screen_unref(txt->screen);
357         txt->font = NULL;
358         txt->screen = NULL;
359         txt->cols = 0;
360         txt->rows = 0;
361         txt->rendering = false;
362 }
363
364 /**
365  * kmscon_text_get_cols:
366  * @txt: valid text renderer
367  *
368  * After setting the arguments with kmscon_text_set(), the renderer will compute
369  * the number of columns/rows of the console that it can display on the screen.
370  * You can retrieve these values via these functions.
371  * If kmscon_text_set() hasn't been called, this will return 0.
372  *
373  * Returns: Number of columns or 0 if @txt is invalid
374  */
375 unsigned int kmscon_text_get_cols(struct kmscon_text *txt)
376 {
377         if (!txt)
378                 return 0;
379
380         return txt->cols;
381 }
382
383 /**
384  * kmscon_text_get_rows:
385  * @txt: valid text renderer
386  *
387  * After setting the arguments with kmscon_text_set(), the renderer will compute
388  * the number of columns/rows of the console that it can display on the screen.
389  * You can retrieve these values via these functions.
390  * If kmscon_text_set() hasn't been called, this will return 0.
391  *
392  * Returns: Number of rows or 0 if @txt is invalid
393  */
394 unsigned int kmscon_text_get_rows(struct kmscon_text *txt)
395 {
396         if (!txt)
397                 return 0;
398
399         return txt->rows;
400 }
401
402 /**
403  * kmscon_text_prepare:
404  * @txt: valid text renderer
405  *
406  * This starts a rendering-round. When rendering a console via a text renderer,
407  * you have to call this first, then render all your glyphs via
408  * kmscon_text_draw() and finally use kmscon_text_render(). If you modify this
409  * renderer during rendering or if you activate different OpenGL contexts in
410  * between, you need to restart rendering by calling kmscon_text_prepare() again
411  * and redoing everything from the beginning.
412  *
413  * Returns: 0 on success, negative error code on failure.
414  */
415 int kmscon_text_prepare(struct kmscon_text *txt)
416 {
417         int ret = 0;
418
419         if (!txt || !txt->font || !txt->screen)
420                 return -EINVAL;
421
422         txt->rendering = true;
423         if (txt->ops->prepare)
424                 ret = txt->ops->prepare(txt);
425         if (ret)
426                 txt->rendering = false;
427
428         return ret;
429 }
430
431 /**
432  * kmscon_text_draw:
433  * @txt: valid text renderer
434  * @ch: symbol you want to draw
435  * @posx: X-position of the glyph
436  * @posy: Y-position of the glyph
437  * @attr: glyph attributes
438  *
439  * This draws a single glyph at the requested position. The position is a
440  * console position, not a pixel position! You must precede this call with
441  * kmscon_text_prepare(). Use this function to feed all glyphs into the
442  * rendering pipeline and finally call kmscon_text_render().
443  *
444  * Returns: 0 on success or negative error code if this glyph couldn't be drawn.
445  */
446 int kmscon_text_draw(struct kmscon_text *txt, kmscon_symbol_t ch,
447                       unsigned int posx, unsigned int posy,
448                       const struct font_char_attr *attr)
449 {
450         if (!txt || !txt->rendering)
451                 return -EINVAL;
452         if (posx >= txt->cols || posy >= txt->rows || !attr)
453                 return -EINVAL;
454
455         return txt->ops->draw(txt, ch, posx, posy, attr);
456 }
457
458 /**
459  * kmscon_text_render:
460  * @txt: valid text renderer
461  *
462  * This does the final rendering round after kmscon_text_prepare() has been
463  * called and all glyphs were sent to the renderer via kmscon_text_draw().
464  *
465  * Returns: 0 on success, negative error on failure.
466  */
467 int kmscon_text_render(struct kmscon_text *txt)
468 {
469         int ret = 0;
470
471         if (!txt || !txt->rendering)
472                 return -EINVAL;
473
474         if (txt->ops->render)
475                 ret = txt->ops->render(txt);
476         txt->rendering = false;
477
478         return ret;
479 }
480
481 /**
482  * kmscon_text_abort:
483  * @txt: valid text renderer
484  *
485  * If you called kmscon_text_prepare() but you want to abort rendering instead
486  * of finishing it with kmscon_text_render(), you can safely call this to reset
487  * internal state. It is optional to call this or simply restart rendering.
488  * Especially if the other renderers return an error, then they probably already
489  * aborted rendering and it is not required to call this.
490  */
491 void kmscon_text_abort(struct kmscon_text *txt)
492 {
493         if (!txt || !txt->rendering)
494                 return;
495
496         if (txt->ops->abort)
497                 txt->ops->abort(txt);
498         txt->rendering = false;
499 }