diff --git a/include/sway/config.h b/include/sway/config.h index f9da19675..40710199a 100644 --- a/include/sway/config.h +++ b/include/sway/config.h @@ -291,6 +291,14 @@ struct output_config { char *background_fallback; }; +/** + * An output config pre-matched to an output + */ +struct matched_output_config { + struct sway_output *output; + struct output_config *config; +}; + /** * Stores size of gaps for each side */ @@ -680,20 +688,15 @@ const char *sway_output_scale_filter_to_string(enum scale_filter_mode scale_filt struct output_config *new_output_config(const char *name); -void merge_output_config(struct output_config *dst, struct output_config *src); +bool apply_output_configs(struct matched_output_config *configs, + size_t configs_len, bool test_only); -bool apply_output_config(struct output_config *oc, struct sway_output *output); - -bool test_output_config(struct output_config *oc, struct sway_output *output); +void apply_all_output_configs(void); struct output_config *store_output_config(struct output_config *oc); struct output_config *find_output_config(struct sway_output *output); -void apply_output_config_to_outputs(struct output_config *oc); - -void reset_outputs(void); - void free_output_config(struct output_config *oc); bool spawn_swaybg(void); diff --git a/include/sway/input/input-manager.h b/include/sway/input/input-manager.h index 145edd4b6..45c751994 100644 --- a/include/sway/input/input-manager.h +++ b/include/sway/input/input-manager.h @@ -4,6 +4,7 @@ #include #include #include +#include #include "sway/server.h" #include "sway/config.h" #include "list.h" @@ -24,6 +25,7 @@ struct sway_input_manager { struct wlr_virtual_keyboard_manager_v1 *virtual_keyboard; struct wlr_virtual_pointer_manager_v1 *virtual_pointer; struct wlr_pointer_gestures_v1 *pointer_gestures; + struct wlr_transient_seat_manager_v1 *transient_seat_manager; struct wl_listener new_input; struct wl_listener inhibit_activate; @@ -31,6 +33,7 @@ struct sway_input_manager { struct wl_listener keyboard_shortcuts_inhibit_new_inhibitor; struct wl_listener virtual_keyboard_new; struct wl_listener virtual_pointer_new; + struct wl_listener transient_seat_create; }; struct sway_input_manager *input_manager_create(struct sway_server *server); diff --git a/include/sway/input/seat.h b/include/sway/input/seat.h index 475753d8a..428f96796 100644 --- a/include/sway/input/seat.h +++ b/include/sway/input/seat.h @@ -124,6 +124,7 @@ struct sway_seat { struct wl_listener start_drag; struct wl_listener request_set_selection; struct wl_listener request_set_primary_selection; + struct wl_listener destroy; struct wl_list devices; // sway_seat_device::link struct wl_list keyboard_groups; // sway_keyboard_group::link diff --git a/include/sway/output.h b/include/sway/output.h index 30595f545..d546d4884 100644 --- a/include/sway/output.h +++ b/include/sway/output.h @@ -50,7 +50,7 @@ struct sway_output { enum wl_output_subpixel detected_subpixel; enum scale_filter_mode scale_filter; - bool enabling, enabled; + bool enabled; list_t *workspaces; struct sway_output_state current; diff --git a/include/sway/server.h b/include/sway/server.h index b2eb2c365..c71851f68 100644 --- a/include/sway/server.h +++ b/include/sway/server.h @@ -46,6 +46,7 @@ struct sway_server { struct wl_listener new_output; struct wl_listener output_layout_change; + struct wl_listener renderer_lost; struct wlr_idle_notifier_v1 *idle_notifier_v1; struct sway_idle_inhibit_manager_v1 idle_inhibit_manager_v1; diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h index ef1a26b8c..7faacdcc2 100644 --- a/include/sway/tree/view.h +++ b/include/sway/tree/view.h @@ -192,6 +192,7 @@ struct sway_xdg_popup { struct wl_listener surface_commit; struct wl_listener new_popup; + struct wl_listener reposition; struct wl_listener destroy; }; diff --git a/sway/commands/output.c b/sway/commands/output.c index 462dffd23..5e5d31b3e 100644 --- a/sway/commands/output.c +++ b/sway/commands/output.c @@ -103,13 +103,13 @@ struct cmd_results *cmd_output(int argc, char **argv) { bool background = output->background; - output = store_output_config(output); + store_output_config(output); // If reloading, the output configs will be applied after reading the // entire config and before the deferred commands so that an auto generated // workspace name is not given to re-enabled outputs. if (!config->reloading && !config->validating) { - apply_output_config_to_outputs(output); + apply_all_output_configs(); if (background) { if (!spawn_swaybg()) { return cmd_results_new(CMD_FAILURE, diff --git a/sway/commands/output/toggle.c b/sway/commands/output/toggle.c index 6342d526b..c6b72845b 100644 --- a/sway/commands/output/toggle.c +++ b/sway/commands/output/toggle.c @@ -29,7 +29,7 @@ struct cmd_results *output_cmd_toggle(int argc, char **argv) { config->handler_context.output_config->enabled = 1; } - free(oc); + free_output_config(oc); config->handler_context.leftovers.argc = argc; config->handler_context.leftovers.argv = argv; return NULL; diff --git a/sway/config.c b/sway/config.c index 568c8b534..f9131e0f7 100644 --- a/sway/config.c +++ b/sway/config.c @@ -37,19 +37,26 @@ struct sway_config *config = NULL; static struct xkb_state *keysym_translation_state_create( - struct xkb_rule_names rules) { - struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_SECURE_GETENV); + struct xkb_rule_names rules, uint32_t context_flags) { + struct xkb_context *context = xkb_context_new(context_flags | XKB_CONTEXT_NO_SECURE_GETENV); struct xkb_keymap *xkb_keymap = xkb_keymap_new_from_names( context, &rules, XKB_KEYMAP_COMPILE_NO_FLAGS); - xkb_context_unref(context); + if (xkb_keymap == NULL) { + sway_log(SWAY_ERROR, "Failed to compile keysym translation XKB keymap"); + return NULL; + } + return xkb_state_new(xkb_keymap); } static void keysym_translation_state_destroy( struct xkb_state *state) { + if (state == NULL) { + return; + } xkb_keymap_unref(xkb_state_get_keymap(state)); xkb_state_unref(state); } @@ -337,8 +344,14 @@ static void config_defaults(struct sway_config *config) { // The keysym to keycode translation struct xkb_rule_names rules = {0}; - config->keysym_translation_state = - keysym_translation_state_create(rules); + config->keysym_translation_state = keysym_translation_state_create(rules, 0); + if (config->keysym_translation_state == NULL) { + config->keysym_translation_state = keysym_translation_state_create(rules, + XKB_CONTEXT_NO_ENVIRONMENT_NAMES); + } + if (config->keysym_translation_state == NULL) { + goto cleanup; + } return; cleanup: @@ -519,7 +532,7 @@ bool load_main_config(const char *file, bool is_active, bool validating) { } sway_switch_retrigger_bindings_for_all(); - reset_outputs(); + apply_all_output_configs(); spawn_swaybg(); config->reloading = false; @@ -985,8 +998,12 @@ void translate_keysyms(struct input_config *input_config) { struct xkb_rule_names rules = {0}; input_config_fill_rule_names(input_config, &rules); - config->keysym_translation_state = - keysym_translation_state_create(rules); + config->keysym_translation_state = keysym_translation_state_create(rules, 0); + if (config->keysym_translation_state == NULL) { + sway_log(SWAY_ERROR, "Failed to create keysym translation XKB state " + "for device '%s'", input_config->identifier); + return; + } for (int i = 0; i < config->modes->length; ++i) { struct sway_mode *mode = config->modes->items[i]; diff --git a/sway/config/output.c b/sway/config/output.c index 557797710..3f1c3126b 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -9,6 +9,7 @@ #include #include #include +#include #include "sway/config.h" #include "sway/input/cursor.h" #include "sway/output.h" @@ -78,7 +79,7 @@ struct output_config *new_output_config(const char *name) { return oc; } -void merge_output_config(struct output_config *dst, struct output_config *src) { +static void merge_output_config(struct output_config *dst, struct output_config *src) { if (src->enabled != -1) { dst->enabled = src->enabled; } @@ -503,31 +504,12 @@ static void queue_output_config(struct output_config *oc, } } -bool apply_output_config(struct output_config *oc, struct sway_output *output) { +static bool finalize_output_config(struct output_config *oc, struct sway_output *output) { if (output == root->fallback_output) { return false; } struct wlr_output *wlr_output = output->wlr_output; - - // Flag to prevent the output mode event handler from calling us - output->enabling = (!oc || oc->enabled); - - struct wlr_output_state pending = {0}; - queue_output_config(oc, output, &pending); - - sway_log(SWAY_DEBUG, "Committing output %s", wlr_output->name); - if (!wlr_output_commit_state(wlr_output, &pending)) { - // Failed to commit output changes, maybe the output is missing a CRTC. - // Leave the output disabled for now and try again when the output gets - // the mode we asked for. - sway_log(SWAY_ERROR, "Failed to commit output %s", wlr_output->name); - output->enabling = false; - return false; - } - - output->enabling = false; - if (oc && !oc->enabled) { sway_log(SWAY_DEBUG, "Disabling output %s", oc->name); if (output->enabled) { @@ -583,25 +565,9 @@ bool apply_output_config(struct output_config *oc, struct sway_output *output) { output->max_render_time = oc->max_render_time; } - // Reconfigure all devices, since input config may have been applied before - // this output came online, and some config items (like map_to_output) are - // dependent on an output being present. - input_manager_configure_all_input_mappings(); - // Reconfigure the cursor images, since the scale may have changed. - input_manager_configure_xcursor(); return true; } -bool test_output_config(struct output_config *oc, struct sway_output *output) { - if (output == root->fallback_output) { - return false; - } - - struct wlr_output_state pending = {0}; - queue_output_config(oc, output, &pending); - return wlr_output_test_state(output->wlr_output, &pending); -} - static void default_output_config(struct output_config *oc, struct wlr_output *wlr_output) { oc->enabled = 1; @@ -711,50 +677,124 @@ struct output_config *find_output_config(struct sway_output *output) { return get_output_config(id, output); } -void apply_output_config_to_outputs(struct output_config *oc) { - // Try to find the output container and apply configuration now. If - // this is during startup then there will be no container and config - // will be applied during normal "new output" event from wlroots. - bool wildcard = strcmp(oc->name, "*") == 0; - struct sway_output *sway_output, *tmp; - wl_list_for_each_safe(sway_output, tmp, &root->all_outputs, link) { - if (output_match_name_or_id(sway_output, oc->name)) { - char id[128]; - output_get_identifier(id, sizeof(id), sway_output); - struct output_config *current = get_output_config(id, sway_output); - if (!current) { - // No stored output config matched, apply oc directly - sway_log(SWAY_DEBUG, "Applying oc directly"); - current = new_output_config(oc->name); - merge_output_config(current, oc); - } - apply_output_config(current, sway_output); - free_output_config(current); +bool apply_output_configs(struct matched_output_config *configs, + size_t configs_len, bool test_only) { + struct wlr_backend_output_state *states = calloc(configs_len, sizeof(*states)); + if (!states) { + return false; + } - if (!wildcard) { - // Stop looking if the output config isn't applicable to all - // outputs - break; - } + sway_log(SWAY_DEBUG, "Committing %zd outputs", configs_len); + for (size_t idx = 0; idx < configs_len; idx++) { + struct matched_output_config *cfg = &configs[idx]; + struct wlr_backend_output_state *backend_state = &states[idx]; + + backend_state->output = cfg->output->wlr_output; + wlr_output_state_init(&backend_state->base); + + sway_log(SWAY_DEBUG, "Preparing config for %s", + cfg->output->wlr_output->name); + queue_output_config(cfg->config, cfg->output, &backend_state->base); + } + + struct wlr_output_swapchain_manager swapchain_mgr; + wlr_output_swapchain_manager_init(&swapchain_mgr, server.backend); + + bool ok = wlr_output_swapchain_manager_prepare(&swapchain_mgr, states, configs_len); + if (!ok) { + sway_log(SWAY_ERROR, "Swapchain prepare failed"); + goto out; + } + + if (test_only) { + // The swapchain manager already did a test for us + goto out; + } + + for (size_t idx = 0; idx < configs_len; idx++) { + struct matched_output_config *cfg = &configs[idx]; + struct wlr_backend_output_state *backend_state = &states[idx]; + + struct wlr_scene_output_state_options opts = { + .swapchain = wlr_output_swapchain_manager_get_swapchain( + &swapchain_mgr, backend_state->output), + }; + struct wlr_scene_output *scene_output = cfg->output->scene_output; + struct wlr_output_state *state = &backend_state->base; + if (!wlr_scene_output_build_state(scene_output, state, &opts)) { + sway_log(SWAY_ERROR, "Building output state for '%s' failed", + backend_state->output->name); + goto out; } } + ok = wlr_backend_commit(server.backend, states, configs_len); + if (!ok) { + sway_log(SWAY_ERROR, "Backend commit failed"); + goto out; + } + + sway_log(SWAY_DEBUG, "Commit of %zd outputs succeeded", configs_len); + + wlr_output_swapchain_manager_apply(&swapchain_mgr); + + for (size_t idx = 0; idx < configs_len; idx++) { + struct matched_output_config *cfg = &configs[idx]; + sway_log(SWAY_DEBUG, "Finalizing config for %s", + cfg->output->wlr_output->name); + finalize_output_config(cfg->config, cfg->output); + } + +out: + wlr_output_swapchain_manager_finish(&swapchain_mgr); + for (size_t idx = 0; idx < configs_len; idx++) { + struct wlr_backend_output_state *backend_state = &states[idx]; + wlr_output_state_finish(&backend_state->base); + } + free(states); + + // Reconfigure all devices, since input config may have been applied before + // this output came online, and some config items (like map_to_output) are + // dependent on an output being present. + input_manager_configure_all_input_mappings(); + // Reconfigure the cursor images, since the scale may have changed. + input_manager_configure_xcursor(); + struct sway_seat *seat; wl_list_for_each(seat, &server.input->seats, link) { wlr_seat_pointer_notify_clear_focus(seat->wlr_seat); cursor_rebase(seat->cursor); } + + return ok; } -void reset_outputs(void) { - struct output_config *oc = NULL; - int i = list_seq_find(config->output_configs, output_name_cmp, "*"); - if (i >= 0) { - oc = config->output_configs->items[i]; - } else { - oc = store_output_config(new_output_config("*")); +void apply_all_output_configs(void) { + size_t configs_len = wl_list_length(&root->all_outputs); + struct matched_output_config *configs = calloc(configs_len, sizeof(*configs)); + if (!configs) { + return; } - apply_output_config_to_outputs(oc); + + int config_idx = 0; + struct sway_output *sway_output; + wl_list_for_each(sway_output, &root->all_outputs, link) { + if (sway_output == root->fallback_output) { + configs_len--; + continue; + } + + struct matched_output_config *config = &configs[config_idx++]; + config->output = sway_output; + config->config = find_output_config(sway_output); + } + + apply_output_configs(configs, configs_len, false); + for (size_t idx = 0; idx < configs_len; idx++) { + struct matched_output_config *cfg = &configs[idx]; + free_output_config(cfg->config); + } + free(configs); } void free_output_config(struct output_config *oc) { diff --git a/sway/desktop/output.c b/sway/desktop/output.c index 600423bcf..bd3de3fe1 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -183,7 +183,15 @@ static void send_frame_done_iterator(struct wlr_scene_buffer *buffer, } } -static enum wlr_scale_filter_mode get_scale_filter(struct sway_output *output) { +static enum wlr_scale_filter_mode get_scale_filter(struct sway_output *output, + struct wlr_scene_buffer *buffer) { + // if we are scaling down, we should always choose linear + if (buffer->dst_width > 0 && buffer->dst_height > 0 && ( + buffer->dst_width < buffer->buffer_width || + buffer->dst_height < buffer->buffer_height)) { + return WLR_SCALE_FILTER_BILINEAR; + } + switch (output->scale_filter) { case SCALE_FILTER_LINEAR: return WLR_SCALE_FILTER_BILINEAR; @@ -212,7 +220,7 @@ static void output_configure_scene(struct sway_output *output, // hack: don't call the scene setter because that will damage all outputs // We don't want to damage outputs that aren't our current output that // we're configuring - buffer->filter_mode = get_scale_filter(output); + buffer->filter_mode = get_scale_filter(output, buffer); wlr_scene_buffer_set_opacity(buffer, opacity); } else if (node->type == WLR_SCENE_NODE_TREE) { @@ -513,9 +521,7 @@ void handle_new_output(struct wl_listener *listener, void *data) { sway_session_lock_add_output(server->session_lock.lock, output); } - struct output_config *oc = find_output_config(output); - apply_output_config(oc, output); - free_output_config(oc); + apply_all_output_configs(); transaction_commit_dirty(); @@ -544,63 +550,88 @@ void handle_gamma_control_set_gamma(struct wl_listener *listener, void *data) { wlr_output_schedule_frame(output->wlr_output); } +static struct output_config *output_config_for_config_head( + struct wlr_output_configuration_head_v1 *config_head, + struct sway_output *output) { + struct output_config *oc = new_output_config(output->wlr_output->name); + oc->enabled = config_head->state.enabled; + if (!oc->enabled) { + return oc; + } + + if (config_head->state.mode != NULL) { + struct wlr_output_mode *mode = config_head->state.mode; + oc->width = mode->width; + oc->height = mode->height; + oc->refresh_rate = mode->refresh / 1000.f; + } else { + oc->width = config_head->state.custom_mode.width; + oc->height = config_head->state.custom_mode.height; + oc->refresh_rate = + config_head->state.custom_mode.refresh / 1000.f; + } + oc->x = config_head->state.x; + oc->y = config_head->state.y; + oc->transform = config_head->state.transform; + oc->scale = config_head->state.scale; + oc->adaptive_sync = config_head->state.adaptive_sync_enabled; + return oc; +} + static void output_manager_apply(struct sway_server *server, struct wlr_output_configuration_v1 *config, bool test_only) { - // TODO: perform atomic tests on the whole backend atomically + size_t configs_len = wl_list_length(&root->all_outputs); + struct matched_output_config *configs = calloc(configs_len, sizeof(*configs)); + if (!configs) { + return; + } - struct wlr_output_configuration_head_v1 *config_head; - // First disable outputs we need to disable - bool ok = true; - wl_list_for_each(config_head, &config->heads, link) { - struct wlr_output *wlr_output = config_head->state.output; - struct sway_output *output = wlr_output->data; - if (!output->enabled || config_head->state.enabled) { + int config_idx = 0; + struct sway_output *sway_output; + wl_list_for_each(sway_output, &root->all_outputs, link) { + if (sway_output == root->fallback_output) { + configs_len--; continue; } - struct output_config *oc = new_output_config(output->wlr_output->name); - oc->enabled = false; - if (test_only) { - ok &= test_output_config(oc, output); - } else { - oc = store_output_config(oc); - ok &= apply_output_config(oc, output); + struct matched_output_config *cfg = &configs[config_idx++]; + cfg->output = sway_output; + + struct wlr_output_configuration_head_v1 *config_head; + wl_list_for_each(config_head, &config->heads, link) { + if (config_head->state.output == sway_output->wlr_output) { + cfg->config = output_config_for_config_head(config_head, sway_output); + break; + } + } + if (!cfg->config) { + cfg->config = find_output_config(sway_output); } } - // Then enable outputs that need to - wl_list_for_each(config_head, &config->heads, link) { - struct wlr_output *wlr_output = config_head->state.output; - struct sway_output *output = wlr_output->data; - if (!config_head->state.enabled) { - continue; - } - struct output_config *oc = new_output_config(output->wlr_output->name); - oc->enabled = true; - if (config_head->state.mode != NULL) { - struct wlr_output_mode *mode = config_head->state.mode; - oc->width = mode->width; - oc->height = mode->height; - oc->refresh_rate = mode->refresh / 1000.f; - } else { - oc->width = config_head->state.custom_mode.width; - oc->height = config_head->state.custom_mode.height; - oc->refresh_rate = - config_head->state.custom_mode.refresh / 1000.f; - } - oc->x = config_head->state.x; - oc->y = config_head->state.y; - oc->transform = config_head->state.transform; - oc->scale = config_head->state.scale; - oc->adaptive_sync = config_head->state.adaptive_sync_enabled; + bool ok = apply_output_configs(configs, configs_len, test_only); + for (size_t idx = 0; idx < configs_len; idx++) { + struct matched_output_config *cfg = &configs[idx]; - if (test_only) { - ok &= test_output_config(oc, output); + // Only store new configs for successful non-test commits. Old configs, + // test-only and failed commits just get freed. + bool store_config = false; + if (!test_only && ok) { + struct wlr_output_configuration_head_v1 *config_head; + wl_list_for_each(config_head, &config->heads, link) { + if (config_head->state.output == sway_output->wlr_output) { + store_config = true; + break; + } + } + } + if (store_config) { + store_output_config(cfg->config); } else { - oc = store_output_config(oc); - ok &= apply_output_config(oc, output); + free_output_config(cfg->config); } } + free(configs); if (ok) { wlr_output_configuration_v1_send_succeeded(config); @@ -644,6 +675,6 @@ void handle_output_power_manager_set_mode(struct wl_listener *listener, oc->power = 1; break; } - oc = store_output_config(oc); - apply_output_config(oc, output); + store_output_config(oc); + apply_all_output_configs(); } diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c index 27a73f8a0..7c4178910 100644 --- a/sway/desktop/xdg_shell.c +++ b/sway/desktop/xdg_shell.c @@ -35,6 +35,7 @@ static void popup_handle_destroy(struct wl_listener *listener, void *data) { wl_list_remove(&popup->new_popup.link); wl_list_remove(&popup->destroy.link); wl_list_remove(&popup->surface_commit.link); + wl_list_remove(&popup->reposition.link); wlr_scene_node_destroy(&popup->scene_tree->node); free(popup); } @@ -70,6 +71,11 @@ static void popup_handle_surface_commit(struct wl_listener *listener, void *data } } +static void popup_handle_reposition(struct wl_listener *listener, void *data) { + struct sway_xdg_popup *popup = wl_container_of(listener, popup, reposition); + popup_unconstrain(popup); +} + static struct sway_xdg_popup *popup_create(struct wlr_xdg_popup *wlr_popup, struct sway_view *view, struct wlr_scene_tree *parent) { struct wlr_xdg_surface *xdg_surface = wlr_popup->base; @@ -116,6 +122,8 @@ static struct sway_xdg_popup *popup_create(struct wlr_xdg_popup *wlr_popup, popup->surface_commit.notify = popup_handle_surface_commit; wl_signal_add(&xdg_surface->events.new_popup, &popup->new_popup); popup->new_popup.notify = popup_handle_new_popup; + wl_signal_add(&wlr_popup->events.reposition, &popup->reposition); + popup->reposition.notify = popup_handle_reposition; wl_signal_add(&wlr_popup->events.destroy, &popup->destroy); popup->destroy.notify = popup_handle_destroy; @@ -281,6 +289,7 @@ static void handle_commit(struct wl_listener *listener, void *data) { } // XXX: https://github.com/swaywm/sway/issues/2176 wlr_xdg_surface_schedule_configure(xdg_surface); + // TODO: wlr_xdg_toplevel_set_bounds() return; } @@ -566,4 +575,7 @@ void handle_xdg_shell_toplevel(struct wl_listener *listener, void *data) { wlr_scene_xdg_surface_create(xdg_shell_view->view.content_tree, xdg_toplevel->base); xdg_toplevel->base->data = xdg_shell_view; + + wlr_xdg_toplevel_set_wm_capabilities(xdg_toplevel, + XDG_TOPLEVEL_WM_CAPABILITIES_FULLSCREEN); } diff --git a/sway/input/input-manager.c b/sway/input/input-manager.c index 056cc3ea8..248ca34ee 100644 --- a/sway/input/input-manager.c +++ b/sway/input/input-manager.c @@ -2,7 +2,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -65,8 +67,15 @@ struct sway_seat *input_manager_sway_seat_from_wlr_seat(struct wlr_seat *wlr_sea } char *input_device_get_identifier(struct wlr_input_device *device) { - int vendor = device->vendor; - int product = device->product; + int vendor = 0, product = 0; +#if WLR_HAS_LIBINPUT_BACKEND + if (wlr_input_device_is_libinput(device)) { + struct libinput_device *libinput_dev = wlr_libinput_get_device_handle(device); + vendor = libinput_device_get_id_vendor(libinput_dev); + product = libinput_device_get_id_product(libinput_dev); + } +#endif + char *name = strdup(device->name ? device->name : ""); strip_whitespace(name); @@ -424,6 +433,20 @@ void handle_virtual_pointer(struct wl_listener *listener, void *data) { } } +static void handle_transient_seat_manager_create_seat( + struct wl_listener *listener, void *data) { + struct wlr_transient_seat_v1 *transient_seat = data; + static uint64_t i; + char name[256]; + snprintf(name, sizeof(name), "transient-%"PRIx64, i++); + struct sway_seat *seat = seat_create(name); + if (seat && seat->wlr_seat) { + wlr_transient_seat_v1_ready(transient_seat, seat->wlr_seat); + } else { + wlr_transient_seat_v1_deny(transient_seat); + } +} + struct sway_input_manager *input_manager_create(struct sway_server *server) { struct sway_input_manager *input = calloc(1, sizeof(struct sway_input_manager)); @@ -459,6 +482,15 @@ struct sway_input_manager *input_manager_create(struct sway_server *server) { input->pointer_gestures = wlr_pointer_gestures_v1_create(server->wl_display); + input->transient_seat_manager = + wlr_transient_seat_manager_v1_create(server->wl_display); + assert(input->transient_seat_manager); + + input->transient_seat_create.notify = + handle_transient_seat_manager_create_seat; + wl_signal_add(&input->transient_seat_manager->events.create_seat, + &input->transient_seat_create); + return input; } diff --git a/sway/input/seat.c b/sway/input/seat.c index f24868935..0c5672bca 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -67,6 +67,12 @@ static void seat_node_destroy(struct sway_seat_node *seat_node) { } void seat_destroy(struct sway_seat *seat) { + wlr_seat_destroy(seat->wlr_seat); +} + +static void handle_seat_destroy(struct wl_listener *listener, void *data) { + struct sway_seat *seat = wl_container_of(listener, seat, destroy); + if (seat == config->handler_context.seat) { config->handler_context.seat = input_manager_get_default_seat(); } @@ -87,7 +93,7 @@ void seat_destroy(struct sway_seat *seat) { wl_list_remove(&seat->request_set_selection.link); wl_list_remove(&seat->request_set_primary_selection.link); wl_list_remove(&seat->link); - wlr_seat_destroy(seat->wlr_seat); + wl_list_remove(&seat->destroy.link); for (int i = 0; i < seat->deferred_bindings->length; i++) { free_sway_binding(seat->deferred_bindings->items[i]); } @@ -534,6 +540,9 @@ struct sway_seat *seat_create(const char *seat_name) { return NULL; } + seat->destroy.notify = handle_seat_destroy; + wl_signal_add(&seat->wlr_seat->events.destroy, &seat->destroy); + seat->idle_inhibit_sources = seat->idle_wake_sources = IDLE_SOURCE_KEYBOARD | IDLE_SOURCE_POINTER | diff --git a/sway/input/seatop_down.c b/sway/input/seatop_down.c index 35fd3bcb2..340e334bf 100644 --- a/sway/input/seatop_down.c +++ b/sway/input/seatop_down.c @@ -117,7 +117,11 @@ static void handle_touch_cancel(struct sway_seat *seat, } if (e->surface) { - wlr_seat_touch_notify_cancel(seat->wlr_seat, e->surface); + struct wl_client *client = wl_resource_get_client(e->surface->resource); + struct wlr_seat_client *seat_client = wlr_seat_client_for_wl_client(seat->wlr_seat, client); + if (seat_client != NULL) { + wlr_seat_touch_notify_cancel(seat->wlr_seat, seat_client); + } } if (wl_list_empty(&e->point_events)) { diff --git a/sway/ipc-json.c b/sway/ipc-json.c index dfbb7a6ec..81ca34831 100644 --- a/sway/ipc-json.c +++ b/sway/ipc-json.c @@ -1097,10 +1097,6 @@ json_object *ipc_json_describe_input(struct sway_input_device *device) { json_object_new_string(device->identifier)); json_object_object_add(object, "name", json_object_new_string(device->wlr_device->name)); - json_object_object_add(object, "vendor", - json_object_new_int(device->wlr_device->vendor)); - json_object_object_add(object, "product", - json_object_new_int(device->wlr_device->product)); json_object_object_add(object, "type", json_object_new_string( input_device_get_type(device))); @@ -1154,6 +1150,10 @@ json_object *ipc_json_describe_input(struct sway_input_device *device) { libinput_dev = wlr_libinput_get_device_handle(device->wlr_device); json_object_object_add(object, "libinput", describe_libinput_device(libinput_dev)); + json_object_object_add(object, "vendor", + json_object_new_int(libinput_device_get_id_vendor(libinput_dev))); + json_object_object_add(object, "product", + json_object_new_int(libinput_device_get_id_product(libinput_dev))); } #endif diff --git a/sway/server.c b/sway/server.c index 684b1dbde..d159dc9bd 100644 --- a/sway/server.c +++ b/sway/server.c @@ -65,7 +65,7 @@ #include #endif -#define SWAY_XDG_SHELL_VERSION 2 +#define SWAY_XDG_SHELL_VERSION 5 #define SWAY_LAYER_SHELL_VERSION 4 #define SWAY_FOREIGN_TOPLEVEL_LIST_VERSION 1 @@ -112,7 +112,8 @@ static bool is_privileged(const struct wl_global *global) { global == server.session_lock.manager->global || global == server.input->keyboard_shortcuts_inhibit->global || global == server.input->virtual_keyboard->global || - global == server.input->virtual_pointer->global; + global == server.input->virtual_pointer->global || + global == server.input->transient_seat_manager->global; } static bool filter_global(const struct wl_client *client, @@ -172,6 +173,45 @@ static void detect_proprietary(struct wlr_backend *backend, void *data) { drmFreeVersion(version); } +static void handle_renderer_lost(struct wl_listener *listener, void *data) { + struct sway_server *server = wl_container_of(listener, server, renderer_lost); + + sway_log(SWAY_INFO, "Re-creating renderer after GPU reset"); + + struct wlr_renderer *renderer = wlr_renderer_autocreate(server->backend); + if (renderer == NULL) { + sway_log(SWAY_ERROR, "Unable to create renderer"); + return; + } + + struct wlr_allocator *allocator = + wlr_allocator_autocreate(server->backend, renderer); + if (allocator == NULL) { + sway_log(SWAY_ERROR, "Unable to create allocator"); + wlr_renderer_destroy(renderer); + return; + } + + struct wlr_renderer *old_renderer = server->renderer; + struct wlr_allocator *old_allocator = server->allocator; + server->renderer = renderer; + server->allocator = allocator; + + wl_list_remove(&server->renderer_lost.link); + wl_signal_add(&server->renderer->events.lost, &server->renderer_lost); + + wlr_compositor_set_renderer(server->compositor, renderer); + + for (int i = 0; i < root->outputs->length; ++i) { + struct sway_output *output = root->outputs->items[i]; + wlr_output_init_render(output->wlr_output, + server->allocator, server->renderer); + } + + wlr_allocator_destroy(old_allocator); + wlr_renderer_destroy(old_renderer); +} + bool server_init(struct sway_server *server) { sway_log(SWAY_DEBUG, "Initializing Wayland server"); server->wl_display = wl_display_create(); @@ -195,6 +235,9 @@ bool server_init(struct sway_server *server) { return false; } + server->renderer_lost.notify = handle_renderer_lost; + wl_signal_add(&server->renderer->events.lost, &server->renderer_lost); + wlr_renderer_init_wl_shm(server->renderer, server->wl_display); if (wlr_renderer_get_dmabuf_texture_formats(server->renderer) != NULL) { @@ -399,6 +442,7 @@ void server_fini(struct sway_server *server) { wlr_xwayland_destroy(server->xwayland.wlr_xwayland); #endif wl_display_destroy_clients(server->wl_display); + wlr_backend_destroy(server->backend); wl_display_destroy(server->wl_display); list_free(server->dirty_nodes); } diff --git a/sway/sway-ipc.7.scd b/sway/sway-ipc.7.scd index c9895e52c..2f6972489 100644 --- a/sway/sway-ipc.7.scd +++ b/sway/sway-ipc.7.scd @@ -1046,7 +1046,7 @@ An object with a single string property containing the contents of the config *Example Reply:* ``` { - "config": "set $mod Mod4\nbindsym $mod+q exit\n" + "config": "set $mod Mod4\\nbindsym $mod+q exit\\n" } ``` diff --git a/sway/sway-output.5.scd b/sway/sway-output.5.scd index 028cb7abc..7d088d5db 100644 --- a/sway/sway-output.5.scd +++ b/sway/sway-output.5.scd @@ -72,13 +72,11 @@ must be separated by one space. For example: *output* scale Scales the specified output by the specified scale _factor_. An integer is - recommended, but fractional values are also supported. If a fractional - value are specified, be warned that it is not possible to faithfully - represent the contents of your windows - they will be rendered at the next - highest integer scale factor and downscaled. You may be better served by - setting an integer scale factor and adjusting the font size of your - applications to taste. HiDPI isn't supported with Xwayland clients (windows - will blur). + recommended, but fractional values are also supported. You may be better + served by setting an integer scale factor and adjusting the font size of + your applications to taste. HiDPI isn't supported with Xwayland clients + (windows will blur). A fractional scale may be slightly adjusted to match + requirements of the protocol. *output* scale_filter linear|nearest|smart Indicates how to scale application buffers that are rendered at a scale diff --git a/sway/sway.5.scd b/sway/sway.5.scd index 7e58b5286..f73db3ba3 100644 --- a/sway/sway.5.scd +++ b/sway/sway.5.scd @@ -400,6 +400,12 @@ runtime. only be available for that group. By default, if you overwrite a binding, swaynag will give you a warning. To silence this, use the _--no-warn_ flag. + For specifying modifier keys, you can use the XKB modifier names _Shift_, + _Lock_ (for Caps Lock), _Control_, _Mod1_ (for Alt), _Mod2_ (for Num Lock), + _Mod3_ (for XKB modifier Mod3), _Mod4_ (for the Logo key), and _Mod5_ (for + AltGr). In addition, you can use the aliases _Ctrl_ (for Control) and _Alt_ + (for Alt). + Unless the flag _--locked_ is set, the command will not be run when a screen locking program is active. If there is a matching binding with and without _--locked_, the one with will be preferred when locked and the