From 272d086326b3a9735e283cc2c82731f71ff47abf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mirco=20M=C3=BCller?= Date: Thu, 16 Oct 2025 23:12:36 +0200 Subject: [PATCH 1/9] Add core rotation support Add rotation support in the core of kmscon. You can specify rotation by command line, or in kmscon.conf It also adds two keyboard shortcut, LOGO+"+" and LOGO+"-" to rotate the current output Co-authored-by: Jocelyn Falempe Signed-off-by: Jocelyn Falempe --- COPYING | 2 + src/kmscon_conf.c | 14 ++++++ src/kmscon_conf.h | 6 +++ src/kmscon_terminal.c | 105 ++++++++++++++++++++++++++++++++++++------ src/text.c | 75 ++++++++++++++++++++++++++++-- src/text.h | 14 +++++- 6 files changed, 196 insertions(+), 20 deletions(-) diff --git a/COPYING b/COPYING index 3ad47b73..ca36050c 100644 --- a/COPYING +++ b/COPYING @@ -15,6 +15,8 @@ This software was written by: Hoàng Đức Hiếu Swift Geek Aetf + Mirco Müller + Jocelyn Falempe = Copyright Notice = diff --git a/src/kmscon_conf.c b/src/kmscon_conf.c index cd8f0a05..7650334a 100644 --- a/src/kmscon_conf.c +++ b/src/kmscon_conf.c @@ -134,6 +134,10 @@ static void print_help() "\t Close current session\n" "\t --grab-terminal-new [Return]\n" "\t Create a new terminal session\n" + "\t --grab-rotate-cw [Plus]\n" + "\t Rotate output clock-wise\n" + "\t --grab-rotate-ccw [Minus]\n" + "\t Rotate output counter-clock-wise\n" "\n" "Video Options:\n" "\t --drm [on] Use DRM if available\n" @@ -149,6 +153,7 @@ static void print_help() "\t error, a default mode will be used.\n" "\t This option is incompatible with\n" "\t --use-original-mode.\n" + "\t --rotate [normal] normal, right, upside-down, left\n" "\n" "Font Options:\n" "\t --font-engine [pango]\n" @@ -646,6 +651,12 @@ static struct conf_grab def_grab_session_close = static struct conf_grab def_grab_terminal_new = CONF_SINGLE_GRAB(SHL_CONTROL_MASK | SHL_LOGO_MASK, XKB_KEY_Return); +static struct conf_grab def_grab_rotate_cw = + CONF_SINGLE_GRAB(SHL_LOGO_MASK, XKB_KEY_plus); + +static struct conf_grab def_grab_rotate_ccw = + CONF_SINGLE_GRAB(SHL_LOGO_MASK, XKB_KEY_minus); + static palette_t def_palette = { [TSM_COLOR_BLACK] = { 0, 0, 0 }, /* black */ [TSM_COLOR_RED] = { 205, 0, 0 }, /* red */ @@ -729,6 +740,8 @@ int kmscon_conf_new(struct conf_ctx **out) CONF_OPTION_GRAB(0, "grab-session-dummy", &conf->grab_session_dummy, &def_grab_session_dummy), CONF_OPTION_GRAB(0, "grab-session-close", &conf->grab_session_close, &def_grab_session_close), CONF_OPTION_GRAB(0, "grab-terminal-new", &conf->grab_terminal_new, &def_grab_terminal_new), + CONF_OPTION_GRAB(0, "grab-rotate-cw", &conf->grab_rotate_cw, &def_grab_rotate_cw), + CONF_OPTION_GRAB(0, "grab-rotate-ccw", &conf->grab_rotate_ccw, &def_grab_rotate_ccw), /* Video Options */ CONF_OPTION_BOOL_FULL(0, "drm", aftercheck_drm, NULL, NULL, &conf->drm, true), @@ -737,6 +750,7 @@ int kmscon_conf_new(struct conf_ctx **out) CONF_OPTION_STRING(0, "render-engine", &conf->render_engine, NULL), CONF_OPTION_BOOL(0, "use-original-mode", &conf->use_original_mode, true), CONF_OPTION_STRING(0, "mode", &conf->mode, NULL), + CONF_OPTION_STRING(0, "rotate", &conf->rotate, "normal"), /* Font Options */ CONF_OPTION_STRING(0, "font-engine", &conf->font_engine, "pango"), diff --git a/src/kmscon_conf.h b/src/kmscon_conf.h index 47f3226d..e62ba18b 100644 --- a/src/kmscon_conf.h +++ b/src/kmscon_conf.h @@ -135,6 +135,10 @@ struct kmscon_conf_t { struct conf_grab *grab_session_close; /* terminal-new grab */ struct conf_grab *grab_terminal_new; + /* rotate output clock-wise grab */ + struct conf_grab *grab_rotate_cw; + /* rotate output counter-clock-wise grab */ + struct conf_grab *grab_rotate_ccw; /* Video Options */ /* use DRM if available */ @@ -149,6 +153,8 @@ struct kmscon_conf_t { bool use_original_mode; /* screen resolution */ char *mode; + /* orientation/rotation of output */ + char *rotate; /* Font Options */ /* font engine */ diff --git a/src/kmscon_terminal.c b/src/kmscon_terminal.c index ee986033..6ea2e354 100644 --- a/src/kmscon_terminal.c +++ b/src/kmscon_terminal.c @@ -89,7 +89,7 @@ static void do_clear_margins(struct screen *scr) unsigned int w, h, sw, sh; struct uterm_mode *mode; struct tsm_screen_attr attr; - int dw, dh; + int dh, dw; mode = uterm_display_get_current(scr->disp); if (!mode) @@ -97,21 +97,35 @@ static void do_clear_margins(struct screen *scr) sw = uterm_mode_get_width(mode); sh = uterm_mode_get_height(mode); - w = scr->txt->font->attr.width * scr->txt->cols; - h = scr->txt->font->attr.height * scr->txt->rows; - dw = sw - w; - dh = sh - h; tsm_vte_get_def_attr(scr->term->vte, &attr); - if (dw > 0) - uterm_display_fill(scr->disp, attr.br, attr.bg, attr.bb, - w, 0, - dw, h); - if (dh > 0) - uterm_display_fill(scr->disp, attr.br, attr.bg, attr.bb, - 0, h, - sw, dh); + if (scr->txt->orientation == OR_NORMAL || scr->txt->orientation == OR_UPSIDE_DOWN) { + w = scr->txt->font->attr.width * scr->txt->cols; + h = scr->txt->font->attr.height * scr->txt->rows; + } else { + w = scr->txt->font->attr.height * scr->txt->rows; + h = scr->txt->font->attr.width * scr->txt->cols; + } + dw = sw - w; + dh = sh - h; + + if (dw) { + if (scr->txt->orientation == OR_NORMAL || scr->txt->orientation == OR_LEFT) + uterm_display_fill(scr->disp, attr.br, attr.bg, attr.bb, + w, 0,dw, sh); + else + uterm_display_fill(scr->disp, attr.br, attr.bg, attr.bb, + 0, 0,dw, sh); + } + if (dh) { + if (scr->txt->orientation == OR_NORMAL || scr->txt->orientation == OR_RIGHT) + uterm_display_fill(scr->disp, attr.br, attr.bg, attr.bb, + 0, h, sw, dh); + else + uterm_display_fill(scr->disp, attr.br, attr.bg, attr.bb, + 0, 0, sw, dh); + } } static int font_set(struct kmscon_terminal *term); @@ -303,6 +317,57 @@ static int font_set(struct kmscon_terminal *term) return 0; } +static void rotate_cw_screen(struct screen *scr) +{ + unsigned int orientation = kmscon_text_get_orientation(scr->txt); + orientation = (orientation + 1) % (OR_LEFT + 1); + kmscon_text_rotate(scr->txt, orientation); +} + +static void rotate_cw_all(struct kmscon_terminal *term) +{ + struct shl_dlist *iter; + struct screen *scr; + + shl_dlist_for_each(iter, &term->screens) { + scr = shl_dlist_entry(iter, struct screen, list); + rotate_cw_screen(scr); + term->min_cols = 0; + term->min_rows = 0; + terminal_resize(term, + kmscon_text_get_cols(scr->txt), + kmscon_text_get_rows(scr->txt), + true, true); + } +} + +static void rotate_ccw_screen(struct screen *scr) +{ + unsigned int orientation = kmscon_text_get_orientation(scr->txt); + if (orientation == OR_NORMAL) + orientation = OR_LEFT; + else + orientation -= 1; + kmscon_text_rotate(scr->txt, orientation); +} + +static void rotate_ccw_all(struct kmscon_terminal *term) +{ + struct shl_dlist *iter; + struct screen *scr; + + shl_dlist_for_each(iter, &term->screens) { + scr = shl_dlist_entry(iter, struct screen, list); + rotate_ccw_screen(scr); + term->min_cols = 0; + term->min_rows = 0; + terminal_resize(term, + kmscon_text_get_cols(scr->txt), + kmscon_text_get_rows(scr->txt), + true, true); + } +} + static int add_display(struct kmscon_terminal *term, struct uterm_display *disp) { struct shl_dlist *iter; @@ -340,7 +405,7 @@ static int add_display(struct kmscon_terminal *term, struct uterm_display *disp) else be = "bbulk"; - ret = kmscon_text_new(&scr->txt, be); + ret = kmscon_text_new(&scr->txt, be, term->conf->rotate); if (ret) { log_error("cannot create text-renderer"); goto err_cb; @@ -480,6 +545,18 @@ static void input_event(struct uterm_input *input, ++term->font_attr.points; return; } + if (conf_grab_matches(term->conf->grab_rotate_cw, + ev->mods, ev->num_syms, ev->keysyms)) { + rotate_cw_all(term); + ev->handled = true; + return; + } + if (conf_grab_matches(term->conf->grab_rotate_ccw, + ev->mods, ev->num_syms, ev->keysyms)) { + rotate_ccw_all(term); + ev->handled = true; + return; + } /* TODO: xkbcommon supports multiple keysyms, but it is currently * unclear how this feature will be used. There is no keymap, which diff --git a/src/text.c b/src/text.c index e70a679a..1d53eb9f 100644 --- a/src/text.c +++ b/src/text.c @@ -35,7 +35,6 @@ #include #include #include -#include "shl_dlist.h" #include "shl_log.h" #include "shl_misc.h" #include "shl_register.h" @@ -103,7 +102,7 @@ void kmscon_text_unregister(const char *name) shl_register_remove(&text_reg, name); } -static int new_text(struct kmscon_text *text, const char *backend) +static int new_text(struct kmscon_text *text, const char *backend, enum Orientation orientation) { struct shl_register_record *record; const char *name = backend ? backend : ""; @@ -124,6 +123,7 @@ static int new_text(struct kmscon_text *text, const char *backend) text->record = record; text->ops = record->data; + text->orientation = orientation; if (text->ops->init) ret = text->ops->init(text); @@ -143,10 +143,11 @@ static int new_text(struct kmscon_text *text, const char *backend) * kmscon_text_new: * @out: A pointer to the new text-renderer is stored here * @backend: Backend to use or NULL for default backend + * @rotate: Orientation ("normal", "upside-down", "right" or "left") to use for output * * 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, const char *rotate) { struct kmscon_text *text; int ret; @@ -160,10 +161,31 @@ int kmscon_text_new(struct kmscon_text **out, const char *backend) return -ENOMEM; } - ret = new_text(text, backend); + text->orientation = OR_NORMAL; + + if (rotate) { + if (strncmp(rotate, "normal", 6) == 0) { + text->orientation = OR_NORMAL; + log_debug("using: orientation: normal"); + } + else if (strncmp(rotate, "right", 5) == 0) { + text->orientation = OR_RIGHT; + log_debug("using: orientation: right"); + } + else if (strncmp(rotate, "upside-down", 8) == 0) { + text->orientation = OR_UPSIDE_DOWN; + log_debug("using: orientation: upside-down"); + } + else if (strncmp(rotate, "left", 4) == 0) { + text->orientation = OR_LEFT; + log_debug("using: orientation: left"); + } + } + + ret = new_text(text, backend, text->orientation); if (ret) { if (backend) - ret = new_text(text, NULL); + ret = new_text(text, NULL, text->orientation); if (ret) goto err_free; } @@ -333,6 +355,49 @@ unsigned int kmscon_text_get_rows(struct kmscon_text *txt) return txt->rows; } +/** + * kmscon_text_get_orientation: + * @txt: valid text renderer + * + * With a valid @txt passed it, this returns the currently active orientation + * of the output/screen. Possible values are: + * + * - OR_NORMAL + * - OR_RIGHT + * - OR_UPSIDE_DOWN + * - OR_LEFT + * + * Returns: Current orientation enum or OR_NORMAL if @txt is invalid + */ +enum Orientation kmscon_text_get_orientation(struct kmscon_text *txt) +{ + if (!txt) + return OR_NORMAL; + + return txt->orientation; +} + +/** + * kmscon_text_rotate: + * @txt: valid text renderer + * @orientation: enum value representing the desired output-orientation + * + * Update the rotation/orientation of the text. It can be one of: + * + * - OR_NORMAL + * - OR_RIGHT + * - OR_UPSIDE_DOWN + * - OR_LEFT + * + * Returns: 0 on success, negative error code on failure. + */ +int kmscon_text_rotate(struct kmscon_text *txt, enum Orientation orientation) +{ + if (txt->ops->rotate) + return txt->ops->rotate(txt, orientation); + return 0; +} + /** * kmscon_text_prepare: * @txt: valid text renderer diff --git a/src/text.h b/src/text.h index 6ab5710a..3869aea3 100644 --- a/src/text.h +++ b/src/text.h @@ -42,6 +42,13 @@ /* text renderer */ +enum Orientation { + OR_NORMAL = 0, // 0 Degree + OR_RIGHT, // 90 Degree + OR_UPSIDE_DOWN, // 180 Degree + OR_LEFT, // 270 Degree +}; + struct kmscon_text; struct kmscon_text_ops; @@ -58,6 +65,7 @@ struct kmscon_text { unsigned int rows; bool rendering; bool overflow_next; + enum Orientation orientation; }; struct kmscon_text_ops { @@ -67,6 +75,7 @@ struct kmscon_text_ops { void (*destroy) (struct kmscon_text *txt); int (*set) (struct kmscon_text *txt); void (*unset) (struct kmscon_text *txt); + int (*rotate) (struct kmscon_text *txt, enum Orientation orientation); int (*prepare) (struct kmscon_text *txt); int (*draw) (struct kmscon_text *txt, uint64_t id, const uint32_t *ch, size_t len, @@ -80,7 +89,7 @@ struct kmscon_text_ops { int kmscon_text_register(const struct kmscon_text_ops *ops); void kmscon_text_unregister(const char *name); -int kmscon_text_new(struct kmscon_text **out, const char *backend); +int kmscon_text_new(struct kmscon_text **out, const char *backend, const char *rotate); void kmscon_text_ref(struct kmscon_text *txt); void kmscon_text_unref(struct kmscon_text *txt); @@ -92,6 +101,9 @@ void kmscon_text_unset(struct kmscon_text *txt); unsigned int kmscon_text_get_cols(struct kmscon_text *txt); unsigned int kmscon_text_get_rows(struct kmscon_text *txt); +enum Orientation kmscon_text_get_orientation(struct kmscon_text *txt); +int kmscon_text_rotate(struct kmscon_text *txt, enum Orientation orientation); + int kmscon_text_prepare(struct kmscon_text *txt); int kmscon_text_draw(struct kmscon_text *txt, uint64_t id, const uint32_t *ch, size_t len, From 9bcf13db7899768476ce1248f648fc42e7d9066c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mirco=20M=C3=BCller?= Date: Thu, 16 Oct 2025 23:18:27 +0200 Subject: [PATCH 2/9] Add rotation documentation Signed-off-by: Jocelyn Falempe --- docs/man/kmscon.1.xml.in | 28 ++++++++++++++++++++++++++++ docs/man/kmscon.conf.1.xml.in | 21 +++++++++++++++++++++ 2 files changed, 49 insertions(+) diff --git a/docs/man/kmscon.1.xml.in b/docs/man/kmscon.1.xml.in index acb4d106..f38262a3 100644 --- a/docs/man/kmscon.1.xml.in +++ b/docs/man/kmscon.1.xml.in @@ -439,6 +439,22 @@ (default: <Ctrl><Logo>Return) + + + + + Rotate output clock-wise: Normal -> Right -> Inverted -> Left and so on. + (default: <Logo>Plus) + + + + + + + Rotate output counter-clock-wise: Normal -> Left -> Inverted -> Right and so on. + (default: <Logo>Minus) + + Video Options: @@ -526,6 +542,18 @@ framebuffers for a large resolution. + + + + + Select the desired orientation of the output. Available orientations + are 'normal' meaning output is not rotated, 'right' meaning output is + rotated 90° clock-wise, 'upside-down' meaning output is rotated 180° + clock-wise and 'left' meaning output is rotated 90° counter clock-wise. + This has only effect for the 'gltex' render-engine. (default: 'normal') + + + Font Options: diff --git a/docs/man/kmscon.conf.1.xml.in b/docs/man/kmscon.conf.1.xml.in index d9849a34..d8b3bac6 100644 --- a/docs/man/kmscon.conf.1.xml.in +++ b/docs/man/kmscon.conf.1.xml.in @@ -340,6 +340,20 @@ font-name=Ubuntu Mono + + + + Rotate output clock-wise. (default: <Logo>Plus) + + + + + + + Rotate output counter-clock-wise. (default: <Logo>Minus) + + + ### Video Options ### @@ -390,6 +404,13 @@ font-name=Ubuntu Mono + + + + Orientation of output to use. (default: normal) + + + ### Font Options ### From 8ad5b4f8c6fb82f045a3147ede6a95799a118f1c Mon Sep 17 00:00:00 2001 From: Jocelyn Falempe Date: Fri, 17 Oct 2025 15:24:37 +0200 Subject: [PATCH 3/9] Add font_rotate helper This will help to add rotation support for pixman, bbulk and bblit. When looking for a glyph, it will rotate it, and save it in the cache. So a simple blit can be used each time this glyph is drawn. Signed-off-by: Jocelyn Falempe --- src/font.h | 1 - src/font_rotate.c | 120 ++++++++++++++++++++++++++++++++++++++++++++ src/font_rotate.h | 31 ++++++++++++ src/meson.build | 1 + src/shl_hashtable.h | 1 + 5 files changed, 153 insertions(+), 1 deletion(-) create mode 100644 src/font_rotate.c create mode 100644 src/font_rotate.h diff --git a/src/font.h b/src/font.h index 0c772454..6f1a79ec 100644 --- a/src/font.h +++ b/src/font.h @@ -64,7 +64,6 @@ bool kmscon_font_attr_match(const struct kmscon_font_attr *a1, struct kmscon_glyph { struct uterm_video_buffer buf; unsigned int width; - void *data; }; struct kmscon_font { diff --git a/src/font_rotate.c b/src/font_rotate.c new file mode 100644 index 00000000..7fe2daa2 --- /dev/null +++ b/src/font_rotate.c @@ -0,0 +1,120 @@ +/* + * kmscon - rotate font + * + * Copyright (c) 2025 Jocelyn Falempe + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "font_rotate.h" +#include "shl_misc.h" + + SHL_EXPORT +int kmscon_rotate_create_tables(struct shl_hashtable **normal, struct shl_hashtable **bold, shl_free_cb free_glyph) +{ + int ret; + + ret = shl_hashtable_new(normal, shl_direct_hash, + shl_direct_equal, free_glyph); + if (ret) + return ret; + + ret = shl_hashtable_new(bold, shl_direct_hash, + shl_direct_equal, free_glyph); + if (ret) + shl_hashtable_free(*normal); + return ret; +} + +SHL_EXPORT +void kmscon_rotate_free_tables(struct shl_hashtable *normal, struct shl_hashtable *bold) +{ + shl_hashtable_free(normal); + shl_hashtable_free(bold); +} + +SHL_EXPORT +int kmscon_rotate_glyph(struct uterm_video_buffer *vb, const struct kmscon_glyph *glyph, enum Orientation orientation, uint8_t align) +{ + int width; + int height; + int stride; + int i, j; + uint8_t *dst, *src; + const struct uterm_video_buffer *buf = &glyph->buf; + + if (orientation == OR_NORMAL || orientation == OR_UPSIDE_DOWN) { + width = buf->width; + height = buf->height; + } else { + width = buf->height; + height = buf->width; + } + + stride = align * ((width + (align - 1)) / align); + vb->data = malloc(stride * height); + + if (!vb->data) + return -ENOMEM; + + src = buf->data; + dst = vb->data; + + switch (orientation) { + default: + case OR_NORMAL: + for (i = 0; i < buf->height; i++) { + memcpy(dst, src, buf->width); + dst += stride; + src += buf->stride; + } + break; + case OR_RIGHT: + for (i = 0; i < buf->height; i++) { + for (j = 0; j < buf->width; j++) { + dst[j * stride + (width - i - 1)] = src[j]; + } + src += buf->stride; + } + break; + case OR_UPSIDE_DOWN: + src += (buf->height - 1) * buf->stride; + for (i = 0; i < buf->height; i++) { + for (j = 0; j < buf->width; j++) + dst[j] = src[buf->width - j - 1]; + dst += stride; + src -= buf->stride; + } + break; + case OR_LEFT: + for (i = 0; i < buf->height; i++) { + for (j = 0; j < buf->width; j++) { + dst[(height -j -1) * stride + i] = src[j]; + } + src += buf->stride; + } + } + vb->width = width; + vb->height = height; + vb->stride = stride; + vb->format = buf->format; + return 0; +} + diff --git a/src/font_rotate.h b/src/font_rotate.h new file mode 100644 index 00000000..aa687ab0 --- /dev/null +++ b/src/font_rotate.h @@ -0,0 +1,31 @@ +/* + * kmscon - rotate font + * + * Copyright (c) 2025 Jocelyn Falempe + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "shl_hashtable.h" +#include "text.h" + +int kmscon_rotate_create_tables(struct shl_hashtable **normal, struct shl_hashtable **bold, shl_free_cb free_glyph); +void kmscon_rotate_free_tables(struct shl_hashtable *normal, struct shl_hashtable *bold); +int kmscon_rotate_glyph(struct uterm_video_buffer *vb, const struct kmscon_glyph *glyph, enum Orientation orientation, uint8_t align); diff --git a/src/meson.build b/src/meson.build index a670f78d..7cf5c889 100644 --- a/src/meson.build +++ b/src/meson.build @@ -242,6 +242,7 @@ conf_deps = declare_dependency( kmscon_srcs = [ 'pty.c', 'font.c', + 'font_rotate.c', 'font_8x16.c', 'text.c', 'text_bblit.c', diff --git a/src/shl_hashtable.h b/src/shl_hashtable.h index e6f05a7f..72ee120b 100644 --- a/src/shl_hashtable.h +++ b/src/shl_hashtable.h @@ -36,6 +36,7 @@ #include #include #include +#include #include "htable.h" struct shl_hashtable; From 203040023a113034b80d12efa6f0453c12cb2237 Mon Sep 17 00:00:00 2001 From: Jocelyn Falempe Date: Wed, 22 Oct 2025 10:57:23 +0200 Subject: [PATCH 4/9] Move FONT_WIDTH()/FONT_HEIGHT() macro to text.h So that bblit and pixman renderer can use it too. Signed-off-by: Jocelyn Falempe --- src/kmscon_terminal.c | 8 ++++---- src/text.h | 3 +++ src/text_bblit.c | 8 +++----- src/text_bbulk.c | 3 --- src/text_gltex.c | 3 --- src/text_pixman.c | 4 ++-- 6 files changed, 12 insertions(+), 17 deletions(-) diff --git a/src/kmscon_terminal.c b/src/kmscon_terminal.c index 6ea2e354..bb1287fe 100644 --- a/src/kmscon_terminal.c +++ b/src/kmscon_terminal.c @@ -101,11 +101,11 @@ static void do_clear_margins(struct screen *scr) tsm_vte_get_def_attr(scr->term->vte, &attr); if (scr->txt->orientation == OR_NORMAL || scr->txt->orientation == OR_UPSIDE_DOWN) { - w = scr->txt->font->attr.width * scr->txt->cols; - h = scr->txt->font->attr.height * scr->txt->rows; + w = FONT_WIDTH(scr->txt) * scr->txt->cols; + h = FONT_HEIGHT(scr->txt) * scr->txt->rows; } else { - w = scr->txt->font->attr.height * scr->txt->rows; - h = scr->txt->font->attr.width * scr->txt->cols; + w = FONT_HEIGHT(scr->txt) * scr->txt->rows; + h = FONT_WIDTH(scr->txt) * scr->txt->cols; } dw = sw - w; dh = sh - h; diff --git a/src/text.h b/src/text.h index 3869aea3..bbba5b0e 100644 --- a/src/text.h +++ b/src/text.h @@ -86,6 +86,9 @@ struct kmscon_text_ops { void (*abort) (struct kmscon_text *txt); }; +#define FONT_WIDTH(txt) ((txt)->font->attr.width) +#define FONT_HEIGHT(txt) ((txt)->font->attr.height) + int kmscon_text_register(const struct kmscon_text_ops *ops); void kmscon_text_unregister(const char *name); diff --git a/src/text_bblit.c b/src/text_bblit.c index 2b5ff2c6..f90c14df 100644 --- a/src/text_bblit.c +++ b/src/text_bblit.c @@ -44,19 +44,17 @@ static int bblit_set(struct kmscon_text *txt) { - unsigned int sw, sh, fw, fh; + unsigned int sw, sh; struct uterm_mode *mode; - fw = txt->font->attr.width; - fh = txt->font->attr.height; mode = uterm_display_get_current(txt->disp); if (!mode) return -EINVAL; sw = uterm_mode_get_width(mode); sh = uterm_mode_get_height(mode); - txt->cols = sw / fw; - txt->rows = sh / fh; + txt->cols = sw / FONT_WIDTH(txt); + txt->rows = sh / FONT_HEIGHT(txt); return 0; } diff --git a/src/text_bbulk.c b/src/text_bbulk.c index 2fc8d8af..c62ce72f 100644 --- a/src/text_bbulk.c +++ b/src/text_bbulk.c @@ -46,9 +46,6 @@ struct bbulk { struct uterm_video_blend_req *reqs; }; -#define FONT_WIDTH(txt) ((txt)->font->attr.width) -#define FONT_HEIGHT(txt) ((txt)->font->attr.height) - static int bbulk_init(struct kmscon_text *txt) { struct bbulk *bb; diff --git a/src/text_gltex.c b/src/text_gltex.c index 12ae8049..19ec48a4 100644 --- a/src/text_gltex.c +++ b/src/text_gltex.c @@ -114,9 +114,6 @@ struct gltex { unsigned int sh; }; -#define FONT_WIDTH(txt) ((txt)->font->attr.width) -#define FONT_HEIGHT(txt) ((txt)->font->attr.height) - static int gltex_init(struct kmscon_text *txt) { struct gltex *gt; diff --git a/src/text_pixman.c b/src/text_pixman.c index b278b7cb..70d18cca 100644 --- a/src/text_pixman.c +++ b/src/text_pixman.c @@ -223,8 +223,8 @@ static int tp_set(struct kmscon_text *txt) } } - txt->cols = w / txt->font->attr.width; - txt->rows = h / txt->font->attr.height; + txt->cols = w / FONT_WIDTH(txt); + txt->rows = h / FONT_HEIGHT(txt); return 0; From 47c2053cd2e428bd135b96453233c29fce689e45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mirco=20M=C3=BCller?= Date: Thu, 16 Oct 2025 23:14:20 +0200 Subject: [PATCH 5/9] Add rotation support to gltex renderer Uses opengl shaders to rotate the glyph Co-authored-by: Jocelyn Falempe Signed-off-by: Jocelyn Falempe --- src/text_gltex.c | 61 ++++++++++++++++++++++++++++++++++++--- src/text_gltex_atlas.vert | 10 ++++++- 2 files changed, 66 insertions(+), 5 deletions(-) diff --git a/src/text_gltex.c b/src/text_gltex.c index 19ec48a4..6a1c8f39 100644 --- a/src/text_gltex.c +++ b/src/text_gltex.c @@ -105,6 +105,8 @@ struct gltex { GLfloat advance_y; struct gl_shader *shader; + GLuint uni_cos; + GLuint uni_sin; GLuint uni_proj; GLuint uni_atlas; GLuint uni_advance_htex; @@ -112,6 +114,9 @@ struct gltex { unsigned int sw; unsigned int sh; + + GLfloat cos; + GLfloat sin; }; static int gltex_init(struct kmscon_text *txt) @@ -183,6 +188,8 @@ static int gltex_set(struct kmscon_text *txt) if (ret) goto err_bold_htable; + gt->uni_cos = gl_shader_get_uniform(gt->shader, "cos"); + gt->uni_sin = gl_shader_get_uniform(gt->shader, "sin"); gt->uni_proj = gl_shader_get_uniform(gt->shader, "projection"); gt->uni_atlas = gl_shader_get_uniform(gt->shader, "atlas"); gt->uni_advance_htex = gl_shader_get_uniform(gt->shader, @@ -199,8 +206,13 @@ static int gltex_set(struct kmscon_text *txt) gt->sw = uterm_mode_get_width(mode); gt->sh = uterm_mode_get_height(mode); - txt->cols = gt->sw / FONT_WIDTH(txt); - txt->rows = gt->sh / FONT_HEIGHT(txt); + if (txt->orientation == OR_NORMAL || txt->orientation == OR_UPSIDE_DOWN) { + txt->cols = gt->sw / FONT_WIDTH(txt); + txt->rows = gt->sh / FONT_HEIGHT(txt); + } else { + txt->cols = gt->sh / FONT_WIDTH(txt); + txt->rows = gt->sw / FONT_HEIGHT(txt); + } glGetIntegerv(GL_MAX_TEXTURE_SIZE, &s); if (s <= 0) @@ -520,6 +532,37 @@ static int find_glyph(struct kmscon_text *txt, struct glyph **out, return ret; } +static void gltex_set_rotate(struct gltex *gt, enum Orientation orientation) +{ + float sin_table[5] = {0.0, 1.0, 0.0, -1.0, 0.0}; + + gt->cos = sin_table[orientation + 1]; + gt->sin = sin_table[orientation]; +} + +static int gltex_rotate(struct kmscon_text *txt, enum Orientation orientation) +{ + struct gltex *gt = txt->data; + + txt->orientation = orientation; + + if (txt->orientation == OR_NORMAL || txt->orientation == OR_UPSIDE_DOWN) { + txt->cols = gt->sw / FONT_WIDTH(txt); + txt->rows = gt->sh / FONT_HEIGHT(txt); + gt->advance_x = 2.0 / gt->sw * FONT_WIDTH(txt); + gt->advance_y = 2.0 / gt->sh * FONT_HEIGHT(txt); + } else { + float aspect = (float) gt->sw / (float) gt->sh; + txt->cols = gt->sh / FONT_WIDTH(txt); + txt->rows = gt->sw / FONT_HEIGHT(txt); + gt->advance_x = 2.0 / gt->sw * FONT_WIDTH(txt) * aspect; + gt->advance_y = 2.0 / gt->sh * FONT_HEIGHT(txt) * (1./aspect); + } + gltex_unset(txt); + gltex_set(txt); + return 0; +} + static int gltex_prepare(struct kmscon_text *txt) { struct gltex *gt = txt->data; @@ -537,8 +580,15 @@ static int gltex_prepare(struct kmscon_text *txt) atlas->cache_num = 0; } - gt->advance_x = 2.0 / gt->sw * FONT_WIDTH(txt); - gt->advance_y = 2.0 / gt->sh * FONT_HEIGHT(txt); + if (txt->orientation == OR_NORMAL || txt->orientation == OR_UPSIDE_DOWN) { + gt->advance_x = 2.0 / gt->sw * FONT_WIDTH(txt); + gt->advance_y = 2.0 / gt->sh * FONT_HEIGHT(txt); + } else { + float aspect = (float) gt->sw / (float) gt->sh; + gt->advance_x = 2.0 / gt->sw * FONT_WIDTH(txt) * aspect; + gt->advance_y = 2.0 / gt->sh * FONT_HEIGHT(txt) * (1./aspect); + } + gltex_set_rotate(gt, txt->orientation); return 0; } @@ -648,6 +698,8 @@ static int gltex_render(struct kmscon_text *txt) gl_m4_identity(mat); glUniformMatrix4fv(gt->uni_proj, 1, GL_FALSE, mat); + glUniform1f(gt->uni_cos, gt->cos); + glUniform1f(gt->uni_sin, gt->sin); glEnableVertexAttribArray(0); glEnableVertexAttribArray(1); @@ -693,6 +745,7 @@ struct kmscon_text_ops kmscon_text_gltex_ops = { .destroy = gltex_destroy, .set = gltex_set, .unset = gltex_unset, + .rotate = gltex_rotate, .prepare = gltex_prepare, .draw = gltex_draw, .render = gltex_render, diff --git a/src/text_gltex_atlas.vert b/src/text_gltex_atlas.vert index e99e1b4a..a6ffa105 100644 --- a/src/text_gltex_atlas.vert +++ b/src/text_gltex_atlas.vert @@ -31,6 +31,8 @@ */ uniform mat4 projection; +uniform float cos; +uniform float sin; attribute vec2 position; attribute vec2 texture_position; @@ -41,9 +43,15 @@ varying vec2 texpos; varying vec3 fgcol; varying vec3 bgcol; +vec2 opRotate(in vec2 p) +{ + return p * mat2(vec2(cos, sin), vec2(-sin, cos)); +} + void main() { - gl_Position = projection * vec4(position, 0.0, 1.0); + vec2 rotatedPosition = opRotate(position); + gl_Position = projection * vec4(rotatedPosition, 0.0, 1.0); texpos = texture_position; fgcol = fgcolor; bgcol = bgcolor; From 44a846b7b2a10a613cf874ddb526e095d4a4b2d1 Mon Sep 17 00:00:00 2001 From: Jocelyn Falempe Date: Wed, 22 Oct 2025 11:04:39 +0200 Subject: [PATCH 6/9] Add rotation support to bblit renderer Signed-off-by: Jocelyn Falempe --- src/text_bblit.c | 180 ++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 153 insertions(+), 27 deletions(-) diff --git a/src/text_bblit.c b/src/text_bblit.c index f90c14df..0c1a3240 100644 --- a/src/text_bblit.c +++ b/src/text_bblit.c @@ -36,14 +36,48 @@ #include #include #include +#include "font_rotate.h" #include "shl_log.h" #include "text.h" #include "uterm_video.h" #define LOG_SUBSYSTEM "text_bblit" +struct bblit { + struct shl_hashtable *glyphs; + struct shl_hashtable *bold_glyphs; +}; + +static int bblit_init(struct kmscon_text *txt) +{ + struct bblit *bb; + + bb = malloc(sizeof(*bb)); + if (!bb) + return -ENOMEM; + + txt->data = bb; + return 0; +} + +static void bblit_destroy(struct kmscon_text *txt) +{ + struct bblit *bb = txt->data; + + free(bb); +} + +static void free_glyph(void *data) +{ + struct uterm_video_buffer *bb_glyph = data; + + free(bb_glyph->data); + free(bb_glyph); +} + static int bblit_set(struct kmscon_text *txt) { + struct bblit *bb = txt->data; unsigned int sw, sh; struct uterm_mode *mode; @@ -53,29 +87,49 @@ static int bblit_set(struct kmscon_text *txt) sw = uterm_mode_get_width(mode); sh = uterm_mode_get_height(mode); - txt->cols = sw / FONT_WIDTH(txt); - txt->rows = sh / FONT_HEIGHT(txt); + if (txt->orientation == OR_NORMAL || txt->orientation == OR_UPSIDE_DOWN) { + txt->cols = sw / FONT_WIDTH(txt); + txt->rows = sh / FONT_HEIGHT(txt); + } else { + txt->rows = sw / FONT_HEIGHT(txt); + txt->cols = sh / FONT_WIDTH(txt); + } - return 0; + return kmscon_rotate_create_tables(&bb->glyphs, &bb->bold_glyphs, free_glyph); } -static int bblit_draw(struct kmscon_text *txt, - uint64_t id, const uint32_t *ch, size_t len, - unsigned int width, - unsigned int posx, unsigned int posy, - const struct tsm_screen_attr *attr) +static void bblit_unset(struct kmscon_text *txt) { + struct bblit *bb = txt->data; + + kmscon_rotate_free_tables(bb->glyphs, bb->bold_glyphs); +} + +static int bblit_rotate(struct kmscon_text *txt, enum Orientation orientation) +{ + bblit_unset(txt); + txt->orientation = orientation; + return bblit_set(txt); +} + +static int find_glyph(struct kmscon_text *txt, struct uterm_video_buffer **out, + uint64_t id, const uint32_t *ch, size_t len, const struct tsm_screen_attr *attr) +{ + struct bblit *bb = txt->data; + struct uterm_video_buffer *bb_glyph; const struct kmscon_glyph *glyph; - int ret; + struct shl_hashtable *gtable; struct kmscon_font *font; + int ret; + bool res; - if (!width) - return 0; - - if (attr->bold) + if (attr->bold) { + gtable = bb->bold_glyphs; font = txt->bold_font; - else + } else { + gtable = bb->glyphs; font = txt->font; + } if (attr->underline) font->attr.underline = true; @@ -87,29 +141,100 @@ static int bblit_draw(struct kmscon_text *txt, else font->attr.italic = false; - if (!len) { + res = shl_hashtable_find(gtable, (void**)&bb_glyph, id); + if (res) { + *out = bb_glyph; + return 0; + } + + bb_glyph = malloc(sizeof(*bb_glyph)); + if (!bb_glyph) + return -ENOMEM; + memset(bb_glyph, 0, sizeof(*bb_glyph)); + + if (!len) ret = kmscon_font_render_empty(font, &glyph); - } else { + else ret = kmscon_font_render(font, id, ch, len, &glyph); - } if (ret) { ret = kmscon_font_render_inval(font, &glyph); if (ret) - return ret; + goto err_free; + } + + ret = kmscon_rotate_glyph( bb_glyph, glyph, txt->orientation, 1); + if (ret) + goto err_free; + + ret = shl_hashtable_insert(gtable, id, bb_glyph); + if (ret) + goto err_free_vb; + + *out = bb_glyph; + return 0; + +err_free_vb: + free(bb_glyph->data); +err_free: + free(bb_glyph); + return ret; +} + + +static int bblit_draw(struct kmscon_text *txt, + uint64_t id, const uint32_t *ch, size_t len, + unsigned int width, + unsigned int posx, unsigned int posy, + const struct tsm_screen_attr *attr) +{ + struct uterm_video_buffer *bb_glyph; + struct uterm_mode *mode; + unsigned sw, sh, x, y, cwidth; + int ret; + + if (!width) + return 0; + + mode = uterm_display_get_current(txt->disp); + if (!mode) + return -EINVAL; + sw = uterm_mode_get_width(mode); + sh = uterm_mode_get_height(mode); + + ret = find_glyph(txt, &bb_glyph, id, ch, len, attr); + if (ret) + return ret; + + switch (txt->orientation) { + default: + case OR_NORMAL: + x = posx * FONT_WIDTH(txt); + y = posy * FONT_HEIGHT(txt); + break; + case OR_UPSIDE_DOWN: + cwidth = bb_glyph->width / FONT_WIDTH(txt); + x = sw - (posx + cwidth) * FONT_WIDTH(txt); + y = sh - (posy + 1) * FONT_HEIGHT(txt); + break; + case OR_RIGHT: + x = sw - (posy + 1) * FONT_HEIGHT(txt); + y = posx * FONT_WIDTH(txt); + break; + case OR_LEFT: + cwidth = bb_glyph->height / FONT_WIDTH(txt); + x = posy * FONT_HEIGHT(txt); + y = sh - (posx + cwidth) * FONT_WIDTH(txt); + break; } /* draw glyph */ if (attr->inverse) { - ret = uterm_display_fake_blend(txt->disp, &glyph->buf, - posx * txt->font->attr.width, - posy * txt->font->attr.height, + ret = uterm_display_fake_blend(txt->disp, bb_glyph, x, y, attr->br, attr->bg, attr->bb, attr->fr, attr->fg, attr->fb); } else { - ret = uterm_display_fake_blend(txt->disp, &glyph->buf, - posx * txt->font->attr.width, - posy * txt->font->attr.height, + ret = uterm_display_fake_blend(txt->disp, bb_glyph, x, y, attr->fr, attr->fg, attr->fb, attr->br, attr->bg, attr->bb); } @@ -120,10 +245,11 @@ static int bblit_draw(struct kmscon_text *txt, struct kmscon_text_ops kmscon_text_bblit_ops = { .name = "bblit", .owner = NULL, - .init = NULL, - .destroy = NULL, + .init = bblit_init, + .destroy = bblit_destroy, .set = bblit_set, - .unset = NULL, + .unset = bblit_unset, + .rotate = bblit_rotate, .prepare = NULL, .draw = bblit_draw, .render = NULL, From 71d016b3aaae13be9a8b930e11e788450ea5f353 Mon Sep 17 00:00:00 2001 From: Jocelyn Falempe Date: Wed, 22 Oct 2025 11:05:23 +0200 Subject: [PATCH 7/9] Add rotation support to bbulk renderer Signed-off-by: Jocelyn Falempe --- src/text_bbulk.c | 176 +++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 147 insertions(+), 29 deletions(-) diff --git a/src/text_bbulk.c b/src/text_bbulk.c index c62ce72f..f22d2dc3 100644 --- a/src/text_bbulk.c +++ b/src/text_bbulk.c @@ -32,10 +32,14 @@ * pushes all of them at once to the video device. */ +#include #include #include #include #include +#include "font.h" +#include "font_rotate.h" +#include "shl_hashtable.h" #include "shl_log.h" #include "text.h" #include "uterm_video.h" @@ -44,6 +48,8 @@ struct bbulk { struct uterm_video_blend_req *reqs; + struct shl_hashtable *glyphs; + struct shl_hashtable *bold_glyphs; }; static int bbulk_init(struct kmscon_text *txt) @@ -65,6 +71,14 @@ static void bbulk_destroy(struct kmscon_text *txt) free(bb); } +static void free_glyph(void *data) +{ + struct uterm_video_buffer *bb_glyph = data; + + free(bb_glyph->data); + free(bb_glyph); +} + static int bbulk_set(struct kmscon_text *txt) { struct bbulk *bb = txt->data; @@ -80,9 +94,13 @@ static int bbulk_set(struct kmscon_text *txt) sw = uterm_mode_get_width(mode); sh = uterm_mode_get_height(mode); - txt->cols = sw / FONT_WIDTH(txt); - txt->rows = sh / FONT_HEIGHT(txt); - + if (txt->orientation == OR_NORMAL || txt->orientation == OR_UPSIDE_DOWN) { + txt->cols = sw / FONT_WIDTH(txt); + txt->rows = sh / FONT_HEIGHT(txt); + } else { + txt->rows = sw / FONT_HEIGHT(txt); + txt->cols = sh / FONT_WIDTH(txt); + } bb->reqs = malloc(sizeof(*bb->reqs) * txt->cols * txt->rows); if (!bb->reqs) return -ENOMEM; @@ -91,43 +109,71 @@ static int bbulk_set(struct kmscon_text *txt) for (i = 0; i < txt->rows; ++i) { for (j = 0; j < txt->cols; ++j) { req = &bb->reqs[i * txt->cols + j]; - req->x = j * FONT_WIDTH(txt); - req->y = i * FONT_HEIGHT(txt); + switch (txt->orientation) { + default: + case OR_NORMAL: + req->x = j * FONT_WIDTH(txt); + req->y = i * FONT_HEIGHT(txt); + break; + case OR_UPSIDE_DOWN: + req->x = sw - (j + 1) * FONT_WIDTH(txt); + req->y = sh - (i + 1) * FONT_HEIGHT(txt); + break; + case OR_RIGHT: + req->x = sw - (i + 1) * FONT_HEIGHT(txt); + req->y = j * FONT_WIDTH(txt); + break; + case OR_LEFT: + req->x = i * FONT_HEIGHT(txt); + req->y = sh - (j + 1) * FONT_WIDTH(txt); + break; + } + } } - + if (kmscon_rotate_create_tables(&bb->glyphs, &bb->bold_glyphs, free_glyph)) + goto free_reqs; return 0; + +free_reqs: + free(bb->reqs); + return -ENOMEM; } static void bbulk_unset(struct kmscon_text *txt) { struct bbulk *bb = txt->data; + kmscon_rotate_free_tables(bb->glyphs, bb->bold_glyphs); free(bb->reqs); bb->reqs = NULL; } -static int bbulk_draw(struct kmscon_text *txt, - uint64_t id, const uint32_t *ch, size_t len, - unsigned int width, - unsigned int posx, unsigned int posy, - const struct tsm_screen_attr *attr) +static int bbulk_rotate(struct kmscon_text *txt, enum Orientation orientation) +{ + bbulk_unset(txt); + txt->orientation = orientation; + return bbulk_set(txt); +} + +static int find_glyph(struct kmscon_text *txt, struct uterm_video_buffer **out, + uint64_t id, const uint32_t *ch, size_t len, const struct tsm_screen_attr *attr) { struct bbulk *bb = txt->data; + struct uterm_video_buffer *bb_glyph; const struct kmscon_glyph *glyph; - int ret; - struct uterm_video_blend_req *req; + struct shl_hashtable *gtable; struct kmscon_font *font; + int ret; + bool res; - if (!width) { - bb->reqs[posy * txt->cols + posx].buf = NULL; - return 0; - } - - if (attr->bold) + if (attr->bold) { + gtable = bb->bold_glyphs; font = txt->bold_font; - else + } else { + gtable = bb->glyphs; font = txt->font; + } if (attr->underline) font->attr.underline = true; @@ -139,20 +185,79 @@ static int bbulk_draw(struct kmscon_text *txt, else font->attr.italic = false; - if (!len) { + res = shl_hashtable_find(gtable, (void**)&bb_glyph, id); + if (res) { + *out = bb_glyph; + return 0; + } + + bb_glyph = malloc(sizeof(*bb_glyph)); + if (!bb_glyph) + return -ENOMEM; + memset(bb_glyph, 0, sizeof(*bb_glyph)); + + if (!len) ret = kmscon_font_render_empty(font, &glyph); - } else { + else ret = kmscon_font_render(font, id, ch, len, &glyph); - } if (ret) { ret = kmscon_font_render_inval(font, &glyph); if (ret) - return ret; + goto err_free; } + ret = kmscon_rotate_glyph( bb_glyph, glyph, txt->orientation, 1); + if (ret) + goto err_free; + + ret = shl_hashtable_insert(gtable, id, bb_glyph); + if (ret) + goto err_free_vb; + + *out = bb_glyph; + return 0; + +err_free_vb: + free(bb_glyph->data); +err_free: + free(bb_glyph); + return ret; +} + +static int bbulk_draw(struct kmscon_text *txt, + uint64_t id, const uint32_t *ch, size_t len, + unsigned int width, + unsigned int posx, unsigned int posy, + const struct tsm_screen_attr *attr) +{ + struct bbulk *bb = txt->data; + struct uterm_video_buffer *bb_glyph; + struct uterm_video_blend_req *req; + int ret; + + if (!width) { + bb->reqs[posy * txt->cols + posx].buf = NULL; + return 0; + } + ret = find_glyph(txt, &bb_glyph, id, ch, len, attr); + if (ret) + return ret; + req = &bb->reqs[posy * txt->cols + posx]; - req->buf = &glyph->buf; + + /* + * In case of left or upside down orientation, we need to draw to the + * next cell, as the glyph is already rotated, so start on the next cell + * and end on this cell + */ + if (txt->orientation == OR_LEFT || txt->orientation == OR_UPSIDE_DOWN) { + if (txt->overflow_next && posx + 1 < txt->cols) { + req = &bb->reqs[posy * txt->cols + posx + 1]; + } + } + + req->buf = bb_glyph; if (attr->inverse) { req->fr = attr->br; req->fg = attr->bg; @@ -168,8 +273,6 @@ static int bbulk_draw(struct kmscon_text *txt, req->bg = attr->bg; req->bb = attr->bb; } - if (txt->overflow_next && posx + 1 < txt->cols) - bb->reqs[posy * txt->cols + posx + 1].buf = NULL; return 0; } @@ -177,9 +280,23 @@ static int bbulk_draw(struct kmscon_text *txt, static int bbulk_render(struct kmscon_text *txt) { struct bbulk *bb = txt->data; + int ret; - return uterm_display_fake_blendv(txt->disp, bb->reqs, + ret = uterm_display_fake_blendv(txt->disp, bb->reqs, txt->cols * txt->rows); + return ret; +} + +static int bbulk_prepare(struct kmscon_text *txt) +{ + struct bbulk *bb = txt->data; + int i; + + // Clear previous requests + for (i = 0; i < txt->rows * txt->cols; ++i) + bb->reqs[i].buf = NULL; + + return 0; } struct kmscon_text_ops kmscon_text_bbulk_ops = { @@ -189,7 +306,8 @@ struct kmscon_text_ops kmscon_text_bbulk_ops = { .destroy = bbulk_destroy, .set = bbulk_set, .unset = bbulk_unset, - .prepare = NULL, + .rotate = bbulk_rotate, + .prepare = bbulk_prepare, .draw = bbulk_draw, .render = bbulk_render, .abort = NULL, From b919df50283197a35616a83525a97d1265a9e72b Mon Sep 17 00:00:00 2001 From: Jocelyn Falempe Date: Wed, 22 Oct 2025 11:06:13 +0200 Subject: [PATCH 8/9] Add rotation support to the pixman renderer Signed-off-by: Jocelyn Falempe --- src/text_pixman.c | 201 +++++++++++++++++++++++++--------------------- 1 file changed, 109 insertions(+), 92 deletions(-) diff --git a/src/text_pixman.c b/src/text_pixman.c index 70d18cca..8f4da68c 100644 --- a/src/text_pixman.c +++ b/src/text_pixman.c @@ -32,6 +32,8 @@ #include #include #include +#include "font.h" +#include "font_rotate.h" #include "shl_hashtable.h" #include "shl_log.h" #include "text.h" @@ -40,9 +42,8 @@ #define LOG_SUBSYSTEM "text_pixman" struct tp_glyph { - const struct kmscon_glyph *glyph; + struct uterm_video_buffer vb; pixman_image_t *surf; - uint8_t *data; }; struct tp_pixman { @@ -54,7 +55,6 @@ struct tp_pixman { pixman_image_t *surf[2]; unsigned int format[2]; - bool new_stride; bool use_indirect; uint8_t *data[2]; struct uterm_video_buffer vbuf; @@ -90,7 +90,7 @@ static void free_glyph(void *data) struct tp_glyph *glyph = data; pixman_image_unref(glyph->surf); - free(glyph->data); + free(glyph->vb.data); free(glyph); } @@ -181,17 +181,10 @@ static int tp_set(struct kmscon_text *txt) log_error("cannot create pixman solid color buffer"); return -ENOMEM; } - - ret = shl_hashtable_new(&tp->glyphs, shl_direct_hash, - shl_direct_equal, free_glyph); + ret = kmscon_rotate_create_tables(&tp->glyphs, &tp->bold_glyphs, free_glyph); if (ret) goto err_white; - ret = shl_hashtable_new(&tp->bold_glyphs, shl_direct_hash, - shl_direct_equal, free_glyph); - if (ret) - goto err_htable; - /* * TODO: It is actually faster to use a local shadow buffer and then * blit all data to the framebuffer afterwards. Reads seem to be @@ -205,7 +198,7 @@ static int tp_set(struct kmscon_text *txt) txt->disp); ret = alloc_indirect(txt, w, h); if (ret) - goto err_htable_bold; + goto err_glyph_table; } else { tp->format[0] = format_u2p(tp->buf[0].format); tp->surf[0] = pixman_image_create_bits_no_clear(tp->format[0], @@ -222,9 +215,13 @@ static int tp_set(struct kmscon_text *txt) goto err_ctx; } } - - txt->cols = w / FONT_WIDTH(txt); - txt->rows = h / FONT_HEIGHT(txt); + if (txt->orientation == OR_NORMAL || txt->orientation == OR_UPSIDE_DOWN) { + txt->cols = w / txt->font->attr.width; + txt->rows = h / txt->font->attr.height; + } else { + txt->cols = h / txt->font->attr.width; + txt->rows = w / txt->font->attr.height; + } return 0; @@ -235,10 +232,8 @@ static int tp_set(struct kmscon_text *txt) pixman_image_unref(tp->surf[0]); free(tp->data[1]); free(tp->data[0]); -err_htable_bold: - shl_hashtable_free(tp->bold_glyphs); -err_htable: - shl_hashtable_free(tp->glyphs); +err_glyph_table: + kmscon_rotate_free_tables(tp->glyphs, tp->bold_glyphs); err_white: pixman_image_unref(tp->white); return ret; @@ -252,8 +247,7 @@ static void tp_unset(struct kmscon_text *txt) pixman_image_unref(tp->surf[0]); free(tp->data[1]); free(tp->data[0]); - shl_hashtable_free(tp->bold_glyphs); - shl_hashtable_free(tp->glyphs); + kmscon_rotate_free_tables(tp->glyphs, tp->bold_glyphs); pixman_image_unref(tp->white); } @@ -261,13 +255,11 @@ static int find_glyph(struct kmscon_text *txt, struct tp_glyph **out, uint64_t id, const uint32_t *ch, size_t len, const struct tsm_screen_attr *attr) { struct tp_pixman *tp = txt->data; - struct tp_glyph *glyph; + const struct kmscon_glyph *glyph; + struct tp_glyph *tp_glyph; struct shl_hashtable *gtable; struct kmscon_font *font; - const struct uterm_video_buffer *buf; - uint8_t *dst, *src; - unsigned int format, i; - int ret, stride; + int ret; bool res; if (attr->bold) { @@ -288,88 +280,89 @@ static int find_glyph(struct kmscon_text *txt, struct tp_glyph **out, else font->attr.italic = false; - res = shl_hashtable_find(gtable, (void**)&glyph, id); + res = shl_hashtable_find(gtable, (void**)&tp_glyph, id); if (res) { - *out = glyph; + *out = tp_glyph; return 0; } - glyph = malloc(sizeof(*glyph)); - if (!glyph) + tp_glyph = malloc(sizeof(*tp_glyph)); + if (!tp_glyph) return -ENOMEM; - memset(glyph, 0, sizeof(*glyph)); + memset(tp_glyph, 0, sizeof(*tp_glyph)); if (!len) - ret = kmscon_font_render_empty(font, &glyph->glyph); + ret = kmscon_font_render_empty(font, &glyph); else - ret = kmscon_font_render(font, id, ch, len, &glyph->glyph); + ret = kmscon_font_render(font, id, ch, len, &glyph); if (ret) { - ret = kmscon_font_render_inval(font, &glyph->glyph); + ret = kmscon_font_render_inval(font, &glyph); if (ret) goto err_free; } - buf = &glyph->glyph->buf; - stride = buf->stride; - format = format_u2p(buf->format); - glyph->surf = pixman_image_create_bits_no_clear(format, - buf->width, - buf->height, - (void*)buf->data, - buf->stride); - if (!glyph->surf) { - stride = (buf->stride + 3) & ~0x3; - if (!tp->new_stride) { - tp->new_stride = true; - log_debug("wrong stride, copy buffer (%d => %d)", - buf->stride, stride); - } + ret = kmscon_rotate_glyph(&tp_glyph->vb, glyph, txt->orientation, 4); + if (ret) + goto err_free; - glyph->data = malloc(stride * buf->height); - if (!glyph->data) { - log_error("cannot allocate memory for glyph storage"); - ret = -ENOMEM; - goto err_free; - } - src = buf->data; - dst = glyph->data; - for (i = 0; i < buf->height; ++i) { - memcpy(dst, src, buf->width); - dst += stride; - src += buf->stride; - } + tp_glyph->surf = pixman_image_create_bits_no_clear(PIXMAN_a8, + tp_glyph->vb.width, + tp_glyph->vb.height, + (void*) tp_glyph->vb.data, + tp_glyph->vb.stride); - glyph->surf = pixman_image_create_bits_no_clear(format, - buf->width, - buf->height, - (void*) - glyph->data, - stride); - } - if (!glyph->surf) { - log_error("cannot create pixman-glyph: %d %p %d %d %d %d", - ret, glyph->data ? glyph->data : buf->data, format, - buf->width, buf->height, stride); + if (!tp_glyph->surf) { + log_error("cannot create pixman-glyph: %d %p %d %d %d", + ret, tp_glyph->vb.data, tp_glyph->vb.width, tp_glyph->vb.height, tp_glyph->vb.stride); ret = -EFAULT; - goto err_free; + goto err_free_vb; } - ret = shl_hashtable_insert(gtable, id, glyph); + ret = shl_hashtable_insert(gtable, id, tp_glyph); if (ret) goto err_pixman; - *out = glyph; + *out = tp_glyph; return 0; err_pixman: - pixman_image_unref(glyph->surf); + pixman_image_unref(tp_glyph->surf); + +err_free_vb: + free(tp_glyph->vb.data); err_free: - free(glyph); + free(tp_glyph); return ret; } +static int tp_rotate(struct kmscon_text *txt, enum Orientation orientation) +{ + struct tp_pixman *tp = txt->data; + unsigned int w, h; + struct uterm_mode *m; + + m = uterm_display_get_current(txt->disp); + w = uterm_mode_get_width(m); + h = uterm_mode_get_height(m); + + txt->orientation = orientation; + + if (txt->orientation == OR_NORMAL || txt->orientation == OR_UPSIDE_DOWN) { + txt->cols = w / txt->font->attr.width; + txt->rows = h / txt->font->attr.height; + } else { + txt->cols = h / txt->font->attr.width; + txt->rows = w / txt->font->attr.height; + } + + // Free glyph cache, as the glyph are rotated in the cache. + kmscon_rotate_free_tables(tp->glyphs, tp->bold_glyphs); + + return kmscon_rotate_create_tables(&tp->glyphs, &tp->bold_glyphs, free_glyph); +} + static int tp_prepare(struct kmscon_text *txt) { struct tp_pixman *tp = txt->data; @@ -398,8 +391,10 @@ static int tp_draw(struct kmscon_text *txt, const struct tsm_screen_attr *attr) { struct tp_pixman *tp = txt->data; + struct uterm_mode *mode; struct tp_glyph *glyph; int ret; + unsigned int x, y, w, h, sw, sh, cwidth; uint32_t bc; pixman_color_t fc; pixman_image_t *col; @@ -407,6 +402,12 @@ static int tp_draw(struct kmscon_text *txt, if (!width) return 0; + mode = uterm_display_get_current(txt->disp); + if (!mode) + return -EINVAL; + sw = uterm_mode_get_width(mode); + sh = uterm_mode_get_height(mode); + ret = find_glyph(txt, &glyph, id, ch, len, attr); if (ret) return ret; @@ -441,33 +442,48 @@ static int tp_draw(struct kmscon_text *txt, } } + w = glyph->vb.width; + h = glyph->vb.height; + + switch (txt->orientation) { + default: + case OR_NORMAL: + x = posx * FONT_WIDTH(txt); + y = posy * FONT_HEIGHT(txt); + break; + case OR_UPSIDE_DOWN: + cwidth = w / FONT_WIDTH(txt); + x = sw - (posx + cwidth) * FONT_WIDTH(txt); + y = sh - (posy + 1) * FONT_HEIGHT(txt); + break; + case OR_RIGHT: + x = sw - (posy + 1) * FONT_HEIGHT(txt); + y = posx * FONT_WIDTH(txt); + break; + case OR_LEFT: + cwidth = h / FONT_WIDTH(txt); + x = posy * FONT_HEIGHT(txt); + y = sh - (posx + cwidth) * FONT_WIDTH(txt); + break; + } + if (!bc) { pixman_image_composite(PIXMAN_OP_SRC, col, glyph->surf, tp->surf[tp->cur], 0, 0, 0, 0, - posx * txt->font->attr.width, - posy * txt->font->attr.height, - glyph->glyph->buf.width, - txt->font->attr.height); + x, y, w, h); } else { pixman_fill(tp->c_data, tp->c_stride / 4, tp->c_bpp, - posx * txt->font->attr.width, - posy * txt->font->attr.height, - glyph->glyph->buf.width, - txt->font->attr.height, - bc); + x, y, w, h, bc); pixman_image_composite(PIXMAN_OP_OVER, col, glyph->surf, tp->surf[tp->cur], 0, 0, 0, 0, - posx * txt->font->attr.width, - posy * txt->font->attr.height, - glyph->glyph->buf.width, - txt->font->attr.height); + x, y, w, h); } pixman_image_unref(col); @@ -500,6 +516,7 @@ struct kmscon_text_ops kmscon_text_pixman_ops = { .destroy = tp_destroy, .set = tp_set, .unset = tp_unset, + .rotate = tp_rotate, .prepare = tp_prepare, .draw = tp_draw, .render = tp_render, From 3874c95f3d983cb4758a921e216a9f1698692139 Mon Sep 17 00:00:00 2001 From: Jocelyn Falempe Date: Wed, 22 Oct 2025 11:38:15 +0200 Subject: [PATCH 9/9] Revert "Pango: align stride to 4 bytes" This reverts commit 1275f4466b58aead9b90f22800ec6fb13c599fa3. With the rotation support, the pixman renderer uses an intermediate rotate glyph with a 4 byptes alignement, so this is no more needed. Signed-off-by: Jocelyn Falempe --- src/font_pango.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/font_pango.c b/src/font_pango.c index ea1f5323..e90423ad 100644 --- a/src/font_pango.c +++ b/src/font_pango.c @@ -60,8 +60,6 @@ #define LOG_SUBSYSTEM "font_pango" -#define align_up4(x) (((x) + 3) & ~0x3) - struct face { unsigned long ref; struct shl_dlist list; @@ -204,7 +202,7 @@ static int get_glyph(struct face *face, struct kmscon_glyph **out, glyph->width = (logical_rec.x + logical_rec.width > rec.x + face->real_attr.width) ? 2 : cwidth; glyph->buf.width = face->real_attr.width * glyph->width; glyph->buf.height = face->real_attr.height; - glyph->buf.stride = align_up4(glyph->buf.width); + glyph->buf.stride = glyph->buf.width; glyph->buf.format = UTERM_FORMAT_GREY; if (!glyph->buf.width || !glyph->buf.height) {