From df167f28396fe68729857cb349c8899deaa0084e Mon Sep 17 00:00:00 2001 From: Cagebreak Signing Key 7 Date: Sun, 26 Sep 2021 21:25:46 +0200 Subject: [PATCH] Release 1.8.0 - Add libinput configuration - Add virtual keyboard and pointer support --- README.md | 21 +- cagebreak.c | 32 +- examples/config | 8 + fuzz/fuzz-lib.c | 40 +-- input.h | 22 ++ input_manager.c | 271 +++++++++++++++ input_manager.h | 111 ++++++ keybinding.c | 43 +++ keybinding.h | 5 + libinput.c | 442 ++++++++++++++++++++++++ man/cagebreak-config.5.md | 75 +++- man/cagebreak.1.md | 5 +- meson.build | 27 +- message.c | 25 +- pango.c | 2 +- parse.c | 310 +++++++++++++++-- parse.h | 6 + seat.c | 143 ++++---- seat.h | 20 +- server.c | 38 ++ server.h | 5 + signatures/1.7.4-cagebreak-config.5.sig | Bin 0 -> 566 bytes signatures/1.7.4-cagebreak.1.sig | Bin 0 -> 566 bytes signatures/1.7.4.sig | Bin 0 -> 566 bytes signatures/cagebreak-config.5.sig | Bin 566 -> 566 bytes signatures/cagebreak.1.sig | Bin 566 -> 566 bytes signatures/cagebreak.sig | Bin 566 -> 566 bytes util.c | 20 ++ util.h | 7 + 29 files changed, 1529 insertions(+), 149 deletions(-) create mode 100644 input.h create mode 100644 input_manager.c create mode 100644 input_manager.h create mode 100644 libinput.c create mode 100644 signatures/1.7.4-cagebreak-config.5.sig create mode 100644 signatures/1.7.4-cagebreak.1.sig create mode 100644 signatures/1.7.4.sig diff --git a/README.md b/README.md index 1e86164..3008d93 100644 --- a/README.md +++ b/README.md @@ -70,7 +70,7 @@ for this to work. #### Man Pages Cagebreak has man pages. To use them, make sure that you have `pandoc` -installed. Then, add `-Dman-pages=true to the `meson` command. +installed. Then, add `-Dman-pages=true` to the `meson` command. ### Running Cagebreak @@ -204,6 +204,21 @@ For every release after 1.0.5, hashes will be provided. For every release after 1.7.0, hashes will be provided for man pages too. +1.8.0 cagebreak + + * sha 256: ac84aa4a08c311da79ca29d92eef4254cb625674b255dbb75621ee6b171a1d79 + * sha 512: c047bb1986fd1d444547e4d64aa7ef9138b3d30327ac3ad9b4c6976e8df6638bae07c39293b5bc9c20e959c0a22b0e40eb2b23b4766d9a5531377ff954b16229 + +1.8.0 cagebreak.1 + + * sha 256: 4de43de7bde67d6d806d206dffc34389f0b5c3ba648bfef673b7bc0adeb29202 + * sha 512: 72aac9a2e9a47f2f85949222d5944dc293332a616e3b961c625fe876172f66638038057c30aa18904d6d969451f468b4fe45e918c0b204cf6952d58ea4c69c33 + +1.8.0 cagebreak-config.5 + + * sha 256: 678aadde06b5ff6bfbd9bc9460423210f9599bcaa0e50c2eae076dc6d6852781 + * sha 512: 72056018fc1c559f26bd7e007b3f1db7d478cec7ec9337620e03a1e3d06910e244a52e8f655ea4f5d73669ffe064bbd23506005d9a6c06764b0f820d7a1df43f + 1.7.4 cagebreak * sha 256: c7442db906239b13f8d09e91cbe01f9e47b2a2c75bbb4b52adefd15dfbfa820f @@ -512,6 +527,10 @@ Adds support for non-build dependencies and an option for builds without pandoc. Improve window ordering. +### Release 1.8.0 + +Adds libinput configuration and virtual keyboard and pointer support. + ## Contributors * Aisha Tammy diff --git a/cagebreak.c b/cagebreak.c index 6e16dc2..0738b67 100644 --- a/cagebreak.c +++ b/cagebreak.c @@ -44,6 +44,7 @@ #endif #include "idle_inhibit_v1.h" +#include "input_manager.h" #include "ipc_server.h" #include "keybinding.h" #include "message.h" @@ -60,6 +61,8 @@ #define WAIT_ANY -1 #endif +bool show_info = false; + void set_sig_handler(int sig, void (*action)(int)) { struct sigaction act; @@ -133,7 +136,8 @@ usage(FILE *file, const char *const cage) { " -D\t Turn on damage tracking debugging\n" #endif " -h\t Display this help message\n" - " -v\t Show the version number and exit\n", + " -v\t Show the version number and exit\n" + " -s\t Show information about the current setup and exit\n", cage); } @@ -141,9 +145,9 @@ static bool parse_args(struct cg_server *server, int argc, char *argv[]) { int c; #ifdef DEBUG - while((c = getopt(argc, argv, "rDhv")) != -1) { + while((c = getopt(argc, argv, "rDhvs")) != -1) { #else - while((c = getopt(argc, argv, "rhv")) != -1) { + while((c = getopt(argc, argv, "rhvs")) != -1) { #endif switch(c) { case 'r': @@ -163,6 +167,9 @@ parse_args(struct cg_server *server, int argc, char *argv[]) { case 'v': fprintf(stdout, "Cagebreak version " CG_VERSION "\n"); exit(0); + case 's': + show_info = true; + break; default: usage(stderr, argv[0]); return false; @@ -264,6 +271,9 @@ main(int argc, char *argv[]) { wlr_log_init(WLR_ERROR, NULL); #endif + wl_list_init(&server.input_config); + wl_list_init(&server.output_config); + server.modes = malloc(4 * sizeof(char *)); if(!server.modes) { wlr_log(WLR_ERROR, "Error allocating mode array"); @@ -328,8 +338,6 @@ main(int argc, char *argv[]) { goto end; } - wl_list_init(&server.output_config); - renderer = wlr_backend_get_renderer(backend); wlr_renderer_init_wl_display(renderer, server.wl_display); @@ -358,6 +366,8 @@ main(int argc, char *argv[]) { goto end; } + server.input = input_manager_create(&server); + data_control_manager = wlr_data_control_manager_v1_create(server.wl_display); if(!data_control_manager) { @@ -523,6 +533,17 @@ main(int argc, char *argv[]) { wlr_xwayland_set_seat(xwayland, server.seat->seat); #endif + if(show_info) { + char *msg = server_show_info(&server); + if(msg != NULL) { + fprintf(stderr, "%s", msg); + free(msg); + } else { + wlr_log(WLR_ERROR, "Failed to get info on cagebreak setup\n"); + } + exit(0); + } + if(ipc_init(&server) != 0) { wlr_log(WLR_ERROR, "Failed to initialize IPC"); ret = 1; @@ -592,6 +613,7 @@ main(int argc, char *argv[]) { wl_display_destroy(server.wl_display); wlr_output_layout_destroy(server.output_layout); + free(server.input); pango_cairo_font_map_set_default(NULL); cairo_debug_reset_static_data(); FcFini(); diff --git a/examples/config b/examples/config index b6a3ca2..0ecf20b 100644 --- a/examples/config +++ b/examples/config @@ -78,3 +78,11 @@ definekey top XF86MonBrightnessUp exec xbacklight -inc 1 output eDP-1 pos 0 0 res 1366x768 rate 60 output eDP-1 disable output eDP-1 enable + +##################### +#Input configuration +##################### + +input 1234:0:Device_Ident click_method clickfinger +input type:pointer scroll_method two_finger +input * calibration_matrix 1 2 3 4 5 6 diff --git a/fuzz/fuzz-lib.c b/fuzz/fuzz-lib.c index cc7b6a4..7f9eb78 100644 --- a/fuzz/fuzz-lib.c +++ b/fuzz/fuzz-lib.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -47,6 +48,7 @@ #endif #include "../idle_inhibit_v1.h" +#include "../input_manager.h" #include "../keybinding.h" #include "../output.h" #include "../parse.h" @@ -132,6 +134,7 @@ LLVMFuzzerInitialize(int *argc, char ***argv) { struct wlr_xdg_decoration_manager_v1 *xdg_decoration_manager = NULL; struct wlr_export_dmabuf_manager_v1 *export_dmabuf_manager = NULL; struct wlr_screencopy_manager_v1 *screencopy_manager = NULL; + struct wlr_data_control_manager_v1 *data_control_manager = NULL; struct wlr_xdg_output_manager_v1 *output_manager = NULL; struct wlr_gamma_control_manager_v1 *gamma_control_manager = NULL; int ret = 0; @@ -146,6 +149,8 @@ LLVMFuzzerInitialize(int *argc, char ***argv) { wlr_log_init(WLR_ERROR, NULL); #endif + wl_list_init(&server.input_config); + /* Wayland requires XDG_RUNTIME_DIR to be set. */ if(!getenv("XDG_RUNTIME_DIR")) { wlr_log(WLR_ERROR, "XDG_RUNTIME_DIR is not set in the environment"); @@ -235,6 +240,16 @@ LLVMFuzzerInitialize(int *argc, char ***argv) { goto end; } + server.input = input_manager_create(&server); + + data_control_manager = + wlr_data_control_manager_v1_create(server.wl_display); + if(!data_control_manager) { + wlr_log(WLR_ERROR, "Unable to create the data control manager"); + ret = 1; + goto end; + } + /* Configure a listener to be notified when new outputs are * available on the backend. We use this only to detect the * first output and ignore subsequent outputs. */ @@ -509,29 +524,14 @@ destroy_input_device(char *line, struct cg_server *server) { --devn; } } else if(strncmp(line, "p", 1) == 0) { - if(wl_list_empty(&server->seat->pointers)) { - return; - } - devn = devn % wl_list_length(&server->seat->pointers); - struct cg_pointer *pointer, *pointer_tmp; - wl_list_for_each_safe(pointer, pointer_tmp, &server->seat->pointers, - link) { - if(devn == 0) { - pointer->destroy.notify(&pointer->destroy, NULL); - break; - } - --devn; - } - } else if(strncmp(line, "t", 1) == 0) { - if(wl_list_empty(&server->seat->touch)) { + if(wl_list_empty(&server->input->devices)) { return; } - devn = devn % wl_list_length(&server->seat->touch); - struct cg_touch *touch, *touch_tmp; - wl_list_for_each_safe(touch, touch_tmp, &server->seat->touch, - link) { + devn = devn % wl_list_length(&server->input->devices); + struct cg_input_device *dev, *dev_tmp; + wl_list_for_each_safe(dev, dev_tmp, &server->input->devices, link) { if(devn == 0) { - touch->destroy.notify(&touch->destroy, NULL); + dev->device_destroy.notify(&dev->device_destroy, NULL); break; } --devn; diff --git a/input.h b/input.h new file mode 100644 index 0000000..7cec2ca --- /dev/null +++ b/input.h @@ -0,0 +1,22 @@ +#ifndef _CG_INPUT_H +#define _CG_INPUT_H + +#include + +struct cg_input_device; +struct cg_input_config; +struct cg_server; + +void +cg_input_configure_libinput_device(struct cg_input_device *device); + +void +cg_input_apply_config(struct cg_input_config *config, struct cg_server *server); + +void +cg_input_reset_libinput_device(struct cg_input_device *device); + +bool +cg_libinput_device_is_builtin(struct cg_input_device *device); + +#endif diff --git a/input_manager.c b/input_manager.c new file mode 100644 index 0000000..10f94fa --- /dev/null +++ b/input_manager.c @@ -0,0 +1,271 @@ +/* + * Cagebreak: A Wayland tiling compositor. + * + * Copyright (C) 2020 The Cagebreak Authors + * Copyright (C) 2018-2020 Jente Hidskes + * + * See the LICENSE file accompanying this file. + */ + +#define _POSIX_C_SOURCE 200812L + +#include "input_manager.h" +#include "config.h" +#include "input.h" +#include "seat.h" +#include "server.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +void +strip_whitespace(char *str) { + static const char whitespace[] = " \f\n\r\t\v"; + size_t len = strlen(str); + size_t start = strspn(str, whitespace); + memmove(str, &str[start], len + 1 - start); + + if(*str) { + for(len -= start + 1; isspace(str[len]); --len) { + } + str[len + 1] = '\0'; + } +} + +char * +input_device_get_identifier(struct wlr_input_device *device) { + int vendor = device->vendor; + int product = device->product; + char *name = strdup(device->name ? device->name : ""); + strip_whitespace(name); + + char *p = name; + for(; *p; ++p) { + // There are in fact input devices with unprintable characters in its + // name + if(*p == ' ' || !isprint(*p)) { + *p = '_'; + } + } + + const char *fmt = "%d:%d:%s"; + int len = snprintf(NULL, 0, fmt, vendor, product, name) + 1; + char *identifier = malloc(len); + if(!identifier) { + wlr_log(WLR_ERROR, "Unable to allocate unique input device name"); + return NULL; + } + + snprintf(identifier, len, fmt, vendor, product, name); + free(name); + return identifier; +} + +void +input_manager_handle_device_destroy(struct wl_listener *listener, void *data) { + struct cg_input_device *input_device = + wl_container_of(listener, input_device, device_destroy); + + if(!input_device) { + wlr_log(WLR_ERROR, "Could not find cagebreak input device to destroy"); + return; + } + + seat_remove_device(input_device->server->seat, input_device); + + wl_list_remove(&input_device->link); + wl_list_remove(&input_device->device_destroy.link); + free(input_device->identifier); + free(input_device); +} + +static void +handle_new_input(struct wl_listener *listener, void *data) { + struct cg_input_manager *input = + wl_container_of(listener, input, new_input); + struct cg_server *server = input->server; + struct wlr_input_device *device = data; + + struct cg_input_device *input_device = + calloc(1, sizeof(struct cg_input_device)); + if(!input_device) { + wlr_log(WLR_ERROR, "Could not allocate input device"); + return; + } + device->data = input_device; + + input_device->wlr_device = device; + input_device->identifier = input_device_get_identifier(device); + input_device->server = server; + input_device->pointer = NULL; + input_device->touch = NULL; + + wl_list_insert(&input->devices, &input_device->link); + + cg_input_configure_libinput_device(input_device); + + wl_signal_add(&device->events.destroy, &input_device->device_destroy); + input_device->device_destroy.notify = input_manager_handle_device_destroy; + + struct cg_seat *seat = server->seat; + seat_add_device(seat, input_device); +} + +void +handle_virtual_keyboard(struct wl_listener *listener, void *data) { + struct cg_input_manager *input_manager = + wl_container_of(listener, input_manager, virtual_keyboard_new); + struct wlr_virtual_keyboard_v1 *keyboard = data; + struct wlr_input_device *device = &keyboard->input_device; + + struct cg_seat *seat = input_manager->server->seat; + + struct cg_input_device *input_device = + calloc(1, sizeof(struct cg_input_device)); + if(!input_device) { + wlr_log(WLR_ERROR, "could not allocate input device"); + return; + } + device->data = input_device; + + input_device->is_virtual = true; + input_device->wlr_device = device; + input_device->identifier = input_device_get_identifier(device); + wl_list_insert(&input_manager->devices, &input_device->link); + input_device->server = input_manager->server; + input_device->pointer = NULL; + input_device->touch = NULL; + + wl_signal_add(&device->events.destroy, &input_device->device_destroy); + input_device->device_destroy.notify = input_manager_handle_device_destroy; + + seat_add_device(seat, input_device); +} + +void +handle_virtual_pointer(struct wl_listener *listener, void *data) { + struct cg_input_manager *input_manager = + wl_container_of(listener, input_manager, virtual_pointer_new); + struct wlr_virtual_pointer_v1_new_pointer_event *event = data; + struct wlr_virtual_pointer_v1 *pointer = event->new_pointer; + struct wlr_input_device *device = &pointer->input_device; + + struct cg_seat *seat = input_manager->server->seat; + + struct cg_input_device *input_device = + calloc(1, sizeof(struct cg_input_device)); + if(!input_device) { + wlr_log(WLR_ERROR, "could not allocate input device"); + return; + } + device->data = input_device; + + input_device->is_virtual = true; + input_device->wlr_device = device; + input_device->identifier = input_device_get_identifier(device); + wl_list_insert(&input_manager->devices, &input_device->link); + input_device->server = input_manager->server; + input_device->pointer = NULL; + input_device->touch = NULL; + + wl_signal_add(&device->events.destroy, &input_device->device_destroy); + input_device->device_destroy.notify = input_manager_handle_device_destroy; + + seat_add_device(seat, input_device); + + if(event->suggested_output) { + wlr_cursor_map_input_to_output(seat->cursor, device, + event->suggested_output); + } +} + +struct cg_input_manager * +input_manager_create(struct cg_server *server) { + struct cg_input_manager *input = calloc(1, sizeof(struct cg_input_manager)); + if(!input) { + return NULL; + } + + wl_list_init(&input->devices); + input->server = server; + + input->new_input.notify = handle_new_input; + wl_signal_add(&server->backend->events.new_input, &input->new_input); + + input->virtual_keyboard = + wlr_virtual_keyboard_manager_v1_create(server->wl_display); + wl_signal_add(&input->virtual_keyboard->events.new_virtual_keyboard, + &input->virtual_keyboard_new); + input->virtual_keyboard_new.notify = handle_virtual_keyboard; + + input->virtual_pointer = + wlr_virtual_pointer_manager_v1_create(server->wl_display); + wl_signal_add(&input->virtual_pointer->events.new_virtual_pointer, + &input->virtual_pointer_new); + input->virtual_pointer_new.notify = handle_virtual_pointer; + + return input; +} + +uint32_t +get_mouse_bindsym(const char *name, char **error) { + // Get event code from name + int code = libevdev_event_code_from_name(EV_KEY, name); + if(code == -1) { + size_t len = snprintf(NULL, 0, "Unknown event %s", name) + 1; + *error = malloc(len); + if(*error) { + snprintf(*error, len, "Unknown event %s", name); + } + return 0; + } + return code; +} + +uint32_t +get_mouse_bindcode(const char *name, char **error) { + // Validate event code + errno = 0; + char *endptr; + int code = strtol(name, &endptr, 10); + if(endptr == name && code <= 0) { + *error = strdup("Button event code must be a positive integer."); + return 0; + } else if(errno == ERANGE) { + *error = strdup("Button event code out of range."); + return 0; + } + const char *event = libevdev_event_code_get_name(EV_KEY, code); + if(!event || strncmp(event, "BTN_", strlen("BTN_")) != 0) { + size_t len = snprintf(NULL, 0, "Event code %d (%s) is not a button", + code, event ? event : "(null)") + + 1; + *error = malloc(len); + if(*error) { + snprintf(*error, len, "Event code %d (%s) is not a button", code, + event ? event : "(null)"); + } + return 0; + } + return code; +} + +uint32_t +input_manager_get_mouse_button(const char *name, char **error) { + uint32_t button = get_mouse_bindsym(name, error); + if(!button && !*error) { + button = get_mouse_bindcode(name, error); + } + return button; +} diff --git a/input_manager.h b/input_manager.h new file mode 100644 index 0000000..6a2cdc3 --- /dev/null +++ b/input_manager.h @@ -0,0 +1,111 @@ +#ifndef CG_INPUT_MANAGER_H +#define CG_INPUT_MANAGER_H + +#include "seat.h" +#include "server.h" +#include +#include + +struct cg_input_manager * +input_manager_create(struct cg_server *server); +struct cg_input_config * +input_device_get_config(struct cg_input_device *device); +void +input_manager_handle_device_destroy(struct wl_listener *listener, void *data); +uint32_t +input_manager_get_mouse_button(const char *name, char **error); + +struct cg_input_manager { + struct wl_list devices; + struct cg_server *server; + + struct wlr_virtual_keyboard_manager_v1 *virtual_keyboard; + struct wlr_virtual_pointer_manager_v1 *virtual_pointer; + + struct wl_listener new_input; + struct wl_listener virtual_keyboard_new; + struct wl_listener virtual_pointer_new; +}; + +struct cg_input_config_mapped_from_region { + double x1, y1; + double x2, y2; + bool mm; +}; + +struct calibration_matrix { + bool configured; + float matrix[6]; +}; + +enum cg_input_config_mapped_to { + MAPPED_TO_DEFAULT, + MAPPED_TO_OUTPUT, + MAPPED_TO_REGION, +}; + +/** + * options for input devices + */ +struct cg_input_config { + char *identifier; + + int accel_profile; + struct calibration_matrix calibration_matrix; + int click_method; + int drag; + int drag_lock; + int dwt; + int left_handed; + int middle_emulation; + int natural_scroll; + float pointer_accel; + float scroll_factor; + /*int repeat_delay; + int repeat_rate;*/ + int scroll_button; + int scroll_method; + int send_events; + int tap; + int tap_button_map; + + /*char *xkb_layout; + char *xkb_model; + char *xkb_options; + char *xkb_rules; + char *xkb_variant; + char *xkb_file; + + bool xkb_file_is_set; + + int xkb_numlock; + int xkb_capslock;*/ + + struct wl_list link; + struct cg_input_config_mapped_from_region *mapped_from_region; + + enum cg_input_config_mapped_to mapped_to; + char *mapped_to_output; + /*struct wlr_box *mapped_to_region;*/ + + /*struct wl_list tools;*/ + + bool capturable; + struct wlr_box region; +}; + +struct cg_input_device { + char *identifier; + struct cg_server *server; + struct wlr_input_device *wlr_device; + struct wl_list link; + struct wl_listener device_destroy; + bool is_virtual; + + /* Only one of the following is non-NULL depending on the type of the input + * device */ + struct cg_pointer *pointer; + struct cg_touch *touch; +}; + +#endif diff --git a/keybinding.c b/keybinding.c index 005b87a..5d802fe 100644 --- a/keybinding.c +++ b/keybinding.c @@ -9,6 +9,8 @@ #include #include +#include "input.h" +#include "input_manager.h" #include "keybinding.h" #include "message.h" #include "output.h" @@ -63,6 +65,16 @@ keybinding_free(struct keybinding *keybinding, bool recursive) { case KEYBINDING_CONFIGURE_OUTPUT: free(keybinding->data.o_cfg->output_name); free(keybinding->data.o_cfg); + break; + case KEYBINDING_CONFIGURE_INPUT: + free(keybinding->data.i_cfg->identifier); + if(keybinding->data.i_cfg->mapped_from_region) { + free(keybinding->data.i_cfg->mapped_from_region); + } + if(keybinding->data.i_cfg->mapped_to_output) { + free(keybinding->data.i_cfg->mapped_to_output); + } + free(keybinding->data.o_cfg); default: break; } @@ -103,6 +115,9 @@ keybinding_list_init() { void keybinding_list_free(struct keybinding_list *list) { + if(!list) { + return; + } for(unsigned int i = 0; i < list->length; ++i) { keybinding_free(list->keybindings[i], true); } @@ -689,6 +704,18 @@ keybinding_show_time(struct cg_server *server) { free(msg); } +void +keybinding_show_info(struct cg_server *server) { + char *msg = server_show_info(server); + + if(!msg) { + return; + } + + message_printf(server->curr_output, "%s", msg); + free(msg); +} + void keybinding_move_view_to_next_output(struct cg_server *server) { if(wl_list_length(&server->outputs) <= 1) { @@ -870,6 +897,16 @@ keybinding_configure_output(struct cg_server *server, } } +void +keybinding_configure_input(struct cg_server *server, + struct cg_input_config *cfg) { + cg_input_apply_config(cfg, server); +} + +void +keybinding_configure_input_dev(struct cg_server *server, + struct cg_input_config *cfg) {} + /* Hint: see keybinding.h for details on "data" */ int run_action(enum keybinding_action action, struct cg_server *server, @@ -918,6 +955,9 @@ run_action(enum keybinding_action action, struct cg_server *server, case KEYBINDING_SHOW_TIME: keybinding_show_time(server); break; + case KEYBINDING_SHOW_INFO: + keybinding_show_info(server); + break; case KEYBINDING_RESIZE_TILE_HORIZONTAL: resize_tile(server, data.i, 0); break; @@ -995,6 +1035,9 @@ run_action(enum keybinding_action action, struct cg_server *server, case KEYBINDING_CONFIGURE_OUTPUT: keybinding_configure_output(server, data.o_cfg); break; + case KEYBINDING_CONFIGURE_INPUT: + keybinding_configure_input(server, data.i_cfg); + break; case KEYBINDING_CLOSE_VIEW: keybinding_close_view( server->curr_output->workspaces[server->curr_output->curr_workspace] diff --git a/keybinding.h b/keybinding.h index 705a4ab..4cd0e87 100644 --- a/keybinding.h +++ b/keybinding.h @@ -4,6 +4,7 @@ #include "config.h" +#include #include #include @@ -24,6 +25,8 @@ enum keybinding_action { KEYBINDING_CYCLE_OUTPUT, // data.b is 0 if forward, 1 if reverse KEYBINDING_CONFIGURE_OUTPUT, // data.o_cfg is the desired output // configuration + KEYBINDING_CONFIGURE_INPUT, // data.i_cfg is the desired input + // configuration KEYBINDING_QUIT, KEYBINDING_NOOP, KEYBINDING_SWITCH_WORKSPACE, // data.u is the desired workspace @@ -36,6 +39,7 @@ enum keybinding_action { KEYBINDING_MOVE_VIEW_TO_WORKSPACE, // data.u is the desired workspace KEYBINDING_MOVE_VIEW_TO_NEXT_OUTPUT, KEYBINDING_SHOW_TIME, + KEYBINDING_SHOW_INFO, KEYBINDING_SWAP_LEFT, KEYBINDING_SWAP_RIGHT, @@ -61,6 +65,7 @@ union keybinding_params { float color[3]; struct keybinding *kb; struct cg_output_config *o_cfg; + struct cg_input_config *i_cfg; }; struct keybinding { diff --git a/libinput.c b/libinput.c new file mode 100644 index 0000000..f6dd957 --- /dev/null +++ b/libinput.c @@ -0,0 +1,442 @@ +#include "input.h" +#include "input_manager.h" +#include "output.h" +#include +#include +#include +#include +#include +#include +#include + +static void +log_status(enum libinput_config_status status) { + if(status != LIBINPUT_CONFIG_STATUS_SUCCESS) { + wlr_log(WLR_ERROR, "Failed to apply libinput config: %s", + libinput_config_status_to_str(status)); + } +} + +static bool +set_send_events(struct libinput_device *device, uint32_t mode) { + if(libinput_device_config_send_events_get_mode(device) == mode) { + return false; + } + wlr_log(WLR_DEBUG, "send_events_set_mode(%" PRIu32 ")", mode); + log_status(libinput_device_config_send_events_set_mode(device, mode)); + return true; +} + +static bool +set_tap(struct libinput_device *device, enum libinput_config_tap_state tap) { + if(libinput_device_config_tap_get_finger_count(device) <= 0 || + libinput_device_config_tap_get_enabled(device) == tap) { + return false; + } + wlr_log(WLR_DEBUG, "tap_set_enabled(%d)", tap); + log_status(libinput_device_config_tap_set_enabled(device, tap)); + return true; +} + +static bool +set_tap_button_map(struct libinput_device *device, + enum libinput_config_tap_button_map map) { + if(libinput_device_config_tap_get_finger_count(device) <= 0 || + libinput_device_config_tap_get_button_map(device) == map) { + return false; + } + wlr_log(WLR_DEBUG, "tap_set_button_map(%d)", map); + log_status(libinput_device_config_tap_set_button_map(device, map)); + return true; +} + +static bool +set_tap_drag(struct libinput_device *device, + enum libinput_config_drag_state drag) { + if(libinput_device_config_tap_get_finger_count(device) <= 0 || + libinput_device_config_tap_get_drag_enabled(device) == drag) { + return false; + } + wlr_log(WLR_DEBUG, "tap_set_drag_enabled(%d)", drag); + log_status(libinput_device_config_tap_set_drag_enabled(device, drag)); + return true; +} + +static bool +set_tap_drag_lock(struct libinput_device *device, + enum libinput_config_drag_lock_state lock) { + if(libinput_device_config_tap_get_finger_count(device) <= 0 || + libinput_device_config_tap_get_drag_lock_enabled(device) == lock) { + return false; + } + wlr_log(WLR_DEBUG, "tap_set_drag_lock_enabled(%d)", lock); + log_status(libinput_device_config_tap_set_drag_lock_enabled(device, lock)); + return true; +} + +static bool +set_accel_speed(struct libinput_device *device, double speed) { + if(!libinput_device_config_accel_is_available(device) || + libinput_device_config_accel_get_speed(device) == speed) { + return false; + } + wlr_log(WLR_DEBUG, "accel_set_speed(%f)", speed); + log_status(libinput_device_config_accel_set_speed(device, speed)); + return true; +} + +static bool +set_accel_profile(struct libinput_device *device, + enum libinput_config_accel_profile profile) { + if(!libinput_device_config_accel_is_available(device) || + libinput_device_config_accel_get_profile(device) == profile) { + return false; + } + wlr_log(WLR_DEBUG, "accel_set_profile(%d)", profile); + log_status(libinput_device_config_accel_set_profile(device, profile)); + return true; +} + +static bool +set_natural_scroll(struct libinput_device *d, bool n) { + if(!libinput_device_config_scroll_has_natural_scroll(d) || + libinput_device_config_scroll_get_natural_scroll_enabled(d) == n) { + return false; + } + wlr_log(WLR_DEBUG, "scroll_set_natural_scroll(%d)", n); + log_status(libinput_device_config_scroll_set_natural_scroll_enabled(d, n)); + return true; +} + +static bool +set_left_handed(struct libinput_device *device, bool left) { + if(!libinput_device_config_left_handed_is_available(device) || + libinput_device_config_left_handed_get(device) == left) { + return false; + } + wlr_log(WLR_DEBUG, "left_handed_set(%d)", left); + log_status(libinput_device_config_left_handed_set(device, left)); + return true; +} + +static bool +set_click_method(struct libinput_device *device, + enum libinput_config_click_method method) { + uint32_t click = libinput_device_config_click_get_methods(device); + if((click & ~LIBINPUT_CONFIG_CLICK_METHOD_NONE) == 0 || + libinput_device_config_click_get_method(device) == method) { + return false; + } + wlr_log(WLR_DEBUG, "click_set_method(%d)", method); + log_status(libinput_device_config_click_set_method(device, method)); + return true; +} + +static bool +set_middle_emulation(struct libinput_device *dev, + enum libinput_config_middle_emulation_state mid) { + if(!libinput_device_config_middle_emulation_is_available(dev) || + libinput_device_config_middle_emulation_get_enabled(dev) == mid) { + return false; + } + wlr_log(WLR_DEBUG, "middle_emulation_set_enabled(%d)", mid); + log_status(libinput_device_config_middle_emulation_set_enabled(dev, mid)); + return true; +} + +static bool +set_scroll_method(struct libinput_device *device, + enum libinput_config_scroll_method method) { + uint32_t scroll = libinput_device_config_scroll_get_methods(device); + if((scroll & ~LIBINPUT_CONFIG_SCROLL_NO_SCROLL) == 0 || + libinput_device_config_scroll_get_method(device) == method) { + return false; + } + wlr_log(WLR_DEBUG, "scroll_set_method(%d)", method); + log_status(libinput_device_config_scroll_set_method(device, method)); + return true; +} + +static bool +set_scroll_button(struct libinput_device *dev, uint32_t button) { + uint32_t scroll = libinput_device_config_scroll_get_methods(dev); + if((scroll & ~LIBINPUT_CONFIG_SCROLL_NO_SCROLL) == 0 || + libinput_device_config_scroll_get_button(dev) == button) { + return false; + } + wlr_log(WLR_DEBUG, "scroll_set_button(%" PRIu32 ")", button); + log_status(libinput_device_config_scroll_set_button(dev, button)); + return true; +} + +static bool +set_dwt(struct libinput_device *device, bool dwt) { + if(!libinput_device_config_dwt_is_available(device) || + libinput_device_config_dwt_get_enabled(device) == dwt) { + return false; + } + wlr_log(WLR_DEBUG, "dwt_set_enabled(%d)", dwt); + log_status(libinput_device_config_dwt_set_enabled(device, dwt)); + return true; +} + +static bool +set_calibration_matrix(struct libinput_device *dev, float mat[6]) { + if(!libinput_device_config_calibration_has_matrix(dev)) { + return false; + } + bool changed = false; + float current[6]; + libinput_device_config_calibration_get_matrix(dev, current); + for(int i = 0; i < 6; i++) { + if(current[i] != mat[i]) { + changed = true; + break; + } + } + if(changed) { + wlr_log(WLR_DEBUG, "calibration_set_matrix(%f, %f, %f, %f, %f, %f)", + mat[0], mat[1], mat[2], mat[3], mat[4], mat[5]); + log_status(libinput_device_config_calibration_set_matrix(dev, mat)); + } + return changed; +} + +void +output_get_identifier(char *identifier, size_t len, struct cg_output *output) { + struct wlr_output *wlr_output = output->wlr_output; + snprintf(identifier, len, "%s %s %s", wlr_output->make, wlr_output->model, + wlr_output->serial); +} + +struct cg_output * +output_by_name_or_id(const char *name_or_id, struct cg_server *server) { + struct cg_output *output = NULL; + wl_list_for_each(output, &server->outputs, link) { + char identifier[128]; + output_get_identifier(identifier, sizeof(identifier), output); + if(strcasecmp(identifier, name_or_id) == 0 || + strcasecmp(output->wlr_output->name, name_or_id) == 0) { + return output; + } + } + return NULL; +} + +static bool +device_is_touchpad(struct cg_input_device *device) { + if(device->wlr_device->type != WLR_INPUT_DEVICE_POINTER || + !wlr_input_device_is_libinput(device->wlr_device)) { + return false; + } + + struct libinput_device *libinput_device = + wlr_libinput_get_device_handle(device->wlr_device); + + return libinput_device_config_tap_get_finger_count(libinput_device) > 0; +} + +const char * +input_device_get_type(struct cg_input_device *device) { + switch(device->wlr_device->type) { + case WLR_INPUT_DEVICE_POINTER: + if(device_is_touchpad(device)) { + return "touchpad"; + } else { + return "pointer"; + } + case WLR_INPUT_DEVICE_KEYBOARD: + return "keyboard"; + case WLR_INPUT_DEVICE_TOUCH: + return "touch"; + case WLR_INPUT_DEVICE_TABLET_TOOL: + return "tablet_tool"; + case WLR_INPUT_DEVICE_TABLET_PAD: + return "tablet_pad"; + case WLR_INPUT_DEVICE_SWITCH: + return "switch"; + } + return "unknown"; +} + +void +apply_config_to_device(struct cg_input_config *config, + struct cg_input_device *input_device) { + if(!wlr_input_device_is_libinput(input_device->wlr_device)) { + return; + } + + struct libinput_device *device = + wlr_libinput_get_device_handle(input_device->wlr_device); + if(config->mapped_to_output && + !output_by_name_or_id(config->mapped_to_output, input_device->server)) { + wlr_log(WLR_DEBUG, + "'%s' is mapped to offline output '%s'; disabling input", + config->identifier, config->mapped_to_output); + set_send_events(device, LIBINPUT_CONFIG_SEND_EVENTS_DISABLED); + } else if(config->send_events != INT_MIN) { + set_send_events(device, config->send_events); + } else { + // Have to reset to the default mode here, otherwise if ic->send_events + // is unset and a mapped output just came online after being disabled, + // we'd remain stuck sending no events. + set_send_events( + device, + libinput_device_config_send_events_get_default_mode(device)); + } + + if(config->tap != INT_MIN) { + set_tap(device, config->tap); + } + if(config->tap_button_map != INT_MIN) { + set_tap_button_map(device, config->tap_button_map); + } + if(config->drag != INT_MIN) { + set_tap_drag(device, config->drag); + } + if(config->drag_lock != INT_MIN) { + set_tap_drag_lock(device, config->drag_lock); + } + if(config->pointer_accel != FLT_MIN) { + set_accel_speed(device, config->pointer_accel); + } + if(config->accel_profile != INT_MIN) { + set_accel_profile(device, config->accel_profile); + } + if(config->natural_scroll != INT_MIN) { + set_natural_scroll(device, config->natural_scroll); + } + if(config->left_handed != INT_MIN) { + set_left_handed(device, config->left_handed); + } + if(config->click_method != INT_MIN) { + set_click_method(device, config->click_method); + } + if(config->middle_emulation != INT_MIN) { + set_middle_emulation(device, config->middle_emulation); + } + if(config->scroll_method != INT_MIN) { + set_scroll_method(device, config->scroll_method); + } + if(config->scroll_button != INT_MIN) { + set_scroll_button(device, config->scroll_button); + } + if(config->dwt != INT_MIN) { + set_dwt(device, config->dwt); + } + if(config->calibration_matrix.configured) { + set_calibration_matrix(device, config->calibration_matrix.matrix); + } +} + +void +cg_input_apply_config(struct cg_input_config *config, + struct cg_server *server) { + struct cg_input_device *device = NULL; + wl_list_for_each(device, &server->input->devices, link) { + if(strcmp(config->identifier, device->identifier) != 0 && + strcmp(config->identifier, "*") != 0) { + continue; + } + + const char *device_type = input_device_get_type(device); + if(strncmp(config->identifier, "type:", 5) == 0 && + strcmp(config->identifier + 5, device_type) != 0) { + continue; + } + apply_config_to_device(config, device); + } +} + +void +cg_input_configure_libinput_device(struct cg_input_device *input_device) { + struct cg_server *server = input_device->server; + struct cg_input_config *config = NULL; + + wl_list_for_each(config, &server->input_config, link) { + if(strcmp(config->identifier, input_device->identifier) != 0 && + strcmp(config->identifier, "*") != 0) { + continue; + } + + const char *device_type = input_device_get_type(input_device); + if(!(strncmp(config->identifier, "type:", 5) && + strcmp(config->identifier + 5, device_type) == 0)) { + continue; + } + apply_config_to_device(config, input_device); + } +} + +void +cg_input_reset_libinput_device(struct cg_input_device *input_device) { + if(!wlr_input_device_is_libinput(input_device->wlr_device)) { + return; + } + + struct libinput_device *device = + wlr_libinput_get_device_handle(input_device->wlr_device); + wlr_log(WLR_DEBUG, "cg_input_reset_libinput_device(%s)", + input_device->identifier); + bool changed = false; + + changed |= set_send_events( + device, libinput_device_config_send_events_get_default_mode(device)); + changed |= + set_tap(device, libinput_device_config_tap_get_default_enabled(device)); + changed |= set_tap_button_map( + device, libinput_device_config_tap_get_default_button_map(device)); + changed |= set_tap_drag( + device, libinput_device_config_tap_get_default_drag_enabled(device)); + changed |= set_tap_drag_lock( + device, + libinput_device_config_tap_get_default_drag_lock_enabled(device)); + changed |= set_accel_speed( + device, libinput_device_config_accel_get_default_speed(device)); + changed |= set_accel_profile( + device, libinput_device_config_accel_get_default_profile(device)); + changed |= set_natural_scroll( + device, + libinput_device_config_scroll_get_default_natural_scroll_enabled( + device)); + changed |= set_left_handed( + device, libinput_device_config_left_handed_get_default(device)); + changed |= set_click_method( + device, libinput_device_config_click_get_default_method(device)); + changed |= set_middle_emulation( + device, + libinput_device_config_middle_emulation_get_default_enabled(device)); + changed |= set_scroll_method( + device, libinput_device_config_scroll_get_default_method(device)); + changed |= set_scroll_button( + device, libinput_device_config_scroll_get_default_button(device)); + changed |= + set_dwt(device, libinput_device_config_dwt_get_default_enabled(device)); + + float matrix[6]; + libinput_device_config_calibration_get_default_matrix(device, matrix); + changed |= set_calibration_matrix(device, matrix); +} + +bool +cg_libinput_device_is_builtin(struct cg_input_device *cg_device) { + if(!wlr_input_device_is_libinput(cg_device->wlr_device)) { + return false; + } + + struct libinput_device *device = + wlr_libinput_get_device_handle(cg_device->wlr_device); + struct udev_device *udev_device = libinput_device_get_udev_device(device); + if(!udev_device) { + return false; + } + + const char *id_path = + udev_device_get_property_value(udev_device, "ID_PATH"); + if(!id_path) { + return false; + } + + const char prefix[] = "platform-"; + return strncmp(id_path, prefix, strlen(prefix)) == 0; +} diff --git a/man/cagebreak-config.5.md b/man/cagebreak-config.5.md index a4a1049..321d6bf 100644 --- a/man/cagebreak-config.5.md +++ b/man/cagebreak-config.5.md @@ -1,4 +1,4 @@ -cagebreak-config(1) "Version 1.7.4" "Cagebreak Manual" +cagebreak-config(1) "Version 1.8.0" "Cagebreak Manual" # NAME @@ -93,6 +93,75 @@ by prepending a line with the # symbol. *hsplit* Split current tile horizontally +*input * + Set the setting "" to "" for device "". The identifier can either be "\*" (wildcard), of the form "type:" or the identifier of the device as printed for example by *cagebreak -s*. The supported input types are + - touchpad + - pointer + - keyboard + - touch + - tablet_tool + - tablet_pad + - switch + + Configurations are applied sequentially. Currently, only libinput devices may be configured. The available settings and their corresponding values are as follows: + + *accel_profile adaptive|flat* + Sets the pointer acceleration profile for the specified input device. + + *calibration_matrix <6 space-separated floating point values>* + Sets the calibration matrix. + + *click_method none|button_areas|clickfinger* + Changes the click method for the specified device. + + *drag enabled|disabled* + Enables or disables tap-and-drag for specified input device. + + *drag_lock enabled|disabled* + Enables or disables drag lock for specified input device. + + *dwt enabled|disabled* + Enables or disables disable-while-typing for the specified input device. + + *enabled|disabled|disabled_on_external_mouse* + Enables or disables send_events for specified input device. Disabling + send_events disables the input device. + + *left_handed enabled|disabled* + Enables or disables left handed mode for specified input device. + + *middle_emulation enabled|disabled* + Enables or disables middle click emulation. + + *natural_scroll enabled|disabled* + Enables or disables natural (inverted) scrolling for the specified input + device. + + *pointer_accel [<-1|1>]* + Changes the pointer acceleration for the specified input device. + + *scroll_button disable|* + Sets the button used for scroll_method on_button_down. The button can + be given as an event name or code, which can be obtained from *libinput + debug-events*. If set to + _disable_, it disables the scroll_method on_button_down. + + *scroll_factor * + Changes the scroll factor for the specified input device. Scroll speed will + be scaled by the given value, which must be non-negative. + + *scroll_method none|two_finger|edge|on_button_down* + Changes the scroll method for the specified input device. + + *tap enabled|disabled* + Enables or disables tap for specified input device. + + *tap_button_map lrm|lmr* + Specifies which button mapping to use for tapping. _lrm_ treats 1 finger as + left click, 2 fingers as right click, and 3 fingers as middle click. _lmr_ + treats 1 finger as left click, 2 fingers as middle click, and 3 fingers as + right click. + *mode * Enter mode "". After a keybinding is processed, return to default mode @@ -140,6 +209,10 @@ by prepending a line with the # symbol. *resizeup* Resize the current tile towards the top +*show_info* + Display info about the current setup. In particular, print the identifiers + of the available inputs and outputs. + *setmode * Set the default mode to diff --git a/man/cagebreak.1.md b/man/cagebreak.1.md index aa66b7e..309be7c 100644 --- a/man/cagebreak.1.md +++ b/man/cagebreak.1.md @@ -1,4 +1,4 @@ -cagebreak(1) "Version 1.7.4" "Cagebreak Manual" +cagebreak(1) "Version 1.8.0" "Cagebreak Manual" # NAME @@ -38,6 +38,9 @@ are displayed in a message box at the top right of the screen. *-r* Rotate the output 90 degrees clockwise, can be specified up to three times +*-s* + Show all available inputs and outputs + *-v* Show version number and exit diff --git a/meson.build b/meson.build index 4f76677..e8a2cfd 100644 --- a/meson.build +++ b/meson.build @@ -1,5 +1,5 @@ project('cagebreak', 'c', - version: '1.7.4', + version: '1.8.0', license: 'MIT', default_options: [ 'c_std=c11', @@ -53,7 +53,7 @@ if is_freebsd ) endif -wlroots = dependency('wlroots', version: ['>=0.14.0', '< 0.15.0']) +wlroots = dependency('wlroots', version: ['>=0.14.1', '< 0.15.0']) wayland_protos = dependency('wayland-protocols', version: '>=1.14') wayland_server = dependency('wayland-server') wayland_cursor = dependency('wayland-cursor') @@ -64,6 +64,9 @@ cairo = dependency('cairo') pango = dependency('pango') pangocairo = dependency('pangocairo') fontconfig = dependency('fontconfig') +libinput = dependency('libinput') +libevdev = dependency('libevdev') +libudev = dependency('libudev') math = cc.find_library('m') wl_protocol_dir = wayland_protos.get_pkgconfig_variable('pkgdatadir') @@ -123,6 +126,7 @@ conf_data.set_quoted('CG_VERSION', version) cagebreak_main_file = [ 'cagebreak.c', ] cagebreak_source_strings = [ 'idle_inhibit_v1.c', + 'input_manager.c', 'ipc_server.c', 'keybinding.c', 'workspace.c', @@ -133,6 +137,7 @@ cagebreak_source_strings = [ 'util.c', 'view.c', 'xdg_shell.c', + 'libinput.c', 'server.c', 'message.c', 'pango.c', @@ -186,6 +191,9 @@ cagebreak_dependencies_dict = { 'wlroots': [wlroots,true], 'xkbcommon': [xkbcommon,true], 'fontconfig': [fontconfig,true], + 'libinput': [libinput,true], + 'libevdev': [libevdev,true], + 'libudev': [libudev,true], 'pixman': [pixman,true], 'pango': [pango,true], 'cairo': [cairo,true], @@ -198,13 +206,16 @@ reproducible_build_versions = { 'wayland_server': '1.19.0', 'wayland_client': '1.19.0', 'wayland_cursor': '1.19.0', - 'wlroots': '0.14.0', - 'xkbcommon': '1.3.0', + 'wlroots': '0.14.1', + 'xkbcommon': '1.3.1', 'fontconfig': '2.13.94', + 'libinput': '1.19.0', + 'libevdev': '1.11.0', + 'libudev': '249', 'pixman': '0.40.0', - 'pango': '1.48.6', + 'pango': '1.48.10', 'cairo': '1.17.4', - 'pangocairo': '1.48.6', + 'pangocairo': '1.48.10', 'math': '-1' } @@ -257,7 +268,7 @@ if get_option('man-pages') output : 'cagebreak.1', input : 'man/cagebreak.1.md', capture : true, - command : [sh, '-c', 'export SOURCE_DATE_EPOCH=1625064321 ; @0@ < @INPUT@'.format(scdoc.path())], + command : [sh, '-c', 'export SOURCE_DATE_EPOCH=1632645085 ; @0@ < @INPUT@'.format(scdoc.path())], install: true, install_dir: mandir1 ) @@ -266,7 +277,7 @@ if get_option('man-pages') output : 'cagebreak-config.5', input : 'man/cagebreak-config.5.md', capture : true, - command : [sh, '-c', 'export SOURCE_DATE_EPOCH=1625064321 ; @0@ < @INPUT@'.format(scdoc.path())], + command : [sh, '-c', 'export SOURCE_DATE_EPOCH=1632645085 ; @0@ < @INPUT@'.format(scdoc.path())], install: true, install_dir: mandir5 ) diff --git a/message.c b/message.c index 2ff2075..f05b175 100644 --- a/message.c +++ b/message.c @@ -12,6 +12,7 @@ #include "output.h" #include "pango.h" #include "server.h" +#include "util.h" cairo_subpixel_order_t to_cairo_subpixel_order(const enum wl_output_subpixel subpixel) { @@ -32,14 +33,14 @@ to_cairo_subpixel_order(const enum wl_output_subpixel subpixel) { struct wlr_texture * create_message_texture(const char *string, const struct cg_output *output) { - const int PADDING = 2; - const int MESSAGE_HEIGHT = 17 + 2 * PADDING; + const int WIDTH_PADDING = 8; + const int HEIGHT_PADDING = 2; const char *font = "pango:Monospace 10"; struct wlr_texture *texture; double scale = output->wlr_output->scale; int width = 0; - int height = MESSAGE_HEIGHT * scale; + int height = 0; // We must use a non-nil cairo_t for cairo_set_font_options to work. // Therefore, we cannot use cairo_create(NULL). @@ -59,8 +60,9 @@ create_message_texture(const char *string, const struct cg_output *output) { cairo_font_options_set_subpixel_order( fo, to_cairo_subpixel_order(output->wlr_output->subpixel)); cairo_set_font_options(c, fo); - get_text_size(c, font, &width, NULL, NULL, scale, "%s", string); - width += 2 * PADDING; + get_text_size(c, font, &width, &height, NULL, scale, "%s", string); + width += 2 * WIDTH_PADDING; + height += 2 * HEIGHT_PADDING; cairo_surface_destroy(dummy_surface); cairo_destroy(c); @@ -77,7 +79,7 @@ create_message_texture(const char *string, const struct cg_output *output) { cairo_set_line_width(cairo, 2); cairo_rectangle(cairo, 0, 0, width, height); cairo_stroke(cairo); - cairo_move_to(cairo, PADDING, PADDING); + cairo_move_to(cairo, WIDTH_PADDING, HEIGHT_PADDING); pango_printf(cairo, font, scale, "%s", string); @@ -149,17 +151,14 @@ message_set_output(struct cg_output *output, const char *string, void message_printf(struct cg_output *output, const char *fmt, ...) { - uint16_t buf_len = 256; - char *buffer = (char *)malloc(buf_len * sizeof(char)); + va_list ap; + va_start(ap, fmt); + char *buffer = malloc_vsprintf_va_list(fmt, ap); + va_end(ap); if(buffer == NULL) { wlr_log(WLR_ERROR, "Failed to allocate buffer in message_printf"); return; } - va_list ap; - - va_start(ap, fmt); - vsnprintf(buffer, buf_len, fmt, ap); - va_end(ap); struct wlr_box *box = malloc(sizeof(struct wlr_box)); if(box == NULL) { diff --git a/pango.c b/pango.c index c1ca414..e9d04d3 100644 --- a/pango.c +++ b/pango.c @@ -25,7 +25,7 @@ get_pango_layout(cairo_t *cairo, const char *font, const char *text, pango_attr_list_insert(attrs, pango_attr_scale_new(scale)); PangoFontDescription *desc = pango_font_description_from_string(font); pango_layout_set_font_description(layout, desc); - pango_layout_set_single_paragraph_mode(layout, 1); + pango_layout_set_single_paragraph_mode(layout, false); pango_layout_set_attributes(layout, attrs); pango_attr_list_unref(attrs); pango_font_description_free(desc); diff --git a/parse.c b/parse.c index 46b275a..4bbf72d 100644 --- a/parse.c +++ b/parse.c @@ -1,30 +1,23 @@ #define _POSIX_C_SOURCE 200812L +#include +#include #include #include #include +#include "input_manager.h" #include "keybinding.h" #include "output.h" #include "parse.h" #include "server.h" - -char * -malloc_vsprintf(const char *fmt, va_list ap) { - va_list ap2; - va_copy(ap2, ap); - int len = vsnprintf(NULL, 0, fmt, ap); - char *ret = malloc(sizeof(char) * (len + 1)); - vsnprintf(ret, len + 1, fmt, ap2); - va_end(ap2); - return ret; -} +#include "util.h" char * log_error(const char *fmt, ...) { va_list args; va_start(args, fmt); - char *ret = malloc_vsprintf(fmt, args); + char *ret = malloc_vsprintf_va_list(fmt, args); if(ret != NULL) { wlr_log(WLR_ERROR, "%s", ret); } @@ -106,6 +99,267 @@ parse_keybinding(struct cg_server *server, char **saveptr, char **errstr) { return keybinding; } +float +parse_float(char **saveptr, const char *delim) { + char *uint_str = strtok_r(NULL, delim, saveptr); + if(uint_str == NULL) { + wlr_log(WLR_ERROR, "Expected a float, got nothing"); + return -1; + } + float ufloat = strtof(uint_str, NULL); + if(ufloat != NAN && ufloat != INFINITY && errno != ERANGE) { + return ufloat; + } else { + wlr_log(WLR_ERROR, "Error parsing float"); + return FLT_MIN; + } +} + +struct cg_input_config * +parse_input_config(char **saveptr, char **errstr) { + struct cg_input_config *cfg = calloc(1, sizeof(struct cg_input_config)); + char *value = NULL; + char *ident = NULL; + if(cfg == NULL) { + *errstr = + log_error("Failed to allocate memory for output configuration"); + goto error; + } + + cfg->tap = INT_MIN; + cfg->tap_button_map = INT_MIN; + cfg->drag = INT_MIN; + cfg->drag_lock = INT_MIN; + cfg->dwt = INT_MIN; + cfg->send_events = INT_MIN; + cfg->click_method = INT_MIN; + cfg->middle_emulation = INT_MIN; + cfg->natural_scroll = INT_MIN; + cfg->accel_profile = INT_MIN; + cfg->pointer_accel = FLT_MIN; + cfg->scroll_factor = FLT_MIN; + cfg->scroll_button = INT_MIN; + cfg->scroll_method = INT_MIN; + cfg->left_handed = INT_MIN; + /*cfg->repeat_delay = INT_MIN; + cfg->repeat_rate = INT_MIN; + cfg->xkb_numlock = INT_MIN; + cfg->xkb_capslock = INT_MIN; + cfg->xkb_file_is_set = false; + wl_list_init(&cfg->tools);*/ + + ident = strtok_r(NULL, " ", saveptr); + + if(ident == NULL) { + *errstr = log_error( + "Expected identifier of input device to configure, got none"); + goto error; + } + cfg->identifier = strdup(ident); + + char *setting = strtok_r(NULL, " ", saveptr); + if(setting == NULL) { + *errstr = + log_error("Expected setting to be set on input device, got none"); + goto error; + } + + value = strdup(*saveptr); + + if(value == NULL) { + *errstr = log_error("Failed to obtain value for input device " + "configuration of device \"%s\"", + ident); + goto error; + } + + if(strcmp(setting, "accel_profile") == 0) { + if(strcmp(value, "adaptive") == 0) { + cfg->accel_profile = LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE; + } else if(strcmp(value, "flat") == 0) { + cfg->accel_profile = LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT; + } else { + *errstr = log_error( + "Invalid profile \"%s\" for accel_profile configuration", + value); + goto error; + } + } else if(strcmp(setting, "calibration_matrix") == 0) { + cfg->calibration_matrix.configured = true; + for(int i = 0; i < 6; ++i) { + cfg->calibration_matrix.matrix[i] = parse_float(saveptr, " "); + if(cfg->calibration_matrix.matrix[i] == FLT_MIN) { + *errstr = + log_error("Failed to read calibration matrix, expected 6 " + "floating point values separated by spaces"); + goto error; + } + } + } else if(strcmp(setting, "click_method") == 0) { + if(strcmp(value, "none")) { + cfg->click_method = LIBINPUT_CONFIG_CLICK_METHOD_NONE; + } else if(strcmp(value, "button_areas")) { + cfg->click_method = LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS; + } else if(strcmp(value, "clickfinger")) { + cfg->click_method = LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER; + } else { + *errstr = log_error( + "Invalid method \"%s\" for click_method configuration", value); + goto error; + } + } else if(strcmp(setting, "drag") == 0) { + if(strcmp(value, "enabled") == 0) { + cfg->drag = LIBINPUT_CONFIG_DRAG_ENABLED; + } else if(strcmp(value, "disabled") == 0) { + cfg->drag = LIBINPUT_CONFIG_DRAG_DISABLED; + } else { + *errstr = + log_error("Invalid option \"%s\" to setting \"drag\"", value); + goto error; + } + } else if(strcmp(setting, "drag_lock") == 0) { + if(strcmp(value, "enabled") == 0) { + cfg->drag_lock = LIBINPUT_CONFIG_DRAG_LOCK_ENABLED; + } else if(strcmp(value, "disabled") == 0) { + cfg->drag_lock = LIBINPUT_CONFIG_DRAG_LOCK_DISABLED; + } else { + *errstr = log_error( + "Invalid option \"%s\" to setting \"drag_lock\"", value); + goto error; + } + } else if(strcmp(setting, "dwt") == 0) { + if(strcmp(value, "enabled") == 0) { + cfg->dwt = LIBINPUT_CONFIG_DWT_ENABLED; + } else if(strcmp(value, "disabled") == 0) { + cfg->dwt = LIBINPUT_CONFIG_DWT_DISABLED; + } else { + *errstr = + log_error("Invalid option \"%s\" to setting \"dwt\"", value); + goto error; + } + } else if(strcmp(setting, "events") == 0) { + if(strcmp(value, "enabled") == 0) { + cfg->send_events = LIBINPUT_CONFIG_SEND_EVENTS_ENABLED; + } else if(strcmp(value, "disabled") == 0) { + cfg->send_events = LIBINPUT_CONFIG_SEND_EVENTS_DISABLED; + } else if(strcmp(value, "disabled_on_external_mouse") == 0) { + cfg->send_events = + LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE; + } else { + *errstr = + log_error("Invalid option \"%s\" to setting \"events\"", value); + goto error; + } + } else if(strcmp(setting, "left_handed") == 0) { + if(strcmp(value, "enabled") == 0) { + cfg->left_handed = true; + } else if(strcmp(value, "disabled") == 0) { + cfg->left_handed = false; + } else { + *errstr = log_error( + "Invalid option \"%s\" to setting \"left_handed\"", value); + goto error; + } + } else if(strcmp(setting, "middle_emulation") == 0) { + if(strcmp(value, "enabled") == 0) { + cfg->middle_emulation = LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED; + } else if(strcmp(value, "disabled") == 0) { + cfg->middle_emulation = LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED; + } else { + *errstr = log_error( + "Invalid option \"%s\" to setting \"middle_emulation\"", value); + goto error; + } + } else if(strcmp(setting, "natural_scroll") == 0) { + if(strcmp(value, "enabled") == 0) { + cfg->natural_scroll = true; + } else if(strcmp(value, "disabled") == 0) { + cfg->natural_scroll = false; + } else { + *errstr = log_error( + "Invalid option \"%s\" to setting \"natural_scroll\"", value); + goto error; + } + } else if(strcmp(setting, "pointer_accel") == 0) { + cfg->pointer_accel = parse_float(saveptr, " "); + if(cfg->pointer_accel == FLT_MIN) { + *errstr = log_error("Invalid option \"%s\" to setting " + "\"pointer_accel\", expected float value", + value); + goto error; + } + } else if(strcmp(setting, "scroll_button") == 0) { + char *err = NULL; + cfg->scroll_button = input_manager_get_mouse_button(value, &err); + if(err) { + *errstr = log_error("Error parsing button for \"scroll_button\" " + "setting. Returned error \"%s\"", + err); + free(err); + goto error; + } + } else if(strcmp(setting, "scroll_factor") == 0) { + cfg->scroll_factor = parse_float(saveptr, " "); + if(cfg->scroll_factor == FLT_MIN) { + *errstr = log_error("Invalid option \"%s\" to setting " + "\"scroll_factor\", expected float value", + value); + goto error; + } + } else if(strcmp(setting, "scroll_method") == 0) { + if(strcmp(value, "none") == 0) { + cfg->scroll_method = LIBINPUT_CONFIG_SCROLL_NO_SCROLL; + } else if(strcmp(value, "two_finger") == 0) { + cfg->scroll_method = LIBINPUT_CONFIG_SCROLL_2FG; + } else if(strcmp(value, "edge") == 0) { + cfg->scroll_method = LIBINPUT_CONFIG_SCROLL_EDGE; + } else if(strcmp(value, "on_button_down") == 0) { + cfg->scroll_method = LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN; + } else { + *errstr = log_error( + "Invalid option \"%s\" to setting \"scroll_method\"", value); + goto error; + } + } else if(strcmp(setting, "tap") == 0) { + if(strcmp(value, "enabled") == 0) { + cfg->tap = LIBINPUT_CONFIG_TAP_ENABLED; + } else if(strcmp(value, "disabled") == 0) { + cfg->tap = LIBINPUT_CONFIG_TAP_DISABLED; + } else { + *errstr = + log_error("Invalid option \"%s\" to setting \"tap\"", value); + goto error; + } + } else if(strcmp(setting, "tap_button_map") == 0) { + if(strcmp(value, "lrm") == 0) { + cfg->tap = LIBINPUT_CONFIG_TAP_MAP_LRM; + } else if(strcmp(value, "lmr") == 0) { + cfg->tap = LIBINPUT_CONFIG_TAP_MAP_LMR; + } else { + *errstr = log_error( + "Invalid option \"%s\" to setting \"tap_button_map\"", value); + goto error; + } + } + + free(value); + return cfg; + +error: + if(cfg) { + if(cfg->identifier) { + free(cfg->identifier); + } + free(cfg); + } + if(value) { + free(value); + } + wlr_log(WLR_ERROR, "Input configuration must be of the form 'input " + "\"\" '"); + return NULL; +} + struct keybinding * parse_bind(struct cg_server *server, char **saveptr, char **errstr) { struct keybinding *keybinding = parse_keybinding(server, saveptr, errstr); @@ -242,23 +496,6 @@ parse_uint(char **saveptr, const char *delim) { } } -float -parse_float(char **saveptr, const char *delim) { - char *uint_str = strtok_r(NULL, delim, saveptr); - if(uint_str == NULL) { - wlr_log(WLR_ERROR, "Expected a non-negative float, got nothing"); - return -1; - } - float ufloat = strtof(uint_str, NULL); - if(ufloat >= 0) { - return ufloat; - } else { - wlr_log(WLR_ERROR, "Error parsing non-negative float. Must be a number " - "larger or equal to 0"); - return -1; - } -} - int parse_output_config_keyword(char *key_str, enum output_status *status) { if(key_str == NULL) { @@ -352,9 +589,10 @@ parse_output_config(char **saveptr, char **errstr) { cfg->refresh_rate = parse_float(saveptr, " "); if(cfg->refresh_rate <= 0.0) { - *errstr = log_error( - "Error parsing refresh rate of output configuration for output %s", - name); + *errstr = + log_error("Error parsing refresh rate of output configuration for " + "output %s, expected positive float", + name); goto error; } @@ -383,6 +621,8 @@ parse_command(struct cg_server *server, struct keybinding *keybinding, keybinding->action = KEYBINDING_SPLIT_HORIZONTAL; } else if(strcmp(action, "quit") == 0) { keybinding->action = KEYBINDING_QUIT; + } else if(strcmp(action, "show_info") == 0) { + keybinding->action = KEYBINDING_SHOW_INFO; } else if(strcmp(action, "close") == 0) { keybinding->action = KEYBINDING_CLOSE_VIEW; } else if(strcmp(action, "focus") == 0) { @@ -562,6 +802,12 @@ parse_command(struct cg_server *server, struct keybinding *keybinding, if(keybinding->data.o_cfg == NULL) { return -1; } + } else if(strcmp(action, "input") == 0) { + keybinding->action = KEYBINDING_CONFIGURE_INPUT; + keybinding->data.i_cfg = parse_input_config(&saveptr, errstr); + if(keybinding->data.i_cfg == NULL) { + return -1; + } } else { *errstr = log_error("Error, unsupported action \"%s\".", action); return -1; diff --git a/parse.h b/parse.h index ba1b2e8..798d732 100644 --- a/parse.h +++ b/parse.h @@ -4,9 +4,15 @@ #define MAX_LINE_SIZE 256 +#include + struct cg_server; int parse_rc_line(struct cg_server *server, char *line, char **errstr); +char * +parse_malloc_vsprintf(const char *fmt, ...); +char * +parse_malloc_vsprintf_va_list(const char *fmt, va_list list); #endif /* end of include guard PARSE_H */ diff --git a/seat.c b/seat.c index 51450ac..91ab374 100644 --- a/seat.c +++ b/seat.c @@ -31,6 +31,7 @@ #include #endif +#include "input_manager.h" #include "keybinding.h" #include "message.h" #include "output.h" @@ -112,13 +113,13 @@ static void update_capabilities(const struct cg_seat *seat) { uint32_t caps = 0; - if(!wl_list_empty(&seat->keyboard_groups)) { + if(seat->num_keyboards > 0) { caps |= WL_SEAT_CAPABILITY_KEYBOARD; } - if(!wl_list_empty(&seat->pointers)) { + if(seat->num_pointers > 0) { caps |= WL_SEAT_CAPABILITY_POINTER; } - if(!wl_list_empty(&seat->touch)) { + if(seat->num_touch > 0) { caps |= WL_SEAT_CAPABILITY_TOUCH; } wlr_seat_set_capabilities(seat->seat, caps); @@ -133,20 +134,21 @@ update_capabilities(const struct cg_seat *seat) { } static void -handle_touch_destroy(struct wl_listener *listener, void *_data) { - struct cg_touch *touch = wl_container_of(listener, touch, destroy); - struct cg_seat *seat = touch->seat; - +remove_touch(struct cg_seat *seat, struct cg_touch *touch) { + if(!touch) { + return; + } wl_list_remove(&touch->link); - wlr_cursor_detach_input_device(seat->cursor, touch->device); - wl_list_remove(&touch->destroy.link); + wlr_cursor_detach_input_device(seat->cursor, touch->device->wlr_device); + --seat->num_touch; free(touch); update_capabilities(seat); } static void -handle_new_touch(struct cg_seat *seat, struct wlr_input_device *device) { +new_touch(struct cg_seat *seat, struct cg_input_device *input_device) { + struct wlr_input_device *device = input_device->wlr_device; struct cg_touch *touch = calloc(1, sizeof(struct cg_touch)); if(!touch) { wlr_log(WLR_ERROR, "Cannot allocate touch"); @@ -154,12 +156,10 @@ handle_new_touch(struct cg_seat *seat, struct wlr_input_device *device) { } touch->seat = seat; - touch->device = device; + touch->device = input_device; wlr_cursor_attach_input_device(seat->cursor, device); - - wl_list_insert(&seat->touch, &touch->link); - touch->destroy.notify = handle_touch_destroy; - wl_signal_add(&touch->device->events.destroy, &touch->destroy); + input_device->touch = touch; + ++seat->num_touch; if(device->output_name != NULL) { struct cg_output *output; @@ -174,20 +174,20 @@ handle_new_touch(struct cg_seat *seat, struct wlr_input_device *device) { } static void -handle_pointer_destroy(struct wl_listener *listener, void *_data) { - struct cg_pointer *pointer = wl_container_of(listener, pointer, destroy); - struct cg_seat *seat = pointer->seat; - - wl_list_remove(&pointer->link); - wlr_cursor_detach_input_device(seat->cursor, pointer->device); - wl_list_remove(&pointer->destroy.link); +remove_pointer(struct cg_seat *seat, struct cg_pointer *pointer) { + if(!pointer) { + return; + } + wlr_cursor_detach_input_device(seat->cursor, pointer->device->wlr_device); + --seat->num_pointers; free(pointer); update_capabilities(seat); } static void -handle_new_pointer(struct cg_seat *seat, struct wlr_input_device *device) { +new_pointer(struct cg_seat *seat, struct cg_input_device *input_device) { + struct wlr_input_device *device = input_device->wlr_device; struct cg_pointer *pointer = calloc(1, sizeof(struct cg_pointer)); if(!pointer) { wlr_log(WLR_ERROR, "Cannot allocate pointer"); @@ -195,12 +195,10 @@ handle_new_pointer(struct cg_seat *seat, struct wlr_input_device *device) { } pointer->seat = seat; - pointer->device = device; + pointer->device = input_device; wlr_cursor_attach_input_device(seat->cursor, device); - - wl_list_insert(&seat->pointers, &pointer->link); - pointer->destroy.notify = handle_pointer_destroy; - wl_signal_add(&device->events.destroy, &pointer->destroy); + input_device->pointer = pointer; + ++seat->num_pointers; if(device->output_name != NULL) { struct cg_output *output; @@ -431,21 +429,16 @@ cg_keyboard_group_add(struct wlr_input_device *device, struct cg_seat *seat) { } static void -handle_new_keyboard(struct cg_seat *seat, struct wlr_input_device *device) { +new_keyboard(struct cg_seat *seat, struct cg_input_device *input_device) { + struct wlr_input_device *device = input_device->wlr_device; struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); if(!context) { - wlr_log(WLR_ERROR, "Unable to create XBK context"); + wlr_log(WLR_ERROR, "Unable to create XKB context"); return; } - struct xkb_rule_names rules = {0}; - rules.rules = getenv("XKB_DEFAULT_RULES"); - rules.model = getenv("XKB_DEFAULT_MODEL"); - rules.layout = getenv("XKB_DEFAULT_LAYOUT"); - rules.variant = getenv("XKB_DEFAULT_VARIANT"); - rules.options = getenv("XKB_DEFAULT_OPTIONS"); struct xkb_keymap *keymap = - xkb_map_new_from_names(context, &rules, XKB_KEYMAP_COMPILE_NO_FLAGS); + xkb_map_new_from_names(context, NULL, XKB_KEYMAP_COMPILE_NO_FLAGS); if(!keymap) { wlr_log(WLR_ERROR, "Unable to configure keyboard: keymap does not exist"); @@ -460,24 +453,64 @@ handle_new_keyboard(struct cg_seat *seat, struct wlr_input_device *device) { wlr_keyboard_set_repeat_info(device->keyboard, 25, 600); cg_keyboard_group_add(device, seat); + ++seat->num_keyboards; wlr_seat_set_keyboard(seat->seat, device); } -static void -handle_new_input(struct wl_listener *listener, void *data) { - struct cg_seat *seat = wl_container_of(listener, seat, new_input); - struct wlr_input_device *device = data; +void +seat_add_device(struct cg_seat *seat, struct cg_input_device *device) { + switch(device->wlr_device->type) { + case WLR_INPUT_DEVICE_KEYBOARD: + new_keyboard(seat, device); + break; + case WLR_INPUT_DEVICE_POINTER: + new_pointer(seat, device); + break; + case WLR_INPUT_DEVICE_TOUCH: + new_touch(seat, device); + break; + case WLR_INPUT_DEVICE_SWITCH: + wlr_log(WLR_DEBUG, "Switch input is not implemented"); + return; + case WLR_INPUT_DEVICE_TABLET_TOOL: + case WLR_INPUT_DEVICE_TABLET_PAD: + wlr_log(WLR_DEBUG, "Tablet input is not implemented"); + return; + } + + update_capabilities(seat); +} + +void +remove_keyboard(struct cg_seat *seat, struct cg_input_device *keyboard) { + if(!keyboard) { + return; + } + struct wlr_seat *wlr_seat = seat->seat; + struct wlr_keyboard *wlr_keyboard = keyboard->wlr_device->keyboard; + if(keyboard->wlr_device->keyboard->group) { + wlr_keyboard_group_remove_keyboard(wlr_keyboard->group, wlr_keyboard); + } + if(wlr_seat_get_keyboard(wlr_seat) == wlr_keyboard) { + wlr_seat_set_keyboard(wlr_seat, NULL); + } + --seat->num_keyboards; +} - switch(device->type) { +void +seat_remove_device(struct cg_seat *seat, struct cg_input_device *device) { + switch(device->wlr_device->type) { case WLR_INPUT_DEVICE_KEYBOARD: - handle_new_keyboard(seat, device); + remove_keyboard(seat, device); break; case WLR_INPUT_DEVICE_POINTER: - handle_new_pointer(seat, device); + remove_pointer(seat, device->pointer); + device->pointer = NULL; break; case WLR_INPUT_DEVICE_TOUCH: - handle_new_touch(seat, device); + remove_touch(seat, device->touch); + device->touch = NULL; break; case WLR_INPUT_DEVICE_SWITCH: wlr_log(WLR_DEBUG, "Switch input is not implemented"); @@ -801,15 +834,11 @@ handle_destroy(struct wl_listener *listener, void *_data) { wl_event_source_remove(group->key_repeat_timer); free(group); } - struct cg_pointer *pointer, *pointer_tmp; - wl_list_for_each_safe(pointer, pointer_tmp, &seat->pointers, link) { - handle_pointer_destroy(&pointer->destroy, NULL); - } - struct cg_touch *touch, *touch_tmp; - wl_list_for_each_safe(touch, touch_tmp, &seat->touch, link) { - handle_touch_destroy(&touch->destroy, NULL); + + struct cg_input_device *it, *it_tmp; + wl_list_for_each_safe(it, it_tmp, &seat->server->input->devices, link) { + input_manager_handle_device_destroy(&it->device_destroy, NULL); } - wl_list_remove(&seat->new_input.link); wlr_xcursor_manager_destroy(seat->xcursor_manager); if(seat->cursor) { @@ -898,11 +927,9 @@ seat_create(struct cg_server *server, struct wlr_backend *backend) { &seat->request_set_primary_selection); wl_list_init(&seat->keyboard_groups); - wl_list_init(&seat->pointers); - wl_list_init(&seat->touch); - - seat->new_input.notify = handle_new_input; - wl_signal_add(&backend->events.new_input, &seat->new_input); + seat->num_keyboards = 0; + seat->num_pointers = 0; + seat->num_touch = 0; wl_list_init(&seat->drag_icons); seat->request_start_drag.notify = handle_request_start_drag; diff --git a/seat.h b/seat.h index a7632d7..9331042 100644 --- a/seat.h +++ b/seat.h @@ -7,6 +7,7 @@ struct cg_server; struct cg_view; struct wlr_cursor; struct wlr_input_device; +struct cg_input_device; struct wlr_seat; struct wlr_xcursor_manager; struct wlr_backend; @@ -20,9 +21,10 @@ struct cg_seat { struct wl_listener destroy; struct wl_list keyboard_groups; - struct wl_list pointers; - struct wl_list touch; - struct wl_listener new_input; + + uint16_t num_keyboards; + uint16_t num_pointers; + uint16_t num_touch; struct wlr_cursor *cursor; struct wlr_xcursor_manager *xcursor_manager; @@ -70,17 +72,13 @@ struct cg_keyboard_group { struct cg_pointer { struct wl_list link; // seat::pointers struct cg_seat *seat; - struct wlr_input_device *device; - - struct wl_listener destroy; + struct cg_input_device *device; }; struct cg_touch { struct wl_list link; // seat::touch struct cg_seat *seat; - struct wlr_input_device *device; - - struct wl_listener destroy; + struct cg_input_device *device; }; struct cg_drag_icon { @@ -102,5 +100,9 @@ struct cg_view * seat_get_focus(const struct cg_seat *seat); void seat_set_focus(struct cg_seat *seat, struct cg_view *view); +void +seat_add_device(struct cg_seat *seat, struct cg_input_device *device); +void +seat_remove_device(struct cg_seat *seat, struct cg_input_device *device); #endif diff --git a/server.c b/server.c index c82296f..1ad23e6 100644 --- a/server.c +++ b/server.c @@ -9,11 +9,16 @@ #define _POSIX_C_SOURCE 200809L +#include #include #include #include +#include +#include "input_manager.h" +#include "output.h" #include "server.h" +#include "util.h" void display_terminate(struct cg_server *server) { @@ -35,3 +40,36 @@ get_mode_index_from_name(char *const *modes, const char *mode_name) { } return -1; } + +char * +server_show_info(struct cg_server *server) { + char *output_str = strdup(""), *output_str_tmp; + struct cg_output *output; + wl_list_for_each(output, &server->outputs, link) { + if(!output_str) { + return NULL; + } + output_str_tmp = output_str; + output_str = malloc_vsprintf("%s\t * %s\n", output_str, + output->wlr_output->name); + free(output_str_tmp); + } + char *input_str = strdup(""), *input_str_tmp; + struct cg_input_device *input; + wl_list_for_each(input, &server->input->devices, link) { + if(!input_str) { + return NULL; + } + input_str_tmp = input_str; + if(strcmp(input->identifier, "") != 0) { + input_str = + malloc_vsprintf("%s\t * %s\n", input_str, input->identifier); + } + free(input_str_tmp); + } + char *ret = + malloc_vsprintf("Outputs:\n%sInputs:\n%s", output_str, input_str); + free(output_str); + free(input_str); + return ret; +} diff --git a/server.h b/server.h index 4e594b9..9ee21e1 100644 --- a/server.h +++ b/server.h @@ -13,12 +13,14 @@ struct keybinding_list; struct wlr_output_layout; struct wlr_idle_inhibit_manager_v1; struct cg_output_config; +struct cg_input_manager; struct cg_server { struct wl_display *wl_display; struct wl_event_loop *event_loop; struct cg_seat *seat; + struct cg_input_manager *input; struct wlr_backend *backend; struct wlr_idle *idle; struct wlr_idle_inhibit_manager_v1 *idle_inhibit_v1; @@ -39,6 +41,7 @@ struct cg_server { struct keybinding_list *keybindings; struct wl_list output_config; + struct wl_list input_config; enum wl_output_transform output_transform; @@ -58,5 +61,7 @@ void display_terminate(struct cg_server *server); int get_mode_index_from_name(char *const *modes, const char *mode_name); +char * +server_show_info(struct cg_server *server); #endif diff --git a/signatures/1.7.4-cagebreak-config.5.sig b/signatures/1.7.4-cagebreak-config.5.sig new file mode 100644 index 0000000000000000000000000000000000000000..0fa98a3b5e3730da2324fb1b7cdc2142ad58171e GIT binary patch literal 566 zcmV-60?GY}0y6{v0SEvc79j+QYm%>XjF(OTWxqo5S8LN|6>GT#0$|*e!T<^h5b{@R z(`FTGxrOu)0JN+!p3s(f6u~g|_}ne$m^*0VAJc~P%dP%gOML&}S1M~4%%m=%@c*dN zjHSR#dBLsEU1ucR=oq0B=U>7QaLc0{TEsZKh(5GrzY^bv*@VRMym-`O1N%_ay;D~~ zX)PR>8RhzhkB5te*+hkZ#nOs#Z4k6y_JHP%@I_d%p_@Am-W4D(hQ?=PCPEOIy#BS~ zWXMn5ccPfHLP|lTa|0+izsqSs!aZWILo{&5!On0$mHsmWmhV9xb;C>4-}u&)x+S4? zTkG_>z$JH|k`POdCKWaV#nU{VjP6Ve9J=4evlME8%NIw0-+2DI5wedxff|qj)v|HK zv;VsJDoc5RpQ`8gArK=7Isc)T)W@1wBxwtbpS6 z1J>RqbH=e4=x2}iVfRi`X^)*jNBQ>7-MWzH)|RK0tE-}bdJA}2sI@P7Z9>!|6Y=4H zUJbf(#dN#O-bzMm5r=he(Vhgvl|f(9Pgy@q2iL?}9ATR5RC2R}iuVUHcZTkLp z#@_VI7lexJZQXjF(OTWxqo5S8LN|6>GT#0$|*ezW@pe5b{@R z(`FTGxk#uG0GRMD6czzY9TRTW1281~uT9l>(Mu`72N#1zJ!S~UJoN!}A~&e&!q+%5 z^!v}G*IMak4Pv5yQcvCPqhsJLnBJ3&qPmB5eG>Z&KOw7uqoQQ+dG z9gwfzK+H}kcX|hPQ4j=h?MY^`0*1(l$Xd?Jmqa4JM&hF zVDbag=E>so4D`CAkHS?;>8CX6w+v{mc)$@tc9BhYIb1ULr4UFh4%UxS)<=-%Zy7s?O$~R&BY7;P`VthCS0pq|y4)}A z>}xdfT+F)dx$)6XQU@!5eW|Q7xvdYzE1wdTY}03~0!f#ICFewX)B&8lcy8&Mb%dIs zp$<0!7zn0GbJwI(acI}`1IgtdNC&s<3a0SxmxHZ-JXq3#U)#fk0SRb_f^Q?yXh}&d zx&W6lP6Kj=-Y(-K&uv0&L6gpZ--RbED0l_xXuTV~mDksnXjF(OTWxqo5S8LN|6>GT#0$|*ey#NXc5b{@R z(`FTGxug^i{yYu;J{9!w^HXyDa*evENQ3S(1@Y=r5>Vtx8-L9)-a<;__a({fOme4K zkPx5|Z?a8>qA3sRu-nYC5ReO~n)Thl*a(ogd5;a&R>48VJ@mN&I>^F=pxZl{fD4X` zyq|wnHeBlWRdi}1bxMsdX3`s;<;>^AJ86UHlABr!X(sFbCBjOCn4vGN13r%PM5k>Z zM6tKYvC`qL5>SaHu>R9}Gsy>)anS^cafQ1Qh_iJc$P^$)j}b72?!?}&)DIO;i-}tX z4RSC5bYYAI%#ntf2km57LDlelrU91B8rpyt94ETUyCy$J7niAjGzyR(R}2XvV57fMH(UOZA?^?L(3hH|IE7_6g+m+n{t^c;fwm z@KJ<GYq_`N5CF51b9cAmN%Oj>k*71BdSWRI zRV}I8C(Up$$oLb0cfwsPXbrJ6ExylM!v3L>9~f(cw-GyhdoaXRjCr+&xZc$3$9Ifm z;liZ)SInvT{_#09Gw4a$U4H3Se~q4y`kt|m+s>{Y(XMFOxPP|?__hD(WTe3))=R+} zFuuyMQV(GfUosIOtQK}4@z;nzp84MAX&w@vJfj^}`V&hw+XIS{qv`k^yJr(nz5qCg zB;a)(2wOzq#L@RLb#f^LgjU-m^~{r5`i0IFyU&yh^#?@atDZSJk5Cbj5R#9dp}#`| zmOn6og+dYZuYdRssy*=WBCN=R1}#Zyk~Hd5*01egV1dEm*|YpCmZ=Wakt~rsMzoKgb#Rd0sgCw0iPz`mVI)m6RIlwjdkNj5)PK$VJSZHGYq^E=5CF8SGM>K2zhL4Afg@4&Zg@47;ig9fav|sjs z=8f=0ShAs;I}P3yATNf-XJjTq5ShIGwc=#RPu+K-n6pAkL8Nm7C^)~%X+gq0Vy{Cq zaL2*Ua6gs)GXs|IK^}F(OVr=^)|9#>p>cN(vX4E18ju0ivT?+-|GN1qOL>8xs^|A15F-dV|DnT>-ey@q2iL?}9ATR5RC2R}iuVUHcZTkLp#@_VI7lexJZQGYq>VY5B(Qbz^|s&H%;mc?xxzI|3_Ry zO{Nwfy8y%3>G<|`7676cCEAu1@QnqxpPM>bd}g$!nP=S=@s6n$UXscEBcGYd&Kc{+ z-Am60?)BRU`6}rY#CBKDOZkGtZ!}8-c7qT1pLN$T;Jq_8=YKI)mX?_64 zue#s26u%I|{(s5YP5;N`{Ug=Hy7Y8`9Qc$x>+IWZO+?YX|9-{OE zZ~oy8LeDy4H7_)7*Ht_?`(+g{@a*mC0F8_-=5Bgy;LzFW#|U`K;`nGT!Ds0~z@T+c zYfgguGcvXWX->W?rl^qPze(P7AEGD`-pC4-ZRBYrKY#Qu=ci?x3A>g|nEtNKdhQk{ zAHy&-O$yQfSiOhJCYRE3n-fU39%A>Z&%k;;qXXK*ytgCS1_rm3xIvF^j}z8#zpwOC z8V;_D@?z!80yNMwG0i9SPcYeIWJ;zwUCh%`GQ2@JqH3=HY%;Ji9U;)p)uAcA h+OILn85lHkmm3r`8|lR`Z!rw1KMA(Ago6kc&$<)r5TF16 delta 543 zcmV+)0^t3&1hxc_B7b1qlfM882@vvEYtv>GYq?0M5CE9)E)*64OdS($)&npk{I5;b zc+pEKzy}wDMm=T-$2|1`bs{&Y>cZDJGW7e;q}N*MXANSae^O7~?xSPiEtuYujH0@S zb$t^13_l}bD|Y!PfTD6Ru(ZAAgHhn(r5%v3-$2YxD0g}Xb$?Tr0nCo>_OV`oZdpn3 zd1bs+OynFI`6WsP9rre1nNY0f%rga!k83rbemp7vWa!vS9X9-lnVIjks;77kVu5Ff=L_x#>DW=*{MebO%6b>B@UON}NM>E7w{W;>Y zTi9yunD+)jUwirB$?iGo#*4OqhHw#XZS8!=IlFzLgwm diff --git a/signatures/cagebreak.sig b/signatures/cagebreak.sig index 22e1818ab09d095c08f005169e584fd605ad2096..a7634a92693b30deb71ef71ca850c6d5cee432f8 100644 GIT binary patch delta 543 zcmV+)0^t3&1hxc_B7b2}$W8zX2@vvEYtv>GYq@KD5C42M41b8lDa1hwp}r$*6mfYIB?xP9hm$L{d+4`+61fy$ha_yf(-u; zOaD|8D6IreUw@pEQcm$JdR}-FdOa!?sBGUlHeF@5>JEKB=OV2~-Iuz0Af)-!8b+;) ziv25Ro?Rw<@n)Rf^)`SS_>l{RD?CrEKTsW1@KGl-8(gt_c1jHGSF@93g>#}VFCxHe zVj~d)1Rdu|p&j}{SE2~TE6f-C9cP`0IG^ z%aDQz5{Sd++aJk?eibqa^NK+47`5T@1;;FbQ;Y`F=+^-+GYq_Kp5B@w2|2`G;@$*x1{c?@Es7QnE zGX?SLQxZ_*NgIF7G2TK-`Zc}Sdb8)5pS|hhN3AC>ag3)vJj99sG9ZNz}N_o zxOtBa)>gqm#Xa=70XoRSgrM6ynScw9i@cwIRW@Ae_f>RiB7b#CjW1@>8=mFN=fpc{ zgXof*S_^3=>;5IeN`#o9FRcSUj`KvPZ68Fjx5=^6;jR);i6yZ9(|R+>2bFQr1c`Bl zyAp`AbsxwSAV-f8Foy2L-mlaT6;F$aTLuksFaUI6j0Mb*hM5QLWLQDf@O-8LmdqO3 zfEOGmy2`sIKYvFUdFEBQg1@#{pJXTuSybKI`ko4+z@QV1_3dUL#I~STc}!&> zR+)Xt?`~CSF3Tg4BJno#Db-kXATr&d=rSYwMwt1;Ab;XlP9P^3(2sBB3u3$fRE{nI zP{j=)THh7UgY;@giKJ|4kaSPon`d9o+kP(IC=$(Kx #include "util.h" +#include int scale_length(int length, int offset, double scale) { @@ -33,3 +34,22 @@ scale_box(struct wlr_box *box, double scale) { box->x = (int)round(box->x * scale); box->y = (int)round(box->y * scale); } + +char * +malloc_vsprintf_va_list(const char *fmt, va_list ap) { + va_list ap2; + va_copy(ap2, ap); + int len = vsnprintf(NULL, 0, fmt, ap); + char *ret = malloc(sizeof(char) * (len + 1)); + vsnprintf(ret, len + 1, fmt, ap2); + va_end(ap2); + return ret; +} + +char * +malloc_vsprintf(const char *fmt, ...) { + va_list args; + va_start(args, fmt); + char *ret = malloc_vsprintf_va_list(fmt, args); + return ret; +} diff --git a/util.h b/util.h index 8ddc7be..01e403e 100644 --- a/util.h +++ b/util.h @@ -1,6 +1,8 @@ #ifndef CG_UTIL_H #define CG_UTIL_H +#include + struct wlr_box; /** Apply scale to a width or height. */ @@ -10,4 +12,9 @@ scale_length(int length, int offset, double scale); void scale_box(struct wlr_box *box, double scale); +char * +malloc_vsprintf(const char *fmt, ...); +char * +malloc_vsprintf_va_list(const char *fmt, va_list list); + #endif