Add per-device keyboard configuration
a30c2cbf6244e2c5b4cebf4c2d109f1b5e3ca143
1 parent
c1910527
config.def.h
+14 -8
| 75 | 75 | { "eDP-1", 2, WL_OUTPUT_TRANSFORM_NORMAL, -1, -1 }, */ |
|
| 76 | 76 | { nullptr, 1.0, WL_OUTPUT_TRANSFORM_NORMAL, -1, -1 }, |
|
| 77 | 77 | /* Keep at least one fallback rule. */ |
|
| 78 | 78 | }; |
|
| 79 | 79 | ||
| 80 | - | /* XKB keyboard layout shared by physical keyboards. */ |
|
| 81 | - | static const struct xkb_rule_names xkb_rules = { |
|
| 82 | - | /* XKB layout name. */ |
|
| 83 | - | .layout = "us", |
|
| 84 | - | /* Layout variant; altgr-intl provides international characters via AltGr. */ |
|
| 85 | - | .variant = nullptr, |
|
| 86 | - | /* Comma-separated XKB options, or nullptr for none. */ |
|
| 87 | - | .options = nullptr, |
|
| 80 | + | /* Keyboard rules match the exact device name. The final rule must have a null |
|
| 81 | + | * name and applies to keyboards without a named rule. */ |
|
| 82 | + | static const keyboard_rule_t keyboardrules[] = { |
|
| 83 | + | { |
|
| 84 | + | .name = nullptr, |
|
| 85 | + | .xkb = { |
|
| 86 | + | /* XKB layout name. */ |
|
| 87 | + | .layout = "us", |
|
| 88 | + | /* Layout variant; altgr-intl provides international characters via AltGr. */ |
|
| 89 | + | .variant = nullptr, |
|
| 90 | + | /* Comma-separated XKB options, or nullptr for none. */ |
|
| 91 | + | .options = nullptr, |
|
| 92 | + | }, |
|
| 93 | + | }, |
|
| 88 | 94 | }; |
|
| 89 | 95 | ||
| 90 | 96 | /* Repeat a held key this many times per second. */ |
|
| 91 | 97 | static const int repeat_rate = 40; |
|
| 92 | 98 | /* Wait this many milliseconds before a held key starts repeating. */ |
swm.c
+119 -58
| 431 | 431 | float scale; |
|
| 432 | 432 | enum wl_output_transform rr; |
|
| 433 | 433 | int x, y; |
|
| 434 | 434 | } monitor_rule_t; |
|
| 435 | 435 | ||
| 436 | + | /* XKB configuration for a named physical keyboard. */ |
|
| 437 | + | typedef struct { |
|
| 438 | + | const char *name; /* Null matches keyboards without a named rule. */ |
|
| 439 | + | struct xkb_rule_names xkb; /* Key map for keyboards that match this rule. */ |
|
| 440 | + | } keyboard_rule_t; |
|
| 441 | + | ||
| 436 | 442 | /* Active pointer constraint. */ |
|
| 437 | 443 | typedef struct { |
|
| 438 | 444 | struct wlr_pointer_constraint_v1 *constraint; |
|
| 439 | 445 | struct wl_listener destroy; |
|
| 440 | 446 | } pointer_constraint_t; |
| 491 | 497 | static void popup_commit(struct wl_listener *listener, void *data); |
|
| 492 | 498 | static void popup_destroy(struct wl_listener *listener, void *data); |
|
| 493 | 499 | static void create_decoration(struct wl_listener *listener, void *data); |
|
| 494 | 500 | static void create_idle_inhibitor(struct wl_listener *listener, void *data); |
|
| 495 | 501 | static void create_keyboard(struct wlr_keyboard *keyboard); |
|
| 496 | - | static keyboard_group_t *create_keyboard_group(struct wlr_virtual_keyboard_v1 *virtual_keyboard); |
|
| 497 | - | static void create_layer_surface(struct wl_listener *listener, void *data); |
|
| 498 | - | static void create_lock_surface(struct wl_listener *listener, void *data); |
|
| 499 | - | static void create_monitor(struct wl_listener *listener, void *data); |
|
| 500 | - | static void create_notify(struct wl_listener *listener, void *data); |
|
| 501 | - | static void create_pointer(struct wlr_pointer *pointer); |
|
| 502 | - | static void create_pointer_constraint(struct wl_listener *listener, void *data); |
|
| 503 | - | static void create_popup(struct wl_listener *listener, void *data); |
|
| 504 | - | static void cursor_constrain(struct wlr_pointer_constraint_v1 *constraint); |
|
| 505 | - | static void cursor_frame(struct wl_listener *listener, void *data); |
|
| 506 | - | static void cursor_warp_to_hint(void); |
|
| 507 | - | static void destroy_decoration(struct wl_listener *listener, void *data); |
|
| 508 | - | static void destroy_drag_icon(struct wl_listener *listener, void *data); |
|
| 509 | - | static void destroy_idle_inhibitor(struct wl_listener *listener, void *data); |
|
| 510 | - | static void layer_surface_destroy_notify(struct wl_listener *listener, void *data); |
|
| 511 | - | static void destroy_lock(session_lock_t *lock, int unlocked); |
|
| 512 | - | static void destroy_lock_surface(struct wl_listener *listener, void *data); |
|
| 513 | - | static void destroy_notify(struct wl_listener *listener, void *data); |
|
| 514 | - | static void destroy_pointer_constraint(struct wl_listener *listener, void *data); |
|
| 515 | - | static void destroy_session_lock(struct wl_listener *listener, void *data); |
|
| 516 | - | static void destroy_keyboard_group(struct wl_listener *listener, void *data); |
|
| 517 | - | static monitor_t *direction_to_monitor(enum wlr_direction dir); |
|
| 518 | - | static void cycle_layout(const arg_t *arg); |
|
| 519 | - | static void cycle_workspace(const arg_t *arg); |
|
| 520 | - | static void focus_client(client_t *c, int lift); |
|
| 521 | - | static client_t *focus_close(client_t *c); |
|
| 522 | - | static void focus_main(const arg_t *arg); |
|
| 523 | - | static void focus_urgent(const arg_t *arg); |
|
| 524 | - | static void ftl_activate_notify(struct wl_listener *listener, void *data); |
|
| 525 | - | static void ftl_capture_request_notify(struct wl_listener *listener, void *data); |
|
| 526 | - | static void ftl_close_notify(struct wl_listener *listener, void *data); |
|
| 527 | - | static void ftl_fullscreen_notify(struct wl_listener *listener, void *data); |
|
| 528 | - | static void ftl_sync(client_t *c); |
|
| 529 | - | static void input_method_commit_notify(struct wl_listener *listener, void *data); |
|
| 530 | - | static void input_method_create_notify(struct wl_listener *listener, void *data); |
|
| 531 | - | static void input_method_destroy_notify(struct wl_listener *listener, void *data); |
|
| 502 | + | static keyboard_group_t *create_keyboard_group( |
|
| 503 | + | struct wlr_virtual_keyboard_v1 *virtual_keyboard, const struct xkb_rule_names *xkb |
|
| 504 | + | ); |
|
| 505 | + | static void create_layer_surface(struct wl_listener *listener, void *data); |
|
| 506 | + | static void create_lock_surface(struct wl_listener *listener, void *data); |
|
| 507 | + | static void create_monitor(struct wl_listener *listener, void *data); |
|
| 508 | + | static void create_notify(struct wl_listener *listener, void *data); |
|
| 509 | + | static void create_pointer(struct wlr_pointer *pointer); |
|
| 510 | + | static void create_pointer_constraint(struct wl_listener *listener, void *data); |
|
| 511 | + | static void create_popup(struct wl_listener *listener, void *data); |
|
| 512 | + | static void cursor_constrain(struct wlr_pointer_constraint_v1 *constraint); |
|
| 513 | + | static void cursor_frame(struct wl_listener *listener, void *data); |
|
| 514 | + | static void cursor_warp_to_hint(void); |
|
| 515 | + | static void destroy_decoration(struct wl_listener *listener, void *data); |
|
| 516 | + | static void destroy_drag_icon(struct wl_listener *listener, void *data); |
|
| 517 | + | static void destroy_idle_inhibitor(struct wl_listener *listener, void *data); |
|
| 518 | + | static void layer_surface_destroy_notify(struct wl_listener *listener, void *data); |
|
| 519 | + | static void destroy_lock(session_lock_t *lock, int unlocked); |
|
| 520 | + | static void destroy_lock_surface(struct wl_listener *listener, void *data); |
|
| 521 | + | static void destroy_notify(struct wl_listener *listener, void *data); |
|
| 522 | + | static void destroy_pointer_constraint(struct wl_listener *listener, void *data); |
|
| 523 | + | static void destroy_session_lock(struct wl_listener *listener, void *data); |
|
| 524 | + | static void destroy_keyboard_group(struct wl_listener *listener, void *data); |
|
| 525 | + | static monitor_t *direction_to_monitor(enum wlr_direction dir); |
|
| 526 | + | static void cycle_layout(const arg_t *arg); |
|
| 527 | + | static void cycle_workspace(const arg_t *arg); |
|
| 528 | + | static void focus_client(client_t *c, int lift); |
|
| 529 | + | static client_t *focus_close(client_t *c); |
|
| 530 | + | static void focus_main(const arg_t *arg); |
|
| 531 | + | static void focus_urgent(const arg_t *arg); |
|
| 532 | + | static void ftl_activate_notify(struct wl_listener *listener, void *data); |
|
| 533 | + | static void ftl_capture_request_notify(struct wl_listener *listener, void *data); |
|
| 534 | + | static void ftl_close_notify(struct wl_listener *listener, void *data); |
|
| 535 | + | static void ftl_fullscreen_notify(struct wl_listener *listener, void *data); |
|
| 536 | + | static void ftl_sync(client_t *c); |
|
| 537 | + | static void input_method_commit_notify(struct wl_listener *listener, void *data); |
|
| 538 | + | static void input_method_create_notify(struct wl_listener *listener, void *data); |
|
| 539 | + | static void input_method_destroy_notify(struct wl_listener *listener, void *data); |
|
| 532 | 540 | static struct wlr_text_input_v3 *input_method_focused_text_input(void); |
|
| 533 | 541 | static void input_method_grab_keyboard(struct wl_listener *listener, void *data); |
|
| 534 | 542 | static void input_method_new_popup(struct wl_listener *listener, void *data); |
|
| 535 | 543 | static void input_method_send_state(struct wlr_text_input_v3 *ti); |
|
| 536 | 544 | static void input_method_set_focus(struct wlr_surface *surface); |
| 837 | 845 | ||
| 838 | 846 | /* Load configuration after declaring the types and state it may use. */ |
|
| 839 | 847 | #include "config.h" |
|
| 840 | 848 | ||
| 841 | 849 | static_assert(WSCOUNT > 0 && WSCOUNT <= 32, "WSCOUNT must be between 1 and 32"); |
|
| 850 | + | static_assert( |
|
| 851 | + | LENGTH(keyboardrules) <= MAX_KEYBOARD_GROUPS, |
|
| 852 | + | "keyboard rules must fit in the keyboard group pool" |
|
| 853 | + | ); |
|
| 842 | 854 | ||
| 843 | - | static workspace_t workspaces[WSCOUNT]; |
|
| 855 | + | static workspace_t workspaces[WSCOUNT]; |
|
| 856 | + | static keyboard_group_t *physical_keyboard_groups[LENGTH(keyboardrules)]; |
|
| 844 | 857 | ||
| 845 | 858 | static pid_t autostart_pids[MAX_AUTOSTART]; |
|
| 846 | 859 | static struct wlr_backend_output_state output_state_pool[MAX_MONITORS]; |
|
| 847 | 860 | ||
| 848 | 861 | /* Fixed-capacity object pools. */ |
| 2188 | 2201 | } |
|
| 2189 | 2202 | for (i = 0; i < LENGTH(signal_sources); i++) |
|
| 2190 | 2203 | wl_event_source_remove(signal_sources[i]); |
|
| 2191 | 2204 | wlr_xcursor_manager_destroy(cursor_mgr); |
|
| 2192 | 2205 | ||
| 2193 | - | destroy_keyboard_group(&kb_group->destroy, nullptr); |
|
| 2206 | + | for (i = 0; i < LENGTH(physical_keyboard_groups); i++) |
|
| 2207 | + | if (physical_keyboard_groups[i]) |
|
| 2208 | + | destroy_keyboard_group(&physical_keyboard_groups[i]->destroy, nullptr); |
|
| 2194 | 2209 | ||
| 2195 | 2210 | /* Destroy this explicitly because wlroots may otherwise retain a pointer |
|
| 2196 | 2211 | * to the seat after the seat is gone. */ |
|
| 2197 | 2212 | wlr_backend_destroy(backend); |
|
| 2198 | 2213 |
| 2497 | 2512 | listen_static(&idle_inhibitor->events.destroy, destroy_idle_inhibitor); |
|
| 2498 | 2513 | ||
| 2499 | 2514 | check_idle_inhibitor(nullptr); |
|
| 2500 | 2515 | } |
|
| 2501 | 2516 | ||
| 2502 | - | /* Add a physical keyboard to the shared keyboard group. */ |
|
| 2517 | + | /* Select the first named keyboard rule or the fallback rule. */ |
|
| 2518 | + | size_t keyboard_rule_index( |
|
| 2519 | + | const keyboard_rule_t *keyboard_rules, size_t rule_count, const char *device_name |
|
| 2520 | + | ) { |
|
| 2521 | + | size_t fallback = rule_count; |
|
| 2522 | + | size_t i; |
|
| 2523 | + | ||
| 2524 | + | for (i = 0; i < rule_count; i++) { |
|
| 2525 | + | if (!keyboard_rules[i].name) { |
|
| 2526 | + | if (fallback == rule_count) |
|
| 2527 | + | fallback = i; |
|
| 2528 | + | } else if (device_name && !strcmp(keyboard_rules[i].name, device_name)) |
|
| 2529 | + | return i; |
|
| 2530 | + | } |
|
| 2531 | + | return fallback; |
|
| 2532 | + | } |
|
| 2533 | + | ||
| 2534 | + | /* Report whether at least one physical keyboard is connected. */ |
|
| 2535 | + | bool has_physical_keyboard(void) { |
|
| 2536 | + | size_t i; |
|
| 2537 | + | ||
| 2538 | + | for (i = 0; i < LENGTH(physical_keyboard_groups); i++) |
|
| 2539 | + | if (physical_keyboard_groups[i] && |
|
| 2540 | + | !wl_list_empty(&physical_keyboard_groups[i]->wlr_group->devices)) |
|
| 2541 | + | return true; |
|
| 2542 | + | return false; |
|
| 2543 | + | } |
|
| 2544 | + | ||
| 2545 | + | /* Add a physical keyboard to the group for its matching rule. */ |
|
| 2503 | 2546 | void create_keyboard(struct wlr_keyboard *keyboard) { |
|
| 2547 | + | size_t index = keyboard_rule_index(keyboardrules, LENGTH(keyboardrules), keyboard->base.name); |
|
| 2548 | + | keyboard_group_t *group; |
|
| 2549 | + | ||
| 2550 | + | if (index == LENGTH(keyboardrules)) |
|
| 2551 | + | die("keyboard rules require a fallback rule"); |
|
| 2552 | + | group = physical_keyboard_groups[index]; |
|
| 2553 | + | ||
| 2504 | 2554 | /* Use the keyboard group's key mapping. */ |
|
| 2505 | - | wlr_keyboard_set_keymap(keyboard, kb_group->wlr_group->keyboard.keymap); |
|
| 2555 | + | wlr_keyboard_set_keymap(keyboard, group->wlr_group->keyboard.keymap); |
|
| 2506 | 2556 | ||
| 2507 | - | /* Add the keyboard to the shared group. */ |
|
| 2508 | - | wlr_keyboard_group_add_keyboard(kb_group->wlr_group, keyboard); |
|
| 2557 | + | /* Combine keyboards that use the same rule. */ |
|
| 2558 | + | wlr_keyboard_group_add_keyboard(group->wlr_group, keyboard); |
|
| 2509 | 2559 | } |
|
| 2510 | 2560 | ||
| 2511 | 2561 | /* Create a keyboard group with the configured key map and repeat settings. */ |
|
| 2512 | - | keyboard_group_t *create_keyboard_group(struct wlr_virtual_keyboard_v1 *virtual_keyboard) { |
|
| 2562 | + | keyboard_group_t *create_keyboard_group( |
|
| 2563 | + | struct wlr_virtual_keyboard_v1 *virtual_keyboard, const struct xkb_rule_names *xkb |
|
| 2564 | + | ) { |
|
| 2513 | 2565 | keyboard_group_t *group = pool_take(&keyboard_group_pool); |
|
| 2514 | 2566 | struct xkb_context *context; |
|
| 2515 | 2567 | struct xkb_keymap *keymap; |
|
| 2516 | 2568 | ||
| 2517 | 2569 | if (!group) |
| 2526 | 2578 | group->virtual_keyboard = virtual_keyboard; |
|
| 2527 | 2579 | ||
| 2528 | 2580 | /* Prepare an XKB keymap and assign it to the keyboard group. */ |
|
| 2529 | 2581 | context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); |
|
| 2530 | 2582 | ||
| 2531 | - | if (!(keymap = xkb_keymap_new_from_names(context, &xkb_rules, XKB_KEYMAP_COMPILE_NO_FLAGS))) |
|
| 2583 | + | if (!(keymap = xkb_keymap_new_from_names(context, xkb, XKB_KEYMAP_COMPILE_NO_FLAGS))) |
|
| 2532 | 2584 | die("failed to compile keymap"); |
|
| 2533 | 2585 | ||
| 2534 | 2586 | wlr_keyboard_set_keymap(&group->wlr_group->keyboard, keymap); |
|
| 2535 | 2587 | xkb_keymap_unref(keymap); |
|
| 2536 | 2588 | xkb_context_unref(context); |
| 2541 | 2593 | LISTEN(&group->wlr_group->keyboard.events.key, &group->key, key_press); |
|
| 2542 | 2594 | LISTEN(&group->wlr_group->keyboard.events.modifiers, &group->modifiers, key_press_modifiers); |
|
| 2543 | 2595 | ||
| 2544 | 2596 | group->key_repeat_source = wl_event_loop_add_timer(event_loop, key_repeat, group); |
|
| 2545 | 2597 | ||
| 2546 | - | /* Wayland exposes one active keyboard per seat, so combine physical |
|
| 2547 | - | * keyboards into one group. */ |
|
| 2548 | - | if (!virtual_keyboard) |
|
| 2549 | - | wlr_seat_set_keyboard(seat, &group->wlr_group->keyboard); |
|
| 2550 | 2598 | return group; |
|
| 2551 | 2599 | } |
|
| 2552 | 2600 | ||
| 2553 | 2601 | /* Choose the layer that contains popups belonging to a layer-shell surface. */ |
|
| 2554 | 2602 | static struct wlr_scene_tree *layer_popup_parent(struct wlr_layer_surface_v1 *layer_surface) { |
| 3098 | 3146 | /* Remove a keyboard group, its timer, and its event listeners. */ |
|
| 3099 | 3147 | void destroy_keyboard_group(struct wl_listener *listener, void *data) { |
|
| 3100 | 3148 | keyboard_group_t *group = wl_container_of(listener, group, destroy); |
|
| 3101 | 3149 | int was_current = wlr_seat_get_keyboard(seat) == &group->wlr_group->keyboard; |
|
| 3102 | 3150 | bool is_main = group == kb_group; |
|
| 3151 | + | size_t i; |
|
| 3103 | 3152 | ||
| 3104 | 3153 | if (group->virtual_keyboard) |
|
| 3105 | 3154 | virtual_keyboards--; |
|
| 3155 | + | else |
|
| 3156 | + | for (i = 0; i < LENGTH(physical_keyboard_groups); i++) |
|
| 3157 | + | if (physical_keyboard_groups[i] == group) |
|
| 3158 | + | physical_keyboard_groups[i] = nullptr; |
|
| 3106 | 3159 | wl_event_source_remove(group->key_repeat_source); |
|
| 3107 | 3160 | wl_list_remove(&group->key.link); |
|
| 3108 | 3161 | wl_list_remove(&group->modifiers.link); |
|
| 3109 | 3162 | wl_list_remove(&group->destroy.link); |
|
| 3110 | 3163 | wlr_keyboard_group_destroy(group->wlr_group); |
| 3117 | 3170 | if (was_current) |
|
| 3118 | 3171 | wlr_seat_set_keyboard(seat, &kb_group->wlr_group->keyboard); |
|
| 3119 | 3172 | wlr_seat_set_capabilities( |
|
| 3120 | 3173 | seat, |
|
| 3121 | 3174 | WL_SEAT_CAPABILITY_POINTER | |
|
| 3122 | - | (!wl_list_empty(&kb_group->wlr_group->devices) || virtual_keyboards |
|
| 3123 | - | ? WL_SEAT_CAPABILITY_KEYBOARD |
|
| 3124 | - | : 0) |
|
| 3175 | + | (has_physical_keyboard() || virtual_keyboards ? WL_SEAT_CAPABILITY_KEYBOARD : 0) |
|
| 3125 | 3176 | ); |
|
| 3126 | 3177 | } |
|
| 3127 | 3178 | ||
| 3128 | 3179 | /* Return the nearest enabled display in the requested direction. */ |
|
| 3129 | 3180 | monitor_t *direction_to_monitor(enum wlr_direction dir) { |
| 3831 | 3882 | /* Advertise the input devices available to applications. swm always has |
|
| 3832 | 3883 | * a cursor, even when no physical pointing device is connected. */ |
|
| 3833 | 3884 | /* TODO: Check whether swm truly needs to always advertise a pointer. */ |
|
| 3834 | 3885 | caps = WL_SEAT_CAPABILITY_POINTER; |
|
| 3835 | 3886 | ||
| 3836 | - | if (!wl_list_empty(&kb_group->wlr_group->devices) || virtual_keyboards) |
|
| 3887 | + | if (has_physical_keyboard() || virtual_keyboards) |
|
| 3837 | 3888 | caps |= WL_SEAT_CAPABILITY_KEYBOARD; |
|
| 3838 | 3889 | wlr_seat_set_capabilities(seat, caps); |
|
| 3839 | 3890 | } |
|
| 3840 | 3891 | ||
| 3841 | 3892 | /* Run the matching window-manager shortcut and report whether it may repeat. */ |
| 6176 | 6227 | ||
| 6177 | 6228 | /* Create the Wayland server, protocols, input state, and rendering resources. */ |
|
| 6178 | 6229 | void setup(void) { |
|
| 6179 | 6230 | static const int signals[] = { SIGCHLD, SIGINT, SIGTERM }; |
|
| 6180 | 6231 | int drm_fd, i; |
|
| 6232 | + | size_t rule_index; |
|
| 6181 | 6233 | ||
| 6182 | 6234 | wlr_log_init(log_level, nullptr); |
|
| 6183 | 6235 | ||
| 6184 | 6236 | /* Create the Wayland server and its event loop. */ |
|
| 6185 | 6237 | dpy = wl_display_create(); |
| 6398 | 6450 | im_mgr = wlr_input_method_manager_v2_create(dpy); |
|
| 6399 | 6451 | listen_global(&im_mgr->events.new_input_method, &new_input_method); |
|
| 6400 | 6452 | ti_mgr = wlr_text_input_manager_v3_create(dpy); |
|
| 6401 | 6453 | listen_global(&ti_mgr->events.new_text_input, &new_text_input); |
|
| 6402 | 6454 | ||
| 6403 | - | kb_group = create_keyboard_group(nullptr); |
|
| 6404 | - | ||
| 6405 | - | if (!kb_group) |
|
| 6406 | - | die("failed to allocate primary keyboard group"); |
|
| 6407 | - | wl_list_init(&kb_group->destroy.link); |
|
| 6455 | + | if (keyboardrules[LENGTH(keyboardrules) - 1].name) |
|
| 6456 | + | die("the last keyboard rule must be a fallback rule"); |
|
| 6457 | + | for (rule_index = 0; rule_index < LENGTH(keyboardrules); rule_index++) { |
|
| 6458 | + | physical_keyboard_groups[rule_index] = |
|
| 6459 | + | create_keyboard_group(nullptr, &keyboardrules[rule_index].xkb); |
|
| 6460 | + | if (!physical_keyboard_groups[rule_index]) |
|
| 6461 | + | die("failed to allocate physical keyboard group"); |
|
| 6462 | + | wl_list_init(&physical_keyboard_groups[rule_index]->destroy.link); |
|
| 6463 | + | } |
|
| 6464 | + | kb_group = physical_keyboard_groups[LENGTH(physical_keyboard_groups) - 1]; |
|
| 6465 | + | /* Wayland exposes one active keyboard per seat. Use the fallback group |
|
| 6466 | + | * until input selects another physical keyboard group. */ |
|
| 6467 | + | wlr_seat_set_keyboard(seat, &kb_group->wlr_group->keyboard); |
|
| 6408 | 6468 | ||
| 6409 | 6469 | output_mgr = wlr_output_manager_v1_create(dpy); |
|
| 6410 | 6470 | listen_global(&output_mgr->events.apply, &output_mgr_apply); |
|
| 6411 | 6471 | listen_global(&output_mgr->events.test, &output_mgr_test); |
|
| 6412 | 6472 |
| 6942 | 7002 | /* Create an independent keyboard group for a virtual keyboard. */ |
|
| 6943 | 7003 | void virtual_keyboard(struct wl_listener *listener, void *data) { |
|
| 6944 | 7004 | struct wlr_virtual_keyboard_v1 *kb = data; |
|
| 6945 | 7005 | ||
| 6946 | 7006 | /* Give each virtual keyboard its own group. */ |
|
| 6947 | - | keyboard_group_t *group = create_keyboard_group(kb); |
|
| 7007 | + | keyboard_group_t *group = |
|
| 7008 | + | create_keyboard_group(kb, &keyboardrules[LENGTH(keyboardrules) - 1].xkb); |
|
| 6948 | 7009 | ||
| 6949 | 7010 | if (!group) { |
|
| 6950 | 7011 | wl_resource_post_no_memory(kb->resource); |
|
| 6951 | 7012 | return; |
|
| 6952 | 7013 | } |
test/unit.c
+13 -0
| 730 | 730 | slot = pool_take(&static_listener_pool); |
|
| 731 | 731 | assert(slot); |
|
| 732 | 732 | listener_release(&slot->listener); |
|
| 733 | 733 | } |
|
| 734 | 734 | ||
| 735 | + | /* Verify exact keyboard rule matching and fallback selection. */ |
|
| 736 | + | static void test_keyboard_rules(void) { |
|
| 737 | + | const keyboard_rule_t keyboard_rules[] = { |
|
| 738 | + | { .name = "Internal Keyboard" }, |
|
| 739 | + | { .name = nullptr }, |
|
| 740 | + | }; |
|
| 741 | + | ||
| 742 | + | assert(keyboard_rule_index(keyboard_rules, LENGTH(keyboard_rules), "Internal Keyboard") == 0); |
|
| 743 | + | assert(keyboard_rule_index(keyboard_rules, LENGTH(keyboard_rules), "USB Keyboard") == 1); |
|
| 744 | + | assert(keyboard_rule_index(keyboard_rules, 1, "USB Keyboard") == 1); |
|
| 745 | + | } |
|
| 746 | + | ||
| 735 | 747 | /* Verify XDG and XWayland client accessors without a running compositor. */ |
|
| 736 | 748 | static void test_client_accessors(void) { |
|
| 737 | 749 | client_t client = {}; |
|
| 738 | 750 | struct wlr_xdg_surface xdg = { .geometry = { 3, 4, 50, 60 } }; |
|
| 739 | 751 | struct wlr_xdg_toplevel toplevel = {}; |
| 1183 | 1195 | { "layer-unmap-monitor", test_layer_unmap_monitor }, |
|
| 1184 | 1196 | { "command-failure-notification", test_command_failure_notification }, |
|
| 1185 | 1197 | { "layer-focus-release", test_layer_focus_release }, |
|
| 1186 | 1198 | { "commands-without-clients", test_commands_without_clients }, |
|
| 1187 | 1199 | { "small-helpers", test_small_helpers }, |
|
| 1200 | + | { "keyboard-rules", test_keyboard_rules }, |
|
| 1188 | 1201 | { "pointer-grab-button", test_pointer_grab_button }, |
|
| 1189 | 1202 | { "client-accessors", test_client_accessors }, |
|
| 1190 | 1203 | { "autostart", test_autostart_execution }, |
|
| 1191 | 1204 | { "pointer-gestures", test_pointer_gestures }, |
|
| 1192 | 1205 | { "x11-urgency", test_x11_urgency }, |