#include <pthread.h>
#include <stdlib.h>
#include <string.h>
-#include "font.h"
-#include "log.h"
-#include "static_misc.h"
+#include "shl_dlist.h"
+#include "shl_log.h"
+#include "shl_misc.h"
+#include "shl_register.h"
#include "text.h"
-#include "uterm.h"
+#include "uterm_video.h"
#define LOG_SUBSYSTEM "text"
-struct text_backend {
- struct kmscon_dlist list;
- const struct kmscon_text_ops *ops;
-};
+static struct shl_register text_reg = SHL_REGISTER_INIT(text_reg);
-static pthread_mutex_t text_mutex = PTHREAD_MUTEX_INITIALIZER;
-static struct kmscon_dlist text__list = KMSCON_DLIST_INIT(text__list);
-
-static void text_lock()
+static inline void kmscon_text_destroy(void *data)
{
- pthread_mutex_lock(&text_mutex);
-}
+ const struct kmscon_text_ops *ops = data;
-static void text_unlock()
-{
- pthread_mutex_unlock(&text_mutex);
+ kmscon_module_unref(ops->owner);
}
/**
*
* Returns: 0 on success, negative error code on failure
*/
+SHL_EXPORT
int kmscon_text_register(const struct kmscon_text_ops *ops)
{
- struct kmscon_dlist *iter;
- struct text_backend *be;
int ret;
- if (!ops || !ops->name)
+ if (!ops)
return -EINVAL;
log_debug("register text backend %s", ops->name);
- text_lock();
-
- kmscon_dlist_for_each(iter, &text__list) {
- be = kmscon_dlist_entry(iter, struct text_backend, list);
- if (!strcmp(be->ops->name, ops->name)) {
- log_error("registering already available backend %s",
- ops->name);
- ret = -EALREADY;
- goto out_unlock;
- }
- }
-
- be = malloc(sizeof(*be));
- if (!be) {
- log_error("cannot allocate memory for backend");
- ret = -ENOMEM;
- goto out_unlock;
+ ret = shl_register_add_cb(&text_reg, ops->name, (void*)ops,
+ kmscon_text_destroy);
+ if (ret) {
+ log_error("cannot register text backend %s: %d", ops->name,
+ ret);
+ return ret;
}
- memset(be, 0, sizeof(*be));
- be->ops = ops;
- kmscon_dlist_link(&text__list, &be->list);
-
- ret = 0;
-
-out_unlock:
- text_unlock();
- return ret;
+ kmscon_module_ref(ops->owner);
+ return 0;
}
/**
* @name: Name of backend
*
* This unregisters the text-backend that is registered with name @name. If
- * @name is not found, a warning is printed but nothing else is done.
+ * @name is not found, nothing is done.
*/
+SHL_EXPORT
void kmscon_text_unregister(const char *name)
{
- struct kmscon_dlist *iter;
- struct text_backend *be;
-
- if (!name)
- return;
-
log_debug("unregister backend %s", name);
+ shl_register_remove(&text_reg, name);
+}
- text_lock();
+static int new_text(struct kmscon_text *text, const char *backend)
+{
+ struct shl_register_record *record;
+ const char *name = backend ? backend : "<default>";
+ int ret;
- kmscon_dlist_for_each(iter, &text__list) {
- be = kmscon_dlist_entry(iter, struct text_backend, list);
- if (strcmp(name, be->ops->name))
- continue;
+ memset(text, 0, sizeof(*text));
+ text->ref = 1;
+
+ if (backend)
+ record = shl_register_find(&text_reg, backend);
+ else
+ record = shl_register_first(&text_reg);
- kmscon_dlist_unlink(&be->list);
- break;
+ if (!record) {
+ log_error("requested backend '%s' not found", name);
+ return -ENOENT;
}
- if (iter == &text__list)
- be = NULL;
+ text->record = record;
+ text->ops = record->data;
- text_unlock();
+ if (text->ops->init)
+ ret = text->ops->init(text);
+ else
+ ret = 0;
- if (!be) {
- log_error("cannot unregister backend %s: not found", name);
- } else {
- free(be);
+ if (ret) {
+ log_warning("backend %s cannot create renderer", name);
+ shl_register_record_unref(record);
+ return ret;
}
+
+ return 0;
}
/**
*
* Returns: 0 on success, error code on failure
*/
-int kmscon_text_new(struct kmscon_text **out,
- const char *backend)
+int kmscon_text_new(struct kmscon_text **out, const char *backend)
{
struct kmscon_text *text;
- struct kmscon_dlist *iter;
- struct text_backend *be, *def;
int ret;
if (!out)
return -EINVAL;
- text_lock();
-
- if (kmscon_dlist_empty(&text__list)) {
- log_error("no text backend available");
- ret = -EFAULT;
- } else {
- ret = 0;
- def = kmscon_dlist_entry(text__list.next,
- struct text_backend,
- list);
- if (!backend) {
- be = def;
- } else {
- kmscon_dlist_for_each(iter, &text__list) {
- be = kmscon_dlist_entry(iter,
- struct text_backend,
- list);
- if (!strcmp(backend, be->ops->name))
- break;
- }
- if (iter == &text__list) {
- log_warning("requested backend %s not found",
- backend);
- be = def;
- }
- }
- }
-
- if (ret)
- goto out_unlock;
-
text = malloc(sizeof(*text));
if (!text) {
log_error("cannot allocate memory for new text-renderer");
- ret = -ENOMEM;
- goto out_unlock;
+ return -ENOMEM;
}
- memset(text, 0, sizeof(*text));
- text->ref = 1;
- text->ops = be->ops;
- ret = text->ops->init(text);
+ ret = new_text(text, backend);
if (ret) {
- if (be == def) {
- log_error("default backend %s cannot create renderer",
- text->ops->name);
- goto err_free;
- }
-
- log_warning("backend %s cannot create renderer; trying default backend %s",
- be->ops->name, def->ops->name);
-
- memset(text, 0, sizeof(*text));
- text->ref = 1;
- text->ops = def->ops;
-
- ret = text->ops->init(text);
- if (ret) {
- log_error("default backend %s cannot create renderer",
- text->ops->name);
+ if (backend)
+ ret = new_text(text, NULL);
+ if (ret)
goto err_free;
- }
}
log_debug("using: be: %s", text->ops->name);
*out = text;
- ret = 0;
- goto out_unlock;
+ return 0;
err_free:
free(text);
-out_unlock:
- text_unlock();
return ret;
}
if (!text || !text->ref || --text->ref)
return;
- text_lock();
log_debug("freeing text renderer");
- text->ops->destroy(text);
- kmscon_font_unref(text->font);
- uterm_screen_unref(text->screen);
+ kmscon_text_unset(text);
+
+ if (text->ops->destroy)
+ text->ops->destroy(text);
+ shl_register_record_unref(text->record);
free(text);
- text_unlock();
}
/**
- * kmscon_text_set_font:
+ * kmscon_text_set:
* @txt: Valid text-renderer object
- * @font: Valid font object
+ * @font: font object
+ * @bold_font: bold font object or NULL
+ * @disp: display object
*
- * This makes the text-renderer @txt use the font @font. You can drop your
- * reference to @font after calling this.
+ * This makes the text-renderer @txt use the font @font and screen @screen. You
+ * can drop your reference to both after calling this.
+ * This calls kmscon_text_unset() first to remove all previous associations.
+ * None of the arguments can be NULL!
+ * If this function fails then you must assume that no font/screen will be set
+ * and the object is invalid.
+ * If @bold_font is NULL, @font is also used for bold characters. The caller
+ * must make sure that @font and @bold_font have the same metrics. The renderers
+ * will always use the metrics of @font.
+ *
+ * Returns: 0 on success, negative error code on failure.
*/
-void kmscon_text_set_font(struct kmscon_text *txt,
- struct kmscon_font *font)
+int kmscon_text_set(struct kmscon_text *txt,
+ struct kmscon_font *font,
+ struct kmscon_font *bold_font,
+ struct uterm_display *disp)
{
- if (!txt || !font)
- return;
+ int ret;
+
+ if (!txt || !font || !disp)
+ return -EINVAL;
+
+ if (!bold_font)
+ bold_font = font;
+
+ kmscon_text_unset(txt);
- kmscon_font_unref(txt->font);
txt->font = font;
+ txt->bold_font = bold_font;
+ txt->disp = disp;
+
+ if (txt->ops->set) {
+ ret = txt->ops->set(txt);
+ if (ret) {
+ txt->font = NULL;
+ txt->bold_font = NULL;
+ txt->disp = NULL;
+ return ret;
+ }
+ }
+
kmscon_font_ref(txt->font);
+ kmscon_font_ref(txt->bold_font);
+ uterm_display_ref(txt->disp);
- txt->ops->new_font(txt);
+ return 0;
}
/**
- * kmscon_text_set_bgcolor:
- * @txt: Valid text-renderer object
- * @r: red value
- * @g: green value
- * @b: blue value
+ * kmscon_text_unset():
+ * @txt: text renderer
*
- * This sets the background color to r/g/b. The background color is a solid
- * color which is used for the whole background. You should give the same as the
- * default background color of your characters as this will speed up the drawing
- * operations.
+ * This redos kmscon_text_set() by dropping the internal references to the font
+ * and screen and invalidating the object. You need to call kmscon_text_set()
+ * again to make use of this text renderer.
+ * This is automatically called when the text renderer is destroyed.
*/
-void kmscon_text_set_bgcolor(struct kmscon_text *txt,
- uint8_t r, uint8_t g, uint8_t b)
+void kmscon_text_unset(struct kmscon_text *txt)
{
- if (!txt)
- return;
-
- txt->bg_r = r;
- txt->bg_g = g;
- txt->bg_b = b;
-
- txt->ops->new_bgcolor(txt);
-}
-
-void kmscon_text_set_screen(struct kmscon_text *txt,
- struct uterm_screen *screen)
-{
- if (!txt || !screen)
+ if (!txt || !txt->disp || !txt->font)
return;
- uterm_screen_unref(txt->screen);
- txt->screen = screen;
- uterm_screen_ref(txt->screen);
+ if (txt->ops->unset)
+ txt->ops->unset(txt);
- txt->ops->new_screen(txt);
+ kmscon_font_unref(txt->font);
+ kmscon_font_unref(txt->bold_font);
+ uterm_display_unref(txt->disp);
+ txt->font = NULL;
+ txt->bold_font = NULL;
+ txt->disp = NULL;
+ txt->cols = 0;
+ txt->rows = 0;
+ txt->rendering = false;
}
+/**
+ * kmscon_text_get_cols:
+ * @txt: valid text renderer
+ *
+ * After setting the arguments with kmscon_text_set(), the renderer will compute
+ * the number of columns/rows of the console that it can display on the screen.
+ * You can retrieve these values via these functions.
+ * If kmscon_text_set() hasn't been called, this will return 0.
+ *
+ * Returns: Number of columns or 0 if @txt is invalid
+ */
unsigned int kmscon_text_get_cols(struct kmscon_text *txt)
{
if (!txt)
return txt->cols;
}
+/**
+ * kmscon_text_get_rows:
+ * @txt: valid text renderer
+ *
+ * After setting the arguments with kmscon_text_set(), the renderer will compute
+ * the number of columns/rows of the console that it can display on the screen.
+ * You can retrieve these values via these functions.
+ * If kmscon_text_set() hasn't been called, this will return 0.
+ *
+ * Returns: Number of rows or 0 if @txt is invalid
+ */
unsigned int kmscon_text_get_rows(struct kmscon_text *txt)
{
if (!txt)
return txt->rows;
}
-void kmscon_text_prepare(struct kmscon_text *txt)
+/**
+ * kmscon_text_prepare:
+ * @txt: valid text renderer
+ *
+ * This starts a rendering-round. When rendering a console via a text renderer,
+ * you have to call this first, then render all your glyphs via
+ * kmscon_text_draw() and finally use kmscon_text_render(). If you modify this
+ * renderer during rendering or if you activate different OpenGL contexts in
+ * between, you need to restart rendering by calling kmscon_text_prepare() again
+ * and redoing everything from the beginning.
+ *
+ * Returns: 0 on success, negative error code on failure.
+ */
+int kmscon_text_prepare(struct kmscon_text *txt)
{
- if (!txt || !txt->font || !txt->screen)
- return;
+ int ret = 0;
+
+ if (!txt || !txt->font || !txt->disp)
+ return -EINVAL;
txt->rendering = true;
- txt->ops->prepare(txt);
+ if (txt->ops->prepare)
+ ret = txt->ops->prepare(txt);
+ if (ret)
+ txt->rendering = false;
+
+ return ret;
}
-void kmscon_text_draw(struct kmscon_text *txt, kmscon_symbol_t ch,
- unsigned int posx, unsigned int posy,
- const struct font_char_attr *attr)
+/**
+ * kmscon_text_draw:
+ * @txt: valid text renderer
+ * @id: a unique ID that identifies @ch globally
+ * @ch: ucs4 symbol you want to draw
+ * @len: length of @ch or 0 for empty cell
+ * @width: cell-width of character
+ * @posx: X-position of the glyph
+ * @posy: Y-position of the glyph
+ * @attr: glyph attributes
+ *
+ * This draws a single glyph at the requested position. The position is a
+ * console position, not a pixel position! You must precede this call with
+ * kmscon_text_prepare(). Use this function to feed all glyphs into the
+ * rendering pipeline and finally call kmscon_text_render().
+ *
+ * Returns: 0 on success or negative error code if this glyph couldn't be drawn.
+ */
+int kmscon_text_draw(struct kmscon_text *txt,
+ uint32_t id, const uint32_t *ch, size_t len,
+ unsigned int width,
+ unsigned int posx, unsigned int posy,
+ const struct tsm_screen_attr *attr)
{
if (!txt || !txt->rendering)
- return;
+ return -EINVAL;
if (posx >= txt->cols || posy >= txt->rows || !attr)
- return;
+ return -EINVAL;
- txt->ops->draw(txt, ch, posx, posy, attr);
+ return txt->ops->draw(txt, id, ch, len, width, posx, posy, attr);
}
-void kmscon_text_render(struct kmscon_text *txt)
+/**
+ * kmscon_text_render:
+ * @txt: valid text renderer
+ *
+ * This does the final rendering round after kmscon_text_prepare() has been
+ * called and all glyphs were sent to the renderer via kmscon_text_draw().
+ *
+ * Returns: 0 on success, negative error on failure.
+ */
+int kmscon_text_render(struct kmscon_text *txt)
+{
+ int ret = 0;
+
+ if (!txt || !txt->rendering)
+ return -EINVAL;
+
+ if (txt->ops->render)
+ ret = txt->ops->render(txt);
+ txt->rendering = false;
+
+ return ret;
+}
+
+/**
+ * kmscon_text_abort:
+ * @txt: valid text renderer
+ *
+ * If you called kmscon_text_prepare() but you want to abort rendering instead
+ * of finishing it with kmscon_text_render(), you can safely call this to reset
+ * internal state. It is optional to call this or simply restart rendering.
+ * Especially if the other renderers return an error, then they probably already
+ * aborted rendering and it is not required to call this.
+ */
+void kmscon_text_abort(struct kmscon_text *txt)
{
if (!txt || !txt->rendering)
return;
- txt->ops->render(txt);
+ if (txt->ops->abort)
+ txt->ops->abort(txt);
txt->rendering = false;
}
+
+int kmscon_text_draw_cb(struct tsm_screen *con,
+ uint32_t id, const uint32_t *ch, size_t len,
+ unsigned int width,
+ unsigned int posx, unsigned int posy,
+ const struct tsm_screen_attr *attr,
+ tsm_age_t age, void *data)
+{
+ return kmscon_text_draw(data, id, ch, len, width, posx, posy, attr);
+}