From e4bfb3bc98b28cb083b4138a76d88384a33d6e57 Mon Sep 17 00:00:00 2001 From: Dominique Martinet Date: Wed, 27 Jun 2018 18:16:49 +0900 Subject: Add idle inhibit unstable v1 support --- include/sway/desktop/idle_inhibit_v1.h | 13 +++++++++++++ include/sway/server.h | 5 +++++ 2 files changed, 18 insertions(+) create mode 100644 include/sway/desktop/idle_inhibit_v1.h (limited to 'include/sway') diff --git a/include/sway/desktop/idle_inhibit_v1.h b/include/sway/desktop/idle_inhibit_v1.h new file mode 100644 index 00000000..94c25a42 --- /dev/null +++ b/include/sway/desktop/idle_inhibit_v1.h @@ -0,0 +1,13 @@ + +#ifndef _SWAY_DESKTOP_IDLE_INHIBIT_V1_H +#define _SWAY_DESKTOP_IDLE_INHIBIT_V1_H +#include +#include "sway/server.h" + +struct sway_idle_inhibitor_v1 { + struct sway_server *server; + + struct wl_listener destroy; +}; + +#endif diff --git a/include/sway/server.h b/include/sway/server.h index 1e1aa3cc..246a9381 100644 --- a/include/sway/server.h +++ b/include/sway/server.h @@ -24,11 +24,15 @@ struct sway_server { struct wlr_compositor *compositor; struct wlr_data_device_manager *data_device_manager; struct wlr_idle *idle; + struct wlr_idle_inhibit_manager_v1 *idle_inhibit; struct sway_input_manager *input; struct wl_listener new_output; + struct wlr_idle_inhibit_manager_v1 *idle_inhibit_v1; + struct wl_listener new_idle_inhibitor_v1; + struct wlr_layer_shell *layer_shell; struct wl_listener layer_shell_surface; @@ -61,6 +65,7 @@ void server_run(struct sway_server *server); void handle_new_output(struct wl_listener *listener, void *data); +void handle_idle_inhibitor_v1(struct wl_listener *listener, void *data); void handle_layer_shell_surface(struct wl_listener *listener, void *data); void handle_xdg_shell_v6_surface(struct wl_listener *listener, void *data); void handle_xdg_shell_surface(struct wl_listener *listener, void *data); -- cgit v1.2.3-70-g09d2 From 072b334abc6f065080bf944767bbd53d7a590e47 Mon Sep 17 00:00:00 2001 From: Dominique Martinet Date: Sat, 30 Jun 2018 14:09:32 +0900 Subject: idle_inhibit: stop inhibitor when views become invisible --- include/sway/desktop/idle_inhibit_v1.h | 4 ++++ include/sway/server.h | 1 + sway/desktop/idle_inhibit_v1.c | 22 ++++++++++++++++++++++ sway/desktop/transaction.c | 3 +++ sway/server.c | 1 + 5 files changed, 31 insertions(+) (limited to 'include/sway') diff --git a/include/sway/desktop/idle_inhibit_v1.h b/include/sway/desktop/idle_inhibit_v1.h index 94c25a42..1764713c 100644 --- a/include/sway/desktop/idle_inhibit_v1.h +++ b/include/sway/desktop/idle_inhibit_v1.h @@ -6,8 +6,12 @@ struct sway_idle_inhibitor_v1 { struct sway_server *server; + struct sway_view *view; + struct wl_list link; struct wl_listener destroy; }; +void idle_inhibit_v1_check_active(struct sway_server *server); + #endif diff --git a/include/sway/server.h b/include/sway/server.h index 246a9381..693e6b82 100644 --- a/include/sway/server.h +++ b/include/sway/server.h @@ -32,6 +32,7 @@ struct sway_server { struct wlr_idle_inhibit_manager_v1 *idle_inhibit_v1; struct wl_listener new_idle_inhibitor_v1; + struct wl_list idle_inhibitors_v1; struct wlr_layer_shell *layer_shell; struct wl_listener layer_shell_surface; diff --git a/sway/desktop/idle_inhibit_v1.c b/sway/desktop/idle_inhibit_v1.c index a06e00d5..a9a8cb24 100644 --- a/sway/desktop/idle_inhibit_v1.c +++ b/sway/desktop/idle_inhibit_v1.c @@ -2,6 +2,7 @@ #include #include "log.h" #include "sway/desktop/idle_inhibit_v1.h" +#include "sway/tree/view.h" #include "sway/server.h" @@ -10,6 +11,7 @@ static void handle_destroy(struct wl_listener *listener, void *data) { wl_container_of(listener, inhibitor, destroy); wlr_log(L_DEBUG, "Sway idle inhibitor destroyed"); wlr_idle_set_enabled(inhibitor->server->idle, NULL, true); + wl_list_remove(&inhibitor->link); wl_list_remove(&inhibitor->destroy.link); free(inhibitor); } @@ -27,9 +29,29 @@ void handle_idle_inhibitor_v1(struct wl_listener *listener, void *data) { } inhibitor->server = server; + inhibitor->view = view_from_wlr_surface(wlr_inhibitor->surface); + wl_list_insert(&server->idle_inhibitors_v1, &inhibitor->link); + inhibitor->destroy.notify = handle_destroy; wl_signal_add(&wlr_inhibitor->events.destroy, &inhibitor->destroy); wlr_idle_set_enabled(server->idle, NULL, false); } + +void idle_inhibit_v1_check_active(struct sway_server *server) { + struct sway_idle_inhibitor_v1 *inhibitor; + bool inhibited = false; + wl_list_for_each(inhibitor, &server->idle_inhibitors_v1, link) { + if (!inhibitor->view) { + /* Cannot guess if view is visible so assume it is */ + inhibited = true; + break; + } + if (view_is_visible(inhibitor->view)) { + inhibited = true; + break; + } + } + wlr_idle_set_enabled(server->idle, NULL, !inhibited); +} diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index d2932c87..7050d70c 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -6,6 +6,7 @@ #include #include #include "sway/debug.h" +#include "sway/desktop/idle_inhibit_v1.h" #include "sway/desktop/transaction.h" #include "sway/output.h" #include "sway/tree/container.h" @@ -245,6 +246,7 @@ static void transaction_progress_queue() { transaction_destroy(transaction); } server.transactions->length = 0; + idle_inhibit_v1_check_active(&server); } static int handle_timeout(void *data) { @@ -320,6 +322,7 @@ void transaction_commit(struct sway_transaction *transaction) { wlr_log(L_DEBUG, "Transaction %p has nothing to wait for", transaction); transaction_apply(transaction); transaction_destroy(transaction); + idle_inhibit_v1_check_active(&server); return; } diff --git a/sway/server.c b/sway/server.c index 3456931c..ee6b7cde 100644 --- a/sway/server.c +++ b/sway/server.c @@ -68,6 +68,7 @@ bool server_init(struct sway_server *server) { wl_signal_add(&server->idle_inhibit->events.new_inhibitor, &server->new_idle_inhibitor_v1); server->new_idle_inhibitor_v1.notify = handle_idle_inhibitor_v1; + wl_list_init(&server->idle_inhibitors_v1); server->layer_shell = wlr_layer_shell_create(server->wl_display); wl_signal_add(&server->layer_shell->events.new_surface, -- cgit v1.2.3-70-g09d2 From 71224781c48f98f43f5836de663ef6e01604419c Mon Sep 17 00:00:00 2001 From: Dominique Martinet Date: Mon, 2 Jul 2018 09:26:57 +0900 Subject: idle_inhibit: move server data to its own struct --- include/sway/desktop/idle_inhibit_v1.h | 17 ++++++++++++--- include/sway/server.h | 7 ++---- sway/desktop/idle_inhibit_v1.c | 40 ++++++++++++++++++++++++++-------- sway/desktop/transaction.c | 4 ++-- sway/server.c | 11 ++++------ 5 files changed, 53 insertions(+), 26 deletions(-) (limited to 'include/sway') diff --git a/include/sway/desktop/idle_inhibit_v1.h b/include/sway/desktop/idle_inhibit_v1.h index 1764713c..e5ed8a3d 100644 --- a/include/sway/desktop/idle_inhibit_v1.h +++ b/include/sway/desktop/idle_inhibit_v1.h @@ -1,17 +1,28 @@ - #ifndef _SWAY_DESKTOP_IDLE_INHIBIT_V1_H #define _SWAY_DESKTOP_IDLE_INHIBIT_V1_H #include +#include #include "sway/server.h" +struct sway_idle_inhibit_manager_v1 { + struct wlr_idle_inhibit_manager_v1 *wlr_manager; + struct wl_listener new_idle_inhibitor_v1; + struct wl_list inhibitors; + + struct wlr_idle *idle; +}; + struct sway_idle_inhibitor_v1 { - struct sway_server *server; + struct sway_idle_inhibit_manager_v1 *manager; struct sway_view *view; struct wl_list link; struct wl_listener destroy; }; -void idle_inhibit_v1_check_active(struct sway_server *server); +void idle_inhibit_v1_check_active( + struct sway_idle_inhibit_manager_v1 *manager); +struct sway_idle_inhibit_manager_v1 *sway_idle_inhibit_manager_v1_create( + struct wl_display *wl_display, struct wlr_idle *idle); #endif diff --git a/include/sway/server.h b/include/sway/server.h index 693e6b82..a3e32898 100644 --- a/include/sway/server.h +++ b/include/sway/server.h @@ -23,16 +23,13 @@ struct sway_server { struct wlr_compositor *compositor; struct wlr_data_device_manager *data_device_manager; - struct wlr_idle *idle; - struct wlr_idle_inhibit_manager_v1 *idle_inhibit; struct sway_input_manager *input; struct wl_listener new_output; - struct wlr_idle_inhibit_manager_v1 *idle_inhibit_v1; - struct wl_listener new_idle_inhibitor_v1; - struct wl_list idle_inhibitors_v1; + struct wlr_idle *idle; + struct sway_idle_inhibit_manager_v1 *idle_inhibit_manager_v1; struct wlr_layer_shell *layer_shell; struct wl_listener layer_shell_surface; diff --git a/sway/desktop/idle_inhibit_v1.c b/sway/desktop/idle_inhibit_v1.c index a9a8cb24..c02ca26e 100644 --- a/sway/desktop/idle_inhibit_v1.c +++ b/sway/desktop/idle_inhibit_v1.c @@ -10,16 +10,16 @@ static void handle_destroy(struct wl_listener *listener, void *data) { struct sway_idle_inhibitor_v1 *inhibitor = wl_container_of(listener, inhibitor, destroy); wlr_log(L_DEBUG, "Sway idle inhibitor destroyed"); - wlr_idle_set_enabled(inhibitor->server->idle, NULL, true); wl_list_remove(&inhibitor->link); wl_list_remove(&inhibitor->destroy.link); + idle_inhibit_v1_check_active(inhibitor->manager); free(inhibitor); } void handle_idle_inhibitor_v1(struct wl_listener *listener, void *data) { struct wlr_idle_inhibitor_v1 *wlr_inhibitor = data; - struct sway_server *server = - wl_container_of(listener, server, new_idle_inhibitor_v1); + struct sway_idle_inhibit_manager_v1 *manager = + wl_container_of(listener, manager, new_idle_inhibitor_v1); wlr_log(L_DEBUG, "New sway idle inhibitor"); struct sway_idle_inhibitor_v1 *inhibitor = @@ -28,21 +28,22 @@ void handle_idle_inhibitor_v1(struct wl_listener *listener, void *data) { return; } - inhibitor->server = server; + inhibitor->manager = manager; inhibitor->view = view_from_wlr_surface(wlr_inhibitor->surface); - wl_list_insert(&server->idle_inhibitors_v1, &inhibitor->link); + wl_list_insert(&manager->inhibitors, &inhibitor->link); inhibitor->destroy.notify = handle_destroy; wl_signal_add(&wlr_inhibitor->events.destroy, &inhibitor->destroy); - wlr_idle_set_enabled(server->idle, NULL, false); + idle_inhibit_v1_check_active(manager); } -void idle_inhibit_v1_check_active(struct sway_server *server) { +void idle_inhibit_v1_check_active( + struct sway_idle_inhibit_manager_v1 *manager) { struct sway_idle_inhibitor_v1 *inhibitor; bool inhibited = false; - wl_list_for_each(inhibitor, &server->idle_inhibitors_v1, link) { + wl_list_for_each(inhibitor, &manager->inhibitors, link) { if (!inhibitor->view) { /* Cannot guess if view is visible so assume it is */ inhibited = true; @@ -53,5 +54,26 @@ void idle_inhibit_v1_check_active(struct sway_server *server) { break; } } - wlr_idle_set_enabled(server->idle, NULL, !inhibited); + wlr_idle_set_enabled(manager->idle, NULL, !inhibited); +} + +struct sway_idle_inhibit_manager_v1 *sway_idle_inhibit_manager_v1_create( + struct wl_display *wl_display, struct wlr_idle *idle) { + struct sway_idle_inhibit_manager_v1 *manager = + calloc(1, sizeof(struct sway_idle_inhibit_manager_v1)); + if (!manager) { + return NULL; + } + + manager->wlr_manager = wlr_idle_inhibit_v1_create(wl_display); + if (!manager->wlr_manager) { + return NULL; + } + manager->idle = idle; + wl_signal_add(&manager->wlr_manager->events.new_inhibitor, + &manager->new_idle_inhibitor_v1); + manager->new_idle_inhibitor_v1.notify = handle_idle_inhibitor_v1; + wl_list_init(&manager->inhibitors); + + return manager; } diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index 7050d70c..e5ceae61 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -246,7 +246,7 @@ static void transaction_progress_queue() { transaction_destroy(transaction); } server.transactions->length = 0; - idle_inhibit_v1_check_active(&server); + idle_inhibit_v1_check_active(server.idle_inhibit_manager_v1); } static int handle_timeout(void *data) { @@ -322,7 +322,7 @@ void transaction_commit(struct sway_transaction *transaction) { wlr_log(L_DEBUG, "Transaction %p has nothing to wait for", transaction); transaction_apply(transaction); transaction_destroy(transaction); - idle_inhibit_v1_check_active(&server); + idle_inhibit_v1_check_active(server.idle_inhibit_manager_v1); return; } diff --git a/sway/server.c b/sway/server.c index ee6b7cde..8106f3c8 100644 --- a/sway/server.c +++ b/sway/server.c @@ -10,7 +10,6 @@ #include #include #include -#include #include #include #include @@ -23,6 +22,7 @@ // TODO WLR: make Xwayland optional #include "list.h" #include "sway/config.h" +#include "sway/desktop/idle_inhibit_v1.h" #include "sway/input/input-manager.h" #include "sway/server.h" #include "sway/tree/layout.h" @@ -53,7 +53,6 @@ bool server_init(struct sway_server *server) { server->data_device_manager = wlr_data_device_manager_create(server->wl_display); - server->idle = wlr_idle_create(server->wl_display); wlr_screenshooter_create(server->wl_display); wlr_gamma_control_manager_create(server->wl_display); wlr_primary_selection_device_manager_create(server->wl_display); @@ -64,11 +63,9 @@ bool server_init(struct sway_server *server) { wlr_xdg_output_manager_create(server->wl_display, root_container.sway_root->output_layout); - server->idle_inhibit = wlr_idle_inhibit_v1_create(server->wl_display); - wl_signal_add(&server->idle_inhibit->events.new_inhibitor, - &server->new_idle_inhibitor_v1); - server->new_idle_inhibitor_v1.notify = handle_idle_inhibitor_v1; - wl_list_init(&server->idle_inhibitors_v1); + server->idle = wlr_idle_create(server->wl_display); + server->idle_inhibit_manager_v1 = + sway_idle_inhibit_manager_v1_create(server->wl_display, server->idle); server->layer_shell = wlr_layer_shell_create(server->wl_display); wl_signal_add(&server->layer_shell->events.new_surface, -- cgit v1.2.3-70-g09d2 From b0918b1058a704675fc2590ee9ff3bef6324f734 Mon Sep 17 00:00:00 2001 From: Dominique Martinet Date: Wed, 4 Jul 2018 14:45:35 +0900 Subject: ipc-server: add display destroy listener and remove ipc_terminate wl_event_source_remove() is illegal after display has been destroyed, so just destroy everything when we still can. ==20392==ERROR: AddressSanitizer: heap-use-after-free on address 0x607000001240 at pc 0x00000048e86e bp 0x7ffe4b557e00 sp 0x7ffe4b557df0 READ of size 8 at 0x607000001240 thread T0 #0 0x48e86d in wl_list_insert ../common/list.c:149 #1 0x7fdf673d4d7d in wl_event_source_remove src/event-loop.c:487 #2 0x41b742 in ipc_terminate ../sway/ipc-server.c:94 #3 0x40b1ad in main ../sway/main.c:440 #4 0x7fdf6664c18a in __libc_start_main ../csu/libc-start.c:308 #5 0x409359 in _start (/opt/wayland/bin/sway+0x409359) 0x607000001240 is located 48 bytes inside of 72-byte region [0x607000001210,0x607000001258) freed by thread T0 here: #0 0x7fdf692c4880 in __interceptor_free (/lib64/libasan.so.5+0xee880) #1 0x7fdf673d371a in wl_display_destroy src/wayland-server.c:1097 previously allocated by thread T0 here: #0 0x7fdf692c4c48 in malloc (/lib64/libasan.so.5+0xeec48) #1 0x7fdf673d4d9e in wl_event_loop_create src/event-loop.c:522 #2 0x40acb2 in main ../sway/main.c:363 #3 0x7fdf6664c18a in __libc_start_main ../csu/libc-start.c:308 --- include/sway/ipc-server.h | 2 -- sway/ipc-server.c | 34 ++++++++++++++++++++-------------- sway/main.c | 2 -- 3 files changed, 20 insertions(+), 18 deletions(-) (limited to 'include/sway') diff --git a/include/sway/ipc-server.h b/include/sway/ipc-server.h index dd16a175..026b5554 100644 --- a/include/sway/ipc-server.h +++ b/include/sway/ipc-server.h @@ -9,8 +9,6 @@ struct sway_server; void ipc_init(struct sway_server *server); -void ipc_terminate(void); - struct sockaddr_un *ipc_user_sockaddr(void); void ipc_event_workspace(struct sway_container *old, diff --git a/sway/ipc-server.c b/sway/ipc-server.c index 3e510c2e..abc2d7cb 100644 --- a/sway/ipc-server.c +++ b/sway/ipc-server.c @@ -31,6 +31,7 @@ static int ipc_socket = -1; static struct wl_event_source *ipc_event_source = NULL; static struct sockaddr_un *ipc_sockaddr = NULL; static list_t *ipc_client_list = NULL; +static struct wl_listener ipc_display_destroy; static const char ipc_magic[] = {'i', '3', '-', 'i', 'p', 'c'}; @@ -56,6 +57,22 @@ void ipc_client_disconnect(struct ipc_client *client); void ipc_client_handle_command(struct ipc_client *client); bool ipc_send_reply(struct ipc_client *client, const char *payload, uint32_t payload_length); +static void handle_display_destroy(struct wl_listener *listener, void *data) { + if (ipc_event_source) { + wl_event_source_remove(ipc_event_source); + } + close(ipc_socket); + unlink(ipc_sockaddr->sun_path); + + list_free(ipc_client_list); + + if (ipc_sockaddr) { + free(ipc_sockaddr); + } + + wl_list_remove(&ipc_display_destroy.link); +} + void ipc_init(struct sway_server *server) { ipc_socket = socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0); if (ipc_socket == -1) { @@ -85,24 +102,13 @@ void ipc_init(struct sway_server *server) { ipc_client_list = create_list(); + ipc_display_destroy.notify = handle_display_destroy; + wl_display_add_destroy_listener(server->wl_display, &ipc_display_destroy); + ipc_event_source = wl_event_loop_add_fd(server->wl_event_loop, ipc_socket, WL_EVENT_READABLE, ipc_handle_connection, server); } -void ipc_terminate(void) { - if (ipc_event_source) { - wl_event_source_remove(ipc_event_source); - } - close(ipc_socket); - unlink(ipc_sockaddr->sun_path); - - list_free(ipc_client_list); - - if (ipc_sockaddr) { - free(ipc_sockaddr); - } -} - struct sockaddr_un *ipc_user_sockaddr(void) { struct sockaddr_un *ipc_sockaddr = malloc(sizeof(struct sockaddr_un)); if (ipc_sockaddr == NULL) { diff --git a/sway/main.c b/sway/main.c index 8b0b8612..96e41bbc 100644 --- a/sway/main.c +++ b/sway/main.c @@ -437,8 +437,6 @@ int main(int argc, char **argv) { server_fini(&server); - ipc_terminate(); - if (config) { free_config(config); } -- cgit v1.2.3-70-g09d2 From fe72e3b349f0905519481b77b22c525aca9c704d Mon Sep 17 00:00:00 2001 From: Dominique Martinet Date: Thu, 5 Jul 2018 07:07:59 +0900 Subject: cmd_results_to_json: return copied string and properly free the json The only user of this function would copy the string right away to get rid of the const flag anyway, and freeing a const string afterwards might work but is not meant to be done according to the json-c API. --- include/sway/commands.h | 2 +- sway/commands.c | 8 ++++---- sway/ipc-server.c | 7 +++---- 3 files changed, 8 insertions(+), 9 deletions(-) (limited to 'include/sway') diff --git a/include/sway/commands.h b/include/sway/commands.h index 7ca0bda8..6d17144a 100644 --- a/include/sway/commands.h +++ b/include/sway/commands.h @@ -79,7 +79,7 @@ void free_cmd_results(struct cmd_results *results); * * Free the JSON string later on. */ -const char *cmd_results_to_json(struct cmd_results *results); +char *cmd_results_to_json(struct cmd_results *results); struct cmd_results *add_color(const char *name, char *buffer, const char *color); diff --git a/sway/commands.c b/sway/commands.c index 5b67e1ec..ef477f38 100644 --- a/sway/commands.c +++ b/sway/commands.c @@ -527,7 +527,7 @@ void free_cmd_results(struct cmd_results *results) { free(results); } -const char *cmd_results_to_json(struct cmd_results *results) { +char *cmd_results_to_json(struct cmd_results *results) { json_object *result_array = json_object_new_array(); json_object *root = json_object_new_object(); json_object_object_add(root, "success", @@ -542,9 +542,9 @@ const char *cmd_results_to_json(struct cmd_results *results) { } json_object_array_add(result_array, root); const char *json = json_object_to_json_string(result_array); - free(result_array); - free(root); - return json; + char *res = strdup(json); + json_object_put(result_array); + return res; } /** diff --git a/sway/ipc-server.c b/sway/ipc-server.c index 01b80b05..96889b39 100644 --- a/sway/ipc-server.c +++ b/sway/ipc-server.c @@ -481,11 +481,10 @@ void ipc_client_handle_command(struct ipc_client *client) { case IPC_COMMAND: { struct cmd_results *results = execute_command(buf, NULL); - const char *json = cmd_results_to_json(results); - char reply[256]; - int length = snprintf(reply, sizeof(reply), "%s", json); + char *json = cmd_results_to_json(results); + int length = strlen(json); + client_valid = ipc_send_reply(client, json, (uint32_t)length); free(json); - client_valid = ipc_send_reply(client, reply, (uint32_t)length); free_cmd_results(results); goto exit_cleanup; } -- cgit v1.2.3-70-g09d2 From 78c08fb0a281cbe74c56f0a2ea4b9370b9372661 Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Thu, 5 Jul 2018 18:12:14 -0400 Subject: Implement mode --pango_markup --- include/sway/config.h | 1 + include/sway/ipc-server.h | 2 +- sway/commands/mode.c | 19 ++++++++++++++++--- sway/ipc-server.c | 4 +++- sway/sway.5.scd | 4 +++- swaybar/render.c | 5 +++-- 6 files changed, 27 insertions(+), 8 deletions(-) (limited to 'include/sway') diff --git a/include/sway/config.h b/include/sway/config.h index e75b0664..ac668c24 100644 --- a/include/sway/config.h +++ b/include/sway/config.h @@ -50,6 +50,7 @@ struct sway_mode { char *name; list_t *keysym_bindings; list_t *keycode_bindings; + bool pango; }; struct input_config_mapped_from_region { diff --git a/include/sway/ipc-server.h b/include/sway/ipc-server.h index 026b5554..6469f097 100644 --- a/include/sway/ipc-server.h +++ b/include/sway/ipc-server.h @@ -15,6 +15,6 @@ void ipc_event_workspace(struct sway_container *old, struct sway_container *new, const char *change); void ipc_event_window(struct sway_container *window, const char *change); void ipc_event_barconfig_update(struct bar_config *bar); -void ipc_event_mode(const char *mode); +void ipc_event_mode(const char *mode, bool pango); #endif diff --git a/sway/commands/mode.c b/sway/commands/mode.c index 00331ccc..d2c14468 100644 --- a/sway/commands/mode.c +++ b/sway/commands/mode.c @@ -26,7 +26,17 @@ struct cmd_results *cmd_mode(int argc, char **argv) { "mode", "Can only be used in config file."); } - const char *mode_name = argv[0]; + bool pango = strcmp(*argv, "--pango_markup") == 0; + if (pango) { + argc--; argv++; + if (argc == 0) { + return cmd_results_new(CMD_FAILURE, "mode", + "Mode name is missing"); + } + } + + char *mode_name = *argv; + strip_quotes(mode_name); struct sway_mode *mode = NULL; // Find mode for (int i = 0; i < config->modes->length; ++i) { @@ -46,6 +56,7 @@ struct cmd_results *cmd_mode(int argc, char **argv) { mode->name = strdup(mode_name); mode->keysym_bindings = create_list(); mode->keycode_bindings = create_list(); + mode->pango = pango; list_add(config->modes, mode); } if (!mode) { @@ -54,13 +65,15 @@ struct cmd_results *cmd_mode(int argc, char **argv) { return error; } if ((config->reading && argc > 1) || (!config->reading && argc == 1)) { - wlr_log(L_DEBUG, "Switching to mode `%s'",mode->name); + wlr_log(L_DEBUG, "Switching to mode `%s' (pango=%d)", + mode->name, mode->pango); } // Set current mode config->current_mode = mode; if (argc == 1) { // trigger IPC mode event - ipc_event_mode(config->current_mode->name); + ipc_event_mode(config->current_mode->name, + config->current_mode->pango); return cmd_results_new(CMD_SUCCESS, NULL, NULL); } diff --git a/sway/ipc-server.c b/sway/ipc-server.c index abc2d7cb..8cfd9f26 100644 --- a/sway/ipc-server.c +++ b/sway/ipc-server.c @@ -332,13 +332,15 @@ void ipc_event_barconfig_update(struct bar_config *bar) { json_object_put(json); } -void ipc_event_mode(const char *mode) { +void ipc_event_mode(const char *mode, bool pango) { if (!ipc_has_event_listeners(IPC_EVENT_MODE)) { return; } wlr_log(L_DEBUG, "Sending mode::%s event", mode); json_object *obj = json_object_new_object(); json_object_object_add(obj, "change", json_object_new_string(mode)); + json_object_object_add(obj, "pango_markup", + json_object_new_boolean(pango)); const char *json_string = json_object_to_json_string(obj); ipc_send_event(json_string, IPC_EVENT_MODE); diff --git a/sway/sway.5.scd b/sway/sway.5.scd index 5ce6bf06..7c553d3f 100644 --- a/sway/sway.5.scd +++ b/sway/sway.5.scd @@ -413,10 +413,12 @@ The default colors are: *mode* Switches to the specified mode. The default mode _default_. -*mode* *{* *}* +*mode* [--pango\_markup] *{* *}* _commands..._ after *{* will be added to the specified mode. A newline is required between *{* and the first command, and *}* must be alone on a line. Only *bindsym* and *bindcode* commands are permitted in mode blocks. + If _--pango\_markup_ is given, then _mode_ will be interpreted as pango + markup. *mouse\_warping* output|none If _output_ is specified, the mouse will be moved to new outputs as you diff --git a/swaybar/render.c b/swaybar/render.c index 327a6f5f..efd3fdeb 100644 --- a/swaybar/render.c +++ b/swaybar/render.c @@ -298,7 +298,8 @@ static uint32_t render_binding_mode_indicator(cairo_t *cairo, int text_width, text_height; get_text_size(cairo, config->font, &text_width, &text_height, - output->scale, config->pango_markup, "%s", mode); + output->scale, config->mode_pango_markup, + "%s", mode); int ws_vertical_padding = WS_VERTICAL_PADDING * output->scale; int ws_horizontal_padding = WS_HORIZONTAL_PADDING * output->scale; @@ -329,7 +330,7 @@ static uint32_t render_binding_mode_indicator(cairo_t *cairo, double text_y = height / 2.0 - text_height / 2.0; cairo_set_source_u32(cairo, config->colors.binding_mode.text); cairo_move_to(cairo, x + width / 2 - text_width / 2, (int)floor(text_y)); - pango_printf(cairo, config->font, output->scale, config->pango_markup, + pango_printf(cairo, config->font, output->scale, config->mode_pango_markup, "%s", mode); return surface_height; } -- cgit v1.2.3-70-g09d2 From 839c3a550043fd38096a15ff8dcd7de1a084efdc Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Tue, 3 Jul 2018 17:29:23 +1000 Subject: Use opaque region to determine if frame done should be sent --- include/sway/output.h | 4 ++++ sway/desktop/output.c | 35 +++++++++++++++++++++++++++++++++-- swaylock/main.c | 15 +++++++++++++++ 3 files changed, 52 insertions(+), 2 deletions(-) (limited to 'include/sway') diff --git a/include/sway/output.h b/include/sway/output.h index 19fc5e99..e6fe55c6 100644 --- a/include/sway/output.h +++ b/include/sway/output.h @@ -54,4 +54,8 @@ void output_damage_whole_container(struct sway_output *output, struct sway_container *output_by_name(const char *name); void output_enable(struct sway_output *output); + +bool output_has_opaque_lockscreen(struct sway_output *output, + struct sway_seat *seat); + #endif diff --git a/sway/desktop/output.c b/sway/desktop/output.c index 52bd1666..a6b2ebc2 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -915,6 +915,36 @@ static struct sway_container *output_get_active_workspace( return workspace; } +bool output_has_opaque_lockscreen(struct sway_output *output, + struct sway_seat *seat) { + if (!seat->exclusive_client) { + return false; + } + + struct wlr_layer_surface *wlr_layer_surface; + wl_list_for_each(wlr_layer_surface, &server.layer_shell->surfaces, link) { + if (wlr_layer_surface->output != output->wlr_output) { + continue; + } + struct wlr_surface *wlr_surface = wlr_layer_surface->surface; + if (wlr_surface->resource->client != seat->exclusive_client) { + continue; + } + int nrects; + pixman_box32_t *rects = + pixman_region32_rectangles(&wlr_surface->current->opaque, &nrects); + for (int i = 0; i < nrects; ++i) { + pixman_box32_t *rect = &rects[i]; + if (rect->x1 <= 0 && rect->y1 <= 0 && + rect->x2 >= output->swayc->current.swayc_width && + rect->y2 >= output->swayc->current.swayc_height) { + return true; + } + } + } + return false; +} + static void render_output(struct sway_output *output, struct timespec *when, pixman_region32_t *damage) { struct wlr_output *wlr_output = output->wlr_output; @@ -950,7 +980,7 @@ static void render_output(struct sway_output *output, struct timespec *when, struct sway_view *fullscreen_view = workspace->current.ws_fullscreen; struct sway_seat *seat = input_manager_current_seat(input_manager); - if (seat->exclusive_client && seat->focused_layer) { + if (output_has_opaque_lockscreen(output, seat)) { float clear_color[] = {0.0f, 0.0f, 0.0f, 1.0f}; int nrects; @@ -1103,7 +1133,8 @@ static void send_frame_done(struct sway_output *output, struct timespec *when) { struct send_frame_done_data data = { .output = output, .when = when, - .exclusive_client = seat->exclusive_client, + .exclusive_client = output_has_opaque_lockscreen(output, seat) ? + seat->exclusive_client : NULL, }; struct sway_container *workspace = output_get_active_workspace(output); diff --git a/swaylock/main.c b/swaylock/main.c index f31ed679..a7a68e9b 100644 --- a/swaylock/main.c +++ b/swaylock/main.c @@ -111,12 +111,27 @@ static void create_layer_surface(struct swaylock_surface *surface) { wl_surface_commit(surface->surface); } +static bool image_is_opaque(cairo_surface_t *image) { + return cairo_surface_get_content(image) == CAIRO_CONTENT_COLOR; +} + static void layer_surface_configure(void *data, struct zwlr_layer_surface_v1 *layer_surface, uint32_t serial, uint32_t width, uint32_t height) { struct swaylock_surface *surface = data; surface->width = width; surface->height = height; + + if (image_is_opaque(surface->image) && + surface->state->args.mode != BACKGROUND_MODE_CENTER && + surface->state->args.mode != BACKGROUND_MODE_FIT) { + struct wl_region *region = + wl_compositor_create_region(surface->state->compositor); + wl_region_add(region, 0, 0, width, height); + wl_surface_set_opaque_region(surface->surface, region); + wl_region_destroy(region); + } + zwlr_layer_surface_v1_ack_configure(layer_surface, serial); render_frame(surface); } -- cgit v1.2.3-70-g09d2 From f9625d1d56482a2c8fce4a2d4743d36607fc639d Mon Sep 17 00:00:00 2001 From: emersion Date: Sat, 7 Jul 2018 10:30:52 +0100 Subject: Split renderer --- include/sway/output.h | 41 ++- sway/desktop/output.c | 917 ++------------------------------------------------ sway/desktop/render.c | 893 ++++++++++++++++++++++++++++++++++++++++++++++++ sway/meson.build | 1 + 4 files changed, 957 insertions(+), 895 deletions(-) create mode 100644 sway/desktop/render.c (limited to 'include/sway') diff --git a/include/sway/output.h b/include/sway/output.h index e6fe55c6..b6cda83c 100644 --- a/include/sway/output.h +++ b/include/sway/output.h @@ -38,6 +38,16 @@ struct sway_output { } events; }; +/** + * Contains a surface's root geometry information. For instance, when rendering + * a popup, this will contain the parent view's position and size. + */ +struct root_geometry { + double x, y; + int width, height; + float rotation; +}; + void output_damage_whole(struct sway_output *output); void output_damage_surface(struct sway_output *output, double ox, double oy, @@ -56,6 +66,35 @@ struct sway_container *output_by_name(const char *name); void output_enable(struct sway_output *output); bool output_has_opaque_lockscreen(struct sway_output *output, - struct sway_seat *seat); + struct sway_seat *seat); + +struct sway_container *output_get_active_workspace(struct sway_output *output); + +void output_render(struct sway_output *output, struct timespec *when, + pixman_region32_t *damage); + +bool output_get_surface_box(struct root_geometry *geo, + struct sway_output *output, struct wlr_surface *surface, int sx, int sy, + struct wlr_box *surface_box); + +void output_surface_for_each_surface(struct wlr_surface *surface, + double ox, double oy, struct root_geometry *geo, + wlr_surface_iterator_func_t iterator, void *user_data); + +void output_view_for_each_surface(struct sway_view *view, + struct sway_output *output, struct root_geometry *geo, + wlr_surface_iterator_func_t iterator, void *user_data); + +void output_layer_for_each_surface(struct wl_list *layer_surfaces, + struct root_geometry *geo, wlr_surface_iterator_func_t iterator, + void *user_data); + +void output_unmanaged_for_each_surface(struct wl_list *unmanaged, + struct sway_output *output, struct root_geometry *geo, + wlr_surface_iterator_func_t iterator, void *user_data); + +void output_drag_icons_for_each_surface(struct wl_list *drag_icons, + struct sway_output *output, struct root_geometry *geo, + wlr_surface_iterator_func_t iterator, void *user_data); #endif diff --git a/sway/desktop/output.c b/sway/desktop/output.c index 336163ea..8b50bc44 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -56,25 +56,7 @@ static void rotate_child_position(double *sx, double *sy, double sw, double sh, *sy = ry + ph/2 - sh/2; } -/** - * Contains a surface's root geometry information. For instance, when rendering - * a popup, this will contain the parent view's position and size. - */ -struct root_geometry { - double x, y; - int width, height; - float rotation; -}; - -struct render_data { - struct root_geometry root_geo; - struct sway_output *output; - pixman_region32_t *damage; - struct sway_view *view; - float alpha; -}; - -static bool get_surface_box(struct root_geometry *geo, +bool output_get_surface_box(struct root_geometry *geo, struct sway_output *output, struct wlr_surface *surface, int sx, int sy, struct wlr_box *surface_box) { if (!wlr_surface_has_buffer(surface)) { @@ -110,7 +92,7 @@ static bool get_surface_box(struct root_geometry *geo, return wlr_box_intersection(&output_box, &rotated_box, &intersection); } -static void surface_for_each_surface(struct wlr_surface *surface, +void output_surface_for_each_surface(struct wlr_surface *surface, double ox, double oy, struct root_geometry *geo, wlr_surface_iterator_func_t iterator, void *user_data) { geo->x = ox; @@ -122,12 +104,11 @@ static void surface_for_each_surface(struct wlr_surface *surface, wlr_surface_for_each_surface(surface, iterator, user_data); } -static void output_view_for_each_surface(struct sway_view *view, - struct root_geometry *geo, wlr_surface_iterator_func_t iterator, - void *user_data) { - struct render_data *data = user_data; - geo->x = view->swayc->current.view_x - data->output->swayc->current.swayc_x; - geo->y = view->swayc->current.view_y - data->output->swayc->current.swayc_y; +void output_view_for_each_surface(struct sway_view *view, + struct sway_output *output, struct root_geometry *geo, + wlr_surface_iterator_func_t iterator, void *user_data) { + geo->x = view->swayc->current.view_x - output->swayc->current.swayc_x; + geo->y = view->swayc->current.view_y - output->swayc->current.swayc_y; geo->width = view->swayc->current.view_width; geo->height = view->swayc->current.view_height; geo->rotation = 0; // TODO @@ -135,20 +116,20 @@ static void output_view_for_each_surface(struct sway_view *view, view_for_each_surface(view, iterator, user_data); } -static void layer_for_each_surface(struct wl_list *layer_surfaces, +void output_layer_for_each_surface(struct wl_list *layer_surfaces, struct root_geometry *geo, wlr_surface_iterator_func_t iterator, void *user_data) { struct sway_layer_surface *layer_surface; wl_list_for_each(layer_surface, layer_surfaces, link) { struct wlr_layer_surface *wlr_layer_surface = layer_surface->layer_surface; - surface_for_each_surface(wlr_layer_surface->surface, + output_surface_for_each_surface(wlr_layer_surface->surface, layer_surface->geo.x, layer_surface->geo.y, geo, iterator, user_data); } } -static void unmanaged_for_each_surface(struct wl_list *unmanaged, +void output_unmanaged_for_each_surface(struct wl_list *unmanaged, struct sway_output *output, struct root_geometry *geo, wlr_surface_iterator_func_t iterator, void *user_data) { struct sway_xwayland_unmanaged *unmanaged_surface; @@ -158,12 +139,12 @@ static void unmanaged_for_each_surface(struct wl_list *unmanaged, double ox = unmanaged_surface->lx - output->swayc->current.swayc_x; double oy = unmanaged_surface->ly - output->swayc->current.swayc_y; - surface_for_each_surface(xsurface->surface, ox, oy, geo, + output_surface_for_each_surface(xsurface->surface, ox, oy, geo, iterator, user_data); } } -static void drag_icons_for_each_surface(struct wl_list *drag_icons, +void output_drag_icons_for_each_surface(struct wl_list *drag_icons, struct sway_output *output, struct root_geometry *geo, wlr_surface_iterator_func_t iterator, void *user_data) { struct sway_drag_icon *drag_icon; @@ -172,7 +153,7 @@ static void drag_icons_for_each_surface(struct wl_list *drag_icons, double oy = drag_icon->y - output->swayc->y; if (drag_icon->wlr_drag_icon->mapped) { - surface_for_each_surface(drag_icon->wlr_drag_icon->surface, + output_surface_for_each_surface(drag_icon->wlr_drag_icon->surface, ox, oy, geo, iterator, user_data); } } @@ -185,746 +166,7 @@ static void scale_box(struct wlr_box *box, float scale) { box->height *= scale; } -static void scissor_output(struct wlr_output *wlr_output, - pixman_box32_t *rect) { - struct wlr_renderer *renderer = wlr_backend_get_renderer(wlr_output->backend); - assert(renderer); - - struct wlr_box box = { - .x = rect->x1, - .y = rect->y1, - .width = rect->x2 - rect->x1, - .height = rect->y2 - rect->y1, - }; - - int ow, oh; - wlr_output_transformed_resolution(wlr_output, &ow, &oh); - - enum wl_output_transform transform = - wlr_output_transform_invert(wlr_output->transform); - wlr_box_transform(&box, transform, ow, oh, &box); - - wlr_renderer_scissor(renderer, &box); -} - -static void render_texture(struct wlr_output *wlr_output, - pixman_region32_t *output_damage, struct wlr_texture *texture, - const struct wlr_box *box, const float matrix[static 9], float alpha) { - struct wlr_renderer *renderer = - wlr_backend_get_renderer(wlr_output->backend); - - pixman_region32_t damage; - pixman_region32_init(&damage); - pixman_region32_union_rect(&damage, &damage, box->x, box->y, - box->width, box->height); - pixman_region32_intersect(&damage, &damage, output_damage); - bool damaged = pixman_region32_not_empty(&damage); - if (!damaged) { - goto damage_finish; - } - - int nrects; - pixman_box32_t *rects = pixman_region32_rectangles(&damage, &nrects); - for (int i = 0; i < nrects; ++i) { - scissor_output(wlr_output, &rects[i]); - wlr_render_texture_with_matrix(renderer, texture, matrix, alpha); - } - -damage_finish: - pixman_region32_fini(&damage); -} - -static void render_surface_iterator(struct wlr_surface *surface, int sx, int sy, - void *_data) { - struct render_data *data = _data; - struct wlr_output *wlr_output = data->output->wlr_output; - float rotation = data->root_geo.rotation; - pixman_region32_t *output_damage = data->damage; - float alpha = data->alpha; - - struct wlr_texture *texture = wlr_surface_get_texture(surface); - if (!texture) { - return; - } - - struct wlr_box box; - bool intersects = get_surface_box(&data->root_geo, data->output, surface, - sx, sy, &box); - if (!intersects) { - return; - } - - scale_box(&box, wlr_output->scale); - - float matrix[9]; - enum wl_output_transform transform = - wlr_output_transform_invert(surface->current.transform); - wlr_matrix_project_box(matrix, &box, transform, rotation, - wlr_output->transform_matrix); - - render_texture(wlr_output, output_damage, texture, &box, matrix, alpha); -} - -static void render_layer(struct sway_output *output, - pixman_region32_t *damage, struct wl_list *layer_surfaces) { - struct render_data data = { - .output = output, - .damage = damage, - .alpha = 1.0f, - }; - layer_for_each_surface(layer_surfaces, &data.root_geo, - render_surface_iterator, &data); -} - -static void render_unmanaged(struct sway_output *output, - pixman_region32_t *damage, struct wl_list *unmanaged) { - struct render_data data = { - .output = output, - .damage = damage, - .alpha = 1.0f, - }; - unmanaged_for_each_surface(unmanaged, output, &data.root_geo, - render_surface_iterator, &data); -} - -static void render_drag_icons(struct sway_output *output, - pixman_region32_t *damage, struct wl_list *drag_icons) { - struct render_data data = { - .output = output, - .damage = damage, - .alpha = 1.0f, - }; - drag_icons_for_each_surface(drag_icons, output, &data.root_geo, - render_surface_iterator, &data); -} - -static void render_rect(struct wlr_output *wlr_output, - pixman_region32_t *output_damage, const struct wlr_box *_box, - float color[static 4]) { - struct wlr_renderer *renderer = - wlr_backend_get_renderer(wlr_output->backend); - - struct wlr_box box; - memcpy(&box, _box, sizeof(struct wlr_box)); - box.x -= wlr_output->lx * wlr_output->scale; - box.y -= wlr_output->ly * wlr_output->scale; - - pixman_region32_t damage; - pixman_region32_init(&damage); - pixman_region32_union_rect(&damage, &damage, box.x, box.y, - box.width, box.height); - pixman_region32_intersect(&damage, &damage, output_damage); - bool damaged = pixman_region32_not_empty(&damage); - if (!damaged) { - goto damage_finish; - } - - int nrects; - pixman_box32_t *rects = pixman_region32_rectangles(&damage, &nrects); - for (int i = 0; i < nrects; ++i) { - scissor_output(wlr_output, &rects[i]); - wlr_render_rect(renderer, &box, color, - wlr_output->transform_matrix); - } - -damage_finish: - pixman_region32_fini(&damage); -} - -static void premultiply_alpha(float color[4], float opacity) { - color[3] *= opacity; - color[0] *= color[3]; - color[1] *= color[3]; - color[2] *= color[3]; -} - -static void render_view_surfaces(struct sway_view *view, - struct sway_output *output, pixman_region32_t *damage, float alpha) { - struct render_data data = { - .output = output, - .damage = damage, - .view = view, - .alpha = alpha, - }; - output_view_for_each_surface( - view, &data.root_geo, render_surface_iterator, &data); -} - -static void render_saved_view(struct sway_view *view, - struct sway_output *output, pixman_region32_t *damage, float alpha) { - struct wlr_output *wlr_output = output->wlr_output; - - int width, height; - struct wlr_texture *texture = - transaction_get_saved_texture(view, &width, &height); - if (!texture) { - return; - } - struct wlr_box box = { - .x = view->swayc->current.view_x - output->swayc->current.swayc_x, - .y = view->swayc->current.view_y - output->swayc->current.swayc_y, - .width = width, - .height = height, - }; - - struct wlr_box output_box = { - .width = output->swayc->current.swayc_width, - .height = output->swayc->current.swayc_height, - }; - - struct wlr_box intersection; - bool intersects = wlr_box_intersection(&output_box, &box, &intersection); - if (!intersects) { - return; - } - - scale_box(&box, wlr_output->scale); - - float matrix[9]; - wlr_matrix_project_box(matrix, &box, WL_OUTPUT_TRANSFORM_NORMAL, 0, - wlr_output->transform_matrix); - - render_texture(wlr_output, damage, texture, &box, matrix, alpha); -} - -/** - * Render a view's surface and left/bottom/right borders. - */ -static void render_view(struct sway_output *output, pixman_region32_t *damage, - struct sway_container *con, struct border_colors *colors) { - struct sway_view *view = con->sway_view; - if (view->swayc->instructions->length) { - render_saved_view(view, output, damage, view->swayc->alpha); - } else { - render_view_surfaces(view, output, damage, view->swayc->alpha); - } - - struct wlr_box box; - float output_scale = output->wlr_output->scale; - float color[4]; - struct sway_container_state *state = &con->current; - - if (state->border != B_NONE) { - if (state->border_left) { - memcpy(&color, colors->child_border, sizeof(float) * 4); - premultiply_alpha(color, con->alpha); - box.x = state->swayc_x; - box.y = state->view_y; - box.width = state->border_thickness; - box.height = state->view_height; - scale_box(&box, output_scale); - render_rect(output->wlr_output, damage, &box, color); - } - - if (state->border_right) { - if (state->parent->current.children->length == 1 - && state->parent->current.layout == L_HORIZ) { - memcpy(&color, colors->indicator, sizeof(float) * 4); - } else { - memcpy(&color, colors->child_border, sizeof(float) * 4); - } - premultiply_alpha(color, con->alpha); - box.x = state->view_x + state->view_width; - box.y = state->view_y; - box.width = state->border_thickness; - box.height = state->view_height; - scale_box(&box, output_scale); - render_rect(output->wlr_output, damage, &box, color); - } - - if (state->border_bottom) { - if (state->parent->current.children->length == 1 - && con->current.parent->current.layout == L_VERT) { - memcpy(&color, colors->indicator, sizeof(float) * 4); - } else { - memcpy(&color, colors->child_border, sizeof(float) * 4); - } - premultiply_alpha(color, con->alpha); - box.x = state->swayc_x; - box.y = state->view_y + state->view_height; - box.width = state->swayc_width; - box.height = state->border_thickness; - scale_box(&box, output_scale); - render_rect(output->wlr_output, damage, &box, color); - } - } -} - -/** - * Render a titlebar. - * - * Care must be taken not to render over the same pixel multiple times, - * otherwise the colors will be incorrect when using opacity. - * - * The height is: 1px border, 3px padding, font height, 3px padding, 1px border - * The left side for L_TABBED is: 1px border, 2px padding, title - * The left side for other layouts is: 3px padding, title - */ -static void render_titlebar(struct sway_output *output, - pixman_region32_t *output_damage, struct sway_container *con, - int x, int y, int width, - struct border_colors *colors, struct wlr_texture *title_texture, - struct wlr_texture *marks_texture) { - struct wlr_box box; - float color[4]; - struct sway_container_state *state = &con->current; - float output_scale = output->wlr_output->scale; - enum sway_container_layout layout = state->parent->current.layout; - list_t *children = state->parent->current.children; - bool is_last_child = children->items[children->length - 1] == con; - double output_x = output->swayc->current.swayc_x; - double output_y = output->swayc->current.swayc_y; - - // Single pixel bar above title - memcpy(&color, colors->border, sizeof(float) * 4); - premultiply_alpha(color, con->alpha); - box.x = x; - box.y = y; - box.width = width; - box.height = TITLEBAR_BORDER_THICKNESS; - scale_box(&box, output_scale); - render_rect(output->wlr_output, output_damage, &box, color); - - // Single pixel bar below title - size_t left_offset = 0, right_offset = 0; - bool connects_sides = false; - if (layout == L_HORIZ || layout == L_VERT || - (layout == L_STACKED && is_last_child)) { - if (con->type == C_VIEW) { - left_offset = state->border_left * state->border_thickness; - right_offset = state->border_right * state->border_thickness; - connects_sides = true; - } - } - box.x = x + left_offset; - box.y = y + container_titlebar_height() - TITLEBAR_BORDER_THICKNESS; - box.width = width - left_offset - right_offset; - box.height = TITLEBAR_BORDER_THICKNESS; - scale_box(&box, output_scale); - render_rect(output->wlr_output, output_damage, &box, color); - - if (layout == L_TABBED) { - // Single pixel left edge - box.x = x; - box.y = y + TITLEBAR_BORDER_THICKNESS; - box.width = TITLEBAR_BORDER_THICKNESS; - box.height = - container_titlebar_height() - TITLEBAR_BORDER_THICKNESS * 2; - scale_box(&box, output_scale); - render_rect(output->wlr_output, output_damage, &box, color); - - // Single pixel right edge - box.x = (x + width - TITLEBAR_BORDER_THICKNESS) * output_scale; - render_rect(output->wlr_output, output_damage, &box, color); - } - - size_t inner_width = width - TITLEBAR_H_PADDING * 2; - - // Marks - size_t marks_ob_width = 0; // output-buffer-local - if (config->show_marks && marks_texture) { - struct wlr_box texture_box; - wlr_texture_get_size(marks_texture, - &texture_box.width, &texture_box.height); - texture_box.x = (x - output_x + width - TITLEBAR_H_PADDING) - * output_scale - texture_box.width; - texture_box.y = (y - output_y + TITLEBAR_V_PADDING) * output_scale; - - float matrix[9]; - wlr_matrix_project_box(matrix, &texture_box, - WL_OUTPUT_TRANSFORM_NORMAL, - 0.0, output->wlr_output->transform_matrix); - - if (inner_width * output_scale < texture_box.width) { - texture_box.width = inner_width * output_scale; - } - render_texture(output->wlr_output, output_damage, marks_texture, - &texture_box, matrix, con->alpha); - marks_ob_width = texture_box.width; - - // Gap between the marks and bottom padding, for when the marks texture - // height is smaller than the config's font height - memcpy(&color, colors->background, sizeof(float) * 4); - premultiply_alpha(color, con->alpha); - box.x = texture_box.x; - box.y = texture_box.y + texture_box.height; - box.width = texture_box.width; - box.height = config->font_height * output_scale - texture_box.height; - if (box.height > 0) { - render_rect(output->wlr_output, output_damage, &box, color); - } - } - - // Title text - size_t title_ob_width = 0; // output-buffer-local - if (title_texture) { - struct wlr_box texture_box; - wlr_texture_get_size(title_texture, - &texture_box.width, &texture_box.height); - texture_box.x = (x - output_x + TITLEBAR_H_PADDING) * output_scale; - texture_box.y = (y - output_y + TITLEBAR_V_PADDING) * output_scale; - - float matrix[9]; - wlr_matrix_project_box(matrix, &texture_box, - WL_OUTPUT_TRANSFORM_NORMAL, - 0.0, output->wlr_output->transform_matrix); - - if (inner_width * output_scale - marks_ob_width < texture_box.width) { - texture_box.width = inner_width * output_scale - marks_ob_width; - } - render_texture(output->wlr_output, output_damage, title_texture, - &texture_box, matrix, con->alpha); - title_ob_width = texture_box.width; - - // Gap between the title and bottom padding, for when the title texture - // height is smaller than the config's font height - memcpy(&color, colors->background, sizeof(float) * 4); - premultiply_alpha(color, con->alpha); - box.x = texture_box.x; - box.y = texture_box.y + texture_box.height; - box.width = texture_box.width; - box.height = config->font_height * output_scale - texture_box.height; - if (box.height > 0) { - render_rect(output->wlr_output, output_damage, &box, color); - } - } - - // Padding above title - memcpy(&color, colors->background, sizeof(float) * 4); - premultiply_alpha(color, con->alpha); - box.x = x + (layout == L_TABBED) * TITLEBAR_BORDER_THICKNESS; - box.y = y + TITLEBAR_BORDER_THICKNESS; - box.width = width - (layout == L_TABBED) * TITLEBAR_BORDER_THICKNESS * 2; - box.height = TITLEBAR_V_PADDING - TITLEBAR_BORDER_THICKNESS; - scale_box(&box, output_scale); - render_rect(output->wlr_output, output_damage, &box, color); - - // Padding below title - box.y = (y + TITLEBAR_V_PADDING + config->font_height) * output_scale; - render_rect(output->wlr_output, output_damage, &box, color); - - // Filler between title and marks - box.width = inner_width * output_scale - title_ob_width - marks_ob_width; - if (box.width > 0) { - box.x = (x + TITLEBAR_H_PADDING) * output_scale + title_ob_width; - box.y = (y + TITLEBAR_V_PADDING) * output_scale; - box.height = config->font_height * output_scale; - render_rect(output->wlr_output, output_damage, &box, color); - } - - // Padding left of title - left_offset = (layout == L_TABBED) * TITLEBAR_BORDER_THICKNESS; - box.x = x + left_offset; - box.y = y + TITLEBAR_V_PADDING; - box.width = TITLEBAR_H_PADDING - left_offset; - box.height = config->font_height; - scale_box(&box, output_scale); - render_rect(output->wlr_output, output_damage, &box, color); - - // Padding right of marks - right_offset = (layout == L_TABBED) * TITLEBAR_BORDER_THICKNESS; - box.x = x + width - TITLEBAR_H_PADDING; - box.y = y + TITLEBAR_V_PADDING; - box.width = TITLEBAR_H_PADDING - right_offset; - box.height = config->font_height; - scale_box(&box, output_scale); - render_rect(output->wlr_output, output_damage, &box, color); - - if (connects_sides) { - // Left pixel in line with bottom bar - box.x = x; - box.y = y + container_titlebar_height() - TITLEBAR_BORDER_THICKNESS; - box.width = state->border_thickness * state->border_left; - box.height = TITLEBAR_BORDER_THICKNESS; - scale_box(&box, output_scale); - render_rect(output->wlr_output, output_damage, &box, color); - - // Right pixel in line with bottom bar - box.x = x + width - state->border_thickness * state->border_right; - box.y = y + container_titlebar_height() - TITLEBAR_BORDER_THICKNESS; - box.width = state->border_thickness * state->border_right; - box.height = TITLEBAR_BORDER_THICKNESS; - scale_box(&box, output_scale); - render_rect(output->wlr_output, output_damage, &box, color); - } -} - -/** - * Render the top border line for a view using "border pixel". - */ -static void render_top_border(struct sway_output *output, - pixman_region32_t *output_damage, struct sway_container *con, - struct border_colors *colors) { - struct sway_container_state *state = &con->current; - if (!state->border_top) { - return; - } - struct wlr_box box; - float color[4]; - float output_scale = output->wlr_output->scale; - - // Child border - top edge - memcpy(&color, colors->child_border, sizeof(float) * 4); - premultiply_alpha(color, con->alpha); - box.x = state->swayc_x; - box.y = state->swayc_y; - box.width = state->swayc_width; - box.height = state->border_thickness; - scale_box(&box, output_scale); - render_rect(output->wlr_output, output_damage, &box, color); -} - -static void render_container(struct sway_output *output, - pixman_region32_t *damage, struct sway_container *con, bool parent_focused); - -/** - * Render a container's children using a L_HORIZ or L_VERT layout. - * - * Wrap child views in borders and leave child containers borderless because - * they'll apply their own borders to their children. - */ -static void render_container_simple(struct sway_output *output, - pixman_region32_t *damage, struct sway_container *con, - bool parent_focused) { - struct sway_seat *seat = input_manager_current_seat(input_manager); - struct sway_container *focus = seat_get_focus(seat); - - for (int i = 0; i < con->current.children->length; ++i) { - struct sway_container *child = con->current.children->items[i]; - - if (child->type == C_VIEW) { - struct sway_view *view = child->sway_view; - struct border_colors *colors; - struct wlr_texture *title_texture; - struct wlr_texture *marks_texture; - struct sway_container_state *state = &child->current; - - if (focus == child || parent_focused) { - colors = &config->border_colors.focused; - title_texture = child->title_focused; - marks_texture = view->marks_focused; - } else if (seat_get_focus_inactive(seat, con) == child) { - colors = &config->border_colors.focused_inactive; - title_texture = child->title_focused_inactive; - marks_texture = view->marks_focused_inactive; - } else { - colors = &config->border_colors.unfocused; - title_texture = child->title_unfocused; - marks_texture = view->marks_unfocused; - } - - if (state->border == B_NORMAL) { - render_titlebar(output, damage, child, state->swayc_x, - state->swayc_y, state->swayc_width, colors, - title_texture, marks_texture); - } else { - render_top_border(output, damage, child, colors); - } - render_view(output, damage, child, colors); - } else { - render_container(output, damage, child, - parent_focused || focus == child); - } - } -} - -/** - * Render a container's children using the L_TABBED layout. - */ -static void render_container_tabbed(struct sway_output *output, - pixman_region32_t *damage, struct sway_container *con, - bool parent_focused) { - if (!con->current.children->length) { - return; - } - struct sway_seat *seat = input_manager_current_seat(input_manager); - struct sway_container *focus = seat_get_focus(seat); - struct sway_container *current = seat_get_active_current_child(seat, con); - struct border_colors *current_colors = &config->border_colors.unfocused; - struct sway_container_state *pstate = &con->current; - - // Render tabs - for (int i = 0; i < con->current.children->length; ++i) { - struct sway_container *child = con->current.children->items[i]; - struct sway_view *view = child->type == C_VIEW ? child->sway_view : NULL; - struct sway_container_state *cstate = &child->current; - struct border_colors *colors; - struct wlr_texture *title_texture; - struct wlr_texture *marks_texture; - - if (focus == child || parent_focused) { - colors = &config->border_colors.focused; - title_texture = child->title_focused; - marks_texture = view ? view->marks_focused : NULL; - } else if (child == current) { - colors = &config->border_colors.focused_inactive; - title_texture = child->title_focused_inactive; - marks_texture = view ? view->marks_focused_inactive : NULL; - } else { - colors = &config->border_colors.unfocused; - title_texture = child->title_unfocused; - marks_texture = view ? view->marks_unfocused : NULL; - } - - int tab_width = pstate->swayc_width / pstate->children->length; - int x = pstate->swayc_x + tab_width * i; - // Make last tab use the remaining width of the parent - if (i == pstate->children->length - 1) { - tab_width = pstate->swayc_width - tab_width * i; - } - - render_titlebar(output, damage, child, x, cstate->swayc_y, tab_width, - colors, title_texture, marks_texture); - - if (child == current) { - current_colors = colors; - } - } - - // Render surface and left/right/bottom borders - if (current) { - if (current->type == C_VIEW) { - render_view(output, damage, current, current_colors); - } else { - render_container(output, damage, current, - parent_focused || current == focus); - } - } -} - -/** - * Render a container's children using the L_STACKED layout. - */ -static void render_container_stacked(struct sway_output *output, - pixman_region32_t *damage, struct sway_container *con, - bool parent_focused) { - if (!con->current.children->length) { - return; - } - struct sway_seat *seat = input_manager_current_seat(input_manager); - struct sway_container *focus = seat_get_focus(seat); - struct sway_container *current = seat_get_active_current_child(seat, con); - struct border_colors *current_colors = &config->border_colors.unfocused; - struct sway_container_state *pstate = &con->current; - - // Render titles - for (int i = 0; i < con->current.children->length; ++i) { - struct sway_container *child = con->current.children->items[i]; - struct sway_view *view = child->type == C_VIEW ? child->sway_view : NULL; - struct sway_container_state *cstate = &child->current; - struct border_colors *colors; - struct wlr_texture *title_texture; - struct wlr_texture *marks_texture; - - if (focus == child || parent_focused) { - colors = &config->border_colors.focused; - title_texture = child->title_focused; - marks_texture = view ? view->marks_focused : NULL; - } else if (child == current) { - colors = &config->border_colors.focused_inactive; - title_texture = child->title_focused_inactive; - marks_texture = view ? view->marks_focused_inactive : NULL; - } else { - colors = &config->border_colors.unfocused; - title_texture = child->title_unfocused; - marks_texture = view ? view->marks_unfocused : NULL; - } - - int y = pstate->swayc_y + container_titlebar_height() * i; - render_titlebar(output, damage, child, cstate->swayc_x, y, - cstate->swayc_width, colors, title_texture, marks_texture); - - if (child == current) { - current_colors = colors; - } - } - - // Render surface and left/right/bottom borders - if (current) { - if (current->type == C_VIEW) { - render_view(output, damage, current, current_colors); - } else { - render_container(output, damage, current, - parent_focused || current == focus); - } - } -} - -static void render_container(struct sway_output *output, - pixman_region32_t *damage, struct sway_container *con, - bool parent_focused) { - switch (con->current.layout) { - case L_NONE: - case L_HORIZ: - case L_VERT: - render_container_simple(output, damage, con, parent_focused); - break; - case L_STACKED: - render_container_stacked(output, damage, con, parent_focused); - break; - case L_TABBED: - render_container_tabbed(output, damage, con, parent_focused); - break; - case L_FLOATING: - sway_assert(false, "Didn't expect to see floating here"); - } -} - -static void render_floating_container(struct sway_output *soutput, - pixman_region32_t *damage, struct sway_container *con) { - if (con->type == C_VIEW) { - struct sway_view *view = con->sway_view; - struct sway_seat *seat = input_manager_current_seat(input_manager); - struct sway_container *focus = seat_get_focus(seat); - struct border_colors *colors; - struct wlr_texture *title_texture; - struct wlr_texture *marks_texture; - - if (focus == con) { - colors = &config->border_colors.focused; - title_texture = con->title_focused; - marks_texture = view->marks_focused; - } else { - colors = &config->border_colors.unfocused; - title_texture = con->title_unfocused; - marks_texture = view->marks_unfocused; - } - - if (con->current.border == B_NORMAL) { - render_titlebar(soutput, damage, con, con->current.swayc_x, - con->current.swayc_y, con->current.swayc_width, colors, - title_texture, marks_texture); - } else if (con->current.border != B_NONE) { - render_top_border(soutput, damage, con, colors); - } - render_view(soutput, damage, con, colors); - } else { - render_container(soutput, damage, con, false); - } -} - -static void render_floating(struct sway_output *soutput, - pixman_region32_t *damage) { - for (int i = 0; i < root_container.current.children->length; ++i) { - struct sway_container *output = - root_container.current.children->items[i]; - for (int j = 0; j < output->current.children->length; ++j) { - struct sway_container *ws = output->current.children->items[j]; - if (!workspace_is_visible(ws)) { - continue; - } - list_t *floating = - ws->current.ws_floating->current.children; - for (int k = 0; k < floating->length; ++k) { - struct sway_container *floater = floating->items[k]; - render_floating_container(soutput, damage, floater); - } - } - } -} - -static struct sway_container *output_get_active_workspace( - struct sway_output *output) { +struct sway_container *output_get_active_workspace(struct sway_output *output) { struct sway_seat *seat = input_manager_current_seat(input_manager); struct sway_container *focus = seat_get_focus_inactive(seat, output->swayc); @@ -975,119 +217,6 @@ bool output_has_opaque_lockscreen(struct sway_output *output, return false; } -static void render_output(struct sway_output *output, struct timespec *when, - pixman_region32_t *damage) { - struct wlr_output *wlr_output = output->wlr_output; - - struct wlr_renderer *renderer = - wlr_backend_get_renderer(wlr_output->backend); - if (!sway_assert(renderer != NULL, - "expected the output backend to have a renderer")) { - return; - } - - wlr_renderer_begin(renderer, wlr_output->width, wlr_output->height); - - bool damage_whole_before_swap = false; - if (!pixman_region32_not_empty(damage)) { - // Output isn't damaged but needs buffer swap - goto renderer_end; - } - - const char *damage_debug = getenv("SWAY_DAMAGE_DEBUG"); - if (damage_debug != NULL) { - if (strcmp(damage_debug, "highlight") == 0) { - wlr_renderer_clear(renderer, (float[]){1, 1, 0, 1}); - damage_whole_before_swap = true; - } else if (strcmp(damage_debug, "rerender") == 0) { - int width, height; - wlr_output_transformed_resolution(wlr_output, &width, &height); - pixman_region32_union_rect(damage, damage, 0, 0, width, height); - } - } - - struct sway_container *workspace = output_get_active_workspace(output); - struct sway_view *fullscreen_view = workspace->current.ws_fullscreen; - struct sway_seat *seat = input_manager_current_seat(input_manager); - - if (output_has_opaque_lockscreen(output, seat)) { - struct wlr_layer_surface *wlr_layer_surface = seat->focused_layer; - struct sway_layer_surface *sway_layer_surface = - layer_from_wlr_layer_surface(seat->focused_layer); - struct render_data data = { - .output = output, - .damage = damage, - .alpha = 1.0f, - }; - surface_for_each_surface(wlr_layer_surface->surface, - sway_layer_surface->geo.x, sway_layer_surface->geo.y, - &data.root_geo, render_surface_iterator, &data); - } else if (fullscreen_view) { - float clear_color[] = {0.0f, 0.0f, 0.0f, 1.0f}; - - int nrects; - pixman_box32_t *rects = pixman_region32_rectangles(damage, &nrects); - for (int i = 0; i < nrects; ++i) { - scissor_output(wlr_output, &rects[i]); - wlr_renderer_clear(renderer, clear_color); - } - - // TODO: handle views smaller than the output - render_view_surfaces(fullscreen_view, output, damage, 1.0f); - - if (fullscreen_view->type == SWAY_VIEW_XWAYLAND) { - render_unmanaged(output, damage, - &root_container.sway_root->xwayland_unmanaged); - } - } else { - float clear_color[] = {0.25f, 0.25f, 0.25f, 1.0f}; - - int nrects; - pixman_box32_t *rects = pixman_region32_rectangles(damage, &nrects); - for (int i = 0; i < nrects; ++i) { - scissor_output(wlr_output, &rects[i]); - wlr_renderer_clear(renderer, clear_color); - } - - render_layer(output, damage, - &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND]); - render_layer(output, damage, - &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM]); - - struct sway_seat *seat = input_manager_current_seat(input_manager); - struct sway_container *focus = seat_get_focus(seat); - render_container(output, damage, workspace, focus == workspace); - render_floating(output, damage); - - render_unmanaged(output, damage, - &root_container.sway_root->xwayland_unmanaged); - render_layer(output, damage, - &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]); - } - render_layer(output, damage, - &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]); - render_drag_icons(output, damage, &root_container.sway_root->drag_icons); - -renderer_end: - if (root_container.sway_root->debug_tree) { - wlr_render_texture(renderer, root_container.sway_root->debug_tree, - wlr_output->transform_matrix, 0, 0, 1); - } - - if (damage_whole_before_swap || root_container.sway_root->debug_tree) { - int width, height; - wlr_output_transformed_resolution(wlr_output, &width, &height); - pixman_region32_union_rect(damage, damage, 0, 0, width, height); - } - - wlr_renderer_scissor(renderer, NULL); - wlr_renderer_end(renderer); - if (!wlr_output_damage_swap_buffers(output->damage, when, damage)) { - return; - } - output->last_frame = *when; -} - struct send_frame_done_data { struct root_geometry root_geo; struct sway_output *output; @@ -1103,7 +232,7 @@ static void send_frame_done_iterator(struct wlr_surface *surface, return; } - bool intersects = get_surface_box(&data->root_geo, data->output, surface, + bool intersects = output_get_surface_box(&data->root_geo, data->output, surface, sx, sy, NULL); if (intersects) { wlr_surface_send_frame_done(surface, data->when); @@ -1112,19 +241,19 @@ static void send_frame_done_iterator(struct wlr_surface *surface, static void send_frame_done_layer(struct send_frame_done_data *data, struct wl_list *layer_surfaces) { - layer_for_each_surface(layer_surfaces, &data->root_geo, + output_layer_for_each_surface(layer_surfaces, &data->root_geo, send_frame_done_iterator, data); } static void send_frame_done_unmanaged(struct send_frame_done_data *data, struct wl_list *unmanaged) { - unmanaged_for_each_surface(unmanaged, data->output, &data->root_geo, + output_unmanaged_for_each_surface(unmanaged, data->output, &data->root_geo, send_frame_done_iterator, data); } static void send_frame_done_drag_icons(struct send_frame_done_data *data, struct wl_list *drag_icons) { - drag_icons_for_each_surface(drag_icons, data->output, &data->root_geo, + output_drag_icons_for_each_surface(drag_icons, data->output, &data->root_geo, send_frame_done_iterator, data); } @@ -1139,7 +268,7 @@ static void send_frame_done_container_iterator(struct sway_container *con, return; } - output_view_for_each_surface(con->sway_view, &data->root_geo, + output_view_for_each_surface(con->sway_view, data->output, &data->root_geo, send_frame_done_iterator, data); } @@ -1206,7 +335,7 @@ static void damage_handle_frame(struct wl_listener *listener, void *data) { } if (needs_swap) { - render_output(output, &now, &damage); + output_render(output, &now, &damage); } pixman_region32_fini(&damage); @@ -1233,7 +362,7 @@ static void damage_surface_iterator(struct wlr_surface *surface, int sx, int sy, bool whole = data->whole; struct wlr_box box; - bool intersects = get_surface_box(&data->root_geo, data->output, surface, + bool intersects = output_get_surface_box(&data->root_geo, data->output, surface, sx, sy, &box); if (!intersects) { return; @@ -1283,7 +412,7 @@ void output_damage_surface(struct sway_output *output, double ox, double oy, .whole = whole, }; - surface_for_each_surface(surface, ox, oy, &data.root_geo, + output_surface_for_each_surface(surface, ox, oy, &data.root_geo, damage_surface_iterator, &data); } @@ -1302,7 +431,7 @@ static void output_damage_view(struct sway_output *output, .whole = whole, }; - output_view_for_each_surface(view, &data.root_geo, + output_view_for_each_surface(view, output, &data.root_geo, damage_surface_iterator, &data); } diff --git a/sway/desktop/render.c b/sway/desktop/render.c new file mode 100644 index 00000000..43948f29 --- /dev/null +++ b/sway/desktop/render.c @@ -0,0 +1,893 @@ +#define _POSIX_C_SOURCE 200809L +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "log.h" +#include "sway/config.h" +#include "sway/input/input-manager.h" +#include "sway/input/seat.h" +#include "sway/layers.h" +#include "sway/output.h" +#include "sway/server.h" +#include "sway/tree/arrange.h" +#include "sway/tree/container.h" +#include "sway/tree/layout.h" +#include "sway/tree/view.h" +#include "sway/tree/workspace.h" + +struct render_data { + struct root_geometry root_geo; + struct sway_output *output; + pixman_region32_t *damage; + struct sway_view *view; + float alpha; +}; + +static void scale_box(struct wlr_box *box, float scale) { + box->x *= scale; + box->y *= scale; + box->width *= scale; + box->height *= scale; +} + +static void scissor_output(struct wlr_output *wlr_output, + pixman_box32_t *rect) { + struct wlr_renderer *renderer = wlr_backend_get_renderer(wlr_output->backend); + assert(renderer); + + struct wlr_box box = { + .x = rect->x1, + .y = rect->y1, + .width = rect->x2 - rect->x1, + .height = rect->y2 - rect->y1, + }; + + int ow, oh; + wlr_output_transformed_resolution(wlr_output, &ow, &oh); + + enum wl_output_transform transform = + wlr_output_transform_invert(wlr_output->transform); + wlr_box_transform(&box, transform, ow, oh, &box); + + wlr_renderer_scissor(renderer, &box); +} + +static void render_texture(struct wlr_output *wlr_output, + pixman_region32_t *output_damage, struct wlr_texture *texture, + const struct wlr_box *box, const float matrix[static 9], float alpha) { + struct wlr_renderer *renderer = + wlr_backend_get_renderer(wlr_output->backend); + + pixman_region32_t damage; + pixman_region32_init(&damage); + pixman_region32_union_rect(&damage, &damage, box->x, box->y, + box->width, box->height); + pixman_region32_intersect(&damage, &damage, output_damage); + bool damaged = pixman_region32_not_empty(&damage); + if (!damaged) { + goto damage_finish; + } + + int nrects; + pixman_box32_t *rects = pixman_region32_rectangles(&damage, &nrects); + for (int i = 0; i < nrects; ++i) { + scissor_output(wlr_output, &rects[i]); + wlr_render_texture_with_matrix(renderer, texture, matrix, alpha); + } + +damage_finish: + pixman_region32_fini(&damage); +} + +static void render_surface_iterator(struct wlr_surface *surface, int sx, int sy, + void *_data) { + struct render_data *data = _data; + struct wlr_output *wlr_output = data->output->wlr_output; + float rotation = data->root_geo.rotation; + pixman_region32_t *output_damage = data->damage; + float alpha = data->alpha; + + struct wlr_texture *texture = wlr_surface_get_texture(surface); + if (!texture) { + return; + } + + struct wlr_box box; + bool intersects = output_get_surface_box(&data->root_geo, data->output, + surface, sx, sy, &box); + if (!intersects) { + return; + } + + scale_box(&box, wlr_output->scale); + + float matrix[9]; + enum wl_output_transform transform = + wlr_output_transform_invert(surface->current.transform); + wlr_matrix_project_box(matrix, &box, transform, rotation, + wlr_output->transform_matrix); + + render_texture(wlr_output, output_damage, texture, &box, matrix, alpha); +} + +static void render_layer(struct sway_output *output, + pixman_region32_t *damage, struct wl_list *layer_surfaces) { + struct render_data data = { + .output = output, + .damage = damage, + .alpha = 1.0f, + }; + output_layer_for_each_surface(layer_surfaces, &data.root_geo, + render_surface_iterator, &data); +} + +static void render_unmanaged(struct sway_output *output, + pixman_region32_t *damage, struct wl_list *unmanaged) { + struct render_data data = { + .output = output, + .damage = damage, + .alpha = 1.0f, + }; + output_unmanaged_for_each_surface(unmanaged, output, &data.root_geo, + render_surface_iterator, &data); +} + +static void render_drag_icons(struct sway_output *output, + pixman_region32_t *damage, struct wl_list *drag_icons) { + struct render_data data = { + .output = output, + .damage = damage, + .alpha = 1.0f, + }; + output_drag_icons_for_each_surface(drag_icons, output, &data.root_geo, + render_surface_iterator, &data); +} + +static void render_rect(struct wlr_output *wlr_output, + pixman_region32_t *output_damage, const struct wlr_box *_box, + float color[static 4]) { + struct wlr_renderer *renderer = + wlr_backend_get_renderer(wlr_output->backend); + + struct wlr_box box; + memcpy(&box, _box, sizeof(struct wlr_box)); + box.x -= wlr_output->lx * wlr_output->scale; + box.y -= wlr_output->ly * wlr_output->scale; + + pixman_region32_t damage; + pixman_region32_init(&damage); + pixman_region32_union_rect(&damage, &damage, box.x, box.y, + box.width, box.height); + pixman_region32_intersect(&damage, &damage, output_damage); + bool damaged = pixman_region32_not_empty(&damage); + if (!damaged) { + goto damage_finish; + } + + int nrects; + pixman_box32_t *rects = pixman_region32_rectangles(&damage, &nrects); + for (int i = 0; i < nrects; ++i) { + scissor_output(wlr_output, &rects[i]); + wlr_render_rect(renderer, &box, color, + wlr_output->transform_matrix); + } + +damage_finish: + pixman_region32_fini(&damage); +} + +static void premultiply_alpha(float color[4], float opacity) { + color[3] *= opacity; + color[0] *= color[3]; + color[1] *= color[3]; + color[2] *= color[3]; +} + +static void render_view_surfaces(struct sway_view *view, + struct sway_output *output, pixman_region32_t *damage, float alpha) { + struct render_data data = { + .output = output, + .damage = damage, + .view = view, + .alpha = alpha, + }; + output_view_for_each_surface(view, output, &data.root_geo, + render_surface_iterator, &data); +} + +static void render_saved_view(struct sway_view *view, + struct sway_output *output, pixman_region32_t *damage, float alpha) { + struct wlr_output *wlr_output = output->wlr_output; + + int width, height; + struct wlr_texture *texture = + transaction_get_saved_texture(view, &width, &height); + if (!texture) { + return; + } + struct wlr_box box = { + .x = view->swayc->current.view_x - output->swayc->current.swayc_x, + .y = view->swayc->current.view_y - output->swayc->current.swayc_y, + .width = width, + .height = height, + }; + + struct wlr_box output_box = { + .width = output->swayc->current.swayc_width, + .height = output->swayc->current.swayc_height, + }; + + struct wlr_box intersection; + bool intersects = wlr_box_intersection(&output_box, &box, &intersection); + if (!intersects) { + return; + } + + scale_box(&box, wlr_output->scale); + + float matrix[9]; + wlr_matrix_project_box(matrix, &box, WL_OUTPUT_TRANSFORM_NORMAL, 0, + wlr_output->transform_matrix); + + render_texture(wlr_output, damage, texture, &box, matrix, alpha); +} + +/** + * Render a view's surface and left/bottom/right borders. + */ +static void render_view(struct sway_output *output, pixman_region32_t *damage, + struct sway_container *con, struct border_colors *colors) { + struct sway_view *view = con->sway_view; + if (view->swayc->instructions->length) { + render_saved_view(view, output, damage, view->swayc->alpha); + } else { + render_view_surfaces(view, output, damage, view->swayc->alpha); + } + + struct wlr_box box; + float output_scale = output->wlr_output->scale; + float color[4]; + struct sway_container_state *state = &con->current; + + if (state->border != B_NONE) { + if (state->border_left) { + memcpy(&color, colors->child_border, sizeof(float) * 4); + premultiply_alpha(color, con->alpha); + box.x = state->swayc_x; + box.y = state->view_y; + box.width = state->border_thickness; + box.height = state->view_height; + scale_box(&box, output_scale); + render_rect(output->wlr_output, damage, &box, color); + } + + if (state->border_right) { + if (state->parent->current.children->length == 1 + && state->parent->current.layout == L_HORIZ) { + memcpy(&color, colors->indicator, sizeof(float) * 4); + } else { + memcpy(&color, colors->child_border, sizeof(float) * 4); + } + premultiply_alpha(color, con->alpha); + box.x = state->view_x + state->view_width; + box.y = state->view_y; + box.width = state->border_thickness; + box.height = state->view_height; + scale_box(&box, output_scale); + render_rect(output->wlr_output, damage, &box, color); + } + + if (state->border_bottom) { + if (state->parent->current.children->length == 1 + && con->current.parent->current.layout == L_VERT) { + memcpy(&color, colors->indicator, sizeof(float) * 4); + } else { + memcpy(&color, colors->child_border, sizeof(float) * 4); + } + premultiply_alpha(color, con->alpha); + box.x = state->swayc_x; + box.y = state->view_y + state->view_height; + box.width = state->swayc_width; + box.height = state->border_thickness; + scale_box(&box, output_scale); + render_rect(output->wlr_output, damage, &box, color); + } + } +} + +/** + * Render a titlebar. + * + * Care must be taken not to render over the same pixel multiple times, + * otherwise the colors will be incorrect when using opacity. + * + * The height is: 1px border, 3px padding, font height, 3px padding, 1px border + * The left side for L_TABBED is: 1px border, 2px padding, title + * The left side for other layouts is: 3px padding, title + */ +static void render_titlebar(struct sway_output *output, + pixman_region32_t *output_damage, struct sway_container *con, + int x, int y, int width, + struct border_colors *colors, struct wlr_texture *title_texture, + struct wlr_texture *marks_texture) { + struct wlr_box box; + float color[4]; + struct sway_container_state *state = &con->current; + float output_scale = output->wlr_output->scale; + enum sway_container_layout layout = state->parent->current.layout; + list_t *children = state->parent->current.children; + bool is_last_child = children->items[children->length - 1] == con; + double output_x = output->swayc->current.swayc_x; + double output_y = output->swayc->current.swayc_y; + + // Single pixel bar above title + memcpy(&color, colors->border, sizeof(float) * 4); + premultiply_alpha(color, con->alpha); + box.x = x; + box.y = y; + box.width = width; + box.height = TITLEBAR_BORDER_THICKNESS; + scale_box(&box, output_scale); + render_rect(output->wlr_output, output_damage, &box, color); + + // Single pixel bar below title + size_t left_offset = 0, right_offset = 0; + bool connects_sides = false; + if (layout == L_HORIZ || layout == L_VERT || + (layout == L_STACKED && is_last_child)) { + if (con->type == C_VIEW) { + left_offset = state->border_left * state->border_thickness; + right_offset = state->border_right * state->border_thickness; + connects_sides = true; + } + } + box.x = x + left_offset; + box.y = y + container_titlebar_height() - TITLEBAR_BORDER_THICKNESS; + box.width = width - left_offset - right_offset; + box.height = TITLEBAR_BORDER_THICKNESS; + scale_box(&box, output_scale); + render_rect(output->wlr_output, output_damage, &box, color); + + if (layout == L_TABBED) { + // Single pixel left edge + box.x = x; + box.y = y + TITLEBAR_BORDER_THICKNESS; + box.width = TITLEBAR_BORDER_THICKNESS; + box.height = + container_titlebar_height() - TITLEBAR_BORDER_THICKNESS * 2; + scale_box(&box, output_scale); + render_rect(output->wlr_output, output_damage, &box, color); + + // Single pixel right edge + box.x = (x + width - TITLEBAR_BORDER_THICKNESS) * output_scale; + render_rect(output->wlr_output, output_damage, &box, color); + } + + size_t inner_width = width - TITLEBAR_H_PADDING * 2; + + // Marks + size_t marks_ob_width = 0; // output-buffer-local + if (config->show_marks && marks_texture) { + struct wlr_box texture_box; + wlr_texture_get_size(marks_texture, + &texture_box.width, &texture_box.height); + texture_box.x = (x - output_x + width - TITLEBAR_H_PADDING) + * output_scale - texture_box.width; + texture_box.y = (y - output_y + TITLEBAR_V_PADDING) * output_scale; + + float matrix[9]; + wlr_matrix_project_box(matrix, &texture_box, + WL_OUTPUT_TRANSFORM_NORMAL, + 0.0, output->wlr_output->transform_matrix); + + if (inner_width * output_scale < texture_box.width) { + texture_box.width = inner_width * output_scale; + } + render_texture(output->wlr_output, output_damage, marks_texture, + &texture_box, matrix, con->alpha); + marks_ob_width = texture_box.width; + + // Gap between the marks and bottom padding, for when the marks texture + // height is smaller than the config's font height + memcpy(&color, colors->background, sizeof(float) * 4); + premultiply_alpha(color, con->alpha); + box.x = texture_box.x; + box.y = texture_box.y + texture_box.height; + box.width = texture_box.width; + box.height = config->font_height * output_scale - texture_box.height; + if (box.height > 0) { + render_rect(output->wlr_output, output_damage, &box, color); + } + } + + // Title text + size_t title_ob_width = 0; // output-buffer-local + if (title_texture) { + struct wlr_box texture_box; + wlr_texture_get_size(title_texture, + &texture_box.width, &texture_box.height); + texture_box.x = (x - output_x + TITLEBAR_H_PADDING) * output_scale; + texture_box.y = (y - output_y + TITLEBAR_V_PADDING) * output_scale; + + float matrix[9]; + wlr_matrix_project_box(matrix, &texture_box, + WL_OUTPUT_TRANSFORM_NORMAL, + 0.0, output->wlr_output->transform_matrix); + + if (inner_width * output_scale - marks_ob_width < texture_box.width) { + texture_box.width = inner_width * output_scale - marks_ob_width; + } + render_texture(output->wlr_output, output_damage, title_texture, + &texture_box, matrix, con->alpha); + title_ob_width = texture_box.width; + + // Gap between the title and bottom padding, for when the title texture + // height is smaller than the config's font height + memcpy(&color, colors->background, sizeof(float) * 4); + premultiply_alpha(color, con->alpha); + box.x = texture_box.x; + box.y = texture_box.y + texture_box.height; + box.width = texture_box.width; + box.height = config->font_height * output_scale - texture_box.height; + if (box.height > 0) { + render_rect(output->wlr_output, output_damage, &box, color); + } + } + + // Padding above title + memcpy(&color, colors->background, sizeof(float) * 4); + premultiply_alpha(color, con->alpha); + box.x = x + (layout == L_TABBED) * TITLEBAR_BORDER_THICKNESS; + box.y = y + TITLEBAR_BORDER_THICKNESS; + box.width = width - (layout == L_TABBED) * TITLEBAR_BORDER_THICKNESS * 2; + box.height = TITLEBAR_V_PADDING - TITLEBAR_BORDER_THICKNESS; + scale_box(&box, output_scale); + render_rect(output->wlr_output, output_damage, &box, color); + + // Padding below title + box.y = (y + TITLEBAR_V_PADDING + config->font_height) * output_scale; + render_rect(output->wlr_output, output_damage, &box, color); + + // Filler between title and marks + box.width = inner_width * output_scale - title_ob_width - marks_ob_width; + if (box.width > 0) { + box.x = (x + TITLEBAR_H_PADDING) * output_scale + title_ob_width; + box.y = (y + TITLEBAR_V_PADDING) * output_scale; + box.height = config->font_height * output_scale; + render_rect(output->wlr_output, output_damage, &box, color); + } + + // Padding left of title + left_offset = (layout == L_TABBED) * TITLEBAR_BORDER_THICKNESS; + box.x = x + left_offset; + box.y = y + TITLEBAR_V_PADDING; + box.width = TITLEBAR_H_PADDING - left_offset; + box.height = config->font_height; + scale_box(&box, output_scale); + render_rect(output->wlr_output, output_damage, &box, color); + + // Padding right of marks + right_offset = (layout == L_TABBED) * TITLEBAR_BORDER_THICKNESS; + box.x = x + width - TITLEBAR_H_PADDING; + box.y = y + TITLEBAR_V_PADDING; + box.width = TITLEBAR_H_PADDING - right_offset; + box.height = config->font_height; + scale_box(&box, output_scale); + render_rect(output->wlr_output, output_damage, &box, color); + + if (connects_sides) { + // Left pixel in line with bottom bar + box.x = x; + box.y = y + container_titlebar_height() - TITLEBAR_BORDER_THICKNESS; + box.width = state->border_thickness * state->border_left; + box.height = TITLEBAR_BORDER_THICKNESS; + scale_box(&box, output_scale); + render_rect(output->wlr_output, output_damage, &box, color); + + // Right pixel in line with bottom bar + box.x = x + width - state->border_thickness * state->border_right; + box.y = y + container_titlebar_height() - TITLEBAR_BORDER_THICKNESS; + box.width = state->border_thickness * state->border_right; + box.height = TITLEBAR_BORDER_THICKNESS; + scale_box(&box, output_scale); + render_rect(output->wlr_output, output_damage, &box, color); + } +} + +/** + * Render the top border line for a view using "border pixel". + */ +static void render_top_border(struct sway_output *output, + pixman_region32_t *output_damage, struct sway_container *con, + struct border_colors *colors) { + struct sway_container_state *state = &con->current; + if (!state->border_top) { + return; + } + struct wlr_box box; + float color[4]; + float output_scale = output->wlr_output->scale; + + // Child border - top edge + memcpy(&color, colors->child_border, sizeof(float) * 4); + premultiply_alpha(color, con->alpha); + box.x = state->swayc_x; + box.y = state->swayc_y; + box.width = state->swayc_width; + box.height = state->border_thickness; + scale_box(&box, output_scale); + render_rect(output->wlr_output, output_damage, &box, color); +} + +static void render_container(struct sway_output *output, + pixman_region32_t *damage, struct sway_container *con, bool parent_focused); + +/** + * Render a container's children using a L_HORIZ or L_VERT layout. + * + * Wrap child views in borders and leave child containers borderless because + * they'll apply their own borders to their children. + */ +static void render_container_simple(struct sway_output *output, + pixman_region32_t *damage, struct sway_container *con, + bool parent_focused) { + struct sway_seat *seat = input_manager_current_seat(input_manager); + struct sway_container *focus = seat_get_focus(seat); + + for (int i = 0; i < con->current.children->length; ++i) { + struct sway_container *child = con->current.children->items[i]; + + if (child->type == C_VIEW) { + struct sway_view *view = child->sway_view; + struct border_colors *colors; + struct wlr_texture *title_texture; + struct wlr_texture *marks_texture; + struct sway_container_state *state = &child->current; + + if (focus == child || parent_focused) { + colors = &config->border_colors.focused; + title_texture = child->title_focused; + marks_texture = view->marks_focused; + } else if (seat_get_focus_inactive(seat, con) == child) { + colors = &config->border_colors.focused_inactive; + title_texture = child->title_focused_inactive; + marks_texture = view->marks_focused_inactive; + } else { + colors = &config->border_colors.unfocused; + title_texture = child->title_unfocused; + marks_texture = view->marks_unfocused; + } + + if (state->border == B_NORMAL) { + render_titlebar(output, damage, child, state->swayc_x, + state->swayc_y, state->swayc_width, colors, + title_texture, marks_texture); + } else { + render_top_border(output, damage, child, colors); + } + render_view(output, damage, child, colors); + } else { + render_container(output, damage, child, + parent_focused || focus == child); + } + } +} + +/** + * Render a container's children using the L_TABBED layout. + */ +static void render_container_tabbed(struct sway_output *output, + pixman_region32_t *damage, struct sway_container *con, + bool parent_focused) { + if (!con->current.children->length) { + return; + } + struct sway_seat *seat = input_manager_current_seat(input_manager); + struct sway_container *focus = seat_get_focus(seat); + struct sway_container *current = seat_get_active_current_child(seat, con); + struct border_colors *current_colors = &config->border_colors.unfocused; + struct sway_container_state *pstate = &con->current; + + // Render tabs + for (int i = 0; i < con->current.children->length; ++i) { + struct sway_container *child = con->current.children->items[i]; + struct sway_view *view = child->type == C_VIEW ? child->sway_view : NULL; + struct sway_container_state *cstate = &child->current; + struct border_colors *colors; + struct wlr_texture *title_texture; + struct wlr_texture *marks_texture; + + if (focus == child || parent_focused) { + colors = &config->border_colors.focused; + title_texture = child->title_focused; + marks_texture = view ? view->marks_focused : NULL; + } else if (child == current) { + colors = &config->border_colors.focused_inactive; + title_texture = child->title_focused_inactive; + marks_texture = view ? view->marks_focused_inactive : NULL; + } else { + colors = &config->border_colors.unfocused; + title_texture = child->title_unfocused; + marks_texture = view ? view->marks_unfocused : NULL; + } + + int tab_width = pstate->swayc_width / pstate->children->length; + int x = pstate->swayc_x + tab_width * i; + // Make last tab use the remaining width of the parent + if (i == pstate->children->length - 1) { + tab_width = pstate->swayc_width - tab_width * i; + } + + render_titlebar(output, damage, child, x, cstate->swayc_y, tab_width, + colors, title_texture, marks_texture); + + if (child == current) { + current_colors = colors; + } + } + + // Render surface and left/right/bottom borders + if (current) { + if (current->type == C_VIEW) { + render_view(output, damage, current, current_colors); + } else { + render_container(output, damage, current, + parent_focused || current == focus); + } + } +} + +/** + * Render a container's children using the L_STACKED layout. + */ +static void render_container_stacked(struct sway_output *output, + pixman_region32_t *damage, struct sway_container *con, + bool parent_focused) { + if (!con->current.children->length) { + return; + } + struct sway_seat *seat = input_manager_current_seat(input_manager); + struct sway_container *focus = seat_get_focus(seat); + struct sway_container *current = seat_get_active_current_child(seat, con); + struct border_colors *current_colors = &config->border_colors.unfocused; + struct sway_container_state *pstate = &con->current; + + // Render titles + for (int i = 0; i < con->current.children->length; ++i) { + struct sway_container *child = con->current.children->items[i]; + struct sway_view *view = child->type == C_VIEW ? child->sway_view : NULL; + struct sway_container_state *cstate = &child->current; + struct border_colors *colors; + struct wlr_texture *title_texture; + struct wlr_texture *marks_texture; + + if (focus == child || parent_focused) { + colors = &config->border_colors.focused; + title_texture = child->title_focused; + marks_texture = view ? view->marks_focused : NULL; + } else if (child == current) { + colors = &config->border_colors.focused_inactive; + title_texture = child->title_focused_inactive; + marks_texture = view ? view->marks_focused_inactive : NULL; + } else { + colors = &config->border_colors.unfocused; + title_texture = child->title_unfocused; + marks_texture = view ? view->marks_unfocused : NULL; + } + + int y = pstate->swayc_y + container_titlebar_height() * i; + render_titlebar(output, damage, child, cstate->swayc_x, y, + cstate->swayc_width, colors, title_texture, marks_texture); + + if (child == current) { + current_colors = colors; + } + } + + // Render surface and left/right/bottom borders + if (current) { + if (current->type == C_VIEW) { + render_view(output, damage, current, current_colors); + } else { + render_container(output, damage, current, + parent_focused || current == focus); + } + } +} + +static void render_container(struct sway_output *output, + pixman_region32_t *damage, struct sway_container *con, + bool parent_focused) { + switch (con->current.layout) { + case L_NONE: + case L_HORIZ: + case L_VERT: + render_container_simple(output, damage, con, parent_focused); + break; + case L_STACKED: + render_container_stacked(output, damage, con, parent_focused); + break; + case L_TABBED: + render_container_tabbed(output, damage, con, parent_focused); + break; + case L_FLOATING: + sway_assert(false, "Didn't expect to see floating here"); + } +} + +static void render_floating_container(struct sway_output *soutput, + pixman_region32_t *damage, struct sway_container *con) { + if (con->type == C_VIEW) { + struct sway_view *view = con->sway_view; + struct sway_seat *seat = input_manager_current_seat(input_manager); + struct sway_container *focus = seat_get_focus(seat); + struct border_colors *colors; + struct wlr_texture *title_texture; + struct wlr_texture *marks_texture; + + if (focus == con) { + colors = &config->border_colors.focused; + title_texture = con->title_focused; + marks_texture = view->marks_focused; + } else { + colors = &config->border_colors.unfocused; + title_texture = con->title_unfocused; + marks_texture = view->marks_unfocused; + } + + if (con->current.border == B_NORMAL) { + render_titlebar(soutput, damage, con, con->current.swayc_x, + con->current.swayc_y, con->current.swayc_width, colors, + title_texture, marks_texture); + } else if (con->current.border != B_NONE) { + render_top_border(soutput, damage, con, colors); + } + render_view(soutput, damage, con, colors); + } else { + render_container(soutput, damage, con, false); + } +} + +static void render_floating(struct sway_output *soutput, + pixman_region32_t *damage) { + for (int i = 0; i < root_container.current.children->length; ++i) { + struct sway_container *output = + root_container.current.children->items[i]; + for (int j = 0; j < output->current.children->length; ++j) { + struct sway_container *ws = output->current.children->items[j]; + if (!workspace_is_visible(ws)) { + continue; + } + list_t *floating = + ws->current.ws_floating->current.children; + for (int k = 0; k < floating->length; ++k) { + struct sway_container *floater = floating->items[k]; + render_floating_container(soutput, damage, floater); + } + } + } +} + +void output_render(struct sway_output *output, struct timespec *when, + pixman_region32_t *damage) { + struct wlr_output *wlr_output = output->wlr_output; + + struct wlr_renderer *renderer = + wlr_backend_get_renderer(wlr_output->backend); + if (!sway_assert(renderer != NULL, + "expected the output backend to have a renderer")) { + return; + } + + wlr_renderer_begin(renderer, wlr_output->width, wlr_output->height); + + bool damage_whole_before_swap = false; + if (!pixman_region32_not_empty(damage)) { + // Output isn't damaged but needs buffer swap + goto renderer_end; + } + + const char *damage_debug = getenv("SWAY_DAMAGE_DEBUG"); + if (damage_debug != NULL) { + if (strcmp(damage_debug, "highlight") == 0) { + wlr_renderer_clear(renderer, (float[]){1, 1, 0, 1}); + damage_whole_before_swap = true; + } else if (strcmp(damage_debug, "rerender") == 0) { + int width, height; + wlr_output_transformed_resolution(wlr_output, &width, &height); + pixman_region32_union_rect(damage, damage, 0, 0, width, height); + } + } + + struct sway_container *workspace = output_get_active_workspace(output); + struct sway_view *fullscreen_view = workspace->current.ws_fullscreen; + struct sway_seat *seat = input_manager_current_seat(input_manager); + + if (output_has_opaque_lockscreen(output, seat)) { + struct wlr_layer_surface *wlr_layer_surface = seat->focused_layer; + struct sway_layer_surface *sway_layer_surface = + layer_from_wlr_layer_surface(seat->focused_layer); + struct render_data data = { + .output = output, + .damage = damage, + .alpha = 1.0f, + }; + output_surface_for_each_surface(wlr_layer_surface->surface, + sway_layer_surface->geo.x, sway_layer_surface->geo.y, + &data.root_geo, render_surface_iterator, &data); + } else if (fullscreen_view) { + float clear_color[] = {0.0f, 0.0f, 0.0f, 1.0f}; + + int nrects; + pixman_box32_t *rects = pixman_region32_rectangles(damage, &nrects); + for (int i = 0; i < nrects; ++i) { + scissor_output(wlr_output, &rects[i]); + wlr_renderer_clear(renderer, clear_color); + } + + // TODO: handle views smaller than the output + render_view_surfaces(fullscreen_view, output, damage, 1.0f); + + if (fullscreen_view->type == SWAY_VIEW_XWAYLAND) { + render_unmanaged(output, damage, + &root_container.sway_root->xwayland_unmanaged); + } + } else { + float clear_color[] = {0.25f, 0.25f, 0.25f, 1.0f}; + + int nrects; + pixman_box32_t *rects = pixman_region32_rectangles(damage, &nrects); + for (int i = 0; i < nrects; ++i) { + scissor_output(wlr_output, &rects[i]); + wlr_renderer_clear(renderer, clear_color); + } + + render_layer(output, damage, + &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND]); + render_layer(output, damage, + &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM]); + + struct sway_seat *seat = input_manager_current_seat(input_manager); + struct sway_container *focus = seat_get_focus(seat); + render_container(output, damage, workspace, focus == workspace); + render_floating(output, damage); + + render_unmanaged(output, damage, + &root_container.sway_root->xwayland_unmanaged); + render_layer(output, damage, + &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]); + } + render_layer(output, damage, + &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]); + render_drag_icons(output, damage, &root_container.sway_root->drag_icons); + +renderer_end: + if (root_container.sway_root->debug_tree) { + wlr_render_texture(renderer, root_container.sway_root->debug_tree, + wlr_output->transform_matrix, 0, 0, 1); + } + + if (damage_whole_before_swap || root_container.sway_root->debug_tree) { + int width, height; + wlr_output_transformed_resolution(wlr_output, &width, &height); + pixman_region32_union_rect(damage, damage, 0, 0, width, height); + } + + wlr_renderer_scissor(renderer, NULL); + wlr_renderer_end(renderer); + if (!wlr_output_damage_swap_buffers(output->damage, when, damage)) { + return; + } + output->last_frame = *when; +} diff --git a/sway/meson.build b/sway/meson.build index a81a3406..e492aeee 100644 --- a/sway/meson.build +++ b/sway/meson.build @@ -13,6 +13,7 @@ sway_sources = files( 'desktop/idle_inhibit_v1.c', 'desktop/layer_shell.c', 'desktop/output.c', + 'desktop/render.c', 'desktop/transaction.c', 'desktop/xdg_shell_v6.c', 'desktop/xdg_shell.c', -- cgit v1.2.3-70-g09d2 From ab8a86369c01c7146991ff4ae2ef04b0a1db06ca Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Sat, 7 Jul 2018 18:36:20 +1000 Subject: Implement some floating move commands This implements the following for floating containers: * move * move [absolute] position * move [absolute] position mouse --- include/sway/desktop.h | 4 ++ include/sway/output.h | 2 + include/sway/tree/container.h | 6 +++ sway/commands/move.c | 122 ++++++++++++++++++++++++++++++++++++------ sway/desktop/desktop.c | 9 ++++ sway/tree/container.c | 82 ++++++++++++++++++++++++++++ 6 files changed, 208 insertions(+), 17 deletions(-) (limited to 'include/sway') diff --git a/include/sway/desktop.h b/include/sway/desktop.h index f1ad759a..348fb187 100644 --- a/include/sway/desktop.h +++ b/include/sway/desktop.h @@ -1,4 +1,8 @@ #include +struct sway_container; + void desktop_damage_surface(struct wlr_surface *surface, double lx, double ly, bool whole); + +void desktop_damage_whole_container(struct sway_container *con); diff --git a/include/sway/output.h b/include/sway/output.h index b6cda83c..bd25e76e 100644 --- a/include/sway/output.h +++ b/include/sway/output.h @@ -97,4 +97,6 @@ void output_drag_icons_for_each_surface(struct wl_list *drag_icons, struct sway_output *output, struct root_geometry *geo, wlr_surface_iterator_func_t iterator, void *user_data); +struct sway_container *output_get_active_workspace(struct sway_output *output); + #endif diff --git a/include/sway/tree/container.h b/include/sway/tree/container.h index 728daa84..a69da9db 100644 --- a/include/sway/tree/container.h +++ b/include/sway/tree/container.h @@ -297,4 +297,10 @@ bool container_is_floating(struct sway_container *container); */ void container_get_box(struct sway_container *container, struct wlr_box *box); +/** + * Move a floating container to a new layout-local position. + */ +void container_floating_move_to(struct sway_container *con, + double lx, double ly); + #endif diff --git a/sway/commands/move.c b/sway/commands/move.c index a4fae388..a1c1e018 100644 --- a/sway/commands/move.c +++ b/sway/commands/move.c @@ -1,11 +1,13 @@ #define _XOPEN_SOURCE 500 #include #include +#include #include #include #include #include "sway/commands.h" #include "sway/desktop/transaction.h" +#include "sway/input/cursor.h" #include "sway/input/seat.h" #include "sway/output.h" #include "sway/tree/arrange.h" @@ -184,11 +186,49 @@ static struct cmd_results *cmd_move_workspace(struct sway_container *current, } static struct cmd_results *move_in_direction(struct sway_container *container, - enum movement_direction direction, int move_amt) { + enum movement_direction direction, int argc, char **argv) { + int move_amt = 10; + if (argc > 1) { + char *inv; + move_amt = (int)strtol(argv[1], &inv, 10); + if (*inv != '\0' && strcasecmp(inv, "px") != 0) { + return cmd_results_new(CMD_FAILURE, "move", + "Invalid distance specified"); + } + } + if (container->type == C_WORKSPACE) { return cmd_results_new(CMD_FAILURE, "move", "Cannot move workspaces in a direction"); } + if (container_is_floating(container)) { + if (container->type == C_VIEW && container->sway_view->is_fullscreen) { + return cmd_results_new(CMD_FAILURE, "move", + "Cannot move fullscreen floating container"); + } + double lx = container->x; + double ly = container->y; + switch (direction) { + case MOVE_LEFT: + lx -= move_amt; + break; + case MOVE_RIGHT: + lx += move_amt; + break; + case MOVE_UP: + ly -= move_amt; + break; + case MOVE_DOWN: + ly += move_amt; + break; + case MOVE_PARENT: + case MOVE_CHILD: + return cmd_results_new(CMD_FAILURE, "move", + "Cannot move floating container to parent or child"); + } + container_floating_move_to(container, lx, ly); + return cmd_results_new(CMD_SUCCESS, NULL, NULL); + } // For simplicity, we'll arrange the entire workspace. The reason for this // is moving the container might reap the old parent, and container_move // does not return a surviving parent. @@ -208,31 +248,78 @@ static struct cmd_results *move_in_direction(struct sway_container *container, return cmd_results_new(CMD_SUCCESS, NULL, NULL); } +static const char* expected_position_syntax = + "Expected 'move [absolute] position ' or " + "'move [absolute] position mouse'"; + +static struct cmd_results *move_to_position(struct sway_container *container, + int argc, char **argv) { + if (!container_is_floating(container)) { + return cmd_results_new(CMD_FAILURE, "move", + "Only floating containers " + "can be moved to an absolute position"); + } + if (!argc) { + return cmd_results_new(CMD_FAILURE, "move", expected_position_syntax); + } + if (strcmp(argv[0], "absolute") == 0) { + --argc; + ++argv; + } + if (!argc) { + return cmd_results_new(CMD_FAILURE, "move", expected_position_syntax); + } + if (strcmp(argv[0], "position") == 0) { + --argc; + ++argv; + } + if (!argc) { + return cmd_results_new(CMD_FAILURE, "move", expected_position_syntax); + } + if (strcmp(argv[0], "mouse") == 0) { + struct sway_seat *seat = config->handler_context.seat; + if (!seat->cursor) { + return cmd_results_new(CMD_FAILURE, "move", "No cursor device"); + } + double lx = seat->cursor->cursor->x - container->width / 2; + double ly = seat->cursor->cursor->y - container->height / 2; + container_floating_move_to(container, lx, ly); + return cmd_results_new(CMD_SUCCESS, NULL, NULL); + } + if (argc != 2) { + return cmd_results_new(CMD_FAILURE, "move", expected_position_syntax); + } + double lx, ly; + char *inv; + lx = (double)strtol(argv[0], &inv, 10); + if (*inv != '\0' && strcasecmp(inv, "px") != 0) { + return cmd_results_new(CMD_FAILURE, "move", + "Invalid position specified"); + } + ly = (double)strtol(argv[1], &inv, 10); + if (*inv != '\0' && strcasecmp(inv, "px") != 0) { + return cmd_results_new(CMD_FAILURE, "move", + "Invalid position specified"); + } + container_floating_move_to(container, lx, ly); + return cmd_results_new(CMD_SUCCESS, NULL, NULL); +} + struct cmd_results *cmd_move(int argc, char **argv) { struct cmd_results *error = NULL; - int move_amt = 10; if ((error = checkarg(argc, "move", EXPECTED_AT_LEAST, 1))) { return error; } struct sway_container *current = config->handler_context.current_container; - if (argc == 2 || (argc == 3 && strcasecmp(argv[2], "px") == 0)) { - char *inv; - move_amt = (int)strtol(argv[1], &inv, 10); - if (*inv != '\0' && strcasecmp(inv, "px") != 0) { - return cmd_results_new(CMD_FAILURE, "move", - "Invalid distance specified"); - } - } - if (strcasecmp(argv[0], "left") == 0) { - return move_in_direction(current, MOVE_LEFT, move_amt); + return move_in_direction(current, MOVE_LEFT, argc, argv); } else if (strcasecmp(argv[0], "right") == 0) { - return move_in_direction(current, MOVE_RIGHT, move_amt); + return move_in_direction(current, MOVE_RIGHT, argc, argv); } else if (strcasecmp(argv[0], "up") == 0) { - return move_in_direction(current, MOVE_UP, move_amt); + return move_in_direction(current, MOVE_UP, argc, argv); } else if (strcasecmp(argv[0], "down") == 0) { - return move_in_direction(current, MOVE_DOWN, move_amt); + return move_in_direction(current, MOVE_DOWN, argc, argv); } else if (strcasecmp(argv[0], "container") == 0 || strcasecmp(argv[0], "window") == 0) { return cmd_move_container(current, argc, argv); @@ -244,8 +331,9 @@ struct cmd_results *cmd_move(int argc, char **argv) { // TODO: scratchpad return cmd_results_new(CMD_FAILURE, "move", "Unimplemented"); } else if (strcasecmp(argv[0], "position") == 0) { - // TODO: floating - return cmd_results_new(CMD_FAILURE, "move", "Unimplemented"); + return move_to_position(current, argc, argv); + } else if (strcasecmp(argv[0], "absolute") == 0) { + return move_to_position(current, argc, argv); } else { return cmd_results_new(CMD_INVALID, "move", expected_syntax); } diff --git a/sway/desktop/desktop.c b/sway/desktop/desktop.c index e495790c..6575519d 100644 --- a/sway/desktop/desktop.c +++ b/sway/desktop/desktop.c @@ -13,3 +13,12 @@ void desktop_damage_surface(struct wlr_surface *surface, double lx, double ly, } } } + +void desktop_damage_whole_container(struct sway_container *con) { + for (int i = 0; i < root_container.children->length; ++i) { + struct sway_container *cont = root_container.children->items[i]; + if (cont->type == C_OUTPUT) { + output_damage_whole_container(cont->sway_output, con); + } + } +} diff --git a/sway/tree/container.c b/sway/tree/container.c index 5fdcb6e3..2df2332c 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -11,11 +11,14 @@ #include "cairo.h" #include "pango.h" #include "sway/config.h" +#include "sway/desktop.h" +#include "sway/desktop/transaction.h" #include "sway/input/input-manager.h" #include "sway/input/seat.h" #include "sway/ipc-server.h" #include "sway/output.h" #include "sway/server.h" +#include "sway/tree/arrange.h" #include "sway/tree/layout.h" #include "sway/tree/view.h" #include "sway/tree/workspace.h" @@ -989,3 +992,82 @@ void container_get_box(struct sway_container *container, struct wlr_box *box) { box->width = container->width; box->height = container->height; } + +/** + * Translate the container's position as well as all children. + */ +static void container_floating_translate(struct sway_container *con, + double x_amount, double y_amount) { + con->x += x_amount; + con->y += y_amount; + con->current.swayc_x += x_amount; + con->current.swayc_y += y_amount; + if (con->type == C_VIEW) { + con->sway_view->x += x_amount; + con->sway_view->y += y_amount; + con->current.view_x += x_amount; + con->current.view_y += y_amount; + } else { + for (int i = 0; i < con->children->length; ++i) { + struct sway_container *child = con->children->items[i]; + container_floating_translate(child, x_amount, y_amount); + } + } +} + +/** + * Choose an output for the floating container's new position. + * + * If the center of the container intersects an output then we'll choose that + * one, otherwise we'll choose whichever output is closest to the container's + * center. + */ +static struct sway_container *container_floating_find_output( + struct sway_container *con) { + double center_x = con->x + con->width / 2; + double center_y = con->y + con->height / 2; + struct sway_container *closest_output = NULL; + double closest_distance = DBL_MAX; + for (int i = 0; i < root_container.children->length; ++i) { + struct sway_container *output = root_container.children->items[i]; + struct wlr_box output_box; + double closest_x, closest_y; + container_get_box(output, &output_box); + wlr_box_closest_point(&output_box, center_x, center_y, + &closest_x, &closest_y); + if (center_x == closest_x && center_y == closest_y) { + // The center of the floating container is on this output + return output; + } + double x_dist = closest_x - center_x; + double y_dist = closest_y - center_y; + double distance = x_dist * x_dist + y_dist * y_dist; + if (distance < closest_distance) { + closest_output = output; + closest_distance = distance; + } + } + return closest_output; +} + +void container_floating_move_to(struct sway_container *con, + double lx, double ly) { + desktop_damage_whole_container(con); + container_floating_translate(con, lx - con->x, ly - con->y); + desktop_damage_whole_container(con); + struct sway_container *old_workspace = container_parent(con, C_WORKSPACE); + struct sway_container *new_output = container_floating_find_output(con); + if (!sway_assert(new_output, "Unable to find any output")) { + return; + } + struct sway_container *new_workspace = + output_get_active_workspace(new_output->sway_output); + if (old_workspace != new_workspace) { + container_remove_child(con); + container_add_child(new_workspace->sway_workspace->floating, con); + struct sway_transaction *transaction = transaction_create(); + arrange_windows(old_workspace, transaction); + arrange_windows(new_workspace, transaction); + transaction_commit(transaction); + } +} -- cgit v1.2.3-70-g09d2 From b0fc7e98501fc885197de90af6579dc121e7ef46 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Mon, 9 Jul 2018 23:41:00 +1000 Subject: Remove duplicate function declaration and add assertion --- include/sway/output.h | 2 -- sway/tree/container.c | 4 ++++ 2 files changed, 4 insertions(+), 2 deletions(-) (limited to 'include/sway') diff --git a/include/sway/output.h b/include/sway/output.h index bd25e76e..b6cda83c 100644 --- a/include/sway/output.h +++ b/include/sway/output.h @@ -97,6 +97,4 @@ void output_drag_icons_for_each_surface(struct wl_list *drag_icons, struct sway_output *output, struct root_geometry *geo, wlr_surface_iterator_func_t iterator, void *user_data); -struct sway_container *output_get_active_workspace(struct sway_output *output); - #endif diff --git a/sway/tree/container.c b/sway/tree/container.c index 2df2332c..92408ce6 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -1052,6 +1052,10 @@ static struct sway_container *container_floating_find_output( void container_floating_move_to(struct sway_container *con, double lx, double ly) { + if (!sway_assert(container_is_floating(con), + "Expected a floating container")) { + return; + } desktop_damage_whole_container(con); container_floating_translate(con, lx - con->x, ly - con->y); desktop_damage_whole_container(con); -- cgit v1.2.3-70-g09d2 From 23c1c26c3fedf5470dbee9fe97c2374a48588863 Mon Sep 17 00:00:00 2001 From: Ian Fan Date: Sun, 8 Jul 2018 20:34:47 +0100 Subject: Add get_config message type to ipc --- include/ipc.h | 1 + include/sway/config.h | 4 +-- sway/commands/output/background.c | 2 +- sway/commands/reload.c | 2 +- sway/config.c | 53 ++++++++++++++++++++++++++++++--------- sway/ipc-json.c | 2 ++ sway/ipc-server.c | 12 +++++++++ swaymsg/main.c | 15 ++++++++++- swaymsg/swaymsg.1.scd | 3 +++ 9 files changed, 76 insertions(+), 18 deletions(-) (limited to 'include/sway') diff --git a/include/ipc.h b/include/ipc.h index c9c5b1cd..6f6795b3 100644 --- a/include/ipc.h +++ b/include/ipc.h @@ -14,6 +14,7 @@ enum ipc_command_type { IPC_GET_BAR_CONFIG = 6, IPC_GET_VERSION = 7, IPC_GET_BINDING_MODES = 8, + IPC_GET_CONFIG = 9, // sway-specific command types IPC_GET_INPUTS = 100, diff --git a/include/sway/config.h b/include/sway/config.h index ac668c24..d5e4116f 100644 --- a/include/sway/config.h +++ b/include/sway/config.h @@ -341,6 +341,7 @@ struct sway_config { int gaps_outer; list_t *config_chain; + const char *current_config_path; const char *current_config; enum sway_container_border border; @@ -496,7 +497,4 @@ void config_update_font_height(bool recalculate); /* Global config singleton. */ extern struct sway_config *config; -/* Config file currently being read */ -extern const char *current_config_path; - #endif diff --git a/sway/commands/output/background.c b/sway/commands/output/background.c index c2c138f8..4ed56c2a 100644 --- a/sway/commands/output/background.c +++ b/sway/commands/output/background.c @@ -80,7 +80,7 @@ struct cmd_results *output_cmd_background(int argc, char **argv) { if (config->reading && *src != '/') { // src file is inside configuration dir - char *conf = strdup(config->current_config); + char *conf = strdup(config->current_config_path); if (!conf) { wlr_log(WLR_ERROR, "Failed to duplicate string"); free(src); diff --git a/sway/commands/reload.c b/sway/commands/reload.c index 9fc213c4..c6715f9c 100644 --- a/sway/commands/reload.c +++ b/sway/commands/reload.c @@ -7,7 +7,7 @@ struct cmd_results *cmd_reload(int argc, char **argv) { if ((error = checkarg(argc, "reload", EXPECTED_EQUAL_TO, 0))) { return error; } - if (!load_main_config(config->current_config, true)) { + if (!load_main_config(config->current_config_path, true)) { return cmd_results_new(CMD_FAILURE, "reload", "Error(s) reloading config."); } diff --git a/sway/config.c b/sway/config.c index d0e0e432..c59f4f0d 100644 --- a/sway/config.c +++ b/sway/config.c @@ -117,6 +117,7 @@ void free_config(struct sway_config *config) { free(config->floating_scroll_left_cmd); free(config->floating_scroll_right_cmd); free(config->font); + free((char *)config->current_config_path); free((char *)config->current_config); free(config); } @@ -205,6 +206,7 @@ static void config_defaults(struct sway_config *config) { if (!(config->active_bar_modifiers = create_list())) goto cleanup; if (!(config->config_chain = create_list())) goto cleanup; + config->current_config_path = NULL; config->current_config = NULL; // borders @@ -304,8 +306,6 @@ static char *get_config_path(void) { return NULL; // Not reached } -const char *current_config_path; - static bool load_config(const char *path, struct sway_config *config) { if (path == NULL) { wlr_log(WLR_ERROR, "Unable to find a config file!"); @@ -313,7 +313,6 @@ static bool load_config(const char *path, struct sway_config *config) { } wlr_log(WLR_INFO, "Loading config from %s", path); - current_config_path = path; struct stat sb; if (stat(path, &sb) == 0 && S_ISDIR(sb.st_mode)) { @@ -333,7 +332,6 @@ static bool load_config(const char *path, struct sway_config *config) { wlr_log(WLR_ERROR, "Error(s) loading config!"); } - current_config_path = NULL; return true; } @@ -358,7 +356,7 @@ bool load_main_config(const char *file, bool is_active) { config->active = true; } - config->current_config = path; + config->current_config_path = path; list_add(config->config_chain, path); config->reading = true; @@ -428,7 +426,7 @@ bool load_main_config(const char *file, bool is_active) { static bool load_include_config(const char *path, const char *parent_dir, struct sway_config *config) { // save parent config - const char *parent_config = config->current_config; + const char *parent_config = config->current_config_path; char *full_path; int len = strlen(path); @@ -466,25 +464,25 @@ static bool load_include_config(const char *path, const char *parent_dir, } } - config->current_config = real_path; + config->current_config_path = real_path; list_add(config->config_chain, real_path); int index = config->config_chain->length - 1; if (!load_config(real_path, config)) { free(real_path); - config->current_config = parent_config; + config->current_config_path = parent_config; list_del(config->config_chain, index); return false; } - // restore current_config - config->current_config = parent_config; + // restore current_config_path + config->current_config_path = parent_config; return true; } bool load_include_configs(const char *path, struct sway_config *config) { char *wd = getcwd(NULL, 0); - char *parent_path = strdup(config->current_config); + char *parent_path = strdup(config->current_config_path); const char *parent_dir = dirname(parent_path); if (chdir(parent_dir) < 0) { @@ -561,6 +559,23 @@ static char *expand_line(const char *block, const char *line, bool add_brace) { } bool read_config(FILE *file, struct sway_config *config) { + bool reading_main_config = false; + char *current_config, *config_pos; + long config_size = 0; + if (config->current_config == NULL) { + reading_main_config = true; + + fseek(file, 0, SEEK_END); + config_size = ftell(file); + rewind(file); + + config_pos = current_config = malloc(config_size + 1); + if (current_config == NULL) { + wlr_log(WLR_ERROR, "Unable to allocate buffer for config contents"); + return false; + } + } + bool success = true; int line_number = 0; char *line; @@ -573,6 +588,14 @@ bool read_config(FILE *file, struct sway_config *config) { } line_number++; wlr_log(WLR_DEBUG, "Read line %d: %s", line_number, line); + + if (reading_main_config) { + size_t l = strlen(line); + memcpy(config_pos, line, l); // don't copy terminating character + config_pos += l; + *config_pos++ = '\n'; + } + line = strip_whitespace(line); if (line[0] == '#') { free(line); @@ -592,6 +615,8 @@ bool read_config(FILE *file, struct sway_config *config) { if (!expanded) { list_foreach(stack, free); list_free(stack); + free(line); + free(current_config); return false; } wlr_log(WLR_DEBUG, "Expanded line: %s", expanded); @@ -607,7 +632,7 @@ bool read_config(FILE *file, struct sway_config *config) { case CMD_FAILURE: case CMD_INVALID: wlr_log(WLR_ERROR, "Error on line %i '%s': %s (%s)", line_number, - line, res->error, config->current_config); + line, res->error, config->current_config_path); success = false; break; @@ -652,6 +677,10 @@ bool read_config(FILE *file, struct sway_config *config) { list_foreach(stack, free); list_free(stack); + if (reading_main_config) { + current_config[config_size - 1] = '\0'; + config->current_config = current_config; + } return success; } diff --git a/sway/ipc-json.c b/sway/ipc-json.c index b9289e25..3d0e88f0 100644 --- a/sway/ipc-json.c +++ b/sway/ipc-json.c @@ -2,6 +2,7 @@ #include #include #include "log.h" +#include "sway/config.h" #include "sway/ipc-json.h" #include "sway/tree/container.h" #include "sway/tree/workspace.h" @@ -41,6 +42,7 @@ json_object *ipc_json_get_version() { json_object_object_add(version, "major", json_object_new_int(major)); json_object_object_add(version, "minor", json_object_new_int(minor)); json_object_object_add(version, "patch", json_object_new_int(patch)); + json_object_object_add(version, "loaded_config_file_name", json_object_new_string(config->current_config_path)); return version; } diff --git a/sway/ipc-server.c b/sway/ipc-server.c index 70a4141e..c5161a6b 100644 --- a/sway/ipc-server.c +++ b/sway/ipc-server.c @@ -17,6 +17,7 @@ #include #include #include "sway/commands.h" +#include "sway/config.h" #include "sway/ipc-json.h" #include "sway/ipc-server.h" #include "sway/output.h" @@ -681,6 +682,17 @@ void ipc_client_handle_command(struct ipc_client *client) { goto exit_cleanup; } + case IPC_GET_CONFIG: + { + json_object *json = json_object_new_object(); + json_object_object_add(json, "config", json_object_new_string(config->current_config)); + const char *json_string = json_object_to_json_string(json); + client_valid = + ipc_send_reply(client, json_string, (uint32_t)strlen(json_string)); + json_object_put(json); // free + goto exit_cleanup; + } + default: wlr_log(WLR_INFO, "Unknown IPC command type %i", client->current_command); goto exit_cleanup; diff --git a/swaymsg/main.c b/swaymsg/main.c index 42e488f3..4c068f69 100644 --- a/swaymsg/main.c +++ b/swaymsg/main.c @@ -240,6 +240,12 @@ static void pretty_print_version(json_object *v) { printf("sway version %s\n", json_object_get_string(ver)); } +static void pretty_print_config(json_object *c) { + json_object *config; + json_object_object_get_ex(c, "config", &config); + printf("%s\n", json_object_get_string(config)); +} + static void pretty_print_clipboard(json_object *v) { if (success(v, true)) { if (json_object_is_type(v, json_type_array)) { @@ -277,7 +283,7 @@ static void pretty_print(int type, json_object *resp) { if (type != IPC_COMMAND && type != IPC_GET_WORKSPACES && type != IPC_GET_INPUTS && type != IPC_GET_OUTPUTS && type != IPC_GET_VERSION && type != IPC_GET_CLIPBOARD && - type != IPC_GET_SEATS) { + type != IPC_GET_SEATS && type != IPC_GET_CONFIG) { printf("%s\n", json_object_to_json_string_ext(resp, JSON_C_TO_STRING_PRETTY | JSON_C_TO_STRING_SPACED)); return; @@ -288,6 +294,11 @@ static void pretty_print(int type, json_object *resp) { return; } + if (type == IPC_GET_CONFIG) { + pretty_print_config(resp); + return; + } + if (type == IPC_GET_CLIPBOARD) { pretty_print_clipboard(resp); return; @@ -409,6 +420,8 @@ int main(int argc, char **argv) { type = IPC_GET_VERSION; } else if (strcasecmp(cmdtype, "get_binding_modes") == 0) { type = IPC_GET_BINDING_MODES; + } else if (strcasecmp(cmdtype, "get_config") == 0) { + type = IPC_GET_CONFIG; } else if (strcasecmp(cmdtype, "get_clipboard") == 0) { type = IPC_GET_CLIPBOARD; } else { diff --git a/swaymsg/swaymsg.1.scd b/swaymsg/swaymsg.1.scd index f9b600b9..59a706d4 100644 --- a/swaymsg/swaymsg.1.scd +++ b/swaymsg/swaymsg.1.scd @@ -62,6 +62,9 @@ _swaymsg_ [options...] [message] *get\_binding\_modes* Gets a JSON-encoded list of currently configured binding modes. +*get\_config* + Gets a JSON-encoded copy of the current configuration. + *get\_clipboard* Get JSON-encoded information about the clipboard. Returns the current clipboard mime-types if called without -- cgit v1.2.3-70-g09d2 From ba3511b2431d331b735770e38e6bbd83cb5a9d66 Mon Sep 17 00:00:00 2001 From: emersion Date: Mon, 9 Jul 2018 22:39:47 +0100 Subject: Remove `clipboard` command and `get_clipboard` message --- completions/zsh/_swaymsg | 1 - include/ipc.h | 3 +-- include/sway/commands.h | 1 - include/sway/config.h | 5 ++--- swaymsg/main.c | 44 ++------------------------------------------ swaymsg/swaymsg.1.scd | 6 ------ 6 files changed, 5 insertions(+), 55 deletions(-) (limited to 'include/sway') diff --git a/completions/zsh/_swaymsg b/completions/zsh/_swaymsg index 6bb03279..2e39deb6 100644 --- a/completions/zsh/_swaymsg +++ b/completions/zsh/_swaymsg @@ -22,7 +22,6 @@ types=( 'get_marks' 'get_bar_config' 'get_version' -'get_clipboard' ) _arguments -s \ diff --git a/include/ipc.h b/include/ipc.h index 6f6795b3..0010718b 100644 --- a/include/ipc.h +++ b/include/ipc.h @@ -18,8 +18,7 @@ enum ipc_command_type { // sway-specific command types IPC_GET_INPUTS = 100, - IPC_GET_CLIPBOARD = 101, - IPC_GET_SEATS = 102, + IPC_GET_SEATS = 101, // Events sent from sway to clients. Events have the highest bits set. IPC_EVENT_WORKSPACE = ((1<<31) | 0), diff --git a/include/sway/commands.h b/include/sway/commands.h index 6d17144a..dda0606a 100644 --- a/include/sway/commands.h +++ b/include/sway/commands.h @@ -95,7 +95,6 @@ sway_cmd cmd_client_unfocused; sway_cmd cmd_client_urgent; sway_cmd cmd_client_placeholder; sway_cmd cmd_client_background; -sway_cmd cmd_clipboard; sway_cmd cmd_commands; sway_cmd cmd_debuglog; sway_cmd cmd_default_border; diff --git a/include/sway/config.h b/include/sway/config.h index d5e4116f..99575274 100644 --- a/include/sway/config.h +++ b/include/sway/config.h @@ -271,11 +271,10 @@ enum ipc_feature { IPC_FEATURE_EVENT_WINDOW = 2048, IPC_FEATURE_EVENT_BINDING = 4096, IPC_FEATURE_EVENT_INPUT = 8192, - IPC_FEATURE_GET_CLIPBOARD = 16384, - IPC_FEATURE_GET_SEATS = 32768, + IPC_FEATURE_GET_SEATS = 16384, IPC_FEATURE_ALL_COMMANDS = - 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128 | 16384 | 32768, + 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128 | 16384, IPC_FEATURE_ALL_EVENTS = 256 | 512 | 1024 | 2048 | 4096 | 8192, IPC_FEATURE_ALL = IPC_FEATURE_ALL_COMMANDS | IPC_FEATURE_ALL_EVENTS, diff --git a/swaymsg/main.c b/swaymsg/main.c index 4c068f69..c4141ca5 100644 --- a/swaymsg/main.c +++ b/swaymsg/main.c @@ -246,44 +246,11 @@ static void pretty_print_config(json_object *c) { printf("%s\n", json_object_get_string(config)); } -static void pretty_print_clipboard(json_object *v) { - if (success(v, true)) { - if (json_object_is_type(v, json_type_array)) { - for (size_t i = 0; i < json_object_array_length(v); ++i) { - json_object *o = json_object_array_get_idx(v, i); - printf("%s\n", json_object_get_string(o)); - } - } else { - // NOTE: could be extended to print all received types - // instead just the first one when sways ipc server - // supports it - struct json_object_iterator iter = json_object_iter_begin(v); - struct json_object_iterator end = json_object_iter_end(v); - if (!json_object_iter_equal(&iter, &end)) { - json_object *obj = json_object_iter_peek_value(&iter); - if (success(obj, false)) { - json_object *content; - json_object_object_get_ex(obj, "content", &content); - printf("%s\n", json_object_get_string(content)); - } else { - json_object *error; - json_object_object_get_ex(obj, "error", &error); - printf("Error: %s\n", json_object_get_string(error)); - } - } - } - } else { - json_object *error; - json_object_object_get_ex(v, "error", &error); - printf("Error: %s\n", json_object_get_string(error)); - } -} - static void pretty_print(int type, json_object *resp) { if (type != IPC_COMMAND && type != IPC_GET_WORKSPACES && type != IPC_GET_INPUTS && type != IPC_GET_OUTPUTS && - type != IPC_GET_VERSION && type != IPC_GET_CLIPBOARD && - type != IPC_GET_SEATS && type != IPC_GET_CONFIG) { + type != IPC_GET_VERSION && type != IPC_GET_SEATS && + type != IPC_GET_CONFIG) { printf("%s\n", json_object_to_json_string_ext(resp, JSON_C_TO_STRING_PRETTY | JSON_C_TO_STRING_SPACED)); return; @@ -299,11 +266,6 @@ static void pretty_print(int type, json_object *resp) { return; } - if (type == IPC_GET_CLIPBOARD) { - pretty_print_clipboard(resp); - return; - } - json_object *obj; size_t len = json_object_array_length(resp); for (size_t i = 0; i < len; ++i) { @@ -422,8 +384,6 @@ int main(int argc, char **argv) { type = IPC_GET_BINDING_MODES; } else if (strcasecmp(cmdtype, "get_config") == 0) { type = IPC_GET_CONFIG; - } else if (strcasecmp(cmdtype, "get_clipboard") == 0) { - type = IPC_GET_CLIPBOARD; } else { sway_abort("Unknown message type %s", cmdtype); } diff --git a/swaymsg/swaymsg.1.scd b/swaymsg/swaymsg.1.scd index 59a706d4..a6e279da 100644 --- a/swaymsg/swaymsg.1.scd +++ b/swaymsg/swaymsg.1.scd @@ -64,9 +64,3 @@ _swaymsg_ [options...] [message] *get\_config* Gets a JSON-encoded copy of the current configuration. - -*get\_clipboard* - Get JSON-encoded information about the clipboard. - Returns the current clipboard mime-types if called without - arguments, otherwise returns the clipboard data in the requested - formats. Encodes the data using base64 for non-text mime types. -- cgit v1.2.3-70-g09d2 From 41b80c28dfafb9bc13b68e4d5d2811d311b59863 Mon Sep 17 00:00:00 2001 From: Robert Kubosz Date: Wed, 11 Jul 2018 22:03:06 +0200 Subject: add scroll button option This commit introduces a scroll_button option, which is intended to be used with scroll_method. Now user can edit his sway config and add an scroll_button option to device section. --- include/sway/commands.h | 1 + include/sway/config.h | 1 + sway/commands/input.c | 1 + sway/commands/input/scroll_button.c | 31 +++++++++++++++++++++++++++++++ sway/config/input.c | 4 ++++ sway/input/input-manager.c | 6 ++++++ sway/meson.build | 1 + 7 files changed, 45 insertions(+) create mode 100644 sway/commands/input/scroll_button.c (limited to 'include/sway') diff --git a/include/sway/commands.h b/include/sway/commands.h index dda0606a..32d6cefd 100644 --- a/include/sway/commands.h +++ b/include/sway/commands.h @@ -207,6 +207,7 @@ sway_cmd input_cmd_natural_scroll; sway_cmd input_cmd_pointer_accel; sway_cmd input_cmd_repeat_delay; sway_cmd input_cmd_repeat_rate; +sway_cmd input_cmd_scroll_button; sway_cmd input_cmd_scroll_method; sway_cmd input_cmd_tap; sway_cmd input_cmd_xkb_layout; diff --git a/include/sway/config.h b/include/sway/config.h index 99575274..75acd4f2 100644 --- a/include/sway/config.h +++ b/include/sway/config.h @@ -75,6 +75,7 @@ struct input_config { float pointer_accel; int repeat_delay; int repeat_rate; + int scroll_button; int scroll_method; int send_events; int tap; diff --git a/sway/commands/input.c b/sway/commands/input.c index 574e1f8c..e7906b0e 100644 --- a/sway/commands/input.c +++ b/sway/commands/input.c @@ -20,6 +20,7 @@ static struct cmd_handler input_handlers[] = { { "pointer_accel", input_cmd_pointer_accel }, { "repeat_delay", input_cmd_repeat_delay }, { "repeat_rate", input_cmd_repeat_rate }, + { "scroll_button", input_cmd_scroll_button }, { "scroll_method", input_cmd_scroll_method }, { "tap", input_cmd_tap }, { "xkb_layout", input_cmd_xkb_layout }, diff --git a/sway/commands/input/scroll_button.c b/sway/commands/input/scroll_button.c new file mode 100644 index 00000000..a9d697cf --- /dev/null +++ b/sway/commands/input/scroll_button.c @@ -0,0 +1,31 @@ +#include +#include +#include "sway/config.h" +#include "sway/commands.h" +#include "sway/input/input-manager.h" + +struct cmd_results *input_cmd_scroll_button(int argc, char **argv) { + struct cmd_results *error = NULL; + if ((error = checkarg(argc, "scroll_button", EXPECTED_AT_LEAST, 1))) { + return error; + } + struct input_config *current_input_config = + config->handler_context.input_config; + if (!current_input_config) { + return cmd_results_new(CMD_FAILURE, "scroll_button", + "No input device defined."); + } + struct input_config *new_config = + new_input_config(current_input_config->identifier); + + int scroll_button = atoi(argv[0]); + if (scroll_button < 1 || scroll_button > 10) { + free_input_config(new_config); + return cmd_results_new(CMD_INVALID, "scroll_button", + "Input out of range [1, 10]"); + } + new_config->scroll_button = scroll_button; + + apply_input_config(new_config); + return cmd_results_new(CMD_SUCCESS, NULL, NULL); +} diff --git a/sway/config/input.c b/sway/config/input.c index 9840df18..62d77cf7 100644 --- a/sway/config/input.c +++ b/sway/config/input.c @@ -28,6 +28,7 @@ struct input_config *new_input_config(const char* identifier) { input->accel_profile = INT_MIN; input->pointer_accel = FLT_MIN; input->scroll_method = INT_MIN; + input->scroll_button = INT_MIN; input->left_handed = INT_MIN; input->repeat_delay = INT_MIN; input->repeat_rate = INT_MIN; @@ -70,6 +71,9 @@ void merge_input_config(struct input_config *dst, struct input_config *src) { if (src->scroll_method != INT_MIN) { dst->scroll_method = src->scroll_method; } + if (src->scroll_button != INT_MIN) { + dst->scroll_button= src->scroll_button; + } if (src->send_events != INT_MIN) { dst->send_events = src->send_events; } diff --git a/sway/input/input-manager.c b/sway/input/input-manager.c index daaf1fb6..b18989d0 100644 --- a/sway/input/input-manager.c +++ b/sway/input/input-manager.c @@ -158,6 +158,12 @@ static void input_manager_libinput_config_pointer( libinput_device_config_accel_set_speed(libinput_device, ic->pointer_accel); } + if (ic->scroll_button != INT_MIN) { + wlr_log(WLR_DEBUG, "libinput_config_pointer(%s) scroll_set_button(%d)", + ic->identifier, ic->scroll_button); + libinput_device_config_scroll_set_button(libinput_device, + ic->scroll_button); + } if (ic->scroll_method != INT_MIN) { wlr_log(WLR_DEBUG, "libinput_config_pointer(%s) scroll_set_method(%d)", ic->identifier, ic->scroll_method); diff --git a/sway/meson.build b/sway/meson.build index 72192917..6fc78db3 100644 --- a/sway/meson.build +++ b/sway/meson.build @@ -118,6 +118,7 @@ sway_sources = files( 'commands/input/pointer_accel.c', 'commands/input/repeat_delay.c', 'commands/input/repeat_rate.c', + 'commands/input/scroll_button.c', 'commands/input/scroll_method.c', 'commands/input/tap.c', 'commands/input/xkb_layout.c', -- cgit v1.2.3-70-g09d2 From 9b16227ec3cfc648f177f186d29b9f0002b7bbde Mon Sep 17 00:00:00 2001 From: emersion Date: Thu, 12 Jul 2018 20:01:33 +0100 Subject: Don't disable borders for xwayland floating views --- include/sway/tree/view.h | 1 + sway/desktop/xwayland.c | 9 +++++++++ sway/tree/view.c | 6 +++++- 3 files changed, 15 insertions(+), 1 deletion(-) (limited to 'include/sway') diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h index 7dc8ac46..21d6403e 100644 --- a/include/sway/tree/view.h +++ b/include/sway/tree/view.h @@ -35,6 +35,7 @@ struct sway_view_impl { void (*set_tiled)(struct sway_view *view, bool tiled); void (*set_fullscreen)(struct sway_view *view, bool fullscreen); bool (*wants_floating)(struct sway_view *view); + bool (*has_client_side_decorations)(struct sway_view *view); void (*for_each_surface)(struct sway_view *view, wlr_surface_iterator_func_t iterator, void *user_data); void (*close)(struct sway_view *view); diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c index 4e5cea7d..460d1cc8 100644 --- a/sway/desktop/xwayland.c +++ b/sway/desktop/xwayland.c @@ -246,6 +246,14 @@ static bool wants_floating(struct sway_view *view) { return false; } +static bool has_client_side_decorations(struct sway_view *view) { + if (xwayland_view_from_view(view) == NULL) { + return false; + } + struct wlr_xwayland_surface *surface = view->wlr_xwayland_surface; + return surface->decorations != WLR_XWAYLAND_SURFACE_DECORATIONS_ALL; +} + static void _close(struct sway_view *view) { if (xwayland_view_from_view(view) == NULL) { return; @@ -269,6 +277,7 @@ static const struct sway_view_impl view_impl = { .set_tiled = set_tiled, .set_fullscreen = set_fullscreen, .wants_floating = wants_floating, + .has_client_side_decorations = has_client_side_decorations, .close = _close, .destroy = destroy, }; diff --git a/sway/tree/view.c b/sway/tree/view.c index 20cbaf1c..b356183c 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -315,7 +315,11 @@ void view_set_activated(struct sway_view *view, bool activated) { } void view_set_tiled(struct sway_view *view, bool tiled) { - view->border = tiled ? config->border : B_NONE; + bool csd = true; + if (view->impl->has_client_side_decorations) { + csd = view->impl->has_client_side_decorations(view); + } + view->border = tiled || !csd ? config->border : B_NONE; if (view->impl->set_tiled) { view->impl->set_tiled(view, tiled); } -- cgit v1.2.3-70-g09d2 From 13c6627ddb7dbe235426e123ee6ff8e6794bda6d Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Sat, 14 Jul 2018 01:01:47 -0400 Subject: Implement tap_button_map for input devices --- include/sway/commands.h | 1 + include/sway/config.h | 1 + sway/commands/input.c | 1 + sway/commands/input/tap_button_map.c | 33 +++++++++++++++++++++++++++++++++ sway/config/input.c | 4 ++++ sway/input/input-manager.c | 6 ++++++ sway/meson.build | 1 + sway/sway-input.5.scd | 6 ++++++ 8 files changed, 53 insertions(+) create mode 100644 sway/commands/input/tap_button_map.c (limited to 'include/sway') diff --git a/include/sway/commands.h b/include/sway/commands.h index 32d6cefd..3ebd0002 100644 --- a/include/sway/commands.h +++ b/include/sway/commands.h @@ -210,6 +210,7 @@ sway_cmd input_cmd_repeat_rate; sway_cmd input_cmd_scroll_button; sway_cmd input_cmd_scroll_method; sway_cmd input_cmd_tap; +sway_cmd input_cmd_tap_button_map; sway_cmd input_cmd_xkb_layout; sway_cmd input_cmd_xkb_model; sway_cmd input_cmd_xkb_options; diff --git a/include/sway/config.h b/include/sway/config.h index 75acd4f2..f660a269 100644 --- a/include/sway/config.h +++ b/include/sway/config.h @@ -79,6 +79,7 @@ struct input_config { int scroll_method; int send_events; int tap; + int tap_button_map; char *xkb_layout; char *xkb_model; diff --git a/sway/commands/input.c b/sway/commands/input.c index e7906b0e..5b203ea0 100644 --- a/sway/commands/input.c +++ b/sway/commands/input.c @@ -23,6 +23,7 @@ static struct cmd_handler input_handlers[] = { { "scroll_button", input_cmd_scroll_button }, { "scroll_method", input_cmd_scroll_method }, { "tap", input_cmd_tap }, + { "tap_button_map", input_cmd_tap_button_map }, { "xkb_layout", input_cmd_xkb_layout }, { "xkb_model", input_cmd_xkb_model }, { "xkb_options", input_cmd_xkb_options }, diff --git a/sway/commands/input/tap_button_map.c b/sway/commands/input/tap_button_map.c new file mode 100644 index 00000000..bdbba472 --- /dev/null +++ b/sway/commands/input/tap_button_map.c @@ -0,0 +1,33 @@ +#include +#include +#include "sway/config.h" +#include "sway/commands.h" +#include "sway/input/input-manager.h" + +struct cmd_results *input_cmd_tap_button_map(int argc, char **argv) { + struct cmd_results *error = NULL; + if ((error = checkarg(argc, "tap_button_map", EXPECTED_AT_LEAST, 1))) { + return error; + } + struct input_config *current_input_config = + config->handler_context.input_config; + if (!current_input_config) { + return cmd_results_new(CMD_FAILURE, "tap_button_map", + "No input device defined."); + } + struct input_config *new_config = + new_input_config(current_input_config->identifier); + + if (strcasecmp(argv[0], "lrm") == 0) { + new_config->tap_button_map = LIBINPUT_CONFIG_TAP_MAP_LRM; + } else if (strcasecmp(argv[0], "lmr") == 0) { + new_config->tap_button_map = LIBINPUT_CONFIG_TAP_MAP_LMR; + } else { + free_input_config(new_config); + return cmd_results_new(CMD_INVALID, "tap_button_map", + "Expected 'tap_button_map '"); + } + + apply_input_config(new_config); + return cmd_results_new(CMD_SUCCESS, NULL, NULL); +} diff --git a/sway/config/input.c b/sway/config/input.c index cbd7d5f0..8d687a6d 100644 --- a/sway/config/input.c +++ b/sway/config/input.c @@ -19,6 +19,7 @@ struct input_config *new_input_config(const char* identifier) { } input->tap = INT_MIN; + input->tap_button_map = INT_MIN; input->drag_lock = INT_MIN; input->dwt = INT_MIN; input->send_events = INT_MIN; @@ -80,6 +81,9 @@ void merge_input_config(struct input_config *dst, struct input_config *src) { if (src->tap != INT_MIN) { dst->tap = src->tap; } + if (src->tap_button_map != INT_MIN) { + dst->tap_button_map = src->tap_button_map; + } if (src->xkb_layout) { free(dst->xkb_layout); dst->xkb_layout = strdup(src->xkb_layout); diff --git a/sway/input/input-manager.c b/sway/input/input-manager.c index b18989d0..0b7cb766 100644 --- a/sway/input/input-manager.c +++ b/sway/input/input-manager.c @@ -181,6 +181,12 @@ static void input_manager_libinput_config_pointer( ic->identifier, ic->tap); libinput_device_config_tap_set_enabled(libinput_device, ic->tap); } + if (ic->tap_button_map != INT_MIN) { + wlr_log(WLR_DEBUG, "libinput_config_pointer(%s) tap_set_button_map(%d)", + ic->identifier, ic->tap); + libinput_device_config_tap_set_button_map(libinput_device, + ic->tap_button_map); + } } static void handle_device_destroy(struct wl_listener *listener, void *data) { diff --git a/sway/meson.build b/sway/meson.build index 6fc78db3..f878450d 100644 --- a/sway/meson.build +++ b/sway/meson.build @@ -121,6 +121,7 @@ sway_sources = files( 'commands/input/scroll_button.c', 'commands/input/scroll_method.c', 'commands/input/tap.c', + 'commands/input/tap_button_map.c', 'commands/input/xkb_layout.c', 'commands/input/xkb_model.c', 'commands/input/xkb_options.c', diff --git a/sway/sway-input.5.scd b/sway/sway-input.5.scd index 4bc66394..b6391431 100644 --- a/sway/sway-input.5.scd +++ b/sway/sway-input.5.scd @@ -100,6 +100,12 @@ For more information on these xkb configuration options, see *input* tap enabled|disabled Enables or disables tap for specified input device. +*input* 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. + ## SEAT CONFIGURATION Configure options for multiseat mode. sway-seat commands must be used inside a -- cgit v1.2.3-70-g09d2 From 2032f85d94f2f222282b242116b3e827dd458f6c Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Sat, 14 Jul 2018 23:14:55 +1000 Subject: Simplify transactions by utilising a dirty flag on containers This PR changes the way we handle transactions to a more simple method. The new method is to mark containers as dirty from low level code (eg. arranging, or container_destroy, and eventually seat_set_focus), then call transaction_commit_dirty which picks up those containers and runs them through a transaction. The old methods of using transactions (arrange_and_commit, or creating one manually) are now no longer possible. The highest-level code (execute_command and view implementation handlers) will call transaction_commit_dirty, so most other code just needs to set containers as dirty. This is done by arranging, but can also be done by calling container_set_dirty. --- include/sway/desktop/transaction.h | 33 ++++++---------- include/sway/server.h | 5 +-- include/sway/tree/arrange.h | 22 +---------- include/sway/tree/container.h | 10 +++++ sway/commands.c | 2 + sway/commands/border.c | 2 +- sway/commands/floating.c | 2 +- sway/commands/fullscreen.c | 2 +- sway/commands/gaps.c | 8 ++-- sway/commands/layout.c | 2 +- sway/commands/move.c | 25 ++++-------- sway/commands/reload.c | 2 +- sway/commands/resize.c | 6 +-- sway/commands/smart_gaps.c | 2 +- sway/commands/split.c | 2 +- sway/commands/swap.c | 9 +---- sway/config.c | 2 +- sway/desktop/layer_shell.c | 3 +- sway/desktop/output.c | 14 ++++--- sway/desktop/transaction.c | 35 +++++++++-------- sway/desktop/xdg_shell.c | 8 ++-- sway/desktop/xdg_shell_v6.c | 8 ++-- sway/desktop/xwayland.c | 8 ++-- sway/server.c | 5 +-- sway/tree/arrange.c | 78 +++++++++++++------------------------- sway/tree/container.c | 24 ++++++------ sway/tree/layout.c | 3 +- sway/tree/view.c | 5 ++- sway/tree/workspace.c | 2 +- 29 files changed, 139 insertions(+), 190 deletions(-) (limited to 'include/sway') diff --git a/include/sway/desktop/transaction.h b/include/sway/desktop/transaction.h index 7ab80eb8..cee4afed 100644 --- a/include/sway/desktop/transaction.h +++ b/include/sway/desktop/transaction.h @@ -6,34 +6,25 @@ /** * Transactions enable us to perform atomic layout updates. * - * When we want to make adjustments to the layout, we create a transaction. - * A transaction contains a list of affected containers and their new state. + * A transaction contains a list of containers and their new state. * A state might contain a new size, or new border settings, or new parent/child * relationships. * - * Calling transaction_commit() makes sway notify of all the affected clients - * with their new sizes. We then wait for all the views to respond with their - * new surface sizes. When all are ready, or when a timeout has passed, we apply - * the updates all at the same time. - */ - -struct sway_transaction; - -/** - * Create a new transaction. - */ -struct sway_transaction *transaction_create(void); - -/** - * Add a container's pending state to the transaction. + * Committing a transaction makes sway notify of all the affected clients with + * their new sizes. We then wait for all the views to respond with their new + * surface sizes. When all are ready, or when a timeout has passed, we apply the + * updates all at the same time. + * + * When we want to make adjustments to the layout, we change the pending state + * in containers, mark them as dirty and call transaction_commit_dirty(). This + * create and commits a transaction from the dirty containers. */ -void transaction_add_container(struct sway_transaction *transaction, - struct sway_container *container); /** - * Submit a transaction to the client views for configuration. + * Find all dirty containers, create and commit a transaction containing them, + * and unmark them as dirty. */ -void transaction_commit(struct sway_transaction *transaction); +void transaction_commit_dirty(void); /** * Notify the transaction system that a view is ready for the new layout. diff --git a/include/sway/server.h b/include/sway/server.h index a3e32898..a017d1c4 100644 --- a/include/sway/server.h +++ b/include/sway/server.h @@ -47,10 +47,7 @@ struct sway_server { bool debug_txn_timings; list_t *transactions; - - // When a view is being destroyed and is waiting for a transaction to - // complete it will be stored here. - list_t *destroying_containers; + list_t *dirty_containers; }; struct sway_server server; diff --git a/include/sway/tree/arrange.h b/include/sway/tree/arrange.h index 58235642..d6abcc81 100644 --- a/include/sway/tree/arrange.h +++ b/include/sway/tree/arrange.h @@ -11,26 +11,8 @@ void remove_gaps(struct sway_container *c); void add_gaps(struct sway_container *c); /** - * Arrange layout for all the children of the given container, and add them to - * the given transaction. - * - * Use this function if you need to arrange multiple sections of the tree in one - * transaction. - * - * You must set the desired state of the container before calling - * arrange_windows, then don't change any state-tracked properties in the - * container until you've called transaction_commit. + * Arrange layout for all the children of the given container. */ -void arrange_windows(struct sway_container *container, - struct sway_transaction *transaction); - -/** - * Arrange layout for the given container and commit the transaction. - * - * This function is a wrapper around arrange_windows, and handles creating and - * committing the transaction for you. Use this function if you're only doing - * one arrange operation. - */ -void arrange_and_commit(struct sway_container *container); +void arrange_windows(struct sway_container *container); #endif diff --git a/include/sway/tree/container.h b/include/sway/tree/container.h index a69da9db..11780916 100644 --- a/include/sway/tree/container.h +++ b/include/sway/tree/container.h @@ -144,6 +144,10 @@ struct sway_container { bool destroying; + // If true, indicates that the container has pending state that differs from + // the current. + bool dirty; + struct { struct wl_signal destroy; // Raised after the tree updates, but before arrange_windows @@ -303,4 +307,10 @@ void container_get_box(struct sway_container *container, struct wlr_box *box); void container_floating_move_to(struct sway_container *con, double lx, double ly); +/** + * Mark a container as dirty if it isn't already. Dirty containers will be + * included in the next transaction then unmarked as dirty. + */ +void container_set_dirty(struct sway_container *container); + #endif diff --git a/sway/commands.c b/sway/commands.c index addd64a6..50d949d4 100644 --- a/sway/commands.c +++ b/sway/commands.c @@ -9,6 +9,7 @@ #include "sway/commands.h" #include "sway/config.h" #include "sway/criteria.h" +#include "sway/desktop/transaction.h" #include "sway/security.h" #include "sway/input/input-manager.h" #include "sway/input/seat.h" @@ -322,6 +323,7 @@ struct cmd_results *execute_command(char *_exec, struct sway_seat *seat) { cleanup: free(exec); free(views); + transaction_commit_dirty(); if (!results) { results = cmd_results_new(CMD_SUCCESS, NULL, NULL); } diff --git a/sway/commands/border.c b/sway/commands/border.c index 6db85395..9c19e20a 100644 --- a/sway/commands/border.c +++ b/sway/commands/border.c @@ -42,7 +42,7 @@ struct cmd_results *cmd_border(int argc, char **argv) { container_set_geometry_from_floating_view(view->swayc); } - arrange_and_commit(view->swayc); + arrange_windows(view->swayc); struct sway_seat *seat = input_manager_current_seat(input_manager); if (seat->cursor) { diff --git a/sway/commands/floating.c b/sway/commands/floating.c index e6003521..6ab56c3b 100644 --- a/sway/commands/floating.c +++ b/sway/commands/floating.c @@ -37,7 +37,7 @@ struct cmd_results *cmd_floating(int argc, char **argv) { container_set_floating(container, wants_floating); struct sway_container *workspace = container_parent(container, C_WORKSPACE); - arrange_and_commit(workspace); + arrange_windows(workspace); return cmd_results_new(CMD_SUCCESS, NULL, NULL); } diff --git a/sway/commands/fullscreen.c b/sway/commands/fullscreen.c index 1a4d8b41..0b5beaa2 100644 --- a/sway/commands/fullscreen.c +++ b/sway/commands/fullscreen.c @@ -34,7 +34,7 @@ struct cmd_results *cmd_fullscreen(int argc, char **argv) { view_set_fullscreen(view, wants_fullscreen); struct sway_container *workspace = container_parent(container, C_WORKSPACE); - arrange_and_commit(workspace->parent); + arrange_windows(workspace->parent); return cmd_results_new(CMD_SUCCESS, NULL, NULL); } diff --git a/sway/commands/gaps.c b/sway/commands/gaps.c index 801fb179..3906eb70 100644 --- a/sway/commands/gaps.c +++ b/sway/commands/gaps.c @@ -43,7 +43,7 @@ struct cmd_results *cmd_gaps(int argc, char **argv) { return cmd_results_new(CMD_INVALID, "gaps", "gaps edge_gaps on|off|toggle"); } - arrange_and_commit(&root_container); + arrange_windows(&root_container); } else { int amount_idx = 0; // the current index in argv enum gaps_op op = GAPS_OP_SET; @@ -124,7 +124,7 @@ struct cmd_results *cmd_gaps(int argc, char **argv) { if (amount_idx == 0) { // gaps config->gaps_inner = val; config->gaps_outer = val; - arrange_and_commit(&root_container); + arrange_windows(&root_container); return cmd_results_new(CMD_SUCCESS, NULL, NULL); } // Other variants. The middle-length variant (gaps inner|outer ) @@ -155,7 +155,7 @@ struct cmd_results *cmd_gaps(int argc, char **argv) { } else { config->gaps_outer = total; } - arrange_and_commit(&root_container); + arrange_windows(&root_container); } else { struct sway_container *c = config->handler_context.current_container; @@ -169,7 +169,7 @@ struct cmd_results *cmd_gaps(int argc, char **argv) { c->gaps_outer = total; } - arrange_and_commit(c->parent ? c->parent : &root_container); + arrange_windows(c->parent ? c->parent : &root_container); } } diff --git a/sway/commands/layout.c b/sway/commands/layout.c index 9945fa5c..c446f1f9 100644 --- a/sway/commands/layout.c +++ b/sway/commands/layout.c @@ -49,7 +49,7 @@ struct cmd_results *cmd_layout(int argc, char **argv) { } container_notify_subtree_changed(parent); - arrange_and_commit(parent); + arrange_windows(parent); return cmd_results_new(CMD_SUCCESS, NULL, NULL); } diff --git a/sway/commands/move.c b/sway/commands/move.c index a1c1e018..6ec050a8 100644 --- a/sway/commands/move.c +++ b/sway/commands/move.c @@ -6,7 +6,6 @@ #include #include #include "sway/commands.h" -#include "sway/desktop/transaction.h" #include "sway/input/cursor.h" #include "sway/input/seat.h" #include "sway/output.h" @@ -105,10 +104,8 @@ static struct cmd_results *cmd_move_container(struct sway_container *current, // TODO: Ideally we would arrange the surviving parent after reaping, // but container_reap_empty does not return it, so we arrange the // workspace instead. - struct sway_transaction *txn = transaction_create(); - arrange_windows(old_ws, txn); - arrange_windows(destination->parent, txn); - transaction_commit(txn); + arrange_windows(old_ws); + arrange_windows(destination->parent); return cmd_results_new(CMD_SUCCESS, NULL, NULL); } else if (strcasecmp(argv[1], "to") == 0 @@ -144,10 +141,8 @@ static struct cmd_results *cmd_move_container(struct sway_container *current, // TODO: Ideally we would arrange the surviving parent after reaping, // but container_reap_empty does not return it, so we arrange the // workspace instead. - struct sway_transaction *txn = transaction_create(); - arrange_windows(old_ws, txn); - arrange_windows(focus->parent, txn); - transaction_commit(txn); + arrange_windows(old_ws); + arrange_windows(focus->parent); return cmd_results_new(CMD_SUCCESS, NULL, NULL); } @@ -177,10 +172,8 @@ static struct cmd_results *cmd_move_workspace(struct sway_container *current, } container_move_to(current, destination); - struct sway_transaction *txn = transaction_create(); - arrange_windows(source, txn); - arrange_windows(destination, txn); - transaction_commit(txn); + arrange_windows(source); + arrange_windows(destination); return cmd_results_new(CMD_SUCCESS, NULL, NULL); } @@ -238,12 +231,10 @@ static struct cmd_results *move_in_direction(struct sway_container *container, container_move(container, direction, move_amt); struct sway_container *new_ws = container_parent(container, C_WORKSPACE); - struct sway_transaction *txn = transaction_create(); - arrange_windows(old_ws, txn); + arrange_windows(old_ws); if (new_ws != old_ws) { - arrange_windows(new_ws, txn); + arrange_windows(new_ws); } - transaction_commit(txn); return cmd_results_new(CMD_SUCCESS, NULL, NULL); } diff --git a/sway/commands/reload.c b/sway/commands/reload.c index c6715f9c..cea6a94b 100644 --- a/sway/commands/reload.c +++ b/sway/commands/reload.c @@ -12,6 +12,6 @@ struct cmd_results *cmd_reload(int argc, char **argv) { } load_swaybars(); - arrange_and_commit(&root_container); + arrange_windows(&root_container); return cmd_results_new(CMD_SUCCESS, NULL, NULL); } diff --git a/sway/commands/resize.c b/sway/commands/resize.c index 2cf811d8..e657864c 100644 --- a/sway/commands/resize.c +++ b/sway/commands/resize.c @@ -268,7 +268,7 @@ static void resize_tiled(int amount, enum resize_axis axis) { } } - arrange_and_commit(parent->parent); + arrange_windows(parent->parent); } /** @@ -338,7 +338,7 @@ static struct cmd_results *resize_adjust_floating(enum resize_axis axis, view->height += grow_height; } - arrange_and_commit(con); + arrange_windows(con); return cmd_results_new(CMD_SUCCESS, NULL, NULL); } @@ -410,7 +410,7 @@ static struct cmd_results *resize_set_floating(struct sway_container *con, view->height += grow_height; } - arrange_and_commit(con); + arrange_windows(con); return cmd_results_new(CMD_SUCCESS, NULL, NULL); } diff --git a/sway/commands/smart_gaps.c b/sway/commands/smart_gaps.c index f687e78e..7d27e571 100644 --- a/sway/commands/smart_gaps.c +++ b/sway/commands/smart_gaps.c @@ -23,7 +23,7 @@ struct cmd_results *cmd_smart_gaps(int argc, char **argv) { "Expected 'smart_gaps ' "); } - arrange_and_commit(&root_container); + arrange_windows(&root_container); return cmd_results_new(CMD_SUCCESS, NULL, NULL); } diff --git a/sway/commands/split.c b/sway/commands/split.c index c40f4d9f..313799da 100644 --- a/sway/commands/split.c +++ b/sway/commands/split.c @@ -16,7 +16,7 @@ static struct cmd_results *do_split(int layout) { } struct sway_container *parent = container_split(con, layout); container_create_notify(parent); - arrange_and_commit(parent->parent); + arrange_windows(parent->parent); return cmd_results_new(CMD_SUCCESS, NULL, NULL); } diff --git a/sway/commands/swap.c b/sway/commands/swap.c index e052058f..2fc88308 100644 --- a/sway/commands/swap.c +++ b/sway/commands/swap.c @@ -1,7 +1,6 @@ #include #include #include "sway/commands.h" -#include "sway/desktop/transaction.h" #include "sway/tree/arrange.h" #include "sway/tree/layout.h" #include "sway/tree/view.h" @@ -79,14 +78,10 @@ struct cmd_results *cmd_swap(int argc, char **argv) { container_swap(current, other); - struct sway_transaction *txn = transaction_create(); - arrange_windows(current->parent, txn); - + arrange_windows(current->parent); if (other->parent != current->parent) { - arrange_windows(other->parent, txn); + arrange_windows(other->parent); } - transaction_commit(txn); - return cmd_results_new(CMD_SUCCESS, NULL, NULL); } diff --git a/sway/config.c b/sway/config.c index d2386f46..636f5f57 100644 --- a/sway/config.c +++ b/sway/config.c @@ -773,6 +773,6 @@ void config_update_font_height(bool recalculate) { } if (config->font_height != prev_max_height) { - arrange_and_commit(&root_container); + arrange_windows(&root_container); } } diff --git a/sway/desktop/layer_shell.c b/sway/desktop/layer_shell.c index 16910c7e..91baa6f8 100644 --- a/sway/desktop/layer_shell.c +++ b/sway/desktop/layer_shell.c @@ -12,7 +12,6 @@ #include "sway/layers.h" #include "sway/output.h" #include "sway/server.h" -#include "sway/tree/arrange.h" #include "sway/tree/layout.h" #include "log.h" @@ -176,7 +175,7 @@ void arrange_layers(struct sway_output *output) { sizeof(struct wlr_box)) != 0) { wlr_log(WLR_DEBUG, "Usable area changed, rearranging output"); memcpy(&output->usable_area, &usable_area, sizeof(struct wlr_box)); - arrange_and_commit(output->swayc); + container_set_dirty(output->swayc); } // Arrange non-exlusive surfaces from top->bottom diff --git a/sway/desktop/output.c b/sway/desktop/output.c index a2720885..a9808406 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -492,19 +492,21 @@ static void handle_destroy(struct wl_listener *listener, void *data) { output->wlr_output->data = NULL; free(output); - arrange_and_commit(&root_container); + arrange_windows(&root_container); } static void handle_mode(struct wl_listener *listener, void *data) { struct sway_output *output = wl_container_of(listener, output, mode); arrange_layers(output); - arrange_and_commit(output->swayc); + arrange_windows(output->swayc); + transaction_commit_dirty(); } static void handle_transform(struct wl_listener *listener, void *data) { struct sway_output *output = wl_container_of(listener, output, transform); arrange_layers(output); - arrange_and_commit(output->swayc); + arrange_windows(output->swayc); + transaction_commit_dirty(); } static void handle_scale_iterator(struct sway_container *view, void *data) { @@ -515,7 +517,8 @@ static void handle_scale(struct wl_listener *listener, void *data) { struct sway_output *output = wl_container_of(listener, output, scale); arrange_layers(output); container_descendants(output->swayc, C_VIEW, handle_scale_iterator, NULL); - arrange_and_commit(output->swayc); + arrange_windows(output->swayc); + transaction_commit_dirty(); } struct sway_output *output_from_wlr_output(struct wlr_output *wlr_output) { @@ -584,5 +587,6 @@ void output_enable(struct sway_output *output) { output->damage_destroy.notify = damage_handle_destroy; arrange_layers(output); - arrange_and_commit(&root_container); + arrange_windows(&root_container); + transaction_commit_dirty(); } diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index 2b3f87c3..d7ef7130 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -47,7 +47,7 @@ struct sway_transaction_instruction { bool ready; }; -struct sway_transaction *transaction_create() { +static struct sway_transaction *transaction_create() { struct sway_transaction *transaction = calloc(1, sizeof(struct sway_transaction)); transaction->instructions = create_list(); @@ -141,23 +141,8 @@ static void copy_pending_state(struct sway_container *container, } } -static bool transaction_has_container(struct sway_transaction *transaction, +static void transaction_add_container(struct sway_transaction *transaction, struct sway_container *container) { - for (int i = 0; i < transaction->instructions->length; ++i) { - struct sway_transaction_instruction *instruction = - transaction->instructions->items[i]; - if (instruction->container == container) { - return true; - } - } - return false; -} - -void transaction_add_container(struct sway_transaction *transaction, - struct sway_container *container) { - if (transaction_has_container(transaction, container)) { - return; - } struct sway_transaction_instruction *instruction = calloc(1, sizeof(struct sway_transaction_instruction)); instruction->transaction = transaction; @@ -285,7 +270,7 @@ static bool should_configure(struct sway_container *con, return true; } -void transaction_commit(struct sway_transaction *transaction) { +static void transaction_commit(struct sway_transaction *transaction) { wlr_log(WLR_DEBUG, "Transaction %p committing with %i instructions", transaction, transaction->instructions->length); transaction->num_waiting = 0; @@ -418,3 +403,17 @@ struct wlr_texture *transaction_get_saved_texture(struct sway_view *view, *height = instruction->saved_buffer_height; return instruction->saved_buffer->texture; } + +void transaction_commit_dirty() { + if (!server.dirty_containers->length) { + return; + } + struct sway_transaction *transaction = transaction_create(); + for (int i = 0; i < server.dirty_containers->length; ++i) { + struct sway_container *container = server.dirty_containers->items[i]; + transaction_add_container(transaction, container); + container->dirty = false; + } + server.dirty_containers->length = 0; + transaction_commit(transaction); +} diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c index fbeeb2e3..98c16faf 100644 --- a/sway/desktop/xdg_shell.c +++ b/sway/desktop/xdg_shell.c @@ -244,7 +244,8 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data) view_set_fullscreen(view, e->fullscreen); struct sway_container *output = container_parent(view->swayc, C_OUTPUT); - arrange_and_commit(output); + arrange_windows(output); + transaction_commit_dirty(); } static void handle_unmap(struct wl_listener *listener, void *data) { @@ -281,10 +282,11 @@ static void handle_map(struct wl_listener *listener, void *data) { if (xdg_surface->toplevel->client_pending.fullscreen) { view_set_fullscreen(view, true); struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); - arrange_and_commit(ws); + arrange_windows(ws); } else { - arrange_and_commit(view->swayc->parent); + arrange_windows(view->swayc->parent); } + transaction_commit_dirty(); xdg_shell_view->commit.notify = handle_commit; wl_signal_add(&xdg_surface->surface->events.commit, diff --git a/sway/desktop/xdg_shell_v6.c b/sway/desktop/xdg_shell_v6.c index 88d9bb94..4d76f0a7 100644 --- a/sway/desktop/xdg_shell_v6.c +++ b/sway/desktop/xdg_shell_v6.c @@ -239,7 +239,8 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data) view_set_fullscreen(view, e->fullscreen); struct sway_container *output = container_parent(view->swayc, C_OUTPUT); - arrange_and_commit(output); + arrange_windows(output); + transaction_commit_dirty(); } static void handle_unmap(struct wl_listener *listener, void *data) { @@ -276,10 +277,11 @@ static void handle_map(struct wl_listener *listener, void *data) { if (xdg_surface->toplevel->client_pending.fullscreen) { view_set_fullscreen(view, true); struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); - arrange_and_commit(ws); + arrange_windows(ws); } else { - arrange_and_commit(view->swayc->parent); + arrange_windows(view->swayc->parent); } + transaction_commit_dirty(); xdg_shell_v6_view->commit.notify = handle_commit; wl_signal_add(&xdg_surface->surface->events.commit, diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c index 460d1cc8..11516673 100644 --- a/sway/desktop/xwayland.c +++ b/sway/desktop/xwayland.c @@ -333,10 +333,11 @@ static void handle_map(struct wl_listener *listener, void *data) { if (xsurface->fullscreen) { view_set_fullscreen(view, true); struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); - arrange_and_commit(ws); + arrange_windows(ws); } else { - arrange_and_commit(view->swayc->parent); + arrange_windows(view->swayc->parent); } + transaction_commit_dirty(); } static void handle_destroy(struct wl_listener *listener, void *data) { @@ -392,7 +393,8 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data) view_set_fullscreen(view, xsurface->fullscreen); struct sway_container *output = container_parent(view->swayc, C_OUTPUT); - arrange_and_commit(output); + arrange_windows(output); + transaction_commit_dirty(); } static void handle_set_title(struct wl_listener *listener, void *data) { diff --git a/sway/server.c b/sway/server.c index 1d8eb964..8566d512 100644 --- a/sway/server.c +++ b/sway/server.c @@ -125,8 +125,7 @@ bool server_init(struct sway_server *server) { if (debug != NULL && strcmp(debug, "txn_timings") == 0) { server->debug_txn_timings = true; } - server->destroying_containers = create_list(); - + server->dirty_containers = create_list(); server->transactions = create_list(); input_manager = input_manager_create(server); @@ -136,7 +135,7 @@ bool server_init(struct sway_server *server) { void server_fini(struct sway_server *server) { // TODO: free sway-specific resources wl_display_destroy(server->wl_display); - list_free(server->destroying_containers); + list_free(server->dirty_containers); list_free(server->transactions); } diff --git a/sway/tree/arrange.c b/sway/tree/arrange.c index bcc3ee9a..533cf71c 100644 --- a/sway/tree/arrange.c +++ b/sway/tree/arrange.c @@ -144,38 +144,22 @@ static void apply_tabbed_or_stacked_layout(struct sway_container *parent) { } } -/** - * If a container has been deleted from the pending tree state, we must add it - * to the transaction so it can be freed afterwards. To do this, we iterate the - * server's destroying_containers list and add all of them. We may add more than - * what we need to, but this is easy and has no negative consequences. - */ -static void add_deleted_containers(struct sway_transaction *transaction) { - for (int i = 0; i < server.destroying_containers->length; ++i) { - struct sway_container *child = server.destroying_containers->items[i]; - transaction_add_container(transaction, child); - } -} - -static void arrange_children_of(struct sway_container *parent, - struct sway_transaction *transaction); +static void arrange_children_of(struct sway_container *parent); -static void arrange_floating(struct sway_container *floating, - struct sway_transaction *transaction) { +static void arrange_floating(struct sway_container *floating) { for (int i = 0; i < floating->children->length; ++i) { struct sway_container *floater = floating->children->items[i]; if (floater->type == C_VIEW) { view_autoconfigure(floater->sway_view); } else { - arrange_children_of(floater, transaction); + arrange_children_of(floater); } - transaction_add_container(transaction, floater); + container_set_dirty(floater); } - transaction_add_container(transaction, floating); + container_set_dirty(floating); } -static void arrange_children_of(struct sway_container *parent, - struct sway_transaction *transaction) { +static void arrange_children_of(struct sway_container *parent) { if (config->reloading) { return; } @@ -198,7 +182,7 @@ static void arrange_children_of(struct sway_container *parent, apply_horiz_layout(parent); break; case L_FLOATING: - arrange_floating(parent, transaction); + arrange_floating(parent); break; } @@ -213,14 +197,13 @@ static void arrange_children_of(struct sway_container *parent, if (child->type == C_VIEW) { view_autoconfigure(child->sway_view); } else { - arrange_children_of(child, transaction); + arrange_children_of(child); } - transaction_add_container(transaction, child); + container_set_dirty(child); } } -static void arrange_workspace(struct sway_container *workspace, - struct sway_transaction *transaction) { +static void arrange_workspace(struct sway_container *workspace) { if (config->reloading) { return; } @@ -234,15 +217,14 @@ static void arrange_workspace(struct sway_container *workspace, workspace->x = output->x + area->x; workspace->y = output->y + area->y; add_gaps(workspace); - transaction_add_container(transaction, workspace); + container_set_dirty(workspace); wlr_log(WLR_DEBUG, "Arranging workspace '%s' at %f, %f", workspace->name, workspace->x, workspace->y); - arrange_floating(workspace->sway_workspace->floating, transaction); - arrange_children_of(workspace, transaction); + arrange_floating(workspace->sway_workspace->floating); + arrange_children_of(workspace); } -static void arrange_output(struct sway_container *output, - struct sway_transaction *transaction) { +static void arrange_output(struct sway_container *output) { if (config->reloading) { return; } @@ -253,16 +235,16 @@ static void arrange_output(struct sway_container *output, output->y = output_box->y; output->width = output_box->width; output->height = output_box->height; - transaction_add_container(transaction, output); + container_set_dirty(output); wlr_log(WLR_DEBUG, "Arranging output '%s' at %f,%f", output->name, output->x, output->y); for (int i = 0; i < output->children->length; ++i) { struct sway_container *workspace = output->children->items[i]; - arrange_workspace(workspace, transaction); + arrange_workspace(workspace); } } -static void arrange_root(struct sway_transaction *transaction) { +static void arrange_root() { if (config->reloading) { return; } @@ -274,43 +256,35 @@ static void arrange_root(struct sway_transaction *transaction) { root_container.y = layout_box->y; root_container.width = layout_box->width; root_container.height = layout_box->height; - transaction_add_container(transaction, &root_container); + container_set_dirty(&root_container); for (int i = 0; i < root_container.children->length; ++i) { struct sway_container *output = root_container.children->items[i]; - arrange_output(output, transaction); + arrange_output(output); } } -void arrange_windows(struct sway_container *container, - struct sway_transaction *transaction) { +void arrange_windows(struct sway_container *container) { switch (container->type) { case C_ROOT: - arrange_root(transaction); + arrange_root(); break; case C_OUTPUT: - arrange_output(container, transaction); + arrange_output(container); break; case C_WORKSPACE: - arrange_workspace(container, transaction); + arrange_workspace(container); break; case C_CONTAINER: - arrange_children_of(container, transaction); - transaction_add_container(transaction, container); + arrange_children_of(container); + container_set_dirty(container); break; case C_VIEW: view_autoconfigure(container->sway_view); - transaction_add_container(transaction, container); + container_set_dirty(container); break; case C_TYPES: break; } - add_deleted_containers(transaction); -} - -void arrange_and_commit(struct sway_container *container) { - struct sway_transaction *transaction = transaction_create(); - arrange_windows(container, transaction); - transaction_commit(transaction); } void remove_gaps(struct sway_container *c) { diff --git a/sway/tree/container.c b/sway/tree/container.c index 58852717..35f67cce 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -159,14 +159,6 @@ void container_free(struct sway_container *cont) { wlr_texture_destroy(cont->title_focused_inactive); wlr_texture_destroy(cont->title_unfocused); wlr_texture_destroy(cont->title_urgent); - - for (int i = 0; i < server.destroying_containers->length; ++i) { - if (server.destroying_containers->items[i] == cont) { - list_del(server.destroying_containers, i); - break; - } - } - list_free(cont->instructions); list_free(cont->children); list_free(cont->current.children); @@ -325,7 +317,7 @@ static struct sway_container *container_destroy_noreaping( } con->destroying = true; - list_add(server.destroying_containers, con); + container_set_dirty(con); if (!con->parent) { return NULL; @@ -1069,9 +1061,15 @@ void container_floating_move_to(struct sway_container *con, if (old_workspace != new_workspace) { container_remove_child(con); container_add_child(new_workspace->sway_workspace->floating, con); - struct sway_transaction *transaction = transaction_create(); - arrange_windows(old_workspace, transaction); - arrange_windows(new_workspace, transaction); - transaction_commit(transaction); + arrange_windows(old_workspace); + arrange_windows(new_workspace); + } +} + +void container_set_dirty(struct sway_container *container) { + if (container->dirty) { + return; } + container->dirty = true; + list_add(server.dirty_containers, container); } diff --git a/sway/tree/layout.c b/sway/tree/layout.c index ba234e89..54ddb3f9 100644 --- a/sway/tree/layout.c +++ b/sway/tree/layout.c @@ -22,7 +22,8 @@ struct sway_container root_container; static void output_layout_handle_change(struct wl_listener *listener, void *data) { - arrange_and_commit(&root_container); + arrange_windows(&root_container); + transaction_commit_dirty(); } void layout_init(void) { diff --git a/sway/tree/view.c b/sway/tree/view.c index b356183c..bf380d98 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -594,11 +594,12 @@ void view_unmap(struct sway_view *view) { ws->sway_workspace->fullscreen = NULL; container_destroy(view->swayc); - arrange_and_commit(ws->parent); + arrange_windows(ws->parent); } else { struct sway_container *parent = container_destroy(view->swayc); - arrange_and_commit(parent); + arrange_windows(parent); } + transaction_commit_dirty(); view->surface = NULL; } diff --git a/sway/tree/workspace.c b/sway/tree/workspace.c index 50f9400a..2a2d834a 100644 --- a/sway/tree/workspace.c +++ b/sway/tree/workspace.c @@ -427,7 +427,7 @@ bool workspace_switch(struct sway_container *workspace) { } seat_set_focus(seat, next); struct sway_container *output = container_parent(workspace, C_OUTPUT); - arrange_and_commit(output); + arrange_windows(output); return true; } -- cgit v1.2.3-70-g09d2 From b1afcc69fa70c20d815940fc25189063ed59ae0f Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Sat, 14 Jul 2018 11:24:22 -0400 Subject: Add extended debugging flags We currently have several ways of setting debug flags, including command line arguments, environment variables, and compile-time macros. This replaces the lot with command line flags. --- include/sway/debug.h | 8 ++++++++ sway/desktop/render.c | 4 +++- sway/desktop/transaction.c | 16 ++++++++-------- sway/main.c | 16 ++++++++++++++-- 4 files changed, 33 insertions(+), 11 deletions(-) (limited to 'include/sway') diff --git a/include/sway/debug.h b/include/sway/debug.h index 2430d319..38d4eccd 100644 --- a/include/sway/debug.h +++ b/include/sway/debug.h @@ -1,7 +1,15 @@ #ifndef SWAY_DEBUG_H #define SWAY_DEBUG_H +// Tree extern bool enable_debug_tree; void update_debug_tree(); +// Damage +extern const char *damage_debug; + +// Transactions +extern int txn_timeout_ms; +extern bool txn_debug; + #endif diff --git a/sway/desktop/render.c b/sway/desktop/render.c index b370f8a2..4bfc573b 100644 --- a/sway/desktop/render.c +++ b/sway/desktop/render.c @@ -15,6 +15,7 @@ #include #include "log.h" #include "sway/config.h" +#include "sway/debug.h" #include "sway/input/input-manager.h" #include "sway/input/seat.h" #include "sway/layers.h" @@ -786,6 +787,8 @@ static void render_floating(struct sway_output *soutput, } } +const char *damage_debug = NULL; + void output_render(struct sway_output *output, struct timespec *when, pixman_region32_t *damage) { struct wlr_output *wlr_output = output->wlr_output; @@ -805,7 +808,6 @@ void output_render(struct sway_output *output, struct timespec *when, goto renderer_end; } - const char *damage_debug = getenv("SWAY_DAMAGE_DEBUG"); if (damage_debug != NULL) { if (strcmp(damage_debug, "highlight") == 0) { wlr_renderer_clear(renderer, (float[]){1, 1, 0, 1}); diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index 2b3f87c3..5e42fde5 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -19,14 +19,14 @@ * How long we should wait for views to respond to the configure before giving * up and applying the transaction anyway. */ -#define TIMEOUT_MS 200 +int txn_timeout_ms = 200; /** * If enabled, sway will always wait for the transaction timeout before * applying it, rather than applying it when the views are ready. This allows us * to observe the rendered state while a transaction is in progress. */ -#define TRANSACTION_DEBUG false +bool txn_debug = false; struct sway_transaction { struct wl_event_source *timer; @@ -330,7 +330,7 @@ void transaction_commit(struct sway_transaction *transaction) { // Set up a timer which the views must respond within transaction->timer = wl_event_loop_add_timer(server.wl_event_loop, handle_timeout, transaction); - wl_event_source_timer_update(transaction->timer, TIMEOUT_MS); + wl_event_source_timer_update(transaction->timer, txn_timeout_ms); } // The debug tree shows the pending/live tree. Here is a good place to @@ -361,11 +361,11 @@ static void set_instruction_ready( // If all views are ready, apply the transaction. // If the transaction has timed out then its num_waiting will be 0 already. if (transaction->num_waiting > 0 && --transaction->num_waiting == 0) { -#if !TRANSACTION_DEBUG - wlr_log(WLR_DEBUG, "Transaction %p is ready", transaction); - wl_event_source_timer_update(transaction->timer, 0); - transaction_progress_queue(); -#endif + if (!txn_debug) { + wlr_log(WLR_DEBUG, "Transaction %p is ready", transaction); + wl_event_source_timer_update(transaction->timer, 0); + transaction_progress_queue(); + } } } diff --git a/sway/main.c b/sway/main.c index c6453226..1d772b48 100644 --- a/sway/main.c +++ b/sway/main.c @@ -251,6 +251,18 @@ static void drop_permissions(bool keep_caps) { #endif } +void enable_debug_flag(const char *flag) { + if (strcmp(flag, "render-tree") == 0) { + enable_debug_tree = true; + } else if (strncmp(flag, "damage=", 7) == 0) { + damage_debug = &flag[7]; + } else if (strcmp(flag, "txn-debug") == 0) { + txn_debug = true; + } else if (strncmp(flag, "txn-timeout=", 12) == 0) { + txn_timeout_ms = atoi(&flag[12]); + } +} + int main(int argc, char **argv) { static int verbose = 0, debug = 0, validate = 0; @@ -290,7 +302,7 @@ int main(int argc, char **argv) { int c; while (1) { int option_index = 0; - c = getopt_long(argc, argv, "hCdDvVc:", long_options, &option_index); + c = getopt_long(argc, argv, "hCdD:vVc:", long_options, &option_index); if (c == -1) { break; } @@ -309,7 +321,7 @@ int main(int argc, char **argv) { debug = 1; break; case 'D': // extended debug options - enable_debug_tree = true; + enable_debug_flag(optarg); break; case 'v': // version fprintf(stdout, "sway version " SWAY_VERSION "\n"); -- cgit v1.2.3-70-g09d2 From a120d4c79f9406a2f7cc38c60069d3183c98ea87 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Sun, 15 Jul 2018 15:20:21 +1000 Subject: Make focus part of transactions Rather than maintain copies of the entire focus stack, this PR transactionises the focus by introducing two new properties to the container state and using those when rendering. * `bool focused` means this container has actual focus. Only one container should have this equalling true in its current state. * `struct sway_container *focus_inactive_child` points to the immediate child that was most recently focused (eg. for tabbed and stacked containers). --- include/sway/input/seat.h | 11 -------- include/sway/tree/container.h | 3 +++ sway/commands.c | 2 -- sway/desktop/render.c | 61 ++++++++++++++++--------------------------- sway/desktop/transaction.c | 18 ++++++++++--- sway/input/cursor.c | 3 +++ sway/input/keyboard.c | 2 ++ sway/input/seat.c | 24 +++++------------ sway/ipc-server.c | 2 ++ sway/main.c | 2 ++ 10 files changed, 55 insertions(+), 73 deletions(-) (limited to 'include/sway') diff --git a/include/sway/input/seat.h b/include/sway/input/seat.h index 0e440701..1f7792ba 100644 --- a/include/sway/input/seat.h +++ b/include/sway/input/seat.h @@ -118,17 +118,6 @@ struct sway_container *seat_get_focus_inactive_view(struct sway_seat *seat, struct sway_container *seat_get_active_child(struct sway_seat *seat, struct sway_container *container); -/** - * Return the immediate child of container which was most recently focused, with - * fallback to selecting the child in the parent's `current` (rendered) children - * list. - * - * This is useful for when a tabbed container and its children are destroyed but - * still being rendered, and we have to render an appropriate child. - */ -struct sway_container *seat_get_active_current_child(struct sway_seat *seat, - struct sway_container *container); - /** * Iterate over the focus-inactive children of the container calling the * function on each. diff --git a/include/sway/tree/container.h b/include/sway/tree/container.h index 11780916..04e50fc6 100644 --- a/include/sway/tree/container.h +++ b/include/sway/tree/container.h @@ -68,6 +68,9 @@ struct sway_container_state { struct sway_container *parent; list_t *children; + struct sway_container *focused_inactive_child; + bool focused; + // View properties double view_x, view_y; double view_width, view_height; diff --git a/sway/commands.c b/sway/commands.c index 50d949d4..addd64a6 100644 --- a/sway/commands.c +++ b/sway/commands.c @@ -9,7 +9,6 @@ #include "sway/commands.h" #include "sway/config.h" #include "sway/criteria.h" -#include "sway/desktop/transaction.h" #include "sway/security.h" #include "sway/input/input-manager.h" #include "sway/input/seat.h" @@ -323,7 +322,6 @@ struct cmd_results *execute_command(char *_exec, struct sway_seat *seat) { cleanup: free(exec); free(views); - transaction_commit_dirty(); if (!results) { results = cmd_results_new(CMD_SUCCESS, NULL, NULL); } diff --git a/sway/desktop/render.c b/sway/desktop/render.c index 4bfc573b..17fe823a 100644 --- a/sway/desktop/render.c +++ b/sway/desktop/render.c @@ -543,9 +543,6 @@ static void render_container(struct sway_output *output, static void render_container_simple(struct sway_output *output, pixman_region32_t *damage, struct sway_container *con, bool parent_focused) { - struct sway_seat *seat = input_manager_current_seat(input_manager); - struct sway_container *focus = seat_get_focus(seat); - for (int i = 0; i < con->current.children->length; ++i) { struct sway_container *child = con->current.children->items[i]; @@ -556,11 +553,11 @@ static void render_container_simple(struct sway_output *output, struct wlr_texture *marks_texture; struct sway_container_state *state = &child->current; - if (focus == child || parent_focused) { + if (state->focused || parent_focused) { colors = &config->border_colors.focused; title_texture = child->title_focused; marks_texture = view->marks_focused; - } else if (seat_get_focus_inactive(seat, con) == child) { + } else if (con->current.focused_inactive_child == child) { colors = &config->border_colors.focused_inactive; title_texture = child->title_focused_inactive; marks_texture = view->marks_focused_inactive; @@ -580,7 +577,7 @@ static void render_container_simple(struct sway_output *output, render_view(output, damage, child, colors); } else { render_container(output, damage, child, - parent_focused || focus == child); + parent_focused || child->current.focused); } } } @@ -594,11 +591,9 @@ static void render_container_tabbed(struct sway_output *output, if (!con->current.children->length) { return; } - struct sway_seat *seat = input_manager_current_seat(input_manager); - struct sway_container *focus = seat_get_focus(seat); - struct sway_container *current = seat_get_active_current_child(seat, con); - struct border_colors *current_colors = &config->border_colors.unfocused; struct sway_container_state *pstate = &con->current; + struct sway_container *current = pstate->focused_inactive_child; + struct border_colors *current_colors = &config->border_colors.unfocused; double width_gap_adjustment = 2 * pstate->current_gaps; int tab_width = @@ -613,11 +608,11 @@ static void render_container_tabbed(struct sway_output *output, struct wlr_texture *title_texture; struct wlr_texture *marks_texture; - if (focus == child || parent_focused) { + if (cstate->focused || parent_focused) { colors = &config->border_colors.focused; title_texture = child->title_focused; marks_texture = view ? view->marks_focused : NULL; - } else if (child == current) { + } else if (child == pstate->focused_inactive_child) { colors = &config->border_colors.focused_inactive; title_texture = child->title_focused_inactive; marks_texture = view ? view->marks_focused_inactive : NULL; @@ -644,13 +639,11 @@ static void render_container_tabbed(struct sway_output *output, } // Render surface and left/right/bottom borders - if (current) { - if (current->type == C_VIEW) { - render_view(output, damage, current, current_colors); - } else { - render_container(output, damage, current, - parent_focused || current == focus); - } + if (current->type == C_VIEW) { + render_view(output, damage, current, current_colors); + } else { + render_container(output, damage, current, + parent_focused || current->current.focused); } } @@ -663,11 +656,9 @@ static void render_container_stacked(struct sway_output *output, if (!con->current.children->length) { return; } - struct sway_seat *seat = input_manager_current_seat(input_manager); - struct sway_container *focus = seat_get_focus(seat); - struct sway_container *current = seat_get_active_current_child(seat, con); - struct border_colors *current_colors = &config->border_colors.unfocused; struct sway_container_state *pstate = &con->current; + struct sway_container *current = pstate->focused_inactive_child; + struct border_colors *current_colors = &config->border_colors.unfocused; size_t titlebar_height = container_titlebar_height(); @@ -680,11 +671,11 @@ static void render_container_stacked(struct sway_output *output, struct wlr_texture *title_texture; struct wlr_texture *marks_texture; - if (focus == child || parent_focused) { + if (cstate->focused || parent_focused) { colors = &config->border_colors.focused; title_texture = child->title_focused; marks_texture = view ? view->marks_focused : NULL; - } else if (child == current) { + } else if (child == pstate->focused_inactive_child) { colors = &config->border_colors.focused_inactive; title_texture = child->title_focused_inactive; marks_texture = view ? view->marks_focused_inactive : NULL; @@ -704,13 +695,11 @@ static void render_container_stacked(struct sway_output *output, } // Render surface and left/right/bottom borders - if (current) { - if (current->type == C_VIEW) { - render_view(output, damage, current, current_colors); - } else { - render_container(output, damage, current, - parent_focused || current == focus); - } + if (current->type == C_VIEW) { + render_view(output, damage, current, current_colors); + } else { + render_container(output, damage, current, + parent_focused || current->current.focused); } } @@ -738,13 +727,11 @@ static void render_floating_container(struct sway_output *soutput, pixman_region32_t *damage, struct sway_container *con) { if (con->type == C_VIEW) { struct sway_view *view = con->sway_view; - struct sway_seat *seat = input_manager_current_seat(input_manager); - struct sway_container *focus = seat_get_focus(seat); struct border_colors *colors; struct wlr_texture *title_texture; struct wlr_texture *marks_texture; - if (focus == con) { + if (con->current.focused) { colors = &config->border_colors.focused; title_texture = con->title_focused; marks_texture = view->marks_focused; @@ -871,9 +858,7 @@ void output_render(struct sway_output *output, struct timespec *when, render_layer(output, damage, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM]); - struct sway_seat *seat = input_manager_current_seat(input_manager); - struct sway_container *focus = seat_get_focus(seat); - render_container(output, damage, workspace, focus == workspace); + render_container(output, damage, workspace, workspace->current.focused); render_floating(output, damage); render_unmanaged(output, damage, diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index 0ae042db..fcfb0b51 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -139,6 +139,14 @@ static void copy_pending_state(struct sway_container *container, state->children = create_list(); list_cat(state->children, container->children); } + + struct sway_seat *seat = input_manager_current_seat(input_manager); + state->focused = seat_get_focus(seat) == container; + + if (container->type != C_VIEW) { + state->focused_inactive_child = + seat_get_active_child(seat, container); + } } static void transaction_add_container(struct sway_transaction *transaction, @@ -195,10 +203,12 @@ static void transaction_apply(struct sway_transaction *transaction) { .width = instruction->state.swayc_width, .height = instruction->state.swayc_height, }; - for (int j = 0; j < root_container.children->length; ++j) { - struct sway_container *output = root_container.children->items[j]; - output_damage_box(output->sway_output, &old_box); - output_damage_box(output->sway_output, &new_box); + for (int j = 0; j < root_container.current.children->length; ++j) { + struct sway_container *output = root_container.current.children->items[j]; + if (output->sway_output) { + output_damage_box(output->sway_output, &old_box); + output_damage_box(output->sway_output, &new_box); + } } // There are separate children lists for each instruction state, the diff --git a/sway/input/cursor.c b/sway/input/cursor.c index 307eedd4..7a9f3ed7 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -10,6 +10,7 @@ #include #include "list.h" #include "log.h" +#include "sway/desktop/transaction.h" #include "sway/input/cursor.h" #include "sway/layers.h" #include "sway/output.h" @@ -219,6 +220,7 @@ void cursor_send_pointer_motion(struct sway_cursor *cursor, uint32_t time_msec, struct sway_drag_icon *drag_icon = wlr_drag_icon->data; drag_icon_update_position(drag_icon); } + transaction_commit_dirty(); } static void handle_cursor_motion(struct wl_listener *listener, void *data) { @@ -278,6 +280,7 @@ void dispatch_cursor_button(struct sway_cursor *cursor, wlr_seat_pointer_notify_button(cursor->seat->wlr_seat, time_msec, button, state); + transaction_commit_dirty(); } static void handle_cursor_button(struct wl_listener *listener, void *data) { diff --git a/sway/input/keyboard.c b/sway/input/keyboard.c index 580c0d4b..ede38519 100644 --- a/sway/input/keyboard.c +++ b/sway/input/keyboard.c @@ -3,6 +3,7 @@ #include #include #include +#include "sway/desktop/transaction.h" #include "sway/input/seat.h" #include "sway/input/keyboard.h" #include "sway/input/input-manager.h" @@ -126,6 +127,7 @@ static void keyboard_execute_command(struct sway_keyboard *keyboard, binding->command); config->handler_context.seat = keyboard->seat_device->sway_seat; struct cmd_results *results = execute_command(binding->command, NULL); + transaction_commit_dirty(); if (results->status != CMD_SUCCESS) { wlr_log(WLR_DEBUG, "could not run command for binding: %s (%s)", binding->command, results->error); diff --git a/sway/input/seat.c b/sway/input/seat.c index 5e65ca70..74f1375e 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -661,9 +661,13 @@ void seat_set_focus_warp(struct sway_seat *seat, if (last_focus) { seat_send_unfocus(last_focus, seat); } - seat_send_focus(container, seat); - container_damage_whole(container->parent); + + container_set_dirty(container); + container_set_dirty(container->parent); // for focused_inactive_child + if (last_focus) { + container_set_dirty(last_focus); + } } // If we've focused a floating container, bring it to the front. @@ -717,10 +721,6 @@ void seat_set_focus_warp(struct sway_seat *seat, } } - if (last_focus) { - container_damage_whole(last_focus); - } - if (last_workspace && last_workspace != new_workspace) { cursor_send_pointer_motion(seat->cursor, 0, true); } @@ -840,18 +840,6 @@ struct sway_container *seat_get_active_child(struct sway_seat *seat, return NULL; } -struct sway_container *seat_get_active_current_child(struct sway_seat *seat, - struct sway_container *container) { - struct sway_seat_container *current = NULL; - wl_list_for_each(current, &seat->focus_stack, link) { - if (current->container->current.parent == container && - current->container->current.layout != L_FLOATING) { - return current->container; - } - } - return NULL; -} - struct sway_container *seat_get_focus(struct sway_seat *seat) { if (!seat->has_focus) { return NULL; diff --git a/sway/ipc-server.c b/sway/ipc-server.c index c5161a6b..be703915 100644 --- a/sway/ipc-server.c +++ b/sway/ipc-server.c @@ -18,6 +18,7 @@ #include #include "sway/commands.h" #include "sway/config.h" +#include "sway/desktop/transaction.h" #include "sway/ipc-json.h" #include "sway/ipc-server.h" #include "sway/output.h" @@ -484,6 +485,7 @@ void ipc_client_handle_command(struct ipc_client *client) { case IPC_COMMAND: { struct cmd_results *results = execute_command(buf, NULL); + transaction_commit_dirty(); char *json = cmd_results_to_json(results); int length = strlen(json); client_valid = ipc_send_reply(client, json, (uint32_t)length); diff --git a/sway/main.c b/sway/main.c index 1d772b48..1a55b519 100644 --- a/sway/main.c +++ b/sway/main.c @@ -20,6 +20,7 @@ #include "sway/commands.h" #include "sway/config.h" #include "sway/debug.h" +#include "sway/desktop/transaction.h" #include "sway/server.h" #include "sway/tree/layout.h" #include "sway/ipc-server.h" @@ -441,6 +442,7 @@ int main(int argc, char **argv) { free(line); list_del(config->cmd_queue, 0); } + transaction_commit_dirty(); if (!terminate_request) { server_run(&server); -- cgit v1.2.3-70-g09d2 From 315d5311b2004b9e148e7b52a7de161b6dfe3878 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Sun, 15 Jul 2018 22:43:33 +1000 Subject: Implement urgency base functionality Introduces a command to manually set urgency, as well as rendering of urgent views, sending the IPC event, removing urgency after focused for one second, and matching urgent views via criteria. --- include/sway/commands.h | 1 + include/sway/tree/view.h | 8 ++++++++ include/sway/tree/workspace.h | 3 +++ sway/commands.c | 1 + sway/commands/urgent.c | 36 +++++++++++++++++++++++++++++++++++ sway/criteria.c | 44 +++++++++++++++++++++++++++++++++++++++++-- sway/desktop/render.c | 24 +++++++++++++++++++---- sway/input/seat.c | 16 ++++++++++++++++ sway/ipc-json.c | 3 ++- sway/meson.build | 1 + sway/tree/view.c | 30 +++++++++++++++++++++++++++++ sway/tree/workspace.c | 10 ++++++++++ 12 files changed, 170 insertions(+), 7 deletions(-) create mode 100644 sway/commands/urgent.c (limited to 'include/sway') diff --git a/include/sway/commands.h b/include/sway/commands.h index 3ebd0002..1e93e2a3 100644 --- a/include/sway/commands.h +++ b/include/sway/commands.h @@ -152,6 +152,7 @@ sway_cmd cmd_swaybg_command; sway_cmd cmd_swap; sway_cmd cmd_title_format; sway_cmd cmd_unmark; +sway_cmd cmd_urgent; sway_cmd cmd_workspace; sway_cmd cmd_ws_auto_back_and_forth; sway_cmd cmd_workspace_layout; diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h index 21d6403e..9022f7a6 100644 --- a/include/sway/tree/view.h +++ b/include/sway/tree/view.h @@ -70,6 +70,10 @@ struct sway_view { bool border_left; bool border_right; + struct timespec urgent; + bool allow_request_urgent; + struct wl_event_source *urgent_timer; + bool destroying; list_t *executed_criteria; // struct criteria * @@ -305,4 +309,8 @@ void view_update_marks_textures(struct sway_view *view); */ bool view_is_visible(struct sway_view *view); +void view_set_urgent(struct sway_view *view, bool enable); + +bool view_is_urgent(struct sway_view *view); + #endif diff --git a/include/sway/tree/workspace.h b/include/sway/tree/workspace.h index c72a4ac0..8c2f4cd5 100644 --- a/include/sway/tree/workspace.h +++ b/include/sway/tree/workspace.h @@ -42,4 +42,7 @@ void workspace_output_add_priority(struct sway_container *workspace, struct sway_container *workspace_output_get_highest_available( struct sway_container *ws, struct sway_container *exclude); + +bool workspace_is_urgent(struct sway_container *workspace); + #endif diff --git a/sway/commands.c b/sway/commands.c index addd64a6..3578e748 100644 --- a/sway/commands.c +++ b/sway/commands.c @@ -153,6 +153,7 @@ static struct cmd_handler command_handlers[] = { { "swap", cmd_swap }, { "title_format", cmd_title_format }, { "unmark", cmd_unmark }, + { "urgent", cmd_urgent }, }; static int handler_compare(const void *_a, const void *_b) { diff --git a/sway/commands/urgent.c b/sway/commands/urgent.c new file mode 100644 index 00000000..d199858a --- /dev/null +++ b/sway/commands/urgent.c @@ -0,0 +1,36 @@ +#include "log.h" +#include "sway/commands.h" +#include "sway/config.h" +#include "sway/tree/arrange.h" +#include "sway/tree/container.h" +#include "sway/tree/view.h" +#include "sway/tree/layout.h" + +struct cmd_results *cmd_urgent(int argc, char **argv) { + struct cmd_results *error = NULL; + if ((error = checkarg(argc, "urgent", EXPECTED_EQUAL_TO, 1))) { + return error; + } + struct sway_container *container = + config->handler_context.current_container; + if (container->type != C_VIEW) { + return cmd_results_new(CMD_INVALID, "urgent", + "Only views can be urgent"); + } + struct sway_view *view = container->sway_view; + + if (strcmp(argv[0], "enable") == 0) { + view_set_urgent(view, true); + } else if (strcmp(argv[0], "disable") == 0) { + view_set_urgent(view, false); + } else if (strcmp(argv[0], "allow") == 0) { + view->allow_request_urgent = true; + } else if (strcmp(argv[0], "deny") == 0) { + view->allow_request_urgent = false; + } else { + return cmd_results_new(CMD_INVALID, "urgent", + "Expected 'urgent '"); + } + + return cmd_results_new(CMD_SUCCESS, NULL, NULL); +} diff --git a/sway/criteria.c b/sway/criteria.c index 29a3668b..c999d248 100644 --- a/sway/criteria.c +++ b/sway/criteria.c @@ -46,6 +46,31 @@ static int regex_cmp(const char *item, const pcre *regex) { return pcre_exec(regex, NULL, item, strlen(item), 0, 0, NULL, 0); } +static int cmp_urgent(const void *_a, const void *_b) { + struct sway_view *a = *(void **)_a; + struct sway_view *b = *(void **)_b; + + if (a->urgent.tv_sec < b->urgent.tv_sec) { + return -1; + } else if (a->urgent.tv_sec > b->urgent.tv_sec) { + return 1; + } + if (a->urgent.tv_nsec < b->urgent.tv_nsec) { + return -1; + } else if (a->urgent.tv_nsec > b->urgent.tv_nsec) { + return 1; + } + return 0; +} + +static void find_urgent_iterator(struct sway_container *swayc, void *data) { + if (swayc->type != C_VIEW || !view_is_urgent(swayc->sway_view)) { + return; + } + list_t *urgent_views = data; + list_add(urgent_views, swayc->sway_view); +} + static bool criteria_matches_view(struct criteria *criteria, struct sway_view *view) { if (criteria->title) { @@ -133,8 +158,23 @@ static bool criteria_matches_view(struct criteria *criteria, } if (criteria->urgent) { - // TODO - return false; + if (!view_is_urgent(view)) { + return false; + } + list_t *urgent_views = create_list(); + container_for_each_descendant_dfs(&root_container, + find_urgent_iterator, urgent_views); + list_stable_sort(urgent_views, cmp_urgent); + struct sway_view *target; + if (criteria->urgent == 'o') { // oldest + target = urgent_views->items[0]; + } else { // latest + target = urgent_views->items[urgent_views->length - 1]; + } + list_free(urgent_views); + if (view != target) { + return false; + } } if (criteria->workspace) { diff --git a/sway/desktop/render.c b/sway/desktop/render.c index 17fe823a..3180f8ba 100644 --- a/sway/desktop/render.c +++ b/sway/desktop/render.c @@ -553,7 +553,11 @@ static void render_container_simple(struct sway_output *output, struct wlr_texture *marks_texture; struct sway_container_state *state = &child->current; - if (state->focused || parent_focused) { + if (view_is_urgent(view)) { + colors = &config->border_colors.urgent; + title_texture = child->title_urgent; + marks_texture = view->marks_urgent; + } else if (state->focused || parent_focused) { colors = &config->border_colors.focused; title_texture = child->title_focused; marks_texture = view->marks_focused; @@ -608,7 +612,11 @@ static void render_container_tabbed(struct sway_output *output, struct wlr_texture *title_texture; struct wlr_texture *marks_texture; - if (cstate->focused || parent_focused) { + if (view && view_is_urgent(view)) { + colors = &config->border_colors.urgent; + title_texture = child->title_urgent; + marks_texture = view->marks_urgent; + } else if (cstate->focused || parent_focused) { colors = &config->border_colors.focused; title_texture = child->title_focused; marks_texture = view ? view->marks_focused : NULL; @@ -671,7 +679,11 @@ static void render_container_stacked(struct sway_output *output, struct wlr_texture *title_texture; struct wlr_texture *marks_texture; - if (cstate->focused || parent_focused) { + if (view && view_is_urgent(view)) { + colors = &config->border_colors.urgent; + title_texture = child->title_urgent; + marks_texture = view->marks_urgent; + } else if (cstate->focused || parent_focused) { colors = &config->border_colors.focused; title_texture = child->title_focused; marks_texture = view ? view->marks_focused : NULL; @@ -731,7 +743,11 @@ static void render_floating_container(struct sway_output *soutput, struct wlr_texture *title_texture; struct wlr_texture *marks_texture; - if (con->current.focused) { + if (view_is_urgent(view)) { + colors = &config->border_colors.urgent; + title_texture = con->title_urgent; + marks_texture = view->marks_urgent; + } else if (con->current.focused) { colors = &config->border_colors.focused; title_texture = con->title_focused; marks_texture = view->marks_focused; diff --git a/sway/input/seat.c b/sway/input/seat.c index 74f1375e..7058cc92 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -594,6 +594,12 @@ static void seat_send_unfocus(struct sway_container *container, } } +static int handle_urgent_timeout(void *data) { + struct sway_view *view = data; + view_set_urgent(view, false); + return 0; +} + void seat_set_focus_warp(struct sway_seat *seat, struct sway_container *container, bool warp) { if (seat->focused_layer) { @@ -670,6 +676,16 @@ void seat_set_focus_warp(struct sway_seat *seat, } } + // If urgent, start a timer to unset it + if (container && container->type == C_VIEW && + view_is_urgent(container->sway_view) && + !container->sway_view->urgent_timer) { + struct sway_view *view = container->sway_view; + view->urgent_timer = wl_event_loop_add_timer(server.wl_event_loop, + handle_urgent_timeout, view); + wl_event_source_timer_update(view->urgent_timer, 1000); + } + // If we've focused a floating container, bring it to the front. // We do this by putting it at the end of the floating list. // This must happen for both the pending and current children lists. diff --git a/sway/ipc-json.c b/sway/ipc-json.c index 3d0e88f0..8c48e724 100644 --- a/sway/ipc-json.c +++ b/sway/ipc-json.c @@ -170,7 +170,8 @@ static void ipc_json_describe_workspace(struct sway_container *workspace, json_object_object_add(object, "output", workspace->parent ? json_object_new_string(workspace->parent->name) : NULL); json_object_object_add(object, "type", json_object_new_string("workspace")); - json_object_object_add(object, "urgent", json_object_new_boolean(false)); + json_object_object_add(object, "urgent", + json_object_new_boolean(workspace_is_urgent(workspace))); json_object_object_add(object, "representation", workspace->formatted_title ? json_object_new_string(workspace->formatted_title) : NULL); diff --git a/sway/meson.build b/sway/meson.build index f878450d..b64bd137 100644 --- a/sway/meson.build +++ b/sway/meson.build @@ -76,6 +76,7 @@ sway_sources = files( 'commands/swap.c', 'commands/title_format.c', 'commands/unmark.c', + 'commands/urgent.c', 'commands/workspace.c', 'commands/workspace_layout.c', 'commands/ws_auto_back_and_forth.c', diff --git a/sway/tree/view.c b/sway/tree/view.c index bf380d98..a2dbe92c 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -25,6 +25,7 @@ void view_init(struct sway_view *view, enum sway_view_type type, view->impl = impl; view->executed_criteria = create_list(); view->marks = create_list(); + view->allow_request_urgent = true; wl_signal_init(&view->events.unmap); } @@ -589,6 +590,11 @@ void view_unmap(struct sway_view *view) { wl_list_remove(&view->surface_new_subsurface.link); wl_list_remove(&view->container_reparent.link); + if (view->urgent_timer) { + wl_event_source_remove(view->urgent_timer); + view->urgent_timer = NULL; + } + if (view->is_fullscreen) { struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); ws->sway_workspace->fullscreen = NULL; @@ -1047,3 +1053,27 @@ bool view_is_visible(struct sway_view *view) { } return true; } + +void view_set_urgent(struct sway_view *view, bool enable) { + if (enable) { + struct sway_seat *seat = input_manager_current_seat(input_manager); + if (seat_get_focus(seat) == view->swayc) { + return; + } + clock_gettime(CLOCK_MONOTONIC, &view->urgent); + } else { + view->urgent = (struct timespec){ 0 }; + if (view->urgent_timer) { + wl_event_source_remove(view->urgent_timer); + view->urgent_timer = NULL; + } + } + container_damage_whole(view->swayc); + + struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); + ipc_event_workspace(ws, NULL, "urgent"); +} + +bool view_is_urgent(struct sway_view *view) { + return view->urgent.tv_sec || view->urgent.tv_nsec; +} diff --git a/sway/tree/workspace.c b/sway/tree/workspace.c index 2a2d834a..d71b0a53 100644 --- a/sway/tree/workspace.c +++ b/sway/tree/workspace.c @@ -11,6 +11,7 @@ #include "sway/ipc-server.h" #include "sway/tree/arrange.h" #include "sway/tree/container.h" +#include "sway/tree/view.h" #include "sway/tree/workspace.h" #include "list.h" #include "log.h" @@ -518,3 +519,12 @@ struct sway_container *workspace_output_get_highest_available( return NULL; } + +static bool find_urgent_iterator(struct sway_container *con, + void *data) { + return con->type == C_VIEW && view_is_urgent(con->sway_view); +} + +bool workspace_is_urgent(struct sway_container *workspace) { + return container_find(workspace, find_urgent_iterator, NULL); +} -- cgit v1.2.3-70-g09d2 From 5f0a4bb6a46cf359dd270e3c448ca1e112331f9d Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Mon, 16 Jul 2018 13:15:35 +1000 Subject: Update workspace urgent state when views close or move workspaces --- include/sway/tree/workspace.h | 3 ++- sway/ipc-json.c | 2 +- sway/tree/container.c | 2 ++ sway/tree/layout.c | 11 +++++++++++ sway/tree/view.c | 11 ++++++++--- sway/tree/workspace.c | 9 +++++++-- 6 files changed, 31 insertions(+), 7 deletions(-) (limited to 'include/sway') diff --git a/include/sway/tree/workspace.h b/include/sway/tree/workspace.h index 8c2f4cd5..bc95317a 100644 --- a/include/sway/tree/workspace.h +++ b/include/sway/tree/workspace.h @@ -10,6 +10,7 @@ struct sway_workspace { struct sway_view *fullscreen; struct sway_container *floating; list_t *output_priority; + bool urgent; }; extern char *prev_workspace_name; @@ -43,6 +44,6 @@ void workspace_output_add_priority(struct sway_container *workspace, struct sway_container *workspace_output_get_highest_available( struct sway_container *ws, struct sway_container *exclude); -bool workspace_is_urgent(struct sway_container *workspace); +void workspace_detect_urgent(struct sway_container *workspace); #endif diff --git a/sway/ipc-json.c b/sway/ipc-json.c index f8de51ed..dbab8e68 100644 --- a/sway/ipc-json.c +++ b/sway/ipc-json.c @@ -171,7 +171,7 @@ static void ipc_json_describe_workspace(struct sway_container *workspace, json_object_new_string(workspace->parent->name) : NULL); json_object_object_add(object, "type", json_object_new_string("workspace")); json_object_object_add(object, "urgent", - json_object_new_boolean(workspace_is_urgent(workspace))); + json_object_new_boolean(workspace->sway_workspace->urgent)); json_object_object_add(object, "representation", workspace->formatted_title ? json_object_new_string(workspace->formatted_title) : NULL); diff --git a/sway/tree/container.c b/sway/tree/container.c index 99d57218..c1de46b5 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -1070,6 +1070,8 @@ void container_floating_move_to(struct sway_container *con, container_add_child(new_workspace->sway_workspace->floating, con); arrange_windows(old_workspace); arrange_windows(new_workspace); + workspace_detect_urgent(old_workspace); + workspace_detect_urgent(new_workspace); } } diff --git a/sway/tree/layout.c b/sway/tree/layout.c index 54ddb3f9..197a2fc8 100644 --- a/sway/tree/layout.c +++ b/sway/tree/layout.c @@ -225,6 +225,15 @@ void container_move_to(struct sway_container *container, } } } + // Update workspace urgent state + struct sway_container *old_workspace = old_parent; + if (old_workspace->type != C_WORKSPACE) { + old_workspace = container_parent(old_workspace, C_WORKSPACE); + } + if (new_workspace != old_workspace) { + workspace_detect_urgent(new_workspace); + workspace_detect_urgent(old_workspace); + } } static bool sway_dir_to_wlr(enum movement_direction dir, @@ -548,6 +557,8 @@ void container_move(struct sway_container *container, } if (last_ws && next_ws && last_ws != next_ws) { ipc_event_workspace(last_ws, container, "focus"); + workspace_detect_urgent(last_ws); + workspace_detect_urgent(next_ws); } } diff --git a/sway/tree/view.c b/sway/tree/view.c index ae520b07..b5b73408 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -595,16 +595,21 @@ void view_unmap(struct sway_view *view) { view->urgent_timer = NULL; } + struct sway_container *parent; + struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); + if (view->is_fullscreen) { - struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); ws->sway_workspace->fullscreen = NULL; - container_destroy(view->swayc); + parent = container_destroy(view->swayc); arrange_windows(ws->parent); } else { struct sway_container *parent = container_destroy(view->swayc); arrange_windows(parent); } + if (parent->type >= C_WORKSPACE) { // if the workspace still exists + workspace_detect_urgent(ws); + } transaction_commit_dirty(); view->surface = NULL; } @@ -1073,7 +1078,7 @@ void view_set_urgent(struct sway_view *view, bool enable) { ipc_event_window(view->swayc, "urgent"); struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); - ipc_event_workspace(NULL, ws, "urgent"); + workspace_detect_urgent(ws); } bool view_is_urgent(struct sway_view *view) { diff --git a/sway/tree/workspace.c b/sway/tree/workspace.c index d71b0a53..d14f01eb 100644 --- a/sway/tree/workspace.c +++ b/sway/tree/workspace.c @@ -525,6 +525,11 @@ static bool find_urgent_iterator(struct sway_container *con, return con->type == C_VIEW && view_is_urgent(con->sway_view); } -bool workspace_is_urgent(struct sway_container *workspace) { - return container_find(workspace, find_urgent_iterator, NULL); +void workspace_detect_urgent(struct sway_container *workspace) { + bool new_urgent = container_find(workspace, find_urgent_iterator, NULL); + + if (workspace->sway_workspace->urgent != new_urgent) { + workspace->sway_workspace->urgent = new_urgent; + ipc_event_workspace(NULL, workspace, "urgent"); + } } -- cgit v1.2.3-70-g09d2 From be28c18ad5a3271aad537a5356662d57f16d9703 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Mon, 16 Jul 2018 14:30:31 +1000 Subject: Mark containers as urgent in IPC if they have urgent views --- include/sway/tree/container.h | 2 ++ sway/ipc-json.c | 7 +++---- sway/tree/container.c | 9 +++++++++ sway/tree/workspace.c | 7 +------ 4 files changed, 15 insertions(+), 10 deletions(-) (limited to 'include/sway') diff --git a/include/sway/tree/container.h b/include/sway/tree/container.h index 04e50fc6..ca7a3288 100644 --- a/include/sway/tree/container.h +++ b/include/sway/tree/container.h @@ -316,4 +316,6 @@ void container_floating_move_to(struct sway_container *con, */ void container_set_dirty(struct sway_container *container); +bool container_has_urgent_child(struct sway_container *container); + #endif diff --git a/sway/ipc-json.c b/sway/ipc-json.c index dbab8e68..c49ea47e 100644 --- a/sway/ipc-json.c +++ b/sway/ipc-json.c @@ -198,10 +198,9 @@ static void ipc_json_describe_view(struct sway_container *c, json_object *object json_object_new_string(ipc_json_layout_description(c->layout))); } - if (c->type == C_VIEW) { - json_object_object_add(object, "urgent", - json_object_new_boolean(view_is_urgent(c->sway_view))); - } + bool urgent = c->type == C_VIEW ? + view_is_urgent(c->sway_view) : container_has_urgent_child(c); + json_object_object_add(object, "urgent", json_object_new_boolean(urgent)); } static void focus_inactive_children_iterator(struct sway_container *c, void *data) { diff --git a/sway/tree/container.c b/sway/tree/container.c index c1de46b5..6d52c38c 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -1082,3 +1082,12 @@ void container_set_dirty(struct sway_container *container) { container->dirty = true; list_add(server.dirty_containers, container); } + +static bool find_urgent_iterator(struct sway_container *con, + void *data) { + return con->type == C_VIEW && view_is_urgent(con->sway_view); +} + +bool container_has_urgent_child(struct sway_container *container) { + return container_find(container, find_urgent_iterator, NULL); +} diff --git a/sway/tree/workspace.c b/sway/tree/workspace.c index d14f01eb..00b479ec 100644 --- a/sway/tree/workspace.c +++ b/sway/tree/workspace.c @@ -520,13 +520,8 @@ struct sway_container *workspace_output_get_highest_available( return NULL; } -static bool find_urgent_iterator(struct sway_container *con, - void *data) { - return con->type == C_VIEW && view_is_urgent(con->sway_view); -} - void workspace_detect_urgent(struct sway_container *workspace) { - bool new_urgent = container_find(workspace, find_urgent_iterator, NULL); + bool new_urgent = container_has_urgent_child(workspace); if (workspace->sway_workspace->urgent != new_urgent) { workspace->sway_workspace->urgent = new_urgent; -- cgit v1.2.3-70-g09d2 From fc2484095a71206fe82f5042c0d127458a8da3bc Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Mon, 16 Jul 2018 22:18:12 +1000 Subject: Implement no_focus command --- include/sway/criteria.h | 5 +++-- sway/commands.c | 1 + sway/commands/no_focus.c | 26 ++++++++++++++++++++++++++ sway/meson.build | 1 + sway/tree/view.c | 28 +++++++++++++++++++++++++--- 5 files changed, 56 insertions(+), 5 deletions(-) create mode 100644 sway/commands/no_focus.c (limited to 'include/sway') diff --git a/include/sway/criteria.h b/include/sway/criteria.h index bd3ca0ac..6a8337c5 100644 --- a/include/sway/criteria.h +++ b/include/sway/criteria.h @@ -6,9 +6,10 @@ #include "tree/view.h" enum criteria_type { - CT_COMMAND = 1 << 0, - CT_ASSIGN_OUTPUT = 1 << 1, + CT_COMMAND = 1 << 0, + CT_ASSIGN_OUTPUT = 1 << 1, CT_ASSIGN_WORKSPACE = 1 << 2, + CT_NO_FOCUS = 1 << 3, }; struct criteria { diff --git a/sway/commands.c b/sway/commands.c index addd64a6..c2ba02cf 100644 --- a/sway/commands.c +++ b/sway/commands.c @@ -114,6 +114,7 @@ static struct cmd_handler handlers[] = { { "input", cmd_input }, { "mode", cmd_mode }, { "mouse_warping", cmd_mouse_warping }, + { "no_focus", cmd_no_focus }, { "output", cmd_output }, { "seat", cmd_seat }, { "set", cmd_set }, diff --git a/sway/commands/no_focus.c b/sway/commands/no_focus.c new file mode 100644 index 00000000..61a8de7e --- /dev/null +++ b/sway/commands/no_focus.c @@ -0,0 +1,26 @@ +#define _XOPEN_SOURCE 500 +#include +#include "sway/commands.h" +#include "sway/criteria.h" +#include "list.h" +#include "log.h" + +struct cmd_results *cmd_no_focus(int argc, char **argv) { + struct cmd_results *error = NULL; + if ((error = checkarg(argc, "no_focus", EXPECTED_AT_LEAST, 1))) { + return error; + } + + char *err_str = NULL; + struct criteria *criteria = criteria_parse(argv[0], &err_str); + if (!criteria) { + error = cmd_results_new(CMD_INVALID, "no_focus", err_str); + free(err_str); + return error; + } + + criteria->type = CT_NO_FOCUS; + list_add(config->criteria, criteria); + + return cmd_results_new(CMD_SUCCESS, NULL, NULL); +} diff --git a/sway/meson.build b/sway/meson.build index f878450d..4afef7b4 100644 --- a/sway/meson.build +++ b/sway/meson.build @@ -59,6 +59,7 @@ sway_sources = files( 'commands/mode.c', 'commands/mouse_warping.c', 'commands/move.c', + 'commands/no_focus.c', 'commands/output.c', 'commands/reload.c', 'commands/rename.c', diff --git a/sway/tree/view.c b/sway/tree/view.c index bf380d98..bc66a701 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -518,6 +518,26 @@ void view_execute_criteria(struct sway_view *view) { seat_set_focus(seat, prior_focus); } +static bool should_focus(struct sway_view *view) { + // If the view is the only one in the focused workspace, it'll get focus + // regardless of any no_focus criteria. + struct sway_container *parent = view->swayc->parent; + struct sway_seat *seat = input_manager_current_seat(input_manager); + if (parent->type == C_WORKSPACE && seat_get_focus(seat) == parent) { + size_t num_children = parent->children->length + + parent->sway_workspace->floating->children->length; + if (num_children == 1) { + return true; + } + } + + // Check no_focus criteria + list_t *criterias = criteria_for_view(view, CT_NO_FOCUS); + size_t len = criterias->length; + list_free(criterias); + return len == 0; +} + void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) { if (!sway_assert(view->surface == NULL, "cannot map mapped view")) { return; @@ -571,9 +591,11 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) { view_set_tiled(view, true); } - input_manager_set_focus(input_manager, cont); - if (workspace) { - workspace_switch(workspace); + if (should_focus(view)) { + input_manager_set_focus(input_manager, cont); + if (workspace) { + workspace_switch(workspace); + } } view_update_title(view, false); -- cgit v1.2.3-70-g09d2 From 75c699db62e63e2a3c2aa652c9ba9482a8f13ec3 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Tue, 17 Jul 2018 10:14:33 +1000 Subject: Implement default_floating_border command and adjust CSD behaviour --- include/sway/tree/view.h | 1 + sway/commands.c | 1 + sway/commands/default_floating_border.c | 29 +++++++++++++++++++++++++++++ sway/desktop/render.c | 32 ++++++++++++++++++++------------ sway/meson.build | 1 + sway/tree/container.c | 11 ++++++++--- sway/tree/view.c | 18 ++++++++++++------ 7 files changed, 72 insertions(+), 21 deletions(-) create mode 100644 sway/commands/default_floating_border.c (limited to 'include/sway') diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h index 9022f7a6..e270f851 100644 --- a/include/sway/tree/view.h +++ b/include/sway/tree/view.h @@ -69,6 +69,7 @@ struct sway_view { bool border_bottom; bool border_left; bool border_right; + bool using_csd; struct timespec urgent; bool allow_request_urgent; diff --git a/sway/commands.c b/sway/commands.c index 27329602..a3e6a500 100644 --- a/sway/commands.c +++ b/sway/commands.c @@ -98,6 +98,7 @@ static struct cmd_handler handlers[] = { { "client.unfocused", cmd_client_unfocused }, { "client.urgent", cmd_client_urgent }, { "default_border", cmd_default_border }, + { "default_floating_border", cmd_default_floating_border }, { "exec", cmd_exec }, { "exec_always", cmd_exec_always }, { "floating_maximum_size", cmd_floating_maximum_size }, diff --git a/sway/commands/default_floating_border.c b/sway/commands/default_floating_border.c new file mode 100644 index 00000000..1bfc24af --- /dev/null +++ b/sway/commands/default_floating_border.c @@ -0,0 +1,29 @@ +#include "log.h" +#include "sway/commands.h" +#include "sway/config.h" +#include "sway/tree/container.h" + +struct cmd_results *cmd_default_floating_border(int argc, char **argv) { + struct cmd_results *error = NULL; + if ((error = checkarg(argc, "default_floating_border", + EXPECTED_AT_LEAST, 1))) { + return error; + } + + if (strcmp(argv[0], "none") == 0) { + config->floating_border = B_NONE; + } else if (strcmp(argv[0], "normal") == 0) { + config->floating_border = B_NORMAL; + } else if (strcmp(argv[0], "pixel") == 0) { + config->floating_border = B_PIXEL; + } else { + return cmd_results_new(CMD_INVALID, "default_floating_border", + "Expected 'default_floating_border ' " + "or 'default_floating_border '"); + } + if (argc == 2) { + config->floating_border_thickness = atoi(argv[1]); + } + + return cmd_results_new(CMD_SUCCESS, NULL, NULL); +} diff --git a/sway/desktop/render.c b/sway/desktop/render.c index cb995215..4c85e516 100644 --- a/sway/desktop/render.c +++ b/sway/desktop/render.c @@ -256,6 +256,10 @@ static void render_view(struct sway_output *output, pixman_region32_t *damage, render_view_surfaces(view, output, damage, view->swayc->alpha); } + if (view->using_csd) { + return; + } + struct wlr_box box; float output_scale = output->wlr_output->scale; float color[4]; @@ -571,12 +575,14 @@ static void render_container_simple(struct sway_output *output, marks_texture = view->marks_unfocused; } - if (state->border == B_NORMAL) { - render_titlebar(output, damage, child, state->swayc_x, - state->swayc_y, state->swayc_width, colors, - title_texture, marks_texture); - } else { - render_top_border(output, damage, child, colors); + if (!view->using_csd) { + if (state->border == B_NORMAL) { + render_titlebar(output, damage, child, state->swayc_x, + state->swayc_y, state->swayc_width, colors, + title_texture, marks_texture); + } else { + render_top_border(output, damage, child, colors); + } } render_view(output, damage, child, colors); } else { @@ -761,12 +767,14 @@ static void render_floating_container(struct sway_output *soutput, marks_texture = view->marks_unfocused; } - if (con->current.border == B_NORMAL) { - render_titlebar(soutput, damage, con, con->current.swayc_x, - con->current.swayc_y, con->current.swayc_width, colors, - title_texture, marks_texture); - } else if (con->current.border != B_NONE) { - render_top_border(soutput, damage, con, colors); + if (!view->using_csd) { + if (con->current.border == B_NORMAL) { + render_titlebar(soutput, damage, con, con->current.swayc_x, + con->current.swayc_y, con->current.swayc_width, colors, + title_texture, marks_texture); + } else if (con->current.border != B_NONE) { + render_top_border(soutput, damage, con, colors); + } } render_view(soutput, damage, con, colors); } else { diff --git a/sway/meson.build b/sway/meson.build index 23e54a62..c58d3470 100644 --- a/sway/meson.build +++ b/sway/meson.build @@ -35,6 +35,7 @@ sway_sources = files( 'commands/border.c', 'commands/client.c', 'commands/default_border.c', + 'commands/default_floating_border.c', 'commands/default_orientation.c', 'commands/exit.c', 'commands/exec.c', diff --git a/sway/tree/container.c b/sway/tree/container.c index 6d52c38c..3f9d701a 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -967,9 +967,14 @@ void container_set_geometry_from_floating_view(struct sway_container *con) { return; } struct sway_view *view = con->sway_view; - size_t border_width = view->border_thickness * (view->border != B_NONE); - size_t top = - view->border == B_NORMAL ? container_titlebar_height() : border_width; + size_t border_width = 0; + size_t top = 0; + + if (!view->using_csd) { + border_width = view->border_thickness * (view->border != B_NONE); + top = view->border == B_NORMAL ? + container_titlebar_height() : border_width; + } con->x = view->x - border_width; con->y = view->y - top; diff --git a/sway/tree/view.c b/sway/tree/view.c index 76e0f42c..d9938130 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -316,11 +316,15 @@ void view_set_activated(struct sway_view *view, bool activated) { } void view_set_tiled(struct sway_view *view, bool tiled) { - bool csd = true; - if (view->impl->has_client_side_decorations) { - csd = view->impl->has_client_side_decorations(view); + if (!tiled) { + view->using_csd = true; + if (view->impl->has_client_side_decorations) { + view->using_csd = view->impl->has_client_side_decorations(view); + } + } else { + view->using_csd = false; } - view->border = tiled || !csd ? config->border : B_NONE; + if (view->impl->set_tiled) { view->impl->set_tiled(view, tiled); } @@ -573,8 +577,6 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) { view->surface = wlr_surface; view->swayc = cont; - view->border = config->border; - view->border_thickness = config->border_thickness; view_init_subsurfaces(view, wlr_surface); wl_signal_add(&wlr_surface->events.new_subsurface, @@ -585,8 +587,12 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) { view->container_reparent.notify = view_handle_container_reparent; if (view->impl->wants_floating && view->impl->wants_floating(view)) { + view->border = config->floating_border; + view->border_thickness = config->floating_border_thickness; container_set_floating(view->swayc, true); } else { + view->border = config->border; + view->border_thickness = config->border_thickness; view_set_tiled(view, true); } -- cgit v1.2.3-70-g09d2 From 747725b8bb35bf3fd38d442f5bb565e7a4b11ec4 Mon Sep 17 00:00:00 2001 From: emersion Date: Wed, 18 Jul 2018 20:00:48 +0100 Subject: Don't unfocus when an override redirect window is mapped --- include/sway/input/seat.h | 2 +- sway/desktop/xwayland.c | 37 +++++++++++++++---------------------- sway/input/seat.c | 6 +++--- 3 files changed, 19 insertions(+), 26 deletions(-) (limited to 'include/sway') diff --git a/include/sway/input/seat.h b/include/sway/input/seat.h index 1f7792ba..eac1626b 100644 --- a/include/sway/input/seat.h +++ b/include/sway/input/seat.h @@ -83,7 +83,7 @@ void seat_set_focus_warp(struct sway_seat *seat, struct sway_container *container, bool warp); void seat_set_focus_surface(struct sway_seat *seat, - struct wlr_surface *surface); + struct wlr_surface *surface, bool unfocus); void seat_set_focus_layer(struct sway_seat *seat, struct wlr_layer_surface *layer); diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c index 1ee3f660..7737a33a 100644 --- a/sway/desktop/xwayland.c +++ b/sway/desktop/xwayland.c @@ -69,16 +69,11 @@ static void unmanaged_handle_map(struct wl_listener *listener, void *data) { surface->ly = xsurface->y; desktop_damage_surface(xsurface->surface, surface->lx, surface->ly, true); - if (!xsurface->override_redirect) { - struct sway_seat *seat = input_manager_current_seat(input_manager); - struct wlr_xwayland *xwayland = - seat->input->server->xwayland.wlr_xwayland; - wlr_xwayland_set_seat(xwayland, seat->wlr_seat); - seat_set_focus_surface(seat, xsurface->surface); - } - - // TODO: we don't send surface enter/leave events to xwayland unmanaged - // surfaces, but xwayland doesn't support HiDPI anyway + struct sway_seat *seat = input_manager_current_seat(input_manager); + struct wlr_xwayland *xwayland = + seat->input->server->xwayland.wlr_xwayland; + wlr_xwayland_set_seat(xwayland, seat->wlr_seat); + seat_set_focus_surface(seat, xsurface->surface, false); } static void unmanaged_handle_unmap(struct wl_listener *listener, void *data) { @@ -89,18 +84,16 @@ static void unmanaged_handle_unmap(struct wl_listener *listener, void *data) { wl_list_remove(&surface->link); wl_list_remove(&surface->commit.link); - if (!xsurface->override_redirect) { - struct sway_seat *seat = input_manager_current_seat(input_manager); - if (seat->wlr_seat->keyboard_state.focused_surface == - xsurface->surface) { - // Restore focus - struct sway_container *previous = - seat_get_focus_inactive(seat, &root_container); - if (previous) { - // Hack to get seat to re-focus the return value of get_focus - seat_set_focus(seat, previous->parent); - seat_set_focus(seat, previous); - } + struct sway_seat *seat = input_manager_current_seat(input_manager); + if (seat->wlr_seat->keyboard_state.focused_surface == + xsurface->surface) { + // Restore focus + struct sway_container *previous = + seat_get_focus_inactive(seat, &root_container); + if (previous) { + // Hack to get seat to re-focus the return value of get_focus + seat_set_focus(seat, previous->parent); + seat_set_focus(seat, previous); } } } diff --git a/sway/input/seat.c b/sway/input/seat.c index 8ed4a3fe..eadf3b26 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -753,11 +753,11 @@ void seat_set_focus(struct sway_seat *seat, } void seat_set_focus_surface(struct sway_seat *seat, - struct wlr_surface *surface) { + struct wlr_surface *surface, bool unfocus) { if (seat->focused_layer != NULL) { return; } - if (seat->has_focus) { + if (seat->has_focus && unfocus) { struct sway_container *focus = seat_get_focus(seat); seat_send_unfocus(focus, seat); seat->has_focus = false; @@ -789,7 +789,7 @@ void seat_set_focus_layer(struct sway_seat *seat, } else if (!layer || seat->focused_layer == layer) { return; } - seat_set_focus_surface(seat, layer->surface); + seat_set_focus_surface(seat, layer->surface, true); if (layer->layer >= ZWLR_LAYER_SHELL_V1_LAYER_TOP) { seat->focused_layer = layer; } -- cgit v1.2.3-70-g09d2 From ec652866060a178796d6dbb32bf7f0e2101e56c9 Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Thu, 19 Jul 2018 01:39:58 -0400 Subject: Fix deferred command handling --- include/sway/server.h | 1 + sway/config.c | 4 ++-- sway/main.c | 9 ++++++++- sway/server.c | 12 +++++++++--- 4 files changed, 20 insertions(+), 6 deletions(-) (limited to 'include/sway') diff --git a/include/sway/server.h b/include/sway/server.h index a017d1c4..70bde6d4 100644 --- a/include/sway/server.h +++ b/include/sway/server.h @@ -56,6 +56,7 @@ struct sway_server server; bool server_privileged_prepare(struct sway_server *server); bool server_init(struct sway_server *server); void server_fini(struct sway_server *server); +bool server_start_backend(struct sway_server *server); void server_run(struct sway_server *server); void handle_new_output(struct wl_listener *listener, void *data); diff --git a/sway/config.c b/sway/config.c index c620e4c7..00500812 100644 --- a/sway/config.c +++ b/sway/config.c @@ -649,7 +649,6 @@ bool read_config(FILE *file, struct sway_config *config) { } else { res = config_command(expanded); } - free(expanded); switch(res->status) { case CMD_FAILURE: case CMD_INVALID: @@ -660,7 +659,7 @@ bool read_config(FILE *file, struct sway_config *config) { case CMD_DEFER: wlr_log(WLR_DEBUG, "Deferring command `%s'", line); - list_add(config->cmd_queue, strdup(line)); + list_add(config->cmd_queue, strdup(expanded)); break; case CMD_BLOCK_COMMANDS: @@ -693,6 +692,7 @@ bool read_config(FILE *file, struct sway_config *config) { sizeof(config->handler_context)); default:; } + free(expanded); free(line); free_cmd_results(res); } diff --git a/sway/main.c b/sway/main.c index 1a55b519..a20f1dac 100644 --- a/sway/main.c +++ b/sway/main.c @@ -429,9 +429,16 @@ int main(int argc, char **argv) { security_sanity_check(); - config->active = true; setenv("WAYLAND_DISPLAY", server.socket, true); + if (!terminate_request) { + if (!server_start_backend(&server)) { + sway_terminate(EXIT_FAILURE); + } + } + + config->active = true; // Execute commands until there are none left + wlr_log(WLR_DEBUG, "Running deferred commands"); while (config->cmd_queue->length) { char *line = config->cmd_queue->items[0]; struct cmd_results *res = execute_command(line, NULL); diff --git a/sway/server.c b/sway/server.c index f904b177..ee71d538 100644 --- a/sway/server.c +++ b/sway/server.c @@ -137,13 +137,19 @@ void server_fini(struct sway_server *server) { list_free(server->transactions); } -void server_run(struct sway_server *server) { - wlr_log(WLR_INFO, "Running compositor on wayland display '%s'", +bool server_start_backend(struct sway_server *server) { + wlr_log(WLR_INFO, "Starting backend on wayland display '%s'", server->socket); if (!wlr_backend_start(server->backend)) { wlr_log(WLR_ERROR, "Failed to start backend"); wlr_backend_destroy(server->backend); - return; + return false; } + return true; +} + +void server_run(struct sway_server *server) { + wlr_log(WLR_INFO, "Running compositor on wayland display '%s'", + server->socket); wl_display_run(server->wl_display); } -- cgit v1.2.3-70-g09d2 From 63d6233fcb601abd40f6c611aa4193766aaf9044 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Thu, 19 Jul 2018 21:08:51 +1000 Subject: Allow xwayland views to become urgent when on a non-visible workspace This removes the urgency stuff from the commit handler and puts it in a new set_hints handler instead. This allows the xwayland surface to become urgent without having to commit (which doesn't happen if it's on an non-visible workspace). --- include/sway/tree/view.h | 1 + sway/desktop/xwayland.c | 21 +++++++++++++++++---- 2 files changed, 18 insertions(+), 4 deletions(-) (limited to 'include/sway') diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h index e270f851..068d92c6 100644 --- a/include/sway/tree/view.h +++ b/include/sway/tree/view.h @@ -140,6 +140,7 @@ struct sway_xwayland_view { struct wl_listener set_title; struct wl_listener set_class; struct wl_listener set_window_type; + struct wl_listener set_hints; struct wl_listener map; struct wl_listener unmap; struct wl_listener destroy; diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c index 7737a33a..72dc7ca2 100644 --- a/sway/desktop/xwayland.c +++ b/sway/desktop/xwayland.c @@ -290,10 +290,6 @@ static void handle_commit(struct wl_listener *listener, void *data) { } view_damage_from(view); - - if (view->allow_request_urgent) { - view_set_urgent(view, (bool)xsurface->hints_urgency); - } } static void handle_destroy(struct wl_listener *listener, void *data) { @@ -312,6 +308,7 @@ static void handle_destroy(struct wl_listener *listener, void *data) { wl_list_remove(&xwayland_view->set_title.link); wl_list_remove(&xwayland_view->set_class.link); wl_list_remove(&xwayland_view->set_window_type.link); + wl_list_remove(&xwayland_view->set_hints.link); wl_list_remove(&xwayland_view->map.link); wl_list_remove(&xwayland_view->unmap.link); view_destroy(&xwayland_view->view); @@ -437,6 +434,19 @@ static void handle_set_window_type(struct wl_listener *listener, void *data) { view_execute_criteria(view); } +static void handle_set_hints(struct wl_listener *listener, void *data) { + struct sway_xwayland_view *xwayland_view = + wl_container_of(listener, xwayland_view, set_hints); + struct sway_view *view = &xwayland_view->view; + struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; + if (!xsurface->mapped) { + return; + } + if (view->allow_request_urgent) { + view_set_urgent(view, (bool)xsurface->hints_urgency); + } +} + struct sway_view *view_from_wlr_xwayland_surface( struct wlr_xwayland_surface *xsurface) { return xsurface->data; @@ -489,6 +499,9 @@ void handle_xwayland_surface(struct wl_listener *listener, void *data) { &xwayland_view->set_window_type); xwayland_view->set_window_type.notify = handle_set_window_type; + wl_signal_add(&xsurface->events.set_hints, &xwayland_view->set_hints); + xwayland_view->set_hints.notify = handle_set_hints; + wl_signal_add(&xsurface->events.unmap, &xwayland_view->unmap); xwayland_view->unmap.notify = handle_unmap; -- cgit v1.2.3-70-g09d2 From 9605ab45f1c863076bd68acfb1d2e5d25c1b285d Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Fri, 20 Jul 2018 12:32:29 -0400 Subject: Fix output wildcard handling --- include/sway/config.h | 2 + sway/commands/output.c | 104 ++++++++++++++++++++++++++----------------------- sway/config/output.c | 64 +++++++++++++++++++++++------- 3 files changed, 108 insertions(+), 62 deletions(-) (limited to 'include/sway') diff --git a/include/sway/config.h b/include/sway/config.h index f660a269..6f6710e9 100644 --- a/include/sway/config.h +++ b/include/sway/config.h @@ -461,6 +461,8 @@ void merge_output_config(struct output_config *dst, struct output_config *src); void apply_output_config(struct output_config *oc, struct sway_container *output); +struct output_config *store_output_config(struct output_config *oc); + void free_output_config(struct output_config *oc); int workspace_output_cmp_workspace(const void *a, const void *b); diff --git a/sway/commands/output.c b/sway/commands/output.c index 15bbd687..4d98162b 100644 --- a/sway/commands/output.c +++ b/sway/commands/output.c @@ -21,6 +21,60 @@ static struct cmd_handler output_handlers[] = { { "transform", output_cmd_transform }, }; +static struct output_config *get_output_config(char *name, char *identifier) { + int i = list_seq_find(config->output_configs, output_name_cmp, name); + if (i >= 0) { + return config->output_configs->items[i]; + } + + i = list_seq_find(config->output_configs, output_name_cmp, identifier); + if (i >= 0) { + return config->output_configs->items[i]; + } + + return NULL; +} + +static 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; + char id[128]; + struct sway_output *sway_output; + wl_list_for_each(sway_output, &root_container.sway_root->outputs, link) { + char *name = sway_output->wlr_output->name; + output_get_identifier(id, sizeof(id), sway_output); + if (wildcard || !strcmp(name, oc->name) || !strcmp(id, oc->name)) { + if (!sway_output->swayc) { + if (!oc->enabled) { + if (!wildcard) { + break; + } + continue; + } + + output_enable(sway_output); + } + + struct output_config *current = oc; + if (wildcard) { + struct output_config *tmp = get_output_config(name, id); + if (tmp) { + current = tmp; + } + } + apply_output_config(current, sway_output->swayc); + + if (!wildcard) { + // Stop looking if the output config isn't applicable to all + // outputs + break; + } + } + } +} + struct cmd_results *cmd_output(int argc, char **argv) { struct cmd_results *error = checkarg(argc, "output", EXPECTED_AT_LEAST, 1); if (error != NULL) { @@ -60,54 +114,8 @@ struct cmd_results *cmd_output(int argc, char **argv) { config->handler_context.leftovers.argc = 0; config->handler_context.leftovers.argv = NULL; - int i = list_seq_find(config->output_configs, output_name_cmp, output->name); - if (i >= 0) { - // Merge existing config - struct output_config *current = config->output_configs->items[i]; - merge_output_config(current, output); - free_output_config(output); - output = current; - } else { - list_add(config->output_configs, output); - } - - wlr_log(WLR_DEBUG, "Config stored for output %s (enabled: %d) (%dx%d@%fHz " - "position %d,%d scale %f transform %d) (bg %s %s) (dpms %d)", - output->name, output->enabled, output->width, output->height, - output->refresh_rate, output->x, output->y, output->scale, - output->transform, output->background, output->background_option, output->dpms_state); - - // 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. - char identifier[128]; - bool all = strcmp(output->name, "*") == 0; - struct sway_output *sway_output; - wl_list_for_each(sway_output, &root_container.sway_root->outputs, link) { - output_get_identifier(identifier, sizeof(identifier), sway_output); - wlr_log(WLR_DEBUG, "Checking identifier %s", identifier); - if (all || strcmp(sway_output->wlr_output->name, output->name) == 0 - || strcmp(identifier, output->name) == 0) { - if (!sway_output->swayc) { - if (!output->enabled) { - if (!all) { - break; - } - continue; - } - - output_enable(sway_output); - } - - apply_output_config(output, sway_output->swayc); - - if (!all) { - // Stop looking if the output config isn't applicable to all - // outputs - break; - } - } - } + output = store_output_config(output); + apply_output_config_to_outputs(output); return cmd_results_new(CMD_SUCCESS, NULL, NULL); diff --git a/sway/config/output.c b/sway/config/output.c index 1bf9e5f1..505fa745 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -45,10 +45,6 @@ struct output_config *new_output_config(const char *name) { } void merge_output_config(struct output_config *dst, struct output_config *src) { - if (src->name) { - free(dst->name); - dst->name = strdup(src->name); - } if (src->enabled != -1) { dst->enabled = src->enabled; } @@ -86,6 +82,56 @@ void merge_output_config(struct output_config *dst, struct output_config *src) { } } +static void merge_wildcard_on_all(struct output_config *wildcard) { + for (int i = 0; i < config->output_configs->length; i++) { + struct output_config *oc = config->output_configs->items[i]; + if (strcmp(wildcard->name, oc->name) != 0) { + wlr_log(WLR_DEBUG, "Merging output * config on %s", oc->name); + merge_output_config(oc, wildcard); + } + } +} + +struct output_config *store_output_config(struct output_config *oc) { + bool wildcard = strcmp(oc->name, "*") == 0; + if (wildcard) { + merge_wildcard_on_all(oc); + } + + int i = list_seq_find(config->output_configs, output_name_cmp, oc->name); + if (i >= 0) { + wlr_log(WLR_DEBUG, "Merging on top of existing output config"); + struct output_config *current = config->output_configs->items[i]; + merge_output_config(current, oc); + free_output_config(oc); + oc = current; + } else if (!wildcard) { + wlr_log(WLR_DEBUG, "Adding non-wildcard output config"); + i = list_seq_find(config->output_configs, output_name_cmp, "*"); + if (i >= 0) { + wlr_log(WLR_DEBUG, "Merging on top of output * config"); + struct output_config *current = new_output_config(oc->name); + merge_output_config(current, config->output_configs->items[i]); + merge_output_config(current, oc); + free_output_config(oc); + oc = current; + } + list_add(config->output_configs, oc); + } else { + // New wildcard config. Just add it + wlr_log(WLR_DEBUG, "Adding output * config"); + list_add(config->output_configs, oc); + } + + wlr_log(WLR_DEBUG, "Config stored for output %s (enabled: %d) (%dx%d@%fHz " + "position %d,%d scale %f transform %d) (bg %s %s) (dpms %d)", + oc->name, oc->enabled, oc->width, oc->height, oc->refresh_rate, + oc->x, oc->y, oc->scale, oc->transform, oc->background, + oc->background_option, oc->dpms_state); + + return oc; +} + static void set_mode(struct wlr_output *output, int width, int height, float refresh_rate) { int mhz = (int)(refresh_rate * 1000); @@ -165,16 +211,6 @@ void apply_output_config(struct output_config *oc, struct sway_container *output wlr_output_layout_add_auto(output_layout, wlr_output); } - if (!oc || !oc->background) { - // Look for a * config for background - int i = list_seq_find(config->output_configs, output_name_cmp, "*"); - if (i >= 0) { - oc = config->output_configs->items[i]; - } else { - oc = NULL; - } - } - int output_i; for (output_i = 0; output_i < root_container.children->length; ++output_i) { if (root_container.children->items[output_i] == output) { -- cgit v1.2.3-70-g09d2 From c2ed3d8bd6e2ec12f2ce70d7e106c09a7078e91f Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Fri, 20 Jul 2018 19:37:27 +1000 Subject: Implement force_display_urgency_hint The directive sets the timeout before an urgent view becomes normal again after switching to it from another workspace. Also: * When an xwayland surface removes the urgent hint while the timer is active, we now ignore the request. This happens as soon as the view receives focus, so it was effectively making the timer pointless. * The timeout is now only applied when switching to it from another workspace. --- include/sway/commands.h | 1 + include/sway/config.h | 1 + sway/commands.c | 1 + sway/commands/force_display_urgency_hint.c | 28 ++++++++++++++++++++++++++++ sway/config.c | 1 + sway/desktop/xwayland.c | 6 ++++++ sway/input/seat.c | 4 +++- sway/meson.build | 1 + 8 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 sway/commands/force_display_urgency_hint.c (limited to 'include/sway') diff --git a/include/sway/commands.h b/include/sway/commands.h index 1e93e2a3..e71a7228 100644 --- a/include/sway/commands.h +++ b/include/sway/commands.h @@ -113,6 +113,7 @@ sway_cmd cmd_focus_follows_mouse; sway_cmd cmd_focus_wrapping; sway_cmd cmd_font; sway_cmd cmd_for_window; +sway_cmd cmd_force_display_urgency_hint; sway_cmd cmd_force_focus_wrapping; sway_cmd cmd_fullscreen; sway_cmd cmd_gaps; diff --git a/include/sway/config.h b/include/sway/config.h index 6f6710e9..87768399 100644 --- a/include/sway/config.h +++ b/include/sway/config.h @@ -324,6 +324,7 @@ struct sway_config { char *font; size_t font_height; bool pango_markup; + size_t urgent_timeout; // Flags bool focus_follows_mouse; diff --git a/sway/commands.c b/sway/commands.c index 73c968ea..f1f03574 100644 --- a/sway/commands.c +++ b/sway/commands.c @@ -108,6 +108,7 @@ static struct cmd_handler handlers[] = { { "focus_wrapping", cmd_focus_wrapping }, { "font", cmd_font }, { "for_window", cmd_for_window }, + { "force_display_urgency_hint", cmd_force_display_urgency_hint }, { "force_focus_wrapping", cmd_force_focus_wrapping }, { "fullscreen", cmd_fullscreen }, { "gaps", cmd_gaps }, diff --git a/sway/commands/force_display_urgency_hint.c b/sway/commands/force_display_urgency_hint.c new file mode 100644 index 00000000..a25ffff8 --- /dev/null +++ b/sway/commands/force_display_urgency_hint.c @@ -0,0 +1,28 @@ +#include "log.h" +#include "sway/commands.h" +#include "sway/config.h" +#include "sway/tree/arrange.h" +#include "sway/tree/container.h" +#include "sway/tree/view.h" +#include "sway/tree/layout.h" + +struct cmd_results *cmd_force_display_urgency_hint(int argc, char **argv) { + struct cmd_results *error = NULL; + if ((error = checkarg(argc, "force_display_urgency_hint", + EXPECTED_AT_LEAST, 1))) { + return error; + } + + char *err; + int timeout = (int)strtol(argv[0], &err, 10); + if (*err) { + if (strcmp(err, "ms") != 0) { + return cmd_results_new(CMD_INVALID, "force_display_urgency_hint", + "Expected 'force_display_urgency_hint ms'"); + } + } + + config->urgent_timeout = timeout > 0 ? timeout : 0; + + return cmd_results_new(CMD_SUCCESS, NULL, NULL); +} diff --git a/sway/config.c b/sway/config.c index 00500812..4b892852 100644 --- a/sway/config.c +++ b/sway/config.c @@ -186,6 +186,7 @@ static void config_defaults(struct sway_config *config) { config->default_orientation = L_NONE; if (!(config->font = strdup("monospace 10"))) goto cleanup; config->font_height = 17; // height of monospace 10 + config->urgent_timeout = 500; // floating view config->floating_maximum_width = 0; diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c index 72dc7ca2..bce0a37b 100644 --- a/sway/desktop/xwayland.c +++ b/sway/desktop/xwayland.c @@ -442,6 +442,12 @@ static void handle_set_hints(struct wl_listener *listener, void *data) { if (!xsurface->mapped) { return; } + if (!xsurface->hints_urgency && view->urgent_timer) { + // The view is is in the timeout period. We'll ignore the request to + // unset urgency so that the view remains urgent until the timer clears + // it. + return; + } if (view->allow_request_urgent) { view_set_urgent(view, (bool)xsurface->hints_urgency); } diff --git a/sway/input/seat.c b/sway/input/seat.c index eadf3b26..816429d3 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -679,12 +679,14 @@ void seat_set_focus_warp(struct sway_seat *seat, // If urgent, start a timer to unset it if (container && container->type == C_VIEW && + last_workspace && last_workspace != new_workspace && view_is_urgent(container->sway_view) && + config->urgent_timeout > 0 && !container->sway_view->urgent_timer) { struct sway_view *view = container->sway_view; view->urgent_timer = wl_event_loop_add_timer(server.wl_event_loop, handle_urgent_timeout, view); - wl_event_source_timer_update(view->urgent_timer, 1000); + wl_event_source_timer_update(view->urgent_timer, config->urgent_timeout); } // If we've focused a floating container, bring it to the front. diff --git a/sway/meson.build b/sway/meson.build index c58d3470..09bc40b8 100644 --- a/sway/meson.build +++ b/sway/meson.build @@ -47,6 +47,7 @@ sway_sources = files( 'commands/focus_wrapping.c', 'commands/font.c', 'commands/for_window.c', + 'commands/force_display_urgency_hint.c', 'commands/force_focus_wrapping.c', 'commands/fullscreen.c', 'commands/gaps.c', -- cgit v1.2.3-70-g09d2 From bc7d3321093339d34839718b35af034de4aeb9f1 Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Fri, 20 Jul 2018 22:17:20 -0400 Subject: Reset outputs on reload --- include/sway/config.h | 4 +++ sway/commands/output.c | 62 +++++--------------------------------- sway/config.c | 4 +++ sway/config/output.c | 80 ++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 95 insertions(+), 55 deletions(-) (limited to 'include/sway') diff --git a/include/sway/config.h b/include/sway/config.h index 87768399..b8da29c5 100644 --- a/include/sway/config.h +++ b/include/sway/config.h @@ -464,8 +464,12 @@ void apply_output_config(struct output_config *oc, struct output_config *store_output_config(struct output_config *oc); +void apply_output_config_to_outputs(struct output_config *oc); + void free_output_config(struct output_config *oc); +void create_default_output_configs(void); + int workspace_output_cmp_workspace(const void *a, const void *b); int sway_binding_cmp(const void *a, const void *b); diff --git a/sway/commands/output.c b/sway/commands/output.c index 4d98162b..ef1b7a69 100644 --- a/sway/commands/output.c +++ b/sway/commands/output.c @@ -21,60 +21,6 @@ static struct cmd_handler output_handlers[] = { { "transform", output_cmd_transform }, }; -static struct output_config *get_output_config(char *name, char *identifier) { - int i = list_seq_find(config->output_configs, output_name_cmp, name); - if (i >= 0) { - return config->output_configs->items[i]; - } - - i = list_seq_find(config->output_configs, output_name_cmp, identifier); - if (i >= 0) { - return config->output_configs->items[i]; - } - - return NULL; -} - -static 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; - char id[128]; - struct sway_output *sway_output; - wl_list_for_each(sway_output, &root_container.sway_root->outputs, link) { - char *name = sway_output->wlr_output->name; - output_get_identifier(id, sizeof(id), sway_output); - if (wildcard || !strcmp(name, oc->name) || !strcmp(id, oc->name)) { - if (!sway_output->swayc) { - if (!oc->enabled) { - if (!wildcard) { - break; - } - continue; - } - - output_enable(sway_output); - } - - struct output_config *current = oc; - if (wildcard) { - struct output_config *tmp = get_output_config(name, id); - if (tmp) { - current = tmp; - } - } - apply_output_config(current, sway_output->swayc); - - if (!wildcard) { - // Stop looking if the output config isn't applicable to all - // outputs - break; - } - } - } -} - struct cmd_results *cmd_output(int argc, char **argv) { struct cmd_results *error = checkarg(argc, "output", EXPECTED_AT_LEAST, 1); if (error != NULL) { @@ -115,7 +61,13 @@ struct cmd_results *cmd_output(int argc, char **argv) { config->handler_context.leftovers.argv = NULL; output = store_output_config(output); - apply_output_config_to_outputs(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) { + apply_output_config_to_outputs(output); + } return cmd_results_new(CMD_SUCCESS, NULL, NULL); diff --git a/sway/config.c b/sway/config.c index 4b892852..ed624bfa 100644 --- a/sway/config.c +++ b/sway/config.c @@ -361,6 +361,7 @@ bool load_main_config(const char *file, bool is_active) { wlr_log(WLR_DEBUG, "Performing configuration file reload"); config->reloading = true; config->active = true; + create_default_output_configs(); } config->current_config_path = path; @@ -419,6 +420,9 @@ bool load_main_config(const char *file, bool is_active) { success = success && load_config(path, config); if (is_active) { + for (int i = 0; i < config->output_configs->length; i++) { + apply_output_config_to_outputs(config->output_configs->items[i]); + } config->reloading = false; } diff --git a/sway/config/output.c b/sway/config/output.c index 505fa745..504c48c6 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -263,6 +263,60 @@ void apply_output_config(struct output_config *oc, struct sway_container *output } } +static struct output_config *get_output_config(char *name, char *identifier) { + int i = list_seq_find(config->output_configs, output_name_cmp, name); + if (i >= 0) { + return config->output_configs->items[i]; + } + + i = list_seq_find(config->output_configs, output_name_cmp, identifier); + if (i >= 0) { + return config->output_configs->items[i]; + } + + return NULL; +} + +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; + char id[128]; + struct sway_output *sway_output; + wl_list_for_each(sway_output, &root_container.sway_root->outputs, link) { + char *name = sway_output->wlr_output->name; + output_get_identifier(id, sizeof(id), sway_output); + if (wildcard || !strcmp(name, oc->name) || !strcmp(id, oc->name)) { + if (!sway_output->swayc) { + if (!oc->enabled) { + if (!wildcard) { + break; + } + continue; + } + + output_enable(sway_output); + } + + struct output_config *current = oc; + if (wildcard) { + struct output_config *tmp = get_output_config(name, id); + if (tmp) { + current = tmp; + } + } + apply_output_config(current, sway_output->swayc); + + if (!wildcard) { + // Stop looking if the output config isn't applicable to all + // outputs + break; + } + } + } +} + void free_output_config(struct output_config *oc) { if (!oc) { return; @@ -272,3 +326,29 @@ void free_output_config(struct output_config *oc) { free(oc->background_option); free(oc); } + +static void default_output_config(struct output_config *oc, + struct wlr_output *wlr_output) { + oc->enabled = 1; + if (!wl_list_empty(&wlr_output->modes)) { + struct wlr_output_mode *mode = + wl_container_of(wlr_output->modes.prev, mode, link); + oc->width = mode->width; + oc->height = mode->height; + oc->refresh_rate = mode->refresh; + } + oc->x = oc->y = -1; + oc->scale = 1; + oc->transform = WL_OUTPUT_TRANSFORM_NORMAL; +} + +void create_default_output_configs(void) { + struct sway_output *sway_output; + wl_list_for_each(sway_output, &root_container.sway_root->outputs, link) { + char *name = sway_output->wlr_output->name; + struct output_config *oc = new_output_config(name); + default_output_config(oc, sway_output->wlr_output); + list_add(config->output_configs, oc); + } +} + -- cgit v1.2.3-70-g09d2 From 9fbe13b9be18c732b58033a57a22a299af91a170 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Wed, 18 Jul 2018 16:13:28 +1000 Subject: Implement floating_modifier and mouse operations for floating views This implements the following: * `floating_modifier` configuration directive * Drag a floating window by its title bar * Hold mod + drag a floating window from anywhere * Resize a floating view by dragging the border * Resize a floating view by holding mod and right clicking anywhere on the view * Resize a floating view and keep aspect ratio by holding shift while resizing using either method * Mouse cursor turns into resize when hovering floating border or corner --- include/sway/commands.h | 2 +- include/sway/input/seat.h | 16 ++ include/sway/tree/container.h | 12 ++ include/sway/tree/layout.h | 9 +- sway/commands.c | 1 + sway/commands/floating_modifier.c | 30 ++++ sway/input/cursor.c | 322 ++++++++++++++++++++++++++++++++++++-- sway/meson.build | 1 + sway/tree/container.c | 16 +- sway/tree/layout.c | 1 + sway/tree/view.c | 2 + 11 files changed, 395 insertions(+), 17 deletions(-) create mode 100644 sway/commands/floating_modifier.c (limited to 'include/sway') diff --git a/include/sway/commands.h b/include/sway/commands.h index e71a7228..f53d335a 100644 --- a/include/sway/commands.h +++ b/include/sway/commands.h @@ -106,7 +106,7 @@ sway_cmd cmd_exit; sway_cmd cmd_floating; sway_cmd cmd_floating_maximum_size; sway_cmd cmd_floating_minimum_size; -sway_cmd cmd_floating_mod; +sway_cmd cmd_floating_modifier; sway_cmd cmd_floating_scroll; sway_cmd cmd_focus; sway_cmd cmd_focus_follows_mouse; diff --git a/include/sway/input/seat.h b/include/sway/input/seat.h index eac1626b..be1f3610 100644 --- a/include/sway/input/seat.h +++ b/include/sway/input/seat.h @@ -34,6 +34,8 @@ struct sway_drag_icon { struct wl_listener destroy; }; +enum resize_edge; + struct sway_seat { struct wlr_seat *wlr_seat; struct sway_cursor *cursor; @@ -52,6 +54,20 @@ struct sway_seat { int32_t touch_id; double touch_x, touch_y; + // Operations (drag and resize) + enum { + OP_NONE, + OP_DRAG, + OP_RESIZE, + } operation; + struct sway_container *op_container; + enum resize_edge op_resize_edge; + uint32_t op_button; + bool op_resize_preserve_ratio; + double op_ref_lx, op_ref_ly; // cursor's x/y at start of op + double op_ref_width, op_ref_height; // container's size at start of op + double op_ref_con_lx, op_ref_con_ly; // container's x/y at start of op + struct wl_listener focus_destroy; struct wl_listener new_container; struct wl_listener new_drag_icon; diff --git a/include/sway/tree/container.h b/include/sway/tree/container.h index ca7a3288..59c5b4c7 100644 --- a/include/sway/tree/container.h +++ b/include/sway/tree/container.h @@ -304,6 +304,12 @@ bool container_is_floating(struct sway_container *container); */ void container_get_box(struct sway_container *container, struct wlr_box *box); +/** + * Move a floating container by the specified amount. + */ +void container_floating_translate(struct sway_container *con, + double x_amount, double y_amount); + /** * Move a floating container to a new layout-local position. */ @@ -318,4 +324,10 @@ void container_set_dirty(struct sway_container *container); bool container_has_urgent_child(struct sway_container *container); +/** + * If the container is involved in a drag or resize operation via a mouse, this + * ends the operation. + */ +void container_end_mouse_operation(struct sway_container *container); + #endif diff --git a/include/sway/tree/layout.h b/include/sway/tree/layout.h index ba265623..5a78fd58 100644 --- a/include/sway/tree/layout.h +++ b/include/sway/tree/layout.h @@ -14,10 +14,11 @@ enum movement_direction { }; enum resize_edge { - RESIZE_EDGE_LEFT, - RESIZE_EDGE_RIGHT, - RESIZE_EDGE_TOP, - RESIZE_EDGE_BOTTOM, + RESIZE_EDGE_NONE = 0, + RESIZE_EDGE_LEFT = 1, + RESIZE_EDGE_RIGHT = 2, + RESIZE_EDGE_TOP = 4, + RESIZE_EDGE_BOTTOM = 8, }; struct sway_container; diff --git a/sway/commands.c b/sway/commands.c index f1f03574..f40f0e9d 100644 --- a/sway/commands.c +++ b/sway/commands.c @@ -103,6 +103,7 @@ static struct cmd_handler handlers[] = { { "exec_always", cmd_exec_always }, { "floating_maximum_size", cmd_floating_maximum_size }, { "floating_minimum_size", cmd_floating_minimum_size }, + { "floating_modifier", cmd_floating_modifier }, { "focus", cmd_focus }, { "focus_follows_mouse", cmd_focus_follows_mouse }, { "focus_wrapping", cmd_focus_wrapping }, diff --git a/sway/commands/floating_modifier.c b/sway/commands/floating_modifier.c new file mode 100644 index 00000000..1ced50af --- /dev/null +++ b/sway/commands/floating_modifier.c @@ -0,0 +1,30 @@ +#ifdef __linux__ +#include +#elif __FreeBSD__ +#include +#endif +#include +#include +#include +#include "sway/commands.h" +#include "sway/config.h" +#include "list.h" +#include "log.h" +#include "util.h" + +struct cmd_results *cmd_floating_modifier(int argc, char **argv) { + struct cmd_results *error = NULL; + if ((error = checkarg(argc, "floating_modifier", EXPECTED_EQUAL_TO, 1))) { + return error; + } + + uint32_t mod = get_modifier_mask_by_name(argv[0]); + if (!mod) { + return cmd_results_new(CMD_INVALID, "floating_modifier", + "Invalid modifier"); + } + + config->floating_mod = mod; + + return cmd_results_new(CMD_SUCCESS, NULL, NULL); +} diff --git a/sway/input/cursor.c b/sway/input/cursor.c index c76c20b3..6ad214b5 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -5,15 +5,19 @@ #elif __FreeBSD__ #include #endif +#include #include #include #include #include "list.h" #include "log.h" +#include "sway/desktop.h" #include "sway/desktop/transaction.h" #include "sway/input/cursor.h" +#include "sway/input/keyboard.h" #include "sway/layers.h" #include "sway/output.h" +#include "sway/tree/arrange.h" #include "sway/tree/view.h" #include "sway/tree/workspace.h" #include "wlr-layer-shell-unstable-v1-protocol.h" @@ -127,7 +131,7 @@ static struct sway_container *container_at_coords( return ws; } - c = seat_get_focus_inactive(seat, output->swayc); + c = seat_get_active_child(seat, output->swayc); if (c) { return c; } @@ -139,6 +143,173 @@ static struct sway_container *container_at_coords( return output->swayc; } +static enum resize_edge find_resize_edge(struct sway_container *cont, + struct sway_cursor *cursor) { + if (cont->type != C_VIEW) { + return RESIZE_EDGE_NONE; + } + struct sway_view *view = cont->sway_view; + if (view->border == B_NONE || !view->border_thickness || view->using_csd) { + return RESIZE_EDGE_NONE; + } + + enum resize_edge edge = 0; + if (cursor->cursor->x < cont->x + view->border_thickness) { + edge |= RESIZE_EDGE_LEFT; + } + if (cursor->cursor->y < cont->y + view->border_thickness) { + edge |= RESIZE_EDGE_TOP; + } + if (cursor->cursor->x >= cont->x + cont->width - view->border_thickness) { + edge |= RESIZE_EDGE_RIGHT; + } + if (cursor->cursor->y >= cont->y + cont->height - view->border_thickness) { + edge |= RESIZE_EDGE_BOTTOM; + } + return edge; +} + +static void handle_drag_motion(struct sway_seat *seat, + struct sway_cursor *cursor) { + struct sway_container *con = seat->op_container; + desktop_damage_whole_container(con); + container_floating_translate(con, + cursor->cursor->x - cursor->previous.x, + cursor->cursor->y - cursor->previous.y); + desktop_damage_whole_container(con); +} + +static void calculate_floating_constraints(struct sway_container *con, + int *min_width, int *max_width, int *min_height, int *max_height) { + if (config->floating_minimum_width == -1) { // no minimum + *min_width = 0; + } else if (config->floating_minimum_width == 0) { // automatic + *min_width = 75; + } else { + *min_width = config->floating_minimum_width; + } + + if (config->floating_minimum_height == -1) { // no minimum + *min_height = 0; + } else if (config->floating_minimum_height == 0) { // automatic + *min_height = 50; + } else { + *min_height = config->floating_minimum_height; + } + + if (config->floating_maximum_width == -1) { // no maximum + *max_width = INT_MAX; + } else if (config->floating_maximum_width == 0) { // automatic + struct sway_container *ws = container_parent(con, C_WORKSPACE); + *max_width = ws->width; + } else { + *max_width = config->floating_maximum_width; + } + + if (config->floating_maximum_height == -1) { // no maximum + *max_height = INT_MAX; + } else if (config->floating_maximum_height == 0) { // automatic + struct sway_container *ws = container_parent(con, C_WORKSPACE); + *max_height = ws->height; + } else { + *max_height = config->floating_maximum_height; + } +} +static void handle_resize_motion(struct sway_seat *seat, + struct sway_cursor *cursor) { + struct sway_container *con = seat->op_container; + double center_lx = con->x + con->width / 2; + double center_ly = con->y + con->height / 2; + enum resize_edge edge = seat->op_resize_edge; + + // The amount the mouse has moved since the start of the resize operation + // Positive is down/right + double mouse_move_x = cursor->cursor->x - seat->op_ref_lx; + double mouse_move_y = cursor->cursor->y - seat->op_ref_ly; + + if (edge == RESIZE_EDGE_TOP || edge == RESIZE_EDGE_BOTTOM) { + mouse_move_x = 0; + } + if (edge == RESIZE_EDGE_LEFT || edge == RESIZE_EDGE_RIGHT) { + mouse_move_y = 0; + } + + double grow_width = seat->op_ref_lx > center_lx ? + mouse_move_x : -mouse_move_x; + double grow_height = seat->op_ref_ly > center_ly ? + mouse_move_y : -mouse_move_y; + + if (seat->op_resize_preserve_ratio) { + double x_multiplier = grow_width / seat->op_ref_width; + double y_multiplier = grow_height / seat->op_ref_height; + double avg_multiplier = (x_multiplier + y_multiplier) / 2; + grow_width = seat->op_ref_width * avg_multiplier; + grow_height = seat->op_ref_height * avg_multiplier; + } + + // If we're resizing from the center (mod + right click), we need to double + // the amount we're growing because we're doing it in both directions. + if (edge == RESIZE_EDGE_NONE) { + grow_width *= 2; + grow_height *= 2; + } + + // Determine new width/height, and accommodate for min/max values + double width = seat->op_ref_width + grow_width; + double height = seat->op_ref_height + grow_height; + int min_width, max_width, min_height, max_height; + calculate_floating_constraints(con, &min_width, &max_width, + &min_height, &max_height); + width = fmax(min_width, fmin(width, max_width)); + height = fmax(min_height, fmin(height, max_height)); + + // Recalculate these, in case we hit a min/max limit + grow_width = width - seat->op_ref_width; + grow_height = height - seat->op_ref_height; + + // Determine grow x/y values - these are relative to the container's x/y at + // the start of the resize operation. + double grow_x = 0, grow_y = 0; + if (edge & RESIZE_EDGE_LEFT) { + grow_x = -grow_width; + } else if (edge & RESIZE_EDGE_RIGHT) { + grow_x = 0; + } else { + grow_x = -grow_width / 2; + } + if (edge & RESIZE_EDGE_TOP) { + grow_y = -grow_height; + } else if (edge & RESIZE_EDGE_BOTTOM) { + grow_y = 0; + } else { + grow_y = -grow_height / 2; + } + + // Determine the amounts we need to bump everything relative to the current + // size. + int relative_grow_width = width - con->width; + int relative_grow_height = height - con->height; + int relative_grow_x = (seat->op_ref_con_lx + grow_x) - con->x; + int relative_grow_y = (seat->op_ref_con_ly + grow_y) - con->y; + + // Actually resize stuff + con->x += relative_grow_x; + con->y += relative_grow_y; + con->width += relative_grow_width; + con->height += relative_grow_height; + + if (con->type == C_VIEW) { + struct sway_view *view = con->sway_view; + view->x += relative_grow_x; + view->y += relative_grow_y; + view->width += relative_grow_width; + view->height += relative_grow_height; + } + + arrange_windows(con); + transaction_commit_dirty(); +} + void cursor_send_pointer_motion(struct sway_cursor *cursor, uint32_t time_msec, bool allow_refocusing) { if (time_msec == 0) { @@ -146,6 +317,18 @@ void cursor_send_pointer_motion(struct sway_cursor *cursor, uint32_t time_msec, } struct sway_seat *seat = cursor->seat; + + if (seat->operation != OP_NONE) { + if (seat->operation == OP_DRAG) { + handle_drag_motion(seat, cursor); + } else { + handle_resize_motion(seat, cursor); + } + cursor->previous.x = cursor->cursor->x; + cursor->previous.y = cursor->cursor->y; + return; + } + struct wlr_seat *wlr_seat = seat->wlr_seat; struct wlr_surface *surface = NULL; double sx, sy; @@ -194,15 +377,54 @@ void cursor_send_pointer_motion(struct sway_cursor *cursor, uint32_t time_msec, } } - // reset cursor if switching between clients - struct wl_client *client = NULL; - if (surface != NULL) { - client = wl_resource_get_client(surface->resource); - } - if (client != cursor->image_client) { + // Handle cursor image + if (surface) { + // Reset cursor if switching between clients + struct wl_client *client = wl_resource_get_client(surface->resource); + if (client != cursor->image_client) { + wlr_xcursor_manager_set_cursor_image(cursor->xcursor_manager, + "left_ptr", cursor->cursor); + cursor->image_client = client; + } + } else if (c && container_is_floating(c)) { + // Try a floating container's resize edge + enum resize_edge edge = find_resize_edge(c, cursor); + if (edge == RESIZE_EDGE_NONE) { + wlr_xcursor_manager_set_cursor_image(cursor->xcursor_manager, + "left_ptr", cursor->cursor); + } else if (edge == RESIZE_EDGE_TOP) { + wlr_xcursor_manager_set_cursor_image(cursor->xcursor_manager, + "top_side", cursor->cursor); + } else if (edge == RESIZE_EDGE_RIGHT) { + wlr_xcursor_manager_set_cursor_image(cursor->xcursor_manager, + "right_side", cursor->cursor); + } else if (edge == RESIZE_EDGE_BOTTOM) { + wlr_xcursor_manager_set_cursor_image(cursor->xcursor_manager, + "bottom_side", cursor->cursor); + } else if (edge == RESIZE_EDGE_LEFT) { + wlr_xcursor_manager_set_cursor_image(cursor->xcursor_manager, + "left_side", cursor->cursor); + } else if (edge == (RESIZE_EDGE_TOP | RESIZE_EDGE_LEFT)) { + wlr_xcursor_manager_set_cursor_image(cursor->xcursor_manager, + "top_left_corner", cursor->cursor); + } else if (edge == (RESIZE_EDGE_TOP | RESIZE_EDGE_RIGHT)) { + wlr_xcursor_manager_set_cursor_image(cursor->xcursor_manager, + "top_right_corner", cursor->cursor); + } else if (edge == (RESIZE_EDGE_BOTTOM | RESIZE_EDGE_LEFT)) { + wlr_xcursor_manager_set_cursor_image(cursor->xcursor_manager, + "bottom_left_corner", cursor->cursor); + } else if (edge == (RESIZE_EDGE_BOTTOM | RESIZE_EDGE_RIGHT)) { + wlr_xcursor_manager_set_cursor_image(cursor->xcursor_manager, + "bottom_right_corner", cursor->cursor); + } else { + wlr_xcursor_manager_set_cursor_image(cursor->xcursor_manager, + "left_ptr", cursor->cursor); + } + cursor->image_client = NULL; + } else { wlr_xcursor_manager_set_cursor_image(cursor->xcursor_manager, - "left_ptr", cursor->cursor); - cursor->image_client = client; + "left_ptr", cursor->cursor); + cursor->image_client = NULL; } // send pointer enter/leave @@ -243,8 +465,79 @@ static void handle_cursor_motion_absolute( transaction_commit_dirty(); } +static void handle_end_operation(struct sway_seat *seat) { + if (seat->operation == OP_DRAG) { + // We "move" the container to its own location so it discovers its + // output again. + struct sway_container *con = seat->op_container; + container_floating_move_to(con, con->x, con->y); + seat->operation = OP_NONE; + seat->op_container = NULL; + } else { + // OP_RESIZE + seat->operation = OP_NONE; + seat->op_container = NULL; + } +} + +static void dispatch_cursor_button_floating(struct sway_cursor *cursor, + uint32_t time_msec, uint32_t button, enum wlr_button_state state, + struct wlr_surface *surface, double sx, double sy, + struct sway_container *cont) { + struct sway_seat *seat = cursor->seat; + + // Deny dragging or resizing a fullscreen view + if (cont->type == C_VIEW && cont->sway_view->is_fullscreen) { + wlr_seat_pointer_notify_button(seat->wlr_seat, time_msec, button, state); + return; + } + + struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat->wlr_seat); + bool mod_pressed = keyboard && + (keyboard->modifiers.depressed & config->floating_mod); + enum resize_edge edge = find_resize_edge(cont, cursor); + bool over_title = edge == RESIZE_EDGE_NONE && !surface; + + // Check for beginning drag + if (button == BTN_LEFT && state == WLR_BUTTON_PRESSED && + (mod_pressed || over_title)) { + seat->operation = OP_DRAG; + seat->op_container = cont; + seat->op_button = button; + return; + } + + // Check for beginning resize + bool resizing_via_border = button == BTN_LEFT && edge; + bool resizing_via_mod = button == BTN_RIGHT && mod_pressed; + if ((resizing_via_border || resizing_via_mod) && + state == WLR_BUTTON_PRESSED) { + seat->operation = OP_RESIZE; + seat->op_container = cont; + seat->op_resize_preserve_ratio = keyboard && + (keyboard->modifiers.depressed & 1); // Shift + seat->op_resize_edge = edge; + seat->op_button = button; + seat->op_ref_lx = cursor->cursor->x; + seat->op_ref_ly = cursor->cursor->y; + seat->op_ref_con_lx = cont->x; + seat->op_ref_con_ly = cont->y; + seat->op_ref_width = cont->width; + seat->op_ref_height = cont->height; + return; + } + + // Send event to surface + wlr_seat_pointer_notify_button(seat->wlr_seat, time_msec, button, state); +} + void dispatch_cursor_button(struct sway_cursor *cursor, uint32_t time_msec, uint32_t button, enum wlr_button_state state) { + if (cursor->seat->operation != OP_NONE && + button == cursor->seat->op_button && state == WLR_BUTTON_RELEASED) { + handle_end_operation(cursor->seat); + return; + } if (time_msec == 0) { time_msec = get_current_time_msec(); } @@ -259,6 +552,11 @@ void dispatch_cursor_button(struct sway_cursor *cursor, if (layer->current.keyboard_interactive) { seat_set_focus_layer(cursor->seat, layer); } + wlr_seat_pointer_notify_button(cursor->seat->wlr_seat, + time_msec, button, state); + } else if (cont && container_is_floating(cont)) { + dispatch_cursor_button_floating(cursor, time_msec, button, state, + surface, sx, sy, cont); } else if (surface && cont && cont->type != C_VIEW) { // Avoid moving keyboard focus from a surface that accepts it to one // that does not unless the change would move us to a new workspace. @@ -275,12 +573,14 @@ void dispatch_cursor_button(struct sway_cursor *cursor, if (new_ws != old_ws) { seat_set_focus(cursor->seat, cont); } + wlr_seat_pointer_notify_button(cursor->seat->wlr_seat, + time_msec, button, state); } else if (cont) { seat_set_focus(cursor->seat, cont); + wlr_seat_pointer_notify_button(cursor->seat->wlr_seat, + time_msec, button, state); } - wlr_seat_pointer_notify_button(cursor->seat->wlr_seat, - time_msec, button, state); transaction_commit_dirty(); } diff --git a/sway/meson.build b/sway/meson.build index 09bc40b8..f4fdc8ea 100644 --- a/sway/meson.build +++ b/sway/meson.build @@ -42,6 +42,7 @@ sway_sources = files( 'commands/exec_always.c', 'commands/floating.c', 'commands/floating_minmax_size.c', + 'commands/floating_modifier.c', 'commands/focus.c', 'commands/focus_follows_mouse.c', 'commands/focus_wrapping.c', diff --git a/sway/tree/container.c b/sway/tree/container.c index 4dbfbb29..ba4af352 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -323,6 +323,8 @@ static struct sway_container *container_destroy_noreaping( } } + container_end_mouse_operation(con); + con->destroying = true; container_set_dirty(con); @@ -964,6 +966,8 @@ void container_set_floating(struct sway_container *container, bool enable) { container_reap_empty_recursive(workspace->sway_workspace->floating); } + container_end_mouse_operation(container); + ipc_event_window(container, "floating"); } @@ -1009,7 +1013,7 @@ void container_get_box(struct sway_container *container, struct wlr_box *box) { /** * Translate the container's position as well as all children. */ -static void container_floating_translate(struct sway_container *con, +void container_floating_translate(struct sway_container *con, double x_amount, double y_amount) { con->x += x_amount; con->y += y_amount; @@ -1105,3 +1109,13 @@ static bool find_urgent_iterator(struct sway_container *con, bool container_has_urgent_child(struct sway_container *container) { return container_find(container, find_urgent_iterator, NULL); } + +void container_end_mouse_operation(struct sway_container *container) { + struct sway_seat *seat; + wl_list_for_each(seat, &input_manager->seats, link) { + if (seat->op_container == container) { + seat->op_container = NULL; + seat->operation = OP_NONE; + } + } +} diff --git a/sway/tree/layout.c b/sway/tree/layout.c index 1f898f8a..533906fa 100644 --- a/sway/tree/layout.c +++ b/sway/tree/layout.c @@ -562,6 +562,7 @@ void container_move(struct sway_container *container, workspace_detect_urgent(last_ws); workspace_detect_urgent(next_ws); } + container_end_mouse_operation(container); } enum sway_container_layout container_get_default_layout( diff --git a/sway/tree/view.c b/sway/tree/view.c index 7881e6d7..24594950 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -387,6 +387,8 @@ void view_set_fullscreen(struct sway_view *view, bool fullscreen) { } } + container_end_mouse_operation(view->swayc); + ipc_event_window(view->swayc, "fullscreen_mode"); } -- cgit v1.2.3-70-g09d2 From ff445cc85597ee6bfae01f03d3c246e2326f3981 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Fri, 20 Jul 2018 09:28:22 +1000 Subject: Implement xdg shell request_move and request_resize events Also does a few other related things: * Now uses enum wlr_edges instead of our own enum resize_edge * Now uses wlr_xcursor_get_resize_name and removes our own find_resize_edge_name * Renames drag to move for consistency --- include/sway/input/seat.h | 13 ++++-- sway/desktop/xdg_shell.c | 33 +++++++++++++++ sway/desktop/xdg_shell_v6.c | 33 +++++++++++++++ sway/input/cursor.c | 100 ++++++++++++++------------------------------ sway/input/seat.c | 38 +++++++++++++++++ 5 files changed, 144 insertions(+), 73 deletions(-) (limited to 'include/sway') diff --git a/include/sway/input/seat.h b/include/sway/input/seat.h index be1f3610..35a965ee 100644 --- a/include/sway/input/seat.h +++ b/include/sway/input/seat.h @@ -3,6 +3,7 @@ #include #include +#include #include "sway/input/input-manager.h" struct sway_seat_device { @@ -34,8 +35,6 @@ struct sway_drag_icon { struct wl_listener destroy; }; -enum resize_edge; - struct sway_seat { struct wlr_seat *wlr_seat; struct sway_cursor *cursor; @@ -57,11 +56,12 @@ struct sway_seat { // Operations (drag and resize) enum { OP_NONE, - OP_DRAG, + OP_MOVE, OP_RESIZE, } operation; + struct sway_container *op_container; - enum resize_edge op_resize_edge; + enum wlr_edges op_resize_edge; uint32_t op_button; bool op_resize_preserve_ratio; double op_ref_lx, op_ref_ly; // cursor's x/y at start of op @@ -150,4 +150,9 @@ bool seat_is_input_allowed(struct sway_seat *seat, struct wlr_surface *surface); void drag_icon_update_position(struct sway_drag_icon *icon); +void seat_begin_move(struct sway_seat *seat, struct sway_container *con); + +void seat_begin_resize(struct sway_seat *seat, struct sway_container *con, + uint32_t button, enum wlr_edges edge); + #endif diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c index 98c16faf..d6c3a9a7 100644 --- a/sway/desktop/xdg_shell.c +++ b/sway/desktop/xdg_shell.c @@ -1,4 +1,9 @@ #define _POSIX_C_SOURCE 199309L +#ifdef __linux__ +#include +#elif __FreeBSD__ +#include +#endif #include #include #include @@ -248,6 +253,24 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data) transaction_commit_dirty(); } +static void handle_request_move(struct wl_listener *listener, void *data) { + struct sway_xdg_shell_view *xdg_shell_view = + wl_container_of(listener, xdg_shell_view, request_move); + struct sway_view *view = &xdg_shell_view->view; + struct wlr_xdg_toplevel_move_event *e = data; + struct sway_seat *seat = e->seat->seat->data; + seat_begin_move(seat, view->swayc); +} + +static void handle_request_resize(struct wl_listener *listener, void *data) { + struct sway_xdg_shell_view *xdg_shell_view = + wl_container_of(listener, xdg_shell_view, request_resize); + struct sway_view *view = &xdg_shell_view->view; + struct wlr_xdg_toplevel_resize_event *e = data; + struct sway_seat *seat = e->seat->seat->data; + seat_begin_resize(seat, view->swayc, BTN_LEFT, e->edges); +} + static void handle_unmap(struct wl_listener *listener, void *data) { struct sway_xdg_shell_view *xdg_shell_view = wl_container_of(listener, xdg_shell_view, unmap); @@ -262,6 +285,8 @@ static void handle_unmap(struct wl_listener *listener, void *data) { wl_list_remove(&xdg_shell_view->commit.link); wl_list_remove(&xdg_shell_view->new_popup.link); wl_list_remove(&xdg_shell_view->request_fullscreen.link); + wl_list_remove(&xdg_shell_view->request_move.link); + wl_list_remove(&xdg_shell_view->request_resize.link); } static void handle_map(struct wl_listener *listener, void *data) { @@ -299,6 +324,14 @@ static void handle_map(struct wl_listener *listener, void *data) { xdg_shell_view->request_fullscreen.notify = handle_request_fullscreen; wl_signal_add(&xdg_surface->toplevel->events.request_fullscreen, &xdg_shell_view->request_fullscreen); + + xdg_shell_view->request_move.notify = handle_request_move; + wl_signal_add(&xdg_surface->toplevel->events.request_move, + &xdg_shell_view->request_move); + + xdg_shell_view->request_resize.notify = handle_request_resize; + wl_signal_add(&xdg_surface->toplevel->events.request_resize, + &xdg_shell_view->request_resize); } static void handle_destroy(struct wl_listener *listener, void *data) { diff --git a/sway/desktop/xdg_shell_v6.c b/sway/desktop/xdg_shell_v6.c index 4d76f0a7..241bd9b0 100644 --- a/sway/desktop/xdg_shell_v6.c +++ b/sway/desktop/xdg_shell_v6.c @@ -1,4 +1,9 @@ #define _POSIX_C_SOURCE 199309L +#ifdef __linux__ +#include +#elif __FreeBSD__ +#include +#endif #include #include #include @@ -243,6 +248,24 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data) transaction_commit_dirty(); } +static void handle_request_move(struct wl_listener *listener, void *data) { + struct sway_xdg_shell_v6_view *xdg_shell_v6_view = + wl_container_of(listener, xdg_shell_v6_view, request_move); + struct sway_view *view = &xdg_shell_v6_view->view; + struct wlr_xdg_toplevel_v6_move_event *e = data; + struct sway_seat *seat = e->seat->seat->data; + seat_begin_move(seat, view->swayc); +} + +static void handle_request_resize(struct wl_listener *listener, void *data) { + struct sway_xdg_shell_v6_view *xdg_shell_v6_view = + wl_container_of(listener, xdg_shell_v6_view, request_resize); + struct sway_view *view = &xdg_shell_v6_view->view; + struct wlr_xdg_toplevel_v6_resize_event *e = data; + struct sway_seat *seat = e->seat->seat->data; + seat_begin_resize(seat, view->swayc, BTN_LEFT, e->edges); +} + static void handle_unmap(struct wl_listener *listener, void *data) { struct sway_xdg_shell_v6_view *xdg_shell_v6_view = wl_container_of(listener, xdg_shell_v6_view, unmap); @@ -257,6 +280,8 @@ static void handle_unmap(struct wl_listener *listener, void *data) { wl_list_remove(&xdg_shell_v6_view->commit.link); wl_list_remove(&xdg_shell_v6_view->new_popup.link); wl_list_remove(&xdg_shell_v6_view->request_fullscreen.link); + wl_list_remove(&xdg_shell_v6_view->request_move.link); + wl_list_remove(&xdg_shell_v6_view->request_resize.link); } static void handle_map(struct wl_listener *listener, void *data) { @@ -294,6 +319,14 @@ static void handle_map(struct wl_listener *listener, void *data) { xdg_shell_v6_view->request_fullscreen.notify = handle_request_fullscreen; wl_signal_add(&xdg_surface->toplevel->events.request_fullscreen, &xdg_shell_v6_view->request_fullscreen); + + xdg_shell_v6_view->request_move.notify = handle_request_move; + wl_signal_add(&xdg_surface->toplevel->events.request_move, + &xdg_shell_v6_view->request_move); + + xdg_shell_v6_view->request_resize.notify = handle_request_resize; + wl_signal_add(&xdg_surface->toplevel->events.request_resize, + &xdg_shell_v6_view->request_resize); } static void handle_destroy(struct wl_listener *listener, void *data) { diff --git a/sway/input/cursor.c b/sway/input/cursor.c index 8723e2b4..8b9208c6 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -143,33 +143,33 @@ static struct sway_container *container_at_coords( return output->swayc; } -static enum resize_edge find_resize_edge(struct sway_container *cont, +static enum wlr_edges find_resize_edge(struct sway_container *cont, struct sway_cursor *cursor) { if (cont->type != C_VIEW) { - return RESIZE_EDGE_NONE; + return WLR_EDGE_NONE; } struct sway_view *view = cont->sway_view; if (view->border == B_NONE || !view->border_thickness || view->using_csd) { - return RESIZE_EDGE_NONE; + return WLR_EDGE_NONE; } - enum resize_edge edge = 0; + enum wlr_edges edge = 0; if (cursor->cursor->x < cont->x + view->border_thickness) { - edge |= RESIZE_EDGE_LEFT; + edge |= WLR_EDGE_LEFT; } if (cursor->cursor->y < cont->y + view->border_thickness) { - edge |= RESIZE_EDGE_TOP; + edge |= WLR_EDGE_TOP; } if (cursor->cursor->x >= cont->x + cont->width - view->border_thickness) { - edge |= RESIZE_EDGE_RIGHT; + edge |= WLR_EDGE_RIGHT; } if (cursor->cursor->y >= cont->y + cont->height - view->border_thickness) { - edge |= RESIZE_EDGE_BOTTOM; + edge |= WLR_EDGE_BOTTOM; } return edge; } -static void handle_drag_motion(struct sway_seat *seat, +static void handle_move_motion(struct sway_seat *seat, struct sway_cursor *cursor) { struct sway_container *con = seat->op_container; desktop_damage_whole_container(con); @@ -218,22 +218,22 @@ static void calculate_floating_constraints(struct sway_container *con, static void handle_resize_motion(struct sway_seat *seat, struct sway_cursor *cursor) { struct sway_container *con = seat->op_container; - enum resize_edge edge = seat->op_resize_edge; + enum wlr_edges edge = seat->op_resize_edge; // The amount the mouse has moved since the start of the resize operation // Positive is down/right double mouse_move_x = cursor->cursor->x - seat->op_ref_lx; double mouse_move_y = cursor->cursor->y - seat->op_ref_ly; - if (edge == RESIZE_EDGE_TOP || edge == RESIZE_EDGE_BOTTOM) { + if (edge == WLR_EDGE_TOP || edge == WLR_EDGE_BOTTOM) { mouse_move_x = 0; } - if (edge == RESIZE_EDGE_LEFT || edge == RESIZE_EDGE_RIGHT) { + if (edge == WLR_EDGE_LEFT || edge == WLR_EDGE_RIGHT) { mouse_move_y = 0; } - double grow_width = edge & RESIZE_EDGE_LEFT ? -mouse_move_x : mouse_move_x; - double grow_height = edge & RESIZE_EDGE_TOP ? -mouse_move_y : mouse_move_y; + double grow_width = edge & WLR_EDGE_LEFT ? -mouse_move_x : mouse_move_x; + double grow_height = edge & WLR_EDGE_TOP ? -mouse_move_y : mouse_move_y; if (seat->op_resize_preserve_ratio) { double x_multiplier = grow_width / seat->op_ref_width; @@ -259,16 +259,16 @@ static void handle_resize_motion(struct sway_seat *seat, // Determine grow x/y values - these are relative to the container's x/y at // the start of the resize operation. double grow_x = 0, grow_y = 0; - if (edge & RESIZE_EDGE_LEFT) { + if (edge & WLR_EDGE_LEFT) { grow_x = -grow_width; - } else if (edge & RESIZE_EDGE_RIGHT) { + } else if (edge & WLR_EDGE_RIGHT) { grow_x = 0; } else { grow_x = -grow_width / 2; } - if (edge & RESIZE_EDGE_TOP) { + if (edge & WLR_EDGE_TOP) { grow_y = -grow_height; - } else if (edge & RESIZE_EDGE_BOTTOM) { + } else if (edge & WLR_EDGE_BOTTOM) { grow_y = 0; } else { grow_y = -grow_height / 2; @@ -299,31 +299,6 @@ static void handle_resize_motion(struct sway_seat *seat, transaction_commit_dirty(); } -static const char *edge_to_image_name(enum resize_edge edge) { - switch (edge) { - case RESIZE_EDGE_NONE: - return "left_ptr"; - case RESIZE_EDGE_TOP: - return "top_side"; - case RESIZE_EDGE_RIGHT: - return "right_side"; - case RESIZE_EDGE_BOTTOM: - return "bottom_side"; - case RESIZE_EDGE_LEFT: - return "left_side"; - } - if (edge == (RESIZE_EDGE_TOP | RESIZE_EDGE_LEFT)) { - return "top_left_corner"; - } else if (edge == (RESIZE_EDGE_TOP | RESIZE_EDGE_RIGHT)) { - return "top_right_corner"; - } else if (edge == (RESIZE_EDGE_BOTTOM | RESIZE_EDGE_LEFT)) { - return "bottom_left_corner"; - } else if (edge == (RESIZE_EDGE_BOTTOM | RESIZE_EDGE_RIGHT)) { - return "bottom_right_corner"; - } - return "left_ptr"; -} - void cursor_send_pointer_motion(struct sway_cursor *cursor, uint32_t time_msec, bool allow_refocusing) { if (time_msec == 0) { @@ -333,8 +308,8 @@ void cursor_send_pointer_motion(struct sway_cursor *cursor, uint32_t time_msec, struct sway_seat *seat = cursor->seat; if (seat->operation != OP_NONE) { - if (seat->operation == OP_DRAG) { - handle_drag_motion(seat, cursor); + if (seat->operation == OP_MOVE) { + handle_move_motion(seat, cursor); } else { handle_resize_motion(seat, cursor); } @@ -402,8 +377,9 @@ void cursor_send_pointer_motion(struct sway_cursor *cursor, uint32_t time_msec, } } else if (c && container_is_floating(c)) { // Try a floating container's resize edge - enum resize_edge edge = find_resize_edge(c, cursor); - const char *image = edge_to_image_name(edge); + enum wlr_edges edge = find_resize_edge(c, cursor); + const char *image = edge == WLR_EDGE_NONE ? + "left_ptr" : wlr_xcursor_get_resize_name(edge); wlr_xcursor_manager_set_cursor_image(cursor->xcursor_manager, image, cursor->cursor); cursor->image_client = NULL; @@ -452,7 +428,7 @@ static void handle_cursor_motion_absolute( } static void handle_end_operation(struct sway_seat *seat) { - if (seat->operation == OP_DRAG) { + if (seat->operation == OP_MOVE) { // We "move" the container to its own location so it discovers its // output again. struct sway_container *con = seat->op_container; @@ -472,7 +448,7 @@ static void dispatch_cursor_button_floating(struct sway_cursor *cursor, struct sway_container *cont) { struct sway_seat *seat = cursor->seat; - // Deny dragging or resizing a fullscreen view + // Deny moving or resizing a fullscreen view if (cont->type == C_VIEW && cont->sway_view->is_fullscreen) { wlr_seat_pointer_notify_button(seat->wlr_seat, time_msec, button, state); return; @@ -481,36 +457,22 @@ static void dispatch_cursor_button_floating(struct sway_cursor *cursor, struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat->wlr_seat); bool mod_pressed = keyboard && (wlr_keyboard_get_modifiers(keyboard) & config->floating_mod); - enum resize_edge edge = find_resize_edge(cont, cursor); - bool over_title = edge == RESIZE_EDGE_NONE && !surface; + enum wlr_edges edge = find_resize_edge(cont, cursor); + bool over_title = edge == WLR_EDGE_NONE && !surface; - // Check for beginning drag + // Check for beginning move if (button == BTN_LEFT && state == WLR_BUTTON_PRESSED && (mod_pressed || over_title)) { - seat->operation = OP_DRAG; - seat->op_container = cont; - seat->op_button = button; + seat_begin_move(seat, cont); return; } // Check for beginning resize - bool resizing_via_border = button == BTN_LEFT && edge != RESIZE_EDGE_NONE; + bool resizing_via_border = button == BTN_LEFT && edge != WLR_EDGE_NONE; bool resizing_via_mod = button == BTN_RIGHT && mod_pressed; if ((resizing_via_border || resizing_via_mod) && state == WLR_BUTTON_PRESSED) { - seat->operation = OP_RESIZE; - seat->op_container = cont; - seat->op_resize_preserve_ratio = keyboard && - (wlr_keyboard_get_modifiers(keyboard) & WLR_MODIFIER_SHIFT); - seat->op_resize_edge = resizing_via_mod ? - RESIZE_EDGE_BOTTOM | RESIZE_EDGE_RIGHT : edge; - seat->op_button = button; - seat->op_ref_lx = cursor->cursor->x; - seat->op_ref_ly = cursor->cursor->y; - seat->op_ref_con_lx = cont->x; - seat->op_ref_con_ly = cont->y; - seat->op_ref_width = cont->width; - seat->op_ref_height = cont->height; + seat_begin_resize(seat, cont, button, edge); return; } diff --git a/sway/input/seat.c b/sway/input/seat.c index e77d88a8..cc5b2e0f 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -1,6 +1,11 @@ #define _XOPEN_SOURCE 700 #define _POSIX_C_SOURCE 199309L #include +#ifdef __linux__ +#include +#elif __FreeBSD__ +#include +#endif #include #include #include @@ -348,6 +353,7 @@ struct sway_seat *seat_create(struct sway_input_manager *input, free(seat); return NULL; } + seat->wlr_seat->data = seat; seat->cursor = sway_cursor_create(seat); if (!seat->cursor) { @@ -894,3 +900,35 @@ struct seat_config *seat_get_config(struct sway_seat *seat) { return NULL; } + +void seat_begin_move(struct sway_seat *seat, struct sway_container *con) { + if (!seat->cursor) { + wlr_log(WLR_DEBUG, "Ignoring move request due to no cursor device"); + return; + } + seat->operation = OP_MOVE; + seat->op_container = con; + seat->op_button = BTN_LEFT; +} + +void seat_begin_resize(struct sway_seat *seat, struct sway_container *con, + uint32_t button, enum wlr_edges edge) { + if (!seat->cursor) { + wlr_log(WLR_DEBUG, "Ignoring resize request due to no cursor device"); + return; + } + struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat->wlr_seat); + seat->operation = OP_RESIZE; + seat->op_container = con; + seat->op_resize_preserve_ratio = keyboard && + (wlr_keyboard_get_modifiers(keyboard) & WLR_MODIFIER_SHIFT); + seat->op_resize_edge = edge == WLR_EDGE_NONE ? + RESIZE_EDGE_BOTTOM | RESIZE_EDGE_RIGHT : edge; + seat->op_button = button; + seat->op_ref_lx = seat->cursor->cursor->x; + seat->op_ref_ly = seat->cursor->cursor->y; + seat->op_ref_con_lx = con->x; + seat->op_ref_con_ly = con->y; + seat->op_ref_width = con->width; + seat->op_ref_height = con->height; +} -- cgit v1.2.3-70-g09d2 From 0c87bff5d1ce686afdc25d37eee4bdd8f08abdd3 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Sat, 21 Jul 2018 10:40:12 +1000 Subject: Replace static handle_end_operation with seat_end_mouse_operation --- include/sway/input/seat.h | 2 ++ sway/input/cursor.c | 17 +---------------- sway/input/seat.c | 19 +++++++++++++++++++ sway/tree/container.c | 3 +-- 4 files changed, 23 insertions(+), 18 deletions(-) (limited to 'include/sway') diff --git a/include/sway/input/seat.h b/include/sway/input/seat.h index 35a965ee..cd36ef5a 100644 --- a/include/sway/input/seat.h +++ b/include/sway/input/seat.h @@ -155,4 +155,6 @@ void seat_begin_move(struct sway_seat *seat, struct sway_container *con); void seat_begin_resize(struct sway_seat *seat, struct sway_container *con, uint32_t button, enum wlr_edges edge); +void seat_end_mouse_operation(struct sway_seat *seat); + #endif diff --git a/sway/input/cursor.c b/sway/input/cursor.c index 8b9208c6..ec83746e 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -427,21 +427,6 @@ static void handle_cursor_motion_absolute( transaction_commit_dirty(); } -static void handle_end_operation(struct sway_seat *seat) { - if (seat->operation == OP_MOVE) { - // We "move" the container to its own location so it discovers its - // output again. - struct sway_container *con = seat->op_container; - container_floating_move_to(con, con->x, con->y); - seat->operation = OP_NONE; - seat->op_container = NULL; - } else { - // OP_RESIZE - seat->operation = OP_NONE; - seat->op_container = NULL; - } -} - static void dispatch_cursor_button_floating(struct sway_cursor *cursor, uint32_t time_msec, uint32_t button, enum wlr_button_state state, struct wlr_surface *surface, double sx, double sy, @@ -484,7 +469,7 @@ void dispatch_cursor_button(struct sway_cursor *cursor, uint32_t time_msec, uint32_t button, enum wlr_button_state state) { if (cursor->seat->operation != OP_NONE && button == cursor->seat->op_button && state == WLR_BUTTON_RELEASED) { - handle_end_operation(cursor->seat); + seat_end_mouse_operation(cursor->seat); return; } if (time_msec == 0) { diff --git a/sway/input/seat.c b/sway/input/seat.c index cc5b2e0f..3a3350e1 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -932,3 +932,22 @@ void seat_begin_resize(struct sway_seat *seat, struct sway_container *con, seat->op_ref_width = con->width; seat->op_ref_height = con->height; } + +void seat_end_mouse_operation(struct sway_seat *seat) { + switch (seat->operation) { + case OP_MOVE: + { + // We "move" the container to its own location so it discovers its + // output again. + struct sway_container *con = seat->op_container; + container_floating_move_to(con, con->x, con->y); + } + case OP_RESIZE: + // Don't need to do anything here. + break; + case OP_NONE: + break; + } + seat->operation = OP_NONE; + seat->op_container = NULL; +} diff --git a/sway/tree/container.c b/sway/tree/container.c index ba4af352..42c1d024 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -1114,8 +1114,7 @@ void container_end_mouse_operation(struct sway_container *container) { struct sway_seat *seat; wl_list_for_each(seat, &input_manager->seats, link) { if (seat->op_container == container) { - seat->op_container = NULL; - seat->operation = OP_NONE; + seat_end_mouse_operation(seat); } } } -- cgit v1.2.3-70-g09d2 From 6767d8a593723a9b69d018eed13d24a789be9516 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Sat, 21 Jul 2018 10:57:14 +1000 Subject: Prevent re-uploading the same cursor image multiple times --- include/sway/input/cursor.h | 4 ++++ sway/input/cursor.c | 22 +++++++++++++--------- 2 files changed, 17 insertions(+), 9 deletions(-) (limited to 'include/sway') diff --git a/include/sway/input/cursor.h b/include/sway/input/cursor.h index 5dd109ca..b0a3a7c5 100644 --- a/include/sway/input/cursor.h +++ b/include/sway/input/cursor.h @@ -11,6 +11,7 @@ struct sway_cursor { } previous; struct wlr_xcursor_manager *xcursor_manager; + const char *image; struct wl_client *image_client; struct wl_listener motion; @@ -37,4 +38,7 @@ void cursor_send_pointer_motion(struct sway_cursor *cursor, uint32_t time_msec, void dispatch_cursor_button(struct sway_cursor *cursor, uint32_t time_msec, uint32_t button, enum wlr_button_state state); +void cursor_set_image(struct sway_cursor *cursor, const char *image, + struct wl_client *client); + #endif diff --git a/sway/input/cursor.c b/sway/input/cursor.c index ec83746e..43721d28 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -371,22 +371,16 @@ void cursor_send_pointer_motion(struct sway_cursor *cursor, uint32_t time_msec, // Reset cursor if switching between clients struct wl_client *client = wl_resource_get_client(surface->resource); if (client != cursor->image_client) { - wlr_xcursor_manager_set_cursor_image(cursor->xcursor_manager, - "left_ptr", cursor->cursor); - cursor->image_client = client; + cursor_set_image(cursor, "left_ptr", client); } } else if (c && container_is_floating(c)) { // Try a floating container's resize edge enum wlr_edges edge = find_resize_edge(c, cursor); const char *image = edge == WLR_EDGE_NONE ? "left_ptr" : wlr_xcursor_get_resize_name(edge); - wlr_xcursor_manager_set_cursor_image(cursor->xcursor_manager, image, - cursor->cursor); - cursor->image_client = NULL; + cursor_set_image(cursor, image, NULL); } else { - wlr_xcursor_manager_set_cursor_image(cursor->xcursor_manager, - "left_ptr", cursor->cursor); - cursor->image_client = NULL; + cursor_set_image(cursor, "left_ptr", NULL); } // send pointer enter/leave @@ -725,6 +719,16 @@ static void handle_request_set_cursor(struct wl_listener *listener, cursor->image_client = focused_client; } +void cursor_set_image(struct sway_cursor *cursor, const char *image, + struct wl_client *client) { + if (!cursor->image || strcmp(cursor->image, image) != 0) { + wlr_xcursor_manager_set_cursor_image(cursor->xcursor_manager, image, + cursor->cursor); + cursor->image = image; + } + cursor->image_client = client; +} + void sway_cursor_destroy(struct sway_cursor *cursor) { if (!cursor) { return; -- cgit v1.2.3-70-g09d2 From 9df660ee3188386c907d8feb999636ce8d61d095 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Sat, 21 Jul 2018 11:23:48 +1000 Subject: Store last button and use it when views request to move or resize --- include/sway/input/seat.h | 9 ++++++++- sway/desktop/xdg_shell.c | 13 ++++++------- sway/desktop/xdg_shell_v6.c | 13 ++++++------- sway/input/cursor.c | 18 +++++++----------- sway/input/seat.c | 12 ++++++++++-- 5 files changed, 37 insertions(+), 28 deletions(-) (limited to 'include/sway') diff --git a/include/sway/input/seat.h b/include/sway/input/seat.h index cd36ef5a..ab25788f 100644 --- a/include/sway/input/seat.h +++ b/include/sway/input/seat.h @@ -68,6 +68,9 @@ struct sway_seat { double op_ref_width, op_ref_height; // container's size at start of op double op_ref_con_lx, op_ref_con_ly; // container's x/y at start of op + uint32_t last_button; + uint32_t last_button_serial; + struct wl_listener focus_destroy; struct wl_listener new_container; struct wl_listener new_drag_icon; @@ -150,11 +153,15 @@ bool seat_is_input_allowed(struct sway_seat *seat, struct wlr_surface *surface); void drag_icon_update_position(struct sway_drag_icon *icon); -void seat_begin_move(struct sway_seat *seat, struct sway_container *con); +void seat_begin_move(struct sway_seat *seat, struct sway_container *con, + uint32_t button); void seat_begin_resize(struct sway_seat *seat, struct sway_container *con, uint32_t button, enum wlr_edges edge); void seat_end_mouse_operation(struct sway_seat *seat); +void seat_pointer_notify_button(struct sway_seat *seat, uint32_t time_msec, + uint32_t button, enum wlr_button_state state); + #endif diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c index d6c3a9a7..c5d53d1d 100644 --- a/sway/desktop/xdg_shell.c +++ b/sway/desktop/xdg_shell.c @@ -1,9 +1,4 @@ #define _POSIX_C_SOURCE 199309L -#ifdef __linux__ -#include -#elif __FreeBSD__ -#include -#endif #include #include #include @@ -259,7 +254,9 @@ static void handle_request_move(struct wl_listener *listener, void *data) { struct sway_view *view = &xdg_shell_view->view; struct wlr_xdg_toplevel_move_event *e = data; struct sway_seat *seat = e->seat->seat->data; - seat_begin_move(seat, view->swayc); + if (e->serial == seat->last_button_serial) { + seat_begin_move(seat, view->swayc, seat->last_button); + } } static void handle_request_resize(struct wl_listener *listener, void *data) { @@ -268,7 +265,9 @@ static void handle_request_resize(struct wl_listener *listener, void *data) { struct sway_view *view = &xdg_shell_view->view; struct wlr_xdg_toplevel_resize_event *e = data; struct sway_seat *seat = e->seat->seat->data; - seat_begin_resize(seat, view->swayc, BTN_LEFT, e->edges); + if (e->serial == seat->last_button_serial) { + seat_begin_resize(seat, view->swayc, seat->last_button, e->edges); + } } static void handle_unmap(struct wl_listener *listener, void *data) { diff --git a/sway/desktop/xdg_shell_v6.c b/sway/desktop/xdg_shell_v6.c index 241bd9b0..4bd6af5e 100644 --- a/sway/desktop/xdg_shell_v6.c +++ b/sway/desktop/xdg_shell_v6.c @@ -1,9 +1,4 @@ #define _POSIX_C_SOURCE 199309L -#ifdef __linux__ -#include -#elif __FreeBSD__ -#include -#endif #include #include #include @@ -254,7 +249,9 @@ static void handle_request_move(struct wl_listener *listener, void *data) { struct sway_view *view = &xdg_shell_v6_view->view; struct wlr_xdg_toplevel_v6_move_event *e = data; struct sway_seat *seat = e->seat->seat->data; - seat_begin_move(seat, view->swayc); + if (e->serial == seat->last_button_serial) { + seat_begin_move(seat, view->swayc, seat->last_button); + } } static void handle_request_resize(struct wl_listener *listener, void *data) { @@ -263,7 +260,9 @@ static void handle_request_resize(struct wl_listener *listener, void *data) { struct sway_view *view = &xdg_shell_v6_view->view; struct wlr_xdg_toplevel_v6_resize_event *e = data; struct sway_seat *seat = e->seat->seat->data; - seat_begin_resize(seat, view->swayc, BTN_LEFT, e->edges); + if (e->serial == seat->last_button_serial) { + seat_begin_resize(seat, view->swayc, seat->last_button, e->edges); + } } static void handle_unmap(struct wl_listener *listener, void *data) { diff --git a/sway/input/cursor.c b/sway/input/cursor.c index 43721d28..ad0ceb94 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -429,7 +429,7 @@ static void dispatch_cursor_button_floating(struct sway_cursor *cursor, // Deny moving or resizing a fullscreen view if (cont->type == C_VIEW && cont->sway_view->is_fullscreen) { - wlr_seat_pointer_notify_button(seat->wlr_seat, time_msec, button, state); + seat_pointer_notify_button(seat, time_msec, button, state); return; } @@ -442,7 +442,7 @@ static void dispatch_cursor_button_floating(struct sway_cursor *cursor, // Check for beginning move if (button == BTN_LEFT && state == WLR_BUTTON_PRESSED && (mod_pressed || over_title)) { - seat_begin_move(seat, cont); + seat_begin_move(seat, cont, BTN_LEFT); return; } @@ -456,7 +456,7 @@ static void dispatch_cursor_button_floating(struct sway_cursor *cursor, } // Send event to surface - wlr_seat_pointer_notify_button(seat->wlr_seat, time_msec, button, state); + seat_pointer_notify_button(seat, time_msec, button, state); } void dispatch_cursor_button(struct sway_cursor *cursor, @@ -480,8 +480,7 @@ void dispatch_cursor_button(struct sway_cursor *cursor, if (layer->current.keyboard_interactive) { seat_set_focus_layer(cursor->seat, layer); } - wlr_seat_pointer_notify_button(cursor->seat->wlr_seat, - time_msec, button, state); + seat_pointer_notify_button(cursor->seat, time_msec, button, state); } else if (cont && container_is_floating(cont)) { dispatch_cursor_button_floating(cursor, time_msec, button, state, surface, sx, sy, cont); @@ -501,15 +500,12 @@ void dispatch_cursor_button(struct sway_cursor *cursor, if (new_ws != old_ws) { seat_set_focus(cursor->seat, cont); } - wlr_seat_pointer_notify_button(cursor->seat->wlr_seat, - time_msec, button, state); + seat_pointer_notify_button(cursor->seat, time_msec, button, state); } else if (cont) { seat_set_focus(cursor->seat, cont); - wlr_seat_pointer_notify_button(cursor->seat->wlr_seat, - time_msec, button, state); + seat_pointer_notify_button(cursor->seat, time_msec, button, state); } else { - wlr_seat_pointer_notify_button(cursor->seat->wlr_seat, - time_msec, button, state); + seat_pointer_notify_button(cursor->seat, time_msec, button, state); } transaction_commit_dirty(); diff --git a/sway/input/seat.c b/sway/input/seat.c index 3a3350e1..4e803efd 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -901,14 +901,15 @@ struct seat_config *seat_get_config(struct sway_seat *seat) { return NULL; } -void seat_begin_move(struct sway_seat *seat, struct sway_container *con) { +void seat_begin_move(struct sway_seat *seat, struct sway_container *con, + uint32_t button) { if (!seat->cursor) { wlr_log(WLR_DEBUG, "Ignoring move request due to no cursor device"); return; } seat->operation = OP_MOVE; seat->op_container = con; - seat->op_button = BTN_LEFT; + seat->op_button = button; } void seat_begin_resize(struct sway_seat *seat, struct sway_container *con, @@ -951,3 +952,10 @@ void seat_end_mouse_operation(struct sway_seat *seat) { seat->operation = OP_NONE; seat->op_container = NULL; } + +void seat_pointer_notify_button(struct sway_seat *seat, uint32_t time_msec, + uint32_t button, enum wlr_button_state state) { + seat->last_button = button; + seat->last_button_serial = wlr_seat_pointer_notify_button(seat->wlr_seat, + time_msec, button, state); +} -- cgit v1.2.3-70-g09d2 From 011d1ebfa4219eb666487529a5a5e7189c14fd40 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Sat, 21 Jul 2018 12:13:00 +1000 Subject: Consider view's min/max sizes when resizing --- include/sway/tree/view.h | 5 +++++ sway/desktop/xdg_shell.c | 12 ++++++++++++ sway/desktop/xdg_shell_v6.c | 12 ++++++++++++ sway/input/cursor.c | 11 ++++++++++- sway/tree/view.c | 13 +++++++++++++ 5 files changed, 52 insertions(+), 1 deletion(-) (limited to 'include/sway') diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h index 068d92c6..1dfb218b 100644 --- a/include/sway/tree/view.h +++ b/include/sway/tree/view.h @@ -26,6 +26,8 @@ enum sway_view_prop { }; struct sway_view_impl { + void (*get_constraints)(struct sway_view *view, double *min_width, + double *max_width, double *min_height, double *max_height); const char *(*get_string_prop)(struct sway_view *view, enum sway_view_prop prop); uint32_t (*get_int_prop)(struct sway_view *view, enum sway_view_prop prop); @@ -215,6 +217,9 @@ uint32_t view_get_window_type(struct sway_view *view); const char *view_get_shell(struct sway_view *view); +void view_get_constraints(struct sway_view *view, double *min_width, + double *max_width, double *min_height, double *max_height); + uint32_t view_configure(struct sway_view *view, double lx, double ly, int width, int height); diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c index c5d53d1d..76fe72ea 100644 --- a/sway/desktop/xdg_shell.c +++ b/sway/desktop/xdg_shell.c @@ -1,4 +1,5 @@ #define _POSIX_C_SOURCE 199309L +#include #include #include #include @@ -95,6 +96,16 @@ static struct sway_xdg_shell_view *xdg_shell_view_from_view( return (struct sway_xdg_shell_view *)view; } +static void get_constraints(struct sway_view *view, double *min_width, + double *max_width, double *min_height, double *max_height) { + struct wlr_xdg_toplevel_state *state = + &view->wlr_xdg_surface->toplevel->current; + *min_width = state->min_width > 0 ? state->min_width : DBL_MIN; + *max_width = state->max_width > 0 ? state->max_width : DBL_MAX; + *min_height = state->min_height > 0 ? state->min_height : DBL_MIN; + *max_height = state->max_height > 0 ? state->max_height : DBL_MAX; +} + static const char *get_string_prop(struct sway_view *view, enum sway_view_prop prop) { if (xdg_shell_view_from_view(view) == NULL) { return NULL; @@ -188,6 +199,7 @@ static void destroy(struct sway_view *view) { } static const struct sway_view_impl view_impl = { + .get_constraints = get_constraints, .get_string_prop = get_string_prop, .configure = configure, .set_activated = set_activated, diff --git a/sway/desktop/xdg_shell_v6.c b/sway/desktop/xdg_shell_v6.c index 4bd6af5e..57b51908 100644 --- a/sway/desktop/xdg_shell_v6.c +++ b/sway/desktop/xdg_shell_v6.c @@ -1,4 +1,5 @@ #define _POSIX_C_SOURCE 199309L +#include #include #include #include @@ -94,6 +95,16 @@ static struct sway_xdg_shell_v6_view *xdg_shell_v6_view_from_view( return (struct sway_xdg_shell_v6_view *)view; } +static void get_constraints(struct sway_view *view, double *min_width, + double *max_width, double *min_height, double *max_height) { + struct wlr_xdg_toplevel_v6_state *state = + &view->wlr_xdg_surface_v6->toplevel->current; + *min_width = state->min_width > 0 ? state->min_width : DBL_MIN; + *max_width = state->max_width > 0 ? state->max_width : DBL_MAX; + *min_height = state->min_height > 0 ? state->min_height : DBL_MIN; + *max_height = state->max_height > 0 ? state->max_height : DBL_MAX; +} + static const char *get_string_prop(struct sway_view *view, enum sway_view_prop prop) { if (xdg_shell_v6_view_from_view(view) == NULL) { return NULL; @@ -184,6 +195,7 @@ static void destroy(struct sway_view *view) { } static const struct sway_view_impl view_impl = { + .get_constraints = get_constraints, .get_string_prop = get_string_prop, .configure = configure, .set_activated = set_activated, diff --git a/sway/input/cursor.c b/sway/input/cursor.c index ad0ceb94..7deb2b19 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -243,7 +243,7 @@ static void handle_resize_motion(struct sway_seat *seat, grow_height = seat->op_ref_height * max_multiplier; } - // Determine new width/height, and accommodate for min/max values + // Determine new width/height, and accommodate for floating min/max values double width = seat->op_ref_width + grow_width; double height = seat->op_ref_height + grow_height; int min_width, max_width, min_height, max_height; @@ -252,6 +252,15 @@ static void handle_resize_motion(struct sway_seat *seat, width = fmax(min_width, fmin(width, max_width)); height = fmax(min_height, fmin(height, max_height)); + // Apply the view's min/max size + if (con->type == C_VIEW) { + double view_min_width, view_max_width, view_min_height, view_max_height; + view_get_constraints(con->sway_view, &view_min_width, &view_max_width, + &view_min_height, &view_max_height); + width = fmax(view_min_width, fmin(width, view_max_width)); + height = fmax(view_min_height, fmin(height, view_max_height)); + } + // Recalculate these, in case we hit a min/max limit grow_width = width - seat->op_ref_width; grow_height = height - seat->op_ref_height; diff --git a/sway/tree/view.c b/sway/tree/view.c index 24594950..89150a69 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -141,6 +141,19 @@ const char *view_get_shell(struct sway_view *view) { return "unknown"; } +void view_get_constraints(struct sway_view *view, double *min_width, + double *max_width, double *min_height, double *max_height) { + if (view->impl->get_constraints) { + view->impl->get_constraints(view, + min_width, max_width, min_height, max_height); + } else { + *min_width = DBL_MIN; + *max_width = DBL_MAX; + *min_height = DBL_MIN; + *max_height = DBL_MAX; + } +} + uint32_t view_configure(struct sway_view *view, double lx, double ly, int width, int height) { if (view->impl->configure) { -- cgit v1.2.3-70-g09d2 From 238c8afc74241efdc44d1cf88322d322ce1226d9 Mon Sep 17 00:00:00 2001 From: emersion Date: Sun, 22 Jul 2018 22:20:07 +0100 Subject: Handle set_{title,app_id} for xdg-shell and zxdg-shell-v6 This allows to update the title even if the view doesn't commit. This is useful e.g. when a terminal sets its toplevel title to the currently running command and when the view isn't visible. --- include/sway/tree/view.h | 4 ++++ sway/desktop/xdg_shell.c | 26 +++++++++++++++++++++++++- sway/desktop/xdg_shell_v6.c | 26 +++++++++++++++++++++++++- 3 files changed, 54 insertions(+), 2 deletions(-) (limited to 'include/sway') diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h index 1dfb218b..3bdfe252 100644 --- a/include/sway/tree/view.h +++ b/include/sway/tree/view.h @@ -110,6 +110,8 @@ struct sway_xdg_shell_v6_view { struct wl_listener request_resize; struct wl_listener request_maximize; struct wl_listener request_fullscreen; + struct wl_listener set_title; + struct wl_listener set_app_id; struct wl_listener new_popup; struct wl_listener map; struct wl_listener unmap; @@ -124,6 +126,8 @@ struct sway_xdg_shell_view { struct wl_listener request_resize; struct wl_listener request_maximize; struct wl_listener request_fullscreen; + struct wl_listener set_title; + struct wl_listener set_app_id; struct wl_listener new_popup; struct wl_listener map; struct wl_listener unmap; diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c index 706b35c3..62c3abc8 100644 --- a/sway/desktop/xdg_shell.c +++ b/sway/desktop/xdg_shell.c @@ -225,10 +225,24 @@ static void handle_commit(struct wl_listener *listener, void *data) { transaction_notify_view_ready(view, xdg_surface->configure_serial); } - view_update_title(view, false); view_damage_from(view); } +static void handle_set_title(struct wl_listener *listener, void *data) { + struct sway_xdg_shell_view *xdg_shell_view = + wl_container_of(listener, xdg_shell_view, set_title); + struct sway_view *view = &xdg_shell_view->view; + view_update_title(view, false); + view_execute_criteria(view); +} + +static void handle_set_app_id(struct wl_listener *listener, void *data) { + struct sway_xdg_shell_view *xdg_shell_view = + wl_container_of(listener, xdg_shell_view, set_app_id); + struct sway_view *view = &xdg_shell_view->view; + view_execute_criteria(view); +} + static void handle_new_popup(struct wl_listener *listener, void *data) { struct sway_xdg_shell_view *xdg_shell_view = wl_container_of(listener, xdg_shell_view, new_popup); @@ -304,6 +318,8 @@ static void handle_unmap(struct wl_listener *listener, void *data) { wl_list_remove(&xdg_shell_view->request_fullscreen.link); wl_list_remove(&xdg_shell_view->request_move.link); wl_list_remove(&xdg_shell_view->request_resize.link); + wl_list_remove(&xdg_shell_view->set_title.link); + wl_list_remove(&xdg_shell_view->set_app_id.link); } static void handle_map(struct wl_listener *listener, void *data) { @@ -349,6 +365,14 @@ static void handle_map(struct wl_listener *listener, void *data) { xdg_shell_view->request_resize.notify = handle_request_resize; wl_signal_add(&xdg_surface->toplevel->events.request_resize, &xdg_shell_view->request_resize); + + xdg_shell_view->set_title.notify = handle_set_title; + wl_signal_add(&xdg_surface->toplevel->events.set_title, + &xdg_shell_view->set_title); + + xdg_shell_view->set_app_id.notify = handle_set_app_id; + wl_signal_add(&xdg_surface->toplevel->events.set_app_id, + &xdg_shell_view->set_app_id); } static void handle_destroy(struct wl_listener *listener, void *data) { diff --git a/sway/desktop/xdg_shell_v6.c b/sway/desktop/xdg_shell_v6.c index 201b5b1e..7fb85410 100644 --- a/sway/desktop/xdg_shell_v6.c +++ b/sway/desktop/xdg_shell_v6.c @@ -220,10 +220,24 @@ static void handle_commit(struct wl_listener *listener, void *data) { transaction_notify_view_ready(view, xdg_surface_v6->configure_serial); } - view_update_title(view, false); view_damage_from(view); } +static void handle_set_title(struct wl_listener *listener, void *data) { + struct sway_xdg_shell_v6_view *xdg_shell_v6_view = + wl_container_of(listener, xdg_shell_v6_view, set_title); + struct sway_view *view = &xdg_shell_v6_view->view; + view_update_title(view, false); + view_execute_criteria(view); +} + +static void handle_set_app_id(struct wl_listener *listener, void *data) { + struct sway_xdg_shell_v6_view *xdg_shell_v6_view = + wl_container_of(listener, xdg_shell_v6_view, set_app_id); + struct sway_view *view = &xdg_shell_v6_view->view; + view_execute_criteria(view); +} + static void handle_new_popup(struct wl_listener *listener, void *data) { struct sway_xdg_shell_v6_view *xdg_shell_v6_view = wl_container_of(listener, xdg_shell_v6_view, new_popup); @@ -299,6 +313,8 @@ static void handle_unmap(struct wl_listener *listener, void *data) { wl_list_remove(&xdg_shell_v6_view->request_fullscreen.link); wl_list_remove(&xdg_shell_v6_view->request_move.link); wl_list_remove(&xdg_shell_v6_view->request_resize.link); + wl_list_remove(&xdg_shell_v6_view->set_title.link); + wl_list_remove(&xdg_shell_v6_view->set_app_id.link); } static void handle_map(struct wl_listener *listener, void *data) { @@ -344,6 +360,14 @@ static void handle_map(struct wl_listener *listener, void *data) { xdg_shell_v6_view->request_resize.notify = handle_request_resize; wl_signal_add(&xdg_surface->toplevel->events.request_resize, &xdg_shell_v6_view->request_resize); + + xdg_shell_v6_view->set_title.notify = handle_set_title; + wl_signal_add(&xdg_surface->toplevel->events.set_title, + &xdg_shell_v6_view->set_title); + + xdg_shell_v6_view->set_app_id.notify = handle_set_app_id; + wl_signal_add(&xdg_surface->toplevel->events.set_app_id, + &xdg_shell_v6_view->set_app_id); } static void handle_destroy(struct wl_listener *listener, void *data) { -- cgit v1.2.3-70-g09d2 From 81e8f31cc6f284b54ab206e14af7ecbc1a9ed1bb Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Sun, 22 Jul 2018 14:10:40 +1000 Subject: Implement scratchpad Implements the following commands: * move scratchpad * scratchpad show * [criteria] scratchpad show Also fixes these: * Fix memory leak when executing command with criteria (use `list_free(views)` instead of `free(views)`) * Fix crash when running `move to` with no further arguments --- include/sway/scratchpad.h | 26 +++++++ include/sway/server.h | 2 + include/sway/tree/container.h | 5 ++ sway/commands.c | 3 +- sway/commands/move.c | 19 ++++- sway/commands/scratchpad.c | 37 +++++++++ sway/criteria.c | 8 ++ sway/meson.build | 2 + sway/scratchpad.c | 173 ++++++++++++++++++++++++++++++++++++++++++ sway/server.c | 3 + sway/tree/container.c | 8 ++ sway/tree/layout.c | 7 ++ sway/tree/view.c | 2 +- 13 files changed, 290 insertions(+), 5 deletions(-) create mode 100644 include/sway/scratchpad.h create mode 100644 sway/commands/scratchpad.c create mode 100644 sway/scratchpad.c (limited to 'include/sway') diff --git a/include/sway/scratchpad.h b/include/sway/scratchpad.h new file mode 100644 index 00000000..5af5256f --- /dev/null +++ b/include/sway/scratchpad.h @@ -0,0 +1,26 @@ +#ifndef _SWAY_SCRATCHPAD_H +#define _SWAY_SCRATCHPAD_H + +#include "tree/container.h" + +/** + * Move a container to the scratchpad. + */ +void scratchpad_add_container(struct sway_container *con); + +/** + * Remove a container from the scratchpad. + */ +void scratchpad_remove_container(struct sway_container *con); + +/** + * Show or hide the next container on the scratchpad. + */ +void scratchpad_toggle_auto(void); + +/** + * Show or hide a specific container on the scratchpad. + */ +void scratchpad_toggle_container(struct sway_container *con); + +#endif diff --git a/include/sway/server.h b/include/sway/server.h index 70bde6d4..6cef2e58 100644 --- a/include/sway/server.h +++ b/include/sway/server.h @@ -48,6 +48,8 @@ struct sway_server { list_t *transactions; list_t *dirty_containers; + + list_t *scratchpad; // struct sway_container }; struct sway_server server; diff --git a/include/sway/tree/container.h b/include/sway/tree/container.h index 59c5b4c7..2a4be18c 100644 --- a/include/sway/tree/container.h +++ b/include/sway/tree/container.h @@ -135,6 +135,11 @@ struct sway_container { struct sway_container *parent; + // Indicates that the container is a scratchpad container. + // Both hidden and visible scratchpad containers have scratchpad=true. + // Hidden scratchpad containers have a NULL parent. + bool scratchpad; + float alpha; struct wlr_texture *title_focused; diff --git a/sway/commands.c b/sway/commands.c index f40f0e9d..fdae1961 100644 --- a/sway/commands.c +++ b/sway/commands.c @@ -149,6 +149,7 @@ static struct cmd_handler command_handlers[] = { { "reload", cmd_reload }, { "rename", cmd_rename }, { "resize", cmd_resize }, + { "scratchpad", cmd_scratchpad }, { "split", cmd_split }, { "splith", cmd_splith }, { "splitt", cmd_splitt }, @@ -326,7 +327,7 @@ struct cmd_results *execute_command(char *_exec, struct sway_seat *seat) { } while(head); cleanup: free(exec); - free(views); + list_free(views); if (!results) { results = cmd_results_new(CMD_SUCCESS, NULL, NULL); } diff --git a/sway/commands/move.c b/sway/commands/move.c index 6ec050a8..1940043d 100644 --- a/sway/commands/move.c +++ b/sway/commands/move.c @@ -9,6 +9,7 @@ #include "sway/input/cursor.h" #include "sway/input/seat.h" #include "sway/output.h" +#include "sway/scratchpad.h" #include "sway/tree/arrange.h" #include "sway/tree/container.h" #include "sway/tree/layout.h" @@ -296,6 +297,19 @@ static struct cmd_results *move_to_position(struct sway_container *container, return cmd_results_new(CMD_SUCCESS, NULL, NULL); } +static struct cmd_results *move_to_scratchpad(struct sway_container *con) { + if (con->type != C_CONTAINER && con->type != C_VIEW) { + return cmd_results_new(CMD_INVALID, "move", + "Only views and containers can be moved to the scratchpad"); + } + if (con->scratchpad) { + return cmd_results_new(CMD_INVALID, "move", + "Container is already in the scratchpad"); + } + scratchpad_add_container(con); + return cmd_results_new(CMD_SUCCESS, NULL, NULL); +} + struct cmd_results *cmd_move(int argc, char **argv) { struct cmd_results *error = NULL; if ((error = checkarg(argc, "move", EXPECTED_AT_LEAST, 1))) { @@ -317,10 +331,9 @@ struct cmd_results *cmd_move(int argc, char **argv) { } else if (strcasecmp(argv[0], "workspace") == 0) { return cmd_move_workspace(current, argc, argv); } else if (strcasecmp(argv[0], "scratchpad") == 0 - || (strcasecmp(argv[0], "to") == 0 + || (strcasecmp(argv[0], "to") == 0 && argc == 2 && strcasecmp(argv[1], "scratchpad") == 0)) { - // TODO: scratchpad - return cmd_results_new(CMD_FAILURE, "move", "Unimplemented"); + return move_to_scratchpad(current); } else if (strcasecmp(argv[0], "position") == 0) { return move_to_position(current, argc, argv); } else if (strcasecmp(argv[0], "absolute") == 0) { diff --git a/sway/commands/scratchpad.c b/sway/commands/scratchpad.c new file mode 100644 index 00000000..8a529cb4 --- /dev/null +++ b/sway/commands/scratchpad.c @@ -0,0 +1,37 @@ +#include "log.h" +#include "sway/commands.h" +#include "sway/config.h" +#include "sway/scratchpad.h" +#include "sway/server.h" +#include "sway/tree/container.h" + +struct cmd_results *cmd_scratchpad(int argc, char **argv) { + struct cmd_results *error = NULL; + if ((error = checkarg(argc, "scratchpad", EXPECTED_EQUAL_TO, 1))) { + return error; + } + if (strcmp(argv[0], "show") != 0) { + return cmd_results_new(CMD_INVALID, "scratchpad", + "Expected 'scratchpad show'"); + } + if (!server.scratchpad->length) { + return cmd_results_new(CMD_INVALID, "scratchpad", + "Scratchpad is empty"); + } + + if (config->handler_context.using_criteria) { + // If using criteria, this command is executed for every container which + // matches the criteria. If this container isn't in the scratchpad, + // we'll just silently return a success. + struct sway_container *con = config->handler_context.current_container; + wlr_log(WLR_INFO, "cmd_scratchpad(%s)", con->name); + if (!con->scratchpad) { + return cmd_results_new(CMD_SUCCESS, NULL, NULL); + } + scratchpad_toggle_container(con); + } else { + scratchpad_toggle_auto(); + } + + return cmd_results_new(CMD_SUCCESS, NULL, NULL); +} diff --git a/sway/criteria.c b/sway/criteria.c index e2b248de..6af97d5b 100644 --- a/sway/criteria.c +++ b/sway/criteria.c @@ -225,6 +225,14 @@ list_t *criteria_get_views(struct criteria *criteria) { }; container_for_each_descendant_dfs(&root_container, criteria_get_views_iterator, &data); + + // Scratchpad items which are hidden are not in the tree. + for (int i = 0; i < server.scratchpad->length; ++i) { + struct sway_container *con = server.scratchpad->items[i]; + if (!con->parent) { + criteria_get_views_iterator(con, &data); + } + } return matches; } diff --git a/sway/meson.build b/sway/meson.build index f4fdc8ea..30c848e2 100644 --- a/sway/meson.build +++ b/sway/meson.build @@ -7,6 +7,7 @@ sway_sources = files( 'debug-tree.c', 'ipc-json.c', 'ipc-server.c', + 'scratchpad.c', 'security.c', 'desktop/desktop.c', @@ -67,6 +68,7 @@ sway_sources = files( 'commands/reload.c', 'commands/rename.c', 'commands/resize.c', + 'commands/scratchpad.c', 'commands/seat.c', 'commands/seat/attach.c', 'commands/seat/cursor.c', diff --git a/sway/scratchpad.c b/sway/scratchpad.c new file mode 100644 index 00000000..e1f931a4 --- /dev/null +++ b/sway/scratchpad.c @@ -0,0 +1,173 @@ +#define _XOPEN_SOURCE 700 +#include +#include +#include +#include "sway/scratchpad.h" +#include "sway/input/seat.h" +#include "sway/tree/arrange.h" +#include "sway/tree/container.h" +#include "sway/tree/view.h" +#include "sway/tree/workspace.h" +#include "list.h" +#include "log.h" + +void scratchpad_add_container(struct sway_container *con) { + if (!sway_assert(!con->scratchpad, "Container is already in scratchpad")) { + return; + } + con->scratchpad = true; + list_add(server.scratchpad, con); + + struct sway_container *parent = con->parent; + container_set_floating(con, true); + container_remove_child(con); + arrange_windows(parent); + + struct sway_seat *seat = input_manager_current_seat(input_manager); + seat_set_focus(seat, seat_get_focus_inactive(seat, parent)); +} + +void scratchpad_remove_container(struct sway_container *con) { + if (!sway_assert(con->scratchpad, "Container is not in scratchpad")) { + return; + } + con->scratchpad = false; + for (int i = 0; i < server.scratchpad->length; ++i) { + if (server.scratchpad->items[i] == con) { + list_del(server.scratchpad, i); + break; + } + } +} + +/** + * Show a single scratchpad container. + * The container might be visible on another workspace already. + */ +static void scratchpad_show(struct sway_container *con) { + struct sway_seat *seat = input_manager_current_seat(input_manager); + struct sway_container *ws = seat_get_focus(seat); + if (ws->type != C_WORKSPACE) { + ws = container_parent(ws, C_WORKSPACE); + } + + // If the current con or any of its parents are in fullscreen mode, we + // first need to disable it before showing the scratchpad con. + if (ws->sway_workspace->fullscreen) { + view_set_fullscreen(ws->sway_workspace->fullscreen, false); + } + + // Show the container + if (con->parent) { + container_remove_child(con); + } + container_add_child(ws->sway_workspace->floating, con); + + // Make sure the container's center point overlaps this workspace + double center_lx = con->x + con->width / 2; + double center_ly = con->y + con->height / 2; + + struct wlr_box workspace_box; + container_get_box(ws, &workspace_box); + if (!wlr_box_contains_point(&workspace_box, center_lx, center_ly)) { + // Maybe resize it + if (con->width > ws->width || con->height > ws->height) { + // TODO: Do this properly once we can float C_CONTAINERs + if (con->type == C_VIEW) { + view_init_floating(con->sway_view); + arrange_windows(con); + } + } + + // Center it + double new_lx = ws->x + (ws->width - con->width) / 2; + double new_ly = ws->y + (ws->height - con->height) / 2; + container_floating_move_to(con, new_lx, new_ly); + } + + seat_set_focus(seat, con); + + container_set_dirty(con->parent); +} + +/** + * Hide a single scratchpad container. + * The container might not be the focused container (eg. when using criteria). + */ +static void scratchpad_hide(struct sway_container *con) { + struct sway_seat *seat = input_manager_current_seat(input_manager); + struct sway_container *focus = seat_get_focus(seat); + struct sway_container *ws = container_parent(con, C_WORKSPACE); + + container_remove_child(con); + arrange_windows(ws); + if (con == focus) { + seat_set_focus(seat, seat_get_focus_inactive(seat, ws)); + } + list_move_to_end(server.scratchpad, con); +} + +void scratchpad_toggle_auto(void) { + struct sway_seat *seat = input_manager_current_seat(input_manager); + struct sway_container *focus = seat_get_focus(seat); + struct sway_container *ws = focus->type == C_WORKSPACE ? + focus : container_parent(focus, C_WORKSPACE); + + // Check if the currently focused window is a scratchpad window and should + // be hidden again. + if (focus->scratchpad) { + wlr_log(WLR_DEBUG, "Focus is a scratchpad window - hiding %s", + focus->name); + scratchpad_hide(focus); + return; + } + + // Check if there is an unfocused scratchpad window on the current workspace + // and focus it. + for (int i = 0; i < ws->sway_workspace->floating->children->length; ++i) { + struct sway_container *floater = + ws->sway_workspace->floating->children->items[i]; + if (floater->scratchpad && focus != floater) { + wlr_log(WLR_DEBUG, + "Focusing other scratchpad window (%s) in this workspace", + floater->name); + scratchpad_show(floater); + return; + } + } + + // Check if there is a visible scratchpad window on another workspace. + // In this case we move it to the current workspace. + for (int i = 0; i < server.scratchpad->length; ++i) { + struct sway_container *con = server.scratchpad->items[i]; + if (con->parent) { + wlr_log(WLR_DEBUG, + "Moving a visible scratchpad window (%s) to this workspace", + con->name); + scratchpad_show(con); + return; + } + } + + // Take the container at the bottom of the scratchpad list + if (!sway_assert(server.scratchpad->length, "Scratchpad is empty")) { + return; + } + struct sway_container *con = server.scratchpad->items[0]; + wlr_log(WLR_DEBUG, "Showing %s from list", con->name); + scratchpad_show(con); +} + +void scratchpad_toggle_container(struct sway_container *con) { + if (!sway_assert(con->scratchpad, "Container isn't in the scratchpad")) { + return; + } + + // Check if it matches a currently visible scratchpad window and hide it. + if (con->parent) { + scratchpad_hide(con); + return; + } + + scratchpad_show(con); +} diff --git a/sway/server.c b/sway/server.c index 89dfbf8c..916e6b71 100644 --- a/sway/server.c +++ b/sway/server.c @@ -126,6 +126,8 @@ bool server_init(struct sway_server *server) { server->dirty_containers = create_list(); server->transactions = create_list(); + server->scratchpad = create_list(); + input_manager = input_manager_create(server); return true; } @@ -135,6 +137,7 @@ void server_fini(struct sway_server *server) { wl_display_destroy(server->wl_display); list_free(server->dirty_containers); list_free(server->transactions); + list_free(server->scratchpad); } bool server_start_backend(struct sway_server *server) { diff --git a/sway/tree/container.c b/sway/tree/container.c index 42c1d024..4f743c40 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -17,6 +17,7 @@ #include "sway/input/seat.h" #include "sway/ipc-server.h" #include "sway/output.h" +#include "sway/scratchpad.h" #include "sway/server.h" #include "sway/tree/arrange.h" #include "sway/tree/layout.h" @@ -328,6 +329,10 @@ static struct sway_container *container_destroy_noreaping( con->destroying = true; container_set_dirty(con); + if (con->scratchpad) { + scratchpad_remove_container(con); + } + if (!con->parent) { return NULL; } @@ -955,6 +960,9 @@ void container_set_floating(struct sway_container *container, bool enable) { container_reap_empty_recursive(workspace); } else { // Returning to tiled + if (container->scratchpad) { + scratchpad_remove_container(container); + } container_remove_child(container); container_add_child(workspace, container); container->width = container->parent->width; diff --git a/sway/tree/layout.c b/sway/tree/layout.c index 533906fa..af37611f 100644 --- a/sway/tree/layout.c +++ b/sway/tree/layout.c @@ -135,6 +135,10 @@ void container_add_child(struct sway_container *parent, list_add(parent->children, child); child->parent = parent; container_handle_fullscreen_reparent(child, old_parent); + if (old_parent) { + container_set_dirty(old_parent); + } + container_set_dirty(child); } struct sway_container *container_remove_child(struct sway_container *child) { @@ -153,6 +157,9 @@ struct sway_container *container_remove_child(struct sway_container *child) { child->parent = NULL; container_notify_subtree_changed(parent); + container_set_dirty(parent); + container_set_dirty(child); + return parent; } diff --git a/sway/tree/view.c b/sway/tree/view.c index 89150a69..9d88d7aa 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -1065,7 +1065,7 @@ void view_update_marks_textures(struct sway_view *view) { } bool view_is_visible(struct sway_view *view) { - if (!view->swayc || view->swayc->destroying) { + if (!view->swayc || view->swayc->destroying || !view->swayc->parent) { return false; } struct sway_container *workspace = -- cgit v1.2.3-70-g09d2 From 12e90fa6006b2cf17a5b5983b5a6e2e70cda58d3 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Sun, 22 Jul 2018 22:28:20 +1000 Subject: Store scratchpad list in sway_root instead of server --- include/sway/server.h | 2 -- include/sway/tree/layout.h | 2 ++ sway/commands/scratchpad.c | 3 +-- sway/criteria.c | 5 +++-- sway/scratchpad.c | 20 +++++++++++--------- sway/server.c | 3 --- sway/tree/layout.c | 1 + 7 files changed, 18 insertions(+), 18 deletions(-) (limited to 'include/sway') diff --git a/include/sway/server.h b/include/sway/server.h index 6cef2e58..70bde6d4 100644 --- a/include/sway/server.h +++ b/include/sway/server.h @@ -48,8 +48,6 @@ struct sway_server { list_t *transactions; list_t *dirty_containers; - - list_t *scratchpad; // struct sway_container }; struct sway_server server; diff --git a/include/sway/tree/layout.h b/include/sway/tree/layout.h index 5a78fd58..7d7da2d7 100644 --- a/include/sway/tree/layout.h +++ b/include/sway/tree/layout.h @@ -35,6 +35,8 @@ struct sway_root { struct wl_list outputs; // sway_output::link + list_t *scratchpad; // struct sway_container + struct { struct wl_signal new_container; } events; diff --git a/sway/commands/scratchpad.c b/sway/commands/scratchpad.c index 8a529cb4..ccc07c87 100644 --- a/sway/commands/scratchpad.c +++ b/sway/commands/scratchpad.c @@ -2,7 +2,6 @@ #include "sway/commands.h" #include "sway/config.h" #include "sway/scratchpad.h" -#include "sway/server.h" #include "sway/tree/container.h" struct cmd_results *cmd_scratchpad(int argc, char **argv) { @@ -14,7 +13,7 @@ struct cmd_results *cmd_scratchpad(int argc, char **argv) { return cmd_results_new(CMD_INVALID, "scratchpad", "Expected 'scratchpad show'"); } - if (!server.scratchpad->length) { + if (!root_container.sway_root->scratchpad->length) { return cmd_results_new(CMD_INVALID, "scratchpad", "Scratchpad is empty"); } diff --git a/sway/criteria.c b/sway/criteria.c index 6af97d5b..c2e9c07e 100644 --- a/sway/criteria.c +++ b/sway/criteria.c @@ -227,8 +227,9 @@ list_t *criteria_get_views(struct criteria *criteria) { criteria_get_views_iterator, &data); // Scratchpad items which are hidden are not in the tree. - for (int i = 0; i < server.scratchpad->length; ++i) { - struct sway_container *con = server.scratchpad->items[i]; + for (int i = 0; i < root_container.sway_root->scratchpad->length; ++i) { + struct sway_container *con = + root_container.sway_root->scratchpad->items[i]; if (!con->parent) { criteria_get_views_iterator(con, &data); } diff --git a/sway/scratchpad.c b/sway/scratchpad.c index e1f931a4..1e836e7d 100644 --- a/sway/scratchpad.c +++ b/sway/scratchpad.c @@ -16,7 +16,7 @@ void scratchpad_add_container(struct sway_container *con) { return; } con->scratchpad = true; - list_add(server.scratchpad, con); + list_add(root_container.sway_root->scratchpad, con); struct sway_container *parent = con->parent; container_set_floating(con, true); @@ -32,9 +32,9 @@ void scratchpad_remove_container(struct sway_container *con) { return; } con->scratchpad = false; - for (int i = 0; i < server.scratchpad->length; ++i) { - if (server.scratchpad->items[i] == con) { - list_del(server.scratchpad, i); + for (int i = 0; i < root_container.sway_root->scratchpad->length; ++i) { + if (root_container.sway_root->scratchpad->items[i] == con) { + list_del(root_container.sway_root->scratchpad, i); break; } } @@ -104,7 +104,7 @@ static void scratchpad_hide(struct sway_container *con) { if (con == focus) { seat_set_focus(seat, seat_get_focus_inactive(seat, ws)); } - list_move_to_end(server.scratchpad, con); + list_move_to_end(root_container.sway_root->scratchpad, con); } void scratchpad_toggle_auto(void) { @@ -138,8 +138,9 @@ void scratchpad_toggle_auto(void) { // Check if there is a visible scratchpad window on another workspace. // In this case we move it to the current workspace. - for (int i = 0; i < server.scratchpad->length; ++i) { - struct sway_container *con = server.scratchpad->items[i]; + for (int i = 0; i < root_container.sway_root->scratchpad->length; ++i) { + struct sway_container *con = + root_container.sway_root->scratchpad->items[i]; if (con->parent) { wlr_log(WLR_DEBUG, "Moving a visible scratchpad window (%s) to this workspace", @@ -150,10 +151,11 @@ void scratchpad_toggle_auto(void) { } // Take the container at the bottom of the scratchpad list - if (!sway_assert(server.scratchpad->length, "Scratchpad is empty")) { + if (!sway_assert(root_container.sway_root->scratchpad->length, + "Scratchpad is empty")) { return; } - struct sway_container *con = server.scratchpad->items[0]; + struct sway_container *con = root_container.sway_root->scratchpad->items[0]; wlr_log(WLR_DEBUG, "Showing %s from list", con->name); scratchpad_show(con); } diff --git a/sway/server.c b/sway/server.c index 916e6b71..89dfbf8c 100644 --- a/sway/server.c +++ b/sway/server.c @@ -126,8 +126,6 @@ bool server_init(struct sway_server *server) { server->dirty_containers = create_list(); server->transactions = create_list(); - server->scratchpad = create_list(); - input_manager = input_manager_create(server); return true; } @@ -137,7 +135,6 @@ void server_fini(struct sway_server *server) { wl_display_destroy(server->wl_display); list_free(server->dirty_containers); list_free(server->transactions); - list_free(server->scratchpad); } bool server_start_backend(struct sway_server *server) { diff --git a/sway/tree/layout.c b/sway/tree/layout.c index af37611f..a2be0ef3 100644 --- a/sway/tree/layout.c +++ b/sway/tree/layout.c @@ -42,6 +42,7 @@ void layout_init(void) { wl_list_init(&root_container.sway_root->xwayland_unmanaged); wl_list_init(&root_container.sway_root->drag_icons); wl_signal_init(&root_container.sway_root->events.new_container); + root_container.sway_root->scratchpad = create_list(); root_container.sway_root->output_layout_change.notify = output_layout_handle_change; -- cgit v1.2.3-70-g09d2