From 1ddc493a523d3cceb648c0b38bc959421fd311bd Mon Sep 17 00:00:00 2001 From: Torben Hohn Date: Thu, 5 Oct 2023 16:39:21 +0200 Subject: [PATCH 01/15] clients: keyboard: Prepare for half width keys: halve keywidth Prepare to make the keyboard layout more realistic by allowing half width granularity with the keys. Multiply all width realted values by 2 and divide key_width by 2. This commit should not change the appearance of the keyboard. Signed-off-by: Torben Hohn --- clients/keyboard.c | 206 ++++++++++++++++++++++----------------------- 1 file changed, 103 insertions(+), 103 deletions(-) diff --git a/clients/keyboard.c b/clients/keyboard.c index 49b94baae..135f89a0b 100644 --- a/clients/keyboard.c +++ b/clients/keyboard.c @@ -101,126 +101,126 @@ struct layout { }; static const struct key normal_keys[] = { - { keytype_default, "q", "Q", "1", 1}, - { keytype_default, "w", "W", "2", 1}, - { keytype_default, "e", "E", "3", 1}, - { keytype_default, "r", "R", "4", 1}, - { keytype_default, "t", "T", "5", 1}, - { keytype_default, "y", "Y", "6", 1}, - { keytype_default, "u", "U", "7", 1}, - { keytype_default, "i", "I", "8", 1}, - { keytype_default, "o", "O", "9", 1}, - { keytype_default, "p", "P", "0", 1}, - { keytype_backspace, "<--", "<--", "<--", 2}, + { keytype_default, "q", "Q", "1", 2}, + { keytype_default, "w", "W", "2", 2}, + { keytype_default, "e", "E", "3", 2}, + { keytype_default, "r", "R", "4", 2}, + { keytype_default, "t", "T", "5", 2}, + { keytype_default, "y", "Y", "6", 2}, + { keytype_default, "u", "U", "7", 2}, + { keytype_default, "i", "I", "8", 2}, + { keytype_default, "o", "O", "9", 2}, + { keytype_default, "p", "P", "0", 2}, + { keytype_backspace, "<--", "<--", "<--", 4}, - { keytype_tab, "->|", "->|", "->|", 1}, - { keytype_default, "a", "A", "-", 1}, - { keytype_default, "s", "S", "@", 1}, - { keytype_default, "d", "D", "*", 1}, - { keytype_default, "f", "F", "^", 1}, - { keytype_default, "g", "G", ":", 1}, - { keytype_default, "h", "H", ";", 1}, - { keytype_default, "j", "J", "(", 1}, - { keytype_default, "k", "K", ")", 1}, - { keytype_default, "l", "L", "~", 1}, - { keytype_enter, "Enter", "Enter", "Enter", 2}, + { keytype_tab, "->|", "->|", "->|", 2}, + { keytype_default, "a", "A", "-", 2}, + { keytype_default, "s", "S", "@", 2}, + { keytype_default, "d", "D", "*", 2}, + { keytype_default, "f", "F", "^", 2}, + { keytype_default, "g", "G", ":", 2}, + { keytype_default, "h", "H", ";", 2}, + { keytype_default, "j", "J", "(", 2}, + { keytype_default, "k", "K", ")", 2}, + { keytype_default, "l", "L", "~", 2}, + { keytype_enter, "Enter", "Enter", "Enter", 4}, + { keytype_switch, "ABC", "abc", "ABC", 4}, + { keytype_default, "z", "Z", "/", 2}, + { keytype_default, "x", "X", "\'", 2}, + { keytype_default, "c", "C", "\"", 2}, + { keytype_default, "v", "V", "+", 2}, + { keytype_default, "b", "B", "=", 2}, + { keytype_default, "n", "N", "?", 2}, + { keytype_default, "m", "M", "!", 2}, + { keytype_default, ",", ",", "\\", 2}, + { keytype_default, ".", ".", "|", 2}, { keytype_switch, "ABC", "abc", "ABC", 2}, - { keytype_default, "z", "Z", "/", 1}, - { keytype_default, "x", "X", "\'", 1}, - { keytype_default, "c", "C", "\"", 1}, - { keytype_default, "v", "V", "+", 1}, - { keytype_default, "b", "B", "=", 1}, - { keytype_default, "n", "N", "?", 1}, - { keytype_default, "m", "M", "!", 1}, - { keytype_default, ",", ",", "\\", 1}, - { keytype_default, ".", ".", "|", 1}, - { keytype_switch, "ABC", "abc", "ABC", 1}, - { keytype_symbols, "?123", "?123", "abc", 1}, - { keytype_space, "", "", "", 5}, - { keytype_arrow_up, "/\\", "/\\", "/\\", 1}, - { keytype_arrow_left, "<", "<", "<", 1}, - { keytype_arrow_right, ">", ">", ">", 1}, - { keytype_arrow_down, "\\/", "\\/", "\\/", 1}, - { keytype_style, "", "", "", 2} + { keytype_symbols, "?123", "?123", "abc", 2}, + { keytype_space, "", "", "", 10}, + { keytype_arrow_up, "/\\", "/\\", "/\\", 2}, + { keytype_arrow_left, "<", "<", "<", 2}, + { keytype_arrow_right, ">", ">", ">", 2}, + { keytype_arrow_down, "\\/", "\\/", "\\/", 2}, + { keytype_style, "", "", "", 4} }; static const struct key numeric_keys[] = { - { keytype_default, "1", "1", "1", 1}, - { keytype_default, "2", "2", "2", 1}, - { keytype_default, "3", "3", "3", 1}, - { keytype_default, "4", "4", "4", 1}, - { keytype_default, "5", "5", "5", 1}, - { keytype_default, "6", "6", "6", 1}, - { keytype_default, "7", "7", "7", 1}, - { keytype_default, "8", "8", "8", 1}, - { keytype_default, "9", "9", "9", 1}, - { keytype_default, "0", "0", "0", 1}, - { keytype_backspace, "<--", "<--", "<--", 2}, + { keytype_default, "1", "1", "1", 2}, + { keytype_default, "2", "2", "2", 2}, + { keytype_default, "3", "3", "3", 2}, + { keytype_default, "4", "4", "4", 2}, + { keytype_default, "5", "5", "5", 2}, + { keytype_default, "6", "6", "6", 2}, + { keytype_default, "7", "7", "7", 2}, + { keytype_default, "8", "8", "8", 2}, + { keytype_default, "9", "9", "9", 2}, + { keytype_default, "0", "0", "0", 2}, + { keytype_backspace, "<--", "<--", "<--", 4}, - { keytype_space, "", "", "", 4}, - { keytype_enter, "Enter", "Enter", "Enter", 2}, - { keytype_arrow_up, "/\\", "/\\", "/\\", 1}, - { keytype_arrow_left, "<", "<", "<", 1}, - { keytype_arrow_right, ">", ">", ">", 1}, - { keytype_arrow_down, "\\/", "\\/", "\\/", 1}, - { keytype_style, "", "", "", 2} + { keytype_space, "", "", "", 8}, + { keytype_enter, "Enter", "Enter", "Enter", 4}, + { keytype_arrow_up, "/\\", "/\\", "/\\", 2}, + { keytype_arrow_left, "<", "<", "<", 2}, + { keytype_arrow_right, ">", ">", ">", 2}, + { keytype_arrow_down, "\\/", "\\/", "\\/", 2}, + { keytype_style, "", "", "", 4} }; static const struct key arabic_keys[] = { - { keytype_default, "ض", "ﹶ", "۱", 1}, - { keytype_default, "ص", "ﹰ", "۲", 1}, - { keytype_default, "ث", "ﹸ", "۳", 1}, - { keytype_default, "ق", "ﹲ", "۴", 1}, - { keytype_default, "ف", "ﻹ", "۵", 1}, - { keytype_default, "غ", "ﺇ", "۶", 1}, - { keytype_default, "ع", "`", "۷", 1}, - { keytype_default, "ه", "٪", "۸", 1}, - { keytype_default, "خ", ">", "۹", 1}, - { keytype_default, "ح", "<", "۰", 1}, - { keytype_backspace, "-->", "-->", "-->", 2}, + { keytype_default, "ض", "ﹶ", "۱", 2}, + { keytype_default, "ص", "ﹰ", "۲", 2}, + { keytype_default, "ث", "ﹸ", "۳", 2}, + { keytype_default, "ق", "ﹲ", "۴", 2}, + { keytype_default, "ف", "ﻹ", "۵", 2}, + { keytype_default, "غ", "ﺇ", "۶", 2}, + { keytype_default, "ع", "`", "۷", 2}, + { keytype_default, "ه", "٪", "۸", 2}, + { keytype_default, "خ", ">", "۹", 2}, + { keytype_default, "ح", "<", "۰", 2}, + { keytype_backspace, "-->", "-->", "-->", 4}, - { keytype_tab, "->|", "->|", "->|", 1}, - { keytype_default, "ش", "ﹺ", "ﹼ", 1}, - { keytype_default, "س", "ﹴ", "!", 1}, - { keytype_default, "ي", "[", "@", 1}, - { keytype_default, "ب", "]", "#", 1}, - { keytype_default, "ل", "ﻷ", "$", 1}, - { keytype_default, "ا", "أ", "%", 1}, - { keytype_default, "ت", "-", "^", 1}, - { keytype_default, "ن", "x", "&", 1}, - { keytype_default, "م", "/", "*", 1}, - { keytype_default, "ك", ":", "_", 1}, - { keytype_default, "د", "\"", "+", 1}, - { keytype_enter, "Enter", "Enter", "Enter", 2}, + { keytype_tab, "->|", "->|", "->|", 2}, + { keytype_default, "ش", "ﹺ", "ﹼ", 2}, + { keytype_default, "س", "ﹴ", "!", 2}, + { keytype_default, "ي", "[", "@", 2}, + { keytype_default, "ب", "]", "#", 2}, + { keytype_default, "ل", "ﻷ", "$", 2}, + { keytype_default, "ا", "أ", "%", 2}, + { keytype_default, "ت", "-", "^", 2}, + { keytype_default, "ن", "x", "&", 2}, + { keytype_default, "م", "/", "*", 2}, + { keytype_default, "ك", ":", "_", 2}, + { keytype_default, "د", "\"", "+", 2}, + { keytype_enter, "Enter", "Enter", "Enter", 4}, - { keytype_switch, "Shift", "Base", "Shift", 2}, - { keytype_default, "ئ", "~", ")", 1}, - { keytype_default, "ء", "°", "(", 1}, - { keytype_default, "ؤ", "{", "\"", 1}, - { keytype_default, "ر", "}", "\'", 1}, - { keytype_default, "ى", "ﺁ", "؟", 1}, - { keytype_default, "ة", "'", "!", 1}, - { keytype_default, "و", ",", ";", 1}, - { keytype_default, "ﺯ", ".", "\\", 1}, - { keytype_default, "ظ", "؟", "=", 1}, - { keytype_switch, "Shift", "Base", "Shift", 2}, + { keytype_switch, "Shift", "Base", "Shift", 4}, + { keytype_default, "ئ", "~", ")", 2}, + { keytype_default, "ء", "°", "(", 2}, + { keytype_default, "ؤ", "{", "\"", 2}, + { keytype_default, "ر", "}", "\'", 2}, + { keytype_default, "ى", "ﺁ", "؟", 2}, + { keytype_default, "ة", "'", "!", 2}, + { keytype_default, "و", ",", ";", 2}, + { keytype_default, "ﺯ", ".", "\\", 2}, + { keytype_default, "ظ", "؟", "=", 2}, + { keytype_switch, "Shift", "Base", "Shift", 4}, - { keytype_symbols, "؟٣٢١", "؟٣٢١", "Base", 1}, - { keytype_default, "ﻻ", "ﻵ", "|", 1}, - { keytype_default, ",", "،", "،", 1}, - { keytype_space, "", "", "", 6}, - { keytype_default, ".", "ذ", "]", 1}, - { keytype_default, "ط", "ﺝ", "[", 1}, - { keytype_style, "", "", "", 2} + { keytype_symbols, "؟٣٢١", "؟٣٢١", "Base", 2}, + { keytype_default, "ﻻ", "ﻵ", "|", 2}, + { keytype_default, ",", "،", "،", 2}, + { keytype_space, "", "", "", 12}, + { keytype_default, ".", "ذ", "]", 2}, + { keytype_default, "ط", "ﺝ", "[", 2}, + { keytype_style, "", "", "", 4} }; static const struct layout normal_layout = { normal_keys, sizeof(normal_keys) / sizeof(*normal_keys), - 12, + 24, 4, "en", ZWP_TEXT_INPUT_V1_TEXT_DIRECTION_LTR @@ -229,7 +229,7 @@ static const struct layout normal_layout = { static const struct layout numeric_layout = { numeric_keys, sizeof(numeric_keys) / sizeof(*numeric_keys), - 12, + 24, 2, "en", ZWP_TEXT_INPUT_V1_TEXT_DIRECTION_LTR @@ -238,7 +238,7 @@ static const struct layout numeric_layout = { static const struct layout arabic_layout = { arabic_keys, sizeof(arabic_keys) / sizeof(*arabic_keys), - 13, + 26, 4, "ar", ZWP_TEXT_INPUT_V1_TEXT_DIRECTION_RTL @@ -255,7 +255,7 @@ static const char *style_labels[] = { "incorrect" }; -static const double key_width = 60; +static const double key_width = 30; static const double key_height = 50; enum keyboard_state { From f30eb47b040abcf90a1a7846f64da671f3a80270 Mon Sep 17 00:00:00 2001 From: Torben Hohn Date: Fri, 6 Oct 2023 14:02:59 +0200 Subject: [PATCH 02/15] clients: keyboard: Implement keytype_none Allow empty space in the keyboard matrix by adding keytype_none. This is effectively a NOP. Nothing happens when this key is pressed and its not even drawn. Signed-off-by: Torben Hohn --- clients/keyboard.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/clients/keyboard.c b/clients/keyboard.c index 135f89a0b..1f203315c 100644 --- a/clients/keyboard.c +++ b/clients/keyboard.c @@ -76,7 +76,8 @@ enum key_type { keytype_arrow_left, keytype_arrow_right, keytype_arrow_down, - keytype_style + keytype_style, + keytype_none, }; struct key { @@ -291,6 +292,9 @@ label_from_key(struct keyboard *keyboard, if (key->key_type == keytype_style) return style_labels[keyboard->keyboard->preedit_style]; + if (key->key_type == keytype_none) + return ""; + switch(keyboard->state) { case KEYBOARD_STATE_DEFAULT: return key->label; @@ -313,6 +317,9 @@ draw_key(struct keyboard *keyboard, const char *label; cairo_text_extents_t extents; + if (key->key_type == keytype_none) + return; + cairo_save(cr); cairo_rectangle(cr, col * key_width, row * key_height, @@ -660,6 +667,8 @@ keyboard_handle_key(struct keyboard *keyboard, uint32_t time, const struct key * keyboard->keyboard->preedit_style = (keyboard->keyboard->preedit_style + 1) % 8; /* TODO */ virtual_keyboard_send_preedit(keyboard->keyboard, -1); break; + case keytype_none: + break; } } From 26258b348f85bb94d4102cb1c791ba5a26e56166 Mon Sep 17 00:00:00 2001 From: Torben Hohn Date: Fri, 6 Oct 2023 14:08:23 +0200 Subject: [PATCH 03/15] clients: keyboard: Implement config Allow configuring the look of the keyboard. This patch does not make use of the colors. Without configuration nothing should change to the old default look. Signed-off-by: Torben Hohn --- clients/keyboard.c | 57 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/clients/keyboard.c b/clients/keyboard.c index 1f203315c..80f6f58bb 100644 --- a/clients/keyboard.c +++ b/clients/keyboard.c @@ -62,6 +62,8 @@ struct virtual_keyboard { bool toplevel; bool overlay; struct zwp_input_panel_surface_v1 *ips; + + struct weston_config *config; }; enum key_type { @@ -271,6 +273,19 @@ struct keyboard { struct widget *widget; enum keyboard_state state; + uint32_t key_margin; + uint32_t key_radius; + + uint32_t key_fg_color; + uint32_t key_bg_color; + uint32_t key_pressed_fg_color; + uint32_t key_pressed_bg_color; + uint32_t special_key_fg_color; + uint32_t special_key_bg_color; + uint32_t special_key_pressed_fg_color; + uint32_t special_key_pressed_bg_color; + uint32_t panel_fg_color; + uint32_t panel_bg_color; }; static void __attribute__ ((format (printf, 1, 2))) @@ -1052,6 +1067,45 @@ keyboard_destroy(struct virtual_keyboard *virtual_keyboard) free(virtual_keyboard->keyboard); } +static void +config_load(struct virtual_keyboard *virtual_keyboard) +{ + struct weston_config_section *s; + const char *config_file; + config_file = weston_config_get_name_from_env(); + virtual_keyboard->config = weston_config_parse(config_file); + s = weston_config_get_section(virtual_keyboard->config, "keyboard", NULL, NULL); + + weston_config_section_get_uint(s, "key-margin", &virtual_keyboard->keyboard->key_margin, 0); + weston_config_section_get_uint(s, "key-radius", &virtual_keyboard->keyboard->key_radius, 0); + + weston_config_section_get_color(s, "key-fg", + &virtual_keyboard->keyboard->key_fg_color, 0xcc000000); + weston_config_section_get_color(s, "key-bg", + &virtual_keyboard->keyboard->key_bg_color, 0xccffffff); + weston_config_section_get_color(s, "key-pressed-fg", + &virtual_keyboard->keyboard->key_pressed_fg_color, 0xcc000000); + weston_config_section_get_color(s, "key-pressed-bg", + &virtual_keyboard->keyboard->key_pressed_bg_color, 0xccffffff); + weston_config_section_get_color(s, "special-key-fg", + &virtual_keyboard->keyboard->special_key_fg_color, 0xcc000000); + weston_config_section_get_color(s, "special-key-bg", + &virtual_keyboard->keyboard->special_key_bg_color, 0xccffffff); + weston_config_section_get_color(s, "special-key-pressed-fg", + &virtual_keyboard->keyboard->special_key_pressed_fg_color, 0xcc000000); + weston_config_section_get_color(s, "special-key-pressed-bg", + &virtual_keyboard->keyboard->special_key_pressed_bg_color, 0xccffffff); + weston_config_section_get_color(s, "panel-bg", + &virtual_keyboard->keyboard->panel_bg_color, 0xccffffff); + weston_config_section_get_color(s, "panel-fg", + &virtual_keyboard->keyboard->panel_fg_color, 0xcc000000); +} + +static void config_destroy(struct virtual_keyboard *virtual_keyboard) +{ + weston_config_destroy(virtual_keyboard->config); +} + int main(int argc, char *argv[]) { @@ -1076,10 +1130,13 @@ main(int argc, char *argv[]) keyboard_create(&virtual_keyboard); + config_load(&virtual_keyboard); + display_run(virtual_keyboard.display); keyboard_destroy(&virtual_keyboard); display_destroy(virtual_keyboard.display); + config_destroy(&virtual_keyboard); return 0; } From 9375cbbbf97065c6cd8ced9b79d714d7989ba5a1 Mon Sep 17 00:00:00 2001 From: Torben Hohn Date: Fri, 6 Oct 2023 14:17:05 +0200 Subject: [PATCH 04/15] clients: keyboard: increase key_heigth to make the keys square Signed-off-by: Torben Hohn --- clients/keyboard.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clients/keyboard.c b/clients/keyboard.c index 80f6f58bb..d62969fde 100644 --- a/clients/keyboard.c +++ b/clients/keyboard.c @@ -259,7 +259,7 @@ static const char *style_labels[] = { }; static const double key_width = 30; -static const double key_height = 50; +static const double key_height = 60; enum keyboard_state { KEYBOARD_STATE_DEFAULT, From 0ced2d5eb2ac7c31ec9a14b0bc7de97e5bf67337 Mon Sep 17 00:00:00 2001 From: Torben Hohn Date: Fri, 6 Oct 2023 14:19:11 +0200 Subject: [PATCH 05/15] clients: keyboard: Add per key state and color rendering Add 2 helper functions for the rendering update. Signed-off-by: Torben Hohn --- clients/keyboard.c | 71 +++++++++++++++++++++++++++++++++++++++------- 1 file changed, 61 insertions(+), 10 deletions(-) diff --git a/clients/keyboard.c b/clients/keyboard.c index d62969fde..ff21136a2 100644 --- a/clients/keyboard.c +++ b/clients/keyboard.c @@ -247,6 +247,9 @@ static const struct layout arabic_layout = { ZWP_TEXT_INPUT_V1_TEXT_DIRECTION_RTL }; +#define MAX(x, y) (((x) > (y)) ? (x) : (y)) +#define max_count MAX(MAX(sizeof(arabic_keys) / sizeof(*arabic_keys), sizeof(numeric_keys) / sizeof(*numeric_keys) ), sizeof(normal_keys) / sizeof(*normal_keys)) + static const char *style_labels[] = { "default", "none", @@ -273,6 +276,8 @@ struct keyboard { struct widget *widget; enum keyboard_state state; + uint32_t key_state[max_count]; + uint32_t key_margin; uint32_t key_radius; @@ -300,6 +305,16 @@ dbg(const char *fmt, ...) #endif } +static void +set_hex_color(cairo_t *cr, uint32_t color) +{ + cairo_set_source_rgba(cr, + ((color >> 16) & 0xff) / 255.0, + ((color >> 8) & 0xff) / 255.0, + ((color >> 0) & 0xff) / 255.0, + ((color >> 24) & 0xff) / 255.0); +} + static const char * label_from_key(struct keyboard *keyboard, const struct key *key) @@ -322,16 +337,42 @@ label_from_key(struct keyboard *keyboard, return ""; } +static void +rounded_rectangle_path(cairo_t *cr, double x, double y, double width, double height, double radius) +{ + double degrees = M_PI / 180.0; + + cairo_new_sub_path (cr); + cairo_arc (cr, x + width - radius, y + radius, radius, -90 * degrees, 0 * degrees); + cairo_arc (cr, x + width - radius, y + height - radius, radius, 0 * degrees, 90 * degrees); + cairo_arc (cr, x + radius, y + height - radius, radius, 90 * degrees, 180 * degrees); + cairo_arc (cr, x + radius, y + radius, radius, 180 * degrees, 270 * degrees); + cairo_close_path (cr); +} + static void draw_key(struct keyboard *keyboard, const struct key *key, + uint32_t key_state, cairo_t *cr, unsigned int row, unsigned int col) { + const uint32_t margin = keyboard->key_margin; const char *label; cairo_text_extents_t extents; + uint32_t fg_color = keyboard->key_fg_color; + uint32_t bg_color = keyboard->key_bg_color; + + if (key_state == WL_KEYBOARD_KEY_STATE_RELEASED) { + fg_color = (key->key_type == keytype_default ? keyboard->key_fg_color : keyboard->special_key_fg_color); + bg_color = (key->key_type == keytype_default ? keyboard->key_bg_color : keyboard->special_key_bg_color); + } else { + fg_color = (key->key_type == keytype_default ? keyboard->key_pressed_fg_color : keyboard->special_key_pressed_fg_color); + bg_color = (key->key_type == keytype_default ? keyboard->key_pressed_bg_color : keyboard->special_key_pressed_bg_color); + } + if (key->key_type == keytype_none) return; @@ -340,11 +381,18 @@ draw_key(struct keyboard *keyboard, col * key_width, row * key_height, key->width * key_width, key_height); cairo_clip(cr); + rounded_rectangle_path(cr, + col * key_width + margin, row * key_height + margin, + key->width * key_width - 2*margin, key_height - 2*margin, + key_width / 5); + + + /* Paint key background */ + set_hex_color(cr, bg_color); + cairo_fill_preserve(cr); /* Paint frame */ - cairo_rectangle(cr, - col * key_width, row * key_height, - key->width * key_width, key_height); + set_hex_color(cr, fg_color); cairo_set_line_width(cr, 3); cairo_stroke(cr); @@ -405,15 +453,16 @@ redraw_handler(struct widget *widget, void *data) cairo_translate(cr, allocation.x, allocation.y); cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); - cairo_set_source_rgba(cr, 1, 1, 1, 0.75); cairo_rectangle(cr, 0, 0, layout->columns * key_width, layout->rows * key_height); - cairo_paint(cr); + set_hex_color(cr, keyboard->panel_bg_color); + cairo_fill_preserve(cr); + set_hex_color(cr, keyboard->panel_fg_color); + cairo_stroke(cr); cairo_set_operator(cr, CAIRO_OPERATOR_OVER); for (i = 0; i < layout->count; ++i) { - cairo_set_source_rgb(cr, 0, 0, 0); - draw_key(keyboard, &layout->keys[i], cr, row, col); + draw_key(keyboard, &layout->keys[i], keyboard->key_state[i], cr, row, col); col += layout->keys[i].width; if (col >= layout->columns) { row += 1; @@ -557,7 +606,7 @@ append(char *s1, const char *s2) } static void -keyboard_handle_key(struct keyboard *keyboard, uint32_t time, const struct key *key, struct input *input, enum wl_pointer_button_state state) +keyboard_handle_key(struct keyboard *keyboard, uint32_t time, const struct key *key, uint32_t key_num, struct input *input, enum wl_pointer_button_state state) { const char *label = NULL; @@ -576,6 +625,8 @@ keyboard_handle_key(struct keyboard *keyboard, uint32_t time, const struct key * xkb_mod_mask_t mod_mask = keyboard->state == KEYBOARD_STATE_DEFAULT ? 0 : keyboard->keyboard->keysym.shift_mask; uint32_t key_state = (state == WL_POINTER_BUTTON_STATE_PRESSED) ? WL_KEYBOARD_KEY_STATE_PRESSED : WL_KEYBOARD_KEY_STATE_RELEASED; + keyboard->key_state[key_num] = key_state; + switch (key->key_type) { case keytype_default: if (state != WL_POINTER_BUTTON_STATE_PRESSED) @@ -717,7 +768,7 @@ button_handler(struct widget *widget, for (i = 0; i < layout->count; ++i) { col -= layout->keys[i].width; if (col < 0) { - keyboard_handle_key(keyboard, time, &layout->keys[i], input, state); + keyboard_handle_key(keyboard, time, &layout->keys[i], i, input, state); break; } } @@ -748,7 +799,7 @@ touch_handler(struct input *input, uint32_t time, col -= layout->keys[i].width; if (col < 0) { keyboard_handle_key(keyboard, time, - &layout->keys[i], input, state); + &layout->keys[i], i, input, state); break; } } From b1ab447d0d5a7331f802359027b1fe30816258a3 Mon Sep 17 00:00:00 2001 From: Torben Hohn Date: Fri, 6 Oct 2023 14:33:17 +0200 Subject: [PATCH 06/15] Adjust keyboard layout with diagonal keys Signed-off-by: Torben Hohn --- clients/keyboard.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/clients/keyboard.c b/clients/keyboard.c index ff21136a2..c5ce2e124 100644 --- a/clients/keyboard.c +++ b/clients/keyboard.c @@ -104,6 +104,7 @@ struct layout { }; static const struct key normal_keys[] = { + { keytype_none, "", "", "", 2}, { keytype_default, "q", "Q", "1", 2}, { keytype_default, "w", "W", "2", 2}, { keytype_default, "e", "E", "3", 2}, @@ -114,9 +115,9 @@ static const struct key normal_keys[] = { { keytype_default, "i", "I", "8", 2}, { keytype_default, "o", "O", "9", 2}, { keytype_default, "p", "P", "0", 2}, - { keytype_backspace, "<--", "<--", "<--", 4}, + { keytype_backspace, "<--", "<--", "<--", 2}, - { keytype_tab, "->|", "->|", "->|", 2}, + { keytype_tab, "->|", "->|", "->|", 3}, { keytype_default, "a", "A", "-", 2}, { keytype_default, "s", "S", "@", 2}, { keytype_default, "d", "D", "*", 2}, @@ -126,7 +127,7 @@ static const struct key normal_keys[] = { { keytype_default, "j", "J", "(", 2}, { keytype_default, "k", "K", ")", 2}, { keytype_default, "l", "L", "~", 2}, - { keytype_enter, "Enter", "Enter", "Enter", 4}, + { keytype_enter, "Enter", "Enter", "Enter", 3}, { keytype_switch, "ABC", "abc", "ABC", 4}, { keytype_default, "z", "Z", "/", 2}, From d728ee0c1dabec994e01c42a92d0ddb14498bb02 Mon Sep 17 00:00:00 2001 From: Torben Hohn Date: Mon, 9 Oct 2023 08:49:07 +0200 Subject: [PATCH 07/15] keyboard: Hardcode font from bold to normal weight this should also be configurable. Signed-off-by: Torben Hohn --- clients/keyboard.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clients/keyboard.c b/clients/keyboard.c index c5ce2e124..9ed491303 100644 --- a/clients/keyboard.c +++ b/clients/keyboard.c @@ -448,7 +448,7 @@ redraw_handler(struct widget *widget, void *data) cairo_rectangle(cr, allocation.x, allocation.y, allocation.width, allocation.height); cairo_clip(cr); - cairo_select_font_face(cr, "sans-serif", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD); + cairo_select_font_face(cr, "sans-serif", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL); cairo_set_font_size(cr, 16); cairo_translate(cr, allocation.x, allocation.y); From 90e54338d741ffeaca7fca81a72392715af57551 Mon Sep 17 00:00:00 2001 From: "Joscha.Wloch" Date: Fri, 3 May 2024 12:56:33 +0200 Subject: [PATCH 08/15] clients: keyboard: changed numpad layout Changed the numeric keys layout to create an easier to use numpad. Adds Numpad floating point number input with checks that such a number always has at max one decimal point. Signed-off-by: Joscha.Wloch --- clients/keyboard.c | 54 ++++++++++++++++++++++++++++++---------------- 1 file changed, 36 insertions(+), 18 deletions(-) diff --git a/clients/keyboard.c b/clients/keyboard.c index 9ed491303..3290c2994 100644 --- a/clients/keyboard.c +++ b/clients/keyboard.c @@ -151,25 +151,19 @@ static const struct key normal_keys[] = { }; static const struct key numeric_keys[] = { - { keytype_default, "1", "1", "1", 2}, - { keytype_default, "2", "2", "2", 2}, - { keytype_default, "3", "3", "3", 2}, - { keytype_default, "4", "4", "4", 2}, - { keytype_default, "5", "5", "5", 2}, - { keytype_default, "6", "6", "6", 2}, { keytype_default, "7", "7", "7", 2}, { keytype_default, "8", "8", "8", 2}, { keytype_default, "9", "9", "9", 2}, - { keytype_default, "0", "0", "0", 2}, - { keytype_backspace, "<--", "<--", "<--", 4}, - - { keytype_space, "", "", "", 8}, - { keytype_enter, "Enter", "Enter", "Enter", 4}, - { keytype_arrow_up, "/\\", "/\\", "/\\", 2}, - { keytype_arrow_left, "<", "<", "<", 2}, - { keytype_arrow_right, ">", ">", ">", 2}, - { keytype_arrow_down, "\\/", "\\/", "\\/", 2}, - { keytype_style, "", "", "", 4} + { keytype_default, "4", "4", "4", 2}, + { keytype_default, "5", "5", "5", 2}, + { keytype_default, "6", "6", "6", 2}, + { keytype_default, "1", "1", "1", 2}, + { keytype_default, "2", "2", "2", 2}, + { keytype_default, "3", "3", "3", 2}, + { keytype_default, "0", "0", "0", 4}, + { keytype_default,".", ".", ".", 2}, + { keytype_enter, "Enter", "Enter", "Enter", 3}, + { keytype_backspace, "<--", "<--", "<--", 3}, }; static const struct key arabic_keys[] = { @@ -233,8 +227,8 @@ static const struct layout normal_layout = { static const struct layout numeric_layout = { numeric_keys, sizeof(numeric_keys) / sizeof(*numeric_keys), - 24, - 2, + 6, + 5, "en", ZWP_TEXT_INPUT_V1_TEXT_DIRECTION_LTR }; @@ -316,6 +310,19 @@ set_hex_color(cairo_t *cr, uint32_t color) ((color >> 24) & 0xff) / 255.0); } +static bool +contains_decimal_point(const char* str) +{ + for (size_t i = 0; i < strlen(str); ++i) + { + if (str[i] == '.') + { + return true; + } + } + return false; +} + static const char * label_from_key(struct keyboard *keyboard, const struct key *key) @@ -633,6 +640,17 @@ keyboard_handle_key(struct keyboard *keyboard, uint32_t time, const struct key * if (state != WL_POINTER_BUTTON_STATE_PRESSED) break; + // Check if content purpose is number + if ((((keyboard->keyboard->content_purpose == ZWP_TEXT_INPUT_V1_CONTENT_PURPOSE_DIGITS) || + (keyboard->keyboard->content_purpose == ZWP_TEXT_INPUT_V1_CONTENT_PURPOSE_NUMBER)) && + contains_decimal_point(label)) && + (contains_decimal_point(keyboard->keyboard->surrounding_text) || + contains_decimal_point(keyboard->keyboard->preedit_string))) + { + // Do not enter a second decimal point. + return; + } + keyboard->keyboard->preedit_string = append(keyboard->keyboard->preedit_string, label); From 366d11c3ee1f943fe73cff7f62033ac00b96c2d4 Mon Sep 17 00:00:00 2001 From: "Joscha.Wloch" Date: Mon, 13 May 2024 14:07:15 +0200 Subject: [PATCH 09/15] clients: window & keyboard: Be able to set the minimum allocation Function added to be able to set the minimum allocation of the window. Set the minimum allocation of the keyboard window manually. Otherwise, the first call to 'window_schedule_resize' sets 'min_allocation' to the size of the current layout. This means that a layout that requires less space no longer has the chance to use the correct allocation size. Signed-off-by: Joscha.Wloch --- clients/keyboard.c | 5 +++++ clients/window.c | 6 ++++++ clients/window.h | 3 +++ 3 files changed, 14 insertions(+) diff --git a/clients/keyboard.c b/clients/keyboard.c index 3290c2994..0687eebc9 100644 --- a/clients/keyboard.c +++ b/clients/keyboard.c @@ -243,8 +243,12 @@ static const struct layout arabic_layout = { }; #define MAX(x, y) (((x) > (y)) ? (x) : (y)) +#define MIN(x, y) (((x) < (y)) ? (x) : (y)) #define max_count MAX(MAX(sizeof(arabic_keys) / sizeof(*arabic_keys), sizeof(numeric_keys) / sizeof(*numeric_keys) ), sizeof(normal_keys) / sizeof(*normal_keys)) +#define min_columns MIN(MIN(normal_layout.columns, numeric_layout.columns), arabic_layout.columns) +#define min_rows MIN(MIN(normal_layout.rows, numeric_layout.rows), arabic_layout.rows) + static const char *style_labels[] = { "default", "none", @@ -1105,6 +1109,7 @@ keyboard_create(struct virtual_keyboard *virtual_keyboard) window_set_appid(keyboard->window, "org.freedesktop.weston.virtual-keyboard"); window_set_user_data(keyboard->window, keyboard); + window_set_min_allocation(keyboard->window, min_columns * (int32_t)key_width, min_rows * (int32_t)key_height); widget_set_redraw_handler(keyboard->widget, redraw_handler); widget_set_resize_handler(keyboard->widget, resize_handler); diff --git a/clients/window.c b/clients/window.c index 90440c114..d88576856 100644 --- a/clients/window.c +++ b/clients/window.c @@ -4871,6 +4871,12 @@ window_is_resizing(struct window *window) return window->resizing; } +void window_set_min_allocation(struct window *window, int32_t width, int32_t height) +{ + window->min_allocation.width = width; + window->min_allocation.height = height; +} + void window_set_minimized(struct window *window) { diff --git a/clients/window.h b/clients/window.h index 8b3d9d1f3..add6ef601 100644 --- a/clients/window.h +++ b/clients/window.h @@ -481,6 +481,9 @@ window_set_maximized(struct window *window, int maximized); int window_is_resizing(struct window *window); +void +window_set_min_allocation(struct window *window, int32_t width, int32_t height); + void window_set_minimized(struct window *window); From 74a7484ae6bb7045bc7efacc5cb1edc732a27819 Mon Sep 17 00:00:00 2001 From: "Joscha.Wloch" Date: Mon, 13 May 2024 14:32:05 +0200 Subject: [PATCH 10/15] clients: keyboard: Prevent NULL context in 'keyboard_handle_key' It can happen that the input context is exited between a key press event and a key release event, as a result it is set to NULL. If the 'keyboard_handle_key' function is called in this case, this leads to a crash. To prevent this, the context is checked beforehand. Signed-off-by: Joscha.Wloch --- clients/keyboard.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/clients/keyboard.c b/clients/keyboard.c index 0687eebc9..7a267ca49 100644 --- a/clients/keyboard.c +++ b/clients/keyboard.c @@ -620,6 +620,11 @@ append(char *s1, const char *s2) static void keyboard_handle_key(struct keyboard *keyboard, uint32_t time, const struct key *key, uint32_t key_num, struct input *input, enum wl_pointer_button_state state) { + if (keyboard->keyboard->context == NULL) + { + return; + } + const char *label = NULL; switch(keyboard->state) { From e0bfeb4f8faf2296ca09ece599999f241007195f Mon Sep 17 00:00:00 2001 From: "Joscha.Wloch" Date: Mon, 13 May 2024 15:10:00 +0200 Subject: [PATCH 11/15] clients: keyboard: fix resize on layput change If the content type changes, then the layout also changes, which means that the size must be adapted to the new layout and the widget must be redrawn. Signed-off-by: Joscha.Wloch --- clients/keyboard.c | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/clients/keyboard.c b/clients/keyboard.c index 7a267ca49..f1035c223 100644 --- a/clients/keyboard.c +++ b/clients/keyboard.c @@ -894,8 +894,28 @@ handle_content_type(void *data, { struct virtual_keyboard *keyboard = data; - keyboard->content_hint = hint; - keyboard->content_purpose = purpose; + if ((keyboard->content_hint != hint) || (keyboard->content_purpose != purpose)) + { + keyboard->content_hint = hint; + keyboard->content_purpose = purpose; + + // A change of the content type means a new layout. + // So we have to update. + const struct layout *layout = get_current_layout(keyboard); + + zwp_input_method_context_v1_language(context, + keyboard->serial, + layout->language); + zwp_input_method_context_v1_text_direction(context, + keyboard->serial, + layout->text_direction); + + window_schedule_resize(keyboard->keyboard->window, + layout->columns * key_width, + layout->rows * key_height); + + widget_schedule_redraw(keyboard->keyboard->widget); + } } static void From cfcfc15554a544b16a6a15addd8d6890348158db Mon Sep 17 00:00:00 2001 From: "Joscha.Wloch" Date: Tue, 24 Mar 2026 11:47:03 +0100 Subject: [PATCH 12/15] clients: keyboard: fix key remains visually in pressed state If you pressed a key and then moved your finger to another position without lifting it, the key remained visually pressed even after you lifted your finger. Now the ID is stored for each touch event and as soon as a release event returns, the corresponding key is put back into the correct state. Signed-off-by: Joscha.Wloch --- clients/keyboard.c | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/clients/keyboard.c b/clients/keyboard.c index f1035c223..91a3e176a 100644 --- a/clients/keyboard.c +++ b/clients/keyboard.c @@ -276,6 +276,7 @@ struct keyboard { enum keyboard_state state; uint32_t key_state[max_count]; + int32_t key_touch_event_id[max_count]; uint32_t key_margin; uint32_t key_radius; @@ -805,7 +806,7 @@ button_handler(struct widget *widget, } static void -touch_handler(struct input *input, uint32_t time, +touch_handler(struct input *input, uint32_t time, int32_t id, float x, float y, uint32_t state, void *data) { struct keyboard *keyboard = data; @@ -826,6 +827,7 @@ touch_handler(struct input *input, uint32_t time, for (i = 0; i < layout->count; ++i) { col -= layout->keys[i].width; if (col < 0) { + keyboard->key_touch_event_id[i] = id; keyboard_handle_key(keyboard, time, &layout->keys[i], i, input, state); break; @@ -840,7 +842,7 @@ touch_down_handler(struct widget *widget, struct input *input, uint32_t serial, uint32_t time, int32_t id, float x, float y, void *data) { - touch_handler(input, time, x, y, + touch_handler(input, time, id, x, y, WL_POINTER_BUTTON_STATE_PRESSED, data); } @@ -849,11 +851,21 @@ touch_up_handler(struct widget *widget, struct input *input, uint32_t serial, uint32_t time, int32_t id, void *data) { - float x, y; + struct keyboard *keyboard = data; - input_get_touch(input, id, &x, &y); + for (size_t i = 0; i < max_count; ++i) + { + if (id == keyboard->key_touch_event_id[i]) + { + keyboard->key_touch_event_id[i] = -1; + keyboard->key_state[i] = WL_KEYBOARD_KEY_STATE_RELEASED; + } + } - touch_handler(input, time, x, y, + float x, y; + input_get_touch(input, id, &x, &y); + + touch_handler(input, time, id, x, y, WL_POINTER_BUTTON_STATE_RELEASED, data); } From 589f92d883d6ef5c450a7c289c58d7acbc8c8971 Mon Sep 17 00:00:00 2001 From: "Joscha.Wloch" Date: Tue, 24 Mar 2026 11:48:21 +0100 Subject: [PATCH 13/15] clients: keyboard: minor code improvements Signed-off-by: Joscha.Wloch --- clients/keyboard.c | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/clients/keyboard.c b/clients/keyboard.c index 91a3e176a..e4a80183d 100644 --- a/clients/keyboard.c +++ b/clients/keyboard.c @@ -640,7 +640,7 @@ keyboard_handle_key(struct keyboard *keyboard, uint32_t time, const struct key * break; } - xkb_mod_mask_t mod_mask = keyboard->state == KEYBOARD_STATE_DEFAULT ? 0 : keyboard->keyboard->keysym.shift_mask; + xkb_mod_mask_t mod_mask = (keyboard->state == KEYBOARD_STATE_DEFAULT) ? 0 : keyboard->keyboard->keysym.shift_mask; uint32_t key_state = (state == WL_POINTER_BUTTON_STATE_PRESSED) ? WL_KEYBOARD_KEY_STATE_PRESSED : WL_KEYBOARD_KEY_STATE_RELEASED; keyboard->key_state[key_num] = key_state; @@ -696,14 +696,12 @@ keyboard_handle_key(struct keyboard *keyboard, uint32_t time, const struct key * break; switch(keyboard->state) { case KEYBOARD_STATE_DEFAULT: + case KEYBOARD_STATE_SYMBOLS: keyboard->state = KEYBOARD_STATE_UPPERCASE; break; case KEYBOARD_STATE_UPPERCASE: keyboard->state = KEYBOARD_STATE_DEFAULT; break; - case KEYBOARD_STATE_SYMBOLS: - keyboard->state = KEYBOARD_STATE_UPPERCASE; - break; } break; case keytype_symbols: @@ -711,8 +709,6 @@ keyboard_handle_key(struct keyboard *keyboard, uint32_t time, const struct key * break; switch(keyboard->state) { case KEYBOARD_STATE_DEFAULT: - keyboard->state = KEYBOARD_STATE_SYMBOLS; - break; case KEYBOARD_STATE_UPPERCASE: keyboard->state = KEYBOARD_STATE_SYMBOLS; break; @@ -773,6 +769,10 @@ button_handler(struct widget *widget, uint32_t button, enum wl_pointer_button_state state, void *data) { + if (button != BTN_LEFT) { + return; + } + struct keyboard *keyboard = data; struct rectangle allocation; int32_t x, y; @@ -782,10 +782,6 @@ button_handler(struct widget *widget, layout = get_current_layout(keyboard->keyboard); - if (button != BTN_LEFT) { - return; - } - input_get_position(input, &x, &y); widget_get_allocation(keyboard->widget, &allocation); @@ -793,7 +789,7 @@ button_handler(struct widget *widget, y -= allocation.y; row = y / key_height; - col = x / key_width + row * layout->columns; + col = x / (key_width + (row * layout->columns)); for (i = 0; i < layout->count; ++i) { col -= layout->keys[i].width; if (col < 0) { From a3a8cf0cf4a1d3888e2f41877efd3820277a201a Mon Sep 17 00:00:00 2001 From: "Joscha.Wloch" Date: Thu, 6 Jun 2024 13:56:33 +0200 Subject: [PATCH 14/15] clients: keyboard: fix backspace deleting two characters with qt The implementation of the 'zwp_input_method_context_v1_delete_surrounding_text' function in 'qtwayland' interprets the first index parameter as the number of characters to be deleted before the cursor and the second as the number of characters to be deleted after the cursor. In the current implementation of the keyboard, this means that if the cursor is between two characters, both the character before the cursor and the character after the cursor are deleted. As only the character before the cursor is to be deleted, it is sufficient to pass a '0' as the second parameter. Deleting a character at the end of the preedit string now also takes UTF-8 characters into account. Signed-off-by: Joscha.Wloch --- clients/keyboard.c | 38 +++++++++++++++++++++++++++++++++++--- 1 file changed, 35 insertions(+), 3 deletions(-) diff --git a/clients/keyboard.c b/clients/keyboard.c index e4a80183d..a8c26b348 100644 --- a/clients/keyboard.c +++ b/clients/keyboard.c @@ -570,6 +570,22 @@ prev_utf8_char(const char *s, const char *p) return NULL; } +static int utf8_char_size(const char* c) +{ + if (c != NULL) + { + if ((*c & 0x80) == 0) + return 1; + if ((*c & 0xe0) == 0xc0) + return 2; + if ((*c & 0xf0) == 0xe0) + return 3; + if ((*c & 0xf8) == 0xf0) + return 4; + } + return 0; +} + static void delete_before_cursor(struct virtual_keyboard *keyboard) { @@ -590,8 +606,8 @@ delete_before_cursor(struct virtual_keyboard *keyboard) end = keyboard->surrounding_text + keyboard->surrounding_cursor; zwp_input_method_context_v1_delete_surrounding_text(keyboard->context, - start - keyboard->surrounding_text, - end - start); + (start - keyboard->surrounding_text) - keyboard->surrounding_cursor, + 0); zwp_input_method_context_v1_commit_string(keyboard->context, keyboard->serial, ""); @@ -673,7 +689,23 @@ keyboard_handle_key(struct keyboard *keyboard, uint32_t time, const struct key * if (strlen(keyboard->keyboard->preedit_string) == 0) { delete_before_cursor(keyboard->keyboard); } else { - keyboard->keyboard->preedit_string[strlen(keyboard->keyboard->preedit_string) - 1] = '\0'; + const size_t len = strlen(keyboard->keyboard->preedit_string); + const char *prev_utf8 = prev_utf8_char(keyboard->keyboard->preedit_string + (len - MIN(len, 4)), + keyboard->keyboard->preedit_string + strlen(keyboard->keyboard->preedit_string)); + + if (prev_utf8) + { + const int pref_utf8_offset = prev_utf8 - keyboard->keyboard->preedit_string; + if ((strlen(keyboard->keyboard->preedit_string + pref_utf8_offset) - utf8_char_size(prev_utf8)) == 0) + { + keyboard->keyboard->preedit_string[pref_utf8_offset] = '\0'; + } + else + { + keyboard->keyboard->preedit_string[strlen(keyboard->keyboard->preedit_string) - 1] = '\0'; + } + } + virtual_keyboard_send_preedit(keyboard->keyboard, -1); } break; From e37c64bc0d3a013a1522fa9313576bdd89aede8f Mon Sep 17 00:00:00 2001 From: "Joscha.Wloch" Date: Tue, 24 Mar 2026 07:45:47 +0100 Subject: [PATCH 15/15] desktop-shell: Even if input is already mapped recalculate position Otherwise when keyboard layout changes it gets positioned wrong. Signed-off-by: Joscha.Wloch --- desktop-shell/input-panel.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/desktop-shell/input-panel.c b/desktop-shell/input-panel.c index a7645b7a4..89a0f33a4 100644 --- a/desktop-shell/input-panel.c +++ b/desktop-shell/input-panel.c @@ -191,7 +191,15 @@ input_panel_committed(struct weston_surface *surface, return; if (weston_surface_is_mapped(surface)) + { + if (shell->showing_input_panels) + { + struct weston_coord_global pos; + if (!calc_input_panel_position(ip_surface, &pos)) + weston_view_set_position(ip_surface->view, pos); + } return; + } weston_surface_map(surface);