From 3ead287064e8176520e7f15bf84fb179cbf1f1b1 Mon Sep 17 00:00:00 2001 From: Rouven Czerwinski Date: Sat, 23 Feb 2019 08:28:49 +0100 Subject: [PATCH 001/191] view: set xdg_decoration->view to NULL, check decoration destroy Fixes heap-use-after-free: ==32046==ERROR: AddressSanitizer: heap-use-after-free on address 0x615000064d20 at pc 0x55571ce4d303 bp 0x7fff545c64c0 sp 0x7fff545c64b0 WRITE of size 8 at 0x615000064d20 thread T0 #0 0x55571ce4d302 in xdg_decoration_handle_destroy ../sway/xdg_decoration.c:13 #1 0x7f64009d6f36 in wlr_signal_emit_safe ../util/signal.c:29 #2 0x7f64009d3c46 in toplevel_decoration_handle_resource_destroy ../types/wlr_xdg_decoration_v1.c:65 #3 0x7f6400a19f8d (/usr/lib/x86_64-linux-gnu/libwayland-server.so.0+0x7f8d) #4 0x7f6400a19fed in wl_resource_destroy (/usr/lib/x86_64-linux-gnu/libwayland-server.so.0+0x7fed) #5 0x7f64009d3d1f in toplevel_decoration_handle_surface_destroy ../types/wlr_xdg_decoration_v1.c:82 #6 0x7f64009d6f36 in wlr_signal_emit_safe ../util/signal.c:29 #7 0x7f64009b059c in reset_xdg_surface ../types/xdg_shell/wlr_xdg_surface.c:453 #8 0x7f64009b0688 in destroy_xdg_surface ../types/xdg_shell/wlr_xdg_surface.c:483 #9 0x7f64009af08c in xdg_client_handle_resource_destroy ../types/xdg_shell/wlr_xdg_shell.c:71 #10 0x7f6400a19f8d (/usr/lib/x86_64-linux-gnu/libwayland-server.so.0+0x7f8d) #11 0x7f6400a1e211 (/usr/lib/x86_64-linux-gnu/libwayland-server.so.0+0xc211) #12 0x7f6400a1e6fe (/usr/lib/x86_64-linux-gnu/libwayland-server.so.0+0xc6fe) #13 0x7f6400a1a0ec in wl_client_destroy (/usr/lib/x86_64-linux-gnu/libwayland-server.so.0+0x80ec) #14 0x7f6400a1a1c4 (/usr/lib/x86_64-linux-gnu/libwayland-server.so.0+0x81c4) #15 0x7f6400a1b941 in wl_event_loop_dispatch (/usr/lib/x86_64-linux-gnu/libwayland-server.so.0+0x9941) #16 0x7f6400a1a569 in wl_display_run (/usr/lib/x86_64-linux-gnu/libwayland-server.so.0+0x8569) #17 0x55571ce4c7fd in server_run ../sway/server.c:214 #18 0x55571ce4ad59 in main ../sway/main.c:405 #19 0x7f640071109a in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2409a) #20 0x55571ce2cfa9 in _start (/usr/local/bin/sway+0x35fa9) 0x615000064d20 is located 32 bytes inside of 504-byte region [0x615000064d00,0x615000064ef8) freed by thread T0 here: #0 0x7f6401531b70 in free (/usr/lib/x86_64-linux-gnu/libasan.so.5+0xedb70) #1 0x55571ce6c72b in destroy ../sway/desktop/xdg_shell.c:252 #2 0x55571cee3f7b in view_destroy ../sway/tree/view.c:60 #3 0x55571cee4090 in view_begin_destroy ../sway/tree/view.c:73 #4 0x55571ce6dd95 in handle_destroy ../sway/desktop/xdg_shell.c:464 #5 0x7f64009d6f36 in wlr_signal_emit_safe ../util/signal.c:29 #6 0x7f64009b059c in reset_xdg_surface ../types/xdg_shell/wlr_xdg_surface.c:453 #7 0x7f64009b0688 in destroy_xdg_surface ../types/xdg_shell/wlr_xdg_surface.c:483 #8 0x7f64009af08c in xdg_client_handle_resource_destroy ../types/xdg_shell/wlr_xdg_shell.c:71 #9 0x7f6400a19f8d (/usr/lib/x86_64-linux-gnu/libwayland-server.so.0+0x7f8d) previously allocated by thread T0 here: #0 0x7f6401532138 in calloc (/usr/lib/x86_64-linux-gnu/libasan.so.5+0xee138) #1 0x55571ce6df39 in handle_xdg_shell_surface ../sway/desktop/xdg_shell.c:485 #2 0x7f64009d6f36 in wlr_signal_emit_safe ../util/signal.c:29 #3 0x7f64009b0167 in handle_xdg_surface_commit ../types/xdg_shell/wlr_xdg_surface.c:350 #4 0x7f64009ce2a5 in surface_commit_pending ../types/wlr_surface.c:372 #5 0x7f64009ce523 in surface_commit ../types/wlr_surface.c:444 #6 0x7f63ff63ddad in ffi_call_unix64 (/usr/lib/x86_64-linux-gnu/libffi.so.6+0x5dad) Fixes #3759 --- sway/desktop/xdg_shell.c | 4 ++++ sway/xdg_decoration.c | 4 +++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c index ce6fe41a1..c84ca1115 100644 --- a/sway/desktop/xdg_shell.c +++ b/sway/desktop/xdg_shell.c @@ -17,6 +17,7 @@ #include "sway/tree/container.h" #include "sway/tree/view.h" #include "sway/tree/workspace.h" +#include "sway/xdg_decoration.h" static const struct sway_view_child_impl popup_impl; @@ -461,6 +462,9 @@ static void handle_destroy(struct wl_listener *listener, void *data) { wl_list_remove(&xdg_shell_view->map.link); wl_list_remove(&xdg_shell_view->unmap.link); view->wlr_xdg_surface = NULL; + if (view->xdg_decoration) { + view->xdg_decoration->view = NULL; + } view_begin_destroy(view); } diff --git a/sway/xdg_decoration.c b/sway/xdg_decoration.c index 39e0df564..9ac87191d 100644 --- a/sway/xdg_decoration.c +++ b/sway/xdg_decoration.c @@ -10,7 +10,9 @@ static void xdg_decoration_handle_destroy(struct wl_listener *listener, void *data) { struct sway_xdg_decoration *deco = wl_container_of(listener, deco, destroy); - deco->view->xdg_decoration = NULL; + if(deco->view) { + deco->view->xdg_decoration = NULL; + } wl_list_remove(&deco->destroy.link); wl_list_remove(&deco->request_mode.link); wl_list_remove(&deco->link); From bcec866c6b383cbba1e7294f011be64425a05ff7 Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Fri, 22 Feb 2019 23:34:26 -0500 Subject: [PATCH 002/191] handle_layer_shell_surface: do not use noop output If the noop output is focused (all other outputs disabled/disconnected), do not auto assign a layer surface to it. The noop output is not enabled and does not have the `output->layers` list initialized. It also does not make sense to map the layer surfaces to something that is not visible. --- sway/desktop/layer_shell.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sway/desktop/layer_shell.c b/sway/desktop/layer_shell.c index 0767247c4..b2fea880f 100644 --- a/sway/desktop/layer_shell.c +++ b/sway/desktop/layer_shell.c @@ -378,7 +378,7 @@ void handle_layer_shell_surface(struct wl_listener *listener, void *data) { output = ws->output; } } - if (!output) { + if (!output || output == root->noop_output) { if (!root->outputs->length) { sway_log(SWAY_ERROR, "no output to auto-assign layer surface '%s' to", From cdf30033afdb4c1ead47ed208cee0c63a82b7ea4 Mon Sep 17 00:00:00 2001 From: Tim Sampson Date: Sat, 23 Feb 2019 08:27:03 +0200 Subject: [PATCH 003/191] fish: improve completions a little bit --- completions/fish/sway.fish | 1 + completions/fish/swaymsg.fish | 1 + completions/fish/swaynag.fish | 45 ++++++++++++++++++----------------- 3 files changed, 25 insertions(+), 22 deletions(-) diff --git a/completions/fish/sway.fish b/completions/fish/sway.fish index 31165ef4f..d488d49ab 100644 --- a/completions/fish/sway.fish +++ b/completions/fish/sway.fish @@ -1,5 +1,6 @@ # sway(1) completion +complete -f -c sway complete -c sway -s h -l help --description "Show help message and quit." complete -c sway -s c -l config --description "Specifies a config file." complete -c sway -s C -l validate --description "Check the validity of the config file, then exit." diff --git a/completions/fish/swaymsg.fish b/completions/fish/swaymsg.fish index fd577413f..636e35006 100644 --- a/completions/fish/swaymsg.fish +++ b/completions/fish/swaymsg.fish @@ -1,5 +1,6 @@ # swaymsg(1) completion +complete -f -c swaymsg complete -c swaymsg -s h -l help --description "Show help message and quit." complete -c swaymsg -s q -l quiet --description "Sends the IPC message but does not print the response from sway." complete -c swaymsg -s v -l version --description "Print the version (of swaymsg) and quit." diff --git a/completions/fish/swaynag.fish b/completions/fish/swaynag.fish index 784d7fade..7015d5e34 100644 --- a/completions/fish/swaynag.fish +++ b/completions/fish/swaynag.fish @@ -1,29 +1,30 @@ # swaynag -complete -c swaynag -s C -l config --description 'The config file to use. Default: $HOME/.swaylock/config, $XDG_CONFIG_HOME/swaylock/config, and SYSCONFDIR/swaylock/config.' +complete -f -c swaynag +complete -c swaynag -s C -l config -r --description 'The config file to use. Default: $HOME/.swaynag/config, $XDG_CONFIG_HOME/swaynag/config, and SYSCONFDIR/swaynag/config.' complete -c swaynag -s d -l debug --description 'Enable debugging.' -complete -c swaynag -s e -l edge --description 'Set the edge to use: top or bottom' -complete -c swaynag -s f -l font --description 'Set the font to use.' +complete -c swaynag -s e -l edge -fr --description 'Set the edge to use: top or bottom' +complete -c swaynag -s f -l font -r --description 'Set the font to use.' complete -c swaynag -s h -l help --description 'Show help message and quit.' -complete -c swaynag -s b -l button --description 'Create a button with a text and an action which is executed when pressed. Multiple buttons can be defined by providing the flag multiple times.' +complete -c swaynag -s b -l button -fr --description 'Create a button with a text and an action which is executed when pressed. Multiple buttons can be defined by providing the flag multiple times.' complete -c swaynag -s l -l detailed-message --description 'Read a detailed message from stdin. A button to toggle details will be added. Details are shown in a scrollable multi-line text area.' -complete -c swaynag -s L -l detailed-button --description 'Set the text for the button that toggles details. This has no effect if there is not a detailed message. The default is "Toggle details".' -complete -c swaynag -s m -l message --description 'Set the message text.' -complete -c swaynag -s o -l output --description 'Set the output to use.' -complete -c swaynag -s s -l dismiss-button --description 'Sets the text for the dismiss nagbar button. The default is "X".' -complete -c swaynag -s t -l type --description 'Set the message type. Two types are created by default "error" and "warning". Custom types can be defined in the config file.' +complete -c swaynag -s L -l detailed-button -fr --description 'Set the text for the button that toggles details. This has no effect if there is not a detailed message. The default is "Toggle details".' +complete -c swaynag -s m -l message -fr --description 'Set the message text.' +complete -c swaynag -s o -l output -fr --description 'Set the output to use.' +complete -c swaynag -s s -l dismiss-button -fr --description 'Sets the text for the dismiss nagbar button. The default is "X".' +complete -c swaynag -s t -l type -fr --description 'Set the message type. Two types are created by default "error" and "warning". Custom types can be defined in the config file.' complete -c swaynag -s v -l version --description 'Show the version number and quit.' # Appearance -complete -c swaynag -l background --description 'Set the color of the background.' -complete -c swaynag -l border --description 'Set the color of the border.' -complete -c swaynag -l border-bottom --description 'Set the color of the bottom border.' -complete -c swaynag -l button-background --description 'Set the color for the background for buttons.' -complete -c swaynag -l text --description 'Set the text color.' -complete -c swaynag -l border-bottom-size --description 'Set the thickness of the bottom border.' -complete -c swaynag -l message-padding --description 'Set the padding for the message.' -complete -c swaynag -l details-border-size --description 'Set the thickness for the details border.' -complete -c swaynag -l button-border-size --description 'Set the thickness for the button border.' -complete -c swaynag -l button-gap --description 'Set the size of the gap between buttons.' -complete -c swaynag -l button-dismiss-gap --description 'Set the size of the gap between the dismiss button and another button.' -complete -c swaynag -l button-margin-right --description 'Set the margin from the right of the dismiss button to edge.' -complete -c swaynag -l button-padding --description 'Set the padding for the button text.' +complete -c swaynag -l background -fr --description 'Set the color of the background.' +complete -c swaynag -l border -fr --description 'Set the color of the border.' +complete -c swaynag -l border-bottom -fr --description 'Set the color of the bottom border.' +complete -c swaynag -l button-background -fr --description 'Set the color for the background for buttons.' +complete -c swaynag -l text -fr --description 'Set the text color.' +complete -c swaynag -l border-bottom-size -fr --description 'Set the thickness of the bottom border.' +complete -c swaynag -l message-padding -fr --description 'Set the padding for the message.' +complete -c swaynag -l details-border-size -fr --description 'Set the thickness for the details border.' +complete -c swaynag -l button-border-size -fr --description 'Set the thickness for the button border.' +complete -c swaynag -l button-gap -fr --description 'Set the size of the gap between buttons.' +complete -c swaynag -l button-dismiss-gap -fr --description 'Set the size of the gap between the dismiss button and another button.' +complete -c swaynag -l button-margin-right -fr --description 'Set the margin from the right of the dismiss button to edge.' +complete -c swaynag -l button-padding -fr --description 'Set the padding for the button text.' From 9350a52c0e8b502f9dc7f876da0527607a44bfec Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Fri, 22 Feb 2019 23:09:39 -0500 Subject: [PATCH 004/191] handle_seat_node_destroy: update seat->workspace If an unmanaged or layer surface is focused when an output gets disabled and an empty workspace on the output was focused by the seat, the seat needs to refocus it's focus inactive to update the value of `seat->workspace`. --- sway/input/seat.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/sway/input/seat.c b/sway/input/seat.c index 69b048430..6482a47cb 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -146,6 +146,19 @@ static void handle_seat_node_destroy(struct wl_listener *listener, void *data) { struct sway_node *focus = seat_get_focus(seat); if (node->type == N_WORKSPACE) { + // If an unmanaged or layer surface is focused when an output gets + // disabled and an empty workspace on the output was focused by the + // seat, the seat needs to refocus it's focus inactive to update the + // value of seat->workspace. + if (seat->workspace == node->sway_workspace) { + struct sway_node *node = seat_get_focus_inactive(seat, &root->node); + seat_set_focus(seat, NULL); + if (node) { + seat_set_focus(seat, node); + } else { + seat->workspace = NULL; + } + } seat_node_destroy(seat_node); return; } From 656541bcc41cf495e87e0be17c8e61d14f586dac Mon Sep 17 00:00:00 2001 From: emersion Date: Sat, 16 Feb 2019 11:55:44 +0100 Subject: [PATCH 005/191] Update for swaywm/wlroots#1517 --- include/sway/input/seat.h | 3 ++- sway/input/cursor.c | 19 ++++++------- sway/input/seat.c | 57 ++++++++++++++++++++++++++++++++------- 3 files changed, 60 insertions(+), 19 deletions(-) diff --git a/include/sway/input/seat.h b/include/sway/input/seat.h index 1c9354df4..8fedf7972 100644 --- a/include/sway/input/seat.h +++ b/include/sway/input/seat.h @@ -75,7 +75,8 @@ struct sway_seat { struct wl_listener focus_destroy; struct wl_listener new_node; - struct wl_listener new_drag_icon; + struct wl_listener request_start_drag; + struct wl_listener start_drag; struct wl_listener request_set_selection; struct wl_listener request_set_primary_selection; diff --git a/sway/input/cursor.c b/sway/input/cursor.c index fb1edd0b0..fb4728b42 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -353,7 +353,6 @@ void cursor_send_pointer_motion(struct sway_cursor *cursor, uint32_t time_msec, } struct sway_seat *seat = cursor->seat; - struct wlr_seat *wlr_seat = seat->wlr_seat; if (seat_doing_seatop(seat)) { seatop_motion(seat, time_msec); @@ -404,10 +403,11 @@ void cursor_send_pointer_motion(struct sway_cursor *cursor, uint32_t time_msec, cursor_do_rebase(cursor, time_msec, node, surface, sx, sy); - struct wlr_drag_icon *wlr_drag_icon; - wl_list_for_each(wlr_drag_icon, &wlr_seat->drag_icons, link) { - struct sway_drag_icon *drag_icon = wlr_drag_icon->data; - drag_icon_update_position(drag_icon); + struct sway_drag_icon *drag_icon; + wl_list_for_each(drag_icon, &root->drag_icons, link) { + if (drag_icon->seat == seat) { + drag_icon_update_position(drag_icon); + } } } @@ -991,10 +991,11 @@ static void handle_touch_motion(struct wl_listener *listener, void *data) { seat->touch_x = lx; seat->touch_y = ly; - struct wlr_drag_icon *wlr_drag_icon; - wl_list_for_each(wlr_drag_icon, &wlr_seat->drag_icons, link) { - struct sway_drag_icon *drag_icon = wlr_drag_icon->data; - drag_icon_update_position(drag_icon); + struct sway_drag_icon *drag_icon; + wl_list_for_each(drag_icon, &root->drag_icons, link) { + if (drag_icon->seat == seat) { + drag_icon_update_position(drag_icon); + } } } diff --git a/sway/input/seat.c b/sway/input/seat.c index 6482a47cb..a16d3f276 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -44,7 +44,8 @@ void seat_destroy(struct sway_seat *seat) { } sway_cursor_destroy(seat->cursor); wl_list_remove(&seat->new_node.link); - wl_list_remove(&seat->new_drag_icon.link); + wl_list_remove(&seat->request_start_drag.link); + wl_list_remove(&seat->start_drag.link); wl_list_remove(&seat->request_set_selection.link); wl_list_remove(&seat->request_set_primary_selection.link); wl_list_remove(&seat->link); @@ -261,12 +262,16 @@ void drag_icon_update_position(struct sway_drag_icon *icon) { struct wlr_drag_icon *wlr_icon = icon->wlr_drag_icon; struct sway_seat *seat = icon->seat; struct wlr_cursor *cursor = seat->cursor->cursor; - if (wlr_icon->is_pointer) { + switch (wlr_icon->drag->grab_type) { + case WLR_DRAG_GRAB_KEYBOARD: + return; + case WLR_DRAG_GRAB_KEYBOARD_POINTER: icon->x = cursor->x; icon->y = cursor->y; - } else { + break; + case WLR_DRAG_GRAB_KEYBOARD_TOUCH:; struct wlr_touch_point *point = - wlr_seat_touch_get_point(seat->wlr_seat, wlr_icon->touch_id); + wlr_seat_touch_get_point(seat->wlr_seat, wlr_icon->drag->touch_id); if (point == NULL) { return; } @@ -305,9 +310,39 @@ static void drag_icon_handle_destroy(struct wl_listener *listener, void *data) { free(icon); } -static void handle_new_drag_icon(struct wl_listener *listener, void *data) { - struct sway_seat *seat = wl_container_of(listener, seat, new_drag_icon); - struct wlr_drag_icon *wlr_drag_icon = data; +static void handle_request_start_drag(struct wl_listener *listener, + void *data) { + struct sway_seat *seat = wl_container_of(listener, seat, request_start_drag); + struct wlr_seat_request_start_drag_event *event = data; + + if (wlr_seat_validate_pointer_grab_serial(seat->wlr_seat, + event->origin, event->serial)) { + wlr_seat_start_pointer_drag(seat->wlr_seat, event->drag, event->serial); + return; + } + + struct wlr_touch_point *point; + if (wlr_seat_validate_touch_grab_serial(seat->wlr_seat, + event->origin, event->serial, &point)) { + wlr_seat_start_touch_drag(seat->wlr_seat, + event->drag, event->serial, point); + return; + } + + // TODO: tablet grabs + + sway_log(SWAY_DEBUG, "Ignoring start_drag request: " + "could not validate pointer or touch serial %" PRIu32, event->serial); + wlr_data_source_destroy(event->drag->source); +} + +static void handle_start_drag(struct wl_listener *listener, void *data) { + struct sway_seat *seat = wl_container_of(listener, seat, start_drag); + struct wlr_drag *wlr_drag = data; + struct wlr_drag_icon *wlr_drag_icon = wlr_drag->icon; + if (wlr_drag_icon == NULL) { + return; + } struct sway_drag_icon *icon = calloc(1, sizeof(struct sway_drag_icon)); if (icon == NULL) { @@ -406,8 +441,12 @@ struct sway_seat *seat_create(const char *seat_name) { wl_signal_add(&root->events.new_node, &seat->new_node); seat->new_node.notify = handle_new_node; - wl_signal_add(&seat->wlr_seat->events.new_drag_icon, &seat->new_drag_icon); - seat->new_drag_icon.notify = handle_new_drag_icon; + wl_signal_add(&seat->wlr_seat->events.request_start_drag, + &seat->request_start_drag); + seat->request_start_drag.notify = handle_request_start_drag; + + wl_signal_add(&seat->wlr_seat->events.start_drag, &seat->start_drag); + seat->start_drag.notify = handle_start_drag; wl_signal_add(&seat->wlr_seat->events.request_set_selection, &seat->request_set_selection); From 4599907de7af4f5b6335399644b47f4be91bd949 Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Sun, 17 Feb 2019 10:13:11 -0500 Subject: [PATCH 006/191] swaybar: process hotspots on touch tap --- include/swaybar/bar.h | 1 + include/swaybar/input.h | 13 ++++ swaybar/input.c | 139 +++++++++++++++++++++++++++++++++++----- 3 files changed, 138 insertions(+), 15 deletions(-) diff --git a/include/swaybar/bar.h b/include/swaybar/bar.h index 2518d5aa8..dfadc200a 100644 --- a/include/swaybar/bar.h +++ b/include/swaybar/bar.h @@ -34,6 +34,7 @@ struct swaybar { struct swaybar_config *config; struct swaybar_pointer pointer; + struct swaybar_touch touch; struct status_line *status; struct loop *eventloop; diff --git a/include/swaybar/input.h b/include/swaybar/input.h index d76cd551f..88e5abc5e 100644 --- a/include/swaybar/input.h +++ b/include/swaybar/input.h @@ -22,6 +22,19 @@ struct swaybar_pointer { uint32_t serial; }; +struct touch_slot { + int32_t id; + uint32_t time; + struct swaybar_output *output; + double start_x, start_y; + double x, y; +}; + +struct swaybar_touch { + struct wl_touch *touch; + struct touch_slot slots[16]; +}; + enum hotspot_event_handling { HOTSPOT_IGNORE, HOTSPOT_PROCESS, diff --git a/swaybar/input.c b/swaybar/input.c index e416f6e73..d443c7772 100644 --- a/swaybar/input.c +++ b/swaybar/input.c @@ -123,6 +123,23 @@ static bool check_bindings(struct swaybar *bar, uint32_t button, return false; } +static void process_hotspots(struct swaybar_output *output, + double x, double y, uint32_t button) { + x *= output->scale; + y *= output->scale; + struct swaybar_hotspot *hotspot; + wl_list_for_each(hotspot, &output->hotspots, link) { + if (x >= hotspot->x && y >= hotspot->y + && x < hotspot->x + hotspot->width + && y < hotspot->y + hotspot->height) { + if (HOTSPOT_IGNORE == hotspot->callback(output, hotspot, + x / output->scale, y / output->scale, button, hotspot->data)) { + return; + } + } + } +} + static void wl_pointer_button(void *data, struct wl_pointer *wl_pointer, uint32_t serial, uint32_t time, uint32_t button, uint32_t state) { struct swaybar *bar = data; @@ -139,20 +156,7 @@ static void wl_pointer_button(void *data, struct wl_pointer *wl_pointer, if (state != WL_POINTER_BUTTON_STATE_PRESSED) { return; } - struct swaybar_hotspot *hotspot; - wl_list_for_each(hotspot, &output->hotspots, link) { - double x = pointer->x * output->scale; - double y = pointer->y * output->scale; - if (x >= hotspot->x - && y >= hotspot->y - && x < hotspot->x + hotspot->width - && y < hotspot->y + hotspot->height) { - if (HOTSPOT_IGNORE == hotspot->callback(output, hotspot, - pointer->x, pointer->y, button, hotspot->data)) { - return; - } - } - } + process_hotspots(output, pointer->x, pointer->y, button); } static void wl_pointer_axis(void *data, struct wl_pointer *wl_pointer, @@ -255,7 +259,7 @@ static void wl_pointer_axis_discrete(void *data, struct wl_pointer *wl_pointer, // Who cares } -struct wl_pointer_listener pointer_listener = { +static struct wl_pointer_listener pointer_listener = { .enter = wl_pointer_enter, .leave = wl_pointer_leave, .motion = wl_pointer_motion, @@ -267,6 +271,107 @@ struct wl_pointer_listener pointer_listener = { .axis_discrete = wl_pointer_axis_discrete, }; +static struct touch_slot *get_touch_slot(struct swaybar_touch *touch, int32_t id) { + int next = -1; + for (size_t i = 0; i < sizeof(touch->slots) / sizeof(*touch->slots); ++i) { + if (touch->slots[i].id == id) { + return &touch->slots[i]; + } + if (next == -1 && !touch->slots[i].output) { + next = i; + } + } + if (next == -1) { + sway_log(SWAY_ERROR, "Ran out of touch slots"); + return NULL; + } + return &touch->slots[next]; +} + +static void wl_touch_down(void *data, struct wl_touch *wl_touch, + uint32_t serial, uint32_t time, struct wl_surface *surface, + int32_t id, wl_fixed_t _x, wl_fixed_t _y) { + struct swaybar *bar = data; + struct swaybar_output *_output, *output; + wl_list_for_each(_output, &bar->outputs, link) { + if (_output->surface == surface) { + output = _output; + break; + } + } + if (!output) { + sway_log(SWAY_DEBUG, "Got touch event for unknown surface"); + return; + } + struct touch_slot *slot = get_touch_slot(&bar->touch, id); + if (!slot) { + return; + } + slot->id = id; + slot->output = output; + slot->x = slot->start_x = wl_fixed_to_double(_x); + slot->y = slot->start_y = wl_fixed_to_double(_y); + slot->time = time; +} + +static void wl_touch_up(void *data, struct wl_touch *wl_touch, + uint32_t serial, uint32_t time, int32_t id) { + struct swaybar *bar = data; + struct touch_slot *slot = get_touch_slot(&bar->touch, id); + if (!slot) { + return; + } + if (time - slot->time < 500) { + // Tap, treat it like a pointer click + process_hotspots(slot->output, slot->x, slot->y, BTN_LEFT); + } + slot->output = NULL; +} + +static void wl_touch_motion(void *data, struct wl_touch *wl_touch, + uint32_t time, int32_t id, wl_fixed_t x, wl_fixed_t y) { + struct swaybar *bar = data; + struct touch_slot *slot = get_touch_slot(&bar->touch, id); + if (!slot) { + return; + } + slot->x = wl_fixed_to_double(x); + slot->y = wl_fixed_to_double(y); + // TODO: Slide gestures +} + +static void wl_touch_frame(void *data, struct wl_touch *wl_touch) { + // Who cares +} + +static void wl_touch_cancel(void *data, struct wl_touch *wl_touch) { + struct swaybar *bar = data; + struct swaybar_touch *touch = &bar->touch; + for (size_t i = 0; i < sizeof(touch->slots) / sizeof(*touch->slots); ++i) { + touch->slots[i].output = NULL; + } +} + +static void wl_touch_shape(void *data, struct wl_touch *wl_touch, int32_t id, + wl_fixed_t major, wl_fixed_t minor) { + // Who cares +} + +static void wl_touch_orientation(void *data, struct wl_touch *wl_touch, + int32_t id, wl_fixed_t orientation) { + // Who cares +} + +static struct wl_touch_listener touch_listener = { + .down = wl_touch_down, + .up = wl_touch_up, + .motion = wl_touch_motion, + .frame = wl_touch_frame, + .cancel = wl_touch_cancel, + .shape = wl_touch_shape, + .orientation = wl_touch_orientation, +}; + static void seat_handle_capabilities(void *data, struct wl_seat *wl_seat, enum wl_seat_capability caps) { struct swaybar *bar = data; @@ -278,6 +383,10 @@ static void seat_handle_capabilities(void *data, struct wl_seat *wl_seat, bar->pointer.pointer = wl_seat_get_pointer(wl_seat); wl_pointer_add_listener(bar->pointer.pointer, &pointer_listener, bar); } + if ((caps & WL_SEAT_CAPABILITY_TOUCH)) { + bar->touch.touch = wl_seat_get_touch(wl_seat); + wl_touch_add_listener(bar->touch.touch, &touch_listener, bar); + } } static void seat_handle_name(void *data, struct wl_seat *wl_seat, From d1588e37390279c0e99304cda93da8c416292097 Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Sun, 17 Feb 2019 11:00:13 -0500 Subject: [PATCH 007/191] swaybar: cycle workspaces on touch drag --- swaybar/input.c | 93 +++++++++++++++++++++++++++++-------------------- 1 file changed, 56 insertions(+), 37 deletions(-) diff --git a/swaybar/input.c b/swaybar/input.c index d443c7772..c83d8c33c 100644 --- a/swaybar/input.c +++ b/swaybar/input.c @@ -159,6 +159,44 @@ static void wl_pointer_button(void *data, struct wl_pointer *wl_pointer, process_hotspots(output, pointer->x, pointer->y, button); } +static void workspace_next(struct swaybar *bar, struct swaybar_output *output, + bool left) { + struct swaybar_config *config = bar->config; + struct swaybar_workspace *first = + wl_container_of(output->workspaces.next, first, link); + struct swaybar_workspace *last = + wl_container_of(output->workspaces.prev, last, link); + + struct swaybar_workspace *active; + wl_list_for_each(active, &output->workspaces, link) { + if (active->visible) { + break; + } + } + if (!sway_assert(active->visible, "axis with null workspace")) { + return; + } + + struct swaybar_workspace *new; + if (left) { + if (active == first) { + new = config->wrap_scroll ? last : NULL; + } else { + new = wl_container_of(active->link.prev, new, link); + } + } else { + if (active == last) { + new = config->wrap_scroll ? first : NULL; + } else { + new = wl_container_of(active->link.next, new, link); + } + } + + if (new) { + ipc_send_workspace_command(bar, new->name); + } +} + static void wl_pointer_axis(void *data, struct wl_pointer *wl_pointer, uint32_t time, uint32_t axis, wl_fixed_t value) { struct swaybar *bar = data; @@ -202,39 +240,7 @@ static void wl_pointer_axis(void *data, struct wl_pointer *wl_pointer, return; } - struct swaybar_workspace *first = - wl_container_of(output->workspaces.next, first, link); - struct swaybar_workspace *last = - wl_container_of(output->workspaces.prev, last, link); - - struct swaybar_workspace *active; - wl_list_for_each(active, &output->workspaces, link) { - if (active->visible) { - break; - } - } - if (!sway_assert(active->visible, "axis with null workspace")) { - return; - } - - struct swaybar_workspace *new; - if (amt < 0.0) { - if (active == first) { - new = config->wrap_scroll ? last : NULL; - } else { - new = wl_container_of(active->link.prev, new, link); - } - } else { - if (active == last) { - new = config->wrap_scroll ? first : NULL; - } else { - new = wl_container_of(active->link.next, new, link); - } - } - - if (new) { - ipc_send_workspace_command(bar, new->name); - } + workspace_next(bar, output, amt < 0.0); // Check button release bindings check_bindings(bar, button, WL_POINTER_BUTTON_STATE_RELEASED); @@ -272,7 +278,7 @@ static struct wl_pointer_listener pointer_listener = { }; static struct touch_slot *get_touch_slot(struct swaybar_touch *touch, int32_t id) { - int next = -1; + ssize_t next = -1; for (size_t i = 0; i < sizeof(touch->slots) / sizeof(*touch->slots); ++i) { if (touch->slots[i].id == id) { return &touch->slots[i]; @@ -292,7 +298,7 @@ static void wl_touch_down(void *data, struct wl_touch *wl_touch, uint32_t serial, uint32_t time, struct wl_surface *surface, int32_t id, wl_fixed_t _x, wl_fixed_t _y) { struct swaybar *bar = data; - struct swaybar_output *_output, *output; + struct swaybar_output *_output = NULL, *output = NULL; wl_list_for_each(_output, &bar->outputs, link) { if (_output->surface == surface) { output = _output; @@ -335,9 +341,18 @@ static void wl_touch_motion(void *data, struct wl_touch *wl_touch, if (!slot) { return; } + int prev_progress = (int)((slot->x - slot->start_x) + / slot->output->width * 100); slot->x = wl_fixed_to_double(x); slot->y = wl_fixed_to_double(y); - // TODO: Slide gestures + // "progress" is a measure from 0..100 representing the fraction of the + // output the touch gesture has travelled, positive when moving to the right + // and negative when moving to the left. + int progress = (int)((slot->x - slot->start_x) + / slot->output->width * 100); + if (abs(progress) / 20 != abs(prev_progress) / 20) { + workspace_next(bar, slot->output, progress - prev_progress < 0); + } } static void wl_touch_frame(void *data, struct wl_touch *wl_touch) { @@ -362,7 +377,7 @@ static void wl_touch_orientation(void *data, struct wl_touch *wl_touch, // Who cares } -static struct wl_touch_listener touch_listener = { +static const struct wl_touch_listener touch_listener = { .down = wl_touch_down, .up = wl_touch_up, .motion = wl_touch_motion, @@ -379,6 +394,10 @@ static void seat_handle_capabilities(void *data, struct wl_seat *wl_seat, wl_pointer_release(bar->pointer.pointer); bar->pointer.pointer = NULL; } + if (bar->touch.touch != NULL) { + wl_touch_release(bar->touch.touch); + bar->touch.touch = NULL; + } if ((caps & WL_SEAT_CAPABILITY_POINTER)) { bar->pointer.pointer = wl_seat_get_pointer(wl_seat); wl_pointer_add_listener(bar->pointer.pointer, &pointer_listener, bar); From f84ac3f1148e2e25c68ed15b75ca69a694050328 Mon Sep 17 00:00:00 2001 From: Caleb Bassi Date: Sat, 23 Feb 2019 07:49:40 -0800 Subject: [PATCH 008/191] ipc: add missing fields to disabled outputs i3 requires all outputs to have certain fields, including 'primary', 'current_workspace', and 'rect' which were missing on disabled outputs. https://i3wm.org/docs/ipc.html#_outputs_reply --- sway/ipc-json.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/sway/ipc-json.c b/sway/ipc-json.c index a2ab2bba8..125df3877 100644 --- a/sway/ipc-json.c +++ b/sway/ipc-json.c @@ -251,6 +251,7 @@ json_object *ipc_json_describe_disabled_output(struct sway_output *output) { json_object_object_add(object, "name", json_object_new_string(wlr_output->name)); json_object_object_add(object, "active", json_object_new_boolean(false)); + json_object_object_add(object, "primary", json_object_new_boolean(false)); json_object_object_add(object, "make", json_object_new_string(wlr_output->make)); json_object_object_add(object, "model", @@ -259,6 +260,15 @@ json_object *ipc_json_describe_disabled_output(struct sway_output *output) { json_object_new_string(wlr_output->serial)); json_object_object_add(object, "modes", json_object_new_array()); + json_object_object_add(object, "current_workspace", NULL); + + json_object *rect_object = json_object_new_object(); + json_object_object_add(rect_object, "x", json_object_new_int(0)); + json_object_object_add(rect_object, "y", json_object_new_int(0)); + json_object_object_add(rect_object, "width", json_object_new_int(0)); + json_object_object_add(rect_object, "height", json_object_new_int(0)); + json_object_object_add(object, "rect", rect_object); + json_object_object_add(object, "percent", NULL); return object; From 713883f04cf1f2a2bafb389562a79d98b87909e9 Mon Sep 17 00:00:00 2001 From: minus Date: Fri, 22 Feb 2019 22:26:40 +0100 Subject: [PATCH 009/191] Fix crash exiting fullscreened floating container container_floating_move_to_center and container_fullscreen_disable were calling recursively when the container spawned as a fullscreen floating container (via for_window). Such a window now doesn't crash sway anymore but is still configured with a wrong, zero size, making it not directly usable. --- sway/tree/container.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/sway/tree/container.c b/sway/tree/container.c index 933907f4e..d448df22a 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -847,16 +847,9 @@ void container_floating_move_to_center(struct sway_container *con) { return; } struct sway_workspace *ws = con->workspace; - enum sway_fullscreen_mode fullscreen_mode = con->fullscreen_mode; - if (fullscreen_mode) { - container_fullscreen_disable(con); - } double new_lx = ws->x + (ws->width - con->width) / 2; double new_ly = ws->y + (ws->height - con->height) / 2; container_floating_translate(con, new_lx - con->x, new_ly - con->y); - if (fullscreen_mode) { - container_set_fullscreen(con, fullscreen_mode); - } } static bool find_urgent_iterator(struct sway_container *con, void *data) { From d4b1e71b919a52fbad0ba8191259eb4f14dfd1ab Mon Sep 17 00:00:00 2001 From: Connor E <38229097+c-edw@users.noreply.github.com> Date: Sun, 24 Feb 2019 04:39:08 +0000 Subject: [PATCH 010/191] Make load_include_configs void. Fix some cases where WD would not be restored. --- include/sway/config.h | 2 +- sway/commands/include.c | 7 ++----- sway/config.c | 37 ++++++++++++++----------------------- 3 files changed, 17 insertions(+), 29 deletions(-) diff --git a/include/sway/config.h b/include/sway/config.h index 54cdcc908..ab494e784 100644 --- a/include/sway/config.h +++ b/include/sway/config.h @@ -513,7 +513,7 @@ bool load_main_config(const char *path, bool is_active, bool validating); /** * Loads an included config. Can only be used after load_main_config. */ -bool load_include_configs(const char *path, struct sway_config *config, +void load_include_configs(const char *path, struct sway_config *config, struct swaynag_instance *swaynag); /** diff --git a/sway/commands/include.c b/sway/commands/include.c index d18098564..d4c14c35f 100644 --- a/sway/commands/include.c +++ b/sway/commands/include.c @@ -7,11 +7,8 @@ struct cmd_results *cmd_include(int argc, char **argv) { return error; } - if (!load_include_configs(argv[0], config, - &config->swaynag_config_errors)) { - return cmd_results_new(CMD_INVALID, - "Failed to include sub configuration file: %s", argv[0]); - } + // We don't care if the included config(s) fails to load. + load_include_configs(argv[0], config, &config->swaynag_config_errors); return cmd_results_new(CMD_SUCCESS, NULL); } diff --git a/sway/config.c b/sway/config.c index 206ca95cb..4cd21bbc4 100644 --- a/sway/config.c +++ b/sway/config.c @@ -549,43 +549,34 @@ static bool load_include_config(const char *path, const char *parent_dir, return true; } -bool load_include_configs(const char *path, struct sway_config *config, +void load_include_configs(const char *path, struct sway_config *config, struct swaynag_instance *swaynag) { char *wd = getcwd(NULL, 0); char *parent_path = strdup(config->current_config_path); const char *parent_dir = dirname(parent_path); if (chdir(parent_dir) < 0) { - free(parent_path); - free(wd); - return false; + sway_log(SWAY_ERROR, "failed to change working directory"); + goto cleanup; } wordexp_t p; - - if (wordexp(path, &p, 0) != 0) { - free(parent_path); - free(wd); - return false; + if (wordexp(path, &p, 0) == 0) { + char **w = p.we_wordv; + size_t i; + for (i = 0; i < p.we_wordc; ++i) { + load_include_config(w[i], parent_dir, config, swaynag); + } + wordfree(&p); } - char **w = p.we_wordv; - size_t i; - for (i = 0; i < p.we_wordc; ++i) { - load_include_config(w[i], parent_dir, config, swaynag); - } - free(parent_path); - wordfree(&p); - - // restore wd + // Attempt to restore working directory before returning. if (chdir(wd) < 0) { - free(wd); - sway_log(SWAY_ERROR, "failed to restore working directory"); - return false; + sway_log(SWAY_ERROR, "failed to change working directory"); } - +cleanup: + free(parent_path); free(wd); - return true; } void run_deferred_commands(void) { From 2510e3df384d9ab7b27e66c27371ec44606f5d8e Mon Sep 17 00:00:00 2001 From: db Date: Sun, 24 Feb 2019 10:00:15 +0100 Subject: [PATCH 011/191] add --i3 flag to hide_edge_borders Enables i3-compatible behavior regarding hiding the title bar on tabbed and stacked containers with one child. Related issues and merge requests: #3031, #3002, #2912, #2987. --- include/sway/config.h | 1 + sway/commands/hide_edge_borders.c | 10 ++++++++-- sway/config.c | 1 + sway/desktop/render.c | 8 ++++++++ sway/sway.5.scd | 6 ++++-- sway/tree/view.c | 19 +++++++++++-------- 6 files changed, 33 insertions(+), 12 deletions(-) diff --git a/include/sway/config.h b/include/sway/config.h index ab494e784..46ca7cee4 100644 --- a/include/sway/config.h +++ b/include/sway/config.h @@ -465,6 +465,7 @@ struct sway_config { int floating_border_thickness; enum edge_border_types hide_edge_borders; enum edge_border_types saved_edge_borders; + bool hide_lone_tab; // border colors struct { diff --git a/sway/commands/hide_edge_borders.c b/sway/commands/hide_edge_borders.c index 84a217b89..6120a17fa 100644 --- a/sway/commands/hide_edge_borders.c +++ b/sway/commands/hide_edge_borders.c @@ -5,10 +5,16 @@ struct cmd_results *cmd_hide_edge_borders(int argc, char **argv) { struct cmd_results *error = NULL; - if ((error = checkarg(argc, "hide_edge_borders", EXPECTED_EQUAL_TO, 1))) { + if ((error = checkarg(argc, "hide_edge_borders", EXPECTED_AT_LEAST, 1))) { return error; } + if (strcmp(*argv, "--i3") == 0) { + config->hide_lone_tab = true; + ++argv; + --argc; + } + if (strcmp(argv[0], "none") == 0) { config->hide_edge_borders = E_NONE; } else if (strcmp(argv[0], "vertical") == 0) { @@ -23,7 +29,7 @@ struct cmd_results *cmd_hide_edge_borders(int argc, char **argv) { config->hide_edge_borders = E_SMART_NO_GAPS; } else { return cmd_results_new(CMD_INVALID, "Expected 'hide_edge_borders " - "'"); + "[--i3] '"); } config->saved_edge_borders = config->hide_edge_borders; diff --git a/sway/config.c b/sway/config.c index 4cd21bbc4..48bbd1ea8 100644 --- a/sway/config.c +++ b/sway/config.c @@ -261,6 +261,7 @@ static void config_defaults(struct sway_config *config) { config->floating_border_thickness = 2; config->hide_edge_borders = E_NONE; config->saved_edge_borders = E_NONE; + config->hide_lone_tab = false; // border colors set_color(config->border_colors.focused.border, 0x4C7899); diff --git a/sway/desktop/render.c b/sway/desktop/render.c index 92e623ef4..5df160757 100644 --- a/sway/desktop/render.c +++ b/sway/desktop/render.c @@ -841,6 +841,14 @@ static void render_containers_stacked(struct sway_output *output, static void render_containers(struct sway_output *output, pixman_region32_t *damage, struct parent_data *parent) { + if (config->hide_lone_tab && parent->children->length == 1) { + struct sway_container *child = parent->children->items[0]; + if (child->view) { + render_containers_linear(output,damage, parent); + return; + } + } + switch (parent->layout) { case L_NONE: case L_HORIZ: diff --git a/sway/sway.5.scd b/sway/sway.5.scd index 1affca5fe..c0ec85cd7 100644 --- a/sway/sway.5.scd +++ b/sway/sway.5.scd @@ -493,8 +493,10 @@ The default colors are: This affects new workspaces only, and is used when the workspace doesn't have its own gaps settings (see: workspace gaps ...). -*hide_edge_borders* none|vertical|horizontal|both|smart|smart_no_gaps - Hides window borders adjacent to the screen edges. Default is _none_. +*hide_edge_borders* [--i3] none|vertical|horizontal|both|smart|smart_no_gaps + Hides window borders adjacent to the screen edges. Default is _none_. The + _--i3_ option enables i3-compatible behavior to hide the title bar on tabbed + and stacked containers with one child. *input* For details on input subcommands, see *sway-input*(5). diff --git a/sway/tree/view.c b/sway/tree/view.c index ca13def72..14cc07d9f 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -246,14 +246,17 @@ void view_autoconfigure(struct sway_view *view) { // In a tabbed or stacked container, the container's y is the top of the // title area. We have to offset the surface y by the height of the title, // bar, and disable any top border because we'll always have the title bar. - enum sway_container_layout layout = container_parent_layout(con); - if (layout == L_TABBED && !container_is_floating(con)) { - y_offset = container_titlebar_height(); - con->border_top = false; - } else if (layout == L_STACKED && !container_is_floating(con)) { - list_t *siblings = container_get_siblings(con); - y_offset = container_titlebar_height() * siblings->length; - con->border_top = false; + list_t *siblings = container_get_siblings(con); + bool show_titlebar = siblings->length > 1 || !config->hide_lone_tab; + if (show_titlebar && !container_is_floating(con)) { + enum sway_container_layout layout = container_parent_layout(con); + if (layout == L_TABBED) { + y_offset = container_titlebar_height(); + con->border_top = false; + } else if (layout == L_STACKED) { + y_offset = container_titlebar_height() * siblings->length; + con->border_top = false; + } } double x, y, width, height; From 2f7247e08a16610228067c9ec34d2b6d897e15fa Mon Sep 17 00:00:00 2001 From: Milkey Mouse Date: Sat, 23 Feb 2019 21:59:36 -0800 Subject: [PATCH 012/191] swaybar: add overlay mode (fix #1620) Overlay mode puts the bar above normal windows and passes through/ignores any touch/mouse/keyboard events that would be sent to it. --- include/swaybar/bar.h | 1 + sway/commands/bar/mode.c | 2 ++ sway/sway-bar.5.scd | 6 ++++-- swaybar/bar.c | 16 ++++++++++++++-- 4 files changed, 21 insertions(+), 4 deletions(-) diff --git a/include/swaybar/bar.h b/include/swaybar/bar.h index dfadc200a..031993b57 100644 --- a/include/swaybar/bar.h +++ b/include/swaybar/bar.h @@ -58,6 +58,7 @@ struct swaybar_output { struct zxdg_output_v1 *xdg_output; struct wl_surface *surface; struct zwlr_layer_surface_v1 *layer_surface; + struct wl_region *input_region; uint32_t wl_name; struct wl_list workspaces; // swaybar_workspace::link diff --git a/sway/commands/bar/mode.c b/sway/commands/bar/mode.c index 68a80abf6..1081ad4b8 100644 --- a/sway/commands/bar/mode.c +++ b/sway/commands/bar/mode.c @@ -20,6 +20,8 @@ static struct cmd_results *bar_set_mode(struct bar_config *bar, const char *mode bar->mode = strdup("hide"); } else if (strcasecmp("invisible", mode) == 0) { bar->mode = strdup("invisible"); + } else if (strcasecmp("overlay", mode) == 0) { + bar->mode = strdup("overlay"); } else { return cmd_results_new(CMD_INVALID, "Invalid value %s", mode); } diff --git a/sway/sway-bar.5.scd b/sway/sway-bar.5.scd index 13827e5ee..1f4ceaf2f 100644 --- a/sway/sway-bar.5.scd +++ b/sway/sway-bar.5.scd @@ -84,11 +84,13 @@ Sway allows configuring swaybar in the sway configuration file. debug-events`. To disable the default behavior for a button, use the command _nop_. -*mode* dock|hide|invisible +*mode* dock|hide|invisible|overlay Specifies the visibility of the bar. In _dock_ mode, it is permanently visible at one edge of the screen. In _hide_ mode, it is hidden unless the modifier key is pressed, though this behaviour depends on the hidden state. - In _invisible_ mode, it is permanently hidden. Default is _dock_. + In _invisible_ mode, it is permanently hidden. In _overlay_ mode, it is + permanently visible on top of other windows. (In _overlay_ mode the bar is + transparent to input events.) Default is _dock_. *hidden_state* hide|show Specifies the behaviour of the bar when it is in _hide_ mode. When the diff --git a/swaybar/bar.c b/swaybar/bar.c index db1c12228..ca7cd88c4 100644 --- a/swaybar/bar.c +++ b/swaybar/bar.c @@ -51,6 +51,9 @@ static void swaybar_output_free(struct swaybar_output *output) { if (output->surface != NULL) { wl_surface_destroy(output->surface); } + if (output->input_region != NULL) { + wl_region_destroy(output->input_region); + } zxdg_output_v1_destroy(output->xdg_output); wl_output_destroy(output->output); destroy_buffer(&output->buffers[0]); @@ -100,16 +103,25 @@ static void add_layer_surface(struct swaybar_output *output) { struct swaybar_config *config = bar->config; bool hidden = strcmp(config->mode, "hide") == 0; + bool overlay = !hidden && strcmp(config->mode, "overlay") == 0; output->layer_surface = zwlr_layer_shell_v1_get_layer_surface( bar->layer_shell, output->surface, output->output, - hidden ? ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY : + hidden || overlay ? ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY : ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM, "panel"); assert(output->layer_surface); zwlr_layer_surface_v1_add_listener(output->layer_surface, &layer_surface_listener, output); + if (overlay) { + // Empty input region + output->input_region = wl_compositor_create_region(bar->compositor); + assert(output->input_region); + + wl_surface_set_input_region(output->surface, output->input_region); + } + zwlr_layer_surface_v1_set_anchor(output->layer_surface, config->position); - if (hidden) { + if (hidden || overlay) { zwlr_layer_surface_v1_set_exclusive_zone(output->layer_surface, -1); } } From 9032be4f08a0b58da383d5d3b965719ad0ef78f1 Mon Sep 17 00:00:00 2001 From: Connor E <38229097+c-edw@users.noreply.github.com> Date: Mon, 25 Feb 2019 09:40:47 +0000 Subject: [PATCH 013/191] Allow 0 degree transform (normal transform). --- sway/commands/output/transform.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sway/commands/output/transform.c b/sway/commands/output/transform.c index 8613a8e78..8e5324adb 100644 --- a/sway/commands/output/transform.c +++ b/sway/commands/output/transform.c @@ -12,7 +12,8 @@ struct cmd_results *output_cmd_transform(int argc, char **argv) { return cmd_results_new(CMD_INVALID, "Missing transform argument."); } enum wl_output_transform transform; - if (strcmp(*argv, "normal") == 0) { + if (strcmp(*argv, "normal") == 0 || + strcmp(*argv, "0") == 0) { transform = WL_OUTPUT_TRANSFORM_NORMAL; } else if (strcmp(*argv, "90") == 0) { transform = WL_OUTPUT_TRANSFORM_90; From b00b0fb9d342ea42a0d15d258206c196b5ef9140 Mon Sep 17 00:00:00 2001 From: Carlo Abelli Date: Mon, 25 Feb 2019 14:24:02 -0500 Subject: [PATCH 014/191] fix smart_borders description in manual The wording for smart_borders was opposite the actual behavior. --- sway/sway.5.scd | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sway/sway.5.scd b/sway/sway.5.scd index c0ec85cd7..25cb9f4d0 100644 --- a/sway/sway.5.scd +++ b/sway/sway.5.scd @@ -513,9 +513,9 @@ The default colors are: *smart_borders* on|no_gaps|off If smart_borders are _on_, borders will only be enabled if the workspace - only has one visible child (identical to _hide_edge_borders_ smart). If - smart_borders is set to _no_gaps_, borders will only be enabled if the - workspace only has one visible child and gaps greater than zero. + has more than one visible child (identical to _hide_edge_borders_ smart). + If smart_borders is set to _no_gaps_, borders will only be enabled if the + workspace has more than one visible child and gaps equal to zero. *smart_gaps* on|off If smart_gaps are _on_ gaps will only be enabled if a workspace has more From efe370ba1220b11d5c736b23cb109d47483878d2 Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Tue, 26 Feb 2019 00:24:39 -0500 Subject: [PATCH 015/191] execute_command: do not strip qoutes for cmd_mode `cmd_mode` performs its own quote stripping for the mode string to avoid double stripping quotes for `cmd_bindcode` and `cmd_bindsym` in `config_command` and `execute_command`. Stripping quotes in `execute_command` for `cmd_mode` will also result in double stripping, which will cause issues for any mode string with spaces, such as pango markup. --- sway/commands.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sway/commands.c b/sway/commands.c index 3fc4f86e4..522a5fd47 100644 --- a/sway/commands.c +++ b/sway/commands.c @@ -255,7 +255,8 @@ list_t *execute_command(char *_exec, struct sway_seat *seat, int argc; char **argv = split_args(cmd, &argc); if (strcmp(argv[0], "exec") != 0 && - strcmp(argv[0], "exec_always") != 0) { + strcmp(argv[0], "exec_always") != 0 && + strcmp(argv[0], "mode") != 0) { int i; for (i = 1; i < argc; ++i) { if (*argv[i] == '\"' || *argv[i] == '\'') { From 35d610a43c01bd9a610a37564cc2de35dc88ca45 Mon Sep 17 00:00:00 2001 From: Rouven Czerwinski Date: Tue, 26 Feb 2019 20:21:33 +0100 Subject: [PATCH 016/191] cursor: remove unused node assignement The node variable is not used before its reassigned later in the function, remove the assignement. --- sway/input/cursor.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sway/input/cursor.c b/sway/input/cursor.c index fb4728b42..210e6144d 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -431,7 +431,7 @@ static void handle_cursor_motion(struct wl_listener *listener, void *data) { struct sway_node *node = NULL; double sx, sy; if (cursor->active_constraint) { - node = node_at_coords(cursor->seat, + node_at_coords(cursor->seat, cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy); if (cursor->active_constraint->surface != surface) { From 4e028dba47f924b7a9133920fe8ebad4668ce9b7 Mon Sep 17 00:00:00 2001 From: Rouven Czerwinski Date: Tue, 26 Feb 2019 20:46:25 +0100 Subject: [PATCH 017/191] cursor: intitialize sx and sy to zero If node_at_coords does an early return without setting these values, they can be used uninitialized later. Initialize both to zero. --- sway/input/cursor.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sway/input/cursor.c b/sway/input/cursor.c index 210e6144d..87811550a 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -280,7 +280,7 @@ static void cursor_do_rebase(struct sway_cursor *cursor, uint32_t time_msec, void cursor_rebase(struct sway_cursor *cursor) { uint32_t time_msec = get_current_time_msec(); struct wlr_surface *surface = NULL; - double sx, sy; + double sx = 0.0, sy = 0.0; cursor->previous.node = node_at_coords(cursor->seat, cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy); cursor_do_rebase(cursor, time_msec, cursor->previous.node, surface, sx, sy); @@ -476,7 +476,7 @@ static void cursor_motion_absolute(struct sway_cursor *cursor, dx, dy, dx, dy); struct wlr_surface *surface = NULL; - double sx, sy; + double sx = 0.0, sy = 0.0; struct sway_node *node = node_at_coords(cursor->seat, lx, ly, &surface, &sx, &sy); From d016848bcee829a9db09ed640d075bcff914f06b Mon Sep 17 00:00:00 2001 From: Rouven Czerwinski Date: Tue, 26 Feb 2019 20:49:42 +0100 Subject: [PATCH 018/191] config: remove double assignement to result in get_output_config --- sway/config/output.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sway/config/output.c b/sway/config/output.c index e7fbad833..cb889b3e8 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -378,7 +378,7 @@ static struct output_config *get_output_config(char *identifier, oc_id = config->output_configs->items[i]; } - struct output_config *result = result = new_output_config("temp"); + struct output_config *result = new_output_config("temp"); if (config->reloading) { default_output_config(result, sway_output->wlr_output); } From f876009c7f5c332af5d60b19ccbf8d991b972ffa Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Wed, 27 Feb 2019 13:23:10 -0500 Subject: [PATCH 019/191] Add sway-ipc.7.scd to document IPC protocol This add `sway-ipc.7.scd` that documents the IPC protocol. This also increased the minimum scdoc version from 1.8.1 to 1.9.0 to allow for table cells to be continued on the following line --- README.es.md | 2 +- README.md | 2 +- README.pl.md | 2 +- meson.build | 1 + sway/sway-ipc.7.scd | 1578 +++++++++++++++++++++++++++++++++++++++++ sway/sway.1.scd | 1 + sway/sway.5.scd | 2 +- swaymsg/swaymsg.1.scd | 4 + 8 files changed, 1588 insertions(+), 4 deletions(-) create mode 100644 sway/sway-ipc.7.scd diff --git a/README.es.md b/README.es.md index daa1fc093..076d89005 100644 --- a/README.es.md +++ b/README.es.md @@ -38,7 +38,7 @@ Instale las dependencias: * pango * cairo * gdk-pixbuf2 \*\* -* [scdoc](https://git.sr.ht/~sircmpwn/scdoc) >= 1.8.1 (optional: man pages) \* +* [scdoc](https://git.sr.ht/~sircmpwn/scdoc) >= 1.9.0 (optional: man pages) \* * git \* _\*Compile-time dep_ diff --git a/README.md b/README.md index e11a0dc13..aa7cd846d 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,7 @@ Install dependencies: * pango * cairo * gdk-pixbuf2 \*\* -* [scdoc](https://git.sr.ht/~sircmpwn/scdoc) >= 1.8.1 (optional: man pages) \* +* [scdoc](https://git.sr.ht/~sircmpwn/scdoc) >= 1.9.0 (optional: man pages) \* * git \* _\*Compile-time dep_ diff --git a/README.pl.md b/README.pl.md index 843fae605..ebfd9214a 100644 --- a/README.pl.md +++ b/README.pl.md @@ -39,7 +39,7 @@ Zainstaluj zależności: * pango * cairo * gdk-pixbuf2 \*\* -* [scdoc](https://git.sr.ht/~sircmpwn/scdoc) >= 1.8.1 (opcjonalnie: strony pomocy man) \* +* [scdoc](https://git.sr.ht/~sircmpwn/scdoc) >= 1.9.0 (opcjonalnie: strony pomocy man) \* * git \* _\*zależności kompilacji_ diff --git a/meson.build b/meson.build index d3172bdd8..45df87998 100644 --- a/meson.build +++ b/meson.build @@ -107,6 +107,7 @@ if scdoc.found() 'sway/sway.5.scd', 'sway/sway-bar.5.scd', 'sway/sway-input.5.scd', + 'sway/sway-ipc.7.scd', 'sway/sway-output.5.scd', 'swaymsg/swaymsg.1.scd', 'swaynag/swaynag.1.scd', diff --git a/sway/sway-ipc.7.scd b/sway/sway-ipc.7.scd new file mode 100644 index 000000000..63fbacc75 --- /dev/null +++ b/sway/sway-ipc.7.scd @@ -0,0 +1,1578 @@ +sway-ipc(7) + +# NAME + +sway-ipc - IPC protocol for sway + +# DESCRIPTION + +This details the interprocess communication (IPC) protocol for *sway*(1). This +IPC protocol can be used to control or obtain information from a sway process. + +The IPC protocol uses a UNIX socket as the method of communication. The path +to the socket is stored in the environment variable _SWAYSOCK_ and, for +backwards compatibility with i3, _I3SOCK_. You can also retrieve the socket +path by calling _sway --get-socketpath_. + +# MESSAGE AND REPLY FORMAT + +The format for messages and replies is: + +Where++ + is _i3-ipc_, for compatibility with i3++ + is a 32-bit integer in native byte order++ + is a 32-bit integer in native byte order + +For example, sending the _exit_ command would look like the following hexdump: +``` +00000000 | 69 33 2d 69 70 63 04 00 00 00 00 00 00 00 65 78 |i3-ipc........ex| +00000010 | 69 74 |it | +``` + +The payload for replies will be a valid serialized JSON data structure. + +# MESSAGES AND REPLIES + +The following message types and their corresponding reply types are currently +supported. *For all replies, any properties not listed are subject to removal.* + +[- *TYPE NUMBER* +:- *MESSAGE NAME* +:- *PURPOSE* +|- 0 +: RUN_COMMAND +:[ Runs the payload as sway commands +|- 1 +: GET_WORKSPACES +: Get the list of current workspaces +|- 2 +: SUBSCRIBE +: Subscribe the IPC connection to the events listed in the payload +|- 3 +: GET_OUTPUTS +: Get the list of current outputs +|- 4 +: GET_TREE +: Get the node layout tree +|- 5 +: GET_MARKS +: Get the names of all the marks currently set +|- 6 +: GET_BAR_CONFIG +: Get the specified bar config or a list of bar config names +|- 7 +: GET_VERSION +: Get the version of sway that owns the IPC socket +|- 8 +: GET_BINDING_MODES +: Get the list of binding mode names +|- 9 +: GET_CONFIG +: Returns the config that was last loaded +|- 10 +: SEND_TICK +: Sends a tick event with the specified payload +|- 11 +: SYNC +: Replies failure object for i3 compatibility +|- 100 +: GET_INPUTS +: Get the list of input devices +|- 101 +: GET_SEATS +: Get the list of seats + +## 0. RUN_COMMAND + +*MESSAGE*++ +Parses and runs the payload as sway commands + +*REPLY*++ +An array of objects corresponding to each command that was parsed. Each object +has the property _success_, which is a boolean indicating whether the command +was successful. The object may also contain the property _error_, which is a +human readable error message. + +*Example Reply:* +``` +[ + { + "success": true + }, + { + "success": false, + "error": "Invalid/unknown command" + } +] +``` + +## 1. GET_WORKSPACES + +*MESSAGE*++ +Retrieves the list of workspaces. + +*REPLY*++ +The reply is an array of objects corresponding to each workspace. Each object +has the following properties: + +[- *PROPERTY* +:- *DATA TYPE* +:- *DESCRIPTION* +|- num +: integer +:[ The workspace number or -1 for workspaces that do not start with a number +|- name +: string +: The name of the workspace +|- visible +: boolean +: Whether the workspace is currently visible on any output +|- focused +: boolean +: Whether the workspace is currently focused by the default seat (_seat0_) +|- urgent +: boolean +: Whether a view on the workspace has the urgent flag set +|- rect +: object +: The bounds of the workspace. It consists of _x_, _y_, _width_, and _height_ +|- output +: string +: The name of the output that the workspace is on + + +*Example Reply:* +``` +[ + { + "num": 1, + "name": "1", + "visible": true, + "focused": true, + "rect": { + "x": 0, + "y": 23, + "width": 1920, + "height": 1057 + }, + "output": "eDP-1" + }, +] +``` + +## 2. SUBSCRIBE + +*MESSAGE*++ +Subscribe this IPC connection to the event types specified in the message +payload. The payload should be a valid JSON array of events. See the _EVENTS_ +section for the list of supported events. + +*REPLY*++ +A single object that contains the property _success_, which is a boolean value +indicating whether the subscription was successful or not. + +*Example Reply:* +``` +{ + "success": true +} +``` + +## 3. GET_OUTPUTS + +*MESSAGE*++ +Retrieve the list of outputs + +*REPLY*++ +An array of objects corresponding to each output. Each object has the +following properties: + +[- *PROPERTY* +:- *DATA TYPE* +:- *DESCRIPTION* +|- name +: string +:[ The name of the output. On DRM, this is the connector +|- make +: string +: The make of the output +|- model +: string +: The model of the output +|- serial +: string +: The output's serial number as a hexadecimal string +|- active +: boolean +: Whether this output is active/enabled +|- primary +: boolean +: For i3 compatibility, this will be false. It does not make sense in Wayland +|- scale +: float +: The scale currently in use on the output or _-1_ for disabled outputs +|- transform +: string +: The transform currently in use for the output. This can be _normal_, _90_, + _180_, _270_, _flipped-90_, _flipped-180_, or _flipped-270_ +|- current_workspace +: string +: The workspace currently visible on the output or _null_ for disabled outputs +|- modes +: array +: An array of supported mode objects. Each object contains _width_, _height_, + and _refresh_ +|- current_mode +: object +: An object representing the current mode containing _width_, _height_, and + _refresh_ +|- rect +: object +: The bounds for the output consisting of _x_, _y_, _width_, and _height_ + + +*Example Reply:* +``` +[ + { + "name": "HDMI-A-2", + "make": "Unknown", + "model": "NS-19E310A13", + "serial": "0x00000001", + "active": true, + "primary": false, + "scale": 1.0, + "transform": "normal", + "current_workspace": "1", + "modes": [ + { + "width": 640, + "height": 480, + "refresh": 59940 + }, + { + "width": 800, + "height": 600, + "refresh": 60317 + }, + { + "width": 1024, + "height": 768, + "refresh": 60004 + }, + { + "width": 1920, + "height": 1080, + "refresh": 60000 + } + ], + "current_mode": { + "width": 1920, + "height": 1080, + "refresh": 60000 + } + } +] +``` + +## 4. GET_TREE + +*MESSAGE*++ +Retrieve a JSON representation of the tree + +*REPLY*++ +An array of object the represent the current tree. Each object represents one +node and will have the following properties: + +[- *PROPERTY* +:- *DATA TYPE* +:- *DESCRIPTION* +|- id +: integer +:[ The internal unique ID for this node +|- name +: string +: The name of the node such as the output name or window title. For the + scratchpad, this will be _\_\_i3\_scratch_ for compatibility with i3. +|- type +: string +: The node type. It can be _root_, _output_, _workspace_, _con_, or + _floating\_con_ +|- border +: string +: The border style for the node. It can be _normal_, _none_, _pixel_, or _csd_ +|- current_border_width +: integer +: Number of pixels used for the border width +|- layout +: string +: The node's layout. It can either be _splith_, _splitv_, _stacked_, + _tabbed_, or _output_ +|- percent +: float +: The percentage of the node's parent that it takes up or _null_ for the root + and other special nodes such as the scratchpad +|- rect +: object +: The absolute geometry of the node +|- window_rect +: object +: The geometry of the contents inside the node +|- deco_rect +: object +: The geometry of the decorations inside the node +|- geometry +: object +: The natural geometry of the contents if it were to size itself +|- urgent +: boolean +: Whether the node or any of its descendants has the urgent hint set. Note: + This may not exist when compiled without _xwayland_ support +|- focused +: boolean +: Whether the node is currently focused by the default seat (_seat0_) +|- focus +: array +: Array of child node IDs in the current focus order +|- node +: array +: The tiling children nodes for the node +|- floating_nodes +: array +: The floating children nodes for the node +|- representation +: string +: (Only workspaces) A string representation of the layout of the workspace + that can be used as an aid in submitting reproduction steps for bug reports +|- app_id +: string +: (Only views) For an xdg-shell view, the name of the application, if set. + Otherwise, _null_ +|- pid +: integer +: (Only views) The PID of the application that owns the view +|- window +: integer +: (Only xwayland views) The X11 window ID for the xwayland view +|- window_properties +: object +: (Only xwayland views) An object containing the _title_, _class_, _instance_, + _window\_role_, and _transient\_for_ for the view + + +*Example Reply:* +``` +{ + "id": 1, + "name": "root", + "rect": { + "x": 0, + "y": 0, + "width": 1920, + "height": 1080 + }, + "focused": false, + "focus": [ + 3 + ], + "border": "none", + "current_border_width": 0, + "layout": "splith", + "percent": null, + "window_rect": { + "x": 0, + "y": 0, + "width": 0, + "height": 0 + }, + "deco_rect": { + "x": 0, + "y": 0, + "width": 0, + "height": 0 + }, + "geometry": { + "x": 0, + "y": 0, + "width": 0, + "height": 0 + }, + "window": null, + "urgent": false, + "floating_nodes": [ + ], + "type": "root", + "nodes": [ + { + "id": 2147483647, + "name": "__i3", + "rect": { + "x": 0, + "y": 0, + "width": 1920, + "height": 1080 + }, + "focused": false, + "focus": [ + 2147483646 + ], + "border": "none", + "current_border_width": 0, + "layout": "output", + "percent": null, + "window_rect": { + "x": 0, + "y": 0, + "width": 0, + "height": 0 + }, + "deco_rect": { + "x": 0, + "y": 0, + "width": 0, + "height": 0 + }, + "geometry": { + "x": 0, + "y": 0, + "width": 0, + "height": 0 + }, + "window": null, + "urgent": false, + "floating_nodes": [ + ], + "type": "output", + "nodes": [ + { + "id": 2147483646, + "name": "__i3_scratch", + "rect": { + "x": 0, + "y": 0, + "width": 1920, + "height": 1080 + }, + "focused": false, + "focus": [ + ], + "border": "none", + "current_border_width": 0, + "layout": "splith", + "percent": null, + "window_rect": { + "x": 0, + "y": 0, + "width": 0, + "height": 0 + }, + "deco_rect": { + "x": 0, + "y": 0, + "width": 0, + "height": 0 + }, + "geometry": { + "x": 0, + "y": 0, + "width": 0, + "height": 0 + }, + "window": null, + "urgent": false, + "floating_nodes": [ + ], + "type": "workspace" + } + ] + }, + { + "id": 3, + "name": "eDP-1", + "rect": { + "x": 0, + "y": 0, + "width": 1920, + "height": 1080 + }, + "focused": false, + "focus": [ + 4 + ], + "border": "none", + "current_border_width": 0, + "layout": "output", + "percent": 1.0, + "window_rect": { + "x": 0, + "y": 0, + "width": 0, + "height": 0 + }, + "deco_rect": { + "x": 0, + "y": 0, + "width": 0, + "height": 0 + }, + "geometry": { + "x": 0, + "y": 0, + "width": 0, + "height": 0 + }, + "window": null, + "urgent": false, + "floating_nodes": [ + ], + "type": "output", + "active": true, + "primary": false, + "make": "Unknown", + "model": "0x38ED", + "serial": "0x00000000", + "scale": 1.0, + "transform": "normal", + "current_workspace": "1", + "modes": [ + { + "width": 1920, + "height": 1080, + "refresh": 60052 + } + ], + "current_mode": { + "width": 1920, + "height": 1080, + "refresh": 60052 + }, + "nodes": [ + { + "id": 4, + "name": "1", + "rect": { + "x": 0, + "y": 23, + "width": 1920, + "height": 1057 + }, + "focused": false, + "focus": [ + 6, + 5 + ], + "border": "none", + "current_border_width": 0, + "layout": "splith", + "percent": null, + "window_rect": { + "x": 0, + "y": 0, + "width": 0, + "height": 0 + }, + "deco_rect": { + "x": 0, + "y": 0, + "width": 0, + "height": 0 + }, + "geometry": { + "x": 0, + "y": 0, + "width": 0, + "height": 0 + }, + "window": null, + "urgent": false, + "floating_nodes": [ + ], + "num": 1, + "output": "eDP-1", + "type": "workspace", + "representation": "H[URxvt termite]", + "nodes": [ + { + "id": 5, + "name": "urxvt", + "rect": { + "x": 0, + "y": 23, + "width": 960, + "height": 1057 + }, + "focused": false, + "focus": [ + ], + "border": "normal", + "current_border_width": 2, + "layout": "none", + "percent": 0.5, + "window_rect": { + "x": 2, + "y": 0, + "width": 956, + "height": 1030 + }, + "deco_rect": { + "x": 0, + "y": 0, + "width": 960, + "height": 25 + }, + "geometry": { + "x": 0, + "y": 0, + "width": 1124, + "height": 422 + }, + "window": 4194313, + "urgent": false, + "floating_nodes": [ + ], + "type": "con", + "pid": 23959, + "app_id": null, + "window_properties": { + "class": "URxvt", + "instance": "urxvt", + "title": "urxvt", + "transient_for": null + }, + "nodes": [ + ] + }, + { + "id": 6, + "name": "", + "rect": { + "x": 960, + "y": 23, + "width": 960, + "height": 1057 + }, + "focused": true, + "focus": [ + ], + "border": "normal", + "current_border_width": 2, + "layout": "none", + "percent": 0.5, + "window_rect": { + "x": 2, + "y": 0, + "width": 956, + "height": 1030 + }, + "deco_rect": { + "x": 0, + "y": 0, + "width": 960, + "height": 25 + }, + "geometry": { + "x": 0, + "y": 0, + "width": 817, + "height": 458 + }, + "window": null, + "urgent": false, + "floating_nodes": [ + ], + "type": "con", + "pid": 25370, + "app_id": "termite", + "nodes": [ + ] + } + ] + } + ] + } + ] +} +``` + +## 5. GET_MARKS + +*MESSAGE*++ +Retrieve the currently set marks + +*REPLY*++ +An array of marks current set. Since each mark can only be set for one +container, this is a set so each value is unique and the order is undefined. + +*Example Reply:* +``` +[ + "one", + "test" +] +``` + +## 6. GET_BAR_CONFIG (WITHOUT A PAYLOAD) + +*MESSAGE*++ +When sending without a payload, this retrieves the list of configured bar IDs + +*REPLY*++ +An array of bar IDs, which are strings + +*Example Reply:* +``` +[ + "bar-0", + "bar-1" +] +``` + +## 6. GET_BAR_CONFIG (WITH A PAYLOAD) + +*MESSAGE*++ +When sent with a bar ID as the payload, this retrieves the config associated +with the specified by the bar ID in the payload. This is used by swaybar, but +could also be used for third party bars + +*REPLY*++ +An object that represents the configuration for the bar with the bar ID sent as +the payload. The following properties exists and more information about what +their value mean can be found in *sway-bar*(5): + +[- *PROPERTY* +:- *DATA TYPE* +:- *DESCRIPTION* +|- id +: string +:[ The bar ID +|- mode +: string +: The mode for the bar. It can be _dock_, _hide_, or _invisible_ +|- position +: string +: The bar's position. It can currently either be _bottom_ or _top_ +|- status_command +: string +: The command which should be run to generate the status line +|- font +: string +: The font to use for the text on the bar +|- workspace_buttons +: boolean +: Whether to display the workspace buttons on the bar +|- binding_mode_indicator +: boolean +: Whether to display the current binding mode on the bar +|- verbose +: boolean +: For i3 compatibility, this will be the boolean value _false_. +|- colors +: object +: An object containing the _#RRGGBBAA_ colors to use for the bar. See below + for more information +|- gaps +: object +: An object representing the gaps for the bar consisting of _top_, _right_, + _bottom_, and _left_. +|- bar_height +: integer +: The absolute height to use for the bar or _0_ to automatically size based + on the font +|- status_padding +: integer +: The vertical padding to use for the status line +|- status_edge_padding +: integer +: The horizontal padding to use for the status line when at the end of an + output + + +The colors object contains the following properties, which are all strings +containing the _#RRGGBBAA_ representation of the color: +[- *PROPERTY* +:- *DESCRIPTION* +|- background +:[ The color to use for the bar background on unfocused outputs +|- statusline +: The color to use for the status line text on unfocused outputs +|- separator +: The color to use for the separator text on unfocused outputs +|- focused_background +: The color to use for the background of the bar on the focused output +|- focused_statusline +: The color to use for the status line text on the focused output +|- focused_separator +: The color to use for the separator text on the focused output +|- focused_workspace_text +: The color to use for the text of the focused workspace button +|- focused_workspace_bg +: The color to use for the background of the focused workspace button +|- focused_workspace_border +: The color to use for the border of the focused workspace button +|- active_workspace_text +: The color to use for the text of the workspace buttons for the visible + workspaces on unfocused outputs +|- active_workspace_bg +: The color to use for the background of the workspace buttons for the visible + workspaces on unfocused outputs +|- active_workspace_border +: The color to use for the border of the workspace buttons for the visible + workspaces on unfocused outputs +|- inactive_workspace_text +: The color to use for the text of the workspace buttons for workspaces that + are not visible +|- inactive_workspace_bg +: The color to use for the background of the workspace buttons for workspaces + that are not visible +|- inactive_workspace_border +: The color to use for the border of the workspace buttons for workspaces + that are not visible +|- urgent_workspace_text +: The color to use for the text of the workspace buttons for workspaces that + contain an urgent view +|- urgent_workspace_bg +: The color to use for the background of the workspace buttons for workspaces + that contain an urgent view +|- urgent_workspace_border +: The color to use for the border of the workspace buttons for workspaces that + contain an urgent view +|- binding_mode_text +: The color to use for the text of the binding mode indicator +|- binding_mode_bg +: The color to use for the background of the binding mode indicator +|- binding_mode_border +: The color to use for the border of the binding mode indicator + + +*Example Reply:* +``` +{ + "id": "bar-0", + "mode": "dock", + "position": "top", + "status_command": "while date +'%Y-%m-%d %l:%M:%S %p'; do sleep 1; done", + "font": "monospace 10", + "gaps": { + "top": 0, + "right": 0, + "bottom": 0, + "left": 0 + }, + "bar_height": 0, + "status_padding": 1, + "status_edge_padding": 3, + "workspace_buttons": true, + "binding_mode_indicator": true, + "verbose": false, + "pango_markup": false, + "colors": { + "background": "#323232ff", + "statusline": "#ffffffff", + "separator": "#666666ff", + "focused_background": "#323232ff", + "focused_statusline": "#ffffffff", + "focused_separator": "#666666ff", + "focused_workspace_border": "#4c7899ff", + "focused_workspace_bg": "#285577ff", + "focused_workspace_text": "#ffffffff", + "inactive_workspace_border": "#32323200", + "inactive_workspace_bg": "#32323200", + "inactive_workspace_text": "#5c5c5cff", + "active_workspace_border": "#333333ff", + "active_workspace_bg": "#5f676aff", + "active_workspace_text": "#ffffffff", + "urgent_workspace_border": "#2f343aff", + "urgent_workspace_bg": "#900000ff", + "urgent_workspace_text": "#ffffffff", + "binding_mode_border": "#2f343aff", + "binding_mode_bg": "#900000ff", + "binding_mode_text": "#ffffffff" + }, +} +``` + +## 7. GET_VERSION + +*MESSAGE*++ +Retrieve version information about the sway process + +*REPLY*++ +An object containing the following properties: + +[- *PROPERTY* +:- *DATA TYPE* +:- *DESCRIPTION* +|- major +: integer +:[ The major version of the sway process +|- minor +: integer +: The minor version of the sway process +|- patch +: integer +: The patch version of the sway process +|- human_readable +: string +: A human readable version string that will likely contain more useful + information such as the git commit short hash and git branch +|- loaded_config_file_name +: string +: The path to the loaded config file + + +*Example Reply:* +``` +{ + "human_readable": "1.0-rc1-117-g2f7247e0 (Feb 24 2019, branch 'master')", + "major": 1, + "minor": 0, + "patch": 0, + "loaded_config_file_name": "/home/redsoxfan/.config/sway/config" +} +``` + +## 8. GET_BINDING_MODES + +*MESSAGE*++ +Retrieve the list of binding modes that currently configured + +*REPLY*++ +An array of strings, with each string being the name of a binding mode. This +will always contain at least one mode (currently _default_), which is the +default binding mode + +*Example Reply:* +``` +[ + "default", + "resize", +] +``` + +## 9. GET_CONFIG + +*MESSAGE*++ +Retrieve the contents of the config that was last loaded + +*REPLY*++ +An object with a single string property containing the contents of the config + +*Example Reply:* +``` +{ + "config": "set $mod Mod4\nbindsym $mod+q exit\n" +} +``` + +## 10. SEND_TICK + +*MESSAGE*++ +Issues a _TICK_ event to all clients subscribing to the event to ensure that +all events prior to the tick were received. If a payload is given, it will be +included in the _TICK_ event + +*REPLY*++ +A single object contains the property _success_, which is a boolean value +indicating whether the _TICK_ event was sent. + +*Example Reply:* +``` +{ + "success": true +} +``` + +## 11. SYNC + +*MESSAGE*++ +For i3 compatibility, this command will just return a failure object since it +does not make sense to implement in sway due to the X11 nature of the command. +If you are curious about what this IPC command does in i3, refer to the i3 +documentation. + +*REPLY*++ +A single object that contains the property _success_, which is set to the +boolean value _false_. + +*Exact Reply:* +``` +{ + "success": false +} +``` + +## 100. GET_INPUTS + +*MESSAGE*++ +Retrieve a list of the input devices currently available + +*REPLY*++ +An array of objects corresponding to each input device. Each object has the +following properties: + +[- *PROPERTY* +:- *DATA TYPE* +:- *DESCRIPTION* +|- identifier +: string +:[ The identifier for the input device +|- name +: string +: The human readable name for the device +|- vendor +: integer +: The vendor code for the input device +|- product +: integer +: The product code for the input device +|- type +: string +: The device type. Currently this can be _keyboard_, _pointer_, _touch_, + _tablet\_tool_, _tablet\_pad_, or _switch_ +|- xkb_active_layout_name +: string +: (Only keyboards) The active keyboard layout in use +|- libinput_send_events +: string +: (Only libinput devices) The send events value in use by libinput for this + device. It can be _enabled_, _disabled_, or _disabled\_on\_external\_mouse_ + + +*Example Reply:* +``` +[ + { + "identifier": "1:1:AT_Translated_Set_2_keyboard", + "name": "AT Translated Set 2 keyboard", + "vendor": 1, + "product": 1, + "type": "keyboard", + "xkb_active_layout_name": "English (US)", + "libinput_send_events": "enabled" + }, + { + "identifier": "1267:5:Elan_Touchpad", + "name": "Elan Touchpad", + "vendor": 1267, + "product": 5, + "type": "pointer", + "libinput_send_events": "enabled" + }, + { + "identifier": "3034:22494:USB2.0_VGA_UVC_WebCam:_USB2.0_V", + "name": "USB2.0 VGA UVC WebCam: USB2.0 V", + "vendor": 3034, + "product": 22494, + "type": "keyboard", + "xkb_active_layout_name": "English (US)", + "libinput_send_events": "enabled" + }, + { + "identifier": "0:3:Sleep_Button", + "name": "Sleep Button", + "vendor": 0, + "product": 3, + "type": "keyboard", + "xkb_active_layout_name": "English (US)", + "libinput_send_events": "enabled" + }, + { + "identifier": "0:5:Lid_Switch", + "name": "Lid Switch", + "vendor": 0, + "product": 5, + "type": "switch", + "libinput_send_events": "enabled" + { + "identifier": "0:6:Video_Bus", + "name": "Video Bus", + "vendor": 0, + "product": 6, + "type": "keyboard", + "xkb_active_layout_name": "English (US)", + "libinput_send_events": "enabled" + }, + { + "identifier": "0:1:Power_Button", + "name": "Power Button", + "vendor": 0, + "product": 1, + "type": "keyboard", + "xkb_active_layout_name": "English (US)", + "libinput_send_events": "enabled" + } +] +``` + +## 101. GET_SEATS + +*MESSAGE*++ +Retrieve a list of the seats currently configured + +*REPLY*++ +An array of objects corresponding to each seat. There will always be at least +one seat. Each object has the following properties: + +[- *PROPERTY* +:- *DATA TYPE* +:- *DESCRIPTION* +|- name +: string +:] The unique name for the seat +|- capabilities +: integer +: The number of capabilities that the seat has +|- focus +: integer +: The id of the node currently focused by the seat or _0_ when the seat is + not currently focused by a node (i.e. a surface layer or xwayland unmanaged + has focus) +|- devices +: array +: An array of input devices that are attached to the seat. Currently, this + is an array of objects that are identical to those returned by _GET\_INPUTS_ + + +*Example Reply:* +``` +[ + { + "name": "seat0", + "capabilities": 3, + "focus": 7, + "devices": [ + { + "identifier": "1:1:AT_Translated_Set_2_keyboard", + "name": "AT Translated Set 2 keyboard", + "vendor": 1, + "product": 1, + "type": "keyboard", + "xkb_active_layout_name": "English (US)", + "libinput_send_events": "enabled" + }, + { + "identifier": "1267:5:Elan_Touchpad", + "name": "Elan Touchpad", + "vendor": 1267, + "product": 5, + "type": "pointer", + "libinput_send_events": "enabled" + }, + { + "identifier": "3034:22494:USB2.0_VGA_UVC_WebCam:_USB2.0_V", + "name": "USB2.0 VGA UVC WebCam: USB2.0 V", + "vendor": 3034, + "product": 22494, + "type": "keyboard", + "xkb_active_layout_name": "English (US)", + "libinput_send_events": "enabled" + }, + { + "identifier": "0:3:Sleep_Button", + "name": "Sleep Button", + "vendor": 0, + "product": 3, + "type": "keyboard", + "xkb_active_layout_name": "English (US)", + "libinput_send_events": "enabled" + }, + { + "identifier": "0:5:Lid_Switch", + "name": "Lid Switch", + "vendor": 0, + "product": 5, + "type": "switch", + "libinput_send_events": "enabled" + { + "identifier": "0:6:Video_Bus", + "name": "Video Bus", + "vendor": 0, + "product": 6, + "type": "keyboard", + "xkb_active_layout_name": "English (US)", + "libinput_send_events": "enabled" + }, + { + "identifier": "0:1:Power_Button", + "name": "Power Button", + "vendor": 0, + "product": 1, + "type": "keyboard", + "xkb_active_layout_name": "English (US)", + "libinput_send_events": "enabled" + } + ] + } +] +``` + +# EVENTS + +Events are a way for client to get notified of changes to sway. A client can +subscribe to any events it wants to be notified of changes for. The event is +sent in the same format as a reply. The following events are currently +available: + +[- *EVENT TYPE* +:- *NAME* +:- *DESCRIPTION* +|- 0x80000000 +: workspace +:[ Sent whenever an event involving a workspace occurs such as initialization + of a new workspace or a different workspace gains focus +|- 0x80000002 +: mode +: Sent whenever the binding mode changes +|- 0x80000003 +: window +: Sent whenever an event involving a view occurs such as being reparented, + focused, or closed +|- 0x80000004 +: barconfig_update +: Sent whenever a bar config changes +|- 0x80000005 +: binding +: Sent when a configured binding is executed +|- 0x80000006 +: shutdown +: Sent when the ipc shuts down because sway is exiting +|- 0x80000007 +: tick +: Sent when an ipc client sends a _SEND\_TICK_ message +|- 0x80000014 +: bar_status_update +: Send when the visibility of a bar should change due to a modifier + + +## 0x80000000. WORKSPACE + +Sent whenever a change involving a workspace occurs. The event consists of a +single object with the following properties: + +[- *PROPERTY* +:- *DATA TYPE* +:- *DESCRIPTION* +|- change +: string +:[ The type of change that occurred. See below for more information +|- current +: object +: An object representing the workspace effected or _null_ for _reload_ changes +|- old +: object +: For a _focus_ change, this is will be an object representing the workspace + being switched from. Otherwise, it is _null_ + + +The following change types are currently available: +[- *TYPE* +:- *DESCRIPTION* +|- init +:[ The workspace was created +|- empty +: The workspace is empty and is being destroyed since it is not visible +|- focus +: The workspace was focused. See the _old_ property for the previous focus +|- move +: The workspace was moved to a different output +|- rename +: The workspace was renamed +|- urgent +: A view on the workspace has had their urgency hint set or all urgency hints + for views on the workspace have been cleared +|- reload +: The configuration file has been reloaded + + +*Example Event:* +``` +{ + "change": "init", + "old": null, + "current": { + "id": 10, + "name": "2", + "rect": { + "x": 0, + "y": 0, + "width": 0, + "height": 0 + }, + "focused": false, + "focus": [ + ], + "border": "none", + "current_border_width": 0, + "layout": "splith", + "percent": null, + "window_rect": { + "x": 0, + "y": 0, + "width": 0, + "height": 0 + }, + "deco_rect": { + "x": 0, + "y": 0, + "width": 0, + "height": 0 + }, + "geometry": { + "x": 0, + "y": 0, + "width": 0, + "height": 0 + }, + "window": null, + "urgent": false, + "floating_nodes": [ + ], + "num": 2, + "output": "eDP-1", + "type": "workspace", + "representation": null, + "nodes": [ + ] + } +} +``` + +## 0x80000002. MODE + +Sent whenever the binding mode changes. The event consists of a single object +with the following properties: + +[- *PROPERTY* +:- *DATA TYPE* +:- *DESCRIPTION* +|- change +: string +:[ The binding mode that became active +|- pango_markup +: boolean +: Whether the mode should be parsed as pango markup + + +*Example Event:* +``` +{ + "change": "default", + "pango_markup": false +} +``` + +## 0x80000003. WINDOW + +Sent whenever a change involving a view occurs. The event consists of a single +object with the following properties: + +[- *PROPERTY* +:- *DATA TYPE* +:- *DESCRIPTION* +|- change +: string +:[ The type of change that occurred. See below for more information +|- container +: object +: An object representing the view effected + + +The following change types are currently available: +[- *TYPE* +:- *DESCRIPTION* +|- new +:[ The view was created +|- close +: The view was closed +|- focus +: The view was focused +|- title +: The view's title has changed +|- fullscreen_mode +: The view's fullscreen mode has changed +|- move +: The view has been reparented in the tree +|- floating +: The view has become floating or is no longer floating +|- urgent +: The view's urgency hint has changed status +|- mark +: A mark has been added or removed from the view + + +*Example Event:* +``` +{ + "change": "new", + "container": { + "id": 12, + "name": null, + "rect": { + "x": 0, + "y": 0, + "width": 0, + "height": 0 + }, + "focused": false, + "focus": [ + ], + "border": "none", + "current_border_width": 0, + "layout": "none", + "percent": 0.0, + "window_rect": { + "x": 0, + "y": 0, + "width": 0, + "height": 0 + }, + "deco_rect": { + "x": 0, + "y": 0, + "width": 0, + "height": 0 + }, + "geometry": { + "x": 0, + "y": 0, + "width": 1124, + "height": 422 + }, + "window": 4194313, + "urgent": false, + "floating_nodes": [ + ], + "type": "con", + "pid": 19787, + "app_id": null, + "window_properties": { + "class": "URxvt", + "instance": "urxvt", + "transient_for": null + }, + "nodes": [ + ] + } +} +``` + +## 0x80000004. BARCONFIG_UPDATE + +Sent whenever a config for a bar changes. The event is identical to that of +_GET_BAR_CONFIG_ when a bar ID is given as a payload. See _6. GET\_BAR\_CONFIG +(WITH A PAYLOAD)_ above for more information. + +## 0x80000005. BINDING + +Sent whenever a binding is executed. The event is a single object with the +following properties: + +[- *PROPERTY* +:- *DATA TYPE* +:- *DESCRIPTION* +|- change +: string +:[ The change that occurred for the binding. Currently this will only be _run_ +|- command +: string +: The command associated with the binding +|- event_state_mask +: array +: An array of strings that correspond to each modifier key for the binding +|- input_code +: integer +: For keyboard bindcodes, this is the key code for the binding. For mouse + bindings, this is the X11 button number, if there is an equivalent. In all + other cases, this will be _0_. +|- symbol +: string +: For keyboard bindsyms, this is the bindsym for the binding. Otherwise, this + will be _null_ +|- input_type +: string +: The input type that triggered the binding. This is either _keyboard_ or + _mouse_ + + +*Example Event:* +``` +{ + "change": "run", + "binding": { + "command": "workspace 2", + "event_state_mask": [ + "Mod4" + ], + "input_code": 0, + "symbol": "2", + "input_type": "keyboard" + } +} +``` + +## 0x80000006. SHUTDOWN + +Sent whenever the IPC is shutting down. The event is a single object with the +property _change_, which is a string containing the reason for the shutdown. +Currently, the only value for _change_ is _exit_, which is issued when sway is +exiting. + +*Example Event:* +``` +{ + "change": "exit" +} +``` + +## 0x80000007. TICK + +Sent when first subscribing to tick events or by a _SEND\_TICK_ message. The +event is a single object with the following properties: + +[- *PROPERTY* +:- *DATA TYPE* +:- *DESCRIPTION* +|- first +: boolean +: Whether this event was triggered by subscribing to the tick events +|- payload +: string +: The payload given with a _SEND\_TICK_ message, if any. Otherwise, an empty + string + + +*Example Event:* +``` +{ + "first": true + "payload": "" +} +``` + +## 0x80000014. BAR_STATUS_UPDATE + +Sent when the visibility of a bar changes due to a modifier being pressed. The +event is a single object with the following properties: + +[- *PROPERTY* +:- *DATA TYPE* +:- *DESCRIPTION* +|- id +: string +:[ The bar ID effected +|- visible_by_modifier +: boolean +: Whether the bar should be made visible due to a modifier being pressed + + +*Example Event:* +``` +{ + "id": "bar-0", + "visible_by_modifier": true +} +``` + +# SEE ALSO + +*sway*(1) *sway*(5) *sway-bar*(5) *swaymsg*(1) *sway-input*(5) *sway-output*(5) diff --git a/sway/sway.1.scd b/sway/sway.1.scd index bce635270..cfb4817a1 100644 --- a/sway/sway.1.scd +++ b/sway/sway.1.scd @@ -89,3 +89,4 @@ source contributors. For more information about sway development, see # SEE ALSO *sway*(5) *swaymsg*(1) *sway-input*(5) *sway-output*(5) *sway-bar*(5) +*sway-ipc*(7) diff --git a/sway/sway.5.scd b/sway/sway.5.scd index 1affca5fe..1b85a75bc 100644 --- a/sway/sway.5.scd +++ b/sway/sway.5.scd @@ -748,4 +748,4 @@ The following attributes may be matched with: # SEE ALSO -*sway*(1) *sway-input*(5) *sway-output*(5) *sway-bar*(5) +*sway*(1) *sway-input*(5) *sway-output*(5) *sway-bar*(5) *sway-ipc*(7) diff --git a/swaymsg/swaymsg.1.scd b/swaymsg/swaymsg.1.scd index f55f86a9d..523db6cc0 100644 --- a/swaymsg/swaymsg.1.scd +++ b/swaymsg/swaymsg.1.scd @@ -82,3 +82,7 @@ _swaymsg_ [options...] [message] Subscribe to a list of event types. The argument for this type should be provided in the form of a valid JSON array. If any of the types are invalid or if an valid JSON array is not provided, this will result in an failure. + +# SEE ALSO + +*sway*(5) *sway-bar*(5) *sway-input*(5) *sway-output*(5) *sway-ipc*(7) From 8228fa60d4e03b4b9f541cdc411d28c106cef2d3 Mon Sep 17 00:00:00 2001 From: emersion Date: Wed, 27 Feb 2019 21:13:16 +0100 Subject: [PATCH 020/191] Set minimum wlroots version --- meson.build | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/meson.build b/meson.build index d3172bdd8..aaffc41fa 100644 --- a/meson.build +++ b/meson.build @@ -60,17 +60,19 @@ rt = cc.find_library('rt') git = find_program('git', required: false) # Try first to find wlroots as a subproject, then as a system dependency +wlroots_version = '>=0.4.1' wlroots_proj = subproject( 'wlroots', default_options: ['rootston=false', 'examples=false'], required: false, + version: wlroots_version, ) if wlroots_proj.found() wlroots = wlroots_proj.get_variable('wlroots') wlroots_conf = wlroots_proj.get_variable('conf_data') wlroots_has_xwayland = wlroots_conf.get('WLR_HAS_XWAYLAND') == 1 else - wlroots = dependency('wlroots') + wlroots = dependency('wlroots', version: wlroots_version) wlroots_has_xwayland = cc.get_define('WLR_HAS_XWAYLAND', prefix: '#include ', dependencies: wlroots) == '1' endif From 6658d69271daa932eeeec9f35bf7b81f84992ba5 Mon Sep 17 00:00:00 2001 From: emersion Date: Wed, 27 Feb 2019 21:26:50 +0100 Subject: [PATCH 021/191] Print Meson features --- meson.build | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/meson.build b/meson.build index d3172bdd8..3c3c1f41f 100644 --- a/meson.build +++ b/meson.build @@ -93,11 +93,6 @@ conf_data.set10('HAVE_SYSTEMD', systemd.found()) conf_data.set10('HAVE_ELOGIND', elogind.found()) conf_data.set10('HAVE_TRAY', have_tray) -if not systemd.found() and not elogind.found() - warning('The sway binary must be setuid when compiled without (e)logind') - warning('You must do this manually post-install: chmod a+s /path/to/sway') -endif - scdoc = find_program('scdoc', required: get_option('man-pages')) if scdoc.found() sh = find_program('sh') @@ -235,3 +230,21 @@ if get_option('fish-completions') install_data(fish_files, install_dir: fish_install_dir) endif + +status = [ + '', + 'Features:', + 'xwayland: @0@'.format(have_xwayland), + 'gdk-pixbuf: @0@'.format(gdk_pixbuf.found()), + 'systemd: @0@'.format(systemd.found()), + 'elogind: @0@'.format(elogind.found()), + 'tray: @0@'.format(have_tray), + 'man-pages: @0@'.format(scdoc.found()), + '', +] +message('\n'.join(status)) + +if not systemd.found() and not elogind.found() + warning('The sway binary must be setuid when compiled without (e)logind') + warning('You must do this manually post-install: chmod a+s /path/to/sway') +endif From 4431ae68fcfcc12bd41f2b2daf4cb2b34a6f5547 Mon Sep 17 00:00:00 2001 From: emersion Date: Wed, 27 Feb 2019 11:48:30 +0100 Subject: [PATCH 022/191] Add output dpms to manpage --- sway/sway-output.5.scd | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/sway/sway-output.5.scd b/sway/sway-output.5.scd index 517f69465..bb3745f36 100644 --- a/sway/sway-output.5.scd +++ b/sway/sway-output.5.scd @@ -51,7 +51,7 @@ must be separated by one space. For example: output eDP1 pos 1600 0 res 1920x1080 - Note that the left x-pos of eDP1 is 1600 = 3200/2 and the bottom y-pos is + Note that the left x-pos of eDP1 is 1600 = 3200/2 and the bottom y-pos is 1020 + (1800 / 2) = 1920 = 0 + 1920 *output* scale @@ -69,8 +69,7 @@ must be separated by one space. For example: given scaling mode (one of "stretch", "fill", "fit", "center", "tile"). If the specified file cannot be accessed or if the image does fill the entire output, a fallback color may be provided to cover the rest of the output. - __fallback_color__ should be specified as _#RRGGBB_. Alpha is not - supported. + __fallback_color__ should be specified as _#RRGGBB_. Alpha is not supported. *output* background|bg solid_color Sets the background of the given output to the specified color. _color_ @@ -80,14 +79,18 @@ must be separated by one space. For example: Sets the background transform to the given value. Can be one of "90", "180", "270" for rotation; or "flipped", "flipped-90", "flipped-180", "flipped-270" to apply a rotation and flip, or "normal" to apply no transform. If a single - output is chosen and a rotation direction is specified - (_clockwise_ or _anticlockwise_) then the transform is added or - subtracted from the current transform. + output is chosen and a rotation direction is specified (_clockwise_ or + _anticlockwise_) then the transform is added or subtracted from the current + transform. *output* disable|enable Enables or disables the specified output (all outputs are enabled by default). +*output* dpms on|off + Enables or disables the specified output via DPMS. To turn an output off + (ie. blank the screen but keep workspaces as-is), one can set DPMS to off. + # SEE ALSO *sway*(5) *sway-input*(5) From 56217bfbc01ca8c244ccbcd3f5b60227f41f5e36 Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Wed, 27 Feb 2019 22:11:58 -0500 Subject: [PATCH 023/191] sway-ipc.7: clarify window_rect omits decorations According to the i3 ipc documentation, `window_rect` excludes the window decorations from the calculation. This just clarifies that in `sway-ipc.7.scd` --- sway/sway-ipc.7.scd | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sway/sway-ipc.7.scd b/sway/sway-ipc.7.scd index 63fbacc75..d598a54fd 100644 --- a/sway/sway-ipc.7.scd +++ b/sway/sway-ipc.7.scd @@ -317,7 +317,8 @@ node and will have the following properties: : The absolute geometry of the node |- window_rect : object -: The geometry of the contents inside the node +: The geometry of the contents inside the node. The window decorations are + excluded from this calculation, but borders are included. |- deco_rect : object : The geometry of the decorations inside the node From 416c6ecb99f90a7c84cce0b106401652692a4681 Mon Sep 17 00:00:00 2001 From: Ian Fan Date: Thu, 28 Feb 2019 12:02:14 +0000 Subject: [PATCH 024/191] tray: fix pixmap colors by converting from network byte order to host byte order --- swaybar/tray/item.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/swaybar/tray/item.c b/swaybar/tray/item.c index 4fa6c97b5..027b3001f 100644 --- a/swaybar/tray/item.c +++ b/swaybar/tray/item.c @@ -1,4 +1,5 @@ #define _POSIX_C_SOURCE 200809L +#include #include #include #include @@ -76,7 +77,12 @@ static int read_pixmap(sd_bus_message *msg, struct swaybar_sni *sni, struct swaybar_pixmap *pixmap = malloc(sizeof(struct swaybar_pixmap) + npixels); pixmap->size = height; - memcpy(pixmap->pixels, pixels, npixels); + + // convert from network byte order to host byte order + for (int i = 0; i < height * width; ++i) { + ((uint32_t *)pixmap->pixels)[i] = ntohl(((uint32_t *)pixels)[i]); + } + list_add(pixmaps, pixmap); } else { sway_log(SWAY_DEBUG, "%s %s: discard invalid icon w:%d h:%d", sni->watcher_id, prop, width, height); From 88b283c55713c45968e2df5f8b89a40a0b32f720 Mon Sep 17 00:00:00 2001 From: emersion Date: Thu, 28 Feb 2019 19:22:47 +0100 Subject: [PATCH 025/191] seat: don't send button release when not pressed All seat operations except "down" eat the button pressed event and don't send it to clients. Thus, when ending such seat operations we shouldn't send the button released event. This commit moves the logic used to send pressed/released into the "down" operation. --- include/sway/input/seat.h | 9 ++++----- sway/input/cursor.c | 6 ++---- sway/input/seat.c | 4 ++-- sway/input/seatop_down.c | 9 ++++++--- sway/input/seatop_move_floating.c | 2 +- sway/input/seatop_move_tiling.c | 2 +- sway/input/seatop_resize_floating.c | 2 +- sway/input/seatop_resize_tiling.c | 2 +- 8 files changed, 18 insertions(+), 18 deletions(-) diff --git a/include/sway/input/seat.h b/include/sway/input/seat.h index 8fedf7972..0f5dab983 100644 --- a/include/sway/input/seat.h +++ b/include/sway/input/seat.h @@ -10,7 +10,7 @@ struct sway_seat; struct sway_seatop_impl { void (*motion)(struct sway_seat *seat, uint32_t time_msec); - void (*finish)(struct sway_seat *seat); + void (*finish)(struct sway_seat *seat, uint32_t time_msec); void (*abort)(struct sway_seat *seat); void (*unref)(struct sway_seat *seat, struct sway_container *con); void (*render)(struct sway_seat *seat, struct sway_output *output, @@ -185,8 +185,8 @@ bool seat_is_input_allowed(struct sway_seat *seat, struct wlr_surface *surface); void drag_icon_update_position(struct sway_drag_icon *icon); -void seatop_begin_down(struct sway_seat *seat, - struct sway_container *con, uint32_t button, int sx, int sy); +void seatop_begin_down(struct sway_seat *seat, struct sway_container *con, + uint32_t time_msec, uint32_t button, int sx, int sy); void seatop_begin_move_floating(struct sway_seat *seat, struct sway_container *con, uint32_t button); @@ -218,7 +218,7 @@ void seatop_motion(struct sway_seat *seat, uint32_t time_msec); /** * End a seatop and apply the affects. */ -void seatop_finish(struct sway_seat *seat); +void seatop_finish(struct sway_seat *seat, uint32_t time_msec); /** * End a seatop without applying the affects. @@ -239,5 +239,4 @@ void seatop_unref(struct sway_seat *seat, struct sway_container *con); void seatop_render(struct sway_seat *seat, struct sway_output *output, pixman_region32_t *damage); - #endif diff --git a/sway/input/cursor.c b/sway/input/cursor.c index 87811550a..44b5ff147 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -606,8 +606,7 @@ void dispatch_cursor_button(struct sway_cursor *cursor, // Handle existing seat operation if (seat_doing_seatop(seat)) { if (button == seat->seatop_button && state == WLR_BUTTON_RELEASED) { - seatop_finish(seat); - seat_pointer_notify_button(seat, time_msec, button, state); + seatop_finish(seat, time_msec); } if (state == WLR_BUTTON_PRESSED) { state_add_button(cursor, button); @@ -784,8 +783,7 @@ void dispatch_cursor_button(struct sway_cursor *cursor, // Handle mousedown on a container surface if (surface && cont && state == WLR_BUTTON_PRESSED) { seat_set_focus_container(seat, cont); - seat_pointer_notify_button(seat, time_msec, button, state); - seatop_begin_down(seat, cont, button, sx, sy); + seatop_begin_down(seat, cont, time_msec, button, sx, sy); return; } diff --git a/sway/input/seat.c b/sway/input/seat.c index a16d3f276..9888ddfcb 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -1211,9 +1211,9 @@ void seatop_motion(struct sway_seat *seat, uint32_t time_msec) { } } -void seatop_finish(struct sway_seat *seat) { +void seatop_finish(struct sway_seat *seat, uint32_t time_msec) { if (seat->seatop_impl && seat->seatop_impl->finish) { - seat->seatop_impl->finish(seat); + seat->seatop_impl->finish(seat, time_msec); } free(seat->seatop_data); seat->seatop_data = NULL; diff --git a/sway/input/seatop_down.c b/sway/input/seatop_down.c index c2256c9a1..33f9b31a7 100644 --- a/sway/input/seatop_down.c +++ b/sway/input/seatop_down.c @@ -24,7 +24,7 @@ static void handle_motion(struct sway_seat *seat, uint32_t time_msec) { e->moved = true; } -static void handle_finish(struct sway_seat *seat) { +static void handle_finish(struct sway_seat *seat, uint32_t time_msec) { struct seatop_down_event *e = seat->seatop_data; struct sway_cursor *cursor = seat->cursor; // Set the cursor's previous coords to the x/y at the start of the @@ -40,6 +40,8 @@ static void handle_finish(struct sway_seat *seat) { cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy); cursor_send_pointer_motion(cursor, 0, node, surface, sx, sy); } + seat_pointer_notify_button(seat, time_msec, + seat->seatop_button, WLR_BUTTON_RELEASED); } static void handle_abort(struct sway_seat *seat) { @@ -60,8 +62,8 @@ static const struct sway_seatop_impl seatop_impl = { .unref = handle_unref, }; -void seatop_begin_down(struct sway_seat *seat, - struct sway_container *con, uint32_t button, int sx, int sy) { +void seatop_begin_down(struct sway_seat *seat, struct sway_container *con, + uint32_t time_msec, uint32_t button, int sx, int sy) { seatop_abort(seat); struct seatop_down_event *e = @@ -80,5 +82,6 @@ void seatop_begin_down(struct sway_seat *seat, seat->seatop_data = e; seat->seatop_button = button; + seat_pointer_notify_button(seat, time_msec, button, WLR_BUTTON_PRESSED); container_raise_floating(con); } diff --git a/sway/input/seatop_move_floating.c b/sway/input/seatop_move_floating.c index 08e3a5a4e..8a48a9681 100644 --- a/sway/input/seatop_move_floating.c +++ b/sway/input/seatop_move_floating.c @@ -17,7 +17,7 @@ static void handle_motion(struct sway_seat *seat, uint32_t time_msec) { desktop_damage_whole_container(e->con); } -static void handle_finish(struct sway_seat *seat) { +static void handle_finish(struct sway_seat *seat, uint32_t time_msec) { struct seatop_move_floating_event *e = seat->seatop_data; // We "move" the container to its own location diff --git a/sway/input/seatop_move_tiling.c b/sway/input/seatop_move_tiling.c index 1e548f5ae..4b5aa81ed 100644 --- a/sway/input/seatop_move_tiling.c +++ b/sway/input/seatop_move_tiling.c @@ -226,7 +226,7 @@ static bool is_parallel(enum sway_container_layout layout, return layout_is_horiz == edge_is_horiz; } -static void handle_finish(struct sway_seat *seat) { +static void handle_finish(struct sway_seat *seat, uint32_t time_msec) { struct seatop_move_tiling_event *e = seat->seatop_data; if (!e->target_node) { diff --git a/sway/input/seatop_resize_floating.c b/sway/input/seatop_resize_floating.c index 12851b406..bf6c7ab42 100644 --- a/sway/input/seatop_resize_floating.c +++ b/sway/input/seatop_resize_floating.c @@ -142,7 +142,7 @@ static void handle_motion(struct sway_seat *seat, uint32_t time_msec) { arrange_container(con); } -static void handle_finish(struct sway_seat *seat) { +static void handle_finish(struct sway_seat *seat, uint32_t time_msec) { cursor_set_image(seat->cursor, "left_ptr", NULL); } diff --git a/sway/input/seatop_resize_tiling.c b/sway/input/seatop_resize_tiling.c index cb0f723d0..db32065c7 100644 --- a/sway/input/seatop_resize_tiling.c +++ b/sway/input/seatop_resize_tiling.c @@ -49,7 +49,7 @@ static void handle_motion(struct sway_seat *seat, uint32_t time_msec) { } } -static void handle_finish(struct sway_seat *seat) { +static void handle_finish(struct sway_seat *seat, uint32_t time_msec) { cursor_set_image(seat->cursor, "left_ptr", NULL); } From f98410c090c995d491ba538850b792c763407583 Mon Sep 17 00:00:00 2001 From: emersion Date: Wed, 27 Feb 2019 22:19:53 +0100 Subject: [PATCH 026/191] meson: check scdoc version --- meson.build | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/meson.build b/meson.build index 53454cdd7..270dc6b79 100644 --- a/meson.build +++ b/meson.build @@ -95,8 +95,9 @@ conf_data.set10('HAVE_SYSTEMD', systemd.found()) conf_data.set10('HAVE_ELOGIND', elogind.found()) conf_data.set10('HAVE_TRAY', have_tray) -scdoc = find_program('scdoc', required: get_option('man-pages')) +scdoc = dependency('scdoc', version: '>=1.9', native: true, required: get_option('man-pages')) if scdoc.found() + scdoc_prog = find_program('scdoc') sh = find_program('sh') mandir = get_option('mandir') man_files = [ @@ -120,7 +121,7 @@ if scdoc.found() input: filename, output: output, command: [ - sh, '-c', '@0@ < @INPUT@ > @1@'.format(scdoc.path(), output) + sh, '-c', '@0@ < @INPUT@ > @1@'.format(scdoc_prog.path(), output) ], install: true, install_dir: '@0@/man@1@'.format(mandir, section) From 77b16a5124efb8ede9c4219ddea7832503ebfddf Mon Sep 17 00:00:00 2001 From: emersion Date: Fri, 1 Mar 2019 19:42:40 +0100 Subject: [PATCH 027/191] ci: use scdoc-git because 1.9.1 is broken --- .builds/alpine.yml | 6 +++++- .builds/archlinux.yml | 6 +++++- .builds/freebsd.yml | 8 +++++++- 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/.builds/alpine.yml b/.builds/alpine.yml index 328625a5d..07a10db07 100644 --- a/.builds/alpine.yml +++ b/.builds/alpine.yml @@ -12,7 +12,6 @@ packages: - meson - pango-dev - pixman-dev - - scdoc - wayland-dev - wayland-protocols - xcb-util-image-dev @@ -20,7 +19,12 @@ packages: sources: - https://github.com/swaywm/sway - https://github.com/swaywm/wlroots + - https://git.sr.ht/~sircmpwn/scdoc tasks: + - scdoc: | + cd scdoc + make PREFIX=/usr + sudo make install PREFIX=/usr - wlroots: | cd wlroots meson --prefix=/usr build -Drootston=false -Dexamples=false diff --git a/.builds/archlinux.yml b/.builds/archlinux.yml index c8f116e00..a9e3a9ae5 100644 --- a/.builds/archlinux.yml +++ b/.builds/archlinux.yml @@ -8,7 +8,6 @@ packages: - libxkbcommon - meson - pango - - scdoc - wayland - wayland-protocols - xcb-util-image @@ -16,7 +15,12 @@ packages: sources: - https://github.com/swaywm/sway - https://github.com/swaywm/wlroots + - https://git.sr.ht/~sircmpwn/scdoc tasks: + - scdoc: | + cd scdoc + make PREFIX=/usr + sudo make install PREFIX=/usr - wlroots: | cd wlroots meson --prefix=/usr build -Drootston=false -Dexamples=false diff --git a/.builds/freebsd.yml b/.builds/freebsd.yml index 8fa760257..91a3817d1 100644 --- a/.builds/freebsd.yml +++ b/.builds/freebsd.yml @@ -8,7 +8,6 @@ packages: - graphics/gdk-pixbuf2 - graphics/wayland - graphics/wayland-protocols -- textproc/scdoc - x11-toolkits/pango - x11/libxcb - x11/libxkbcommon @@ -22,10 +21,17 @@ packages: - x11/libX11 - x11/pixman - x11/xcb-util-wm +# scdoc dependencies +- devel/gmake sources: - https://github.com/swaywm/sway - https://github.com/swaywm/wlroots +- https://git.sr.ht/~sircmpwn/scdoc tasks: +- scdoc: | + cd scdoc + gmake PREFIX=/usr/local + sudo gmake install PREFIX=/usr/local - fixup_epoll: | cat << 'EOF' | sudo tee /usr/local/libdata/pkgconfig/epoll-shim.pc prefix=/usr/local From 37f0e1f1a2cc8e072c8e01e0769c62c536b8295b Mon Sep 17 00:00:00 2001 From: hugbubby Date: Fri, 1 Mar 2019 12:14:14 -0600 Subject: [PATCH 028/191] Minor fix of code duplication. Removes 3~ lines of code that didn't need to be restated. --- sway/main.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/sway/main.c b/sway/main.c index 6754190f9..22b728131 100644 --- a/sway/main.c +++ b/sway/main.c @@ -186,11 +186,7 @@ static void log_kernel(void) { static bool drop_permissions(void) { if (getuid() != geteuid() || getgid() != getegid()) { - if (setgid(getgid()) != 0) { - sway_log(SWAY_ERROR, "Unable to drop root, refusing to start"); - return false; - } - if (setuid(getuid()) != 0) { + if (setuid(getuid()) != 0 || setgid(getgid()) != 0) { sway_log(SWAY_ERROR, "Unable to drop root, refusing to start"); return false; } From 430359519c1cb9583020fc8da04f5ecc31b0e914 Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Sat, 2 Mar 2019 02:29:28 -0500 Subject: [PATCH 029/191] floating_maximum_size: change default behavior This changes the way zero (which is the default) is interpreted for both the width and height of `floating_maximum_size`. It now refers to the width and height of the entire output layout, which matches i3's behavior. This also removes duplicated code to calculate the floating constraints in three files. Before this, `container_init_floating` used two-thirds of the workspace width/height as the max and the entire workspace width/height was used everywhere else. Now, all callers use a single function `floating_calculate_constraints`. --- include/sway/tree/container.h | 3 ++ sway/commands/resize.c | 45 ++------------------ sway/input/seatop_resize_floating.c | 37 +---------------- sway/sway.5.scd | 3 +- sway/tree/container.c | 64 +++++++++++++++++++---------- 5 files changed, 53 insertions(+), 99 deletions(-) diff --git a/include/sway/tree/container.h b/include/sway/tree/container.h index f7a4ac37e..d25089949 100644 --- a/include/sway/tree/container.h +++ b/include/sway/tree/container.h @@ -212,6 +212,9 @@ void container_update_representation(struct sway_container *container); */ size_t container_titlebar_height(void); +void floating_calculate_constraints(int *min_width, int *max_width, + int *min_height, int *max_height); + /** * Resize and center the container in its workspace. */ diff --git a/sway/commands/resize.c b/sway/commands/resize.c index c9261535a..440937f0d 100644 --- a/sway/commands/resize.c +++ b/sway/commands/resize.c @@ -66,45 +66,6 @@ static int parse_resize_amount(int argc, char **argv, return 2; } -static void calculate_constraints(int *min_width, int *max_width, - int *min_height, int *max_height) { - struct sway_container *con = config->handler_context.container; - - 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 || - container_is_scratchpad_hidden(con)) { // no max - *max_width = INT_MAX; - } else if (config->floating_maximum_width == 0) { // automatic - *max_width = con->workspace->width; - } else { - *max_width = config->floating_maximum_width; - } - - if (config->floating_maximum_height == -1 || - container_is_scratchpad_hidden(con)) { // no max - *max_height = INT_MAX; - } else if (config->floating_maximum_height == 0) { // automatic - *max_height = con->workspace->height; - } else { - *max_height = config->floating_maximum_height; - } -} - static uint32_t parse_resize_axis(const char *axis) { if (strcasecmp(axis, "width") == 0 || strcasecmp(axis, "horizontal") == 0) { return AXIS_HORIZONTAL; @@ -258,7 +219,8 @@ static struct cmd_results *resize_adjust_floating(uint32_t axis, // Make sure we're not adjusting beyond floating min/max size int min_width, max_width, min_height, max_height; - calculate_constraints(&min_width, &max_width, &min_height, &max_height); + floating_calculate_constraints(&min_width, &max_width, + &min_height, &max_height); if (con->width + grow_width < min_width) { grow_width = min_width - con->width; } else if (con->width + grow_width > max_width) { @@ -383,7 +345,8 @@ static struct cmd_results *resize_set_tiled(struct sway_container *con, static struct cmd_results *resize_set_floating(struct sway_container *con, struct resize_amount *width, struct resize_amount *height) { int min_width, max_width, min_height, max_height, grow_width = 0, grow_height = 0; - calculate_constraints(&min_width, &max_width, &min_height, &max_height); + floating_calculate_constraints(&min_width, &max_width, + &min_height, &max_height); if (width->amount) { switch (width->unit) { diff --git a/sway/input/seatop_resize_floating.c b/sway/input/seatop_resize_floating.c index bf6c7ab42..18c6db73b 100644 --- a/sway/input/seatop_resize_floating.c +++ b/sway/input/seatop_resize_floating.c @@ -17,41 +17,6 @@ struct seatop_resize_floating_event { double ref_con_lx, ref_con_ly; // container's x/y at start of op }; -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 - *max_width = con->workspace->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 - *max_height = con->workspace->height; - } else { - *max_height = config->floating_maximum_height; - } -} - static void handle_motion(struct sway_seat *seat, uint32_t time_msec) { struct seatop_resize_floating_event *e = seat->seatop_data; struct sway_container *con = e->con; @@ -85,7 +50,7 @@ static void handle_motion(struct sway_seat *seat, uint32_t time_msec) { double width = e->ref_width + grow_width; double height = e->ref_height + grow_height; int min_width, max_width, min_height, max_height; - calculate_floating_constraints(con, &min_width, &max_width, + floating_calculate_constraints(&min_width, &max_width, &min_height, &max_height); width = fmax(min_width, fmin(width, max_width)); height = fmax(min_height, fmin(height, max_height)); diff --git a/sway/sway.5.scd b/sway/sway.5.scd index 8f8b7e3d2..4f4522fb5 100644 --- a/sway/sway.5.scd +++ b/sway/sway.5.scd @@ -430,7 +430,8 @@ The default colors are: *floating_maximum_size* x Specifies the maximum size of floating windows. -1 x -1 removes the upper - limit. + limit. The default is 0 x 0, which will use the width and height of the + entire output layout as the maximums *floating_minimum_size* x Specifies the minimum size of floating windows. The default is 75 x 50. diff --git a/sway/tree/container.c b/sway/tree/container.c index d448df22a..330439411 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -611,56 +611,78 @@ size_t container_titlebar_height(void) { return config->font_height + config->titlebar_v_padding * 2; } -void container_init_floating(struct sway_container *con) { - struct sway_workspace *ws = con->workspace; - int min_width, min_height; - int max_width, max_height; - +void floating_calculate_constraints(int *min_width, int *max_width, + int *min_height, int *max_height) { if (config->floating_minimum_width == -1) { // no minimum - min_width = 0; + *min_width = 0; } else if (config->floating_minimum_width == 0) { // automatic - min_width = 75; + *min_width = 75; } else { - min_width = config->floating_minimum_width; + *min_width = config->floating_minimum_width; } if (config->floating_minimum_height == -1) { // no minimum - min_height = 0; + *min_height = 0; } else if (config->floating_minimum_height == 0) { // automatic - min_height = 50; + *min_height = 50; } else { - min_height = config->floating_minimum_height; + *min_height = config->floating_minimum_height; } + struct wlr_box *box = wlr_output_layout_get_box(root->output_layout, NULL); + if (config->floating_maximum_width == -1) { // no maximum - max_width = INT_MAX; + *max_width = INT_MAX; } else if (config->floating_maximum_width == 0) { // automatic - max_width = ws->width * 0.6666; + *max_width = box->width; } else { - max_width = config->floating_maximum_width; + *max_width = config->floating_maximum_width; } if (config->floating_maximum_height == -1) { // no maximum - max_height = INT_MAX; + *max_height = INT_MAX; } else if (config->floating_maximum_height == 0) { // automatic - max_height = ws->height * 0.6666; + *max_height = box->height; } else { - max_height = config->floating_maximum_height; + *max_height = config->floating_maximum_height; } +} + +void container_init_floating(struct sway_container *con) { + struct sway_workspace *ws = con->workspace; + int min_width, max_width, min_height, max_height; + floating_calculate_constraints(&min_width, &max_width, + &min_height, &max_height); + if (!con->view) { con->width = max_width; con->height = max_height; - con->x = ws->x + (ws->width - con->width) / 2; - con->y = ws->y + (ws->height - con->height) / 2; + if (con->width > ws->width || con->height > ws->height) { + struct wlr_box *ob = wlr_output_layout_get_box(root->output_layout, + ws->output->wlr_output); + con->x = ob->x + (ob->width - con->width) / 2; + con->y = ob->y + (ob->height - con->height) / 2; + } else { + con->x = ws->x + (ws->width - con->width) / 2; + con->y = ws->y + (ws->height - con->height) / 2; + } } else { struct sway_view *view = con->view; con->content_width = fmax(min_width, fmin(view->natural_width, max_width)); con->content_height = fmax(min_height, fmin(view->natural_height, max_height)); - con->content_x = ws->x + (ws->width - con->content_width) / 2; - con->content_y = ws->y + (ws->height - con->content_height) / 2; + if (con->content_width > ws->width + || con->content_height > ws->height) { + struct wlr_box *ob = wlr_output_layout_get_box(root->output_layout, + ws->output->wlr_output); + con->content_x = ob->x + (ob->width - con->content_width) / 2; + con->content_y = ob->y + (ob->height - con->content_height) / 2; + } else { + con->content_x = ws->x + (ws->width - con->content_width) / 2; + con->content_y = ws->y + (ws->height - con->content_height) / 2; + } // If the view's border is B_NONE then these properties are ignored. con->border_top = con->border_bottom = true; From 1c329f2fe6ae344b3201447a586f6c9a571d44ff Mon Sep 17 00:00:00 2001 From: Noam Preil Date: Sat, 2 Mar 2019 16:09:14 -0500 Subject: [PATCH 030/191] Fixes crash in spawn_swaybg (closes #3733) --- sway/config/output.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sway/config/output.c b/sway/config/output.c index cb889b3e8..3a36ed180 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -221,10 +221,10 @@ static bool spawn_swaybg(struct sway_output *output, char *const cmd[]) { pid = fork(); if (pid < 0) { sway_log_errno(SWAY_ERROR, "fork failed"); - exit(EXIT_FAILURE); + _exit(EXIT_FAILURE); } else if (pid == 0) { if (!set_cloexec(sockets[1], false)) { - exit(EXIT_FAILURE); + _exit(EXIT_FAILURE); } char wayland_socket_str[16]; @@ -234,9 +234,9 @@ static bool spawn_swaybg(struct sway_output *output, char *const cmd[]) { execvp(cmd[0], cmd); sway_log_errno(SWAY_ERROR, "execvp failed"); - exit(EXIT_FAILURE); + _exit(EXIT_FAILURE); } - exit(EXIT_SUCCESS); + _exit(EXIT_SUCCESS); } if (close(sockets[1]) != 0) { From 23f075e71d985754effde5372f4242ddb09cbbc0 Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Sat, 2 Mar 2019 23:17:46 -0500 Subject: [PATCH 031/191] render_floating: skip fullscreen floaters If a floater is fullscreen either on a workspace or globally, it should not be rendered on any output is is not fullscreened on. When rendering it on an output it should not be rendered on, there will be an extraneous border along the adjacent side of the output. This adds a check in render_floating to skip all fullscreened floaters --- sway/desktop/render.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sway/desktop/render.c b/sway/desktop/render.c index 5df160757..4b36a9c24 100644 --- a/sway/desktop/render.c +++ b/sway/desktop/render.c @@ -944,6 +944,9 @@ static void render_floating(struct sway_output *soutput, } for (int k = 0; k < ws->current.floating->length; ++k) { struct sway_container *floater = ws->current.floating->items[k]; + if (floater->fullscreen_mode != FULLSCREEN_NONE) { + continue; + } render_floating_container(soutput, damage, floater); } } From a053823f39c0ea72f3d3763e7c1e0f18ab9c4d61 Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Sun, 3 Mar 2019 12:38:05 -0500 Subject: [PATCH 032/191] sway.1.scd: document environment vars set by sway This just documents the few environment variables set by sway in sway.1.scd --- sway/sway.1.scd | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/sway/sway.1.scd b/sway/sway.1.scd index cfb4817a1..f8820c4e0 100644 --- a/sway/sway.1.scd +++ b/sway/sway.1.scd @@ -80,6 +80,27 @@ _XKB\_DEFAULT\_VARIANT_, _XKB\_DEFAULT\_OPTIONS_ preferred way to configure the keyboard is via the configuration file, see *sway-input*(5). +The following environment variables are set by sway: + +_DISPLAY_ + If compiled with Xwayland support and Xwayland is not disabled by the + config, this will be set to the name of the X display used for Xwayland + +_I3SOCK_ + For compatibility with i3, specifies the path to the sway IPC socket + +_SWAYSOCK_ + Specifies the path to the sway IPC socket + +_WAYLAND_DISPLAY_ + Specifies the name of the Wayland display that sway is running on + +_XCURSOR_SIZE_ + Specifies the configured cursor size + +_XCURSOR_THEME_ + Specifies the configured cursor theme + # AUTHORS Maintained by Drew DeVault , who is assisted by other open From bdac0df4f8fd1a816f0cfff8223d54d09823233b Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Mon, 4 Mar 2019 21:04:16 +1000 Subject: [PATCH 033/191] Allow concurrent clicks If two cursor buttons are pressed at the same time, the client will now be notified of the second button press. The main reason for not sending the concurrent presses was due to an early return in dispatch_cursor_button if a seatop is in progress. This patch makes it call seat_pointer_notify_button prior to returning. But it also has to make sure there's not a mismatch in events such as a release without a press. Prior to this patch, the down seatop would send press and release events in its begin and finish functions. No other seatops did this. A press event would be sent prior to starting tiling drag, but never an associated release. After this patch, no seatops send their own press or release events. We send them prior to calling the seatop begin functions, then the first part of dispatch_cursor_button handles all presses during seatops and when releasing the seatop. --- sway/input/cursor.c | 7 +++++++ sway/input/seatop_down.c | 3 --- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/sway/input/cursor.c b/sway/input/cursor.c index 44b5ff147..b96fde889 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -613,6 +613,7 @@ void dispatch_cursor_button(struct sway_cursor *cursor, } else { state_erase_button(cursor, button); } + seat_pointer_notify_button(seat, time_msec, button, state); return; } @@ -681,6 +682,7 @@ void dispatch_cursor_button(struct sway_cursor *cursor, if (cont && resize_edge && button == BTN_LEFT && state == WLR_BUTTON_PRESSED && !is_floating) { seat_set_focus_container(seat, cont); + seat_pointer_notify_button(seat, time_msec, button, state); seatop_begin_resize_tiling(seat, cont, button, edge); return; } @@ -711,6 +713,7 @@ void dispatch_cursor_button(struct sway_cursor *cursor, } cursor_set_image(seat->cursor, image, NULL); seat_set_focus_container(seat, cont); + seat_pointer_notify_button(seat, time_msec, button, state); seatop_begin_resize_tiling(seat, cont, button, edge); return; } @@ -726,6 +729,7 @@ void dispatch_cursor_button(struct sway_cursor *cursor, cont = cont->parent; } seat_set_focus_container(seat, cont); + seat_pointer_notify_button(seat, time_msec, button, state); seatop_begin_move_floating(seat, cont, button); return; } @@ -736,6 +740,7 @@ void dispatch_cursor_button(struct sway_cursor *cursor, state == WLR_BUTTON_PRESSED) { // Via border if (button == BTN_LEFT && resize_edge != WLR_EDGE_NONE) { + seat_pointer_notify_button(seat, time_msec, button, state); seatop_begin_resize_floating(seat, cont, button, resize_edge); return; } @@ -753,6 +758,7 @@ void dispatch_cursor_button(struct sway_cursor *cursor, WLR_EDGE_RIGHT : WLR_EDGE_LEFT; edge |= cursor->cursor->y > floater->y + floater->height / 2 ? WLR_EDGE_BOTTOM : WLR_EDGE_TOP; + seat_pointer_notify_button(seat, time_msec, button, state); seatop_begin_resize_floating(seat, floater, button, edge); return; } @@ -784,6 +790,7 @@ void dispatch_cursor_button(struct sway_cursor *cursor, if (surface && cont && state == WLR_BUTTON_PRESSED) { seat_set_focus_container(seat, cont); seatop_begin_down(seat, cont, time_msec, button, sx, sy); + seat_pointer_notify_button(seat, time_msec, button, WLR_BUTTON_PRESSED); return; } diff --git a/sway/input/seatop_down.c b/sway/input/seatop_down.c index 33f9b31a7..7f3940957 100644 --- a/sway/input/seatop_down.c +++ b/sway/input/seatop_down.c @@ -40,8 +40,6 @@ static void handle_finish(struct sway_seat *seat, uint32_t time_msec) { cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy); cursor_send_pointer_motion(cursor, 0, node, surface, sx, sy); } - seat_pointer_notify_button(seat, time_msec, - seat->seatop_button, WLR_BUTTON_RELEASED); } static void handle_abort(struct sway_seat *seat) { @@ -82,6 +80,5 @@ void seatop_begin_down(struct sway_seat *seat, struct sway_container *con, seat->seatop_data = e; seat->seatop_button = button; - seat_pointer_notify_button(seat, time_msec, button, WLR_BUTTON_PRESSED); container_raise_floating(con); } From 3f184cf188234083498e0b03435853bbadd01a99 Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Mon, 4 Mar 2019 10:25:34 -0500 Subject: [PATCH 034/191] meson: update scdoc requirement to >= 1.9.2 Since scdoc 1.9.1 is bugged, this updates the meson version check to >= 1.9.2 and drops the version requirement from the README. This should make it more obvious to users who have 1.9.1 that they need to update scdoc to be able to compile man pages and hopefully cut down on the duplicate issues --- README.es.md | 2 +- README.md | 2 +- README.pl.md | 2 +- meson.build | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.es.md b/README.es.md index 076d89005..79953848b 100644 --- a/README.es.md +++ b/README.es.md @@ -38,7 +38,7 @@ Instale las dependencias: * pango * cairo * gdk-pixbuf2 \*\* -* [scdoc](https://git.sr.ht/~sircmpwn/scdoc) >= 1.9.0 (optional: man pages) \* +* [scdoc](https://git.sr.ht/~sircmpwn/scdoc) (optional: man pages) \* * git \* _\*Compile-time dep_ diff --git a/README.md b/README.md index aa7cd846d..99835d6e5 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,7 @@ Install dependencies: * pango * cairo * gdk-pixbuf2 \*\* -* [scdoc](https://git.sr.ht/~sircmpwn/scdoc) >= 1.9.0 (optional: man pages) \* +* [scdoc](https://git.sr.ht/~sircmpwn/scdoc) (optional: man pages) \* * git \* _\*Compile-time dep_ diff --git a/README.pl.md b/README.pl.md index ebfd9214a..0574093ab 100644 --- a/README.pl.md +++ b/README.pl.md @@ -39,7 +39,7 @@ Zainstaluj zależności: * pango * cairo * gdk-pixbuf2 \*\* -* [scdoc](https://git.sr.ht/~sircmpwn/scdoc) >= 1.9.0 (opcjonalnie: strony pomocy man) \* +* [scdoc](https://git.sr.ht/~sircmpwn/scdoc) (opcjonalnie: strony pomocy man) \* * git \* _\*zależności kompilacji_ diff --git a/meson.build b/meson.build index 270dc6b79..a08b2603d 100644 --- a/meson.build +++ b/meson.build @@ -95,7 +95,7 @@ conf_data.set10('HAVE_SYSTEMD', systemd.found()) conf_data.set10('HAVE_ELOGIND', elogind.found()) conf_data.set10('HAVE_TRAY', have_tray) -scdoc = dependency('scdoc', version: '>=1.9', native: true, required: get_option('man-pages')) +scdoc = dependency('scdoc', version: '>=1.9.2', native: true, required: get_option('man-pages')) if scdoc.found() scdoc_prog = find_program('scdoc') sh = find_program('sh') From a6711740bcd311e1ee551c83a5dfc46d9344d17e Mon Sep 17 00:00:00 2001 From: emersion Date: Mon, 4 Mar 2019 20:51:48 +0100 Subject: [PATCH 035/191] Set DISPLAY after initializing Xwayland This is necessary after https://github.com/swaywm/wlroots/pull/1596 --- sway/server.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sway/server.c b/sway/server.c index 0fcd58003..b24cc1dc4 100644 --- a/sway/server.c +++ b/sway/server.c @@ -184,6 +184,8 @@ bool server_start(struct sway_server *server) { &server->xwayland_ready); server->xwayland_ready.notify = handle_xwayland_ready; + setenv("DISPLAY", server->xwayland.wlr_xwayland->display_name, true); + server->xwayland.xcursor_manager = wlr_xcursor_manager_create(cursor_theme, cursor_size); wlr_xcursor_manager_load(server->xwayland.xcursor_manager, 1); From 19df2e590602abe0b5e1b53bc11debdb37be3fbe Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Wed, 6 Mar 2019 00:33:10 -0500 Subject: [PATCH 036/191] ipc: change {,deco_}rect to match i3 This fixes the `deco_rect` and `rect` properties in the IPC responses to match i3's behavior. `deco_rect` should be relative to the parent node, not the current node. This also takes tabbed and stacked decorations into account and will calculate `deco_rect` for all containers since tabbed and stacked child containers will have decorations. `rect` should exclude the window decorations. --- sway/ipc-json.c | 51 +++++++++++++++++++++++++++++++++++++-------- sway/sway-ipc.7.scd | 5 +++-- 2 files changed, 45 insertions(+), 11 deletions(-) diff --git a/sway/ipc-json.c b/sway/ipc-json.c index 125df3877..c008f241f 100644 --- a/sway/ipc-json.c +++ b/sway/ipc-json.c @@ -350,6 +350,38 @@ static void ipc_json_describe_workspace(struct sway_workspace *workspace, json_object_object_add(object, "floating_nodes", floating_array); } +static void get_deco_rect(struct sway_container *c, struct wlr_box *deco_rect) { + enum sway_container_layout parent_layout = container_parent_layout(c); + if (parent_layout != L_TABBED && parent_layout != L_STACKED && + c->current.border != B_NORMAL) { + deco_rect->x = deco_rect->y = deco_rect->width = deco_rect->height = 0; + return; + } + + if (c->parent) { + deco_rect->x = c->x - c->parent->x; + deco_rect->y = c->y - c->parent->y; + } else { + deco_rect->x = c->x - c->workspace->x; + deco_rect->y = c->y - c->workspace->y; + } + deco_rect->width = c->width; + deco_rect->height = container_titlebar_height(); + + if (parent_layout == L_TABBED) { + deco_rect->width = c->parent + ? c->parent->width / c->parent->children->length + : c->workspace->width / c->workspace->tiling->length; + deco_rect->x += deco_rect->width * container_sibling_index(c); + } else if (container_parent_layout(c) == L_STACKED) { + if (!c->view) { + size_t siblings = container_get_siblings(c)->length; + deco_rect->y -= deco_rect->height * siblings; + } + deco_rect->y += deco_rect->height * container_sibling_index(c); + } +} + static void ipc_json_describe_view(struct sway_container *c, json_object *object) { json_object_object_add(object, "pid", json_object_new_int(c->view->pid)); @@ -377,15 +409,6 @@ static void ipc_json_describe_view(struct sway_container *c, json_object *object json_object_object_add(object, "window_rect", ipc_json_create_rect(&window_box)); - struct wlr_box deco_box = {0, 0, 0, 0}; - - if (c->current.border == B_NORMAL) { - deco_box.width = c->width; - deco_box.height = c->content_y - c->y; - } - - json_object_object_add(object, "deco_rect", ipc_json_create_rect(&deco_box)); - struct wlr_box geometry = {0, 0, c->view->natural_width, c->view->natural_height}; json_object_object_add(object, "geometry", ipc_json_create_rect(&geometry)); @@ -465,6 +488,10 @@ static void ipc_json_describe_container(struct sway_container *c, json_object *o json_object_new_int(c->current.border_thickness)); json_object_object_add(object, "floating_nodes", json_object_new_array()); + struct wlr_box deco_box = {0, 0, 0, 0}; + get_deco_rect(c, &deco_box); + json_object_object_add(object, "deco_rect", ipc_json_create_rect(&deco_box)); + if (c->view) { ipc_json_describe_view(c, object); } @@ -505,6 +532,12 @@ json_object *ipc_json_describe_node(struct sway_node *node) { struct wlr_box box; node_get_box(node, &box); + if (node->type == N_CONTAINER) { + struct wlr_box deco_rect = {0, 0, 0, 0}; + get_deco_rect(node->sway_container, &deco_rect); + box.y += deco_rect.height; + box.height -= deco_rect.height; + } json_object *focus = json_object_new_array(); struct focus_inactive_data data = { diff --git a/sway/sway-ipc.7.scd b/sway/sway-ipc.7.scd index d598a54fd..6b4004536 100644 --- a/sway/sway-ipc.7.scd +++ b/sway/sway-ipc.7.scd @@ -314,14 +314,15 @@ node and will have the following properties: and other special nodes such as the scratchpad |- rect : object -: The absolute geometry of the node +: The absolute geometry of the node. The window decorations are excluded from + this, but borders are included. |- window_rect : object : The geometry of the contents inside the node. The window decorations are excluded from this calculation, but borders are included. |- deco_rect : object -: The geometry of the decorations inside the node +: The geometry of the decorations for the node relative to the parent node |- geometry : object : The natural geometry of the contents if it were to size itself From 8b6bd106aa92192e9e2aa6781e9b2e51f1e22fe2 Mon Sep 17 00:00:00 2001 From: emersion Date: Wed, 6 Mar 2019 10:47:50 +0100 Subject: [PATCH 037/191] Fix container_parent_layout for scratchpad windows --- sway/tree/container.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/sway/tree/container.c b/sway/tree/container.c index 330439411..a6142193f 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -1163,7 +1163,10 @@ enum sway_container_layout container_parent_layout(struct sway_container *con) { if (con->parent) { return con->parent->layout; } - return con->workspace->layout; + if (con->workspace) { + return con->workspace->layout; + } + return L_NONE; } enum sway_container_layout container_current_parent_layout( From 8ada2daba5f58fe2bba77127bedd4e9af7c8844f Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Wed, 6 Mar 2019 12:16:28 -0500 Subject: [PATCH 038/191] ipc: fix rect for stacked children This now takes all titlebars for stacked children into account for the ipc property `rect` --- sway/ipc-json.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/sway/ipc-json.c b/sway/ipc-json.c index c008f241f..bcc525ad1 100644 --- a/sway/ipc-json.c +++ b/sway/ipc-json.c @@ -535,8 +535,12 @@ json_object *ipc_json_describe_node(struct sway_node *node) { if (node->type == N_CONTAINER) { struct wlr_box deco_rect = {0, 0, 0, 0}; get_deco_rect(node->sway_container, &deco_rect); - box.y += deco_rect.height; - box.height -= deco_rect.height; + size_t count = 1; + if (container_parent_layout(node->sway_container) == L_STACKED) { + count = container_get_siblings(node->sway_container)->length; + } + box.y += deco_rect.height * count; + box.height -= deco_rect.height * count; } json_object *focus = json_object_new_array(); From 0df76ed96afa75ff8dd8ab6cf7748026e1caf5e7 Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Wed, 6 Mar 2019 12:02:19 -0500 Subject: [PATCH 039/191] ipc: fix fullscreen deco_rect This fixes the deco_rect reported by the ipc for fullscreen containers to be all zeroes. Children of the fullscreen container should still have their decorations reported correctly --- sway/ipc-json.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/sway/ipc-json.c b/sway/ipc-json.c index bcc525ad1..20dcafb1e 100644 --- a/sway/ipc-json.c +++ b/sway/ipc-json.c @@ -352,8 +352,9 @@ static void ipc_json_describe_workspace(struct sway_workspace *workspace, static void get_deco_rect(struct sway_container *c, struct wlr_box *deco_rect) { enum sway_container_layout parent_layout = container_parent_layout(c); - if (parent_layout != L_TABBED && parent_layout != L_STACKED && - c->current.border != B_NORMAL) { + if ((parent_layout != L_TABBED && parent_layout != L_STACKED && + c->current.border != B_NORMAL) || + c->fullscreen_mode != FULLSCREEN_NONE) { deco_rect->x = deco_rect->y = deco_rect->width = deco_rect->height = 0; return; } From 7f700e08ac8ac95c395b54c86b40d2fc79242310 Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Thu, 7 Mar 2019 03:37:49 -0500 Subject: [PATCH 040/191] ipc: describe libinput device configuration This adds the device configurations to the ipc response for libinput devices. Only supported configuration options for the device will be added. This also moves `libinput_send_events` inside a new `libinput` object that contains the rest of the configuration options. sway-ipc(7) has been updated to reflect the changes and document the new additions. --- sway/ipc-json.c | 198 ++++++++++++++++++++++++++++++++++++++++---- sway/sway-ipc.7.scd | 144 ++++++++++++++++++++++++++++---- swaymsg/main.c | 10 ++- 3 files changed, 317 insertions(+), 35 deletions(-) diff --git a/sway/ipc-json.c b/sway/ipc-json.c index 20dcafb1e..e9564b049 100644 --- a/sway/ipc-json.c +++ b/sway/ipc-json.c @@ -617,6 +617,187 @@ json_object *ipc_json_describe_node_recursive(struct sway_node *node) { return object; } +static json_object *describe_libinput_device(struct libinput_device *device) { + json_object *object = json_object_new_object(); + + const char *events = "unknown"; + switch (libinput_device_config_send_events_get_mode(device)) { + case LIBINPUT_CONFIG_SEND_EVENTS_ENABLED: + events = "enabled"; + break; + case LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE: + events = "disabled_on_external_mouse"; + break; + case LIBINPUT_CONFIG_SEND_EVENTS_DISABLED: + events = "disabled"; + break; + } + json_object_object_add(object, "send_events", + json_object_new_string(events)); + + if (libinput_device_config_tap_get_finger_count(device) > 0) { + const char *tap = "unknown"; + switch (libinput_device_config_tap_get_enabled(device)) { + case LIBINPUT_CONFIG_TAP_ENABLED: + tap = "enabled"; + break; + case LIBINPUT_CONFIG_TAP_DISABLED: + tap = "disabled"; + break; + } + json_object_object_add(object, "tap", json_object_new_string(tap)); + + const char *button_map = "unknown"; + switch (libinput_device_config_tap_get_button_map(device)) { + case LIBINPUT_CONFIG_TAP_MAP_LRM: + button_map = "lrm"; + break; + case LIBINPUT_CONFIG_TAP_MAP_LMR: + button_map = "lmr"; + break; + } + json_object_object_add(object, "tap_button_map", + json_object_new_string(button_map)); + + const char* drag = "unknown"; + switch (libinput_device_config_tap_get_drag_enabled(device)) { + case LIBINPUT_CONFIG_DRAG_ENABLED: + drag = "enabled"; + break; + case LIBINPUT_CONFIG_DRAG_DISABLED: + drag = "disabled"; + break; + } + json_object_object_add(object, "tap_drag", + json_object_new_string(drag)); + + const char *drag_lock = "unknown"; + switch (libinput_device_config_tap_get_drag_lock_enabled(device)) { + case LIBINPUT_CONFIG_DRAG_LOCK_ENABLED: + drag_lock = "enabled"; + break; + case LIBINPUT_CONFIG_DRAG_LOCK_DISABLED: + drag_lock = "disabled"; + break; + } + json_object_object_add(object, "tap_drag_lock", + json_object_new_string(drag_lock)); + } + + if (libinput_device_config_accel_is_available(device)) { + double accel = libinput_device_config_accel_get_speed(device); + json_object_object_add(object, "accel_speed", + json_object_new_double(accel)); + + const char *accel_profile = "unknown"; + switch (libinput_device_config_accel_get_profile(device)) { + case LIBINPUT_CONFIG_ACCEL_PROFILE_NONE: + accel_profile = "none"; + break; + case LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT: + accel_profile = "flat"; + break; + case LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE: + accel_profile = "adaptive"; + break; + } + json_object_object_add(object, "accel_profile", + json_object_new_string(accel_profile)); + } + + if (libinput_device_config_scroll_has_natural_scroll(device)) { + const char *natural_scroll = "disabled"; + if (libinput_device_config_scroll_get_natural_scroll_enabled(device)) { + natural_scroll = "enabled"; + } + json_object_object_add(object, "natural_scroll", + json_object_new_string(natural_scroll)); + } + + if (libinput_device_config_left_handed_is_available(device)) { + const char *left_handed = "disabled"; + if (libinput_device_config_left_handed_get(device) != 0) { + left_handed = "enabled"; + } + json_object_object_add(object, "left_handed", + json_object_new_string(left_handed)); + } + + uint32_t click_methods = libinput_device_config_click_get_methods(device); + if ((click_methods & ~LIBINPUT_CONFIG_CLICK_METHOD_NONE) != 0) { + const char *click_method = "unknown"; + switch (libinput_device_config_click_get_method(device)) { + case LIBINPUT_CONFIG_CLICK_METHOD_NONE: + click_method = "none"; + break; + case LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS: + click_method = "button_areas"; + break; + case LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER: + click_method = "clickfinger"; + break; + } + json_object_object_add(object, "click_method", + json_object_new_string(click_method)); + } + + if (libinput_device_config_middle_emulation_is_available(device)) { + const char *middle_emulation = "unknown"; + switch (libinput_device_config_middle_emulation_get_enabled(device)) { + case LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED: + middle_emulation = "enabled"; + break; + case LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED: + middle_emulation = "disabled"; + break; + } + json_object_object_add(object, "middle_emulation", + json_object_new_string(middle_emulation)); + } + + uint32_t scroll_methods = libinput_device_config_scroll_get_methods(device); + if ((scroll_methods & ~LIBINPUT_CONFIG_SCROLL_NO_SCROLL) != 0) { + const char *scroll_method = "unknown"; + switch (libinput_device_config_scroll_get_method(device)) { + case LIBINPUT_CONFIG_SCROLL_NO_SCROLL: + scroll_method = "none"; + break; + case LIBINPUT_CONFIG_SCROLL_2FG: + scroll_method = "two_finger"; + break; + case LIBINPUT_CONFIG_SCROLL_EDGE: + scroll_method = "edge"; + break; + case LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN: + scroll_method = "on_button_down"; + break; + } + json_object_object_add(object, "scroll_method", + json_object_new_string(scroll_method)); + + if ((scroll_methods & LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN) != 0) { + uint32_t button = libinput_device_config_scroll_get_button(device); + json_object_object_add(object, "scroll_button", + json_object_new_int(button)); + } + } + + if (libinput_device_config_dwt_is_available(device)) { + const char *dwt = "unknown"; + switch (libinput_device_config_dwt_get_enabled(device)) { + case LIBINPUT_CONFIG_DWT_ENABLED: + dwt = "enabled"; + break; + case LIBINPUT_CONFIG_DWT_DISABLED: + dwt = "disabled"; + break; + } + json_object_object_add(object, "dwt", json_object_new_string(dwt)); + } + + return object; +} + json_object *ipc_json_describe_input(struct sway_input_device *device) { if (!(sway_assert(device, "Device must not be null"))) { return NULL; @@ -660,21 +841,8 @@ json_object *ipc_json_describe_input(struct sway_input_device *device) { if (wlr_input_device_is_libinput(device->wlr_device)) { struct libinput_device *libinput_dev; libinput_dev = wlr_libinput_get_device_handle(device->wlr_device); - - const char *events = "unknown"; - switch (libinput_device_config_send_events_get_mode(libinput_dev)) { - case LIBINPUT_CONFIG_SEND_EVENTS_ENABLED: - events = "enabled"; - break; - case LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE: - events = "disabled_on_external_mouse"; - break; - case LIBINPUT_CONFIG_SEND_EVENTS_DISABLED: - events = "disabled"; - break; - } - json_object_object_add(object, "libinput_send_events", - json_object_new_string(events)); + json_object_object_add(object, "libinput", + describe_libinput_device(libinput_dev)); } return object; diff --git a/sway/sway-ipc.7.scd b/sway/sway-ipc.7.scd index 6b4004536..b43b30302 100644 --- a/sway/sway-ipc.7.scd +++ b/sway/sway-ipc.7.scd @@ -1034,10 +1034,66 @@ following properties: |- xkb_active_layout_name : string : (Only keyboards) The active keyboard layout in use -|- libinput_send_events +|- libinput +: object +: (Only libinput devices) An object describing the current device settings. + See below for more information + +The _libinput_ object describes the device configuration for libinput devices. +Only properties that are supported for the device will be added to the object. +In addition to the possible options listed, all string properties may also be +_unknown_, in the case that a new option is added to libinput. See +*sway-input*(5) for information on the meaning of the possible values. The +following properties will be included for devices that support them: + +[- *PROPERTY* +:- *DATA TYPE* +:- *DESCRIPTION* +|- send_events : string -: (Only libinput devices) The send events value in use by libinput for this - device. It can be _enabled_, _disabled_, or _disabled\_on\_external\_mouse_ +:[ Whether events are being sent by the device. It can be _enabled_, + _disabled_, or _disabled\_on\_external\_mouse_ +|- tap +: string +: Whether tap to click is enabled. It can be _enabled_ or _disabled_ +|- tap_button_map +: string +: The finger to button mapping in use. It can be _lmr_ or _lrm_ +|- tap_drag +: string +: Whether tap-and-drag is enabled. It can be _enabled_ or _disabled_ +|- tap_drag_lock +: string +: Whether drag-lock is enabled. It can be _enabled_ or _disabled_ +|- accel_speed +: double +: The pointer-acceleration in use +|- accel_profile +: string +: The acceleration profile in use. It can be _none_, _flat_, or _adaptive_ +|- natural_scroll +: string +: Whether natural scrolling is enabled. It can be _enabled_ or _disabled_ +|- left_handed +: string +: Whether left-handed mode is enabled. It can be _enabled_ or _disabled_ +|- click_method +: string +: The click method in use. It can be _none_, _button_areas_, or _clickfinger_ +|- middle_emulation +: string +: Whether middle emulation is enabled. It can be _enabled_ or _disabled_ +|- scroll_method +: string +: The scroll method in use. It can be _none_, _two_finger_, _edge_, or + _on_button_down_ +|- scroll_button +: int +: The scroll button to use when _scroll_method_ is _on_button_down_. This + will be given as an input event code +|- dwt +: string +: Whether disable-while-typing is enabled. It can be _enabled_ or _disabled_ *Example Reply:* @@ -1050,7 +1106,9 @@ following properties: "product": 1, "type": "keyboard", "xkb_active_layout_name": "English (US)", - "libinput_send_events": "enabled" + "libinput": { + "send_events": "enabled" + } }, { "identifier": "1267:5:Elan_Touchpad", @@ -1058,7 +1116,21 @@ following properties: "vendor": 1267, "product": 5, "type": "pointer", - "libinput_send_events": "enabled" + "libinput": { + "send_events": "enabled", + "tap": "enabled", + "tap_button_map": "lmr", + "tap_drag": "enabled", + "tap_drag_lock": "disabled", + "accel_speed": 0.0, + "accel_profile": "none", + "natural_scroll", "disabled", + "left_handed": "disabled", + "click_method": "button_areas", + "middle_emulation": "disabled", + "scroll_method": "edge", + "dwt": "enabled" + } }, { "identifier": "3034:22494:USB2.0_VGA_UVC_WebCam:_USB2.0_V", @@ -1067,7 +1139,9 @@ following properties: "product": 22494, "type": "keyboard", "xkb_active_layout_name": "English (US)", - "libinput_send_events": "enabled" + "libinput": { + "send_events": "enabled" + } }, { "identifier": "0:3:Sleep_Button", @@ -1076,7 +1150,9 @@ following properties: "product": 3, "type": "keyboard", "xkb_active_layout_name": "English (US)", - "libinput_send_events": "enabled" + "libinput": { + "send_events": "enabled" + } }, { "identifier": "0:5:Lid_Switch", @@ -1084,7 +1160,10 @@ following properties: "vendor": 0, "product": 5, "type": "switch", - "libinput_send_events": "enabled" + "libinput": { + "send_events": "enabled" + } + }, { "identifier": "0:6:Video_Bus", "name": "Video Bus", @@ -1092,7 +1171,9 @@ following properties: "product": 6, "type": "keyboard", "xkb_active_layout_name": "English (US)", - "libinput_send_events": "enabled" + "libinput": { + "send_events": "enabled" + } }, { "identifier": "0:1:Power_Button", @@ -1101,7 +1182,9 @@ following properties: "product": 1, "type": "keyboard", "xkb_active_layout_name": "English (US)", - "libinput_send_events": "enabled" + "libinput": { + "send_events": "enabled" + } } ] ``` @@ -1150,7 +1233,9 @@ one seat. Each object has the following properties: "product": 1, "type": "keyboard", "xkb_active_layout_name": "English (US)", - "libinput_send_events": "enabled" + "libinput": { + "send_events": "enabled" + } }, { "identifier": "1267:5:Elan_Touchpad", @@ -1158,7 +1243,21 @@ one seat. Each object has the following properties: "vendor": 1267, "product": 5, "type": "pointer", - "libinput_send_events": "enabled" + "libinput": { + "send_events": "enabled", + "tap": "enabled", + "tap_button_map": "lmr", + "tap_drag": "enabled", + "tap_drag_lock": "disabled", + "accel_speed": 0.0, + "accel_profile": "none", + "natural_scroll", "disabled", + "left_handed": "disabled", + "click_method": "button_areas", + "middle_emulation": "disabled", + "scroll_method": "edge", + "dwt": "enabled" + } }, { "identifier": "3034:22494:USB2.0_VGA_UVC_WebCam:_USB2.0_V", @@ -1167,7 +1266,9 @@ one seat. Each object has the following properties: "product": 22494, "type": "keyboard", "xkb_active_layout_name": "English (US)", - "libinput_send_events": "enabled" + "libinput": { + "send_events": "enabled" + } }, { "identifier": "0:3:Sleep_Button", @@ -1176,7 +1277,9 @@ one seat. Each object has the following properties: "product": 3, "type": "keyboard", "xkb_active_layout_name": "English (US)", - "libinput_send_events": "enabled" + "libinput": { + "send_events": "enabled" + } }, { "identifier": "0:5:Lid_Switch", @@ -1184,7 +1287,10 @@ one seat. Each object has the following properties: "vendor": 0, "product": 5, "type": "switch", - "libinput_send_events": "enabled" + "libinput": { + "send_events": "enabled" + } + }, { "identifier": "0:6:Video_Bus", "name": "Video Bus", @@ -1192,7 +1298,9 @@ one seat. Each object has the following properties: "product": 6, "type": "keyboard", "xkb_active_layout_name": "English (US)", - "libinput_send_events": "enabled" + "libinput": { + "send_events": "enabled" + } }, { "identifier": "0:1:Power_Button", @@ -1201,7 +1309,9 @@ one seat. Each object has the following properties: "product": 1, "type": "keyboard", "xkb_active_layout_name": "English (US)", - "libinput_send_events": "enabled" + "libinput": { + "send_events": "enabled" + } } ] } diff --git a/swaymsg/main.c b/swaymsg/main.c index 716d2d2ed..e51c00d92 100644 --- a/swaymsg/main.c +++ b/swaymsg/main.c @@ -113,7 +113,7 @@ static const char *pretty_type_name(const char *name) { } static void pretty_print_input(json_object *i) { - json_object *id, *name, *type, *product, *vendor, *kbdlayout, *events; + json_object *id, *name, *type, *product, *vendor, *kbdlayout, *libinput; json_object_object_get_ex(i, "identifier", &id); json_object_object_get_ex(i, "name", &name); json_object_object_get_ex(i, "type", &type); @@ -139,8 +139,12 @@ static void pretty_print_input(json_object *i) { printf(" Active Keyboard Layout: %s\n", layout ? layout : "(unnamed)"); } - if (json_object_object_get_ex(i, "libinput_send_events", &events)) { - printf(" Libinput Send Events: %s\n", json_object_get_string(events)); + if (json_object_object_get_ex(i, "libinput", &libinput)) { + json_object *events; + if (json_object_object_get_ex(libinput, "send_events", &events)) { + printf(" Libinput Send Events: %s\n", + json_object_get_string(events)); + } } printf("\n"); From b7fe5097e949b9bfde6fd52f26d2599068833563 Mon Sep 17 00:00:00 2001 From: Peter Grayson Date: Fri, 8 Mar 2019 12:43:04 -0500 Subject: [PATCH 041/191] Add -p/--pretty option to swaymsg This new option forces pretty (non-raw/non-JSON) output. By default, when not using a tty, swaymsg outputs using the "raw" format. This makes it impossible to, for example, pipe the pretty output to a pager such as `less` since piping does not use a tty. The new -p/--pretty option gives the user explicit control over the output format while retaining the default tty-dependent behavior. Signed-off-by: Peter Grayson --- completions/bash/swaymsg | 2 ++ completions/fish/swaymsg.fish | 3 ++- completions/zsh/_swaymsg | 7 ++++--- swaymsg/main.c | 7 ++++++- swaymsg/swaymsg.1.scd | 3 +++ 5 files changed, 17 insertions(+), 5 deletions(-) diff --git a/completions/bash/swaymsg b/completions/bash/swaymsg index 20092bdc4..0b0119029 100644 --- a/completions/bash/swaymsg +++ b/completions/bash/swaymsg @@ -21,6 +21,7 @@ _swaymsg() short=( -h + -p -q -r -s @@ -30,6 +31,7 @@ _swaymsg() long=( --help + --pretty --quiet --raw --socket diff --git a/completions/fish/swaymsg.fish b/completions/fish/swaymsg.fish index 636e35006..3f1cc50c1 100644 --- a/completions/fish/swaymsg.fish +++ b/completions/fish/swaymsg.fish @@ -2,10 +2,11 @@ complete -f -c swaymsg complete -c swaymsg -s h -l help --description "Show help message and quit." +complete -c swaymsg -s p -l pretty --description "Use pretty output even when not using a tty." complete -c swaymsg -s q -l quiet --description "Sends the IPC message but does not print the response from sway." -complete -c swaymsg -s v -l version --description "Print the version (of swaymsg) and quit." complete -c swaymsg -s r -l raw --description "Use raw output even if using tty." complete -c swaymsg -s s -l socket -r --description "Use the specified socket path. Otherwise, swaymsg will ask where the socket is (which is the value of $SWAYSOCK, then of $I3SOCK)." +complete -c swaymsg -s v -l version --description "Print the version (of swaymsg) and quit." complete -c swaymsg -s t -l type -fr --description "Specify the type of IPC message." complete -c swaymsg -s t -l type -fra 'get_workspaces' --description "Gets a JSON-encoded list of workspaces and their status." diff --git a/completions/zsh/_swaymsg b/completions/zsh/_swaymsg index 0ba45d4a9..c11fa9905 100644 --- a/completions/zsh/_swaymsg +++ b/completions/zsh/_swaymsg @@ -28,10 +28,11 @@ types=( ) _arguments -s \ - '(-v --version)'{-v,--version}'[Show the version number and quit]' \ - '(-m --monitor)'{-m,--monitor}'[Monitor until killed (-t SUBSCRIBE only)]' \ '(-h --help)'{-h,--help}'[Show help message and quit]' \ + '(-m --monitor)'{-m,--monitor}'[Monitor until killed (-t SUBSCRIBE only)]' \ + '(-p --pretty)'{-p,--pretty}'[Use pretty output even when not using a tty]' \ '(-q --quiet)'{-q,--quiet}'[Be quiet]' \ '(-r --raw)'{-r,--raw}'[Use raw output even if using a tty]' \ '(-s --socket)'{-s,--socket}'[Use the specified socket path]:files:_files' \ - '(-t --type)'{-t,--type}'[Specify the message type]:type:{_describe "type" types}' + '(-t --type)'{-t,--type}'[Specify the message type]:type:{_describe "type" types}' \ + '(-v --version)'{-v,--version}'[Show the version number and quit]' diff --git a/swaymsg/main.c b/swaymsg/main.c index e51c00d92..a0ef7e3d9 100644 --- a/swaymsg/main.c +++ b/swaymsg/main.c @@ -326,6 +326,7 @@ int main(int argc, char **argv) { static struct option long_options[] = { {"help", no_argument, NULL, 'h'}, {"monitor", no_argument, NULL, 'm'}, + {"pretty", no_argument, NULL, 'p'}, {"quiet", no_argument, NULL, 'q'}, {"raw", no_argument, NULL, 'r'}, {"socket", required_argument, NULL, 's'}, @@ -339,6 +340,7 @@ int main(int argc, char **argv) { "\n" " -h, --help Show help message and quit.\n" " -m, --monitor Monitor until killed (-t SUBSCRIBE only)\n" + " -p, --pretty Use pretty output even when not using a tty\n" " -q, --quiet Be quiet.\n" " -r, --raw Use raw output even if using a tty\n" " -s, --socket Use the specified socket.\n" @@ -350,7 +352,7 @@ int main(int argc, char **argv) { int c; while (1) { int option_index = 0; - c = getopt_long(argc, argv, "hmqrs:t:v", long_options, &option_index); + c = getopt_long(argc, argv, "hmpqrs:t:v", long_options, &option_index); if (c == -1) { break; } @@ -358,6 +360,9 @@ int main(int argc, char **argv) { case 'm': // Monitor monitor = true; break; + case 'p': // Pretty + raw = false; + break; case 'q': // Quiet quiet = true; break; diff --git a/swaymsg/swaymsg.1.scd b/swaymsg/swaymsg.1.scd index 523db6cc0..1bcf956ad 100644 --- a/swaymsg/swaymsg.1.scd +++ b/swaymsg/swaymsg.1.scd @@ -19,6 +19,9 @@ _swaymsg_ [options...] [message] there is a malformed response or an invalid event type was requested, swaymsg will stop monitoring and exit. +*-p, --pretty* + Use raw output even when not using a tty. + *-q, --quiet* Sends the IPC message but does not print the response from sway. From 1a9145e40983aa84da0e08ec3806b6e6fc8983f8 Mon Sep 17 00:00:00 2001 From: Peter Grayson Date: Fri, 8 Mar 2019 14:39:35 -0500 Subject: [PATCH 042/191] Add missing swaymsg completions The `-m/--monitor` option was missing from the bash and fish completions. The `subscribe` IPC message type was missing from the bash, fish, and zsh completions. Signed-off-by: Peter Grayson --- completions/bash/swaymsg | 3 +++ completions/fish/swaymsg.fish | 2 ++ completions/zsh/_swaymsg | 1 + 3 files changed, 6 insertions(+) diff --git a/completions/bash/swaymsg b/completions/bash/swaymsg index 0b0119029..a333c9124 100644 --- a/completions/bash/swaymsg +++ b/completions/bash/swaymsg @@ -17,10 +17,12 @@ _swaymsg() 'get_binding_modes' 'get_config' 'send_tick' + 'subscribe' ) short=( -h + -m -p -q -r @@ -31,6 +33,7 @@ _swaymsg() long=( --help + --monitor --pretty --quiet --raw diff --git a/completions/fish/swaymsg.fish b/completions/fish/swaymsg.fish index 3f1cc50c1..25085f9a7 100644 --- a/completions/fish/swaymsg.fish +++ b/completions/fish/swaymsg.fish @@ -2,6 +2,7 @@ complete -f -c swaymsg complete -c swaymsg -s h -l help --description "Show help message and quit." +complete -c swaymsg -s m -l monitor --description "Monitor subscribed events until killed." complete -c swaymsg -s p -l pretty --description "Use pretty output even when not using a tty." complete -c swaymsg -s q -l quiet --description "Sends the IPC message but does not print the response from sway." complete -c swaymsg -s r -l raw --description "Use raw output even if using tty." @@ -20,3 +21,4 @@ complete -c swaymsg -s t -l type -fra 'get_binding_modes' --description "Gets a complete -c swaymsg -s t -l type -fra 'get_config' --description "Gets a JSON-encoded copy of the current configuration." complete -c swaymsg -s t -l type -fra 'get_seats' --description "Gets a JSON-encoded list of all seats, its properties and all assigned devices." complete -c swaymsg -s t -l type -fra 'send_tick' --description "Sends a tick event to all subscribed clients." +complete -c swaymsg -s t -l type -fra 'subscribe' --description "Subscribe to a list of event types." diff --git a/completions/zsh/_swaymsg b/completions/zsh/_swaymsg index c11fa9905..3f42a77d7 100644 --- a/completions/zsh/_swaymsg +++ b/completions/zsh/_swaymsg @@ -25,6 +25,7 @@ types=( 'get_binding_modes' 'get_config' 'send_tick' +'subscribe' ) _arguments -s \ From 5fab8a2ad4c05fb7bfdbb45915b8abf7ebeab5fe Mon Sep 17 00:00:00 2001 From: minus Date: Sat, 9 Mar 2019 11:06:09 +0100 Subject: [PATCH 043/191] Fix crash when moving window to scratchpad --- sway/ipc-json.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sway/ipc-json.c b/sway/ipc-json.c index e9564b049..bebe6dd7b 100644 --- a/sway/ipc-json.c +++ b/sway/ipc-json.c @@ -354,7 +354,8 @@ static void get_deco_rect(struct sway_container *c, struct wlr_box *deco_rect) { enum sway_container_layout parent_layout = container_parent_layout(c); if ((parent_layout != L_TABBED && parent_layout != L_STACKED && c->current.border != B_NORMAL) || - c->fullscreen_mode != FULLSCREEN_NONE) { + c->fullscreen_mode != FULLSCREEN_NONE || + c->workspace == NULL) { deco_rect->x = deco_rect->y = deco_rect->width = deco_rect->height = 0; return; } From a73e8f1328a247766f4ff78fdc73b38488dec385 Mon Sep 17 00:00:00 2001 From: Jeff Peeler Date: Fri, 8 Mar 2019 22:06:38 -0500 Subject: [PATCH 044/191] fix "directive argument is null" errors --- sway/criteria.c | 2 +- sway/input/seat.c | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/sway/criteria.c b/sway/criteria.c index 48ef39df4..5ad481452 100644 --- a/sway/criteria.c +++ b/sway/criteria.c @@ -624,8 +624,8 @@ struct criteria *criteria_parse(char *raw, char **error_arg) { in_quotes = false; } unescape(value); + sway_log(SWAY_DEBUG, "Found pair: %s=%s", name, value); } - sway_log(SWAY_DEBUG, "Found pair: %s=%s", name, value); if (!parse_token(criteria, name, value)) { *error_arg = error; goto cleanup; diff --git a/sway/input/seat.c b/sway/input/seat.c index 9888ddfcb..245326a1e 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -705,8 +705,7 @@ void seat_configure_xcursor(struct sway_seat *seat) { seat->cursor->xcursor_manager = wlr_xcursor_manager_create(cursor_theme, 24); if (sway_assert(seat->cursor->xcursor_manager, - "Cannot create XCursor manager for theme %s", - cursor_theme)) { + "Cannot create XCursor manager for theme")) { return; } } From 7580718d12ed752ed5565e28ce3d46c3a589ffe3 Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Fri, 8 Mar 2019 12:44:42 -0500 Subject: [PATCH 045/191] output_damage_whole_container: damage subsurfaces This adds an iterative call in `output_damage_whole_container` to damage the subsurfaces for all visible views that are inside of the container. This is needed to damage subsurfaces that extend outside the box of the container. Without this, those subsurfaces will create artifacts when moving or resizing. --- sway/desktop/output.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/sway/desktop/output.c b/sway/desktop/output.c index d7d3fc07e..54b9f2942 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -473,6 +473,17 @@ void output_damage_box(struct sway_output *output, struct wlr_box *_box) { wlr_output_damage_add_box(output->damage, &box); } +static void damage_child_views_iterator(struct sway_container *con, + void *data) { + if (!con->view || !view_is_visible(con->view)) { + return; + } + struct sway_output *output = data; + bool whole = true; + output_view_for_each_surface(output, con->view, damage_surface_iterator, + &whole); +} + void output_damage_whole_container(struct sway_output *output, struct sway_container *con) { // Pad the box by 1px, because the width is a double and might be a fraction @@ -484,6 +495,12 @@ void output_damage_whole_container(struct sway_output *output, }; scale_box(&box, output->wlr_output->scale); wlr_output_damage_add_box(output->damage, &box); + // Damage subsurfaces as well, which may extend outside the box + if (con->view) { + damage_child_views_iterator(con, output); + } else { + container_for_each_child(con, damage_child_views_iterator, output); + } } static void damage_handle_destroy(struct wl_listener *listener, void *data) { From c02d2a01289581fd3c32a22e1a498ffad736281d Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Fri, 8 Mar 2019 12:57:50 -0500 Subject: [PATCH 046/191] damage: remove output_damage_view This removes `output_damage_view` since it is unnecessary. The logic has been moved into its only caller `output_damage_from_view`. When damaging the whole view, `output_damage_whole_container` should be used instead --- sway/desktop/output.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/sway/desktop/output.c b/sway/desktop/output.c index 54b9f2942..3ff4d7262 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -450,19 +450,15 @@ void output_damage_surface(struct sway_output *output, double ox, double oy, damage_surface_iterator, &whole); } -static void output_damage_view(struct sway_output *output, - struct sway_view *view, bool whole) { +void output_damage_from_view(struct sway_output *output, + struct sway_view *view) { if (!view_is_visible(view)) { return; } + bool whole = false; output_view_for_each_surface(output, view, damage_surface_iterator, &whole); } -void output_damage_from_view(struct sway_output *output, - struct sway_view *view) { - output_damage_view(output, view, false); -} - // Expecting an unscaled box in layout coordinates void output_damage_box(struct sway_output *output, struct wlr_box *_box) { struct wlr_box box; From 9a75f0010f9bc448dfedb5de6e484719613d6428 Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Sun, 10 Mar 2019 15:00:15 -0400 Subject: [PATCH 047/191] arrange: use int not size_t for title offsets This changes `apply_tabbed_layout` and `apply_stacked_layout` to use `int` instead of `size_t`. This is necessary for tabbed and stacked containers to be positioned correctly when the y-location is negative. The reasoning for this is signed plus unsigned is always an unsigned value. This was causing the y-location of the container to be positioned near `INT_MIN` due to an unsigned integer underflow --- sway/tree/arrange.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sway/tree/arrange.c b/sway/tree/arrange.c index da372aa44..438a21335 100644 --- a/sway/tree/arrange.c +++ b/sway/tree/arrange.c @@ -99,7 +99,7 @@ static void apply_tabbed_layout(list_t *children, struct wlr_box *parent) { } for (int i = 0; i < children->length; ++i) { struct sway_container *child = children->items[i]; - size_t parent_offset = child->view ? 0 : container_titlebar_height(); + int parent_offset = child->view ? 0 : container_titlebar_height(); container_remove_gaps(child); child->x = parent->x; child->y = parent->y + parent_offset; @@ -115,7 +115,7 @@ static void apply_stacked_layout(list_t *children, struct wlr_box *parent) { } for (int i = 0; i < children->length; ++i) { struct sway_container *child = children->items[i]; - size_t parent_offset = child->view ? 0 : + int parent_offset = child->view ? 0 : container_titlebar_height() * children->length; container_remove_gaps(child); child->x = parent->x; From 83b68b21541ac00649c9d100cfec198326152fec Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Sun, 10 Mar 2019 15:09:23 -0400 Subject: [PATCH 048/191] Update language in sway.desktop & sway(1) --- sway.desktop | 2 +- sway/sway.1.scd | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sway.desktop b/sway.desktop index 98c9af293..420db5aa1 100644 --- a/sway.desktop +++ b/sway.desktop @@ -1,5 +1,5 @@ [Desktop Entry] Name=Sway -Comment=SirCmpwn's Wayland window manager +Comment=An i3-compatible Wayland compositor Exec=sway Type=Application diff --git a/sway/sway.1.scd b/sway/sway.1.scd index f8820c4e0..ba31e4a68 100644 --- a/sway/sway.1.scd +++ b/sway/sway.1.scd @@ -2,7 +2,7 @@ sway(1) # NAME -sway - SirCmpwn's Wayland window manager +sway - An i3-compatible Wayland compositor # SYNOPSIS From 191305468c0fe9b1acf8b6ad6c8a49b97cf793aa Mon Sep 17 00:00:00 2001 From: emersion Date: Sun, 10 Mar 2019 21:47:30 +0100 Subject: [PATCH 049/191] Fix crash in cmd_workspace when layer surface has focus --- sway/commands/workspace.c | 5 +++++ sway/input/seat.c | 4 ++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/sway/commands/workspace.c b/sway/commands/workspace.c index 5fde81299..362dcd1b2 100644 --- a/sway/commands/workspace.c +++ b/sway/commands/workspace.c @@ -184,6 +184,11 @@ struct cmd_results *cmd_workspace(int argc, char **argv) { bool create = argc > 1 && strcasecmp(argv[1], "--create") == 0; struct sway_seat *seat = config->handler_context.seat; struct sway_workspace *current = seat_get_focused_workspace(seat); + if (!current) { + return cmd_results_new(CMD_FAILURE, "workspace", + "No workspace to switch from"); + } + struct sway_workspace *ws = NULL; if (strcasecmp(argv[0], "number") == 0) { if (argc < 2) { diff --git a/sway/input/seat.c b/sway/input/seat.c index 245326a1e..3a68904bc 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -1101,7 +1101,7 @@ struct sway_node *seat_get_focus(struct sway_seat *seat) { } struct sway_workspace *seat_get_focused_workspace(struct sway_seat *seat) { - struct sway_node *focus = seat_get_focus(seat); + struct sway_node *focus = seat_get_focus_inactive(seat, &root->node); if (!focus) { return NULL; } @@ -1111,7 +1111,7 @@ struct sway_workspace *seat_get_focused_workspace(struct sway_seat *seat) { if (focus->type == N_WORKSPACE) { return focus->sway_workspace; } - return NULL; // unreachable + return NULL; // output doesn't have a workspace yet } struct sway_container *seat_get_focused_container(struct sway_seat *seat) { From 30931ad9e77b106956007530821d98bc20bca969 Mon Sep 17 00:00:00 2001 From: Ben Challenor Date: Sun, 10 Mar 2019 20:26:50 +0000 Subject: [PATCH 050/191] Make raw keysyms take precedence over translated Allows both BackSpace and Shift+BackSpace to be bound under the US keyboard layout, per #3705. --- sway/input/keyboard.c | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/sway/input/keyboard.c b/sway/input/keyboard.c index 00fc6a13f..06ae99c49 100644 --- a/sway/input/keyboard.c +++ b/sway/input/keyboard.c @@ -292,32 +292,32 @@ static void handle_keyboard_key(struct wl_listener *listener, void *data) { // Identify new keycode, raw keysym(s), and translated keysym(s) xkb_keycode_t keycode = event->keycode + 8; + const xkb_keysym_t *raw_keysyms; + uint32_t raw_modifiers; + size_t raw_keysyms_len = + keyboard_keysyms_raw(keyboard, keycode, &raw_keysyms, &raw_modifiers); + const xkb_keysym_t *translated_keysyms; uint32_t translated_modifiers; size_t translated_keysyms_len = keyboard_keysyms_translated(keyboard, keycode, &translated_keysyms, &translated_modifiers); - const xkb_keysym_t *raw_keysyms; - uint32_t raw_modifiers; - size_t raw_keysyms_len = - keyboard_keysyms_raw(keyboard, keycode, &raw_keysyms, &raw_modifiers); - uint32_t code_modifiers = wlr_keyboard_get_modifiers(wlr_device->keyboard); // Update shortcut model state update_shortcut_state(&keyboard->state_keycodes, event, (uint32_t)keycode, code_modifiers); - for (size_t i = 0; i < translated_keysyms_len; ++i) { - update_shortcut_state(&keyboard->state_keysyms_translated, - event, (uint32_t)translated_keysyms[i], - code_modifiers); - } for (size_t i = 0; i < raw_keysyms_len; ++i) { update_shortcut_state(&keyboard->state_keysyms_raw, event, (uint32_t)raw_keysyms[i], code_modifiers); } + for (size_t i = 0; i < translated_keysyms_len; ++i) { + update_shortcut_state(&keyboard->state_keysyms_translated, + event, (uint32_t)translated_keysyms[i], + code_modifiers); + } bool handled = false; @@ -326,12 +326,12 @@ static void handle_keyboard_key(struct wl_listener *listener, void *data) { get_active_binding(&keyboard->state_keycodes, config->current_mode->keycode_bindings, &binding_released, code_modifiers, true, input_inhibited, device_identifier); - get_active_binding(&keyboard->state_keysyms_translated, - config->current_mode->keysym_bindings, &binding_released, - translated_modifiers, true, input_inhibited, device_identifier); get_active_binding(&keyboard->state_keysyms_raw, config->current_mode->keysym_bindings, &binding_released, raw_modifiers, true, input_inhibited, device_identifier); + get_active_binding(&keyboard->state_keysyms_translated, + config->current_mode->keysym_bindings, &binding_released, + translated_modifiers, true, input_inhibited, device_identifier); // Execute stored release binding once no longer active if (keyboard->held_binding && binding_released != keyboard->held_binding && @@ -352,13 +352,13 @@ static void handle_keyboard_key(struct wl_listener *listener, void *data) { get_active_binding(&keyboard->state_keycodes, config->current_mode->keycode_bindings, &binding, code_modifiers, false, input_inhibited, device_identifier); + get_active_binding(&keyboard->state_keysyms_raw, + config->current_mode->keysym_bindings, &binding, + raw_modifiers, false, input_inhibited, device_identifier); get_active_binding(&keyboard->state_keysyms_translated, config->current_mode->keysym_bindings, &binding, translated_modifiers, false, input_inhibited, device_identifier); - get_active_binding(&keyboard->state_keysyms_raw, - config->current_mode->keysym_bindings, &binding, - raw_modifiers, false, input_inhibited, device_identifier); } // Set up (or clear) keyboard repeat for a pressed binding. Since the From a418349c188288ebe1e8aeb9cb002e484d3e1f17 Mon Sep 17 00:00:00 2001 From: emersion Date: Sun, 10 Mar 2019 22:59:15 +0100 Subject: [PATCH 051/191] meson: use pkg-config var for scdoc path --- meson.build | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/meson.build b/meson.build index a08b2603d..02b5d606c 100644 --- a/meson.build +++ b/meson.build @@ -57,7 +57,7 @@ elogind = dependency('libelogind', version: '>=239', required: false) xcb = dependency('xcb', required: get_option('xwayland')) math = cc.find_library('m') rt = cc.find_library('rt') -git = find_program('git', required: false) +git = find_program('git', native: true, required: false) # Try first to find wlroots as a subproject, then as a system dependency wlroots_version = '>=0.4.1' @@ -97,8 +97,8 @@ conf_data.set10('HAVE_TRAY', have_tray) scdoc = dependency('scdoc', version: '>=1.9.2', native: true, required: get_option('man-pages')) if scdoc.found() - scdoc_prog = find_program('scdoc') - sh = find_program('sh') + scdoc_prog = find_program(scdoc.get_pkgconfig_variable('scdoc'), native: true) + sh = find_program('sh', native: true) mandir = get_option('mandir') man_files = [ 'sway/sway.1.scd', From a729bda17fb25afcf043dd740e304b8c2c1302a1 Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Sun, 10 Mar 2019 18:43:22 -0400 Subject: [PATCH 052/191] fullscreen: init floating on disable without size If a container gets mapped as fullscreen and set to floating by criteria, the size and location are never set for the floating container. This adds a check in container_fullscreen_disable for a width or height of 0 and calls container_init_floating --- sway/tree/container.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/sway/tree/container.c b/sway/tree/container.c index a6142193f..93cff7ff5 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -984,6 +984,13 @@ void container_fullscreen_disable(struct sway_container *con) { root->fullscreen_global = NULL; } + // If the container was mapped as fullscreen and set as floating by + // criteria, it needs to be reinitialized as floating to get the proper + // size and location + if (container_is_floating(con) && (con->width == 0 || con->height == 0)) { + container_init_floating(con); + } + con->fullscreen_mode = FULLSCREEN_NONE; container_end_mouse_operation(con); ipc_event_window(con, "fullscreen_mode"); From 20c4d242f64f68fc89d72c364506fa7bd854904e Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Mon, 11 Mar 2019 00:10:18 -0400 Subject: [PATCH 053/191] detect_proprietary: use strncmp Only the main nvidia module needs to be blocked. Others such as nvidiafb are benign and do not need to be blocked --- sway/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sway/main.c b/sway/main.c index 22b728131..f3cc1bc8b 100644 --- a/sway/main.c +++ b/sway/main.c @@ -91,7 +91,7 @@ void detect_proprietary(int allow_unsupported_gpu) { char *line = NULL; size_t line_size = 0; while (getline(&line, &line_size, f) != -1) { - if (strstr(line, "nvidia")) { + if (strncmp(line, "nvidia ", 7) == 0) { if (allow_unsupported_gpu) { sway_log(SWAY_ERROR, "!!! Proprietary Nvidia drivers are in use !!!"); From 017a7c4da169ace601f282c87a9aaabc138a34d3 Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Sat, 9 Mar 2019 02:23:20 -0500 Subject: [PATCH 054/191] sway_view_child: add listener for view unmap Since not all child views's have an unmap event, it is possible for it to still be mapped (default state) in the destruction handler. When the destruction handler is called, the corresponding view may have already been freed and the memory location reallocated. This adds a listener for the view unmapping and removes the mapped status. This ensures that the child view is damaged due to destruction while the view still exists and not after. --- include/sway/tree/view.h | 1 + sway/tree/view.c | 12 ++++++++++++ 2 files changed, 13 insertions(+) diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h index 5cc9777b4..ac203ac79 100644 --- a/include/sway/tree/view.h +++ b/include/sway/tree/view.h @@ -202,6 +202,7 @@ struct sway_view_child { struct wl_listener surface_map; struct wl_listener surface_unmap; struct wl_listener surface_destroy; + struct wl_listener view_unmap; }; struct sway_subsurface { diff --git a/sway/tree/view.c b/sway/tree/view.c index 14cc07d9f..4759c9982 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -820,6 +820,14 @@ static void view_child_handle_surface_unmap(struct wl_listener *listener, child->mapped = false; } +static void view_child_handle_view_unmap(struct wl_listener *listener, + void *data) { + struct sway_view_child *child = + wl_container_of(listener, child, view_unmap); + view_child_damage(child, true); + child->mapped = false; +} + void view_child_init(struct sway_view_child *child, const struct sway_view_child_impl *impl, struct sway_view *view, struct wlr_surface *surface) { @@ -840,6 +848,9 @@ void view_child_init(struct sway_view_child *child, child->surface_map.notify = view_child_handle_surface_map; child->surface_unmap.notify = view_child_handle_surface_unmap; + wl_signal_add(&view->events.unmap, &child->view_unmap); + child->view_unmap.notify = view_child_handle_view_unmap; + struct sway_output *output = child->view->container->workspace->output; wlr_surface_send_enter(child->surface, output->wlr_output); @@ -853,6 +864,7 @@ void view_child_destroy(struct sway_view_child *child) { wl_list_remove(&child->surface_commit.link); wl_list_remove(&child->surface_destroy.link); + wl_list_remove(&child->view_unmap.link); if (child->impl && child->impl->destroy) { child->impl->destroy(child); From 5c8424c0743c36b11baabe8b316ee6daecd0cfb8 Mon Sep 17 00:00:00 2001 From: Ian Fan Date: Thu, 17 Jan 2019 16:47:44 +0000 Subject: [PATCH 055/191] stringop.c: remove unused functions The only use of `join_list` in swaybar/tray/icon.c has been rewritten. --- common/stringop.c | 115 -------------------------------------------- include/stringop.h | 13 ----- swaybar/tray/icon.c | 47 ++++++++++++++---- 3 files changed, 38 insertions(+), 137 deletions(-) diff --git a/common/stringop.c b/common/stringop.c index 709be6842..b9c9a1939 100644 --- a/common/stringop.c +++ b/common/stringop.c @@ -150,29 +150,6 @@ void free_argv(int argc, char **argv) { free(argv); } -char *code_strstr(const char *haystack, const char *needle) { - /* TODO */ - return strstr(haystack, needle); -} - -char *code_strchr(const char *str, char delimiter) { - int in_string = 0, in_character = 0; - int i = 0; - while (str[i] != '\0') { - if (str[i] == '"' && !in_character) { - in_string = !in_string; - } else if (str[i] == '\'' && !in_string) { - in_character = !in_character; - } else if (!in_character && !in_string) { - if (str[i] == delimiter) { - return (char *)str + i; - } - } - ++i; - } - return NULL; -} - int unescape_string(char *string) { /* TODO: More C string escapes */ int len = strlen(string); @@ -276,84 +253,6 @@ char *join_args(char **argv, int argc) { return res; } -static bool has_whitespace(const char *str) { - while (*str) { - if (isspace(*str)) { - return true; - } - ++str; - } - return false; -} - -/** - * Add quotes around any argv with whitespaces. - */ -void add_quotes(char **argv, int argc) { - int i; - for (i = 0; i < argc; ++i) { - if (has_whitespace(argv[i])) { - int len = strlen(argv[i]) + 3; - char *tmp = argv[i]; - argv[i] = malloc(len * sizeof(char)); - snprintf(argv[i], len, "\"%s\"", tmp); - free(tmp); - } - } -} - -/* - * Join a list of strings, adding separator in between. Separator can be NULL. - */ -char *join_list(list_t *list, char *separator) { - if (!sway_assert(list != NULL, "list != NULL") || list->length == 0) { - return NULL; - } - - size_t len = 1; // NULL terminator - size_t sep_len = 0; - if (separator != NULL) { - sep_len = strlen(separator); - len += (list->length - 1) * sep_len; - } - - for (int i = 0; i < list->length; i++) { - len += strlen(list->items[i]); - } - - char *res = malloc(len); - - char *p = res + strlen(list->items[0]); - strcpy(res, list->items[0]); - - for (int i = 1; i < list->length; i++) { - if (sep_len) { - memcpy(p, separator, sep_len); - p += sep_len; - } - strcpy(p, list->items[i]); - p += strlen(list->items[i]); - } - - *p = '\0'; - - return res; -} - -char *cmdsep(char **stringp, const char *delim) { - // skip over leading delims - char *head = *stringp + strspn(*stringp, delim); - // Find end token - char *tail = *stringp += strcspn(*stringp, delim); - // Set stringp to beginning of next token - *stringp += strspn(*stringp, delim); - // Set stringp to null if last token - if (!**stringp) *stringp = NULL; - // Nullify end of first token - *tail = 0; - return head; -} - char *argsep(char **stringp, const char *delim) { char *start = *stringp; char *end = start; @@ -389,17 +288,3 @@ char *argsep(char **stringp, const char *delim) { found: return start; } - -const char *strcasestr(const char *haystack, const char *needle) { - size_t needle_len = strlen(needle); - const char *pos = haystack; - const char *end = pos + strlen(haystack) - needle_len; - - while (pos <= end) { - if (strncasecmp(pos, needle, needle_len) == 0) { - return pos; - } - ++pos; - } - return NULL; -} diff --git a/include/stringop.h b/include/stringop.h index f7ca60a51..6f920999a 100644 --- a/include/stringop.h +++ b/include/stringop.h @@ -4,7 +4,6 @@ #include "list.h" void strip_whitespace(char *str); -char *strip_comments(char *str); void strip_quotes(char *str); // strcat that does nothing if dest or src is NULL @@ -21,22 +20,10 @@ list_t *split_string(const char *str, const char *delims); char **split_args(const char *str, int *argc); void free_argv(int argc, char **argv); -char *code_strchr(const char *string, char delimiter); -char *code_strstr(const char *haystack, const char *needle); int unescape_string(char *string); char *join_args(char **argv, int argc); -char *join_list(list_t *list, char *separator); -/** - * Add quotes around any argv with whitespaces. - */ -void add_quotes(char **argv, int argc); - -// split string into 2 by delim. -char *cmdsep(char **stringp, const char *delim); // Split string into 2 by delim, handle quotes char *argsep(char **stringp, const char *delim); -const char *strcasestr(const char *haystack, const char *needle); - #endif diff --git a/swaybar/tray/icon.c b/swaybar/tray/icon.c index bf2736c2a..c7ce20b4d 100644 --- a/swaybar/tray/icon.c +++ b/swaybar/tray/icon.c @@ -301,6 +301,43 @@ static list_t *load_themes_in_dir(char *basedir) { return themes; } +static void log_loaded_themes(list_t *themes) { + if (themes->length == 0) { + sway_log(SWAY_INFO, "Warning: no icon themes loaded"); + return; + } + + const char *sep = ", "; + size_t sep_len = strlen(sep); + + size_t len = 1 - sep_len; + for (int i = 0; i < themes->length; ++i) { + struct icon_theme *theme = themes->items[i]; + len += strlen(theme->name) + sep_len; + } + + char *str = malloc(len); + if (!str) { + return; + } + char *p = str; + for (int i = 0; i < themes->length; ++i) { + if (i > 0) { + memcpy(p, sep, sep_len); + p += sep_len; + } + + struct icon_theme *theme = themes->items[i]; + size_t name_len = strlen(theme->name); + memcpy(p, theme->name, name_len); + p += name_len; + } + *p = '\0'; + + sway_log(SWAY_DEBUG, "Loaded icon themes: %s", str); + free(str); +} + void init_themes(list_t **themes, list_t **basedirs) { *basedirs = get_basedirs(); @@ -311,15 +348,7 @@ void init_themes(list_t **themes, list_t **basedirs) { list_free(dir_themes); } - list_t *theme_names = create_list(); - for (int i = 0; i < (*themes)->length; ++i) { - struct icon_theme *theme = (*themes)->items[i]; - list_add(theme_names, theme->name); - } - char *theme_list = join_list(theme_names, ", "); - sway_log(SWAY_DEBUG, "Loaded themes: %s", theme_list); - free(theme_list); - list_free(theme_names); + log_loaded_themes(*themes); } void finish_themes(list_t *themes, list_t *basedirs) { From be1543d301ecd6933b87c90ec527d235af49377c Mon Sep 17 00:00:00 2001 From: Ian Fan Date: Thu, 17 Jan 2019 16:57:34 +0000 Subject: [PATCH 056/191] stringop.c: clean up headers --- common/stringop.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/common/stringop.c b/common/stringop.c index b9c9a1939..b42604c17 100644 --- a/common/stringop.c +++ b/common/stringop.c @@ -1,13 +1,13 @@ #define _POSIX_C_SOURCE 200809L -#include +#include +#include #include +#include #include #include -#include -#include "stringop.h" -#include "log.h" -#include "string.h" #include "list.h" +#include "log.h" +#include "stringop.h" static const char whitespace[] = " \f\n\r\t\v"; From 00570c139e037d5d88f1683bb71b42a7f34139b8 Mon Sep 17 00:00:00 2001 From: Ian Fan Date: Thu, 17 Jan 2019 16:56:04 +0000 Subject: [PATCH 057/191] stringop.c: refactor a few functions --- common/stringop.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/common/stringop.c b/common/stringop.c index b42604c17..dea152cce 100644 --- a/common/stringop.c +++ b/common/stringop.c @@ -78,12 +78,10 @@ int lenient_strcmp(char *a, char *b) { list_t *split_string(const char *str, const char *delims) { list_t *res = create_list(); char *copy = strdup(str); - char *token; - token = strtok(copy, delims); - while(token) { - token = strdup(token); - list_add(res, token); + char *token = strtok(copy, delims); + while (token) { + list_add(res, strdup(token)); token = strtok(NULL, delims); } free(copy); @@ -268,13 +266,13 @@ char *argsep(char **stringp, const char *delim) { escaped = !escaped; } else if (*end == '\0') { *stringp = NULL; - goto found; + break; } else if (!in_string && !in_char && !escaped && strchr(delim, *end)) { if (end - start) { *(end++) = 0; *stringp = end + strspn(end, delim);; if (!**stringp) *stringp = NULL; - goto found; + break; } else { ++start; end = start; @@ -285,6 +283,5 @@ char *argsep(char **stringp, const char *delim) { } ++end; } - found: return start; } From d16845d5f0f7ea0316fb9d5e2b7d20b78ca41991 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Wed, 6 Mar 2019 18:54:41 +1000 Subject: [PATCH 058/191] Don't send button events to surfaces when dragging or resizing It turns out sending button events during all seat operations is not desirable. This patch introduces a new property `seatop_impl.allows_events` which allows each operation to define whether button events should be passed to the surface or not. The `down` seat operation is the only one that supports this. As all the other seatops don't support it, the calls to seat_pointer_notify_button prior to starting them have been removed. --- include/sway/input/seat.h | 3 +++ sway/input/cursor.c | 11 +++-------- sway/input/seat.c | 4 ++++ sway/input/seatop_down.c | 1 + 4 files changed, 11 insertions(+), 8 deletions(-) diff --git a/include/sway/input/seat.h b/include/sway/input/seat.h index 0f5dab983..eb674b705 100644 --- a/include/sway/input/seat.h +++ b/include/sway/input/seat.h @@ -15,6 +15,7 @@ struct sway_seatop_impl { void (*unref)(struct sway_seat *seat, struct sway_container *con); void (*render)(struct sway_seat *seat, struct sway_output *output, pixman_region32_t *damage); + bool allows_events; }; struct sway_seat_device { @@ -239,4 +240,6 @@ void seatop_unref(struct sway_seat *seat, struct sway_container *con); void seatop_render(struct sway_seat *seat, struct sway_output *output, pixman_region32_t *damage); +bool seatop_allows_events(struct sway_seat *seat); + #endif diff --git a/sway/input/cursor.c b/sway/input/cursor.c index b96fde889..ef03c6aa4 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -613,7 +613,9 @@ void dispatch_cursor_button(struct sway_cursor *cursor, } else { state_erase_button(cursor, button); } - seat_pointer_notify_button(seat, time_msec, button, state); + if (seatop_allows_events(seat)) { + seat_pointer_notify_button(seat, time_msec, button, state); + } return; } @@ -682,7 +684,6 @@ void dispatch_cursor_button(struct sway_cursor *cursor, if (cont && resize_edge && button == BTN_LEFT && state == WLR_BUTTON_PRESSED && !is_floating) { seat_set_focus_container(seat, cont); - seat_pointer_notify_button(seat, time_msec, button, state); seatop_begin_resize_tiling(seat, cont, button, edge); return; } @@ -713,7 +714,6 @@ void dispatch_cursor_button(struct sway_cursor *cursor, } cursor_set_image(seat->cursor, image, NULL); seat_set_focus_container(seat, cont); - seat_pointer_notify_button(seat, time_msec, button, state); seatop_begin_resize_tiling(seat, cont, button, edge); return; } @@ -729,7 +729,6 @@ void dispatch_cursor_button(struct sway_cursor *cursor, cont = cont->parent; } seat_set_focus_container(seat, cont); - seat_pointer_notify_button(seat, time_msec, button, state); seatop_begin_move_floating(seat, cont, button); return; } @@ -740,7 +739,6 @@ void dispatch_cursor_button(struct sway_cursor *cursor, state == WLR_BUTTON_PRESSED) { // Via border if (button == BTN_LEFT && resize_edge != WLR_EDGE_NONE) { - seat_pointer_notify_button(seat, time_msec, button, state); seatop_begin_resize_floating(seat, cont, button, resize_edge); return; } @@ -758,7 +756,6 @@ void dispatch_cursor_button(struct sway_cursor *cursor, WLR_EDGE_RIGHT : WLR_EDGE_LEFT; edge |= cursor->cursor->y > floater->y + floater->height / 2 ? WLR_EDGE_BOTTOM : WLR_EDGE_TOP; - seat_pointer_notify_button(seat, time_msec, button, state); seatop_begin_resize_floating(seat, floater, button, edge); return; } @@ -775,8 +772,6 @@ void dispatch_cursor_button(struct sway_cursor *cursor, seat_set_focus(seat, node); } - seat_pointer_notify_button(seat, time_msec, button, state); - // If moving a container by it's title bar, use a threshold for the drag if (!mod_pressed && config->tiling_drag_threshold > 0) { seatop_begin_move_tiling_threshold(seat, cont, button); diff --git a/sway/input/seat.c b/sway/input/seat.c index 3a68904bc..be5235390 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -1234,3 +1234,7 @@ void seatop_render(struct sway_seat *seat, struct sway_output *output, seat->seatop_impl->render(seat, output, damage); } } + +bool seatop_allows_events(struct sway_seat *seat) { + return seat->seatop_impl && seat->seatop_impl->allows_events; +} diff --git a/sway/input/seatop_down.c b/sway/input/seatop_down.c index 7f3940957..895571b10 100644 --- a/sway/input/seatop_down.c +++ b/sway/input/seatop_down.c @@ -58,6 +58,7 @@ static const struct sway_seatop_impl seatop_impl = { .finish = handle_finish, .abort = handle_abort, .unref = handle_unref, + .allows_events = true, }; void seatop_begin_down(struct sway_seat *seat, struct sway_container *con, From 783b3d6b378db0bdf2a10bfa8a6787bd4b152a5a Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Mon, 11 Mar 2019 18:11:01 +1000 Subject: [PATCH 059/191] Fix click behaviour By the time seatop_allows_events was called, seatop_impl was already NULL, causing the function to always return false. This means a press event was sent to clients without a corresponding release event. This patch moves the call to seatop_finish to after the seatop_allows_events check. --- sway/input/cursor.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sway/input/cursor.c b/sway/input/cursor.c index ef03c6aa4..d531a20e3 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -605,9 +605,6 @@ void dispatch_cursor_button(struct sway_cursor *cursor, // Handle existing seat operation if (seat_doing_seatop(seat)) { - if (button == seat->seatop_button && state == WLR_BUTTON_RELEASED) { - seatop_finish(seat, time_msec); - } if (state == WLR_BUTTON_PRESSED) { state_add_button(cursor, button); } else { @@ -616,6 +613,9 @@ void dispatch_cursor_button(struct sway_cursor *cursor, if (seatop_allows_events(seat)) { seat_pointer_notify_button(seat, time_msec, button, state); } + if (button == seat->seatop_button && state == WLR_BUTTON_RELEASED) { + seatop_finish(seat, time_msec); + } return; } From bcde298a719f60b9913133dbd2a169dedbc8dd7d Mon Sep 17 00:00:00 2001 From: emersion Date: Mon, 11 Mar 2019 17:00:06 +0100 Subject: [PATCH 060/191] Fix size_t temporary underflow in log_loaded_themes `len` will underflow but will overflow right after, so it's not as bad as it may appear. Still better not to under/overflow at all. Fixes https://github.com/swaywm/sway/issues/3862 --- swaybar/tray/icon.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/swaybar/tray/icon.c b/swaybar/tray/icon.c index c7ce20b4d..2276e36de 100644 --- a/swaybar/tray/icon.c +++ b/swaybar/tray/icon.c @@ -307,16 +307,16 @@ static void log_loaded_themes(list_t *themes) { return; } - const char *sep = ", "; + const char sep[] = ", "; size_t sep_len = strlen(sep); - size_t len = 1 - sep_len; + size_t len = 0; for (int i = 0; i < themes->length; ++i) { struct icon_theme *theme = themes->items[i]; len += strlen(theme->name) + sep_len; } - char *str = malloc(len); + char *str = malloc(len + 1); if (!str) { return; } From 076257a978ce5f93b9b1613e43a067e602b5b041 Mon Sep 17 00:00:00 2001 From: emersion Date: Mon, 11 Mar 2019 11:45:01 +0100 Subject: [PATCH 061/191] Stop using wlr_output->{lx,ly} Also fixes sway_output->{lx,ly,width,height} not being updated. Also fixes output_get_in_direction adding buffer coords to layout coords. --- include/sway/output.h | 6 ++-- sway/config/output.c | 8 +++++ sway/desktop/desktop.c | 6 ++-- sway/desktop/output.c | 24 +++++++-------- sway/desktop/render.c | 53 +++++++++++++++++---------------- sway/input/seatop_move_tiling.c | 2 +- sway/tree/arrange.c | 4 +-- sway/tree/output.c | 11 +++---- 8 files changed, 61 insertions(+), 53 deletions(-) diff --git a/include/sway/output.h b/include/sway/output.h index ea7a21741..32ed1e28e 100644 --- a/include/sway/output.h +++ b/include/sway/output.h @@ -29,8 +29,8 @@ struct sway_output { struct timespec last_frame; struct wlr_output_damage *damage; - int lx, ly; - int width, height; + int lx, ly; // layout coords + int width, height; // transformed buffer size bool enabled, configured; list_t *workspaces; @@ -144,7 +144,7 @@ void output_get_box(struct sway_output *output, struct wlr_box *box); enum sway_container_layout output_get_default_layout( struct sway_output *output); -void render_rect(struct wlr_output *wlr_output, +void render_rect(struct sway_output *output, pixman_region32_t *output_damage, const struct wlr_box *_box, float color[static 4]); diff --git a/sway/config/output.c b/sway/config/output.c index 3a36ed180..1f55fd6ab 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -319,6 +319,14 @@ bool apply_output_config(struct output_config *oc, struct sway_output *output) { wlr_output_layout_add_auto(root->output_layout, wlr_output); } + // Update output->{lx, ly, width, height} + struct wlr_box *output_box = + wlr_output_layout_get_box(root->output_layout, wlr_output); + output->lx = output_box->x; + output->ly = output_box->y; + wlr_output_transformed_resolution(wlr_output, + &output->width, &output->height); + if (output->swaybg_client != NULL) { wl_client_destroy(output->swaybg_client); } diff --git a/sway/desktop/desktop.c b/sway/desktop/desktop.c index d8dd02403..ec45d80a1 100644 --- a/sway/desktop/desktop.c +++ b/sway/desktop/desktop.c @@ -6,8 +6,10 @@ void desktop_damage_surface(struct wlr_surface *surface, double lx, double ly, bool whole) { for (int i = 0; i < root->outputs->length; ++i) { struct sway_output *output = root->outputs->items[i]; - output_damage_surface(output, lx - output->wlr_output->lx, - ly - output->wlr_output->ly, surface, whole); + struct wlr_box *output_box = wlr_output_layout_get_box( + root->output_layout, output->wlr_output); + output_damage_surface(output, lx - output_box->x, + ly - output_box->y, surface, whole); } } diff --git a/sway/desktop/output.c b/sway/desktop/output.c index 3ff4d7262..1d9abbfd9 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -150,9 +150,9 @@ void output_view_for_each_surface(struct sway_output *output, .user_iterator = iterator, .user_data = user_data, .output = output, - .ox = view->container->current.content_x - output->wlr_output->lx + .ox = view->container->current.content_x - output->lx - view->geometry.x, - .oy = view->container->current.content_y - output->wlr_output->ly + .oy = view->container->current.content_y - output->ly - view->geometry.y, .width = view->container->current.content_width, .height = view->container->current.content_height, @@ -169,9 +169,9 @@ void output_view_for_each_popup(struct sway_output *output, .user_iterator = iterator, .user_data = user_data, .output = output, - .ox = view->container->current.content_x - output->wlr_output->lx + .ox = view->container->current.content_x - output->lx - view->geometry.x, - .oy = view->container->current.content_y - output->wlr_output->ly + .oy = view->container->current.content_y - output->ly - view->geometry.y, .width = view->container->current.content_width, .height = view->container->current.content_height, @@ -202,8 +202,8 @@ void output_unmanaged_for_each_surface(struct sway_output *output, wl_list_for_each(unmanaged_surface, unmanaged, link) { struct wlr_xwayland_surface *xsurface = unmanaged_surface->wlr_xwayland_surface; - double ox = unmanaged_surface->lx - output->wlr_output->lx; - double oy = unmanaged_surface->ly - output->wlr_output->ly; + double ox = unmanaged_surface->lx - output->lx; + double oy = unmanaged_surface->ly - output->ly; output_surface_for_each_surface(output, xsurface->surface, ox, oy, iterator, user_data); @@ -216,8 +216,8 @@ void output_drag_icons_for_each_surface(struct sway_output *output, void *user_data) { struct sway_drag_icon *drag_icon; wl_list_for_each(drag_icon, drag_icons, link) { - double ox = drag_icon->x - output->wlr_output->lx; - double oy = drag_icon->y - output->wlr_output->ly; + double ox = drag_icon->x - output->lx; + double oy = drag_icon->y - output->ly; if (drag_icon->wlr_drag_icon->mapped) { output_surface_for_each_surface(output, @@ -463,8 +463,8 @@ void output_damage_from_view(struct sway_output *output, void output_damage_box(struct sway_output *output, struct wlr_box *_box) { struct wlr_box box; memcpy(&box, _box, sizeof(struct wlr_box)); - box.x -= output->wlr_output->lx; - box.y -= output->wlr_output->ly; + box.x -= output->lx; + box.y -= output->ly; scale_box(&box, output->wlr_output->scale); wlr_output_damage_add_box(output->damage, &box); } @@ -484,8 +484,8 @@ void output_damage_whole_container(struct sway_output *output, struct sway_container *con) { // Pad the box by 1px, because the width is a double and might be a fraction struct wlr_box box = { - .x = con->current.x - output->wlr_output->lx - 1, - .y = con->current.y - output->wlr_output->ly - 1, + .x = con->current.x - output->lx - 1, + .y = con->current.y - output->ly - 1, .width = con->current.width + 2, .height = con->current.height + 2, }; diff --git a/sway/desktop/render.c b/sway/desktop/render.c index 4b36a9c24..9e936bb53 100644 --- a/sway/desktop/render.c +++ b/sway/desktop/render.c @@ -157,16 +157,17 @@ static void render_drag_icons(struct sway_output *output, // _box.x and .y are expected to be layout-local // _box.width and .height are expected to be output-buffer-local -void render_rect(struct wlr_output *wlr_output, +void render_rect(struct sway_output *output, pixman_region32_t *output_damage, const struct wlr_box *_box, float color[static 4]) { + struct wlr_output *wlr_output = output->wlr_output; 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; + box.x -= output->lx * wlr_output->scale; + box.y -= output->ly * wlr_output->scale; pixman_region32_t damage; pixman_region32_init(&damage); @@ -205,9 +206,9 @@ static void render_view_toplevels(struct sway_view *view, }; // Render all toplevels without descending into popups double ox = view->container->surface_x - - output->wlr_output->lx - view->geometry.x; + output->lx - view->geometry.x; double oy = view->container->surface_y - - output->wlr_output->ly - view->geometry.y; + output->ly - view->geometry.y; output_surface_for_each_surface(output, view->surface, ox, oy, render_surface_iterator, &data); } @@ -240,9 +241,9 @@ static void render_saved_view(struct sway_view *view, return; } struct wlr_box box = { - .x = view->container->surface_x - output->wlr_output->lx - + .x = view->container->surface_x - output->lx - view->saved_geometry.x, - .y = view->container->surface_y - output->wlr_output->ly - + .y = view->container->surface_y - output->ly - view->saved_geometry.y, .width = view->saved_buffer_width, .height = view->saved_buffer_height, @@ -298,7 +299,7 @@ static void render_view(struct sway_output *output, pixman_region32_t *damage, box.width = state->border_thickness; box.height = state->content_height; scale_box(&box, output_scale); - render_rect(output->wlr_output, damage, &box, color); + render_rect(output, damage, &box, color); } list_t *siblings = container_get_current_siblings(con); @@ -317,7 +318,7 @@ static void render_view(struct sway_output *output, pixman_region32_t *damage, box.width = state->border_thickness; box.height = state->content_height; scale_box(&box, output_scale); - render_rect(output->wlr_output, damage, &box, color); + render_rect(output, damage, &box, color); } if (state->border_bottom) { @@ -332,7 +333,7 @@ static void render_view(struct sway_output *output, pixman_region32_t *damage, box.width = state->width; box.height = state->border_thickness; scale_box(&box, output_scale); - render_rect(output->wlr_output, damage, &box, color); + render_rect(output, damage, &box, color); } } @@ -359,8 +360,8 @@ static void render_titlebar(struct sway_output *output, list_t *children = container_get_current_siblings(con); bool is_last_child = children->length == 0 || children->items[children->length - 1] == con; - double output_x = output->wlr_output->lx; - double output_y = output->wlr_output->ly; + double output_x = output->lx; + double output_y = output->ly; int titlebar_border_thickness = config->titlebar_border_thickness; int titlebar_h_padding = config->titlebar_h_padding; int titlebar_v_padding = config->titlebar_v_padding; @@ -374,7 +375,7 @@ static void render_titlebar(struct sway_output *output, box.width = width; box.height = titlebar_border_thickness; scale_box(&box, output_scale); - render_rect(output->wlr_output, output_damage, &box, color); + render_rect(output, output_damage, &box, color); // Single pixel bar below title size_t left_offset = 0, right_offset = 0; @@ -392,7 +393,7 @@ static void render_titlebar(struct sway_output *output, 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); + render_rect(output, output_damage, &box, color); if (layout == L_TABBED) { // Single pixel left edge @@ -402,7 +403,7 @@ static void render_titlebar(struct sway_output *output, box.height = container_titlebar_height() - titlebar_border_thickness * 2; scale_box(&box, output_scale); - render_rect(output->wlr_output, output_damage, &box, color); + render_rect(output, output_damage, &box, color); // Single pixel right edge box.x = x + width - titlebar_border_thickness; @@ -411,7 +412,7 @@ static void render_titlebar(struct sway_output *output, box.height = container_titlebar_height() - titlebar_border_thickness * 2; scale_box(&box, output_scale); - render_rect(output->wlr_output, output_damage, &box, color); + render_rect(output, output_damage, &box, color); } int inner_x = x - output_x + titlebar_h_padding; @@ -470,12 +471,12 @@ static void render_titlebar(struct sway_output *output, box.y = round((y + titlebar_border_thickness) * output_scale); box.width = texture_box.width; box.height = ob_padding_above; - render_rect(output->wlr_output, output_damage, &box, color); + render_rect(output, output_damage, &box, color); // Padding below box.y += ob_padding_above + texture_box.height; box.height = ob_padding_below; - render_rect(output->wlr_output, output_damage, &box, color); + render_rect(output, output_damage, &box, color); } // Title text @@ -538,12 +539,12 @@ static void render_titlebar(struct sway_output *output, box.y = round((y + titlebar_border_thickness) * output_scale); box.width = texture_box.width; box.height = ob_padding_above; - render_rect(output->wlr_output, output_damage, &box, color); + render_rect(output, output_damage, &box, color); // Padding below box.y += ob_padding_above + texture_box.height; box.height = ob_padding_below; - render_rect(output->wlr_output, output_damage, &box, color); + render_rect(output, output_damage, &box, color); } // Determine the left + right extends of the textures (output-buffer local) @@ -577,7 +578,7 @@ static void render_titlebar(struct sway_output *output, box.x = ob_left_x + ob_left_width + round(output_x * output_scale); box.y = round(bg_y * output_scale); box.height = ob_bg_height; - render_rect(output->wlr_output, output_damage, &box, color); + render_rect(output, output_damage, &box, color); } // Padding on left side @@ -592,7 +593,7 @@ static void render_titlebar(struct sway_output *output, if (box.x + box.width < left_x) { box.width += left_x - box.x - box.width; } - render_rect(output->wlr_output, output_damage, &box, color); + render_rect(output, output_damage, &box, color); // Padding on right side right_offset = (layout == L_TABBED) * titlebar_border_thickness; @@ -607,7 +608,7 @@ static void render_titlebar(struct sway_output *output, box.width += box.x - right_rx; box.x = right_rx; } - render_rect(output->wlr_output, output_damage, &box, color); + render_rect(output, output_damage, &box, color); if (connects_sides) { // Left pixel in line with bottom bar @@ -616,7 +617,7 @@ static void render_titlebar(struct sway_output *output, 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); + render_rect(output, output_damage, &box, color); // Right pixel in line with bottom bar box.x = x + width - state->border_thickness * state->border_right; @@ -624,7 +625,7 @@ static void render_titlebar(struct sway_output *output, 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_rect(output, output_damage, &box, color); } } @@ -650,7 +651,7 @@ static void render_top_border(struct sway_output *output, box.width = state->width; box.height = state->border_thickness; scale_box(&box, output_scale); - render_rect(output->wlr_output, output_damage, &box, color); + render_rect(output, output_damage, &box, color); } struct parent_data { diff --git a/sway/input/seatop_move_tiling.c b/sway/input/seatop_move_tiling.c index 4b5aa81ed..2904792bd 100644 --- a/sway/input/seatop_move_tiling.c +++ b/sway/input/seatop_move_tiling.c @@ -37,7 +37,7 @@ static void handle_render(struct sway_seat *seat, struct wlr_box box; memcpy(&box, &e->drop_box, sizeof(struct wlr_box)); scale_box(&box, output->wlr_output->scale); - render_rect(output->wlr_output, damage, &box, color); + render_rect(output, damage, &box, color); } } diff --git a/sway/tree/arrange.c b/sway/tree/arrange.c index 438a21335..8583c53e5 100644 --- a/sway/tree/arrange.c +++ b/sway/tree/arrange.c @@ -195,8 +195,8 @@ void arrange_workspace(struct sway_workspace *workspace) { double prev_y = workspace->y; workspace->width = area->width; workspace->height = area->height; - workspace->x = output->wlr_output->lx + area->x; - workspace->y = output->wlr_output->ly + area->y; + workspace->x = output->lx + area->x; + workspace->y = output->ly + area->y; // Adjust any floating containers double diff_x = workspace->x - prev_x; diff --git a/sway/tree/output.c b/sway/tree/output.c index e0a66e0bd..227d487c1 100644 --- a/sway/tree/output.c +++ b/sway/tree/output.c @@ -101,11 +101,6 @@ void output_enable(struct sway_output *output, struct output_config *oc) { output->configured = true; list_add(root->outputs, output); - output->lx = wlr_output->lx; - output->ly = wlr_output->ly; - wlr_output_transformed_resolution(wlr_output, - &output->width, &output->height); - restore_workspaces(output); struct sway_workspace *ws = NULL; @@ -311,8 +306,10 @@ struct sway_output *output_get_in_direction(struct sway_output *reference, if (!sway_assert(direction, "got invalid direction: %d", direction)) { return NULL; } - int lx = reference->wlr_output->lx + reference->width / 2; - int ly = reference->wlr_output->ly + reference->height / 2; + struct wlr_box *output_box = + wlr_output_layout_get_box(root->output_layout, reference->wlr_output); + int lx = output_box->x + output_box->width / 2; + int ly = output_box->y + output_box->height / 2; struct wlr_output *wlr_adjacent = wlr_output_layout_adjacent_output( root->output_layout, direction, reference->wlr_output, lx, ly); if (!wlr_adjacent) { From 055d662baa3975d71552992772e97d6b489f0771 Mon Sep 17 00:00:00 2001 From: Ian Fan Date: Mon, 11 Mar 2019 16:50:52 +0000 Subject: [PATCH 062/191] commands: allow tiled sticky containers to be moved Namely, to a workspace on the same output. However, tiled sticky children of floating containers are still restricted. --- sway/commands/move.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sway/commands/move.c b/sway/commands/move.c index 4306aac86..926b2e8e9 100644 --- a/sway/commands/move.c +++ b/sway/commands/move.c @@ -473,7 +473,7 @@ static struct cmd_results *cmd_move_container(int argc, char **argv) { // We have to create the workspace, but if the container is // sticky and the workspace is going to be created on the same // output, we'll bail out first. - if (container->is_sticky) { + if (container->is_sticky && container_is_floating_or_child(container)) { struct sway_output *new_output = workspace_get_initial_output(ws_name); if (old_output == new_output) { @@ -507,8 +507,8 @@ static struct cmd_results *cmd_move_container(int argc, char **argv) { return cmd_results_new(CMD_INVALID, expected_syntax); } - if (container->is_sticky && old_output && - node_has_ancestor(destination, &old_output->node)) { + if (container->is_sticky && container_is_floating_or_child(container) && + old_output && node_has_ancestor(destination, &old_output->node)) { return cmd_results_new(CMD_FAILURE, "Can't move sticky " "container to another workspace on the same output"); } From 79369681ab3d6785aabf39bd8080cd4f30507524 Mon Sep 17 00:00:00 2001 From: Peter Grayson Date: Mon, 11 Mar 2019 22:28:35 -0400 Subject: [PATCH 063/191] Repair swaynag crash reading message from stdin When swaynag is run with the -l/--detailed-message option, a crash may occur if the detailed message read from stdin is large enough. E.g.: swaynag -m hello -l < ~/.config/sway/config The root cause is that the read_from_stdin() function under-allocates memory for the destination buffer which causes that buffer to be overflowed when copying line data to it with snprintf(). The repair is to allocate one more byte for the terminating null byte. N.B. although getline() returns the number of bytes read excluding a terminating null byte, the line buffer is terminated with a null byte. Thus we have a guarantee that the line buffer will be null terminated (which is important when copying with snprintf()). --- swaynag/config.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/swaynag/config.c b/swaynag/config.c index 200611f4b..fb2aa820b 100644 --- a/swaynag/config.c +++ b/swaynag/config.c @@ -18,7 +18,7 @@ static char *read_from_stdin(void) { size_t line_size = 0; ssize_t nread; while ((nread = getline(&line, &line_size, stdin)) != -1) { - buffer = realloc(buffer, buffer_len + nread); + buffer = realloc(buffer, buffer_len + nread + 1); snprintf(&buffer[buffer_len], nread + 1, "%s", line); buffer_len += nread; } From 1bab5a95531826fa54097bed48f8cc05d4233a9f Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Mon, 11 Mar 2019 22:21:38 -0400 Subject: [PATCH 064/191] get_deco_rect: fix floaters on tabbed/stacked ws This fixes the decoration rects for floating containers on a workspace that is either tabbed or stacked. Without this, the floater would incorrectly try to calculate where it's tab or stack decorations were on the workspace. This would cause a SIGFPE (due to a divide-by-zero) when the floater was on a tabbed workspace without any tiling children. Furthermore, the floater does not care what the workspace's layout is and should just use the location relative to the workspace. This should have no effect on children of a floating container. --- sway/ipc-json.c | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/sway/ipc-json.c b/sway/ipc-json.c index bebe6dd7b..f61e1a8c8 100644 --- a/sway/ipc-json.c +++ b/sway/ipc-json.c @@ -352,8 +352,9 @@ static void ipc_json_describe_workspace(struct sway_workspace *workspace, static void get_deco_rect(struct sway_container *c, struct wlr_box *deco_rect) { enum sway_container_layout parent_layout = container_parent_layout(c); - if ((parent_layout != L_TABBED && parent_layout != L_STACKED && - c->current.border != B_NORMAL) || + bool tab_or_stack = parent_layout == L_TABBED || parent_layout == L_STACKED; + if (((!tab_or_stack || container_is_floating(c)) && + c->current.border != B_NORMAL) || c->fullscreen_mode != FULLSCREEN_NONE || c->workspace == NULL) { deco_rect->x = deco_rect->y = deco_rect->width = deco_rect->height = 0; @@ -370,17 +371,19 @@ static void get_deco_rect(struct sway_container *c, struct wlr_box *deco_rect) { deco_rect->width = c->width; deco_rect->height = container_titlebar_height(); - if (parent_layout == L_TABBED) { - deco_rect->width = c->parent - ? c->parent->width / c->parent->children->length - : c->workspace->width / c->workspace->tiling->length; - deco_rect->x += deco_rect->width * container_sibling_index(c); - } else if (container_parent_layout(c) == L_STACKED) { - if (!c->view) { - size_t siblings = container_get_siblings(c)->length; - deco_rect->y -= deco_rect->height * siblings; + if (!container_is_floating(c)) { + if (parent_layout == L_TABBED) { + deco_rect->width = c->parent + ? c->parent->width / c->parent->children->length + : c->workspace->width / c->workspace->tiling->length; + deco_rect->x += deco_rect->width * container_sibling_index(c); + } else if (parent_layout == L_STACKED) { + if (!c->view) { + size_t siblings = container_get_siblings(c)->length; + deco_rect->y -= deco_rect->height * siblings; + } + deco_rect->y += deco_rect->height * container_sibling_index(c); } - deco_rect->y += deco_rect->height * container_sibling_index(c); } } From a280facd5fb1c07dab97bbef1d8db534edf7dfa6 Mon Sep 17 00:00:00 2001 From: 3ap Date: Tue, 12 Mar 2019 15:46:23 +0300 Subject: [PATCH 065/191] config.in: allow launch apps with args via dmenu Without this change you can't execute apps with command line arguments (e.g. firefox -P profile) because -P will be parsed as argument for "swaymsg exec". --- config.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config.in b/config.in index fbd952353..9d6f2e666 100644 --- a/config.in +++ b/config.in @@ -17,7 +17,7 @@ set $right l set $term urxvt # Your preferred application launcher # Note: it's recommended that you pass the final command to sway -set $menu dmenu_path | dmenu | xargs swaymsg exec +set $menu dmenu_path | dmenu | xargs swaymsg exec -- ### Output configuration # From 3330faded5ed59b19b2a1982bb52c05322b03510 Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Tue, 12 Mar 2019 13:57:40 -0400 Subject: [PATCH 066/191] Handle seat_get_focused_workspace returning NULL This modifiers the callers of seat_get_focused_workspace to handle getting NULL as the return value, if they did not already. --- sway/commands/focus.c | 5 +++++ sway/commands/scratchpad.c | 5 +++++ sway/commands/workspace.c | 6 ++++-- sway/desktop/layer_shell.c | 1 - sway/tree/root.c | 4 ++++ sway/tree/workspace.c | 13 +++++++++---- 6 files changed, 27 insertions(+), 7 deletions(-) diff --git a/sway/commands/focus.c b/sway/commands/focus.c index 25df51304..6344a7658 100644 --- a/sway/commands/focus.c +++ b/sway/commands/focus.c @@ -208,6 +208,11 @@ static struct cmd_results *focus_output(struct sway_seat *seat, "There is no output with that name"); } struct sway_workspace *ws = seat_get_focused_workspace(seat); + if (!ws) { + free(identifier); + return cmd_results_new(CMD_FAILURE, + "No focused workspace to base directions off of"); + } output = output_get_in_direction(ws->output, direction); if (!output) { diff --git a/sway/commands/scratchpad.c b/sway/commands/scratchpad.c index 71afa306d..34871bc6a 100644 --- a/sway/commands/scratchpad.c +++ b/sway/commands/scratchpad.c @@ -12,6 +12,11 @@ static void scratchpad_toggle_auto(void) { struct sway_seat *seat = input_manager_current_seat(); struct sway_container *focus = seat_get_focused_container(seat); struct sway_workspace *ws = seat_get_focused_workspace(seat); + if (!ws) { + sway_log(SWAY_DEBUG, + "No focused workspace to toggle scratchpad windows on"); + return; + } // If the focus is in a floating split container, // operate on the split container instead of the child. diff --git a/sway/commands/workspace.c b/sway/commands/workspace.c index 362dcd1b2..b911b2f61 100644 --- a/sway/commands/workspace.c +++ b/sway/commands/workspace.c @@ -185,8 +185,7 @@ struct cmd_results *cmd_workspace(int argc, char **argv) { struct sway_seat *seat = config->handler_context.seat; struct sway_workspace *current = seat_get_focused_workspace(seat); if (!current) { - return cmd_results_new(CMD_FAILURE, "workspace", - "No workspace to switch from"); + return cmd_results_new(CMD_FAILURE, "No workspace to switch from"); } struct sway_workspace *ws = NULL; @@ -227,6 +226,9 @@ struct cmd_results *cmd_workspace(int argc, char **argv) { } free(name); } + if (!ws) { + return cmd_results_new(CMD_FAILURE, "No workspace to switch to"); + } workspace_switch(ws, no_auto_back_and_forth); seat_consider_warp_to_focus(seat); } diff --git a/sway/desktop/layer_shell.c b/sway/desktop/layer_shell.c index b2fea880f..1667995c7 100644 --- a/sway/desktop/layer_shell.c +++ b/sway/desktop/layer_shell.c @@ -373,7 +373,6 @@ void handle_layer_shell_surface(struct wl_listener *listener, void *data) { struct sway_seat *seat = input_manager_get_default_seat(); if (seat) { struct sway_workspace *ws = seat_get_focused_workspace(seat); - if (ws != NULL) { output = ws->output; } diff --git a/sway/tree/root.c b/sway/tree/root.c index 6e13d6ceb..0744192b2 100644 --- a/sway/tree/root.c +++ b/sway/tree/root.c @@ -96,6 +96,10 @@ void root_scratchpad_remove_container(struct sway_container *con) { void root_scratchpad_show(struct sway_container *con) { struct sway_seat *seat = input_manager_current_seat(); struct sway_workspace *new_ws = seat_get_focused_workspace(seat); + if (!new_ws) { + sway_log(SWAY_DEBUG, "No focused workspace to show scratchpad on"); + return; + } struct sway_workspace *old_ws = con->workspace; // If the current con or any of its parents are in fullscreen mode, we diff --git a/sway/tree/workspace.c b/sway/tree/workspace.c index 73322491b..5e28197be 100644 --- a/sway/tree/workspace.c +++ b/sway/tree/workspace.c @@ -368,13 +368,13 @@ struct sway_workspace *workspace_by_name(const char *name) { struct sway_seat *seat = input_manager_current_seat(); struct sway_workspace *current = seat_get_focused_workspace(seat); - if (strcmp(name, "prev") == 0) { + if (current && strcmp(name, "prev") == 0) { return workspace_prev(current); - } else if (strcmp(name, "prev_on_output") == 0) { + } else if (current && strcmp(name, "prev_on_output") == 0) { return workspace_output_prev(current, false); - } else if (strcmp(name, "next") == 0) { + } else if (current && strcmp(name, "next") == 0) { return workspace_next(current); - } else if (strcmp(name, "next_on_output") == 0) { + } else if (current && strcmp(name, "next_on_output") == 0) { return workspace_output_next(current, false); } else if (strcmp(name, "current") == 0) { return current; @@ -399,6 +399,11 @@ static struct sway_workspace *workspace_output_prev_next_impl( struct sway_output *output, int dir, bool create) { struct sway_seat *seat = input_manager_current_seat(); struct sway_workspace *workspace = seat_get_focused_workspace(seat); + if (!workspace) { + sway_log(SWAY_DEBUG, + "No focused workspace to base prev/next on output off of"); + return NULL; + } int index = list_find(output->workspaces, workspace); if (!workspace_is_empty(workspace) && create && From 52a61671e93953a06b6b440ede5512e8fe45b35e Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Tue, 12 Mar 2019 22:33:49 -0400 Subject: [PATCH 067/191] criteria: change workspace to support regex This changes the workspace criteria to support regex instead of basic strings. This matches i3's behavior. --- include/sway/criteria.h | 2 +- sway/criteria.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/include/sway/criteria.h b/include/sway/criteria.h index 3eb583d5a..f7e788c8b 100644 --- a/include/sway/criteria.h +++ b/include/sway/criteria.h @@ -35,7 +35,7 @@ struct criteria { bool floating; bool tiling; char urgent; // 'l' for latest or 'o' for oldest - char *workspace; + pcre *workspace; }; bool criteria_is_empty(struct criteria *criteria); diff --git a/sway/criteria.c b/sway/criteria.c index 5ad481452..f2db6c184 100644 --- a/sway/criteria.c +++ b/sway/criteria.c @@ -208,7 +208,7 @@ static bool criteria_matches_view(struct criteria *criteria, if (criteria->workspace) { struct sway_workspace *ws = view->container->workspace; - if (!ws || strcmp(ws->name, criteria->workspace) != 0) { + if (!ws || regex_cmp(ws->name, criteria->workspace) != 0) { return false; } } @@ -515,7 +515,7 @@ static bool parse_token(struct criteria *criteria, char *name, char *value) { } break; case T_WORKSPACE: - criteria->workspace = strdup(effective_value); + generate_regex(&criteria->workspace, effective_value); break; case T_INVALID: break; From acdb4ed7a32a7cefd4ac37190ba65d33d752bee0 Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Mon, 11 Mar 2019 12:19:20 -0400 Subject: [PATCH 068/191] xwayland: handle floating configure request size This makes it so the container gets resized by a configure request for xwayland floating views. The minimum and maximum sizes are also respected. Previously, the configure request was resizing the surface to the size requested, but never changing the container size. This caused the surface to be rendered outside of the container or to be smaller than the container. The former is never ideal and the latter makes no sense for floating views since the container itself can just be shrunk. --- sway/desktop/xwayland.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c index e0d307e86..a2aa2e08e 100644 --- a/sway/desktop/xwayland.c +++ b/sway/desktop/xwayland.c @@ -432,8 +432,16 @@ static void handle_request_configure(struct wl_listener *listener, void *data) { return; } if (container_is_floating(view->container)) { - configure(view, view->container->current.content_x, - view->container->current.content_y, ev->width, ev->height); + // Respect minimum and maximum sizes + view->natural_width = ev->width; + view->natural_height = ev->height; + container_init_floating(view->container); + + configure(view, view->container->content_x, + view->container->content_y, + view->container->content_width, + view->container->content_height); + node_set_dirty(&view->container->node); } else { configure(view, view->container->current.content_x, view->container->current.content_y, From d64e8ba9469f4fa0ff693a22c88c1afa687c4c6c Mon Sep 17 00:00:00 2001 From: Stephan Hilb Date: Wed, 13 Mar 2019 21:32:02 +0100 Subject: [PATCH 069/191] swaybar: fix loading of malformed icon theme If the icon index.theme contained a key-value pair without a preceding group header, entry_handler() would be called with a zero pointer and lead to a segfault. Set the error flag and break on such malformed files. --- swaybar/tray/icon.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/swaybar/tray/icon.c b/swaybar/tray/icon.c index 2276e36de..8587f3f7b 100644 --- a/swaybar/tray/icon.c +++ b/swaybar/tray/icon.c @@ -243,6 +243,10 @@ static struct icon_theme *read_theme_file(char *basedir, char *theme_name) { free(group); group = strdup(&line[1]); } else { // key-value pair + if (!group) { + error = true; + break; + } // check well-formed int eok = 0; for (; isalnum(line[eok]) || line[eok] == '-'; ++eok) {} // TODO locale? From d8f74e4706104ac751706d5071838f97e3956a5e Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Thu, 14 Mar 2019 11:43:39 -0400 Subject: [PATCH 070/191] Set prev_workspace_name based off of focus This moves setting `seat->prev_workspace_name` from `workspace_switch` to `set_workspace`. `workspace_switch` is only called when using a `workspace` command to change the workspace so any workspace change based on criteria was not altering `seat->prev_workspace_name`. By moving it to `set_workspace`, which is called by `seat_set_focus`, it will change any time focus changes to a node on a different workspace --- sway/input/seat.c | 12 ++++++++++++ sway/tree/workspace.c | 12 ------------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/sway/input/seat.c b/sway/input/seat.c index be5235390..e56a65105 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -763,6 +763,18 @@ static void set_workspace(struct sway_seat *seat, if (seat->workspace == new_ws) { return; } + + if (seat->workspace) { + free(seat->prev_workspace_name); + seat->prev_workspace_name = malloc(strlen(seat->workspace->name) + 1); + if (!seat->prev_workspace_name) { + sway_log(SWAY_ERROR, "Unable to allocate previous workspace name"); + seat->prev_workspace_name = NULL; + } else { + strcpy(seat->prev_workspace_name, seat->workspace->name); + } + } + ipc_event_workspace(seat->workspace, new_ws, "focus"); seat->workspace = new_ws; } diff --git a/sway/tree/workspace.c b/sway/tree/workspace.c index 5e28197be..68a55e036 100644 --- a/sway/tree/workspace.c +++ b/sway/tree/workspace.c @@ -481,18 +481,6 @@ bool workspace_switch(struct sway_workspace *workspace, workspace_create(NULL, seat->prev_workspace_name); } - if (active_ws && (!seat->prev_workspace_name || - (strcmp(seat->prev_workspace_name, active_ws->name) - && active_ws != workspace))) { - free(seat->prev_workspace_name); - seat->prev_workspace_name = malloc(strlen(active_ws->name) + 1); - if (!seat->prev_workspace_name) { - sway_log(SWAY_ERROR, "Unable to allocate previous workspace name"); - return false; - } - strcpy(seat->prev_workspace_name, active_ws->name); - } - sway_log(SWAY_DEBUG, "Switching to workspace %p:%s", workspace, workspace->name); struct sway_node *next = seat_get_focus_inactive(seat, &workspace->node); From e687e120e0cd49157a2bc2d1a509d4fb1fa43490 Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Fri, 15 Mar 2019 03:00:40 -0400 Subject: [PATCH 071/191] output_cmd_background: validate colors This validates the color and fallback color in `output_cmd_background` to ensure that only colors of the form `#RRGGBB` are accepted. --- sway/commands/output/background.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/sway/commands/output/background.c b/sway/commands/output/background.c index 5a15ed0fc..054fb707e 100644 --- a/sway/commands/output/background.c +++ b/sway/commands/output/background.c @@ -20,6 +20,16 @@ static const char *bg_options[] = { "tile", }; +static bool validate_color(const char *color) { + if (strlen(color) != 7 || color[0] != '#') { + return false; + } + + char *ptr = NULL; + strtol(&color[1], &ptr, 16); + return *ptr == '\0'; +} + struct cmd_results *output_cmd_background(int argc, char **argv) { if (!config->handler_context.output_config) { return cmd_results_new(CMD_FAILURE, "Missing output config"); @@ -36,6 +46,10 @@ struct cmd_results *output_cmd_background(int argc, char **argv) { struct output_config *output = config->handler_context.output_config; if (strcasecmp(argv[1], "solid_color") == 0) { + if (!validate_color(argv[0])) { + return cmd_results_new(CMD_INVALID, + "Colors should be of the form #RRGGBB"); + } output->background = strdup(argv[0]); output->background_option = strdup("solid_color"); output->background_fallback = NULL; @@ -130,6 +144,11 @@ struct cmd_results *output_cmd_background(int argc, char **argv) { output->background_fallback = NULL; if (argc && *argv[0] == '#') { + if (!validate_color(argv[0])) { + return cmd_results_new(CMD_INVALID, + "fallback color should be of the form #RRGGBB"); + } + output->background_fallback = strdup(argv[0]); argc--; argv++; From 264e213c08bf1e184f7e540ae841996292ed16bd Mon Sep 17 00:00:00 2001 From: minus Date: Fri, 15 Mar 2019 19:37:20 +0100 Subject: [PATCH 072/191] Fix quoting of commands passed to for_window E.g. `for_window [class="mpv"] move container to output "Dell Inc. ..."` does not work because the executed move command only uses `Dell` as output name. --- sway/commands.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sway/commands.c b/sway/commands.c index 522a5fd47..cb32169f6 100644 --- a/sway/commands.c +++ b/sway/commands.c @@ -387,6 +387,7 @@ struct cmd_results *config_command(char *exec, char **new_block) { && handler->handle != cmd_bindsym && handler->handle != cmd_bindcode && handler->handle != cmd_set + && handler->handle != cmd_for_window && (*argv[i] == '\"' || *argv[i] == '\'')) { strip_quotes(argv[i]); } From 2578669de76c6d4b2038a648d1cfcd3c7e9e320a Mon Sep 17 00:00:00 2001 From: Flakebi Date: Sat, 16 Mar 2019 17:20:54 +0100 Subject: [PATCH 073/191] Flush stdout in swaymsg when in subscribe mode --- swaymsg/main.c | 1 + 1 file changed, 1 insertion(+) diff --git a/swaymsg/main.c b/swaymsg/main.c index a0ef7e3d9..65cc4bbb1 100644 --- a/swaymsg/main.c +++ b/swaymsg/main.c @@ -493,6 +493,7 @@ int main(int argc, char **argv) { printf("%s\n", json_object_to_json_string_ext(obj, JSON_C_TO_STRING_PRETTY | JSON_C_TO_STRING_SPACED)); } + fflush(stdout); json_object_put(obj); } From 3106ef23a7b4f7f7efeb43d47e724f5b23c4fd78 Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Fri, 15 Mar 2019 15:09:55 -0400 Subject: [PATCH 074/191] Fix output config retrieval for new outputs This removes `output_find_config`, which would take the first matching output config it found. This is fine if only a name output config, identifier output config, or even just wildcard exist, but if there is a name output config and identifier output config, they are not merged. Instead, this introduces find_output_config, which is just a wrapper for `get_output_config`. This ensures that both the name and identifier output configs are respected. This fixes the following case: - For simplicity in this example, remove all output configs from config - Run `swaymsg output bg #ff0000 solid_color` - Run `swaymsg output scale 2` - Disconnect and reconnect output Without this, the output will have the background, but not the scale. With this, the output will have both the background and scale --- include/sway/config.h | 2 ++ include/sway/output.h | 2 -- sway/config/output.c | 6 ++++++ sway/desktop/output.c | 4 ++-- sway/tree/output.c | 30 ------------------------------ 5 files changed, 10 insertions(+), 34 deletions(-) diff --git a/include/sway/config.h b/include/sway/config.h index 46ca7cee4..7c5445412 100644 --- a/include/sway/config.h +++ b/include/sway/config.h @@ -585,6 +585,8 @@ bool apply_output_config(struct output_config *oc, struct sway_output *output); struct output_config *store_output_config(struct output_config *oc); +struct output_config *find_output_config(struct sway_output *output); + void apply_output_config_to_outputs(struct output_config *oc); void reset_outputs(void); diff --git a/include/sway/output.h b/include/sway/output.h index 32ed1e28e..8015f2110 100644 --- a/include/sway/output.h +++ b/include/sway/output.h @@ -88,8 +88,6 @@ struct sway_output *output_by_name_or_id(const char *name_or_id); void output_sort_workspaces(struct sway_output *output); -struct output_config *output_find_config(struct sway_output *output); - void output_enable(struct sway_output *output, struct output_config *oc); void output_disable(struct sway_output *output); diff --git a/sway/config/output.c b/sway/config/output.c index 1f55fd6ab..a20c5ad4a 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -438,6 +438,12 @@ static struct output_config *get_output_config(char *identifier, return result; } +struct output_config *find_output_config(struct sway_output *output) { + char id[128]; + output_get_identifier(id, sizeof(id), output); + return get_output_config(id, output); +} + void apply_output_config_to_outputs(struct output_config *oc) { // Try to find the output container and apply configuration now. If // this is during startup then there will be no container and config diff --git a/sway/desktop/output.c b/sway/desktop/output.c index 1d9abbfd9..9d0c0ef5d 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -533,7 +533,7 @@ static void handle_destroy(struct wl_listener *listener, void *data) { static void handle_mode(struct wl_listener *listener, void *data) { struct sway_output *output = wl_container_of(listener, output, mode); if (!output->configured && !output->enabled) { - struct output_config *oc = output_find_config(output); + struct output_config *oc = find_output_config(output); if (output->wlr_output->current_mode != NULL && (!oc || oc->enabled)) { // We want to enable this output, but it didn't work last time, @@ -634,7 +634,7 @@ void handle_new_output(struct wl_listener *listener, void *data) { output->damage_destroy.notify = damage_handle_destroy; wl_list_init(&output->swaybg_client_destroy.link); - struct output_config *oc = output_find_config(output); + struct output_config *oc = find_output_config(output); if (!oc || oc->enabled) { output_enable(output, oc); } else { diff --git a/sway/tree/output.c b/sway/tree/output.c index 227d487c1..1202ba3c5 100644 --- a/sway/tree/output.c +++ b/sway/tree/output.c @@ -267,36 +267,6 @@ void output_begin_destroy(struct sway_output *output) { output->wlr_output = NULL; } -struct output_config *output_find_config(struct sway_output *output) { - const char *name = output->wlr_output->name; - char identifier[128]; - output_get_identifier(identifier, sizeof(identifier), output); - - struct output_config *oc = NULL, *all = NULL; - for (int i = 0; i < config->output_configs->length; ++i) { - struct output_config *cur = config->output_configs->items[i]; - - if (strcasecmp(name, cur->name) == 0 || - strcasecmp(identifier, cur->name) == 0) { - sway_log(SWAY_DEBUG, "Matched output config for %s", name); - oc = cur; - } - if (strcasecmp("*", cur->name) == 0) { - sway_log(SWAY_DEBUG, "Matched wildcard output config for %s", name); - all = cur; - } - - if (oc && all) { - break; - } - } - if (!oc) { - oc = all; - } - - return oc; -} - struct sway_output *output_from_wlr_output(struct wlr_output *output) { return output->data; } From 73605dac2ae18e2ca84a74da895c4098a1bee6f8 Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Fri, 15 Mar 2019 02:20:07 -0400 Subject: [PATCH 075/191] config/output: revamp identifier/name layering This revamps the way that output configs are handled when referencing an output by both identifier and name. If an output is always referred to by name or by identifier, this should have no noticeable change. As soon as there is a name output config and an identifier output config that matches an output, an output config is generated that is named ` on ` that is generated with the identifier output config merged on top of the name output config and stored. When a change to either is stored, the delta is merged on top of that "id on name" output config, as well. If the "id on name" output config exists, it has the highest precedence and will be used when applying a config to the output. This fixes the following case: - `swaymsg output bg /path/to/wallpaper1 fill` - `swaymsg output bg /path/to/wallpaper2 fill` - `swaymsg output dpms on` Without this, the wallpaper is changed to `/path/to/wallpaper1`. With this, the wallpaper remains `/path/to/wallpaper2`. --- sway/config/output.c | 116 +++++++++++++++++++++++++++++++++++-------- 1 file changed, 94 insertions(+), 22 deletions(-) diff --git a/sway/config/output.c b/sway/config/output.c index a20c5ad4a..5656e2c10 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -98,10 +98,67 @@ static void merge_wildcard_on_all(struct output_config *wildcard) { } } +static void merge_id_on_name(struct output_config *oc) { + char *id_on_name = NULL; + char id[128]; + char *name = NULL; + struct sway_output *output; + wl_list_for_each(output, &root->all_outputs, link) { + name = output->wlr_output->name; + output_get_identifier(id, sizeof(id), output); + if (strcmp(name, oc->name) != 0 || strcmp(id, oc->name) != 0) { + size_t length = snprintf(NULL, 0, "%s on %s", id, name) + 1; + id_on_name = malloc(length); + if (!id_on_name) { + sway_log(SWAY_ERROR, "Failed to allocate id on name string"); + return; + } + snprintf(id_on_name, length, "%s on %s", id, name); + break; + } + } + + if (!id_on_name) { + return; + } + + int i = list_seq_find(config->output_configs, output_name_cmp, id_on_name); + if (i >= 0) { + sway_log(SWAY_DEBUG, "Merging on top of existing id on name config"); + merge_output_config(config->output_configs->items[i], oc); + } else { + // If both a name and identifier config, exist generate an id on name + int ni = list_seq_find(config->output_configs, output_name_cmp, name); + int ii = list_seq_find(config->output_configs, output_name_cmp, id); + if ((ni >= 0 && ii >= 0) || (ni >= 0 && strcmp(oc->name, id) == 0) + || (ii >= 0 && strcmp(oc->name, name) == 0)) { + struct output_config *ion_oc = new_output_config(id_on_name); + if (ni >= 0) { + merge_output_config(ion_oc, config->output_configs->items[ni]); + } + if (ii >= 0) { + merge_output_config(ion_oc, config->output_configs->items[ii]); + } + merge_output_config(ion_oc, oc); + list_add(config->output_configs, ion_oc); + sway_log(SWAY_DEBUG, "Generated id on name output config \"%s\"" + " (enabled: %d) (%dx%d@%fHz position %d,%d scale %f " + "transform %d) (bg %s %s) (dpms %d)", ion_oc->name, + ion_oc->enabled, ion_oc->width, ion_oc->height, + ion_oc->refresh_rate, ion_oc->x, ion_oc->y, ion_oc->scale, + ion_oc->transform, ion_oc->background, + ion_oc->background_option, ion_oc->dpms_state); + } + } + free(id_on_name); +} + struct output_config *store_output_config(struct output_config *oc) { bool wildcard = strcmp(oc->name, "*") == 0; if (wildcard) { merge_wildcard_on_all(oc); + } else { + merge_id_on_name(oc); } int i = list_seq_find(config->output_configs, output_name_cmp, oc->name); @@ -374,35 +431,51 @@ static void default_output_config(struct output_config *oc, static struct output_config *get_output_config(char *identifier, struct sway_output *sway_output) { const char *name = sway_output->wlr_output->name; - struct output_config *oc_name = NULL; - int i = list_seq_find(config->output_configs, output_name_cmp, name); - if (i >= 0) { - oc_name = config->output_configs->items[i]; - } + struct output_config *oc_id_on_name = NULL; + struct output_config *oc_name = NULL; struct output_config *oc_id = NULL; - i = list_seq_find(config->output_configs, output_name_cmp, identifier); + + size_t length = snprintf(NULL, 0, "%s on %s", identifier, name) + 1; + char *id_on_name = malloc(length); + snprintf(id_on_name, length, "%s on %s", identifier, name); + int i = list_seq_find(config->output_configs, output_name_cmp, id_on_name); if (i >= 0) { - oc_id = config->output_configs->items[i]; + oc_id_on_name = config->output_configs->items[i]; + } else { + i = list_seq_find(config->output_configs, output_name_cmp, name); + if (i >= 0) { + oc_name = config->output_configs->items[i]; + } + + i = list_seq_find(config->output_configs, output_name_cmp, identifier); + if (i >= 0) { + oc_id = config->output_configs->items[i]; + } } struct output_config *result = new_output_config("temp"); if (config->reloading) { default_output_config(result, sway_output->wlr_output); } - if (oc_name && oc_id) { + if (oc_id_on_name) { + // Already have an identifier on name config, use that + free(result->name); + result->name = strdup(id_on_name); + merge_output_config(result, oc_id_on_name); + } else if (oc_name && oc_id) { // Generate a config named ` on ` which contains a // merged copy of the identifier on name. This will make sure that both // identifier and name configs are respected, with identifier getting // priority - size_t length = snprintf(NULL, 0, "%s on %s", identifier, name) + 1; - char *temp = malloc(length); - snprintf(temp, length, "%s on %s", identifier, name); + struct output_config *temp = new_output_config(id_on_name); + merge_output_config(temp, oc_name); + merge_output_config(temp, oc_id); + list_add(config->output_configs, temp); free(result->name); - result->name = temp; - merge_output_config(result, oc_name); - merge_output_config(result, oc_id); + result->name = strdup(id_on_name); + merge_output_config(result, temp); sway_log(SWAY_DEBUG, "Generated output config \"%s\" (enabled: %d)" " (%dx%d@%fHz position %d,%d scale %f transform %d) (bg %s %s)" @@ -435,6 +508,7 @@ static struct output_config *get_output_config(char *identifier, result = NULL; } + free(id_on_name); return result; } @@ -455,14 +529,12 @@ void apply_output_config_to_outputs(struct output_config *oc) { 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)) { - struct output_config *current = new_output_config(oc->name); - merge_output_config(current, oc); - if (wildcard) { - struct output_config *tmp = get_output_config(id, sway_output); - if (tmp) { - free_output_config(current); - current = tmp; - } + struct output_config *current = get_output_config(id, sway_output); + if (!current) { + // No stored output config matched, apply oc directly + sway_log(SWAY_DEBUG, "Applying oc directly"); + current = new_output_config(oc->name); + merge_output_config(current, oc); } apply_output_config(current, sway_output); free_output_config(current); From fb3475e291ea6be94131c19fdc006e9ad873ea5f Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Sat, 16 Mar 2019 09:18:54 +1000 Subject: [PATCH 076/191] Replace seatup allows_events with button callback --- include/sway/input/seat.h | 10 +++++++--- sway/input/cursor.c | 4 +--- sway/input/seat.c | 12 ++++++++---- sway/input/seatop_down.c | 8 +++++++- 4 files changed, 23 insertions(+), 11 deletions(-) diff --git a/include/sway/input/seat.h b/include/sway/input/seat.h index eb674b705..ff4476d19 100644 --- a/include/sway/input/seat.h +++ b/include/sway/input/seat.h @@ -9,13 +9,15 @@ struct sway_seat; struct sway_seatop_impl { + void (*button)(struct sway_seat *seat, uint32_t time_msec, + struct wlr_input_device *device, uint32_t button, + enum wlr_button_state state); void (*motion)(struct sway_seat *seat, uint32_t time_msec); void (*finish)(struct sway_seat *seat, uint32_t time_msec); void (*abort)(struct sway_seat *seat); void (*unref)(struct sway_seat *seat, struct sway_container *con); void (*render)(struct sway_seat *seat, struct sway_output *output, pixman_region32_t *damage); - bool allows_events; }; struct sway_seat_device { @@ -214,6 +216,10 @@ void seat_consider_warp_to_focus(struct sway_seat *seat); bool seat_doing_seatop(struct sway_seat *seat); +void seatop_button(struct sway_seat *seat, uint32_t time_msec, + struct wlr_input_device *device, uint32_t button, + enum wlr_button_state state); + void seatop_motion(struct sway_seat *seat, uint32_t time_msec); /** @@ -240,6 +246,4 @@ void seatop_unref(struct sway_seat *seat, struct sway_container *con); void seatop_render(struct sway_seat *seat, struct sway_output *output, pixman_region32_t *damage); -bool seatop_allows_events(struct sway_seat *seat); - #endif diff --git a/sway/input/cursor.c b/sway/input/cursor.c index d531a20e3..011b4929a 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -610,9 +610,7 @@ void dispatch_cursor_button(struct sway_cursor *cursor, } else { state_erase_button(cursor, button); } - if (seatop_allows_events(seat)) { - seat_pointer_notify_button(seat, time_msec, button, state); - } + seatop_button(seat, time_msec, device, button, state); if (button == seat->seatop_button && state == WLR_BUTTON_RELEASED) { seatop_finish(seat, time_msec); } diff --git a/sway/input/seat.c b/sway/input/seat.c index e56a65105..2c9a85c49 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -1216,6 +1216,14 @@ void seatop_unref(struct sway_seat *seat, struct sway_container *con) { } } +void seatop_button(struct sway_seat *seat, uint32_t time_msec, + struct wlr_input_device *device, uint32_t button, + enum wlr_button_state state) { + if (seat->seatop_impl && seat->seatop_impl->button) { + seat->seatop_impl->button(seat, time_msec, device, button, state); + } +} + void seatop_motion(struct sway_seat *seat, uint32_t time_msec) { if (seat->seatop_impl && seat->seatop_impl->motion) { seat->seatop_impl->motion(seat, time_msec); @@ -1246,7 +1254,3 @@ void seatop_render(struct sway_seat *seat, struct sway_output *output, seat->seatop_impl->render(seat, output, damage); } } - -bool seatop_allows_events(struct sway_seat *seat) { - return seat->seatop_impl && seat->seatop_impl->allows_events; -} diff --git a/sway/input/seatop_down.c b/sway/input/seatop_down.c index 895571b10..fb2cf1d07 100644 --- a/sway/input/seatop_down.c +++ b/sway/input/seatop_down.c @@ -11,6 +11,12 @@ struct seatop_down_event { bool moved; }; +static void handle_button(struct sway_seat *seat, uint32_t time_msec, + struct wlr_input_device *device, uint32_t button, + enum wlr_button_state state) { + seat_pointer_notify_button(seat, time_msec, button, state); +} + static void handle_motion(struct sway_seat *seat, uint32_t time_msec) { struct seatop_down_event *e = seat->seatop_data; struct sway_container *con = e->con; @@ -54,11 +60,11 @@ static void handle_unref(struct sway_seat *seat, struct sway_container *con) { } static const struct sway_seatop_impl seatop_impl = { + .button = handle_button, .motion = handle_motion, .finish = handle_finish, .abort = handle_abort, .unref = handle_unref, - .allows_events = true, }; void seatop_begin_down(struct sway_seat *seat, struct sway_container *con, From 7b9ae42331e49fea0f566fa7592855dee5da1991 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Sat, 16 Mar 2019 17:47:39 +1000 Subject: [PATCH 077/191] Introduce default seatop This introduces a `default` seat operation which is used when no mouse buttons are being held. This means there is now always a seat operation in progress. It allows us to separate `default` code from the standard cursor management code. The sway_seatop_impl struct has gained callbacks `axis`, `rebase` and `end`, and lost callbacks `finish` and `abort`. `axis` and `rebase` are only used by the default seatop. `end` is called when a seatop is being replaced by another one and allows the seatop to free any resources, though no seatop currently needs to do this. `finish` is no longer required, as each seatop can gracefully finish in their `button` callback. And `abort` is not needed, as calling `end` would achieve the same thing. The struct has also gained a bool named allow_set_cursor which allows the client to set a new cursor during `default` and `down` seatops. Seatops would previously store which button they were started with and stop when that button was released. This behaviour is changed so that it only ends once all buttons are released. So you can start a drag with $mod+left, then click and hold right, release left and it'll continue dragging while the right button is held. The motion callback now accepts dx and dy. Most seatops don't use this as they store the cursor position when the seatop is started and compare it with the current cursor position. This approach doesn't make sense for the default seatop though, hence why dx and dy are needed. The pressed_buttons array has been moved from the sway_cursor struct to the default seatop's data. This is only used for the default seatop to check bindings. The total pressed button count remains in the sway_cursor struct though, because all the other seatops check it to know if they should end. The `down` seatop no longer has a `moved` property. This was used to track if the cursor moved and to recheck focus_follows_mouse, but seems to work without it. The logic for focus_follows_mouse has been refactored. As part of this I've removed the call to wlr_seat_keyboard_has_grab as we don't appear to use keyboard grabs. The functions for handling relative motion, absolute motion and tool axis have been changed. Previously the handler functions were handle_cursor_motion, handle_cursor_motion_absolute and handle_tool_axis. The latter two both called cursor_motion_absolute. Both handle_cursor_motion and cursor_motion_absolute did very similar things. These are now simplified into three handlers and a single common function called cursor_motion. All three handlers call cursor_motion. As cursor_motion works with relative distances, the absolute and tool axis handlers convert them to relative first. --- include/sway/input/cursor.h | 9 - include/sway/input/seat.h | 48 +- sway/desktop/xdg_shell.c | 5 +- sway/desktop/xdg_shell_v6.c | 5 +- sway/desktop/xwayland.c | 5 +- sway/input/cursor.c | 680 ++-------------------------- sway/input/seat.c | 47 +- sway/input/seatop_default.c | 629 +++++++++++++++++++++++++ sway/input/seatop_down.c | 43 +- sway/input/seatop_move_floating.c | 44 +- sway/input/seatop_move_tiling.c | 33 +- sway/input/seatop_resize_floating.c | 29 +- sway/input/seatop_resize_tiling.c | 29 +- sway/meson.build | 1 + 14 files changed, 811 insertions(+), 796 deletions(-) create mode 100644 sway/input/seatop_default.c diff --git a/include/sway/input/cursor.h b/include/sway/input/cursor.h index 98eb4679b..516718c97 100644 --- a/include/sway/input/cursor.h +++ b/include/sway/input/cursor.h @@ -52,8 +52,6 @@ struct sway_cursor { struct wl_event_source *hide_source; bool hidden; - // Mouse binding state - uint32_t pressed_buttons[SWAY_CURSOR_PRESSED_BUTTONS_CAP]; size_t pressed_button_count; }; @@ -78,13 +76,6 @@ void cursor_handle_activity(struct sway_cursor *cursor); void cursor_unhide(struct sway_cursor *cursor); int cursor_get_timeout(struct sway_cursor *cursor); -/** - * Like cursor_rebase, but also allows focus to change when the cursor enters a - * new container. - */ -void cursor_send_pointer_motion(struct sway_cursor *cursor, uint32_t time_msec, - struct sway_node *node, struct wlr_surface *surface, double sx, double sy); - void dispatch_cursor_button(struct sway_cursor *cursor, struct wlr_input_device *device, uint32_t time_msec, uint32_t button, enum wlr_button_state state); diff --git a/include/sway/input/seat.h b/include/sway/input/seat.h index ff4476d19..a5361e8c1 100644 --- a/include/sway/input/seat.h +++ b/include/sway/input/seat.h @@ -12,12 +12,15 @@ struct sway_seatop_impl { void (*button)(struct sway_seat *seat, uint32_t time_msec, struct wlr_input_device *device, uint32_t button, enum wlr_button_state state); - void (*motion)(struct sway_seat *seat, uint32_t time_msec); - void (*finish)(struct sway_seat *seat, uint32_t time_msec); - void (*abort)(struct sway_seat *seat); + void (*motion)(struct sway_seat *seat, uint32_t time_msec, + double dx, double dy); + void (*axis)(struct sway_seat *seat, struct wlr_event_pointer_axis *event); + void (*rebase)(struct sway_seat *seat, uint32_t time_msec); + void (*end)(struct sway_seat *seat); void (*unref)(struct sway_seat *seat, struct sway_container *con); void (*render)(struct sway_seat *seat, struct sway_output *output, pixman_region32_t *damage); + bool allow_set_cursor; }; struct sway_seat_device { @@ -71,9 +74,7 @@ struct sway_seat { // Seat operations (drag and resize) const struct sway_seatop_impl *seatop_impl; void *seatop_data; - uint32_t seatop_button; - uint32_t last_button; uint32_t last_button_serial; struct wl_listener focus_destroy; @@ -188,23 +189,25 @@ bool seat_is_input_allowed(struct sway_seat *seat, struct wlr_surface *surface); void drag_icon_update_position(struct sway_drag_icon *icon); +void seatop_begin_default(struct sway_seat *seat); + void seatop_begin_down(struct sway_seat *seat, struct sway_container *con, - uint32_t time_msec, uint32_t button, int sx, int sy); + uint32_t time_msec, int sx, int sy); void seatop_begin_move_floating(struct sway_seat *seat, - struct sway_container *con, uint32_t button); + struct sway_container *con); void seatop_begin_move_tiling_threshold(struct sway_seat *seat, - struct sway_container *con, uint32_t button); + struct sway_container *con); void seatop_begin_move_tiling(struct sway_seat *seat, - struct sway_container *con, uint32_t button); + struct sway_container *con); void seatop_begin_resize_floating(struct sway_seat *seat, - struct sway_container *con, uint32_t button, enum wlr_edges edge); + struct sway_container *con, enum wlr_edges edge); void seatop_begin_resize_tiling(struct sway_seat *seat, - struct sway_container *con, uint32_t button, enum wlr_edges edge); + struct sway_container *con, enum wlr_edges edge); struct sway_container *seat_get_focus_inactive_floating(struct sway_seat *seat, struct sway_workspace *workspace); @@ -214,23 +217,24 @@ void seat_pointer_notify_button(struct sway_seat *seat, uint32_t time_msec, void seat_consider_warp_to_focus(struct sway_seat *seat); -bool seat_doing_seatop(struct sway_seat *seat); - void seatop_button(struct sway_seat *seat, uint32_t time_msec, struct wlr_input_device *device, uint32_t button, enum wlr_button_state state); -void seatop_motion(struct sway_seat *seat, uint32_t time_msec); +/** + * dx and dy are distances relative to previous position. + */ +void seatop_motion(struct sway_seat *seat, uint32_t time_msec, + double dx, double dy); + +void seatop_axis(struct sway_seat *seat, struct wlr_event_pointer_axis *event); + +void seatop_rebase(struct sway_seat *seat, uint32_t time_msec); /** - * End a seatop and apply the affects. + * End a seatop (ie. free any seatop specific resources). */ -void seatop_finish(struct sway_seat *seat, uint32_t time_msec); - -/** - * End a seatop without applying the affects. - */ -void seatop_abort(struct sway_seat *seat); +void seatop_end(struct sway_seat *seat); /** * Instructs the seatop implementation to drop any references to the given @@ -246,4 +250,6 @@ void seatop_unref(struct sway_seat *seat, struct sway_container *con); void seatop_render(struct sway_seat *seat, struct sway_output *output, pixman_region32_t *damage); +bool seatop_allows_set_cursor(struct sway_seat *seat); + #endif diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c index c84ca1115..ea5dcd16c 100644 --- a/sway/desktop/xdg_shell.c +++ b/sway/desktop/xdg_shell.c @@ -355,7 +355,7 @@ static void handle_request_move(struct wl_listener *listener, void *data) { struct wlr_xdg_toplevel_move_event *e = data; struct sway_seat *seat = e->seat->seat->data; if (e->serial == seat->last_button_serial) { - seatop_begin_move_floating(seat, view->container, seat->last_button); + seatop_begin_move_floating(seat, view->container); } } @@ -369,8 +369,7 @@ static void handle_request_resize(struct wl_listener *listener, void *data) { struct wlr_xdg_toplevel_resize_event *e = data; struct sway_seat *seat = e->seat->seat->data; if (e->serial == seat->last_button_serial) { - seatop_begin_resize_floating(seat, view->container, - seat->last_button, e->edges); + seatop_begin_resize_floating(seat, view->container, e->edges); } } diff --git a/sway/desktop/xdg_shell_v6.c b/sway/desktop/xdg_shell_v6.c index 3eed54ab0..7ff4c4de8 100644 --- a/sway/desktop/xdg_shell_v6.c +++ b/sway/desktop/xdg_shell_v6.c @@ -355,7 +355,7 @@ static void handle_request_move(struct wl_listener *listener, void *data) { struct wlr_xdg_toplevel_v6_move_event *e = data; struct sway_seat *seat = e->seat->seat->data; if (e->serial == seat->last_button_serial) { - seatop_begin_move_floating(seat, view->container, seat->last_button); + seatop_begin_move_floating(seat, view->container); } } @@ -369,8 +369,7 @@ static void handle_request_resize(struct wl_listener *listener, void *data) { struct wlr_xdg_toplevel_v6_resize_event *e = data; struct sway_seat *seat = e->seat->seat->data; if (e->serial == seat->last_button_serial) { - seatop_begin_resize_floating(seat, view->container, - seat->last_button, e->edges); + seatop_begin_resize_floating(seat, view->container, e->edges); } } diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c index a2aa2e08e..f5ade8dcb 100644 --- a/sway/desktop/xwayland.c +++ b/sway/desktop/xwayland.c @@ -476,7 +476,7 @@ static void handle_request_move(struct wl_listener *listener, void *data) { return; } struct sway_seat *seat = input_manager_current_seat(); - seatop_begin_move_floating(seat, view->container, seat->last_button); + seatop_begin_move_floating(seat, view->container); } static void handle_request_resize(struct wl_listener *listener, void *data) { @@ -492,8 +492,7 @@ static void handle_request_resize(struct wl_listener *listener, void *data) { } struct wlr_xwayland_resize_event *e = data; struct sway_seat *seat = input_manager_current_seat(); - seatop_begin_resize_floating(seat, view->container, - seat->last_button, e->edges); + seatop_begin_resize_floating(seat, view->container, e->edges); } static void handle_request_activate(struct wl_listener *listener, void *data) { diff --git a/sway/input/cursor.c b/sway/input/cursor.c index 011b4929a..610844479 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -158,132 +158,9 @@ struct sway_node *node_at_coords( return &ws->node; } -/** - * Determine if the edge of the given container is on the edge of the - * workspace/output. - */ -static bool edge_is_external(struct sway_container *cont, enum wlr_edges edge) { - enum sway_container_layout layout = L_NONE; - switch (edge) { - case WLR_EDGE_TOP: - case WLR_EDGE_BOTTOM: - layout = L_VERT; - break; - case WLR_EDGE_LEFT: - case WLR_EDGE_RIGHT: - layout = L_HORIZ; - break; - case WLR_EDGE_NONE: - sway_assert(false, "Never reached"); - return false; - } - - // Iterate the parents until we find one with the layout we want, - // then check if the child has siblings between it and the edge. - while (cont) { - if (container_parent_layout(cont) == layout) { - list_t *siblings = container_get_siblings(cont); - int index = list_find(siblings, cont); - if (index > 0 && (edge == WLR_EDGE_LEFT || edge == WLR_EDGE_TOP)) { - return false; - } - if (index < siblings->length - 1 && - (edge == WLR_EDGE_RIGHT || edge == WLR_EDGE_BOTTOM)) { - return false; - } - } - cont = cont->parent; - } - return true; -} - -static enum wlr_edges find_edge(struct sway_container *cont, - struct sway_cursor *cursor) { - if (!cont->view) { - return WLR_EDGE_NONE; - } - if (cont->border == B_NONE || !cont->border_thickness || - cont->border == B_CSD) { - return WLR_EDGE_NONE; - } - - enum wlr_edges edge = 0; - if (cursor->cursor->x < cont->x + cont->border_thickness) { - edge |= WLR_EDGE_LEFT; - } - if (cursor->cursor->y < cont->y + cont->border_thickness) { - edge |= WLR_EDGE_TOP; - } - if (cursor->cursor->x >= cont->x + cont->width - cont->border_thickness) { - edge |= WLR_EDGE_RIGHT; - } - if (cursor->cursor->y >= cont->y + cont->height - cont->border_thickness) { - edge |= WLR_EDGE_BOTTOM; - } - - return edge; -} - -/** - * If the cursor is over a _resizable_ edge, return the edge. - * Edges that can't be resized are edges of the workspace. - */ -static enum wlr_edges find_resize_edge(struct sway_container *cont, - struct sway_cursor *cursor) { - enum wlr_edges edge = find_edge(cont, cursor); - if (edge && !container_is_floating(cont) && edge_is_external(cont, edge)) { - return WLR_EDGE_NONE; - } - return edge; -} - -static void cursor_do_rebase(struct sway_cursor *cursor, uint32_t time_msec, - struct sway_node *node, struct wlr_surface *surface, - double sx, double sy) { - // 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) { - cursor_set_image(cursor, "left_ptr", client); - } - } else if (node && node->type == N_CONTAINER) { - // Try a node's resize edge - enum wlr_edges edge = find_resize_edge(node->sway_container, cursor); - if (edge == WLR_EDGE_NONE) { - cursor_set_image(cursor, "left_ptr", NULL); - } else if (container_is_floating(node->sway_container)) { - cursor_set_image(cursor, wlr_xcursor_get_resize_name(edge), NULL); - } else { - if (edge & (WLR_EDGE_LEFT | WLR_EDGE_RIGHT)) { - cursor_set_image(cursor, "col-resize", NULL); - } else { - cursor_set_image(cursor, "row-resize", NULL); - } - } - } else { - cursor_set_image(cursor, "left_ptr", NULL); - } - - // Send pointer enter/leave - struct wlr_seat *wlr_seat = cursor->seat->wlr_seat; - if (surface) { - if (seat_is_input_allowed(cursor->seat, surface)) { - wlr_seat_pointer_notify_enter(wlr_seat, surface, sx, sy); - wlr_seat_pointer_notify_motion(wlr_seat, time_msec, sx, sy); - } - } else { - wlr_seat_pointer_clear_focus(wlr_seat); - } -} - void cursor_rebase(struct sway_cursor *cursor) { uint32_t time_msec = get_current_time_msec(); - struct wlr_surface *surface = NULL; - double sx = 0.0, sy = 0.0; - cursor->previous.node = node_at_coords(cursor->seat, - cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy); - cursor_do_rebase(cursor, time_msec, cursor->previous.node, surface, sx, sy); + seatop_rebase(cursor->seat, time_msec); } void cursor_rebase_all(void) { @@ -293,9 +170,7 @@ void cursor_rebase_all(void) { struct sway_seat *seat; wl_list_for_each(seat, &server.input->seats, link) { - if (!seat_doing_seatop(seat)) { - cursor_rebase(seat->cursor); - } + cursor_rebase(seat->cursor); } } @@ -345,90 +220,17 @@ void cursor_unhide(struct sway_cursor *cursor) { cursor_rebase(cursor); } -void cursor_send_pointer_motion(struct sway_cursor *cursor, uint32_t time_msec, - struct sway_node *node, struct wlr_surface *surface, - double sx, double sy) { - if (time_msec == 0) { - time_msec = get_current_time_msec(); - } - - struct sway_seat *seat = cursor->seat; - - if (seat_doing_seatop(seat)) { - seatop_motion(seat, time_msec); - cursor->previous.x = cursor->cursor->x; - cursor->previous.y = cursor->cursor->y; - return; - } - - struct sway_node *prev_node = cursor->previous.node; - - // Update the stored previous position - cursor->previous.x = cursor->cursor->x; - cursor->previous.y = cursor->cursor->y; - cursor->previous.node = node; - - if (node && (config->focus_follows_mouse == FOLLOWS_YES || - config->focus_follows_mouse == FOLLOWS_ALWAYS)) { - struct sway_node *focus = seat_get_focus(seat); - if (focus && node->type == N_WORKSPACE) { - // Only follow the mouse if it would move to a new output - // Otherwise we'll focus the workspace, which is probably wrong - struct sway_output *focused_output = node_get_output(focus); - struct sway_output *output = node_get_output(node); - if (output != focused_output) { - seat_set_focus(seat, seat_get_focus_inactive(seat, node)); - } - } else if (node->type == N_CONTAINER && node->sway_container->view) { - // Focus node if the following are true: - // - cursor is over a new view, i.e. entered a new window; and - // - the new view is visible, i.e. not hidden in a stack or tab; and - // - the seat does not have a keyboard grab - if ((!wlr_seat_keyboard_has_grab(cursor->seat->wlr_seat) && - node != prev_node && - view_is_visible(node->sway_container->view)) || - config->focus_follows_mouse == FOLLOWS_ALWAYS) { - seat_set_focus(seat, node); - } else { - struct sway_node *next_focus = - seat_get_focus_inactive(seat, &root->node); - if (next_focus && next_focus->type == N_CONTAINER && - next_focus->sway_container->view && - view_is_visible(next_focus->sway_container->view)) { - seat_set_focus(seat, next_focus); - } - } - } - } - - cursor_do_rebase(cursor, time_msec, node, surface, sx, sy); - - struct sway_drag_icon *drag_icon; - wl_list_for_each(drag_icon, &root->drag_icons, link) { - if (drag_icon->seat == seat) { - drag_icon_update_position(drag_icon); - } - } -} - -static void handle_cursor_motion(struct wl_listener *listener, void *data) { - struct sway_cursor *cursor = wl_container_of(listener, cursor, motion); - struct wlr_event_pointer_motion *event = data; +static void cursor_motion(struct sway_cursor *cursor, uint32_t time_msec, + struct wlr_input_device *device, double dx, double dy, + double dx_unaccel, double dy_unaccel) { cursor_handle_activity(cursor); - double dx = event->delta_x; - double dy = event->delta_y; - - double dx_unaccel = event->unaccel_dx; - double dy_unaccel = event->unaccel_dy; - wlr_relative_pointer_manager_v1_send_relative_motion( server.relative_pointer_manager, - cursor->seat->wlr_seat, (uint64_t)event->time_msec * 1000, + cursor->seat->wlr_seat, (uint64_t)time_msec * 1000, dx, dy, dx_unaccel, dy_unaccel); struct wlr_surface *surface = NULL; - struct sway_node *node = NULL; double sx, sy; if (cursor->active_constraint) { node_at_coords(cursor->seat, @@ -448,50 +250,18 @@ static void handle_cursor_motion(struct wl_listener *listener, void *data) { dy = sy_confined - sy; } - wlr_cursor_move(cursor->cursor, event->device, dx, dy); + wlr_cursor_move(cursor->cursor, device, dx, dy); - // Recalculate pointer location after layout checks - node = node_at_coords(cursor->seat, - cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy); - - cursor_send_pointer_motion(cursor, event->time_msec, node, surface, - sx, sy); - transaction_commit_dirty(); + seatop_motion(cursor->seat, time_msec, dx, dy); } -static void cursor_motion_absolute(struct sway_cursor *cursor, - uint32_t time_msec, struct wlr_input_device *dev, - double x, double y) { - cursor_handle_activity(cursor); +static void handle_cursor_motion_relative( + struct wl_listener *listener, void *data) { + struct sway_cursor *cursor = wl_container_of(listener, cursor, motion); + struct wlr_event_pointer_motion *e = data; - double lx, ly; - wlr_cursor_absolute_to_layout_coords(cursor->cursor, dev, - x, y, &lx, &ly); - - double dx = lx - cursor->cursor->x; - double dy = ly - cursor->cursor->y; - wlr_relative_pointer_manager_v1_send_relative_motion( - server.relative_pointer_manager, - cursor->seat->wlr_seat, (uint64_t)time_msec * 1000, - dx, dy, dx, dy); - - struct wlr_surface *surface = NULL; - double sx = 0.0, sy = 0.0; - struct sway_node *node = node_at_coords(cursor->seat, - lx, ly, &surface, &sx, &sy); - - if (cursor->active_constraint) { - if (cursor->active_constraint->surface != surface) { - return; - } - if (!pixman_region32_contains_point(&cursor->confine, - floor(sx), floor(sy), NULL)) { - return; - } - } - - wlr_cursor_warp_closest(cursor->cursor, dev, lx, ly); - cursor_send_pointer_motion(cursor, time_msec, node, surface, sx, sy); + cursor_motion(cursor, e->time_msec, e->device, e->delta_x, e->delta_y, + e->unaccel_dx, e->unaccel_dy); transaction_commit_dirty(); } @@ -501,98 +271,15 @@ static void handle_cursor_motion_absolute( wl_container_of(listener, cursor, motion_absolute); struct wlr_event_pointer_motion_absolute *event = data; - cursor_motion_absolute(cursor, event->time_msec, event->device, - event->x, event->y); -} + double lx, ly; + wlr_cursor_absolute_to_layout_coords(cursor->cursor, event->device, + event->x, event->y, &lx, &ly); -/** - * Remove a button (and duplicates) to the sorted list of currently pressed buttons - */ -static void state_erase_button(struct sway_cursor *cursor, uint32_t button) { - size_t j = 0; - for (size_t i = 0; i < cursor->pressed_button_count; ++i) { - if (i > j) { - cursor->pressed_buttons[j] = cursor->pressed_buttons[i]; - } - if (cursor->pressed_buttons[i] != button) { - ++j; - } - } - while (cursor->pressed_button_count > j) { - --cursor->pressed_button_count; - cursor->pressed_buttons[cursor->pressed_button_count] = 0; - } -} + double dx = lx - cursor->cursor->x; + double dy = ly - cursor->cursor->y; -/** - * Add a button to the sorted list of currently pressed buttons, if there - * is space. - */ -static void state_add_button(struct sway_cursor *cursor, uint32_t button) { - if (cursor->pressed_button_count >= SWAY_CURSOR_PRESSED_BUTTONS_CAP) { - return; - } - size_t i = 0; - while (i < cursor->pressed_button_count && cursor->pressed_buttons[i] < button) { - ++i; - } - size_t j = cursor->pressed_button_count; - while (j > i) { - cursor->pressed_buttons[j] = cursor->pressed_buttons[j - 1]; - --j; - } - cursor->pressed_buttons[i] = button; - cursor->pressed_button_count++; -} - -/** - * Return the mouse binding which matches modifier, click location, release, - * and pressed button state, otherwise return null. - */ -static struct sway_binding* get_active_mouse_binding( - const struct sway_cursor *cursor, list_t *bindings, uint32_t modifiers, - bool release, bool on_titlebar, bool on_border, bool on_content, - bool on_workspace, const char *identifier) { - uint32_t click_region = - ((on_titlebar || on_workspace) ? BINDING_TITLEBAR : 0) | - ((on_border || on_workspace) ? BINDING_BORDER : 0) | - ((on_content || on_workspace) ? BINDING_CONTENTS : 0); - - struct sway_binding *current = NULL; - for (int i = 0; i < bindings->length; ++i) { - struct sway_binding *binding = bindings->items[i]; - if (modifiers ^ binding->modifiers || - cursor->pressed_button_count != (size_t)binding->keys->length || - release != (binding->flags & BINDING_RELEASE) || - !(click_region & binding->flags) || - (on_workspace && - (click_region & binding->flags) != click_region) || - (strcmp(binding->input, identifier) != 0 && - strcmp(binding->input, "*") != 0)) { - continue; - } - - bool match = true; - for (size_t j = 0; j < cursor->pressed_button_count; j++) { - uint32_t key = *(uint32_t *)binding->keys->items[j]; - if (key != cursor->pressed_buttons[j]) { - match = false; - break; - } - } - if (!match) { - continue; - } - - if (!current || strcmp(current->input, "*") == 0) { - current = binding; - if (strcmp(current->input, identifier) == 0) { - // If a binding is found for the exact input, quit searching - break; - } - } - } - return current; + cursor_motion(cursor, event->time_msec, event->device, dx, dy, dx, dy); + transaction_commit_dirty(); } void dispatch_cursor_button(struct sway_cursor *cursor, @@ -601,318 +288,33 @@ void dispatch_cursor_button(struct sway_cursor *cursor, if (time_msec == 0) { time_msec = get_current_time_msec(); } - struct sway_seat *seat = cursor->seat; - // Handle existing seat operation - if (seat_doing_seatop(seat)) { - if (state == WLR_BUTTON_PRESSED) { - state_add_button(cursor, button); - } else { - state_erase_button(cursor, button); - } - seatop_button(seat, time_msec, device, button, state); - if (button == seat->seatop_button && state == WLR_BUTTON_RELEASED) { - seatop_finish(seat, time_msec); - } - return; - } - - // Determine what's under the cursor - struct wlr_surface *surface = NULL; - double sx, sy; - struct sway_node *node = node_at_coords(seat, - cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy); - struct sway_container *cont = node && node->type == N_CONTAINER ? - node->sway_container : NULL; - bool is_floating = cont && container_is_floating(cont); - bool is_floating_or_child = cont && container_is_floating_or_child(cont); - bool is_fullscreen_or_child = cont && container_is_fullscreen_or_child(cont); - enum wlr_edges edge = cont ? find_edge(cont, cursor) : WLR_EDGE_NONE; - enum wlr_edges resize_edge = edge ? - find_resize_edge(cont, cursor) : WLR_EDGE_NONE; - bool on_border = edge != WLR_EDGE_NONE; - bool on_contents = cont && !on_border && surface; - bool on_workspace = node && node->type == N_WORKSPACE; - bool on_titlebar = cont && !on_border && !surface; - - // Handle mouse bindings - struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat->wlr_seat); - uint32_t modifiers = keyboard ? wlr_keyboard_get_modifiers(keyboard) : 0; - - char *device_identifier = device ? input_device_get_identifier(device) - : strdup("*"); - struct sway_binding *binding = NULL; - if (state == WLR_BUTTON_PRESSED) { - state_add_button(cursor, button); - binding = get_active_mouse_binding(cursor, - config->current_mode->mouse_bindings, modifiers, false, - on_titlebar, on_border, on_contents, on_workspace, - device_identifier); - } else { - binding = get_active_mouse_binding(cursor, - config->current_mode->mouse_bindings, modifiers, true, - on_titlebar, on_border, on_contents, on_workspace, - device_identifier); - state_erase_button(cursor, button); - } - free(device_identifier); - if (binding) { - seat_execute_command(seat, binding); - return; - } - - // Handle clicking an empty workspace - if (node && node->type == N_WORKSPACE) { - seat_set_focus(seat, node); - return; - } - - // Handle clicking a layer surface - if (surface && wlr_surface_is_layer_surface(surface)) { - struct wlr_layer_surface_v1 *layer = - wlr_layer_surface_v1_from_wlr_surface(surface); - if (layer->current.keyboard_interactive) { - seat_set_focus_layer(seat, layer); - } - seat_pointer_notify_button(seat, time_msec, button, state); - return; - } - - // Handle tiling resize via border - if (cont && resize_edge && button == BTN_LEFT && - state == WLR_BUTTON_PRESSED && !is_floating) { - seat_set_focus_container(seat, cont); - seatop_begin_resize_tiling(seat, cont, button, edge); - return; - } - - // Handle tiling resize via mod - bool mod_pressed = keyboard && - (wlr_keyboard_get_modifiers(keyboard) & config->floating_mod); - if (cont && !is_floating_or_child && mod_pressed && - state == WLR_BUTTON_PRESSED) { - uint32_t btn_resize = config->floating_mod_inverse ? - BTN_LEFT : BTN_RIGHT; - if (button == btn_resize) { - edge = 0; - edge |= cursor->cursor->x > cont->x + cont->width / 2 ? - WLR_EDGE_RIGHT : WLR_EDGE_LEFT; - edge |= cursor->cursor->y > cont->y + cont->height / 2 ? - WLR_EDGE_BOTTOM : WLR_EDGE_TOP; - - const char *image = NULL; - if (edge == (WLR_EDGE_LEFT | WLR_EDGE_TOP)) { - image = "nw-resize"; - } else if (edge == (WLR_EDGE_TOP | WLR_EDGE_RIGHT)) { - image = "ne-resize"; - } else if (edge == (WLR_EDGE_RIGHT | WLR_EDGE_BOTTOM)) { - image = "se-resize"; - } else if (edge == (WLR_EDGE_BOTTOM | WLR_EDGE_LEFT)) { - image = "sw-resize"; - } - cursor_set_image(seat->cursor, image, NULL); - seat_set_focus_container(seat, cont); - seatop_begin_resize_tiling(seat, cont, button, edge); - return; - } - } - - // Handle beginning floating move - if (cont && is_floating_or_child && !is_fullscreen_or_child && - state == WLR_BUTTON_PRESSED) { - uint32_t btn_move = config->floating_mod_inverse ? BTN_RIGHT : BTN_LEFT; - if (button == btn_move && state == WLR_BUTTON_PRESSED && - (mod_pressed || on_titlebar)) { - while (cont->parent) { - cont = cont->parent; - } - seat_set_focus_container(seat, cont); - seatop_begin_move_floating(seat, cont, button); - return; - } - } - - // Handle beginning floating resize - if (cont && is_floating_or_child && !is_fullscreen_or_child && - state == WLR_BUTTON_PRESSED) { - // Via border - if (button == BTN_LEFT && resize_edge != WLR_EDGE_NONE) { - seatop_begin_resize_floating(seat, cont, button, resize_edge); - return; - } - - // Via mod+click - uint32_t btn_resize = config->floating_mod_inverse ? - BTN_LEFT : BTN_RIGHT; - if (mod_pressed && button == btn_resize) { - struct sway_container *floater = cont; - while (floater->parent) { - floater = floater->parent; - } - edge = 0; - edge |= cursor->cursor->x > floater->x + floater->width / 2 ? - WLR_EDGE_RIGHT : WLR_EDGE_LEFT; - edge |= cursor->cursor->y > floater->y + floater->height / 2 ? - WLR_EDGE_BOTTOM : WLR_EDGE_TOP; - seatop_begin_resize_floating(seat, floater, button, edge); - return; - } - } - - // Handle moving a tiling container - if (config->tiling_drag && (mod_pressed || on_titlebar) && - state == WLR_BUTTON_PRESSED && !is_floating_or_child && - cont && cont->fullscreen_mode == FULLSCREEN_NONE) { - struct sway_container *focus = seat_get_focused_container(seat); - bool focused = focus == cont || container_has_ancestor(focus, cont); - if (on_titlebar && !focused) { - node = seat_get_focus_inactive(seat, &cont->node); - seat_set_focus(seat, node); - } - - // If moving a container by it's title bar, use a threshold for the drag - if (!mod_pressed && config->tiling_drag_threshold > 0) { - seatop_begin_move_tiling_threshold(seat, cont, button); - } else { - seatop_begin_move_tiling(seat, cont, button); - } - return; - } - - // Handle mousedown on a container surface - if (surface && cont && state == WLR_BUTTON_PRESSED) { - seat_set_focus_container(seat, cont); - seatop_begin_down(seat, cont, time_msec, button, sx, sy); - seat_pointer_notify_button(seat, time_msec, button, WLR_BUTTON_PRESSED); - return; - } - - // Handle clicking a container surface or decorations - if (cont) { - node = seat_get_focus_inactive(seat, &cont->node); - seat_set_focus(seat, node); - seat_pointer_notify_button(seat, time_msec, button, state); - return; - } - - seat_pointer_notify_button(seat, time_msec, button, state); + seatop_button(cursor->seat, time_msec, device, button, state); } static void handle_cursor_button(struct wl_listener *listener, void *data) { struct sway_cursor *cursor = wl_container_of(listener, cursor, button); struct wlr_event_pointer_button *event = data; cursor_handle_activity(cursor); + + if (event->state == WLR_BUTTON_PRESSED) { + cursor->pressed_button_count++; + } else { + if (cursor->pressed_button_count > 0) { + cursor->pressed_button_count--; + } else { + sway_log(SWAY_ERROR, "Pressed button count was wrong"); + } + } + dispatch_cursor_button(cursor, event->device, event->time_msec, event->button, event->state); transaction_commit_dirty(); } -static uint32_t wl_axis_to_button(struct wlr_event_pointer_axis *event) { - switch (event->orientation) { - case WLR_AXIS_ORIENTATION_VERTICAL: - return event->delta < 0 ? SWAY_SCROLL_UP : SWAY_SCROLL_DOWN; - case WLR_AXIS_ORIENTATION_HORIZONTAL: - return event->delta < 0 ? SWAY_SCROLL_LEFT : SWAY_SCROLL_RIGHT; - default: - sway_log(SWAY_DEBUG, "Unknown axis orientation"); - return 0; - } -} - void dispatch_cursor_axis(struct sway_cursor *cursor, struct wlr_event_pointer_axis *event) { - struct sway_seat *seat = cursor->seat; - struct sway_input_device *input_device = - event->device ? event->device->data : NULL; - struct input_config *ic = - input_device ? input_device_get_config(input_device) : NULL; - - // Determine what's under the cursor - struct wlr_surface *surface = NULL; - double sx, sy; - struct sway_node *node = node_at_coords(seat, - cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy); - struct sway_container *cont = node && node->type == N_CONTAINER ? - node->sway_container : NULL; - enum wlr_edges edge = cont ? find_edge(cont, cursor) : WLR_EDGE_NONE; - bool on_border = edge != WLR_EDGE_NONE; - bool on_titlebar = cont && !on_border && !surface; - bool on_titlebar_border = cont && on_border && - cursor->cursor->y < cont->content_y; - bool on_contents = cont && !on_border && surface; - bool on_workspace = node && node->type == N_WORKSPACE; - float scroll_factor = - (ic == NULL || ic->scroll_factor == FLT_MIN) ? 1.0f : ic->scroll_factor; - - bool handled = false; - - // Gather information needed for mouse bindings - struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat->wlr_seat); - uint32_t modifiers = keyboard ? wlr_keyboard_get_modifiers(keyboard) : 0; - struct wlr_input_device *device = - input_device ? input_device->wlr_device : NULL; - char *dev_id = device ? input_device_get_identifier(device) : strdup("*"); - uint32_t button = wl_axis_to_button(event); - - // Handle mouse bindings - x11 mouse buttons 4-7 - press event - struct sway_binding *binding = NULL; - state_add_button(cursor, button); - binding = get_active_mouse_binding(cursor, - config->current_mode->mouse_bindings, modifiers, false, - on_titlebar, on_border, on_contents, on_workspace, dev_id); - if (binding) { - seat_execute_command(seat, binding); - handled = true; - } - - // Scrolling on a tabbed or stacked title bar (handled as press event) - if (!handled && (on_titlebar || on_titlebar_border)) { - enum sway_container_layout layout = container_parent_layout(cont); - if (layout == L_TABBED || layout == L_STACKED) { - struct sway_node *tabcontainer = node_get_parent(node); - struct sway_node *active = - seat_get_active_tiling_child(seat, tabcontainer); - list_t *siblings = container_get_siblings(cont); - int desired = list_find(siblings, active->sway_container) + - round(scroll_factor * event->delta_discrete); - if (desired < 0) { - desired = 0; - } else if (desired >= siblings->length) { - desired = siblings->length - 1; - } - struct sway_node *old_focus = seat_get_focus(seat); - struct sway_container *new_sibling_con = siblings->items[desired]; - struct sway_node *new_sibling = &new_sibling_con->node; - struct sway_node *new_focus = - seat_get_focus_inactive(seat, new_sibling); - if (node_has_ancestor(old_focus, tabcontainer)) { - seat_set_focus(seat, new_focus); - } else { - // Scrolling when focus is not in the tabbed container at all - seat_set_raw_focus(seat, new_sibling); - seat_set_raw_focus(seat, new_focus); - seat_set_raw_focus(seat, old_focus); - } - handled = true; - } - } - - // Handle mouse bindings - x11 mouse buttons 4-7 - release event - binding = get_active_mouse_binding(cursor, - config->current_mode->mouse_bindings, modifiers, true, - on_titlebar, on_border, on_contents, on_workspace, dev_id); - state_erase_button(cursor, button); - if (binding) { - seat_execute_command(seat, binding); - handled = true; - } - free(dev_id); - - if (!handled) { - wlr_seat_pointer_notify_axis(cursor->seat->wlr_seat, event->time_msec, - event->orientation, scroll_factor * event->delta, - round(scroll_factor * event->delta_discrete), event->source); - } + seatop_axis(cursor->seat, event); } static void handle_cursor_axis(struct wl_listener *listener, void *data) { @@ -1054,8 +456,16 @@ static void handle_tool_axis(struct wl_listener *listener, void *data) { apply_mapping_from_region(event->device, ic->mapped_from_region, &x, &y); } - cursor_motion_absolute(cursor, event->time_msec, event->device, x, y); + double lx, ly; + wlr_cursor_absolute_to_layout_coords(cursor->cursor, event->device, + x, y, &lx, &ly); + + double dx = lx - cursor->cursor->x; + double dy = ly - cursor->cursor->y; + + cursor_motion(cursor, event->time_msec, event->device, dx, dy, dx, dy); wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); + transaction_commit_dirty(); } static void handle_tool_tip(struct wl_listener *listener, void *data) { @@ -1142,7 +552,7 @@ static void handle_request_set_cursor(struct wl_listener *listener, void *data) { struct sway_cursor *cursor = wl_container_of(listener, cursor, request_set_cursor); - if (seat_doing_seatop(cursor->seat)) { + if (!seatop_allows_set_cursor(cursor->seat)) { return; } struct wlr_seat_pointer_request_set_cursor_event *event = data; @@ -1257,7 +667,7 @@ struct sway_cursor *sway_cursor_create(struct sway_seat *seat) { // input events wl_signal_add(&wlr_cursor->events.motion, &cursor->motion); - cursor->motion.notify = handle_cursor_motion; + cursor->motion.notify = handle_cursor_motion_relative; wl_signal_add(&wlr_cursor->events.motion_absolute, &cursor->motion_absolute); diff --git a/sway/input/seat.c b/sway/input/seat.c index 2c9a85c49..2d3552753 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -365,7 +365,7 @@ static void handle_start_drag(struct wl_listener *listener, void *data) { wl_list_insert(&root->drag_icons, &icon->link); drag_icon_update_position(icon); - seatop_abort(seat); + seatop_begin_default(seat); } static void handle_request_set_selection(struct wl_listener *listener, @@ -461,6 +461,8 @@ struct sway_seat *seat_create(const char *seat_name) { wl_list_insert(&server.input->seats, &seat->link); + seatop_begin_default(seat); + return seat; } @@ -1175,7 +1177,6 @@ struct seat_config *seat_get_config_by_name(const char *name) { 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); } @@ -1206,12 +1207,8 @@ void seat_consider_warp_to_focus(struct sway_seat *seat) { } } -bool seat_doing_seatop(struct sway_seat *seat) { - return seat->seatop_impl != NULL; -} - void seatop_unref(struct sway_seat *seat, struct sway_container *con) { - if (seat->seatop_impl && seat->seatop_impl->unref) { + if (seat->seatop_impl->unref) { seat->seatop_impl->unref(seat, con); } } @@ -1219,29 +1216,33 @@ void seatop_unref(struct sway_seat *seat, struct sway_container *con) { void seatop_button(struct sway_seat *seat, uint32_t time_msec, struct wlr_input_device *device, uint32_t button, enum wlr_button_state state) { - if (seat->seatop_impl && seat->seatop_impl->button) { + if (seat->seatop_impl->button) { seat->seatop_impl->button(seat, time_msec, device, button, state); } } -void seatop_motion(struct sway_seat *seat, uint32_t time_msec) { - if (seat->seatop_impl && seat->seatop_impl->motion) { - seat->seatop_impl->motion(seat, time_msec); +void seatop_motion(struct sway_seat *seat, uint32_t time_msec, + double dx, double dy) { + if (seat->seatop_impl->motion) { + seat->seatop_impl->motion(seat, time_msec, dx, dy); } } -void seatop_finish(struct sway_seat *seat, uint32_t time_msec) { - if (seat->seatop_impl && seat->seatop_impl->finish) { - seat->seatop_impl->finish(seat, time_msec); +void seatop_axis(struct sway_seat *seat, struct wlr_event_pointer_axis *event) { + if (seat->seatop_impl->axis) { + seat->seatop_impl->axis(seat, event); } - free(seat->seatop_data); - seat->seatop_data = NULL; - seat->seatop_impl = NULL; } -void seatop_abort(struct sway_seat *seat) { - if (seat->seatop_impl && seat->seatop_impl->abort) { - seat->seatop_impl->abort(seat); +void seatop_rebase(struct sway_seat *seat, uint32_t time_msec) { + if (seat->seatop_impl->rebase) { + seat->seatop_impl->rebase(seat, time_msec); + } +} + +void seatop_end(struct sway_seat *seat) { + if (seat->seatop_impl && seat->seatop_impl->end) { + seat->seatop_impl->end(seat); } free(seat->seatop_data); seat->seatop_data = NULL; @@ -1250,7 +1251,11 @@ void seatop_abort(struct sway_seat *seat) { void seatop_render(struct sway_seat *seat, struct sway_output *output, pixman_region32_t *damage) { - if (seat->seatop_impl && seat->seatop_impl->render) { + if (seat->seatop_impl->render) { seat->seatop_impl->render(seat, output, damage); } } + +bool seatop_allows_set_cursor(struct sway_seat *seat) { + return seat->seatop_impl->allow_set_cursor; +} diff --git a/sway/input/seatop_default.c b/sway/input/seatop_default.c new file mode 100644 index 000000000..fc785cda4 --- /dev/null +++ b/sway/input/seatop_default.c @@ -0,0 +1,629 @@ +#define _POSIX_C_SOURCE 200809L +#include +#include +#include +#include +#include "sway/input/cursor.h" +#include "sway/input/seat.h" +#include "sway/tree/view.h" +#include "log.h" + +struct seatop_default_event { + struct sway_node *previous_node; + uint32_t pressed_buttons[SWAY_CURSOR_PRESSED_BUTTONS_CAP]; + size_t pressed_button_count; +}; + +/*-----------------------------------------\ + * Functions shared by multiple callbacks / + *---------------------------------------*/ + +/** + * Determine if the edge of the given container is on the edge of the + * workspace/output. + */ +static bool edge_is_external(struct sway_container *cont, enum wlr_edges edge) { + enum sway_container_layout layout = L_NONE; + switch (edge) { + case WLR_EDGE_TOP: + case WLR_EDGE_BOTTOM: + layout = L_VERT; + break; + case WLR_EDGE_LEFT: + case WLR_EDGE_RIGHT: + layout = L_HORIZ; + break; + case WLR_EDGE_NONE: + sway_assert(false, "Never reached"); + return false; + } + + // Iterate the parents until we find one with the layout we want, + // then check if the child has siblings between it and the edge. + while (cont) { + if (container_parent_layout(cont) == layout) { + list_t *siblings = container_get_siblings(cont); + int index = list_find(siblings, cont); + if (index > 0 && (edge == WLR_EDGE_LEFT || edge == WLR_EDGE_TOP)) { + return false; + } + if (index < siblings->length - 1 && + (edge == WLR_EDGE_RIGHT || edge == WLR_EDGE_BOTTOM)) { + return false; + } + } + cont = cont->parent; + } + return true; +} + +static enum wlr_edges find_edge(struct sway_container *cont, + struct sway_cursor *cursor) { + if (!cont->view) { + return WLR_EDGE_NONE; + } + if (cont->border == B_NONE || !cont->border_thickness || + cont->border == B_CSD) { + return WLR_EDGE_NONE; + } + + enum wlr_edges edge = 0; + if (cursor->cursor->x < cont->x + cont->border_thickness) { + edge |= WLR_EDGE_LEFT; + } + if (cursor->cursor->y < cont->y + cont->border_thickness) { + edge |= WLR_EDGE_TOP; + } + if (cursor->cursor->x >= cont->x + cont->width - cont->border_thickness) { + edge |= WLR_EDGE_RIGHT; + } + if (cursor->cursor->y >= cont->y + cont->height - cont->border_thickness) { + edge |= WLR_EDGE_BOTTOM; + } + + return edge; +} + +/** + * If the cursor is over a _resizable_ edge, return the edge. + * Edges that can't be resized are edges of the workspace. + */ +static enum wlr_edges find_resize_edge(struct sway_container *cont, + struct sway_cursor *cursor) { + enum wlr_edges edge = find_edge(cont, cursor); + if (edge && !container_is_floating(cont) && edge_is_external(cont, edge)) { + return WLR_EDGE_NONE; + } + return edge; +} + +/** + * Return the mouse binding which matches modifier, click location, release, + * and pressed button state, otherwise return null. + */ +static struct sway_binding* get_active_mouse_binding( + struct seatop_default_event *e, list_t *bindings, uint32_t modifiers, + bool release, bool on_titlebar, bool on_border, bool on_content, + bool on_workspace, const char *identifier) { + uint32_t click_region = + ((on_titlebar || on_workspace) ? BINDING_TITLEBAR : 0) | + ((on_border || on_workspace) ? BINDING_BORDER : 0) | + ((on_content || on_workspace) ? BINDING_CONTENTS : 0); + + struct sway_binding *current = NULL; + for (int i = 0; i < bindings->length; ++i) { + struct sway_binding *binding = bindings->items[i]; + if (modifiers ^ binding->modifiers || + e->pressed_button_count != (size_t)binding->keys->length || + release != (binding->flags & BINDING_RELEASE) || + !(click_region & binding->flags) || + (on_workspace && + (click_region & binding->flags) != click_region) || + (strcmp(binding->input, identifier) != 0 && + strcmp(binding->input, "*") != 0)) { + continue; + } + + bool match = true; + for (size_t j = 0; j < e->pressed_button_count; j++) { + uint32_t key = *(uint32_t *)binding->keys->items[j]; + if (key != e->pressed_buttons[j]) { + match = false; + break; + } + } + if (!match) { + continue; + } + + if (!current || strcmp(current->input, "*") == 0) { + current = binding; + if (strcmp(current->input, identifier) == 0) { + // If a binding is found for the exact input, quit searching + break; + } + } + } + return current; +} + +/** + * Remove a button (and duplicates) from the sorted list of currently pressed + * buttons. + */ +static void state_erase_button(struct seatop_default_event *e, + uint32_t button) { + size_t j = 0; + for (size_t i = 0; i < e->pressed_button_count; ++i) { + if (i > j) { + e->pressed_buttons[j] = e->pressed_buttons[i]; + } + if (e->pressed_buttons[i] != button) { + ++j; + } + } + while (e->pressed_button_count > j) { + --e->pressed_button_count; + e->pressed_buttons[e->pressed_button_count] = 0; + } +} + +/** + * Add a button to the sorted list of currently pressed buttons, if there + * is space. + */ +static void state_add_button(struct seatop_default_event *e, uint32_t button) { + if (e->pressed_button_count >= SWAY_CURSOR_PRESSED_BUTTONS_CAP) { + return; + } + size_t i = 0; + while (i < e->pressed_button_count && e->pressed_buttons[i] < button) { + ++i; + } + size_t j = e->pressed_button_count; + while (j > i) { + e->pressed_buttons[j] = e->pressed_buttons[j - 1]; + --j; + } + e->pressed_buttons[i] = button; + e->pressed_button_count++; +} + +static void cursor_do_rebase(struct sway_cursor *cursor, uint32_t time_msec, + struct sway_node *node, struct wlr_surface *surface, + double sx, double sy) { + // 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) { + cursor_set_image(cursor, "left_ptr", client); + } + } else if (node && node->type == N_CONTAINER) { + // Try a node's resize edge + enum wlr_edges edge = find_resize_edge(node->sway_container, cursor); + if (edge == WLR_EDGE_NONE) { + cursor_set_image(cursor, "left_ptr", NULL); + } else if (container_is_floating(node->sway_container)) { + cursor_set_image(cursor, wlr_xcursor_get_resize_name(edge), NULL); + } else { + if (edge & (WLR_EDGE_LEFT | WLR_EDGE_RIGHT)) { + cursor_set_image(cursor, "col-resize", NULL); + } else { + cursor_set_image(cursor, "row-resize", NULL); + } + } + } else { + cursor_set_image(cursor, "left_ptr", NULL); + } + + // Send pointer enter/leave + struct wlr_seat *wlr_seat = cursor->seat->wlr_seat; + if (surface) { + if (seat_is_input_allowed(cursor->seat, surface)) { + wlr_seat_pointer_notify_enter(wlr_seat, surface, sx, sy); + wlr_seat_pointer_notify_motion(wlr_seat, time_msec, sx, sy); + } + } else { + wlr_seat_pointer_clear_focus(wlr_seat); + } +} + +/*----------------------------------\ + * Functions used by handle_button / + *--------------------------------*/ + +static void handle_button(struct sway_seat *seat, uint32_t time_msec, + struct wlr_input_device *device, uint32_t button, + enum wlr_button_state state) { + struct seatop_default_event *e = seat->seatop_data; + struct sway_cursor *cursor = seat->cursor; + + // Determine what's under the cursor + struct wlr_surface *surface = NULL; + double sx, sy; + struct sway_node *node = node_at_coords(seat, + cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy); + struct sway_container *cont = node && node->type == N_CONTAINER ? + node->sway_container : NULL; + bool is_floating = cont && container_is_floating(cont); + bool is_floating_or_child = cont && container_is_floating_or_child(cont); + bool is_fullscreen_or_child = cont && container_is_fullscreen_or_child(cont); + enum wlr_edges edge = cont ? find_edge(cont, cursor) : WLR_EDGE_NONE; + enum wlr_edges resize_edge = edge ? + find_resize_edge(cont, cursor) : WLR_EDGE_NONE; + bool on_border = edge != WLR_EDGE_NONE; + bool on_contents = cont && !on_border && surface; + bool on_workspace = node && node->type == N_WORKSPACE; + bool on_titlebar = cont && !on_border && !surface; + + // Handle mouse bindings + struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat->wlr_seat); + uint32_t modifiers = keyboard ? wlr_keyboard_get_modifiers(keyboard) : 0; + + char *device_identifier = device ? input_device_get_identifier(device) + : strdup("*"); + struct sway_binding *binding = NULL; + if (state == WLR_BUTTON_PRESSED) { + state_add_button(e, button); + binding = get_active_mouse_binding(e, + config->current_mode->mouse_bindings, modifiers, false, + on_titlebar, on_border, on_contents, on_workspace, + device_identifier); + } else { + binding = get_active_mouse_binding(e, + config->current_mode->mouse_bindings, modifiers, true, + on_titlebar, on_border, on_contents, on_workspace, + device_identifier); + state_erase_button(e, button); + } + free(device_identifier); + if (binding) { + seat_execute_command(seat, binding); + return; + } + + // Handle clicking an empty workspace + if (node && node->type == N_WORKSPACE) { + seat_set_focus(seat, node); + return; + } + + // Handle clicking a layer surface + if (surface && wlr_surface_is_layer_surface(surface)) { + struct wlr_layer_surface_v1 *layer = + wlr_layer_surface_v1_from_wlr_surface(surface); + if (layer->current.keyboard_interactive) { + seat_set_focus_layer(seat, layer); + } + seat_pointer_notify_button(seat, time_msec, button, state); + return; + } + + // Handle tiling resize via border + if (cont && resize_edge && button == BTN_LEFT && + state == WLR_BUTTON_PRESSED && !is_floating) { + seat_set_focus_container(seat, cont); + seatop_begin_resize_tiling(seat, cont, edge); + return; + } + + // Handle tiling resize via mod + bool mod_pressed = keyboard && + (wlr_keyboard_get_modifiers(keyboard) & config->floating_mod); + if (cont && !is_floating_or_child && mod_pressed && + state == WLR_BUTTON_PRESSED) { + uint32_t btn_resize = config->floating_mod_inverse ? + BTN_LEFT : BTN_RIGHT; + if (button == btn_resize) { + edge = 0; + edge |= cursor->cursor->x > cont->x + cont->width / 2 ? + WLR_EDGE_RIGHT : WLR_EDGE_LEFT; + edge |= cursor->cursor->y > cont->y + cont->height / 2 ? + WLR_EDGE_BOTTOM : WLR_EDGE_TOP; + + const char *image = NULL; + if (edge == (WLR_EDGE_LEFT | WLR_EDGE_TOP)) { + image = "nw-resize"; + } else if (edge == (WLR_EDGE_TOP | WLR_EDGE_RIGHT)) { + image = "ne-resize"; + } else if (edge == (WLR_EDGE_RIGHT | WLR_EDGE_BOTTOM)) { + image = "se-resize"; + } else if (edge == (WLR_EDGE_BOTTOM | WLR_EDGE_LEFT)) { + image = "sw-resize"; + } + cursor_set_image(seat->cursor, image, NULL); + seat_set_focus_container(seat, cont); + seatop_begin_resize_tiling(seat, cont, edge); + return; + } + } + + // Handle beginning floating move + if (cont && is_floating_or_child && !is_fullscreen_or_child && + state == WLR_BUTTON_PRESSED) { + uint32_t btn_move = config->floating_mod_inverse ? BTN_RIGHT : BTN_LEFT; + if (button == btn_move && state == WLR_BUTTON_PRESSED && + (mod_pressed || on_titlebar)) { + while (cont->parent) { + cont = cont->parent; + } + seat_set_focus_container(seat, cont); + seatop_begin_move_floating(seat, cont); + return; + } + } + + // Handle beginning floating resize + if (cont && is_floating_or_child && !is_fullscreen_or_child && + state == WLR_BUTTON_PRESSED) { + // Via border + if (button == BTN_LEFT && resize_edge != WLR_EDGE_NONE) { + seatop_begin_resize_floating(seat, cont, resize_edge); + return; + } + + // Via mod+click + uint32_t btn_resize = config->floating_mod_inverse ? + BTN_LEFT : BTN_RIGHT; + if (mod_pressed && button == btn_resize) { + struct sway_container *floater = cont; + while (floater->parent) { + floater = floater->parent; + } + edge = 0; + edge |= cursor->cursor->x > floater->x + floater->width / 2 ? + WLR_EDGE_RIGHT : WLR_EDGE_LEFT; + edge |= cursor->cursor->y > floater->y + floater->height / 2 ? + WLR_EDGE_BOTTOM : WLR_EDGE_TOP; + seatop_begin_resize_floating(seat, floater, edge); + return; + } + } + + // Handle moving a tiling container + if (config->tiling_drag && (mod_pressed || on_titlebar) && + state == WLR_BUTTON_PRESSED && !is_floating_or_child && + cont && cont->fullscreen_mode == FULLSCREEN_NONE) { + struct sway_container *focus = seat_get_focused_container(seat); + bool focused = focus == cont || container_has_ancestor(focus, cont); + if (on_titlebar && !focused) { + node = seat_get_focus_inactive(seat, &cont->node); + seat_set_focus(seat, node); + } + + // If moving a container by it's title bar, use a threshold for the drag + if (!mod_pressed && config->tiling_drag_threshold > 0) { + seatop_begin_move_tiling_threshold(seat, cont); + } else { + seatop_begin_move_tiling(seat, cont); + } + return; + } + + // Handle mousedown on a container surface + if (surface && cont && state == WLR_BUTTON_PRESSED) { + seat_set_focus_container(seat, cont); + seatop_begin_down(seat, cont, time_msec, sx, sy); + seat_pointer_notify_button(seat, time_msec, button, WLR_BUTTON_PRESSED); + return; + } + + // Handle clicking a container surface or decorations + if (cont) { + node = seat_get_focus_inactive(seat, &cont->node); + seat_set_focus(seat, node); + seat_pointer_notify_button(seat, time_msec, button, state); + return; + } + + seat_pointer_notify_button(seat, time_msec, button, state); +} + +/*----------------------------------\ + * Functions used by handle_motion / + *--------------------------------*/ + +static void check_focus_follows_mouse(struct sway_seat *seat, + struct seatop_default_event *e, struct sway_node *hovered_node) { + struct sway_node *focus = seat_get_focus(seat); + + // If a workspace node is hovered (eg. in the gap area), only set focus if + // the workspace is on a different output to the previous focus. + if (focus && hovered_node->type == N_WORKSPACE) { + struct sway_output *focused_output = node_get_output(focus); + struct sway_output *hovered_output = node_get_output(hovered_node); + if (hovered_output != focused_output) { + seat_set_focus(seat, seat_get_focus_inactive(seat, hovered_node)); + } + return; + } + + if (node_is_view(hovered_node)) { + if (hovered_node != e->previous_node || + config->focus_follows_mouse == FOLLOWS_ALWAYS) { + seat_set_focus(seat, hovered_node); + } else { + // Focusing a tab which contains a split child + struct sway_node *next_focus = + seat_get_focus_inactive(seat, &root->node); + if (next_focus && node_is_view(next_focus) && + view_is_visible(next_focus->sway_container->view)) { + seat_set_focus(seat, next_focus); + } + } + } +} + +static void handle_motion(struct sway_seat *seat, uint32_t time_msec, + double dx, double dy) { + struct seatop_default_event *e = seat->seatop_data; + struct sway_cursor *cursor = seat->cursor; + + struct wlr_surface *surface = NULL; + double sx, sy; + struct sway_node *node = node_at_coords(seat, + cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy); + + if (node && config->focus_follows_mouse != FOLLOWS_NO) { + check_focus_follows_mouse(seat, e, node); + } + + cursor_do_rebase(cursor, time_msec, node, surface, sx, sy); + + struct sway_drag_icon *drag_icon; + wl_list_for_each(drag_icon, &root->drag_icons, link) { + if (drag_icon->seat == seat) { + drag_icon_update_position(drag_icon); + } + } + + e->previous_node = node; +} + +/*--------------------------------\ + * Functions used by handle_axis / + *------------------------------*/ + +static uint32_t wl_axis_to_button(struct wlr_event_pointer_axis *event) { + switch (event->orientation) { + case WLR_AXIS_ORIENTATION_VERTICAL: + return event->delta < 0 ? SWAY_SCROLL_UP : SWAY_SCROLL_DOWN; + case WLR_AXIS_ORIENTATION_HORIZONTAL: + return event->delta < 0 ? SWAY_SCROLL_LEFT : SWAY_SCROLL_RIGHT; + default: + sway_log(SWAY_DEBUG, "Unknown axis orientation"); + return 0; + } +} + +static void handle_axis(struct sway_seat *seat, + struct wlr_event_pointer_axis *event) { + struct sway_input_device *input_device = + event->device ? event->device->data : NULL; + struct input_config *ic = + input_device ? input_device_get_config(input_device) : NULL; + struct sway_cursor *cursor = seat->cursor; + struct seatop_default_event *e = seat->seatop_data; + + // Determine what's under the cursor + struct wlr_surface *surface = NULL; + double sx, sy; + struct sway_node *node = node_at_coords(seat, + cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy); + struct sway_container *cont = node && node->type == N_CONTAINER ? + node->sway_container : NULL; + enum wlr_edges edge = cont ? find_edge(cont, cursor) : WLR_EDGE_NONE; + bool on_border = edge != WLR_EDGE_NONE; + bool on_titlebar = cont && !on_border && !surface; + bool on_titlebar_border = cont && on_border && + cursor->cursor->y < cont->content_y; + bool on_contents = cont && !on_border && surface; + bool on_workspace = node && node->type == N_WORKSPACE; + float scroll_factor = + (ic == NULL || ic->scroll_factor == FLT_MIN) ? 1.0f : ic->scroll_factor; + + bool handled = false; + + // Gather information needed for mouse bindings + struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat->wlr_seat); + uint32_t modifiers = keyboard ? wlr_keyboard_get_modifiers(keyboard) : 0; + struct wlr_input_device *device = + input_device ? input_device->wlr_device : NULL; + char *dev_id = device ? input_device_get_identifier(device) : strdup("*"); + uint32_t button = wl_axis_to_button(event); + + // Handle mouse bindings - x11 mouse buttons 4-7 - press event + struct sway_binding *binding = NULL; + state_add_button(e, button); + binding = get_active_mouse_binding(e, config->current_mode->mouse_bindings, + modifiers, false, on_titlebar, on_border, on_contents, on_workspace, + dev_id); + if (binding) { + seat_execute_command(seat, binding); + handled = true; + } + + // Scrolling on a tabbed or stacked title bar (handled as press event) + if (!handled && (on_titlebar || on_titlebar_border)) { + enum sway_container_layout layout = container_parent_layout(cont); + if (layout == L_TABBED || layout == L_STACKED) { + struct sway_node *tabcontainer = node_get_parent(node); + struct sway_node *active = + seat_get_active_tiling_child(seat, tabcontainer); + list_t *siblings = container_get_siblings(cont); + int desired = list_find(siblings, active->sway_container) + + round(scroll_factor * event->delta_discrete); + if (desired < 0) { + desired = 0; + } else if (desired >= siblings->length) { + desired = siblings->length - 1; + } + struct sway_node *old_focus = seat_get_focus(seat); + struct sway_container *new_sibling_con = siblings->items[desired]; + struct sway_node *new_sibling = &new_sibling_con->node; + struct sway_node *new_focus = + seat_get_focus_inactive(seat, new_sibling); + if (node_has_ancestor(old_focus, tabcontainer)) { + seat_set_focus(seat, new_focus); + } else { + // Scrolling when focus is not in the tabbed container at all + seat_set_raw_focus(seat, new_sibling); + seat_set_raw_focus(seat, new_focus); + seat_set_raw_focus(seat, old_focus); + } + handled = true; + } + } + + // Handle mouse bindings - x11 mouse buttons 4-7 - release event + binding = get_active_mouse_binding(e, config->current_mode->mouse_bindings, + modifiers, true, on_titlebar, on_border, on_contents, on_workspace, + dev_id); + state_erase_button(e, button); + if (binding) { + seat_execute_command(seat, binding); + handled = true; + } + free(dev_id); + + if (!handled) { + wlr_seat_pointer_notify_axis(cursor->seat->wlr_seat, event->time_msec, + event->orientation, scroll_factor * event->delta, + round(scroll_factor * event->delta_discrete), event->source); + } +} + +/*----------------------------------\ + * Functions used by handle_rebase / + *--------------------------------*/ + +static void handle_rebase(struct sway_seat *seat, uint32_t time_msec) { + struct seatop_default_event *e = seat->seatop_data; + struct sway_cursor *cursor = seat->cursor; + struct wlr_surface *surface = NULL; + double sx = 0.0, sy = 0.0; + e->previous_node = node_at_coords(seat, + cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy); + cursor_do_rebase(cursor, time_msec, e->previous_node, surface, sx, sy); +} + +static const struct sway_seatop_impl seatop_impl = { + .button = handle_button, + .motion = handle_motion, + .axis = handle_axis, + .rebase = handle_rebase, + .allow_set_cursor = true, +}; + +void seatop_begin_default(struct sway_seat *seat) { + seatop_end(seat); + + struct seatop_default_event *e = + calloc(1, sizeof(struct seatop_default_event)); + sway_assert(e, "Unable to allocate seatop_default_event"); + seat->seatop_impl = &seatop_impl; + seat->seatop_data = e; + + seatop_rebase(seat, 0); +} diff --git a/sway/input/seatop_down.c b/sway/input/seatop_down.c index fb2cf1d07..95ea7cbb2 100644 --- a/sway/input/seatop_down.c +++ b/sway/input/seatop_down.c @@ -3,21 +3,26 @@ #include "sway/input/cursor.h" #include "sway/input/seat.h" #include "sway/tree/view.h" +#include "log.h" struct seatop_down_event { struct sway_container *con; double ref_lx, ref_ly; // cursor's x/y at start of op double ref_con_lx, ref_con_ly; // container's x/y at start of op - bool moved; }; static void handle_button(struct sway_seat *seat, uint32_t time_msec, struct wlr_input_device *device, uint32_t button, enum wlr_button_state state) { seat_pointer_notify_button(seat, time_msec, button, state); + + if (seat->cursor->pressed_button_count == 0) { + seatop_begin_default(seat); + } } -static void handle_motion(struct sway_seat *seat, uint32_t time_msec) { +static void handle_motion(struct sway_seat *seat, uint32_t time_msec, + double dx, double dy) { struct seatop_down_event *e = seat->seatop_data; struct sway_container *con = e->con; if (seat_is_input_allowed(seat, con->view->surface)) { @@ -27,49 +32,25 @@ static void handle_motion(struct sway_seat *seat, uint32_t time_msec) { double sy = e->ref_con_ly + moved_y; wlr_seat_pointer_notify_motion(seat->wlr_seat, time_msec, sx, sy); } - e->moved = true; -} - -static void handle_finish(struct sway_seat *seat, uint32_t time_msec) { - struct seatop_down_event *e = seat->seatop_data; - struct sway_cursor *cursor = seat->cursor; - // Set the cursor's previous coords to the x/y at the start of the - // operation, so the container change will be detected if using - // focus_follows_mouse and the cursor moved off the original container - // during the operation. - cursor->previous.x = e->ref_lx; - cursor->previous.y = e->ref_ly; - if (e->moved) { - struct wlr_surface *surface = NULL; - double sx, sy; - struct sway_node *node = node_at_coords(seat, - cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy); - cursor_send_pointer_motion(cursor, 0, node, surface, sx, sy); - } -} - -static void handle_abort(struct sway_seat *seat) { - cursor_set_image(seat->cursor, "left_ptr", NULL); } static void handle_unref(struct sway_seat *seat, struct sway_container *con) { struct seatop_down_event *e = seat->seatop_data; if (e->con == con) { - seatop_abort(seat); + seatop_begin_default(seat); } } static const struct sway_seatop_impl seatop_impl = { .button = handle_button, .motion = handle_motion, - .finish = handle_finish, - .abort = handle_abort, .unref = handle_unref, + .allow_set_cursor = true, }; void seatop_begin_down(struct sway_seat *seat, struct sway_container *con, - uint32_t time_msec, uint32_t button, int sx, int sy) { - seatop_abort(seat); + uint32_t time_msec, int sx, int sy) { + seatop_end(seat); struct seatop_down_event *e = calloc(1, sizeof(struct seatop_down_event)); @@ -81,11 +62,9 @@ void seatop_begin_down(struct sway_seat *seat, struct sway_container *con, e->ref_ly = seat->cursor->cursor->y; e->ref_con_lx = sx; e->ref_con_ly = sy; - e->moved = false; seat->seatop_impl = &seatop_impl; seat->seatop_data = e; - seat->seatop_button = button; container_raise_floating(con); } diff --git a/sway/input/seatop_move_floating.c b/sway/input/seatop_move_floating.c index 8a48a9681..73e489642 100644 --- a/sway/input/seatop_move_floating.c +++ b/sway/input/seatop_move_floating.c @@ -8,45 +8,44 @@ struct seatop_move_floating_event { struct sway_container *con; }; -static void handle_motion(struct sway_seat *seat, uint32_t time_msec) { - struct seatop_move_floating_event *e = seat->seatop_data; - desktop_damage_whole_container(e->con); - container_floating_translate(e->con, - seat->cursor->cursor->x - seat->cursor->previous.x, - seat->cursor->cursor->y - seat->cursor->previous.y); - desktop_damage_whole_container(e->con); +static void handle_button(struct sway_seat *seat, uint32_t time_msec, + struct wlr_input_device *device, uint32_t button, + enum wlr_button_state state) { + if (seat->cursor->pressed_button_count == 0) { + struct seatop_move_floating_event *e = seat->seatop_data; + + // We "move" the container to its own location + // so it discovers its output again. + container_floating_move_to(e->con, e->con->x, e->con->y); + + seatop_begin_default(seat); + } } -static void handle_finish(struct sway_seat *seat, uint32_t time_msec) { +static void handle_motion(struct sway_seat *seat, uint32_t time_msec, + double dx, double dy) { struct seatop_move_floating_event *e = seat->seatop_data; - - // We "move" the container to its own location - // so it discovers its output again. - container_floating_move_to(e->con, e->con->x, e->con->y); - cursor_set_image(seat->cursor, "left_ptr", NULL); -} - -static void handle_abort(struct sway_seat *seat) { - cursor_set_image(seat->cursor, "left_ptr", NULL); + desktop_damage_whole_container(e->con); + container_floating_translate(e->con, dx, dy); + desktop_damage_whole_container(e->con); } static void handle_unref(struct sway_seat *seat, struct sway_container *con) { struct seatop_move_floating_event *e = seat->seatop_data; if (e->con == con) { - seatop_abort(seat); + seatop_begin_default(seat); } } static const struct sway_seatop_impl seatop_impl = { + .button = handle_button, .motion = handle_motion, - .finish = handle_finish, - .abort = handle_abort, .unref = handle_unref, }; void seatop_begin_move_floating(struct sway_seat *seat, - struct sway_container *con, uint32_t button) { - seatop_abort(seat); + struct sway_container *con) { + seatop_end(seat); struct seatop_move_floating_event *e = calloc(1, sizeof(struct seatop_move_floating_event)); @@ -57,7 +56,6 @@ void seatop_begin_move_floating(struct sway_seat *seat, seat->seatop_impl = &seatop_impl; seat->seatop_data = e; - seat->seatop_button = button; container_raise_floating(con); diff --git a/sway/input/seatop_move_tiling.c b/sway/input/seatop_move_tiling.c index 2904792bd..0a2480919 100644 --- a/sway/input/seatop_move_tiling.c +++ b/sway/input/seatop_move_tiling.c @@ -206,7 +206,8 @@ static void handle_motion_postthreshold(struct sway_seat *seat) { desktop_damage_box(&e->drop_box); } -static void handle_motion(struct sway_seat *seat, uint32_t time_msec) { +static void handle_motion(struct sway_seat *seat, uint32_t time_msec, + double dx, double dy) { struct seatop_move_tiling_event *e = seat->seatop_data; if (e->threshold_reached) { handle_motion_postthreshold(seat); @@ -215,10 +216,6 @@ static void handle_motion(struct sway_seat *seat, uint32_t time_msec) { } } -static void handle_abort(struct sway_seat *seat) { - cursor_set_image(seat->cursor, "left_ptr", NULL); -} - static bool is_parallel(enum sway_container_layout layout, enum wlr_edges edge) { bool layout_is_horiz = layout == L_HORIZ || layout == L_TABBED; @@ -226,11 +223,17 @@ static bool is_parallel(enum sway_container_layout layout, return layout_is_horiz == edge_is_horiz; } -static void handle_finish(struct sway_seat *seat, uint32_t time_msec) { +static void handle_button(struct sway_seat *seat, uint32_t time_msec, + struct wlr_input_device *device, uint32_t button, + enum wlr_button_state state) { + if (seat->cursor->pressed_button_count != 0) { + return; + } + struct seatop_move_tiling_event *e = seat->seatop_data; if (!e->target_node) { - handle_abort(seat); + seatop_begin_default(seat); return; } @@ -287,7 +290,7 @@ static void handle_finish(struct sway_seat *seat, uint32_t time_msec) { arrange_workspace(new_ws); } - cursor_set_image(seat->cursor, "left_ptr", NULL); + seatop_begin_default(seat); } static void handle_unref(struct sway_seat *seat, struct sway_container *con) { @@ -296,21 +299,20 @@ static void handle_unref(struct sway_seat *seat, struct sway_container *con) { e->target_node = NULL; } if (e->con == con) { // The container being moved - seatop_abort(seat); + seatop_begin_default(seat); } } static const struct sway_seatop_impl seatop_impl = { + .button = handle_button, .motion = handle_motion, - .finish = handle_finish, - .abort = handle_abort, .unref = handle_unref, .render = handle_render, }; void seatop_begin_move_tiling_threshold(struct sway_seat *seat, - struct sway_container *con, uint32_t button) { - seatop_abort(seat); + struct sway_container *con) { + seatop_end(seat); struct seatop_move_tiling_event *e = calloc(1, sizeof(struct seatop_move_tiling_event)); @@ -323,14 +325,13 @@ void seatop_begin_move_tiling_threshold(struct sway_seat *seat, seat->seatop_impl = &seatop_impl; seat->seatop_data = e; - seat->seatop_button = button; container_raise_floating(con); } void seatop_begin_move_tiling(struct sway_seat *seat, - struct sway_container *con, uint32_t button) { - seatop_begin_move_tiling_threshold(seat, con, button); + struct sway_container *con) { + seatop_begin_move_tiling_threshold(seat, con); struct seatop_move_tiling_event *e = seat->seatop_data; if (e) { e->threshold_reached = true; diff --git a/sway/input/seatop_resize_floating.c b/sway/input/seatop_resize_floating.c index 18c6db73b..b6950bbf3 100644 --- a/sway/input/seatop_resize_floating.c +++ b/sway/input/seatop_resize_floating.c @@ -17,7 +17,16 @@ struct seatop_resize_floating_event { double ref_con_lx, ref_con_ly; // container's x/y at start of op }; -static void handle_motion(struct sway_seat *seat, uint32_t time_msec) { +static void handle_button(struct sway_seat *seat, uint32_t time_msec, + struct wlr_input_device *device, uint32_t button, + enum wlr_button_state state) { + if (seat->cursor->pressed_button_count == 0) { + seatop_begin_default(seat); + } +} + +static void handle_motion(struct sway_seat *seat, uint32_t time_msec, + double dx, double dy) { struct seatop_resize_floating_event *e = seat->seatop_data; struct sway_container *con = e->con; enum wlr_edges edge = e->edge; @@ -107,31 +116,22 @@ static void handle_motion(struct sway_seat *seat, uint32_t time_msec) { arrange_container(con); } -static void handle_finish(struct sway_seat *seat, uint32_t time_msec) { - cursor_set_image(seat->cursor, "left_ptr", NULL); -} - -static void handle_abort(struct sway_seat *seat) { - cursor_set_image(seat->cursor, "left_ptr", NULL); -} - static void handle_unref(struct sway_seat *seat, struct sway_container *con) { struct seatop_resize_floating_event *e = seat->seatop_data; if (e->con == con) { - seatop_abort(seat); + seatop_begin_default(seat); } } static const struct sway_seatop_impl seatop_impl = { + .button = handle_button, .motion = handle_motion, - .finish = handle_finish, - .abort = handle_abort, .unref = handle_unref, }; void seatop_begin_resize_floating(struct sway_seat *seat, - struct sway_container *con, uint32_t button, enum wlr_edges edge) { - seatop_abort(seat); + struct sway_container *con, enum wlr_edges edge) { + seatop_end(seat); struct seatop_resize_floating_event *e = calloc(1, sizeof(struct seatop_resize_floating_event)); @@ -154,7 +154,6 @@ void seatop_begin_resize_floating(struct sway_seat *seat, seat->seatop_impl = &seatop_impl; seat->seatop_data = e; - seat->seatop_button = button; container_raise_floating(con); diff --git a/sway/input/seatop_resize_tiling.c b/sway/input/seatop_resize_tiling.c index db32065c7..6b705823a 100644 --- a/sway/input/seatop_resize_tiling.c +++ b/sway/input/seatop_resize_tiling.c @@ -19,7 +19,16 @@ struct seatop_resize_tiling_event { double v_con_orig_height; // height of the vertical ancestor at start }; -static void handle_motion(struct sway_seat *seat, uint32_t time_msec) { +static void handle_button(struct sway_seat *seat, uint32_t time_msec, + struct wlr_input_device *device, uint32_t button, + enum wlr_button_state state) { + if (seat->cursor->pressed_button_count == 0) { + seatop_begin_default(seat); + } +} + +static void handle_motion(struct sway_seat *seat, uint32_t time_msec, + double dx, double dy) { struct seatop_resize_tiling_event *e = seat->seatop_data; int amount_x = 0; int amount_y = 0; @@ -49,31 +58,22 @@ static void handle_motion(struct sway_seat *seat, uint32_t time_msec) { } } -static void handle_finish(struct sway_seat *seat, uint32_t time_msec) { - cursor_set_image(seat->cursor, "left_ptr", NULL); -} - -static void handle_abort(struct sway_seat *seat) { - cursor_set_image(seat->cursor, "left_ptr", NULL); -} - static void handle_unref(struct sway_seat *seat, struct sway_container *con) { struct seatop_resize_tiling_event *e = seat->seatop_data; if (e->con == con) { - seatop_abort(seat); + seatop_begin_default(seat); } } static const struct sway_seatop_impl seatop_impl = { + .button = handle_button, .motion = handle_motion, - .finish = handle_finish, - .abort = handle_abort, .unref = handle_unref, }; void seatop_begin_resize_tiling(struct sway_seat *seat, - struct sway_container *con, uint32_t button, enum wlr_edges edge) { - seatop_abort(seat); + struct sway_container *con, enum wlr_edges edge) { + seatop_end(seat); struct seatop_resize_tiling_event *e = calloc(1, sizeof(struct seatop_resize_tiling_event)); @@ -105,5 +105,4 @@ void seatop_begin_resize_tiling(struct sway_seat *seat, seat->seatop_impl = &seatop_impl; seat->seatop_data = e; - seat->seatop_button = button; } diff --git a/sway/meson.build b/sway/meson.build index 293a4ed20..cb1f8e258 100644 --- a/sway/meson.build +++ b/sway/meson.build @@ -23,6 +23,7 @@ sway_sources = files( 'input/input-manager.c', 'input/seat.c', + 'input/seatop_default.c', 'input/seatop_down.c', 'input/seatop_move_floating.c', 'input/seatop_move_tiling.c', From 0327c999d7f69de6356b3b30c19b131d029f6e95 Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Sat, 16 Mar 2019 22:45:06 -0400 Subject: [PATCH 078/191] config/output: handle wildcard in get_output_config In #3916, I overlooked that `get_output_config` does not handle wildcards unless the config is reloading, which is a remnant of older iterations of the output config handling that went unnoticed due to `output_find_config` handling it. With the current version of the output config handling, having `get_output_config` handle wildcard configs is actually preferable. This fixes having only a wildcard output config in the config file or when connecting/enabling a new output with only a wildcard config existing. --- sway/config/output.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/sway/config/output.c b/sway/config/output.c index 5656e2c10..44aae03aa 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -493,19 +493,20 @@ static struct output_config *get_output_config(char *identifier, free(result->name); result->name = strdup(identifier); merge_output_config(result, oc_id); - } else if (config->reloading) { - // Neither config exists, but we need to reset the output so create a - // default config for the output and if a wildcard config exists, merge - // that on top - free(result->name); - result->name = strdup("*"); + } else { i = list_seq_find(config->output_configs, output_name_cmp, "*"); if (i >= 0) { + // No name or identifier config, but there is a wildcard config + free(result->name); + result->name = strdup("*"); merge_output_config(result, config->output_configs->items[i]); + } else if (!config->reloading) { + // No name, identifier, or wildcard config. Since we are not + // reloading with defaults, the output config will be empty, so + // just return NULL + free_output_config(result); + result = NULL; } - } else { - free_output_config(result); - result = NULL; } free(id_on_name); From 38bd60c4b3e70cb5584529358162f8b37d43669f Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Mon, 18 Mar 2019 21:23:28 +1000 Subject: [PATCH 079/191] Document the title_format command --- sway/sway.5.scd | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/sway/sway.5.scd b/sway/sway.5.scd index 4f4522fb5..989717cb1 100644 --- a/sway/sway.5.scd +++ b/sway/sway.5.scd @@ -265,6 +265,24 @@ set|plus|minus becomes fullscreen on the same workspace as the first container. In either of those cases, the second container will gain focus. +*title_format* + Sets the format of window titles. The following placeholders may be used: + + %title - The title supplied by the window + %app_id - The wayland app ID (applicable to wayland windows only) + %class - The X11 classname (applicable to xwayland windows only) + %instance - The X11 instance (applicable to xwayland windows only) + %shell - The protocol the window is using (typically xwayland or + xdg_shell) + + This command is typically used with *for_window* criteria. For example: + + for_window [title="."] title_format "%title (%app_id)" + + Note that markup requires pango to be enabled via the *font* command. + + The default format is "%title". + The following commands may be used either in the configuration file or at runtime. @@ -300,7 +318,7 @@ runtime. values in X11 (1=left, 2=middle, 3=right, 4=scroll up, 5=scroll down, 6=scroll left, 7=scroll right, 8=back, 9=forward). For the latter option, you can find the event names using _libinput debug-events_. - + _--whole-window_, _--border_, and _--exclude-titlebar_ are mouse-only options which affect the region in which the mouse bindings can be triggered. By default, mouse bindings are only triggered when over the title bar. With the From e9a476244df7a8886fc6fc6785251198ed76e601 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Mon, 18 Mar 2019 20:52:56 +1000 Subject: [PATCH 080/191] Remove debug tree This feature has served its purpose. It's better to use IPC now. --- include/sway/debug.h | 22 ----- include/sway/server.h | 14 ++++ include/sway/tree/root.h | 2 - sway/debug-tree.c | 161 ------------------------------------- sway/desktop/render.c | 7 -- sway/desktop/transaction.c | 6 -- sway/input/seat.c | 4 - sway/main.c | 3 - sway/meson.build | 1 - 9 files changed, 14 insertions(+), 206 deletions(-) delete mode 100644 include/sway/debug.h delete mode 100644 sway/debug-tree.c diff --git a/include/sway/debug.h b/include/sway/debug.h deleted file mode 100644 index 0e9bb0565..000000000 --- a/include/sway/debug.h +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef SWAY_DEBUG_H -#define SWAY_DEBUG_H -#include - -struct sway_debug { - bool noatomic; // Ignore atomic layout updates - bool render_tree; // Render the tree overlay - bool txn_timings; // Log verbose messages about transactions - bool txn_wait; // Always wait for the timeout before applying - - enum { - DAMAGE_DEFAULT, // Default behaviour - DAMAGE_HIGHLIGHT, // Highlight regions of the screen being damaged - DAMAGE_RERENDER, // Render the full output when any damage occurs - } damage; -}; - -extern struct sway_debug debug; - -void update_debug_tree(void); - -#endif diff --git a/include/sway/server.h b/include/sway/server.h index 5eef7c1a9..39cf4f18b 100644 --- a/include/sway/server.h +++ b/include/sway/server.h @@ -74,6 +74,20 @@ struct sway_server { struct sway_server server; +struct sway_debug { + bool noatomic; // Ignore atomic layout updates + bool txn_timings; // Log verbose messages about transactions + bool txn_wait; // Always wait for the timeout before applying + + enum { + DAMAGE_DEFAULT, // Default behaviour + DAMAGE_HIGHLIGHT, // Highlight regions of the screen being damaged + DAMAGE_RERENDER, // Render the full output when any damage occurs + } damage; +}; + +struct sway_debug debug; + /* Prepares an unprivileged server_init by performing all privileged operations in advance */ bool server_privileged_prepare(struct sway_server *server); bool server_init(struct sway_server *server); diff --git a/include/sway/tree/root.h b/include/sway/tree/root.h index 9ff45eb5f..9f6cd3bb6 100644 --- a/include/sway/tree/root.h +++ b/include/sway/tree/root.h @@ -21,8 +21,6 @@ struct sway_root { #endif struct wl_list drag_icons; // sway_drag_icon::link - struct wlr_texture *debug_tree; - // Includes disabled outputs struct wl_list all_outputs; // sway_output::link diff --git a/sway/debug-tree.c b/sway/debug-tree.c deleted file mode 100644 index 0444bb3fd..000000000 --- a/sway/debug-tree.c +++ /dev/null @@ -1,161 +0,0 @@ -#include -#include -#include -#include "config.h" -#include "sway/debug.h" -#include "sway/input/input-manager.h" -#include "sway/input/seat.h" -#include "sway/output.h" -#include "sway/server.h" -#include "sway/tree/container.h" -#include "sway/tree/root.h" -#include "sway/tree/workspace.h" -#include "cairo.h" -#include "config.h" -#include "pango.h" - -struct sway_debug debug; - -static const char *layout_to_str(enum sway_container_layout layout) { - switch (layout) { - case L_HORIZ: - return "L_HORIZ"; - case L_VERT: - return "L_VERT"; - case L_STACKED: - return "L_STACKED"; - case L_TABBED: - return "L_TABBED"; - case L_NONE: - return "L_NONE"; - } - return "L_NONE"; -} - -static char *get_string(struct sway_node *node) { - char *buffer = malloc(512); - switch (node->type) { - case N_ROOT: - snprintf(buffer, 512, "N_ROOT id:%zd %.fx%.f@%.f,%.f", node->id, - root->width, root->height, root->x, root->y); - break; - case N_OUTPUT: - snprintf(buffer, 512, "N_OUTPUT id:%zd '%s' %dx%d@%d,%d", node->id, - node->sway_output->wlr_output->name, - node->sway_output->width, - node->sway_output->height, - node->sway_output->lx, - node->sway_output->ly); - break; - case N_WORKSPACE: - snprintf(buffer, 512, "N_WORKSPACE id:%zd '%s' %s %dx%d@%.f,%.f", - node->id, node->sway_workspace->name, - layout_to_str(node->sway_workspace->layout), - node->sway_workspace->width, node->sway_workspace->height, - node->sway_workspace->x, node->sway_workspace->y); - break; - case N_CONTAINER: - snprintf(buffer, 512, "N_CONTAINER id:%zd '%s' %s %.fx%.f@%.f,%.f", - node->id, node->sway_container->title, - layout_to_str(node->sway_container->layout), - node->sway_container->width, node->sway_container->height, - node->sway_container->x, node->sway_container->y); - break; - } - return buffer; -} - -static list_t *get_children(struct sway_node *node) { - switch (node->type) { - case N_ROOT: - return root->outputs; - case N_OUTPUT: - return node->sway_output->workspaces; - case N_WORKSPACE: - return node->sway_workspace->tiling; - case N_CONTAINER: - return node->sway_container->children; - } - return NULL; -} - -static int draw_node(cairo_t *cairo, struct sway_node *node, - struct sway_node *focus, int x, int y) { - int text_width, text_height; - char *buffer = get_string(node); - get_text_size(cairo, "monospace", &text_width, &text_height, NULL, - 1, false, buffer); - cairo_save(cairo); - cairo_rectangle(cairo, x + 2, y, text_width - 2, text_height); - cairo_set_source_u32(cairo, 0xFFFFFFE0); - cairo_fill(cairo); - int height = text_height; - list_t *children = get_children(node); - if (children) { - for (int i = 0; i < children->length; ++i) { - // This is really dirty - the list contains specific structs but - // we're casting them as nodes. This works because node is the first - // item in each specific struct. This is acceptable because this is - // debug code. - struct sway_node *child = children->items[i]; - if (node_get_parent(child) == node) { - cairo_set_source_u32(cairo, 0x000000FF); - } else { - cairo_set_source_u32(cairo, 0xFF0000FF); - } - height += draw_node(cairo, child, focus, x + 10, y + height); - } - } - cairo_set_source_u32(cairo, 0xFFFFFFE0); - cairo_rectangle(cairo, x, y, 2, height); - cairo_fill(cairo); - cairo_restore(cairo); - cairo_move_to(cairo, x, y); - if (focus == node) { - cairo_set_source_u32(cairo, 0x0000FFFF); - } - pango_printf(cairo, "monospace", 1, false, buffer); - free(buffer); - return height; -} - -void update_debug_tree(void) { - if (!debug.render_tree) { - return; - } - - int width = 640, height = 480; - for (int i = 0; i < root->outputs->length; ++i) { - struct sway_output *output = root->outputs->items[i]; - if (output->width > width) { - width = output->width; - } - if (output->height > height) { - height = output->height; - } - } - cairo_surface_t *surface = - cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height); - cairo_t *cairo = cairo_create(surface); - PangoContext *pango = pango_cairo_create_context(cairo); - - struct sway_seat *seat = input_manager_current_seat(); - struct sway_node *focus = seat_get_focus(seat); - - cairo_set_source_u32(cairo, 0x000000FF); - draw_node(cairo, &root->node, focus, 0, 0); - - cairo_surface_flush(surface); - struct wlr_renderer *renderer = wlr_backend_get_renderer(server.backend); - if (root->debug_tree) { - wlr_texture_destroy(root->debug_tree); - } - unsigned char *data = cairo_image_surface_get_data(surface); - int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width); - struct wlr_texture *texture = wlr_texture_from_pixels(renderer, - WL_SHM_FORMAT_ARGB8888, stride, width, height, data); - root->debug_tree = texture; - cairo_surface_destroy(surface); - g_object_unref(pango); - cairo_destroy(cairo); -} diff --git a/sway/desktop/render.c b/sway/desktop/render.c index 9e936bb53..7216e30bb 100644 --- a/sway/desktop/render.c +++ b/sway/desktop/render.c @@ -16,7 +16,6 @@ #include "log.h" #include "config.h" #include "sway/config.h" -#include "sway/debug.h" #include "sway/input/input-manager.h" #include "sway/input/seat.h" #include "sway/layers.h" @@ -1075,12 +1074,6 @@ render_overlay: render_drag_icons(output, damage, &root->drag_icons); renderer_end: - if (debug.render_tree) { - wlr_renderer_scissor(renderer, NULL); - wlr_render_texture(renderer, root->debug_tree, - wlr_output->transform_matrix, 0, 40, 1); - } - wlr_renderer_scissor(renderer, NULL); wlr_output_render_software_cursors(wlr_output, damage); wlr_renderer_end(renderer); diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index 4098ed22c..e5f0ee3d1 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -5,7 +5,6 @@ #include #include #include "sway/config.h" -#include "sway/debug.h" #include "sway/desktop.h" #include "sway/desktop/idle_inhibit_v1.h" #include "sway/desktop/transaction.h" @@ -465,11 +464,6 @@ static void transaction_commit(struct sway_transaction *transaction) { transaction->num_waiting = 0; } } - - // The debug tree shows the pending/live tree. Here is a good place to - // update it, because we make a transaction every time we change the pending - // tree. - update_debug_tree(); } static void set_instruction_ready( diff --git a/sway/input/seat.c b/sway/input/seat.c index 2d3552753..7fc87ee4a 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -10,7 +10,6 @@ #include #include "config.h" #include "log.h" -#include "sway/debug.h" #include "sway/desktop.h" #include "sway/input/cursor.h" #include "sway/input/input-manager.h" @@ -812,7 +811,6 @@ void seat_set_focus(struct sway_seat *seat, struct sway_node *node) { } seat_send_unfocus(last_focus, seat); seat->has_focus = false; - update_debug_tree(); return; } @@ -937,8 +935,6 @@ void seat_set_focus(struct sway_seat *seat, struct sway_node *node) { // the workspace needs to be arranged arrange_workspace(new_workspace); } - - update_debug_tree(); } void seat_set_focus_container(struct sway_seat *seat, diff --git a/sway/main.c b/sway/main.c index f3cc1bc8b..ba4e25624 100644 --- a/sway/main.c +++ b/sway/main.c @@ -15,7 +15,6 @@ #include #include "sway/commands.h" #include "sway/config.h" -#include "sway/debug.h" #include "sway/server.h" #include "sway/swaynag.h" #include "sway/tree/root.h" @@ -206,8 +205,6 @@ void enable_debug_flag(const char *flag) { debug.damage = DAMAGE_RERENDER; } else if (strcmp(flag, "noatomic") == 0) { debug.noatomic = true; - } else if (strcmp(flag, "render-tree") == 0) { - debug.render_tree = true; } else if (strcmp(flag, "txn-wait") == 0) { debug.txn_wait = true; } else if (strcmp(flag, "txn-timings") == 0) { diff --git a/sway/meson.build b/sway/meson.build index cb1f8e258..1138bc73f 100644 --- a/sway/meson.build +++ b/sway/meson.build @@ -2,7 +2,6 @@ sway_sources = files( 'commands.c', 'config.c', 'criteria.c', - 'debug-tree.c', 'decoration.c', 'ipc-json.c', 'ipc-server.c', From bfa20e65d846f8e8bf7b967b2440d99d82ca9a86 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Tue, 19 Mar 2019 19:41:24 +1000 Subject: [PATCH 081/191] Clean up focus follows mouse logic Firstly, this fixes a recent regression where having `focus_follows_mouse yes` and hovering an inactive tab caused it to gain focus. The code was missing a view_is_visible check. The code is handling the logic for both focus_follows_mouse yes and focus_follows_mouse always, where the latter will apply when nudging the mouse after a workspace switch. However, the view_is_visible check didn't apply when using focus_follows_mouse always, so hovering a tab with that configuration would cause is to focus. This was a bug. When adding the view_is_visible check, it now applies to both yes and always. Note that the comment about the split container was wrong. At this point the hovered node cannot be a split container because it passed the node_is_view check. The comment has been removed. Lastly, the else condition is completely removed. This didn't appear to have any practical use. Setting focus to the result of seat_get_focus_inactive is very likely going to be a no op. There is a slim chance that this will break something, and if so I'd like to find out what so it can be properly documented in the code. --- sway/input/seatop_default.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/sway/input/seatop_default.c b/sway/input/seatop_default.c index fc785cda4..9cf58c47f 100644 --- a/sway/input/seatop_default.c +++ b/sway/input/seatop_default.c @@ -439,18 +439,17 @@ static void check_focus_follows_mouse(struct sway_seat *seat, return; } - if (node_is_view(hovered_node)) { + // This is where we handle the common case. We don't want to focus inactive + // tabs, hence the view_is_visible check. + if (node_is_view(hovered_node) && + view_is_visible(hovered_node->sway_container->view)) { + // e->previous_node is the node which the cursor was over previously. + // If focus_follows_mouse is yes and the cursor got over the view due + // to, say, a workspace switch, we don't want to set the focus. + // But if focus_follows_mouse is "always", we do. if (hovered_node != e->previous_node || config->focus_follows_mouse == FOLLOWS_ALWAYS) { seat_set_focus(seat, hovered_node); - } else { - // Focusing a tab which contains a split child - struct sway_node *next_focus = - seat_get_focus_inactive(seat, &root->node); - if (next_focus && node_is_view(next_focus) && - view_is_visible(next_focus->sway_container->view)) { - seat_set_focus(seat, next_focus); - } } } } From bdb402404cd6d54242b0b1dc2360cfc5679e52f2 Mon Sep 17 00:00:00 2001 From: Ryan Walklin Date: Wed, 20 Mar 2019 14:47:29 +1100 Subject: [PATCH 082/191] Support WLR_INPUT_DEVICE_SWITCH in sway This commit adds support for laptop lid and tablet mode switches as provided by evdev/libinput and handled by wlroots. Adds a new bindswitch command with syntax: bindswitch : Where is one of: tablet for WLR_SWITCH_TYPE_TABLET_MODE lid for WLR_SWITCH_TYPE_LID is one of: on for WLR_SWITCH_STATE_ON off for WLR_SWITCH_STATE_OFF toggle for WLR_SWITCH_STATE_TOGGLE (Note that WLR_SWITCH_STATE_TOGGLE doesn't map to libinput and will trigger at both on and off events) --- include/sway/commands.h | 1 + include/sway/config.h | 15 +++++ include/sway/input/seat.h | 1 + include/sway/input/switch.h | 19 ++++++ sway/commands.c | 2 + sway/commands/bind.c | 118 ++++++++++++++++++++++++++++++++++++ sway/commands/mode.c | 2 + sway/config.c | 7 +++ sway/input/input-manager.c | 47 ++++++++++++++ sway/input/seat.c | 18 ++++-- sway/input/switch.c | 85 ++++++++++++++++++++++++++ sway/ipc-json.c | 4 +- sway/meson.build | 1 + sway/sway.5.scd | 28 ++++++++- 14 files changed, 339 insertions(+), 9 deletions(-) create mode 100644 include/sway/input/switch.h create mode 100644 sway/input/switch.c diff --git a/include/sway/commands.h b/include/sway/commands.h index 764821a0e..1c147c5ab 100644 --- a/include/sway/commands.h +++ b/include/sway/commands.h @@ -101,6 +101,7 @@ struct sway_container *container_find_resize_parent(struct sway_container *con, sway_cmd cmd_assign; sway_cmd cmd_bar; sway_cmd cmd_bindcode; +sway_cmd cmd_bindswitch; sway_cmd cmd_bindsym; sway_cmd cmd_border; sway_cmd cmd_client_noop; diff --git a/include/sway/config.h b/include/sway/config.h index 7c5445412..8970696c4 100644 --- a/include/sway/config.h +++ b/include/sway/config.h @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include "../include/config.h" @@ -29,6 +30,7 @@ enum binding_input_type { BINDING_KEYSYM, BINDING_MOUSECODE, BINDING_MOUSESYM, + BINDING_SWITCH }; enum binding_flags { @@ -60,6 +62,16 @@ struct sway_mouse_binding { char *command; }; +/** + * A laptop switch binding and an associated command. + */ +struct sway_switch_binding { + enum wlr_switch_type type; + enum wlr_switch_state state; + uint32_t flags; + char *command; +}; + /** * Focus on window activation. */ @@ -78,6 +90,7 @@ struct sway_mode { list_t *keysym_bindings; list_t *keycode_bindings; list_t *mouse_bindings; + list_t *switch_bindings; bool pango; }; @@ -603,6 +616,8 @@ int sway_binding_cmp_keys(const void *a, const void *b); void free_sway_binding(struct sway_binding *sb); +void free_switch_binding(struct sway_switch_binding *binding); + void seat_execute_command(struct sway_seat *seat, struct sway_binding *binding); void load_swaybar(struct bar_config *bar); diff --git a/include/sway/input/seat.h b/include/sway/input/seat.h index a5361e8c1..f2af1066b 100644 --- a/include/sway/input/seat.h +++ b/include/sway/input/seat.h @@ -27,6 +27,7 @@ struct sway_seat_device { struct sway_seat *sway_seat; struct sway_input_device *input_device; struct sway_keyboard *keyboard; + struct sway_switch *switch_device; struct wl_list link; // sway_seat::devices }; diff --git a/include/sway/input/switch.h b/include/sway/input/switch.h new file mode 100644 index 000000000..19bb1e772 --- /dev/null +++ b/include/sway/input/switch.h @@ -0,0 +1,19 @@ +#ifndef _SWAY_INPUT_SWITCH_H +#define _SWAY_INPUT_SWITCH_H + +#include "sway/input/seat.h" + +struct sway_switch { + struct sway_seat_device *seat_device; + + struct wl_listener switch_toggle; +}; + +struct sway_switch *sway_switch_create(struct sway_seat *seat, + struct sway_seat_device *device); + +void sway_switch_configure(struct sway_switch *sway_switch); + +void sway_switch_destroy(struct sway_switch *sway_switch); + +#endif diff --git a/sway/commands.c b/sway/commands.c index cb32169f6..18b95c738 100644 --- a/sway/commands.c +++ b/sway/commands.c @@ -47,6 +47,7 @@ static struct cmd_handler handlers[] = { { "assign", cmd_assign }, { "bar", cmd_bar }, { "bindcode", cmd_bindcode }, + { "bindswitch", cmd_bindswitch }, { "bindsym", cmd_bindsym }, { "client.background", cmd_client_noop }, { "client.focused", cmd_client_focused }, @@ -386,6 +387,7 @@ struct cmd_results *config_command(char *exec, char **new_block) { && handler->handle != cmd_mode && handler->handle != cmd_bindsym && handler->handle != cmd_bindcode + && handler->handle != cmd_bindswitch && handler->handle != cmd_set && handler->handle != cmd_for_window && (*argv[i] == '\"' || *argv[i] == '\'')) { diff --git a/sway/commands/bind.c b/sway/commands/bind.c index 172e6b8ae..b3d391da6 100644 --- a/sway/commands/bind.c +++ b/sway/commands/bind.c @@ -29,6 +29,28 @@ void free_sway_binding(struct sway_binding *binding) { free(binding); } +void free_switch_binding(struct sway_switch_binding *binding) { + if (!binding) { + return; + } + free(binding->command); + free(binding); +} + +/** + * Returns true if the bindings have the same switch type and state combinations. + */ +static bool binding_switch_compare(struct sway_switch_binding *binding_a, + struct sway_switch_binding *binding_b) { + if (binding_a->type != binding_b->type) { + return false; + } + if (binding_a->state != binding_b->state) { + return false; + } + return true; +} + /** * Returns true if the bindings have the same key and modifier combinations. * Note that keyboard layout is not considered, so the bindings might actually @@ -325,6 +347,102 @@ struct cmd_results *cmd_bindcode(int argc, char **argv) { return cmd_bindsym_or_bindcode(argc, argv, true); } +struct cmd_results *cmd_bindswitch(int argc, char **argv) { + + struct cmd_results *error = NULL; + if ((error = checkarg(argc, "bindswitch", EXPECTED_AT_LEAST, 2))) { + return error; + } + struct sway_switch_binding *binding = calloc(1, sizeof(struct sway_switch_binding)); + if (!binding) { + return cmd_results_new(CMD_FAILURE, "Unable to allocate binding"); + } + + bool warn = true; + + // Handle flags + while (argc > 0) { + if (strcmp("--locked", argv[0]) == 0) { + binding->flags |= BINDING_LOCKED; + } else if (strcmp("--no-warn", argv[0]) == 0) { + warn = false; + } else { + break; + } + argv++; + argc--; + } + + if (argc < 2) { + free(binding); + return cmd_results_new(CMD_FAILURE, + "Invalid bindswitch command (expected at least 2 " + "non-option arguments, got %d)", argc); + } + binding->command = join_args(argv + 1, argc - 1); + + list_t *split = split_string(argv[0], ":"); + if (split->length != 2) { + free_switch_binding(binding); + return cmd_results_new(CMD_FAILURE, + "Invalid bindswitch command (expected two arguments with " + "format : , got %d)", argc); + } + if (strcmp(split->items[0], "tablet") == 0) { + binding->type = WLR_SWITCH_TYPE_TABLET_MODE; + } else if (strcmp(split->items[0], "lid") == 0) { + binding->type = WLR_SWITCH_TYPE_LID; + } else { + free_switch_binding(binding); + return cmd_results_new(CMD_FAILURE, + "Invalid bindswitch command (expected switch binding: " + "unknown switch %s)", split->items[0]); + } + if (strcmp(split->items[1], "on") == 0) { + binding->state = WLR_SWITCH_STATE_ON; + } else if (strcmp(split->items[1], "off") == 0) { + binding->state = WLR_SWITCH_STATE_OFF; + } else if (strcmp(split->items[1], "toggle") == 0) { + binding->state = WLR_SWITCH_STATE_TOGGLE; + } else { + free_switch_binding(binding); + return cmd_results_new(CMD_FAILURE, + "Invalid bindswitch command " + "(expected switch state: unknown state %d)", + split->items[0]); + } + list_free_items_and_destroy(split); + + list_t *mode_bindings = config->current_mode->switch_bindings; + + // overwrite the binding if it already exists + bool overwritten = false; + for (int i = 0; i < mode_bindings->length; ++i) { + struct sway_switch_binding *config_binding = mode_bindings->items[i]; + if (binding_switch_compare(binding, config_binding)) { + sway_log(SWAY_INFO, "Overwriting binding '%s' from `%s` to `%s`", + argv[0], binding->command, config_binding->command); + if (warn) { + config_add_swaynag_warning("Overwriting binding" + "'%s' to `%s` from `%s`", + argv[0], binding->command, + config_binding->command); + } + free_switch_binding(config_binding); + mode_bindings->items[i] = binding; + overwritten = true; + } + } + + if (!overwritten) { + list_add(mode_bindings, binding); + } + + sway_log(SWAY_DEBUG, "bindswitch - Bound %s to command `%s`", + argv[0], binding->command); + return cmd_results_new(CMD_SUCCESS, NULL); +} + /** * Execute the command associated to a binding */ diff --git a/sway/commands/mode.c b/sway/commands/mode.c index d424eb1ce..3e0ee80cb 100644 --- a/sway/commands/mode.c +++ b/sway/commands/mode.c @@ -12,6 +12,7 @@ // Must be in order for the bsearch static struct cmd_handler mode_handlers[] = { { "bindcode", cmd_bindcode }, + { "bindswitch", cmd_bindswitch }, { "bindsym", cmd_bindsym } }; @@ -54,6 +55,7 @@ struct cmd_results *cmd_mode(int argc, char **argv) { mode->keysym_bindings = create_list(); mode->keycode_bindings = create_list(); mode->mouse_bindings = create_list(); + mode->switch_bindings = create_list(); mode->pango = pango; list_add(config->modes, mode); } diff --git a/sway/config.c b/sway/config.c index 48bbd1ea8..7104f55d7 100644 --- a/sway/config.c +++ b/sway/config.c @@ -56,6 +56,12 @@ static void free_mode(struct sway_mode *mode) { } list_free(mode->mouse_bindings); } + if (mode->switch_bindings) { + for (int i = 0; i < mode->switch_bindings->length; i++) { + free_switch_binding(mode->switch_bindings->items[i]); + } + list_free(mode->switch_bindings); + } free(mode); } @@ -195,6 +201,7 @@ static void config_defaults(struct sway_config *config) { if (!(config->current_mode->keysym_bindings = create_list())) goto cleanup; if (!(config->current_mode->keycode_bindings = create_list())) goto cleanup; if (!(config->current_mode->mouse_bindings = create_list())) goto cleanup; + if (!(config->current_mode->switch_bindings = create_list())) goto cleanup; list_add(config->modes, config->current_mode); config->floating_mod = 0; diff --git a/sway/input/input-manager.c b/sway/input/input-manager.c index f99fc395b..adb36af93 100644 --- a/sway/input/input-manager.c +++ b/sway/input/input-manager.c @@ -155,6 +155,47 @@ static void input_manager_libinput_reset_keyboard( libinput_device, send_events)); } +static void input_manager_libinput_config_switch( + struct sway_input_device *input_device) { + struct wlr_input_device *wlr_device = input_device->wlr_device; + struct input_config *ic = input_device_get_config(input_device); + struct libinput_device *libinput_device; + + if (!ic || !wlr_input_device_is_libinput(wlr_device)) { + return; + } + + libinput_device = wlr_libinput_get_device_handle(wlr_device); + sway_log(SWAY_DEBUG, "input_manager_libinput_config_switch(%s)", + ic->identifier); + + if (ic->send_events != INT_MIN) { + sway_log(SWAY_DEBUG, "libinput_config_switch(%s) send_events_set_mode(%d)", + ic->identifier, ic->send_events); + log_libinput_config_status(libinput_device_config_send_events_set_mode( + libinput_device, ic->send_events)); + } +} + +static void input_manager_libinput_reset_switch( + struct sway_input_device *input_device) { + struct wlr_input_device *wlr_device = input_device->wlr_device; + struct libinput_device *libinput_device; + + if (!wlr_input_device_is_libinput(wlr_device)) { + return; + } + + libinput_device = wlr_libinput_get_device_handle(wlr_device); + + uint32_t send_events = + libinput_device_config_send_events_get_default_mode(libinput_device); + sway_log(SWAY_DEBUG, "libinput_reset_switch(%s) send_events_set_mode(%d)", + input_device->identifier, send_events); + log_libinput_config_status(libinput_device_config_send_events_set_mode( + libinput_device, send_events)); +} + static void input_manager_libinput_config_touch( struct sway_input_device *input_device) { struct wlr_input_device *wlr_device = input_device->wlr_device; @@ -471,6 +512,8 @@ static void handle_new_input(struct wl_listener *listener, void *data) { input_manager_libinput_config_pointer(input_device); } else if (input_device->wlr_device->type == WLR_INPUT_DEVICE_KEYBOARD) { input_manager_libinput_config_keyboard(input_device); + } else if (input_device->wlr_device->type == WLR_INPUT_DEVICE_SWITCH) { + input_manager_libinput_config_switch(input_device); } else if (input_device->wlr_device->type == WLR_INPUT_DEVICE_TOUCH) { input_manager_libinput_config_touch(input_device); } @@ -624,6 +667,8 @@ void input_manager_apply_input_config(struct input_config *input_config) { input_manager_libinput_config_pointer(input_device); } else if (input_device->wlr_device->type == WLR_INPUT_DEVICE_KEYBOARD) { input_manager_libinput_config_keyboard(input_device); + } else if (input_device->wlr_device->type == WLR_INPUT_DEVICE_SWITCH) { + input_manager_libinput_config_switch(input_device); } else if (input_device->wlr_device->type == WLR_INPUT_DEVICE_TOUCH) { input_manager_libinput_config_touch(input_device); } @@ -642,6 +687,8 @@ void input_manager_reset_input(struct sway_input_device *input_device) { input_manager_libinput_reset_pointer(input_device); } else if (input_device->wlr_device->type == WLR_INPUT_DEVICE_KEYBOARD) { input_manager_libinput_reset_keyboard(input_device); + } else if (input_device->wlr_device->type == WLR_INPUT_DEVICE_SWITCH) { + input_manager_libinput_reset_switch(input_device); } else if (input_device->wlr_device->type == WLR_INPUT_DEVICE_TOUCH) { input_manager_libinput_reset_touch(input_device); } diff --git a/sway/input/seat.c b/sway/input/seat.c index 7fc87ee4a..d58ff9e6f 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -15,6 +15,7 @@ #include "sway/input/input-manager.h" #include "sway/input/keyboard.h" #include "sway/input/seat.h" +#include "sway/input/switch.h" #include "sway/ipc-server.h" #include "sway/layers.h" #include "sway/output.h" @@ -482,8 +483,8 @@ static void seat_update_capabilities(struct sway_seat *seat) { case WLR_INPUT_DEVICE_TABLET_TOOL: caps |= WL_SEAT_CAPABILITY_POINTER; break; - case WLR_INPUT_DEVICE_TABLET_PAD: case WLR_INPUT_DEVICE_SWITCH: + case WLR_INPUT_DEVICE_TABLET_PAD: break; } } @@ -570,6 +571,15 @@ static void seat_configure_keyboard(struct sway_seat *seat, } } +static void seat_configure_switch(struct sway_seat *seat, + struct sway_seat_device *seat_device) { + if (!seat_device->switch_device) { + sway_switch_create(seat, seat_device); + } + seat_apply_input_config(seat, seat_device); + sway_switch_configure(seat_device->switch_device); +} + static void seat_configure_touch(struct sway_seat *seat, struct sway_seat_device *sway_device) { wlr_cursor_attach_input_device(seat->cursor->cursor, @@ -611,6 +621,9 @@ void seat_configure_device(struct sway_seat *seat, case WLR_INPUT_DEVICE_KEYBOARD: seat_configure_keyboard(seat, seat_device); break; + case WLR_INPUT_DEVICE_SWITCH: + seat_configure_switch(seat, seat_device); + break; case WLR_INPUT_DEVICE_TOUCH: seat_configure_touch(seat, seat_device); break; @@ -620,9 +633,6 @@ void seat_configure_device(struct sway_seat *seat, case WLR_INPUT_DEVICE_TABLET_PAD: sway_log(SWAY_DEBUG, "TODO: configure tablet pad"); break; - case WLR_INPUT_DEVICE_SWITCH: - sway_log(SWAY_DEBUG, "TODO: configure switch device"); - break; } } diff --git a/sway/input/switch.c b/sway/input/switch.c new file mode 100644 index 000000000..d56e65254 --- /dev/null +++ b/sway/input/switch.c @@ -0,0 +1,85 @@ +#include "sway/config.h" +#include "sway/desktop/transaction.h" +#include "sway/input/switch.h" +#include +#include "log.h" + +struct sway_switch *sway_switch_create(struct sway_seat *seat, + struct sway_seat_device *device) { + struct sway_switch *switch_device = + calloc(1, sizeof(struct sway_switch)); + if (!sway_assert(switch_device, "could not allocate switch")) { + return NULL; + } + device->switch_device = switch_device; + switch_device->seat_device = device; + wl_list_init(&switch_device->switch_toggle.link); + sway_log(SWAY_DEBUG, "Allocated switch for device"); + + return switch_device; +} + +static void handle_switch_toggle(struct wl_listener *listener, void *data) { + struct sway_switch *sway_switch = + wl_container_of(listener, sway_switch, switch_toggle); + struct sway_seat* seat = sway_switch->seat_device->sway_seat; + struct wlr_seat *wlr_seat = seat->wlr_seat; + struct wlr_input_device *wlr_device = + sway_switch->seat_device->input_device->wlr_device; + + wlr_idle_notify_activity(server.idle, wlr_seat); + bool input_inhibited = seat->exclusive_client != NULL; + + char *device_identifier = input_device_get_identifier(wlr_device); + + struct wlr_event_switch_toggle *event = data; + enum wlr_switch_type type = event->switch_type; + enum wlr_switch_state state = event->switch_state; + sway_log(SWAY_DEBUG, "%s: type %d state %d", device_identifier, type, state); + + list_t *bindings = config->current_mode->switch_bindings; + for (int i = 0; i < bindings->length; ++i) { + struct sway_switch_binding *binding = bindings->items[i]; + if (binding->type != type) { + continue; + } + if (binding->state != WLR_SWITCH_STATE_TOGGLE && + binding->state != state) { + continue; + } + bool binding_locked = binding->flags & BINDING_LOCKED; + if (!binding_locked && input_inhibited) { + continue; + } + + struct sway_binding *dummy_binding = calloc(1, sizeof(struct sway_binding)); + dummy_binding->type = BINDING_SWITCH; + dummy_binding->flags = binding->flags; + dummy_binding->command = binding->command; + + seat_execute_command(seat, dummy_binding); + free(dummy_binding); + } + + transaction_commit_dirty(); + + free(device_identifier); +} + +void sway_switch_configure(struct sway_switch *sway_switch) { + struct wlr_input_device *wlr_device = + sway_switch->seat_device->input_device->wlr_device; + wl_list_remove(&sway_switch->switch_toggle.link); + wl_signal_add(&wlr_device->switch_device->events.toggle, + &sway_switch->switch_toggle); + sway_switch->switch_toggle.notify = handle_switch_toggle; + sway_log(SWAY_DEBUG, "Configured switch for device"); +} + +void sway_switch_destroy(struct sway_switch *sway_switch) { + if (!sway_switch) { + return; + } + wl_list_remove(&sway_switch->switch_toggle.link); + free(sway_switch); +} diff --git a/sway/ipc-json.c b/sway/ipc-json.c index f61e1a8c8..c320d9586 100644 --- a/sway/ipc-json.c +++ b/sway/ipc-json.c @@ -91,14 +91,14 @@ static const char *ipc_json_device_type_description(struct sway_input_device *de return "pointer"; case WLR_INPUT_DEVICE_KEYBOARD: return "keyboard"; + case WLR_INPUT_DEVICE_SWITCH: + return "switch"; case WLR_INPUT_DEVICE_TOUCH: return "touch"; case WLR_INPUT_DEVICE_TABLET_TOOL: return "tablet_tool"; case WLR_INPUT_DEVICE_TABLET_PAD: return "tablet_pad"; - case WLR_INPUT_DEVICE_SWITCH: - return "switch"; } return "unknown"; } diff --git a/sway/meson.build b/sway/meson.build index 1138bc73f..66876bdcd 100644 --- a/sway/meson.build +++ b/sway/meson.build @@ -30,6 +30,7 @@ sway_sources = files( 'input/seatop_resize_tiling.c', 'input/cursor.c', 'input/keyboard.c', + 'input/switch.c', 'config/bar.c', 'config/output.c', diff --git a/sway/sway.5.scd b/sway/sway.5.scd index 989717cb1..18fc28a30 100644 --- a/sway/sway.5.scd +++ b/sway/sway.5.scd @@ -341,6 +341,28 @@ runtime. *bindcode* [--whole-window] [--border] [--exclude-titlebar] [--release] [--locked] [--input-device=] [--no-warn] is also available for binding with key/button codes instead of key/button names. +*bindswitch* [--locked] [--no-warn] : + Binds to execute the sway command _command_ on state changes. + Supported switches are _lid_ (laptop lid) and _tablet_ (tablet mode) + switches. Valid values for _state_ are _on_, _off_ and _toggle. These + switches are on when the device lid is shut and when tablet mode is active + respectively. _toggle_ is also supported to run a command both when the + switch is toggled on or off. + + Unless the flag _--locked_ is set, the command will not be run + when a screen locking program is active. By default, if you + overwrite a binding, swaynag will give you a warning. To silence this, use + the _--no-warn_ flag. + + Example: +``` + # Show the virtual keyboard when tablet mode is entered. + bindswitch tablet:on busctl call --user sm.puri.OSK0 /sm/puri/OSK0 sm.puri.OSK0 SetVisible b true + + # Log a message when the laptop lid is opened or closed. + bindswitch lid:toggle exec echo "Lid moved" +``` + *client.* Configures the color of window borders and title bars. All 5 colors are required, with the exception of *client.background*, which requires exactly @@ -551,9 +573,9 @@ The default colors are: Switches to the specified mode. The default mode _default_. *mode* [--pango_markup] - The only two valid _mode-subcommands..._ are *bindsym* and *bindcode*. - If _--pango_markup_ is given, then _mode_ will be interpreted as pango - markup. + The only valid _mode-subcommands..._ are *bindsym*, *bindcode* and + *bindswitch*. If _--pango_markup_ is given, then _mode_ will be interpreted + as pango markup. *mouse_warping* output|container|none If _output_ is specified, the mouse will be moved to new outputs as you From cdcc2a5bb52a34865eee2ec3d09a38b047c08ffd Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Wed, 20 Mar 2019 22:16:53 +1000 Subject: [PATCH 083/191] Support focus for floating containers This kind of worked before in that focus would change, but it wasn't intentionally supported and had side effects such as not raising the container, and being unable to cycle through all floaters depending on the direction used. This commit makes it properly supported. The new focus is chosen based on the distance to the center point of each floating container in the workspace, and the container is raised. In a multi output setup, if both visible workspaces have floating containers, focus will NOT cross into the other output. It is assumed the user will use a workspace binding in this case. If two floating containers occupy the exact same center point and you try to focus in a direction, the behaviour is undefined. --- sway/commands/focus.c | 49 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 45 insertions(+), 4 deletions(-) diff --git a/sway/commands/focus.c b/sway/commands/focus.c index 6344a7658..33d014050 100644 --- a/sway/commands/focus.c +++ b/sway/commands/focus.c @@ -1,3 +1,4 @@ +#include #include #include #include "log.h" @@ -90,8 +91,9 @@ static struct sway_node *get_node_in_output_direction( return &ws->node; } -static struct sway_node *node_get_in_direction(struct sway_container *container, - struct sway_seat *seat, enum wlr_direction dir) { +static struct sway_node *node_get_in_direction_tiling( + struct sway_container *container, struct sway_seat *seat, + enum wlr_direction dir) { struct sway_container *wrap_candidate = NULL; struct sway_container *current = container; while (current) { @@ -172,6 +174,37 @@ static struct sway_node *node_get_in_direction(struct sway_container *container, return NULL; } +static struct sway_node *node_get_in_direction_floating( + struct sway_container *con, struct sway_seat *seat, + enum wlr_direction dir) { + double ref_lx = con->x + con->width / 2; + double ref_ly = con->y + con->height / 2; + double closest_distance = DBL_MAX; + struct sway_container *closest_con = NULL; + + for (int i = 0; i < con->workspace->floating->length; i++) { + struct sway_container *floater = con->workspace->floating->items[i]; + if (floater == con) { + continue; + } + float distance = dir == WLR_DIRECTION_LEFT || dir == WLR_DIRECTION_RIGHT + ? (floater->x + floater->width / 2) - ref_lx + : (floater->y + floater->height / 2) - ref_ly; + if (dir == WLR_DIRECTION_LEFT || dir == WLR_DIRECTION_UP) { + distance = -distance; + } + if (distance < 0) { + continue; + } + if (distance < closest_distance) { + closest_distance = distance; + closest_con = floater; + } + } + + return closest_con ? &closest_con->node : NULL; +} + static struct cmd_results *focus_mode(struct sway_workspace *ws, struct sway_seat *seat, bool floating) { struct sway_container *new_focus = NULL; @@ -330,11 +363,19 @@ struct cmd_results *cmd_focus(int argc, char **argv) { return cmd_results_new(CMD_SUCCESS, NULL); } - struct sway_node *next_focus = - node_get_in_direction(container, seat, direction); + struct sway_node *next_focus = NULL; + if (container_is_floating(container)) { + next_focus = node_get_in_direction_floating(container, seat, direction); + } else { + next_focus = node_get_in_direction_tiling(container, seat, direction); + } if (next_focus) { seat_set_focus(seat, next_focus); seat_consider_warp_to_focus(seat); + + if (next_focus->type == N_CONTAINER) { + container_raise_floating(next_focus->sway_container); + } } return cmd_results_new(CMD_SUCCESS, NULL); From 8cd7f0171ad8ffc4ce490f6f4b18522cb178c951 Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Tue, 19 Mar 2019 23:07:32 -0400 Subject: [PATCH 084/191] cmd_mode: allow cmd_set to be a subcommand This allows set to be used in mode blocks --- sway/commands/mode.c | 3 ++- sway/sway.5.scd | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/sway/commands/mode.c b/sway/commands/mode.c index 3e0ee80cb..ef2c5d794 100644 --- a/sway/commands/mode.c +++ b/sway/commands/mode.c @@ -13,7 +13,8 @@ static struct cmd_handler mode_handlers[] = { { "bindcode", cmd_bindcode }, { "bindswitch", cmd_bindswitch }, - { "bindsym", cmd_bindsym } + { "bindsym", cmd_bindsym }, + { "set", cmd_set }, }; struct cmd_results *cmd_mode(int argc, char **argv) { diff --git a/sway/sway.5.scd b/sway/sway.5.scd index 18fc28a30..dbfeefe3c 100644 --- a/sway/sway.5.scd +++ b/sway/sway.5.scd @@ -573,9 +573,9 @@ The default colors are: Switches to the specified mode. The default mode _default_. *mode* [--pango_markup] - The only valid _mode-subcommands..._ are *bindsym*, *bindcode* and - *bindswitch*. If _--pango_markup_ is given, then _mode_ will be interpreted - as pango markup. + The only valid _mode-subcommands..._ are *bindsym*, *bindcode*, + *bindswitch*, and *set*. If _--pango_markup_ is given, then _mode_ will be + interpreted as pango markup. *mouse_warping* output|container|none If _output_ is specified, the mouse will be moved to new outputs as you From 7d2076cbff2b363061d52362927d0da2f3c4865b Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Sat, 23 Mar 2019 03:49:29 -0400 Subject: [PATCH 085/191] criteria: fix __focused__ when no focus or unset This fixes the behavior of `__focused__` when there is no focused view to match i3's behavior of successfully matching no views instead of returning an error of a missing value. It also applies the same logic when a token is not applicable (or unset) for a view such as `app_id` for a focused xwayland view or `class` for a focused xdg-shell view. This adds an `autofail` boolean to `struct criteria`. If it is set to `true`, then `criteria_matches_view` will immediately bail out as a no match. If `autofail` is set, the criteria will also not be considered empty by `criteria_is_empty`. To set this new `autofail` property, `get_focused_prop` will now take in a boolean pointer of the same name. If `__focused__` is supported for the token and there is no focused view or the focused view does not have a value for the token, then the boolean will be set to true. In `parse_token`, the boolean value will be checked and if set to true, then `criteria->autofail` will be set to true and `parse_token` will bail successfully. Tokens will still be parsed to make sure the whole criteria is syntactically valid, which is also why `&criteria->autofail` is not passed to `get_focused_prop` and a local boolean is declared in `parse_token`. --- include/sway/criteria.h | 1 + sway/criteria.c | 69 ++++++++++++++++++++++++++++------------- 2 files changed, 49 insertions(+), 21 deletions(-) diff --git a/include/sway/criteria.h b/include/sway/criteria.h index f7e788c8b..8a1d9e5ef 100644 --- a/include/sway/criteria.h +++ b/include/sway/criteria.h @@ -20,6 +20,7 @@ struct criteria { char *cmdlist; char *target; // workspace or output name for `assign` criteria + bool autofail; // __focused__ while no focus or n/a for focused view pcre *title; pcre *shell; pcre *app_id; diff --git a/sway/criteria.c b/sway/criteria.c index f2db6c184..11b41f356 100644 --- a/sway/criteria.c +++ b/sway/criteria.c @@ -16,7 +16,8 @@ #include "config.h" bool criteria_is_empty(struct criteria *criteria) { - return !criteria->title + return !criteria->autofail + && !criteria->title && !criteria->shell && !criteria->app_id && !criteria->con_mark @@ -98,6 +99,10 @@ static void find_urgent_iterator(struct sway_container *con, void *data) { static bool criteria_matches_view(struct criteria *criteria, struct sway_view *view) { + if (criteria->autofail) { + return false; + } + if (criteria->title) { const char *title = view_get_title(view); if (!title || regex_cmp(title, criteria->title) != 0) { @@ -366,50 +371,66 @@ static enum criteria_token token_from_name(char *name) { * using criteria via IPC. Using __focused__ in config is not useful because * criteria is only executed once per view. */ -static char *get_focused_prop(enum criteria_token token) { +static char *get_focused_prop(enum criteria_token token, bool *autofail) { struct sway_seat *seat = input_manager_current_seat(); struct sway_container *focus = seat_get_focused_container(seat); - if (!focus || !focus->view) { - return NULL; - } - struct sway_view *view = focus->view; + struct sway_view *view = focus ? focus->view : NULL; const char *value = NULL; switch (token) { case T_APP_ID: - value = view_get_app_id(view); + *autofail = true; + if (view) { + value = view_get_app_id(view); + } break; case T_SHELL: - value = view_get_shell(view); + *autofail = true; + if (view) { + value = view_get_shell(view); + } break; case T_TITLE: - value = view_get_title(view); + *autofail = true; + if (view) { + value = view_get_title(view); + } break; case T_WORKSPACE: - if (focus->workspace) { + *autofail = true; + if (focus && focus->workspace) { value = focus->workspace->name; } break; case T_CON_ID: - if (view->container == NULL) { - return NULL; + *autofail = true; + if (view && view->container) { + size_t id = view->container->node.id; + size_t id_size = snprintf(NULL, 0, "%zu", id) + 1; + char *id_str = malloc(id_size); + snprintf(id_str, id_size, "%zu", id); + value = id_str; } - size_t id = view->container->node.id; - size_t id_size = snprintf(NULL, 0, "%zu", id) + 1; - char *id_str = malloc(id_size); - snprintf(id_str, id_size, "%zu", id); - value = id_str; break; #if HAVE_XWAYLAND case T_CLASS: - value = view_get_class(view); + *autofail = true; + if (view) { + value = view_get_class(view); + } break; case T_INSTANCE: - value = view_get_instance(view); + *autofail = true; + if (view) { + value = view_get_instance(view); + } break; case T_WINDOW_ROLE: - value = view_get_window_role(view); + *autofail = true; + if (view) { + value = view_get_window_role(view); + } break; case T_WINDOW_TYPE: // These do not support __focused__ case T_ID: @@ -419,6 +440,7 @@ static char *get_focused_prop(enum criteria_token token) { case T_TILING: case T_URGENT: case T_INVALID: + *autofail = false; break; } if (value) { @@ -439,7 +461,12 @@ static bool parse_token(struct criteria *criteria, char *name, char *value) { char *effective_value = NULL; if (value && strcmp(value, "__focused__") == 0) { - effective_value = get_focused_prop(token); + bool autofail = false; + effective_value = get_focused_prop(token, &autofail); + if (!effective_value && autofail) { + criteria->autofail = true; + return true; + } } else if (value) { effective_value = strdup(value); } From cd8b4ace926bd5a5289a5d5d6f6dddc1e4732287 Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Sat, 23 Mar 2019 17:24:53 -0400 Subject: [PATCH 086/191] fix opening a floating view on the NOOP output Since the NOOP output has no size, the minimum floating size is greater than the workspace size for the NOOP output. In this case, the floater gets centered in the output instead of the workspace. However, the NOOP output is not part of the output layout and thus has a NULL box. Attempting to access the properties of this box was causing a segfault. This fixes the issue by just setting the floater's box to all zeroes when mapping on the NOOP output. When the workspace gets moved from the NOOP output to a new output, any floater whose width or height is zero or has an x or y location outside of the output, gets passed to `container_init_floating` again. This will then set the appropriate size and centering. For any floater that has a valid size and location, they are preserved. --- sway/tree/container.c | 15 +++++++++++---- sway/tree/output.c | 21 +++++++++++++++++++++ 2 files changed, 32 insertions(+), 4 deletions(-) diff --git a/sway/tree/container.c b/sway/tree/container.c index 93cff7ff5..f84ce3602 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -651,6 +651,17 @@ void floating_calculate_constraints(int *min_width, int *max_width, void container_init_floating(struct sway_container *con) { struct sway_workspace *ws = con->workspace; + struct wlr_box *ob = wlr_output_layout_get_box(root->output_layout, + ws->output->wlr_output); + if (!ob) { + // On NOOP output. Will be called again when moved to an output + con->x = 0; + con->y = 0; + con->width = 0; + con->height = 0; + return; + } + int min_width, max_width, min_height, max_height; floating_calculate_constraints(&min_width, &max_width, &min_height, &max_height); @@ -659,8 +670,6 @@ void container_init_floating(struct sway_container *con) { con->width = max_width; con->height = max_height; if (con->width > ws->width || con->height > ws->height) { - struct wlr_box *ob = wlr_output_layout_get_box(root->output_layout, - ws->output->wlr_output); con->x = ob->x + (ob->width - con->width) / 2; con->y = ob->y + (ob->height - con->height) / 2; } else { @@ -675,8 +684,6 @@ void container_init_floating(struct sway_container *con) { fmax(min_height, fmin(view->natural_height, max_height)); if (con->content_width > ws->width || con->content_height > ws->height) { - struct wlr_box *ob = wlr_output_layout_get_box(root->output_layout, - ws->output->wlr_output); con->content_x = ob->x + (ob->width - con->content_width) / 2; con->content_y = ob->y + (ob->height - con->content_height) / 2; } else { diff --git a/sway/tree/output.c b/sway/tree/output.c index 1202ba3c5..7867c6bc6 100644 --- a/sway/tree/output.c +++ b/sway/tree/output.c @@ -60,6 +60,27 @@ static void restore_workspaces(struct sway_output *output) { struct sway_workspace *ws = root->noop_output->workspaces->items[0]; workspace_detach(ws); output_add_workspace(output, ws); + + // If the floater was made floating while on the NOOP output, its width + // and height will be zero and it should be reinitialized as a floating + // container to get the appropriate size and location. Additionally, if + // the floater is wider or taller than the output or is completely + // outside of the output's bounds, do the same as the output layout has + // likely changed and the maximum size needs to be checked and the + // floater re-centered + for (int i = 0; i < ws->floating->length; i++) { + struct sway_container *floater = ws->floating->items[i]; + if (floater->width == 0 || floater->height == 0 || + floater->width > output->width || + floater->height > output->height || + floater->x > output->lx + output->width || + floater->y > output->ly + output->height || + floater->x + floater->width < output->lx || + floater->y + floater->height < output->ly) { + container_init_floating(floater); + } + } + ipc_event_workspace(NULL, ws, "move"); } From 200833caaea36dd65324e5460520731f5c98ff8a Mon Sep 17 00:00:00 2001 From: mliszcz Date: Sat, 23 Mar 2019 11:32:44 +0100 Subject: [PATCH 087/191] Allow for workspace renaming during exec handling This change adds support for renaming a workspace when `exec` command is being processed by keeping sway_workspace and pid_workspace names in sync. The change can be verified by running following command: swaymsg exec ; swaymsg rename workspace number 1 to 5 Fixes: #3952 --- include/sway/tree/root.h | 2 ++ sway/commands/rename.c | 4 ++++ sway/tree/root.c | 14 ++++++++++++++ 3 files changed, 20 insertions(+) diff --git a/include/sway/tree/root.h b/include/sway/tree/root.h index 9f6cd3bb6..c4f84207d 100644 --- a/include/sway/tree/root.h +++ b/include/sway/tree/root.h @@ -85,4 +85,6 @@ struct sway_container *root_find_container( void root_get_box(struct sway_root *root, struct wlr_box *box); +void root_rename_pid_workspaces(const char *old_name, const char *new_name); + #endif diff --git a/sway/commands/rename.c b/sway/commands/rename.c index 88377b092..3b855fdf7 100644 --- a/sway/commands/rename.c +++ b/sway/commands/rename.c @@ -9,6 +9,7 @@ #include "sway/output.h" #include "sway/tree/container.h" #include "sway/tree/workspace.h" +#include "sway/tree/root.h" static const char expected_syntax[] = "Expected 'rename workspace to ' or " @@ -89,6 +90,9 @@ struct cmd_results *cmd_rename(int argc, char **argv) { } sway_log(SWAY_DEBUG, "renaming workspace '%s' to '%s'", workspace->name, new_name); + + root_rename_pid_workspaces(workspace->name, new_name); + free(workspace->name); workspace->name = new_name; diff --git a/sway/tree/root.c b/sway/tree/root.c index 0744192b2..5dde9f22d 100644 --- a/sway/tree/root.c +++ b/sway/tree/root.c @@ -390,3 +390,17 @@ void root_get_box(struct sway_root *root, struct wlr_box *box) { box->width = root->width; box->height = root->height; } + +void root_rename_pid_workspaces(const char *old_name, const char *new_name) { + if (!pid_workspaces.prev && !pid_workspaces.next) { + wl_list_init(&pid_workspaces); + } + + struct pid_workspace *pw = NULL; + wl_list_for_each(pw, &pid_workspaces, link) { + if (strcmp(pw->workspace, old_name) == 0) { + free(pw->workspace); + pw->workspace = strdup(new_name); + } + } +} From 6e3046878d4dced3f2e503973ad31d7921c0c400 Mon Sep 17 00:00:00 2001 From: Geoff Greer Date: Sun, 10 Feb 2019 16:56:57 -0800 Subject: [PATCH 088/191] Add support for manually setting subpixel hinting on outputs. Many laptop screens report unknown subpixel order. Allow users to manually set subpixel hinting to work around this. Addresses https://github.com/swaywm/sway/issues/3163 --- common/util.c | 21 +++++++++++++++++++ include/sway/commands.h | 1 + include/sway/config.h | 1 + include/sway/output.h | 1 + include/util.h | 3 +++ sway/commands/output.c | 1 + sway/commands/output/subpixel.c | 36 +++++++++++++++++++++++++++++++++ sway/config/output.c | 23 +++++++++++++++++---- sway/ipc-server.c | 2 ++ sway/meson.build | 1 + sway/sway-ipc.7.scd | 4 ++++ sway/sway-output.5.scd | 7 +++++++ sway/tree/output.c | 1 + swaymsg/main.c | 5 ++++- 14 files changed, 102 insertions(+), 5 deletions(-) create mode 100644 sway/commands/output/subpixel.c diff --git a/common/util.c b/common/util.c index edbbf3f77..c43c5ddf6 100644 --- a/common/util.c +++ b/common/util.c @@ -4,6 +4,7 @@ #include #include #include +#include #include "log.h" #include "util.h" @@ -54,3 +55,23 @@ float parse_float(const char *value) { } return flt; } + + +const char *sway_wl_output_subpixel_to_string(enum wl_output_subpixel subpixel) { + switch (subpixel) { + case WL_OUTPUT_SUBPIXEL_UNKNOWN: + return "unknown"; + case WL_OUTPUT_SUBPIXEL_NONE: + return "none"; + case WL_OUTPUT_SUBPIXEL_HORIZONTAL_RGB: + return "rgb"; + case WL_OUTPUT_SUBPIXEL_HORIZONTAL_BGR: + return "bgr"; + case WL_OUTPUT_SUBPIXEL_VERTICAL_RGB: + return "vrgb"; + case WL_OUTPUT_SUBPIXEL_VERTICAL_BGR: + return "vbgr"; + } + sway_assert(false, "Unknown value for wl_output_subpixel."); + return NULL; +} diff --git a/include/sway/commands.h b/include/sway/commands.h index 1c147c5ab..7533a14d3 100644 --- a/include/sway/commands.h +++ b/include/sway/commands.h @@ -261,6 +261,7 @@ sway_cmd output_cmd_enable; sway_cmd output_cmd_mode; sway_cmd output_cmd_position; sway_cmd output_cmd_scale; +sway_cmd output_cmd_subpixel; sway_cmd output_cmd_transform; sway_cmd seat_cmd_attach; diff --git a/include/sway/config.h b/include/sway/config.h index 8970696c4..d49120a06 100644 --- a/include/sway/config.h +++ b/include/sway/config.h @@ -184,6 +184,7 @@ struct output_config { int x, y; float scale; int32_t transform; + enum wl_output_subpixel subpixel; char *background; char *background_option; diff --git a/include/sway/output.h b/include/sway/output.h index 8015f2110..c336c5592 100644 --- a/include/sway/output.h +++ b/include/sway/output.h @@ -31,6 +31,7 @@ struct sway_output { int lx, ly; // layout coords int width, height; // transformed buffer size + enum wl_output_subpixel detected_subpixel; bool enabled, configured; list_t *workspaces; diff --git a/include/util.h b/include/util.h index 1fd772c0c..6a668fd65 100644 --- a/include/util.h +++ b/include/util.h @@ -3,6 +3,7 @@ #include #include +#include /** * Wrap i into the range [0, max[ @@ -29,4 +30,6 @@ bool parse_boolean(const char *boolean, bool current); */ float parse_float(const char *value); +const char *sway_wl_output_subpixel_to_string(enum wl_output_subpixel subpixel); + #endif diff --git a/sway/commands/output.c b/sway/commands/output.c index 40dbf3ca4..44e28512f 100644 --- a/sway/commands/output.c +++ b/sway/commands/output.c @@ -18,6 +18,7 @@ static struct cmd_handler output_handlers[] = { { "res", output_cmd_mode }, { "resolution", output_cmd_mode }, { "scale", output_cmd_scale }, + { "subpixel", output_cmd_subpixel }, { "transform", output_cmd_transform }, }; diff --git a/sway/commands/output/subpixel.c b/sway/commands/output/subpixel.c new file mode 100644 index 000000000..63191ee62 --- /dev/null +++ b/sway/commands/output/subpixel.c @@ -0,0 +1,36 @@ +#include +#include "log.h" +#include "sway/commands.h" +#include "sway/config.h" +#include "sway/output.h" + +struct cmd_results *output_cmd_subpixel(int argc, char **argv) { + if (!config->handler_context.output_config) { + return cmd_results_new(CMD_FAILURE, "Missing output config"); + } + if (!argc) { + return cmd_results_new(CMD_INVALID, "Missing subpixel argument."); + } + enum wl_output_subpixel subpixel; + + if (strcmp(*argv, "rgb") == 0) { + subpixel = WL_OUTPUT_SUBPIXEL_HORIZONTAL_RGB; + } else if (strcmp(*argv, "bgr") == 0) { + subpixel = WL_OUTPUT_SUBPIXEL_HORIZONTAL_BGR; + } else if (strcmp(*argv, "vrgb") == 0) { + subpixel = WL_OUTPUT_SUBPIXEL_VERTICAL_RGB; + } else if (strcmp(*argv, "vbgr") == 0) { + subpixel = WL_OUTPUT_SUBPIXEL_VERTICAL_BGR; + } else if (strcmp(*argv, "none") == 0) { + subpixel = WL_OUTPUT_SUBPIXEL_NONE; + } else { + return cmd_results_new(CMD_INVALID, "Invalid output subpixel."); + } + + struct output_config *oc = config->handler_context.output_config; + config->handler_context.leftovers.argc = argc - 1; + config->handler_context.leftovers.argv = argv + 1; + + oc->subpixel = subpixel; + return NULL; +} diff --git a/sway/config/output.c b/sway/config/output.c index 44aae03aa..d06051b34 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -8,10 +8,11 @@ #include #include #include -#include "log.h" #include "sway/config.h" #include "sway/output.h" #include "sway/tree/root.h" +#include "log.h" +#include "util.h" int output_name_cmp(const void *item, const void *data) { const struct output_config *output = item; @@ -43,6 +44,7 @@ struct output_config *new_output_config(const char *name) { oc->x = oc->y = -1; oc->scale = -1; oc->transform = -1; + oc->subpixel = WL_OUTPUT_SUBPIXEL_UNKNOWN; return oc; } @@ -65,6 +67,9 @@ void merge_output_config(struct output_config *dst, struct output_config *src) { if (src->scale != -1) { dst->scale = src->scale; } + if (src->subpixel != WL_OUTPUT_SUBPIXEL_UNKNOWN) { + dst->subpixel = src->subpixel; + } if (src->refresh_rate != -1) { dst->refresh_rate = src->refresh_rate; } @@ -187,10 +192,10 @@ struct output_config *store_output_config(struct output_config *oc) { } sway_log(SWAY_DEBUG, "Config stored for output %s (enabled: %d) (%dx%d@%fHz " - "position %d,%d scale %f transform %d) (bg %s %s) (dpms %d)", + "position %d,%d scale %f subpixel %s 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); + oc->x, oc->y, oc->scale, sway_wl_output_subpixel_to_string(oc->subpixel), + oc->transform, oc->background, oc->background_option, oc->dpms_state); return oc; } @@ -363,6 +368,14 @@ bool apply_output_config(struct output_config *oc, struct sway_output *output) { sway_log(SWAY_DEBUG, "Set %s scale to %f", oc->name, oc->scale); wlr_output_set_scale(wlr_output, oc->scale); } + + if (oc && (oc->subpixel != WL_OUTPUT_SUBPIXEL_UNKNOWN || config->reloading)) { + sway_log(SWAY_DEBUG, "Set %s subpixel to %s", oc->name, + sway_wl_output_subpixel_to_string(oc->subpixel)); + wlr_output_set_subpixel(wlr_output, oc->subpixel); + output_damage_whole(output); + } + if (oc && oc->transform >= 0) { sway_log(SWAY_DEBUG, "Set %s transform to %d", oc->name, oc->transform); wlr_output_set_transform(wlr_output, oc->transform); @@ -424,6 +437,8 @@ static void default_output_config(struct output_config *oc, } oc->x = oc->y = -1; oc->scale = 1; + struct sway_output *output = wlr_output->data; + oc->subpixel = output->detected_subpixel; oc->transform = WL_OUTPUT_TRANSFORM_NORMAL; oc->dpms_state = DPMS_ON; } diff --git a/sway/ipc-server.c b/sway/ipc-server.c index df57cba51..e133a5bf1 100644 --- a/sway/ipc-server.c +++ b/sway/ipc-server.c @@ -644,6 +644,8 @@ void ipc_client_handle_command(struct ipc_client *client) { json_object_object_add(output_json, "focused", json_object_new_boolean(focused)); + const char *subpixel = sway_wl_output_subpixel_to_string(output->wlr_output->subpixel); + json_object_object_add(output_json, "subpixel_hinting", json_object_new_string(subpixel)); json_object_array_add(outputs, output_json); } struct sway_output *output; diff --git a/sway/meson.build b/sway/meson.build index 66876bdcd..9f79fb6ce 100644 --- a/sway/meson.build +++ b/sway/meson.build @@ -169,6 +169,7 @@ sway_sources = files( 'commands/output/mode.c', 'commands/output/position.c', 'commands/output/scale.c', + 'commands/output/subpixel.c', 'commands/output/transform.c', 'tree/arrange.c', diff --git a/sway/sway-ipc.7.scd b/sway/sway-ipc.7.scd index b43b30302..3d22008e4 100644 --- a/sway/sway-ipc.7.scd +++ b/sway/sway-ipc.7.scd @@ -211,6 +211,9 @@ following properties: |- scale : float : The scale currently in use on the output or _-1_ for disabled outputs +|- subpixel_hinting +: string +: The subpixel hinting current in use on the output. This can be _rgb_, _bgr_, _vrgb_, _vbgr_, or _none_ |- transform : string : The transform currently in use for the output. This can be _normal_, _90_, @@ -242,6 +245,7 @@ following properties: "active": true, "primary": false, "scale": 1.0, + "subpixel_hinting": "rgb", "transform": "normal", "current_workspace": "1", "modes": [ diff --git a/sway/sway-output.5.scd b/sway/sway-output.5.scd index bb3745f36..1efe2f7bf 100644 --- a/sway/sway-output.5.scd +++ b/sway/sway-output.5.scd @@ -64,6 +64,13 @@ must be separated by one space. For example: applications to taste. HiDPI isn't supported with Xwayland clients (windows will blur). +*output* subpixel rgb|bgr|vrgb|vbgr|none + Manually sets the subpixel hinting for the specified output. This value is + usually auto-detected, but some displays may misreport their subpixel + geometry. Using the correct subpixel hinting allows for sharper text. + Incorrect values will result in blurrier text. When changing this via + *swaymsg*, some applications may need to be restarted to use the new value. + *output* background|bg [] Sets the wallpaper for the given output to the specified file, using the given scaling mode (one of "stretch", "fill", "fit", "center", "tile"). If diff --git a/sway/tree/output.c b/sway/tree/output.c index 7867c6bc6..283036524 100644 --- a/sway/tree/output.c +++ b/sway/tree/output.c @@ -92,6 +92,7 @@ struct sway_output *output_create(struct wlr_output *wlr_output) { node_init(&output->node, N_OUTPUT, output); output->wlr_output = wlr_output; wlr_output->data = output; + output->detected_subpixel = wlr_output->subpixel; wl_signal_init(&output->events.destroy); diff --git a/swaymsg/main.c b/swaymsg/main.c index 65cc4bbb1..f86000a42 100644 --- a/swaymsg/main.c +++ b/swaymsg/main.c @@ -186,11 +186,12 @@ static void pretty_print_output(json_object *o) { json_object_object_get_ex(o, "focused", &focused); json_object_object_get_ex(o, "active", &active); json_object_object_get_ex(o, "current_workspace", &ws); - json_object *make, *model, *serial, *scale, *transform; + json_object *make, *model, *serial, *scale, *subpixel, *transform; json_object_object_get_ex(o, "make", &make); json_object_object_get_ex(o, "model", &model); json_object_object_get_ex(o, "serial", &serial); json_object_object_get_ex(o, "scale", &scale); + json_object_object_get_ex(o, "subpixel_hinting", &subpixel); json_object_object_get_ex(o, "transform", &transform); json_object *x, *y; json_object_object_get_ex(rect, "x", &x); @@ -209,6 +210,7 @@ static void pretty_print_output(json_object *o) { " Current mode: %dx%d @ %f Hz\n" " Position: %d,%d\n" " Scale factor: %f\n" + " Subpixel hinting: %s\n" " Transform: %s\n" " Workspace: %s\n", json_object_get_string(name), @@ -221,6 +223,7 @@ static void pretty_print_output(json_object *o) { (float)json_object_get_int(refresh) / 1000, json_object_get_int(x), json_object_get_int(y), json_object_get_double(scale), + json_object_get_string(subpixel), json_object_get_string(transform), json_object_get_string(ws) ); From c3d7036867b958b08f6506737ffafe9052c17194 Mon Sep 17 00:00:00 2001 From: Philz69 Date: Sun, 24 Mar 2019 15:19:13 -0400 Subject: [PATCH 089/191] Updated the french readme (#3964) * Updated the french readme Removed the image at the start of the readme. Removed the mention of bounties. Updated the dependencies. Removed a few lines that were not present in the english readme anymore. * Fix errors. Fixed capitalisation. Changed "root" to italics. --- README.fr.md | 36 ++++++++++++++---------------------- 1 file changed, 14 insertions(+), 22 deletions(-) diff --git a/README.fr.md b/README.fr.md index ad627ba54..97c09667b 100644 --- a/README.fr.md +++ b/README.fr.md @@ -6,14 +6,8 @@ avec i3, **en cours de développement**. Lisez la IRC](http://webchat.freenode.net/?channels=sway&uio=d4) (#sway sur irc.freenode.net). -[![](https://sr.ht/ICd5.png)](https://sr.ht/ICd5.png) - -Si vous souhaitez soutenir le développement de Sway, vous pouvez contribuer à [ma page -Patreon](https://patreon.com/sircmpwn) ou aux [primes](https://github.com/swaywm/sway/issues/986) -pour des fonctionnalités spécifiques. -Tout le monde est invité à réclamer une prime et vous pouvez donner une prime pour n'importe quelle -fonctionnalité souhaitée. Patreon est plus utile pour supporter l'état général et la -maintenance de Sway. +Si vous souhaitez soutenir le développement de Sway, vous pouvez contribuer à [la page +Patreon de SirCmpwn](https://patreon.com/sircmpwn). ## Aide en français @@ -39,21 +33,21 @@ IRC ou envoyez un e-mail à sir@cmpwn.com (en anglais seulement) pour des consei Installez les dépendances : -* meson -* [wlc](https://github.com/Cloudef/wlc) +* meson \* +* [wlroots](https://github.com/swaywm/wlroots) * wayland -* xwayland -* libinput >= 1.6.0 -* libcap +* wayland-protocols \* * pcre -* json-c >= 0.13 +* json-c * pango * cairo -* gdk-pixbuf2 * -* [scdoc](https://git.sr.ht/~sircmpwn/scdoc) (requis pour les pages man) -* git +* gdk-pixbuf2 \*\* +* [scdoc](https://git.sr.ht/~sircmpwn/scdoc) (optionnel: requis pour les pages man) \* +* git \* -_\*Uniquement requis pour swaybar, swaybg_ +_\*Requis uniquement pour la compilation_ + +_\*\*Optionnel: requis uniquement pour swaybg_ Exécutez ces commandes : @@ -65,6 +59,8 @@ Sur les systèmes sans logind, vous devez suid le binaire de sway : sudo chmod a+s /usr/local/bin/sway +Sway se débarassera des permissions *root* peu de temps après le démarrage. + ## Configuration Si vous utilisez déjà i3, copiez votre configuration i3 à `~/.config/sway/config` et @@ -72,10 +68,6 @@ cela va fonctionner. Sinon, copiez l'exemple de fichier de configuration à `~/.config/sway/config`. Il se trouve généralement dans `/etc/sway/config`. Exécutez `man 5 sway` pour l'information sur la configuration. -Mes propres dotfiles sont disponibles [ici](https://git.sr.ht/~sircmpwn/dotfiles) si -vous voulez un peu d'inspiration. Je vous recommande aussi de consulter le -[wiki](https://github.com/swaywm/sway/wiki). - ## Exécution Exécutez `sway` à partir d'un TTY. Certains gestionnaires d'affichage peuvent fonctionner, From 8d2c982f3fea5b7f83680809bf4d986edfd625a3 Mon Sep 17 00:00:00 2001 From: Philipe Goulet Date: Fri, 22 Mar 2019 20:51:10 -0400 Subject: [PATCH 090/191] Fix #3924 Removes "unescape_string(argv[i]);". Since "do_var_replacement(argv[i])" never adds escape characters, it is both wrong and unnecessary to remove escape characters on the next line. This caused characters that were meant to be escaped to not be anymore. --- sway/commands.c | 1 - 1 file changed, 1 deletion(-) diff --git a/sway/commands.c b/sway/commands.c index 18b95c738..0d9460a21 100644 --- a/sway/commands.c +++ b/sway/commands.c @@ -276,7 +276,6 @@ list_t *execute_command(char *_exec, struct sway_seat *seat, // Var replacement, for all but first argument of set for (int i = handler->handle == cmd_set ? 2 : 1; i < argc; ++i) { argv[i] = do_var_replacement(argv[i]); - unescape_string(argv[i]); } if (!config->handler_context.using_criteria) { From d9de5b87583ccf8b633980ebbdec67227bbe7db4 Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Sun, 24 Mar 2019 21:21:24 -0400 Subject: [PATCH 091/191] Implement inhibit_idle command This implements the following command to set/unset a user idle inhibitor for a view: `inhibit_idle focus|fullscreen|open|none|visible` The modes are as follows: - focus: inhibited when the view is focused by any seat - fullscreen: inhibited when the view is fullscreen (or a descendant of a fullscreen container) and is visible on any output - open: inhibited until the view is closed or the inhibitor is unset or changed - none: unsets any user set idle inhibitors for the view - visible: inhibited when the view is visible on any output This should have no effect on idle inhibitors set by the applications themselves and those should still work as intended. Since this operates on the view in the handler context, it is possible to set it on the currently focused view, on any existing view with criteria, or for any future view with for_window. --- include/sway/commands.h | 1 + include/sway/desktop/idle_inhibit_v1.h | 20 +++++- sway/commands.c | 1 + sway/commands/inhibit_idle.c | 51 +++++++++++++ sway/desktop/idle_inhibit_v1.c | 99 ++++++++++++++++++++++---- sway/desktop/transaction.c | 2 +- sway/meson.build | 1 + sway/sway.5.scd | 12 ++++ 8 files changed, 171 insertions(+), 16 deletions(-) create mode 100644 sway/commands/inhibit_idle.c diff --git a/include/sway/commands.h b/include/sway/commands.h index 7533a14d3..9bd0f1cb5 100644 --- a/include/sway/commands.h +++ b/include/sway/commands.h @@ -136,6 +136,7 @@ sway_cmd cmd_fullscreen; sway_cmd cmd_gaps; sway_cmd cmd_hide_edge_borders; sway_cmd cmd_include; +sway_cmd cmd_inhibit_idle; sway_cmd cmd_input; sway_cmd cmd_seat; sway_cmd cmd_ipc; diff --git a/include/sway/desktop/idle_inhibit_v1.h b/include/sway/desktop/idle_inhibit_v1.h index e5ed8a3de..4d4e59b0b 100644 --- a/include/sway/desktop/idle_inhibit_v1.h +++ b/include/sway/desktop/idle_inhibit_v1.h @@ -4,6 +4,14 @@ #include #include "sway/server.h" +enum sway_idle_inhibit_mode { + INHIBIT_IDLE_APPLICATION, // Application set inhibitor (when visible) + INHIBIT_IDLE_FOCUS, // User set inhibitor when focused + INHIBIT_IDLE_FULLSCREEN, // User set inhibitor when fullscreen + visible + INHIBIT_IDLE_OPEN, // User set inhibitor while open + INHIBIT_IDLE_VISIBLE // User set inhibitor when visible +}; + struct sway_idle_inhibit_manager_v1 { struct wlr_idle_inhibit_manager_v1 *wlr_manager; struct wl_listener new_idle_inhibitor_v1; @@ -15,14 +23,24 @@ struct sway_idle_inhibit_manager_v1 { struct sway_idle_inhibitor_v1 { struct sway_idle_inhibit_manager_v1 *manager; struct sway_view *view; + enum sway_idle_inhibit_mode mode; struct wl_list link; struct wl_listener destroy; }; -void idle_inhibit_v1_check_active( +void sway_idle_inhibit_v1_check_active( struct sway_idle_inhibit_manager_v1 *manager); +void sway_idle_inhibit_v1_user_inhibitor_register(struct sway_view *view, + enum sway_idle_inhibit_mode mode); + +struct sway_idle_inhibitor_v1 *sway_idle_inhibit_v1_user_inhibitor_for_view( + struct sway_view *view); + +void sway_idle_inhibit_v1_user_inhibitor_destroy( + struct sway_idle_inhibitor_v1 *inhibitor); + 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/sway/commands.c b/sway/commands.c index 0d9460a21..abdaa3b87 100644 --- a/sway/commands.c +++ b/sway/commands.c @@ -112,6 +112,7 @@ static struct cmd_handler command_handlers[] = { { "exit", cmd_exit }, { "floating", cmd_floating }, { "fullscreen", cmd_fullscreen }, + { "inhibit_idle", cmd_inhibit_idle }, { "kill", cmd_kill }, { "layout", cmd_layout }, { "mark", cmd_mark }, diff --git a/sway/commands/inhibit_idle.c b/sway/commands/inhibit_idle.c new file mode 100644 index 000000000..aebc2bf9f --- /dev/null +++ b/sway/commands/inhibit_idle.c @@ -0,0 +1,51 @@ +#include +#include "sway/commands.h" +#include "sway/config.h" +#include "sway/desktop/idle_inhibit_v1.h" +#include "sway/tree/container.h" +#include "sway/tree/view.h" + +struct cmd_results *cmd_inhibit_idle(int argc, char **argv) { + struct cmd_results *error = NULL; + if ((error = checkarg(argc, "inhibit_idle", EXPECTED_EQUAL_TO, 1))) { + return error; + } + + struct sway_container *con = config->handler_context.container; + if (!con || !con->view) { + return cmd_results_new(CMD_INVALID, + "Only views can have idle inhibitors"); + } + + bool clear = false; + enum sway_idle_inhibit_mode mode; + if (strcmp(argv[0], "focus") == 0) { + mode = INHIBIT_IDLE_FOCUS; + } else if (strcmp(argv[0], "fullscreen") == 0) { + mode = INHIBIT_IDLE_FULLSCREEN; + } else if (strcmp(argv[0], "open") == 0) { + mode = INHIBIT_IDLE_OPEN; + } else if (strcmp(argv[0], "none") == 0) { + clear = true; + } else if (strcmp(argv[0], "visible") == 0) { + mode = INHIBIT_IDLE_VISIBLE; + } else { + return cmd_results_new(CMD_INVALID, + "Expected `inhibit_idle focus|fullscreen|open|none|visible`"); + } + + struct sway_idle_inhibitor_v1 *inhibitor = + sway_idle_inhibit_v1_user_inhibitor_for_view(con->view); + if (inhibitor) { + if (clear) { + sway_idle_inhibit_v1_user_inhibitor_destroy(inhibitor); + } else { + inhibitor->mode = mode; + sway_idle_inhibit_v1_check_active(server.idle_inhibit_manager_v1); + } + } else if (!clear) { + sway_idle_inhibit_v1_user_inhibitor_register(con->view, mode); + } + + return cmd_results_new(CMD_SUCCESS, NULL); +} diff --git a/sway/desktop/idle_inhibit_v1.c b/sway/desktop/idle_inhibit_v1.c index 87b4ef43d..b981e5e5a 100644 --- a/sway/desktop/idle_inhibit_v1.c +++ b/sway/desktop/idle_inhibit_v1.c @@ -2,18 +2,24 @@ #include #include "log.h" #include "sway/desktop/idle_inhibit_v1.h" +#include "sway/input/seat.h" +#include "sway/tree/container.h" #include "sway/tree/view.h" #include "sway/server.h" +static void destroy_inhibitor(struct sway_idle_inhibitor_v1 *inhibitor) { + wl_list_remove(&inhibitor->link); + wl_list_remove(&inhibitor->destroy.link); + sway_idle_inhibit_v1_check_active(inhibitor->manager); + free(inhibitor); +} + static void handle_destroy(struct wl_listener *listener, void *data) { struct sway_idle_inhibitor_v1 *inhibitor = wl_container_of(listener, inhibitor, destroy); sway_log(SWAY_DEBUG, "Sway idle inhibitor destroyed"); - wl_list_remove(&inhibitor->link); - wl_list_remove(&inhibitor->destroy.link); - idle_inhibit_v1_check_active(inhibitor->manager); - free(inhibitor); + destroy_inhibitor(inhibitor); } void handle_idle_inhibitor_v1(struct wl_listener *listener, void *data) { @@ -29,28 +35,93 @@ void handle_idle_inhibitor_v1(struct wl_listener *listener, void *data) { } inhibitor->manager = manager; + inhibitor->mode = INHIBIT_IDLE_APPLICATION; inhibitor->view = view_from_wlr_surface(wlr_inhibitor->surface); wl_list_insert(&manager->inhibitors, &inhibitor->link); - inhibitor->destroy.notify = handle_destroy; wl_signal_add(&wlr_inhibitor->events.destroy, &inhibitor->destroy); - idle_inhibit_v1_check_active(manager); + sway_idle_inhibit_v1_check_active(manager); } -void idle_inhibit_v1_check_active( +void sway_idle_inhibit_v1_user_inhibitor_register(struct sway_view *view, + enum sway_idle_inhibit_mode mode) { + struct sway_idle_inhibitor_v1 *inhibitor = + calloc(1, sizeof(struct sway_idle_inhibitor_v1)); + if (!inhibitor) { + return; + } + + inhibitor->manager = server.idle_inhibit_manager_v1; + inhibitor->mode = mode; + inhibitor->view = view; + wl_list_insert(&inhibitor->manager->inhibitors, &inhibitor->link); + + inhibitor->destroy.notify = handle_destroy; + wl_signal_add(&view->events.unmap, &inhibitor->destroy); + + sway_idle_inhibit_v1_check_active(inhibitor->manager); +} + +struct sway_idle_inhibitor_v1 *sway_idle_inhibit_v1_user_inhibitor_for_view( + struct sway_view *view) { + struct sway_idle_inhibitor_v1 *inhibitor; + wl_list_for_each(inhibitor, &server.idle_inhibit_manager_v1->inhibitors, + link) { + if (inhibitor->view == view && + inhibitor->mode != INHIBIT_IDLE_APPLICATION) { + return inhibitor; + } + } + return NULL; +} + +void sway_idle_inhibit_v1_user_inhibitor_destroy( + struct sway_idle_inhibitor_v1 *inhibitor) { + if (!inhibitor) { + return; + } + if (!sway_assert(inhibitor->mode != INHIBIT_IDLE_APPLICATION, + "User should not be able to destroy application inhibitor")) { + return; + } + destroy_inhibitor(inhibitor); +} + +static bool check_active(struct sway_idle_inhibitor_v1 *inhibitor) { + switch (inhibitor->mode) { + case INHIBIT_IDLE_APPLICATION: + // If there is no view associated with the inhibitor, assume visible + return !inhibitor->view || view_is_visible(inhibitor->view); + case INHIBIT_IDLE_FOCUS:; + struct sway_seat *seat = NULL; + wl_list_for_each(seat, &server.input->seats, link) { + struct sway_container *con = seat_get_focused_container(seat); + if (con && con->view && con->view == inhibitor->view) { + return true; + } + } + return false; + case INHIBIT_IDLE_FULLSCREEN: + return inhibitor->view->container && + container_is_fullscreen_or_child(inhibitor->view->container) && + view_is_visible(inhibitor->view); + case INHIBIT_IDLE_OPEN: + // Inhibitor is destroyed on unmap so it must be open/mapped + return true; + case INHIBIT_IDLE_VISIBLE: + return view_is_visible(inhibitor->view); + } + return false; +} + +void sway_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, &manager->inhibitors, link) { - if (!inhibitor->view || !inhibitor->view->container) { - /* Cannot guess if view is visible so assume it is */ - inhibited = true; - break; - } - if (view_is_visible(inhibitor->view)) { - inhibited = true; + if ((inhibited = check_active(inhibitor))) { break; } } diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index e5f0ee3d1..51c6e7fca 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -349,7 +349,7 @@ static void transaction_progress_queue(void) { list_del(server.transactions, 0); if (!server.transactions->length) { - idle_inhibit_v1_check_active(server.idle_inhibit_manager_v1); + sway_idle_inhibit_v1_check_active(server.idle_inhibit_manager_v1); return; } diff --git a/sway/meson.build b/sway/meson.build index 9f79fb6ce..12b86efb8 100644 --- a/sway/meson.build +++ b/sway/meson.build @@ -63,6 +63,7 @@ sway_sources = files( 'commands/fullscreen.c', 'commands/gaps.c', 'commands/hide_edge_borders.c', + 'commands/inhibit_idle.c', 'commands/kill.c', 'commands/mark.c', 'commands/opacity.c', diff --git a/sway/sway.5.scd b/sway/sway.5.scd index dbfeefe3c..1650cd60c 100644 --- a/sway/sway.5.scd +++ b/sway/sway.5.scd @@ -146,6 +146,18 @@ set|plus|minus _right_, _bottom_, and _left_ or per direction with _horizontal_ and _vertical_. +*inhibit_idle* focus|fullscreen|open|none|visible + Set/unset an idle inhibitor for the view. _focus_ will inhibit idle when + the view is focused by any seat. _fullscreen_ will inhibit idle when the + view is fullscreen (or a descendant of a fullscreen container) and is + visible. _open_ will inhibit idle until the view is closed (or the + inhibitor is unset/changed). _visible_ will inhibit idle when the view is + visible on any output. _none_ will remove any existing idle inhibitor for + the view. + + This can also be used with criteria to set an idle inhibitor for any + existing view or with _for_window_ to set idle inhibitors for future views. + *layout* default|splith|splitv|stacking|tabbed Sets the layout mode of the focused container. From e9d096468a3fe9d8346dd97a67e050938f0befde Mon Sep 17 00:00:00 2001 From: Igor Sviatniy Date: Mon, 25 Mar 2019 17:53:10 +0200 Subject: [PATCH 092/191] Update README.uk.md --- README.uk.md | 49 +++++++++++++++++++------------------------------ 1 file changed, 19 insertions(+), 30 deletions(-) diff --git a/README.uk.md b/README.uk.md index 4c378a89f..b0c81c512 100644 --- a/README.uk.md +++ b/README.uk.md @@ -1,20 +1,12 @@ # sway -**Sway** це сумісний з i3 композитор [Wayland](http://wayland.freedesktop.org/) -(**у стані розробки**). Ознайомтесь з -[ЧаПами](https://github.com/swaywm/sway/wiki). Приєднуйтесь до [спільноти в +Sway це сумісний з i3 композитор [Wayland](http://wayland.freedesktop.org/). +Ознайомтесь з [ЧаПами](https://github.com/swaywm/sway/wiki). Приєднуйтесь до [спільноти в IRC](http://webchat.freenode.net/?channels=sway&uio=d4) (#sway на irc.freenode.net). -[![](https://sr.ht/ICd5.png)](https://sr.ht/ICd5.png) - -Якщо ви хочете підтримати розробку Sway, ви можете зробити свій внесок у -[SirCmpwn'ову сторінку Patreon](https://patreon.com/sircmpwn) або до -[фонду винагород](https://github.com/swaywm/sway/issues/986) за реалізацію -певного функціоналу. -Кожен може виставити винагороду за реалізацію довільної функції -(і, відповідно, забрати її собі, виконавши це завдання); -кошти від сторінки Patreon підтримують загальну розробку та підтримку Sway. +Якщо ви маєте бажання підтримати розробку sway, ви можете зробити свій внесок на сторінці +[SirCmpwn у Patreon](https://patreon.com/sircmpwn). ## Підтримка українською мовою @@ -39,28 +31,28 @@ Sway доступний у багатьох дистрибутивах Linux (а для інформації щодо встановлення на вашому дистрибутиві. Якщо ви готові та зацікавлені запакувати і підтримувати Sway у вашому -дистрибутиві, будемо раді вас бачити у нашому каналі IRC. Ви також можете -спитати порад за адресою sir@cmpwn.com. +дистрибутиві, звертайтесь за порадами до нашого каналу в IRC або +пишіть на електронну пошту [sir@cmpwn.com](mailto:sir@cmpwn.com). ### З вихідного коду Встановіть залежності: -* meson -* [wlc](https://github.com/Cloudef/wlc) +* meson \* +* [wlroots](https://github.com/swaywm/wlroots) * wayland -* xwayland -* libinput >= 1.6.0 -* libcap +* wayland-protocols \* * pcre -* json-c >= 0.13 +* json-c * pango * cairo -* gdk-pixbuf2 * -* [scdoc](https://git.sr.ht/~sircmpwn/scdoc) (required for man pages) -* git +* gdk-pixbuf2 \*\* +* [scdoc](https://git.sr.ht/~sircmpwn/scdoc) (необов'язково, необхідно для сторінок man) \* +* git \* -_\*Лише для swaybar, swaybg_ +_\*Лише для компіляції_ + +_\*\*Необов'язково, необхідно для swaybg_ Виконайте ці команди: @@ -68,15 +60,12 @@ _\*Лише для swaybar, swaybg_ ninja -C build sudo ninja -C build install -На системах **з** logind, варто встановити декілька можливостей (caps) -на виконуваний файл sway: - - sudo setcap "cap_sys_ptrace,cap_sys_tty_config=eip" /usr/local/bin/sway - -На системах **без** logind, необхідно встановити біт SUID на виконуваний файл sway: +На системах без logind, необхідно встановити біт SUID на виконуваний файл sway: sudo chmod a+s /usr/local/bin/sway +Sway втратить права доступу root незабаром після запуску. + ## Налаштування Якщо ви вже використовуєте i3, скопіюйте свій файл налаштувань From 3a31889d7cb00c28724bc093653b3015393c5cb4 Mon Sep 17 00:00:00 2001 From: Ashkan Kiani Date: Tue, 26 Mar 2019 20:32:50 -0700 Subject: [PATCH 093/191] Fix crash for floating command on scratchpad window --- sway/commands/floating.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/sway/commands/floating.c b/sway/commands/floating.c index 821093695..5df9b1bfb 100644 --- a/sway/commands/floating.c +++ b/sway/commands/floating.c @@ -45,7 +45,10 @@ struct cmd_results *cmd_floating(int argc, char **argv) { container_set_floating(container, wants_floating); - arrange_workspace(container->workspace); + // Floating containers in the scratchpad should be ignored + if (container->workspace) { + arrange_workspace(container->workspace); + } return cmd_results_new(CMD_SUCCESS, NULL); } From bfdee1319ffc8a720d0536a752a19ba23615a1e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Pokorn=C3=BD?= Date: Tue, 26 Mar 2019 23:21:30 +0100 Subject: [PATCH 094/191] bindings: fix overwrite log argument mismatch Thanks, @RedSoxFan, for the review spotting another instance. --- sway/commands/bind.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sway/commands/bind.c b/sway/commands/bind.c index b3d391da6..9a937c611 100644 --- a/sway/commands/bind.c +++ b/sway/commands/bind.c @@ -316,7 +316,7 @@ static struct cmd_results *cmd_bindsym_or_bindcode(int argc, char **argv, struct sway_binding *config_binding = mode_bindings->items[i]; if (binding_key_compare(binding, config_binding)) { sway_log(SWAY_INFO, "Overwriting binding '%s' for device '%s' " - "from `%s` to `%s`", argv[0], binding->input, + "to `%s` from `%s`", argv[0], binding->input, binding->command, config_binding->command); if (warn) { config_add_swaynag_warning("Overwriting binding" @@ -420,7 +420,7 @@ struct cmd_results *cmd_bindswitch(int argc, char **argv) { for (int i = 0; i < mode_bindings->length; ++i) { struct sway_switch_binding *config_binding = mode_bindings->items[i]; if (binding_switch_compare(binding, config_binding)) { - sway_log(SWAY_INFO, "Overwriting binding '%s' from `%s` to `%s`", + sway_log(SWAY_INFO, "Overwriting binding '%s' to `%s` from `%s`", argv[0], binding->command, config_binding->command); if (warn) { config_add_swaynag_warning("Overwriting binding" From 2cae0d5e3a9c5abc7c2aa2aad4021c86730b2ec9 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Fri, 29 Mar 2019 18:29:34 +1000 Subject: [PATCH 095/191] Fix null pointer crash when doing tiling drag * Create layout S[V[view view] view] * Drag bottom view to the top * Sway would crash when the cursor hovers the V[view view] title while dragging --- sway/input/seatop_move_tiling.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sway/input/seatop_move_tiling.c b/sway/input/seatop_move_tiling.c index 0a2480919..64a16c098 100644 --- a/sway/input/seatop_move_tiling.c +++ b/sway/input/seatop_move_tiling.c @@ -108,7 +108,7 @@ static void handle_motion_postthreshold(struct sway_seat *seat) { } if (node->type == N_WORKSPACE) { - // Emtpy workspace + // Empty workspace e->target_node = node; e->target_edge = WLR_EDGE_NONE; workspace_get_box(node->sway_workspace, &e->drop_box); @@ -164,7 +164,7 @@ static void handle_motion_postthreshold(struct sway_seat *seat) { // Use the hovered view - but we must be over the actual surface con = node->sway_container; - if (!con->view->surface || node == &e->con->node + if (!con->view || !con->view->surface || node == &e->con->node || node_has_ancestor(node, &e->con->node)) { e->target_node = NULL; e->target_edge = WLR_EDGE_NONE; From 31eeda11b0952e7520a5171c5b683ad6fba0f519 Mon Sep 17 00:00:00 2001 From: Alex Maese Date: Sat, 30 Mar 2019 13:01:38 -0500 Subject: [PATCH 096/191] Fix a crash in swaybar when an icon dir is not readable --- swaybar/tray/icon.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/swaybar/tray/icon.c b/swaybar/tray/icon.c index 8587f3f7b..56f230e14 100644 --- a/swaybar/tray/icon.c +++ b/swaybar/tray/icon.c @@ -348,6 +348,9 @@ void init_themes(list_t **themes, list_t **basedirs) { *themes = create_list(); for (int i = 0; i < (*basedirs)->length; ++i) { list_t *dir_themes = load_themes_in_dir((*basedirs)->items[i]); + if (dir_themes == NULL) { + continue; + } list_cat(*themes, dir_themes); list_free(dir_themes); } From dd28e6a6d6abf06d2d16e6c91aeaf942bf225af7 Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Wed, 27 Mar 2019 14:00:19 -0400 Subject: [PATCH 097/191] Fix xwayland configure request scratchpad crash This fixes a crash in `container_init_floating` when a xwayland view sends a configure request while in the scratchpad. `container_init_floating` gets called so the configured minimum and maximum sizes gets respected when resizing to the requested size. Since the workspace was NULL, it would SIGSEGV when attempting to get the workspace's output for the output box retrieval. This extracts the resizing portion of `container_init_floating` into a separate function. If the container is in the scratchpad, it will just be resized and skip the centering. Additionally, `container_init_floating` has been renamed to `container_floating_resize_and_center` to more accurately describe what it does. --- include/sway/tree/container.h | 5 +---- sway/desktop/xwayland.c | 2 +- sway/tree/container.c | 41 +++++++++++++++++++++++------------ sway/tree/output.c | 2 +- sway/tree/root.c | 10 +-------- 5 files changed, 31 insertions(+), 29 deletions(-) diff --git a/include/sway/tree/container.h b/include/sway/tree/container.h index d25089949..964061db6 100644 --- a/include/sway/tree/container.h +++ b/include/sway/tree/container.h @@ -215,10 +215,7 @@ size_t container_titlebar_height(void); void floating_calculate_constraints(int *min_width, int *max_width, int *min_height, int *max_height); -/** - * Resize and center the container in its workspace. - */ -void container_init_floating(struct sway_container *container); +void container_floating_resize_and_center(struct sway_container *con); void container_set_floating(struct sway_container *container, bool enable); diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c index f5ade8dcb..37d0b986a 100644 --- a/sway/desktop/xwayland.c +++ b/sway/desktop/xwayland.c @@ -435,7 +435,7 @@ static void handle_request_configure(struct wl_listener *listener, void *data) { // Respect minimum and maximum sizes view->natural_width = ev->width; view->natural_height = ev->height; - container_init_floating(view->container); + container_floating_resize_and_center(view->container); configure(view, view->container->content_x, view->container->content_y, diff --git a/sway/tree/container.c b/sway/tree/container.c index f84ce3602..b7ea2b7e2 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -649,8 +649,31 @@ void floating_calculate_constraints(int *min_width, int *max_width, } -void container_init_floating(struct sway_container *con) { +static void floating_natural_resize(struct sway_container *con) { + int min_width, max_width, min_height, max_height; + floating_calculate_constraints(&min_width, &max_width, + &min_height, &max_height); + if (!con->view) { + con->width = max_width; + con->height = max_height; + } else { + struct sway_view *view = con->view; + con->content_width = + fmax(min_width, fmin(view->natural_width, max_width)); + con->content_height = + fmax(min_height, fmin(view->natural_height, max_height)); + container_set_geometry_from_content(con); + } +} + +void container_floating_resize_and_center(struct sway_container *con) { struct sway_workspace *ws = con->workspace; + if (!ws) { + // On scratchpad, just resize + floating_natural_resize(con); + return; + } + struct wlr_box *ob = wlr_output_layout_get_box(root->output_layout, ws->output->wlr_output); if (!ob) { @@ -662,13 +685,8 @@ void container_init_floating(struct sway_container *con) { return; } - int min_width, max_width, min_height, max_height; - floating_calculate_constraints(&min_width, &max_width, - &min_height, &max_height); - + floating_natural_resize(con); if (!con->view) { - con->width = max_width; - con->height = max_height; if (con->width > ws->width || con->height > ws->height) { con->x = ob->x + (ob->width - con->width) / 2; con->y = ob->y + (ob->height - con->height) / 2; @@ -677,11 +695,6 @@ void container_init_floating(struct sway_container *con) { con->y = ws->y + (ws->height - con->height) / 2; } } else { - struct sway_view *view = con->view; - con->content_width = - fmax(min_width, fmin(view->natural_width, max_width)); - con->content_height = - fmax(min_height, fmin(view->natural_height, max_height)); if (con->content_width > ws->width || con->content_height > ws->height) { con->content_x = ob->x + (ob->width - con->content_width) / 2; @@ -711,7 +724,7 @@ void container_set_floating(struct sway_container *container, bool enable) { struct sway_container *old_parent = container->parent; container_detach(container); workspace_add_floating(workspace, container); - container_init_floating(container); + container_floating_resize_and_center(container); if (container->view) { view_set_tiled(container->view, false); if (container->view->using_csd) { @@ -995,7 +1008,7 @@ void container_fullscreen_disable(struct sway_container *con) { // criteria, it needs to be reinitialized as floating to get the proper // size and location if (container_is_floating(con) && (con->width == 0 || con->height == 0)) { - container_init_floating(con); + container_floating_resize_and_center(con); } con->fullscreen_mode = FULLSCREEN_NONE; diff --git a/sway/tree/output.c b/sway/tree/output.c index 283036524..b3589be5d 100644 --- a/sway/tree/output.c +++ b/sway/tree/output.c @@ -77,7 +77,7 @@ static void restore_workspaces(struct sway_output *output) { floater->y > output->ly + output->height || floater->x + floater->width < output->lx || floater->y + floater->height < output->ly) { - container_init_floating(floater); + container_floating_resize_and_center(floater); } } diff --git a/sway/tree/root.c b/sway/tree/root.c index 5dde9f22d..bc9c610df 100644 --- a/sway/tree/root.c +++ b/sway/tree/root.c @@ -124,15 +124,7 @@ void root_scratchpad_show(struct sway_container *con) { struct wlr_box workspace_box; workspace_get_box(new_ws, &workspace_box); if (!wlr_box_contains_point(&workspace_box, center_lx, center_ly)) { - // Maybe resize it - if (con->width > new_ws->width || con->height > new_ws->height) { - container_init_floating(con); - } - - // Center it - double new_lx = new_ws->x + (new_ws->width - con->width) / 2; - double new_ly = new_ws->y + (new_ws->height - con->height) / 2; - container_floating_move_to(con, new_lx, new_ly); + container_floating_resize_and_center(con); } arrange_workspace(new_ws); From 0676ace97fdd0054af6b0a4950e219ebd0a18de4 Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Fri, 29 Mar 2019 12:15:17 -0400 Subject: [PATCH 098/191] floating: fix size of non-view containers This fixes the sizing of floating non-view containers. On master, the floater will get set to the maximum width and height, which by default is the entire output layout. When setting a non-view container to floating, this will set a sane default size of 50% of the workspace width and 75% of the workspace height, or whatever the closest is that the minimum and maximum floating width/height values allow for. On all future calls to `floating_natural_resize`, the width and height will be kept unless they need to be changed to respect the min/max floating width/height values. --- sway/tree/container.c | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/sway/tree/container.c b/sway/tree/container.c index b7ea2b7e2..064d85f61 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -654,8 +654,8 @@ static void floating_natural_resize(struct sway_container *con) { floating_calculate_constraints(&min_width, &max_width, &min_height, &max_height); if (!con->view) { - con->width = max_width; - con->height = max_height; + con->width = fmax(min_width, fmin(con->width, max_width)); + con->height = fmax(min_height, fmin(con->height, max_height)); } else { struct sway_view *view = con->view; con->content_width = @@ -712,6 +712,22 @@ void container_floating_resize_and_center(struct sway_container *con) { } } +static void container_floating_set_default_size(struct sway_container *con) { + if (!sway_assert(con->workspace, "Expected a container on a workspace")) { + return; + } + int min_width, max_width, min_height, max_height; + floating_calculate_constraints(&min_width, &max_width, + &min_height, &max_height); + struct wlr_box *box = calloc(1, sizeof(struct wlr_box)); + workspace_get_box(con->workspace, box); + if (!con->view) { + con->width = fmax(min_width, fmin(box->width * 0.5, max_width)); + con->height = fmax(min_height, fmin(box->height * 0.75, max_height)); + } + free(box); +} + void container_set_floating(struct sway_container *container, bool enable) { if (container_is_floating(container) == enable) { return; @@ -724,6 +740,7 @@ void container_set_floating(struct sway_container *container, bool enable) { struct sway_container *old_parent = container->parent; container_detach(container); workspace_add_floating(workspace, container); + container_floating_set_default_size(container); container_floating_resize_and_center(container); if (container->view) { view_set_tiled(container->view, false); From 679c058fac986314675bacf7a7b01d263fb0db39 Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Fri, 29 Mar 2019 12:26:08 -0400 Subject: [PATCH 099/191] scratchpad: set initial size This matches i3's behavior of setting scratchpad containers to 50% of the workspace's width and 75% of the workspace's height, bound by the minimum and maximum floating width/height. --- include/sway/tree/container.h | 2 ++ sway/tree/container.c | 15 ++++++++++++--- sway/tree/root.c | 2 ++ 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/include/sway/tree/container.h b/include/sway/tree/container.h index 964061db6..8448d7059 100644 --- a/include/sway/tree/container.h +++ b/include/sway/tree/container.h @@ -217,6 +217,8 @@ void floating_calculate_constraints(int *min_width, int *max_width, void container_floating_resize_and_center(struct sway_container *con); +void container_floating_set_default_size(struct sway_container *con); + void container_set_floating(struct sway_container *container, bool enable); void container_set_geometry_from_content(struct sway_container *con); diff --git a/sway/tree/container.c b/sway/tree/container.c index 064d85f61..02b4d1b0e 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -712,19 +712,28 @@ void container_floating_resize_and_center(struct sway_container *con) { } } -static void container_floating_set_default_size(struct sway_container *con) { +void container_floating_set_default_size(struct sway_container *con) { if (!sway_assert(con->workspace, "Expected a container on a workspace")) { return; } + int min_width, max_width, min_height, max_height; floating_calculate_constraints(&min_width, &max_width, &min_height, &max_height); struct wlr_box *box = calloc(1, sizeof(struct wlr_box)); workspace_get_box(con->workspace, box); + + double width = fmax(min_width, fmin(box->width * 0.5, max_width)); + double height = fmax(min_height, fmin(box->height * 0.75, max_height)); if (!con->view) { - con->width = fmax(min_width, fmin(box->width * 0.5, max_width)); - con->height = fmax(min_height, fmin(box->height * 0.75, max_height)); + con->width = width; + con->height = height; + } else { + con->content_width = width; + con->content_height = height; + container_set_geometry_from_content(con); } + free(box); } diff --git a/sway/tree/root.c b/sway/tree/root.c index bc9c610df..3d93405e4 100644 --- a/sway/tree/root.c +++ b/sway/tree/root.c @@ -62,6 +62,8 @@ void root_scratchpad_add_container(struct sway_container *con) { struct sway_container *parent = con->parent; struct sway_workspace *workspace = con->workspace; container_set_floating(con, true); + container_floating_set_default_size(con); + container_floating_move_to_center(con); container_detach(con); con->scratchpad = true; list_add(root->scratchpad, con); From a32125c984a41dbbfbe5a12b450a84d78e04867a Mon Sep 17 00:00:00 2001 From: Dacheng Gao <13791720+gaodacheng@users.noreply.github.com> Date: Wed, 27 Mar 2019 21:55:59 +0800 Subject: [PATCH 100/191] add chinese translation --- README.zh-CN.md | 68 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 README.zh-CN.md diff --git a/README.zh-CN.md b/README.zh-CN.md new file mode 100644 index 000000000..3e56f229b --- /dev/null +++ b/README.zh-CN.md @@ -0,0 +1,68 @@ +# sway + +sway 是和 i3 兼容的 [Wayland](http://wayland.freedesktop.org/) compositor. +阅读 [FAQ](https://github.com/swaywm/sway/wiki). 加入 [IRC +频道](http://webchat.freenode.net/?channels=sway&uio=d4) (#sway on +irc.freenode.net). + +如果你想要支持 sway 的发展, 请到这 [SirCmpwn's +Patreon page](https://patreon.com/sircmpwn)贡献. + +## 发布签名 + +发布以 [B22DA89A](http://pgp.mit.edu/pks/lookup?op=vindex&search=0x52CB6609B22DA89A) 签名 +并发布在 [GitHub](https://github.com/swaywm/sway/releases). + +## 安装 + +### 从软件包中 + +Sway 在很多发行版中可用. 尝试在你的发行版中安装 "sway" 包. +如何这不可用, 请到 [此 wiki 页](https://github.com/swaywm/sway/wiki/Unsupported-packages) +检查针对你的发行版关于安装的信息. + +如果你有兴趣给你的发行版打包 sway, 停下来到 IRC 频道或者发邮件至 sir@cmpwn.com 获取建议. + +### 从源代码编译 + +安装依赖: + +* meson \* +* [wlroots](https://github.com/swaywm/wlroots) +* wayland +* wayland-protocols \* +* pcre +* json-c +* pango +* cairo +* gdk-pixbuf2 \*\* +* [scdoc](https://git.sr.ht/~sircmpwn/scdoc) (可选的: man pages) \* +* git \* + +_\*编译时依赖_ + +_\*\*可选的: swaybg 依赖_ + +运行这些命令: + + meson build + ninja -C build + sudo ninja -C build install + +在没有 logind 的系统上, 你需要给 sway 二进制设置 suid: + + sudo chmod a+s /usr/local/bin/sway + +Sway 将会在启动后尽快丢掉 root 权限. + +## 配置 + +如果你已经在使用 i3, 接下来复制你的 i3 配置到 `~/.config/sway/config` +它可以直接工作. 或者, 复制样本配置文件到 +`~/.config/sway/config`. 它通常位于 `/etc/sway/config`. +运行 `man 5 sway` 获取关于配置的信息. + +## 运行 + +从 TTY 中运行 `sway` . 某些显示管理器可能会工作但并不被 sway 支持 +(已知的 gdm 工作得非常好. From 3353bb58329aab882ff1d547448fa89d166d53d3 Mon Sep 17 00:00:00 2001 From: Dacheng Gao <13791720+gaodacheng@users.noreply.github.com> Date: Wed, 27 Mar 2019 21:58:41 +0800 Subject: [PATCH 101/191] fix cs --- README.zh-CN.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.zh-CN.md b/README.zh-CN.md index 3e56f229b..a443889da 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -65,4 +65,4 @@ Sway 将会在启动后尽快丢掉 root 权限. ## 运行 从 TTY 中运行 `sway` . 某些显示管理器可能会工作但并不被 sway 支持 -(已知的 gdm 工作得非常好. +(已知的 gdm 工作得非常好). From 078935ac8856a3c6ee5d0f61e2f9760520465520 Mon Sep 17 00:00:00 2001 From: Dacheng Gao <13791720+gaodacheng@users.noreply.github.com> Date: Wed, 27 Mar 2019 22:00:40 +0800 Subject: [PATCH 102/191] fix cs --- README.zh-CN.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.zh-CN.md b/README.zh-CN.md index a443889da..a60090f6a 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -5,12 +5,12 @@ sway 是和 i3 兼容的 [Wayland](http://wayland.freedesktop.org/) compositor. 频道](http://webchat.freenode.net/?channels=sway&uio=d4) (#sway on irc.freenode.net). -如果你想要支持 sway 的发展, 请到这 [SirCmpwn's +如果你想要支持 sway 的发展, 请到 [SirCmpwn's Patreon page](https://patreon.com/sircmpwn)贡献. ## 发布签名 -发布以 [B22DA89A](http://pgp.mit.edu/pks/lookup?op=vindex&search=0x52CB6609B22DA89A) 签名 +发布是以 [B22DA89A](http://pgp.mit.edu/pks/lookup?op=vindex&search=0x52CB6609B22DA89A) 签名 并发布在 [GitHub](https://github.com/swaywm/sway/releases). ## 安装 From 4938da2a341888dd5cd0ae3c9bf37ec417e05e25 Mon Sep 17 00:00:00 2001 From: Dacheng Gao <13791720+gaodacheng@users.noreply.github.com> Date: Wed, 27 Mar 2019 22:22:00 +0800 Subject: [PATCH 103/191] add link to README-zh-CN --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 99835d6e5..662025ef5 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ [**English**](https://github.com/swaywm/sway/blob/master/README.md#sway--) - [日本語](https://github.com/swaywm/sway/blob/master/README.ja.md#sway--) - [Deutsch](https://github.com/swaywm/sway/blob/master/README.de.md#sway--) - [Ελληνικά](https://github.com/swaywm/sway/blob/master/README.el.md#sway--) - [Français](https://github.com/swaywm/sway/blob/master/README.fr.md#sway--) - [Українська](https://github.com/swaywm/sway/blob/master/README.uk.md#sway--) - [Italiano](https://github.com/swaywm/sway/blob/master/README.it.md#sway--) - [Português](https://github.com/swaywm/sway/blob/master/README.pt.md#sway--) - [Русский](https://github.com/swaywm/sway/blob/master/README.ru.md#sway--) - [Български](https://github.com/swaywm/sway/blob/master/README.bg.md#sway--) - [Español](https://github.com/swaywm/sway/blob/master/README.es.md#sway--) - -[Polski](https://github.com/swaywm/sway/blob/master/README.pl.md#sway--) +[Polski](https://github.com/swaywm/sway/blob/master/README.pl.md#sway--) - [**中文-简体**](https://github.com/swaywm/sway/blob/master/README-zh-CN.md#sway--) sway is an i3-compatible [Wayland](http://wayland.freedesktop.org/) compositor. Read the [FAQ](https://github.com/swaywm/sway/wiki). Join the [IRC From f3140506a886107c39da3ea5b17ccc3519b2ac26 Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Mon, 1 Apr 2019 10:56:56 -0400 Subject: [PATCH 104/191] Update formatting of zh-cn link in README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 662025ef5..d1d78799e 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ [**English**](https://github.com/swaywm/sway/blob/master/README.md#sway--) - [日本語](https://github.com/swaywm/sway/blob/master/README.ja.md#sway--) - [Deutsch](https://github.com/swaywm/sway/blob/master/README.de.md#sway--) - [Ελληνικά](https://github.com/swaywm/sway/blob/master/README.el.md#sway--) - [Français](https://github.com/swaywm/sway/blob/master/README.fr.md#sway--) - [Українська](https://github.com/swaywm/sway/blob/master/README.uk.md#sway--) - [Italiano](https://github.com/swaywm/sway/blob/master/README.it.md#sway--) - [Português](https://github.com/swaywm/sway/blob/master/README.pt.md#sway--) - [Русский](https://github.com/swaywm/sway/blob/master/README.ru.md#sway--) - [Български](https://github.com/swaywm/sway/blob/master/README.bg.md#sway--) - [Español](https://github.com/swaywm/sway/blob/master/README.es.md#sway--) - -[Polski](https://github.com/swaywm/sway/blob/master/README.pl.md#sway--) - [**中文-简体**](https://github.com/swaywm/sway/blob/master/README-zh-CN.md#sway--) +[Polski](https://github.com/swaywm/sway/blob/master/README.pl.md#sway--) - [中文-简体](https://github.com/swaywm/sway/blob/master/README-zh-CN.md#sway--) sway is an i3-compatible [Wayland](http://wayland.freedesktop.org/) compositor. Read the [FAQ](https://github.com/swaywm/sway/wiki). Join the [IRC From 5e0a9282010896ac39305988a914186695f9ed85 Mon Sep 17 00:00:00 2001 From: Dacheng Gao <13791720+gaodacheng@users.noreply.github.com> Date: Tue, 2 Apr 2019 10:47:28 +0800 Subject: [PATCH 105/191] fix typo --- README.zh-CN.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.zh-CN.md b/README.zh-CN.md index a60090f6a..6df5a1f32 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -18,7 +18,7 @@ Patreon page](https://patreon.com/sircmpwn)贡献. ### 从软件包中 Sway 在很多发行版中可用. 尝试在你的发行版中安装 "sway" 包. -如何这不可用, 请到 [此 wiki 页](https://github.com/swaywm/sway/wiki/Unsupported-packages) +如果这不可用, 请到 [此 wiki 页](https://github.com/swaywm/sway/wiki/Unsupported-packages) 检查针对你的发行版关于安装的信息. 如果你有兴趣给你的发行版打包 sway, 停下来到 IRC 频道或者发邮件至 sir@cmpwn.com 获取建议. From 1e1d9e12991e9a9be72da1fe0c887d9087dffe3a Mon Sep 17 00:00:00 2001 From: Dacheng Gao <13791720+gdcfivepercent@users.noreply.github.com> Date: Tue, 2 Apr 2019 15:17:58 +0800 Subject: [PATCH 106/191] fix broken link to README.zh-CN.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d1d78799e..dfddae416 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ [**English**](https://github.com/swaywm/sway/blob/master/README.md#sway--) - [日本語](https://github.com/swaywm/sway/blob/master/README.ja.md#sway--) - [Deutsch](https://github.com/swaywm/sway/blob/master/README.de.md#sway--) - [Ελληνικά](https://github.com/swaywm/sway/blob/master/README.el.md#sway--) - [Français](https://github.com/swaywm/sway/blob/master/README.fr.md#sway--) - [Українська](https://github.com/swaywm/sway/blob/master/README.uk.md#sway--) - [Italiano](https://github.com/swaywm/sway/blob/master/README.it.md#sway--) - [Português](https://github.com/swaywm/sway/blob/master/README.pt.md#sway--) - [Русский](https://github.com/swaywm/sway/blob/master/README.ru.md#sway--) - [Български](https://github.com/swaywm/sway/blob/master/README.bg.md#sway--) - [Español](https://github.com/swaywm/sway/blob/master/README.es.md#sway--) - -[Polski](https://github.com/swaywm/sway/blob/master/README.pl.md#sway--) - [中文-简体](https://github.com/swaywm/sway/blob/master/README-zh-CN.md#sway--) +[Polski](https://github.com/swaywm/sway/blob/master/README.pl.md#sway--) - [中文-简体](https://github.com/swaywm/sway/blob/master/README.zh-CN.md#sway--) sway is an i3-compatible [Wayland](http://wayland.freedesktop.org/) compositor. Read the [FAQ](https://github.com/swaywm/sway/wiki). Join the [IRC From f0f20f96aa5cea382ca079b6a88fbcaf5b3782ad Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Tue, 2 Apr 2019 13:17:01 -0400 Subject: [PATCH 107/191] root_scratchpad_hide: fix crash when layer focused This fixes a crash in `root_scratchpad_hide` when a layer surface is focused. Since `seat_get_focus` is NULL when a layer surface is focused, the call to `node_has_ancestor` was causing a SIGSEGV since it was attempting to access the parent of NULL. This changes the call to `seat_get_focus_inactive`, which will return a node even when a layer surface is focused and is also guaranteed to have something in the focus stack if a scratchpad container is being hidden (otherwise there would not be any containers yet). --- sway/tree/root.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sway/tree/root.c b/sway/tree/root.c index 3d93405e4..c7c5e1df3 100644 --- a/sway/tree/root.c +++ b/sway/tree/root.c @@ -135,7 +135,7 @@ void root_scratchpad_show(struct sway_container *con) { void root_scratchpad_hide(struct sway_container *con) { struct sway_seat *seat = input_manager_current_seat(); - struct sway_node *focus = seat_get_focus(seat); + struct sway_node *focus = seat_get_focus_inactive(seat, &root->node); struct sway_workspace *ws = con->workspace; container_detach(con); From 0ad905f23cc2862c96401b6a27d918b237db35b5 Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Tue, 2 Apr 2019 11:51:46 -0400 Subject: [PATCH 108/191] idle_inhibit: fix crash during view destruction This fixes a crash for application set idle inhibitors when their associated view is being destroyed. There is a call to `view_is_visible` to determine is the view is visible and it assumes that the view has an container, but it is possible for the container to already have been destroyed at this point. There is a NULL check for the view in `check_active` and this re-adds the NULL check for the container that I accidentally dropped when refactoring during the inhibit_idle command PR --- sway/desktop/idle_inhibit_v1.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sway/desktop/idle_inhibit_v1.c b/sway/desktop/idle_inhibit_v1.c index b981e5e5a..73e46a8f8 100644 --- a/sway/desktop/idle_inhibit_v1.c +++ b/sway/desktop/idle_inhibit_v1.c @@ -93,7 +93,8 @@ static bool check_active(struct sway_idle_inhibitor_v1 *inhibitor) { switch (inhibitor->mode) { case INHIBIT_IDLE_APPLICATION: // If there is no view associated with the inhibitor, assume visible - return !inhibitor->view || view_is_visible(inhibitor->view); + return !inhibitor->view || !inhibitor->view->container || + view_is_visible(inhibitor->view); case INHIBIT_IDLE_FOCUS:; struct sway_seat *seat = NULL; wl_list_for_each(seat, &server.input->seats, link) { From 75e7bd24ccb9731065bb7f8313aa53ba11ddc420 Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Wed, 3 Apr 2019 21:53:43 -0400 Subject: [PATCH 109/191] swaybg: one instance for all outputs This makes it so there will only be one swaybg instance running instead of one per output. swaybg's cli has been changed to a xrandr like interface, where you select an output and then change properties for that output and then select another output and repeat. This also makes it so swaybg is only killed and respawned when a background changes or when reloading. --- include/sway/config.h | 10 +- include/sway/output.h | 3 - sway/commands/output.c | 5 + sway/config.c | 4 + sway/config/output.c | 252 +++++++++++++--------- sway/desktop/output.c | 2 - sway/tree/output.c | 4 - swaybg/main.c | 468 +++++++++++++++++++++++++++++------------ 8 files changed, 497 insertions(+), 251 deletions(-) diff --git a/include/sway/config.h b/include/sway/config.h index d49120a06..fe06fb9de 100644 --- a/include/sway/config.h +++ b/include/sway/config.h @@ -424,7 +424,6 @@ struct sway_config { list_t *active_bar_modifiers; struct sway_mode *current_mode; struct bar_config *current_bar; - char *swaybg_command; uint32_t floating_mod; bool floating_mod_inverse; uint32_t dragging_key; @@ -447,6 +446,11 @@ struct sway_config { enum sway_popup_during_fullscreen popup_during_fullscreen; bool xwayland; + // swaybg + char *swaybg_command; + struct wl_client *swaybg_client; + struct wl_listener swaybg_client_destroy; + // Flags enum focus_follows_mouse_mode focus_follows_mouse; enum mouse_warping_mode mouse_warping; @@ -607,6 +611,8 @@ void reset_outputs(void); void free_output_config(struct output_config *oc); +bool spawn_swaybg(void); + int workspace_output_cmp_workspace(const void *a, const void *b); int sway_binding_cmp(const void *a, const void *b); @@ -625,8 +631,6 @@ void load_swaybar(struct bar_config *bar); void load_swaybars(void); -void terminate_swaybg(pid_t pid); - struct bar_config *default_bar_config(void); void free_bar_config(struct bar_config *bar); diff --git a/include/sway/output.h b/include/sway/output.h index c336c5592..cae77e2ec 100644 --- a/include/sway/output.h +++ b/include/sway/output.h @@ -38,8 +38,6 @@ struct sway_output { struct sway_output_state current; - struct wl_client *swaybg_client; - struct wl_listener destroy; struct wl_listener mode; struct wl_listener transform; @@ -47,7 +45,6 @@ struct sway_output { struct wl_listener present; struct wl_listener damage_destroy; struct wl_listener damage_frame; - struct wl_listener swaybg_client_destroy; struct { struct wl_signal destroy; diff --git a/sway/commands/output.c b/sway/commands/output.c index 44e28512f..6b9eafdbe 100644 --- a/sway/commands/output.c +++ b/sway/commands/output.c @@ -68,6 +68,8 @@ struct cmd_results *cmd_output(int argc, char **argv) { config->handler_context.leftovers.argc = 0; config->handler_context.leftovers.argv = NULL; + bool background = output->background; + output = store_output_config(output); // If reloading, the output configs will be applied after reading the @@ -75,6 +77,9 @@ struct cmd_results *cmd_output(int argc, char **argv) { // workspace name is not given to re-enabled outputs. if (!config->reloading) { apply_output_config_to_outputs(output); + if (background) { + spawn_swaybg(); + } } return cmd_results_new(CMD_SUCCESS, NULL); diff --git a/sway/config.c b/sway/config.c index 7104f55d7..d5bfe105f 100644 --- a/sway/config.c +++ b/sway/config.c @@ -104,6 +104,9 @@ void free_config(struct sway_config *config) { } list_free(config->output_configs); } + if (config->swaybg_client != NULL) { + wl_client_destroy(config->swaybg_client); + } if (config->input_configs) { for (int i = 0; i < config->input_configs->length; i++) { free_input_config(config->input_configs->items[i]); @@ -480,6 +483,7 @@ bool load_main_config(const char *file, bool is_active, bool validating) { if (is_active) { reset_outputs(); + spawn_swaybg(); config->reloading = false; if (config->swaynag_config_errors.pid > 0) { diff --git a/sway/config/output.c b/sway/config/output.c index d06051b34..0473d0ad5 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -228,91 +228,6 @@ static bool set_mode(struct wlr_output *output, int width, int height, return wlr_output_set_mode(output, best); } -static void handle_swaybg_client_destroy(struct wl_listener *listener, - void *data) { - struct sway_output *output = - wl_container_of(listener, output, swaybg_client_destroy); - wl_list_remove(&output->swaybg_client_destroy.link); - wl_list_init(&output->swaybg_client_destroy.link); - output->swaybg_client = NULL; -} - -static bool set_cloexec(int fd, bool cloexec) { - int flags = fcntl(fd, F_GETFD); - if (flags == -1) { - sway_log_errno(SWAY_ERROR, "fcntl failed"); - return false; - } - if (cloexec) { - flags = flags | FD_CLOEXEC; - } else { - flags = flags & ~FD_CLOEXEC; - } - if (fcntl(fd, F_SETFD, flags) == -1) { - sway_log_errno(SWAY_ERROR, "fcntl failed"); - return false; - } - return true; -} - -static bool spawn_swaybg(struct sway_output *output, char *const cmd[]) { - int sockets[2]; - if (socketpair(AF_UNIX, SOCK_STREAM, 0, sockets) != 0) { - sway_log_errno(SWAY_ERROR, "socketpair failed"); - return false; - } - if (!set_cloexec(sockets[0], true) || !set_cloexec(sockets[1], true)) { - return false; - } - - output->swaybg_client = wl_client_create(server.wl_display, sockets[0]); - if (output->swaybg_client == NULL) { - sway_log_errno(SWAY_ERROR, "wl_client_create failed"); - return false; - } - - output->swaybg_client_destroy.notify = handle_swaybg_client_destroy; - wl_client_add_destroy_listener(output->swaybg_client, - &output->swaybg_client_destroy); - - pid_t pid = fork(); - if (pid < 0) { - sway_log_errno(SWAY_ERROR, "fork failed"); - return false; - } else if (pid == 0) { - pid = fork(); - if (pid < 0) { - sway_log_errno(SWAY_ERROR, "fork failed"); - _exit(EXIT_FAILURE); - } else if (pid == 0) { - if (!set_cloexec(sockets[1], false)) { - _exit(EXIT_FAILURE); - } - - char wayland_socket_str[16]; - snprintf(wayland_socket_str, sizeof(wayland_socket_str), - "%d", sockets[1]); - setenv("WAYLAND_SOCKET", wayland_socket_str, true); - - execvp(cmd[0], cmd); - sway_log_errno(SWAY_ERROR, "execvp failed"); - _exit(EXIT_FAILURE); - } - _exit(EXIT_SUCCESS); - } - - if (close(sockets[1]) != 0) { - sway_log_errno(SWAY_ERROR, "close failed"); - return false; - } - if (waitpid(pid, NULL, 0) < 0) { - sway_log_errno(SWAY_ERROR, "waitpid failed"); - return false; - } - - return true; -} - bool apply_output_config(struct output_config *oc, struct sway_output *output) { if (output == root->noop_output) { return false; @@ -397,25 +312,6 @@ bool apply_output_config(struct output_config *oc, struct sway_output *output) { wlr_output_transformed_resolution(wlr_output, &output->width, &output->height); - if (output->swaybg_client != NULL) { - wl_client_destroy(output->swaybg_client); - } - if (oc && oc->background && config->swaybg_command) { - sway_log(SWAY_DEBUG, "Setting background for output %s to %s", - wlr_output->name, oc->background); - - char *const cmd[] = { - config->swaybg_command, - wlr_output->name, - oc->background, - oc->background_option, - oc->background_fallback ? oc->background_fallback : NULL, - NULL, - }; - if (!spawn_swaybg(output, cmd)) { - return false; - } - } if (oc && oc->dpms_state == DPMS_OFF) { sway_log(SWAY_DEBUG, "Turning off screen"); @@ -584,3 +480,151 @@ void free_output_config(struct output_config *oc) { free(oc->background_option); free(oc); } + +static void handle_swaybg_client_destroy(struct wl_listener *listener, + void *data) { + wl_list_remove(&config->swaybg_client_destroy.link); + wl_list_init(&config->swaybg_client_destroy.link); + config->swaybg_client = NULL; +} + +static bool set_cloexec(int fd, bool cloexec) { + int flags = fcntl(fd, F_GETFD); + if (flags == -1) { + sway_log_errno(SWAY_ERROR, "fcntl failed"); + return false; + } + if (cloexec) { + flags = flags | FD_CLOEXEC; + } else { + flags = flags & ~FD_CLOEXEC; + } + if (fcntl(fd, F_SETFD, flags) == -1) { + sway_log_errno(SWAY_ERROR, "fcntl failed"); + return false; + } + return true; +} + +static bool _spawn_swaybg(char **command) { + if (config->swaybg_client != NULL) { + wl_client_destroy(config->swaybg_client); + } + int sockets[2]; + if (socketpair(AF_UNIX, SOCK_STREAM, 0, sockets) != 0) { + sway_log_errno(SWAY_ERROR, "socketpair failed"); + return false; + } + if (!set_cloexec(sockets[0], true) || !set_cloexec(sockets[1], true)) { + return false; + } + + config->swaybg_client = wl_client_create(server.wl_display, sockets[0]); + if (config->swaybg_client == NULL) { + sway_log_errno(SWAY_ERROR, "wl_client_create failed"); + return false; + } + + config->swaybg_client_destroy.notify = handle_swaybg_client_destroy; + wl_client_add_destroy_listener(config->swaybg_client, + &config->swaybg_client_destroy); + + pid_t pid = fork(); + if (pid < 0) { + sway_log_errno(SWAY_ERROR, "fork failed"); + return false; + } else if (pid == 0) { + pid = fork(); + if (pid < 0) { + sway_log_errno(SWAY_ERROR, "fork failed"); + _exit(EXIT_FAILURE); + } else if (pid == 0) { + if (!set_cloexec(sockets[1], false)) { + _exit(EXIT_FAILURE); + } + + char wayland_socket_str[16]; + snprintf(wayland_socket_str, sizeof(wayland_socket_str), + "%d", sockets[1]); + setenv("WAYLAND_SOCKET", wayland_socket_str, true); + + execvp(command[0], command); + sway_log_errno(SWAY_ERROR, "execvp failed"); + _exit(EXIT_FAILURE); + } + _exit(EXIT_SUCCESS); + } + + if (close(sockets[1]) != 0) { + sway_log_errno(SWAY_ERROR, "close failed"); + return false; + } + if (waitpid(pid, NULL, 0) < 0) { + sway_log_errno(SWAY_ERROR, "waitpid failed"); + return false; + } + + return true; +} + +bool spawn_swaybg(void) { + if (!config->swaybg_command) { + return true; + } + + size_t length = 2; + for (int i = 0; i < config->output_configs->length; i++) { + struct output_config *oc = config->output_configs->items[i]; + if (!oc->background) { + continue; + } + if (strcmp(oc->background_option, "solid_color") == 0) { + length += 4; + } else if (oc->background_fallback) { + length += 8; + } else { + length += 6; + } + } + + char **cmd = calloc(1, sizeof(char **) * length); + if (!cmd) { + sway_log(SWAY_ERROR, "Failed to allocate spawn_swaybg command"); + return false; + } + + size_t i = 0; + cmd[i++] = config->swaybg_command; + for (int j = 0; j < config->output_configs->length; j++) { + struct output_config *oc = config->output_configs->items[j]; + if (!oc->background) { + continue; + } + if (strcmp(oc->background_option, "solid_color") == 0) { + cmd[i++] = "-o"; + cmd[i++] = oc->name; + cmd[i++] = "-c"; + cmd[i++] = oc->background; + } else { + cmd[i++] = "-o"; + cmd[i++] = oc->name; + cmd[i++] = "-i"; + cmd[i++] = oc->background; + cmd[i++] = "-m"; + cmd[i++] = oc->background_option; + if (oc->background_fallback) { + cmd[i++] = "-c"; + cmd[i++] = oc->background_fallback; + } + } + assert(i <= length); + } + + for (size_t k = 0; k < i; k++) { + sway_log(SWAY_DEBUG, "spawn_swaybg cmd[%ld] = %s", k, cmd[k]); + } + + bool result = _spawn_swaybg(cmd); + free(cmd); + return result; +} diff --git a/sway/desktop/output.c b/sway/desktop/output.c index 9d0c0ef5d..0b3e1edb9 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -525,7 +525,6 @@ static void handle_destroy(struct wl_listener *listener, void *data) { wl_list_remove(&output->present.link); wl_list_remove(&output->damage_destroy.link); wl_list_remove(&output->damage_frame.link); - wl_list_remove(&output->swaybg_client_destroy.link); transaction_commit_dirty(); } @@ -632,7 +631,6 @@ void handle_new_output(struct wl_listener *listener, void *data) { output->damage_frame.notify = damage_handle_frame; wl_signal_add(&output->damage->events.destroy, &output->damage_destroy); output->damage_destroy.notify = damage_handle_destroy; - wl_list_init(&output->swaybg_client_destroy.link); struct output_config *oc = find_output_config(output); if (!oc || oc->enabled) { diff --git a/sway/tree/output.c b/sway/tree/output.c index b3589be5d..24adc08d6 100644 --- a/sway/tree/output.c +++ b/sway/tree/output.c @@ -262,10 +262,6 @@ void output_disable(struct sway_output *output) { root_for_each_container(untrack_output, output); - if (output->swaybg_client != NULL) { - wl_client_destroy(output->swaybg_client); - } - int index = list_find(root->outputs, output); list_del(root->outputs, index); diff --git a/swaybg/main.c b/swaybg/main.c index e66221f06..b983dd6a0 100644 --- a/swaybg/main.c +++ b/swaybg/main.c @@ -1,10 +1,12 @@ +#define _POSIX_C_SOURCE 200809L #include #include +#include #include #include #include #include -#include +#include #include #include "background-image.h" #include "cairo.h" @@ -14,49 +16,44 @@ #include "wlr-layer-shell-unstable-v1-client-protocol.h" #include "xdg-output-unstable-v1-client-protocol.h" -struct swaybg_state; - -struct swaybg_args { - const char *output; - const char *path; - enum background_mode mode; - const char *fallback; -}; - -struct swaybg_context { - uint32_t color; - cairo_surface_t *image; -}; - -struct swaybg_output { - struct wl_output *wl_output; - struct zxdg_output_v1 *xdg_output; - struct swaybg_state *state; - struct wl_list link; - - int32_t scale; -}; - struct swaybg_state { - const struct swaybg_args *args; - struct swaybg_context context; - struct wl_display *display; struct wl_compositor *compositor; struct wl_shm *shm; - struct wl_list outputs; struct zwlr_layer_shell_v1 *layer_shell; struct zxdg_output_manager_v1 *xdg_output_manager; - - struct swaybg_output *output; - struct wl_surface *surface; - struct wl_region *input_region; - struct zwlr_layer_surface_v1 *layer_surface; - + struct wl_list configs; // struct swaybg_output_config::link + struct wl_list outputs; // struct swaybg_output::link bool run_display; - uint32_t width, height; +}; + +struct swaybg_output_config { + char *output; + cairo_surface_t *image; + enum background_mode mode; + uint32_t color; + struct wl_list link; +}; + +struct swaybg_output { + uint32_t wl_name; + struct wl_output *wl_output; + struct zxdg_output_v1 *xdg_output; + char *name; + char *identifier; + + struct swaybg_state *state; + struct swaybg_output_config *config; + + struct wl_surface *surface; + struct zwlr_layer_surface_v1 *layer_surface; struct pool_buffer buffers[2]; struct pool_buffer *current_buffer; + + uint32_t width, height; + int32_t scale; + + struct wl_list link; }; bool is_valid_color(const char *color) { @@ -77,68 +74,82 @@ bool is_valid_color(const char *color) { return true; } -static void render_frame(struct swaybg_state *state) { - int buffer_width = state->width * state->output->scale, - buffer_height = state->height * state->output->scale; - state->current_buffer = get_next_buffer(state->shm, - state->buffers, buffer_width, buffer_height); - if (!state->current_buffer) { +static void render_frame(struct swaybg_output *output) { + int buffer_width = output->width * output->scale, + buffer_height = output->height * output->scale; + output->current_buffer = get_next_buffer(output->state->shm, + output->buffers, buffer_width, buffer_height); + if (!output->current_buffer) { return; } - cairo_t *cairo = state->current_buffer->cairo; + cairo_t *cairo = output->current_buffer->cairo; cairo_save(cairo); cairo_set_operator(cairo, CAIRO_OPERATOR_CLEAR); cairo_paint(cairo); cairo_restore(cairo); - if (state->args->mode == BACKGROUND_MODE_SOLID_COLOR) { - cairo_set_source_u32(cairo, state->context.color); + if (output->config->mode == BACKGROUND_MODE_SOLID_COLOR) { + cairo_set_source_u32(cairo, output->config->color); cairo_paint(cairo); } else { - if (state->args->fallback && state->context.color) { - cairo_set_source_u32(cairo, state->context.color); + if (output->config->color) { + cairo_set_source_u32(cairo, output->config->color); cairo_paint(cairo); } - render_background_image(cairo, state->context.image, - state->args->mode, buffer_width, buffer_height); + render_background_image(cairo, output->config->image, + output->config->mode, buffer_width, buffer_height); } - wl_surface_set_buffer_scale(state->surface, state->output->scale); - wl_surface_attach(state->surface, state->current_buffer->buffer, 0, 0); - wl_surface_damage_buffer(state->surface, 0, 0, INT32_MAX, INT32_MAX); - wl_surface_commit(state->surface); + wl_surface_set_buffer_scale(output->surface, output->scale); + wl_surface_attach(output->surface, output->current_buffer->buffer, 0, 0); + wl_surface_damage_buffer(output->surface, 0, 0, INT32_MAX, INT32_MAX); + wl_surface_commit(output->surface); } -static bool prepare_context(struct swaybg_state *state) { - if (state->args->mode == BACKGROUND_MODE_SOLID_COLOR) { - state->context.color = parse_color(state->args->path); - return is_valid_color(state->args->path); +static void destroy_swaybg_output_config(struct swaybg_output_config *config) { + if (!config) { + return; } - if (state->args->fallback && is_valid_color(state->args->fallback)) { - state->context.color = parse_color(state->args->fallback); + wl_list_remove(&config->link); + free(config->output); + free(config); +} + +static void destroy_swaybg_output(struct swaybg_output *output) { + if (!output) { + return; } - if (!(state->context.image = load_background_image(state->args->path))) { - return false; + wl_list_remove(&output->link); + if (output->layer_surface != NULL) { + zwlr_layer_surface_v1_destroy(output->layer_surface); } - return true; + if (output->surface != NULL) { + wl_surface_destroy(output->surface); + } + zxdg_output_v1_destroy(output->xdg_output); + wl_output_destroy(output->wl_output); + destroy_buffer(&output->buffers[0]); + destroy_buffer(&output->buffers[1]); + free(output->name); + free(output->identifier); + free(output); } static void layer_surface_configure(void *data, struct zwlr_layer_surface_v1 *surface, uint32_t serial, uint32_t width, uint32_t height) { - struct swaybg_state *state = data; - state->width = width; - state->height = height; + struct swaybg_output *output = data; + output->width = width; + output->height = height; zwlr_layer_surface_v1_ack_configure(surface, serial); - render_frame(state); + render_frame(output); } static void layer_surface_closed(void *data, struct zwlr_layer_surface_v1 *surface) { - struct swaybg_state *state = data; - zwlr_layer_surface_v1_destroy(state->layer_surface); - wl_surface_destroy(state->surface); - wl_region_destroy(state->input_region); - state->run_display = false; + struct swaybg_output *output = data; + sway_log(SWAY_DEBUG, "Destroying output %s (%s)", + output->name, output->identifier); + destroy_swaybg_output(output); } static const struct zwlr_layer_surface_v1_listener layer_surface_listener = { @@ -164,12 +175,9 @@ static void output_done(void *data, struct wl_output *output) { static void output_scale(void *data, struct wl_output *wl_output, int32_t scale) { struct swaybg_output *output = data; - struct swaybg_state *state = output->state; - output->scale = scale; - - if (state->output == output && state->run_display) { - render_frame(state); + if (output->state->run_display && output->width > 0 && output->height > 0) { + render_frame(output); } } @@ -190,24 +198,91 @@ static void xdg_output_handle_logical_size(void *data, // Who cares } +static void find_config(struct swaybg_output *output, const char *name) { + struct swaybg_output_config *config = NULL; + wl_list_for_each(config, &output->state->configs, link) { + if (strcmp(config->output, name) == 0) { + output->config = config; + return; + } else if (!output->config && strcmp(config->output, "*") == 0) { + output->config = config; + } + } +} + static void xdg_output_handle_name(void *data, struct zxdg_output_v1 *xdg_output, const char *name) { struct swaybg_output *output = data; - struct swaybg_state *state = output->state; - if (strcmp(name, state->args->output) == 0) { - assert(state->output == NULL); - state->output = output; + output->name = strdup(name); + + // If description was sent first, the config may already be populated. If + // there is an identifier config set, keep it. + if (!output->config || strcmp(output->config->output, "*") == 0) { + find_config(output, name); } } static void xdg_output_handle_description(void *data, struct zxdg_output_v1 *xdg_output, const char *description) { - // Who cares + struct swaybg_output *output = data; + + // wlroots currently sets the description to `make model serial (name)` + // If this changes in the future, this will need to be modified. + char *paren = strrchr(description, '('); + if (paren) { + size_t length = paren - description; + output->identifier = malloc(length); + if (!output->identifier) { + sway_log(SWAY_ERROR, "Failed to allocate output identifier"); + return; + } + strncpy(output->identifier, description, length); + output->identifier[length - 1] = '\0'; + + find_config(output, output->identifier); + } +} + +static void create_layer_surface(struct swaybg_output *output) { + output->surface = wl_compositor_create_surface(output->state->compositor); + assert(output->surface); + + // Empty input region + struct wl_region *input_region = + wl_compositor_create_region(output->state->compositor); + assert(input_region); + wl_surface_set_input_region(output->surface, input_region); + wl_region_destroy(input_region); + + output->layer_surface = zwlr_layer_shell_v1_get_layer_surface( + output->state->layer_shell, output->surface, output->wl_output, + ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND, "wallpaper"); + assert(output->layer_surface); + + zwlr_layer_surface_v1_set_size(output->layer_surface, 0, 0); + zwlr_layer_surface_v1_set_anchor(output->layer_surface, + ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | + ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT | + ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM | + ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT); + zwlr_layer_surface_v1_set_exclusive_zone(output->layer_surface, -1); + zwlr_layer_surface_v1_add_listener(output->layer_surface, + &layer_surface_listener, output); + wl_surface_commit(output->surface); } static void xdg_output_handle_done(void *data, struct zxdg_output_v1 *xdg_output) { - // Who cares + struct swaybg_output *output = data; + if (!output->config) { + sway_log(SWAY_DEBUG, "Could not find config for output %s (%s)", + output->name, output->identifier); + destroy_swaybg_output(output); + } else if (!output->layer_surface) { + sway_log(SWAY_DEBUG, "Found config %s for output %s (%s)", + output->config->output, output->name, output->identifier); + create_layer_surface(output); + } } static const struct zxdg_output_v1_listener xdg_output_listener = { @@ -229,10 +304,18 @@ static void handle_global(void *data, struct wl_registry *registry, } else if (strcmp(interface, wl_output_interface.name) == 0) { struct swaybg_output *output = calloc(1, sizeof(struct swaybg_output)); output->state = state; + output->wl_name = name; output->wl_output = wl_registry_bind(registry, name, &wl_output_interface, 3); wl_output_add_listener(output->wl_output, &output_listener, output); wl_list_insert(&state->outputs, &output->link); + + if (state->run_display) { + output->xdg_output = zxdg_output_manager_v1_get_xdg_output( + state->xdg_output_manager, output->wl_output); + zxdg_output_v1_add_listener(output->xdg_output, + &xdg_output_listener, output); + } } else if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) { state->layer_shell = wl_registry_bind(registry, name, &zwlr_layer_shell_v1_interface, 1); @@ -244,7 +327,16 @@ static void handle_global(void *data, struct wl_registry *registry, static void handle_global_remove(void *data, struct wl_registry *registry, uint32_t name) { - // who cares + struct swaybg_state *state = data; + struct swaybg_output *output, *tmp; + wl_list_for_each_safe(output, tmp, &state->outputs, link) { + if (output->wl_name == name) { + sway_log(SWAY_DEBUG, "Destroying output %s (%s)", + output->name, output->identifier); + destroy_swaybg_output(output); + break; + } + } } static const struct wl_registry_listener registry_listener = { @@ -252,32 +344,159 @@ static const struct wl_registry_listener registry_listener = { .global_remove = handle_global_remove, }; -int main(int argc, const char **argv) { +static bool store_swaybg_output_config(struct swaybg_state *state, + struct swaybg_output_config *config) { + struct swaybg_output_config *oc = NULL; + wl_list_for_each(oc, &state->configs, link) { + if (strcmp(config->output, oc->output) == 0) { + // Merge on top + if (config->image) { + free(oc->image); + oc->image = config->image; + config->image = NULL; + } + if (config->color) { + oc->color = config->color; + } + if (config->mode != BACKGROUND_MODE_INVALID) { + oc->mode = config->mode; + } + return false; + } + } + // New config, just add it + wl_list_insert(&state->configs, &config->link); + return true; +} + +static void parse_command_line(int argc, char **argv, + struct swaybg_state *state) { + static struct option long_options[] = { + {"color", required_argument, NULL, 'c'}, + {"help", no_argument, NULL, 'h'}, + {"image", required_argument, NULL, 'i'}, + {"mode", required_argument, NULL, 'm'}, + {"output", required_argument, NULL, 'o'}, + {"version", no_argument, NULL, 'v'}, + {0, 0, 0, 0} + }; + + const char *usage = + "Usage: swaybg \n" + "\n" + " -c, --color Set the background color.\n" + " -h, --help Show help message and quit.\n" + " -i, --image Set the image to display.\n" + " -m, --mode Set the mode to use for the image.\n" + " -o, --output Set the output to operate on or * for all.\n" + " -v, --version Show the version number and quit.\n" + "\n" + "Background Modes:\n" + " stretch, fit, fill, center, tile, or solid_color\n"; + + struct swaybg_output_config *config = NULL; + + int c; + while (1) { + int option_index = 0; + c = getopt_long(argc, argv, "c:hi:m:o:v", long_options, &option_index); + if (c == -1) { + break; + } + switch (c) { + case 'c': // color + if (!config) { + goto no_output; + } + if (!is_valid_color(optarg)) { + sway_log(SWAY_ERROR, "Invalid color: %s", optarg); + continue; + } + config->color = parse_color(optarg); + break; + case 'i': // image + if (!config) { + goto no_output; + } + free(config->image); + config->image = load_background_image(optarg); + if (!config->image) { + sway_log(SWAY_ERROR, "Failed to load image: %s", optarg); + } + break; + case 'm': // mode + if (!config) { + goto no_output; + } + config->mode = parse_background_mode(optarg); + if (config->mode == BACKGROUND_MODE_INVALID) { + sway_log(SWAY_ERROR, "Invalid mode: %s", optarg); + } + break; + case 'o': // output + if (config && !store_swaybg_output_config(state, config)) { + // Empty config or merged on top of an existing one + destroy_swaybg_output_config(config); + } + config = calloc(sizeof(struct swaybg_output_config), 1); + config->output = strdup(optarg); + config->mode = BACKGROUND_MODE_INVALID; + wl_list_init(&config->link); // init for safe removal + break; + case 'v': // version + fprintf(stdout, "swaybg version " SWAY_VERSION "\n"); + exit(EXIT_SUCCESS); + break; + default: + fprintf(c == 'h' ? stdout : stderr, "%s", usage); + exit(c == 'h' ? EXIT_SUCCESS : EXIT_FAILURE); + } + } + if (config && !store_swaybg_output_config(state, config)) { + // Empty config or merged on top of an existing one + destroy_swaybg_output_config(config); + } + + // Check for invalid options + if (optind < argc) { + config = NULL; + struct swaybg_output_config *tmp = NULL; + wl_list_for_each_safe(config, tmp, &state->configs, link) { + destroy_swaybg_output_config(config); + } + // continue into empty list + } + if (wl_list_empty(&state->configs)) { + fprintf(stderr, "%s", usage); + exit(EXIT_FAILURE); + } + + // Set default mode and remove empties + config = NULL; + struct swaybg_output_config *tmp = NULL; + wl_list_for_each_safe(config, tmp, &state->configs, link) { + if (!config->image && !config->color) { + destroy_swaybg_output_config(config); + } else if (config->mode == BACKGROUND_MODE_INVALID) { + config->mode = config->image + ? BACKGROUND_MODE_STRETCH + : BACKGROUND_MODE_SOLID_COLOR; + } + } + return; +no_output: + fprintf(stderr, "Cannot operate on NULL output config\n"); + exit(EXIT_FAILURE); +} + +int main(int argc, char **argv) { sway_log_init(SWAY_DEBUG, NULL); - struct swaybg_args args = {0}; - struct swaybg_state state = { .args = &args }; + struct swaybg_state state = {0}; + wl_list_init(&state.configs); wl_list_init(&state.outputs); - if (argc < 4 || argc > 5) { - sway_log(SWAY_ERROR, "Do not run this program manually. " - "See `man 5 sway-output` and look for background options."); - return 1; - } - - args.output = argv[1]; - args.path = argv[2]; - - args.mode = parse_background_mode(argv[3]); - if (args.mode == BACKGROUND_MODE_INVALID) { - return 1; - } - - args.fallback = argc == 5 ? argv[4] : NULL; - - if (!prepare_context(&state)) { - return 1; - } + parse_command_line(argc, argv, &state); state.display = wl_display_connect(NULL); if (!state.display) { @@ -303,42 +522,21 @@ int main(int argc, const char **argv) { zxdg_output_v1_add_listener(output->xdg_output, &xdg_output_listener, output); } - // Second roundtrip to get xdg_output properties - wl_display_roundtrip(state.display); - if (state.output == NULL) { - sway_log(SWAY_ERROR, "Cannot find output '%s'", args.output); - return 1; - } - - state.surface = wl_compositor_create_surface(state.compositor); - assert(state.surface); - - // Empty input region - state.input_region = wl_compositor_create_region(state.compositor); - assert(state.input_region); - wl_surface_set_input_region(state.surface, state.input_region); - - state.layer_surface = zwlr_layer_shell_v1_get_layer_surface( - state.layer_shell, state.surface, state.output->wl_output, - ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND, "wallpaper"); - assert(state.layer_surface); - - zwlr_layer_surface_v1_set_size(state.layer_surface, 0, 0); - zwlr_layer_surface_v1_set_anchor(state.layer_surface, - ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | - ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT | - ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM | - ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT); - zwlr_layer_surface_v1_set_exclusive_zone(state.layer_surface, -1); - zwlr_layer_surface_v1_add_listener(state.layer_surface, - &layer_surface_listener, &state); - wl_surface_commit(state.surface); - wl_display_roundtrip(state.display); state.run_display = true; while (wl_display_dispatch(state.display) != -1 && state.run_display) { // This space intentionally left blank } + struct swaybg_output *tmp_output; + wl_list_for_each_safe(output, tmp_output, &state.outputs, link) { + destroy_swaybg_output(output); + } + + struct swaybg_output_config *config = NULL, *tmp_config = NULL; + wl_list_for_each_safe(config, tmp_config, &state.configs, link) { + destroy_swaybg_output_config(config); + } + return 0; } From 0553e75b53137e6d509b6e336c21586f2b75d527 Mon Sep 17 00:00:00 2001 From: Dmitri Kourennyi Date: Fri, 22 Mar 2019 17:18:21 -0400 Subject: [PATCH 110/191] Implement handling of short_text field of i3 input protocol. Matches i3bar behavior of setting all blocks to use the short_text if the full text width does not fit. --- swaybar/render.c | 174 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 170 insertions(+), 4 deletions(-) diff --git a/swaybar/render.c b/swaybar/render.c index 116cc595b..faf175096 100644 --- a/swaybar/render.c +++ b/swaybar/render.c @@ -144,16 +144,21 @@ static void i3bar_block_unref_callback(void *data) { static uint32_t render_status_block(cairo_t *cairo, struct swaybar_output *output, struct i3bar_block *block, double *x, - bool edge) { + bool edge, bool use_short_text) { if (!block->full_text || !*block->full_text) { return 0; } + char* text = block->full_text; + if (use_short_text && block->short_text && *block->short_text) { + text = block->short_text; + } + struct swaybar_config *config = output->bar->config; int text_width, text_height; get_text_size(cairo, config->font, &text_width, &text_height, NULL, - output->scale, block->markup, "%s", block->full_text); + output->scale, block->markup, "%s", text); int margin = 3 * output->scale; double ws_vertical_padding = config->status_padding * output->scale; @@ -263,7 +268,7 @@ static uint32_t render_status_block(cairo_t *cairo, color = block->urgent ? config->colors.urgent_workspace.text : color; cairo_set_source_u32(cairo, color); pango_printf(cairo, config->font, output->scale, - block->markup, "%s", block->full_text); + block->markup, "%s", text); x_pos += width; if (block->border && block->border_right > 0) { @@ -294,13 +299,174 @@ static uint32_t render_status_block(cairo_t *cairo, return output->height; } +static void predict_status_block_pos(cairo_t *cairo, + struct swaybar_output *output, struct i3bar_block *block, double *x, + bool edge) { + if (!block->full_text || !*block->full_text) { + return; + } + + struct swaybar_config *config = output->bar->config; + + int text_width, text_height; + get_text_size(cairo, config->font, &text_width, &text_height, NULL, + output->scale, block->markup, "%s", block->full_text); + + int margin = 3 * output->scale; + double ws_vertical_padding = config->status_padding * output->scale; + + int width = text_width; + + if (block->min_width_str) { + int w; + get_text_size(cairo, config->font, &w, NULL, NULL, + output->scale, block->markup, "%s", block->min_width_str); + block->min_width = w; + } + if (width < block->min_width) { + width = block->min_width; + } + + double block_width = width; + uint32_t ideal_height = text_height + ws_vertical_padding * 2; + uint32_t ideal_surface_height = ideal_height / output->scale; + if (!output->bar->config->height && + output->height < ideal_surface_height) { + return; + } + + *x -= width; + if ((block->border || block->urgent) && block->border_left > 0) { + *x -= (block->border_left * output->scale + margin); + block_width += block->border_left * output->scale + margin; + } + if ((block->border || block->urgent) && block->border_right > 0) { + *x -= (block->border_right * output->scale + margin); + block_width += block->border_right * output->scale + margin; + } + + int sep_width, sep_height; + int sep_block_width = block->separator_block_width; + if (!edge) { + if (config->sep_symbol) { + get_text_size(cairo, config->font, &sep_width, &sep_height, NULL, + output->scale, false, "%s", config->sep_symbol); + uint32_t _ideal_height = sep_height + ws_vertical_padding * 2; + uint32_t _ideal_surface_height = _ideal_height / output->scale; + if (!output->bar->config->height && + output->height < _ideal_surface_height) { + return; + } + if (sep_width > sep_block_width) { + sep_block_width = sep_width + margin * 2; + } + } + *x -= sep_block_width; + } else if (config->status_edge_padding) { + *x -= config->status_edge_padding * output->scale; + } +} + +static double predict_status_line_pos(cairo_t *cairo, + struct swaybar_output *output, double x) { + bool edge = x == output->width * output->scale; + struct i3bar_block *block; + wl_list_for_each(block, &output->bar->status->blocks, link) { + predict_status_block_pos(cairo, output, block, &x, edge); + edge = false; + } + return x; +} + +static uint32_t predict_workspace_button_length(cairo_t *cairo, + struct swaybar_output *output, + struct swaybar_workspace *ws) { + struct swaybar_config *config = output->bar->config; + + int text_width, text_height; + get_text_size(cairo, config->font, &text_width, &text_height, NULL, + output->scale, config->pango_markup, "%s", ws->label); + + int ws_vertical_padding = WS_VERTICAL_PADDING * output->scale; + int ws_horizontal_padding = WS_HORIZONTAL_PADDING * output->scale; + int border_width = BORDER_WIDTH * output->scale; + + uint32_t ideal_height = ws_vertical_padding * 2 + text_height + + border_width * 2; + uint32_t ideal_surface_height = ideal_height / output->scale; + if (!output->bar->config->height && + output->height < ideal_surface_height) { + return 0; + } + + return ws_horizontal_padding * 2 + text_width + border_width * 2; +} + +static uint32_t predict_workspace_buttons_length(cairo_t *cairo, + struct swaybar_output *output) { + uint32_t width = 0; + if (output->bar->config->workspace_buttons) { + struct swaybar_workspace *ws; + wl_list_for_each(ws, &output->workspaces, link) { + width += predict_workspace_button_length(cairo, output, ws); + } + } + return width; +} + +static uint32_t predict_binding_mode_indicator_length(cairo_t *cairo, + struct swaybar_output *output) { + const char *mode = output->bar->mode; + if (!mode) { + return 0; + } + + struct swaybar_config *config = output->bar->config; + + if (!config->binding_mode_indicator) { + return 0; + } + + int text_width, text_height; + get_text_size(cairo, config->font, &text_width, &text_height, NULL, + output->scale, output->bar->mode_pango_markup, + "%s", mode); + + int ws_vertical_padding = WS_VERTICAL_PADDING * output->scale; + int ws_horizontal_padding = WS_HORIZONTAL_PADDING * output->scale; + int border_width = BORDER_WIDTH * output->scale; + + uint32_t ideal_height = text_height + ws_vertical_padding * 2 + + border_width * 2; + uint32_t ideal_surface_height = ideal_height / output->scale; + if (!output->bar->config->height && + output->height < ideal_surface_height) { + return 0; + } + return text_width + ws_horizontal_padding * 2 + border_width * 2; +} + static uint32_t render_status_line_i3bar(cairo_t *cairo, struct swaybar_output *output, double *x) { uint32_t max_height = 0; bool edge = *x == output->width * output->scale; struct i3bar_block *block; + bool use_short_text = false; + + // TODO: Add margin here? + uint32_t reserved_width = predict_workspace_buttons_length(cairo, output) + + predict_binding_mode_indicator_length(cairo, output); + + uint32_t predicted_full_pos = + predict_status_line_pos(cairo, output, *x); + + if (predicted_full_pos < reserved_width) { + use_short_text = true; + } + wl_list_for_each(block, &output->bar->status->blocks, link) { - uint32_t h = render_status_block(cairo, output, block, x, edge); + uint32_t h = render_status_block(cairo, output, block, x, edge, + use_short_text); max_height = h > max_height ? h : max_height; edge = false; } From 94dc57f3c5e147ebcb378104d51352c33f2fa9da Mon Sep 17 00:00:00 2001 From: Dmitri Kourennyi Date: Mon, 1 Apr 2019 16:12:42 -0400 Subject: [PATCH 111/191] Ensure predicted position for short text handling doesn't overflow. - Predicted status line can be negative, so corresponding variables should not be unsigned. Changed to double as position is actually calculated as double. --- swaybar/render.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/swaybar/render.c b/swaybar/render.c index faf175096..a0200f0d8 100644 --- a/swaybar/render.c +++ b/swaybar/render.c @@ -454,10 +454,11 @@ static uint32_t render_status_line_i3bar(cairo_t *cairo, bool use_short_text = false; // TODO: Add margin here? - uint32_t reserved_width = predict_workspace_buttons_length(cairo, output) + + double reserved_width = + predict_workspace_buttons_length(cairo, output) + predict_binding_mode_indicator_length(cairo, output); - uint32_t predicted_full_pos = + double predicted_full_pos = predict_status_line_pos(cairo, output, *x); if (predicted_full_pos < reserved_width) { From 4b892a79aae47ef2a7a4776e959eca612e0df08b Mon Sep 17 00:00:00 2001 From: Dmitri Kourennyi Date: Mon, 1 Apr 2019 16:13:39 -0400 Subject: [PATCH 112/191] Add margin to short_text handling. Add a 3xscale margin matching other spacing in swaybar as part of short text width calculations --- swaybar/render.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/swaybar/render.c b/swaybar/render.c index a0200f0d8..e17c13a88 100644 --- a/swaybar/render.c +++ b/swaybar/render.c @@ -453,10 +453,10 @@ static uint32_t render_status_line_i3bar(cairo_t *cairo, struct i3bar_block *block; bool use_short_text = false; - // TODO: Add margin here? double reserved_width = predict_workspace_buttons_length(cairo, output) + - predict_binding_mode_indicator_length(cairo, output); + predict_binding_mode_indicator_length(cairo, output) + + 3 * output->scale; // require a bit of space for margin double predicted_full_pos = predict_status_line_pos(cairo, output, *x); From e0324fc88c4a9041e8c97bdf99ee33eb198c1310 Mon Sep 17 00:00:00 2001 From: Jan Beich Date: Mon, 8 Apr 2019 19:18:42 +0000 Subject: [PATCH 113/191] config/output: unbreak on 32-bit architectures sway/config/output.c:624:54: error: format specifies type 'long' but the argument has type 'size_t' (aka 'unsigned int') [-Werror,-Wformat] sway_log(SWAY_DEBUG, "spawn_swaybg cmd[%ld] = %s", k, cmd[k]); ~~~ ^ %zu include/log.h:40:74: note: expanded from macro 'sway_log' _sway_log(verb, "[%s:%d] " fmt, _sway_strip_path(__FILE__), __LINE__, ##__VA_ARGS__) ^~~~~~~~~~~ --- sway/config/output.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sway/config/output.c b/sway/config/output.c index 0473d0ad5..747ab28b7 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -621,7 +621,7 @@ bool spawn_swaybg(void) { } for (size_t k = 0; k < i; k++) { - sway_log(SWAY_DEBUG, "spawn_swaybg cmd[%ld] = %s", k, cmd[k]); + sway_log(SWAY_DEBUG, "spawn_swaybg cmd[%zd] = %s", k, cmd[k]); } bool result = _spawn_swaybg(cmd); From 195226120fcf4854c90e544a6e7e0ec0b1c3312c Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Sun, 24 Mar 2019 23:24:02 -0400 Subject: [PATCH 114/191] Honor output for xdg_toplevel_set_fullscreen This honors the fullscreen output request for `xdg_toplevel_set_fullscreen` and `zxdg_toplevel_v6_set_fullscreen`. If the request was sent before mapping, the fullscreen output request will be retrieved from the client_pending state for the toplevel. The output will be passed to `view_map` and if there is a workspace on the output, the view will be placed on that workspace. If the request comes in after being mapped, the view will be moved to the workspace on the output (if there is one) before becoming fullscreen. --- include/sway/tree/view.h | 2 +- sway/desktop/xdg_shell.c | 16 +++++++++++++++- sway/desktop/xdg_shell_v6.c | 16 +++++++++++++++- sway/desktop/xwayland.c | 2 +- sway/tree/view.c | 16 ++++++++++++++-- 5 files changed, 46 insertions(+), 6 deletions(-) diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h index ac203ac79..bdd8960cd 100644 --- a/include/sway/tree/view.h +++ b/include/sway/tree/view.h @@ -314,7 +314,7 @@ void view_destroy(struct sway_view *view); void view_begin_destroy(struct sway_view *view); void view_map(struct sway_view *view, struct wlr_surface *wlr_surface, - bool fullscreen, bool decoration); + bool fullscreen, struct wlr_output *fullscreen_output, bool decoration); void view_unmap(struct sway_view *view); diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c index ea5dcd16c..9e914f144 100644 --- a/sway/desktop/xdg_shell.c +++ b/sway/desktop/xdg_shell.c @@ -339,6 +339,18 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data) return; } + if (e->fullscreen && e->output && e->output->data) { + struct sway_output *output = e->output->data; + struct sway_workspace *ws = output_get_active_workspace(output); + if (ws && !container_is_scratchpad_hidden(view->container)) { + if (container_is_floating(view->container)) { + workspace_add_floating(ws, view->container); + } else { + workspace_add_tiling(ws, view->container); + } + } + } + container_set_fullscreen(view->container, e->fullscreen); arrange_root(); @@ -417,7 +429,9 @@ static void handle_map(struct wl_listener *listener, void *data) { } view_map(view, view->wlr_xdg_surface->surface, - xdg_surface->toplevel->client_pending.fullscreen, csd); + xdg_surface->toplevel->client_pending.fullscreen, + xdg_surface->toplevel->client_pending.fullscreen_output, + csd); transaction_commit_dirty(); diff --git a/sway/desktop/xdg_shell_v6.c b/sway/desktop/xdg_shell_v6.c index 7ff4c4de8..5ac589cfd 100644 --- a/sway/desktop/xdg_shell_v6.c +++ b/sway/desktop/xdg_shell_v6.c @@ -339,6 +339,18 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data) return; } + if (e->fullscreen && e->output && e->output->data) { + struct sway_output *output = e->output->data; + struct sway_workspace *ws = output_get_active_workspace(output); + if (ws && !container_is_scratchpad_hidden(view->container)) { + if (container_is_floating(view->container)) { + workspace_add_floating(ws, view->container); + } else { + workspace_add_tiling(ws, view->container); + } + } + } + container_set_fullscreen(view->container, e->fullscreen); arrange_root(); @@ -411,7 +423,9 @@ static void handle_map(struct wl_listener *listener, void *data) { == WLR_SERVER_DECORATION_MANAGER_MODE_CLIENT; view_map(view, view->wlr_xdg_surface_v6->surface, - xdg_surface->toplevel->client_pending.fullscreen, csd); + xdg_surface->toplevel->client_pending.fullscreen, + xdg_surface->toplevel->client_pending.fullscreen_output, + csd); transaction_commit_dirty(); diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c index 37d0b986a..f6ca8f818 100644 --- a/sway/desktop/xwayland.c +++ b/sway/desktop/xwayland.c @@ -415,7 +415,7 @@ static void handle_map(struct wl_listener *listener, void *data) { xwayland_view->commit.notify = handle_commit; // Put it back into the tree - view_map(view, xsurface->surface, xsurface->fullscreen, false); + view_map(view, xsurface->surface, xsurface->fullscreen, NULL, false); transaction_commit_dirty(); } diff --git a/sway/tree/view.c b/sway/tree/view.c index 4759c9982..2c8839f59 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -561,14 +561,26 @@ static bool should_focus(struct sway_view *view) { } void view_map(struct sway_view *view, struct wlr_surface *wlr_surface, - bool fullscreen, bool decoration) { + bool fullscreen, struct wlr_output *fullscreen_output, + bool decoration) { if (!sway_assert(view->surface == NULL, "cannot map mapped view")) { return; } view->surface = wlr_surface; + // If there is a request to be opened fullscreen on a specific output, try + // to honor that request. Otherwise, fallback to assigns, pid mappings, + // focused workspace, etc + struct sway_workspace *ws = NULL; + if (fullscreen_output && fullscreen_output->data) { + struct sway_output *output = fullscreen_output->data; + ws = output_get_active_workspace(output); + } + if (!ws) { + ws = select_workspace(view); + } + struct sway_seat *seat = input_manager_current_seat(); - struct sway_workspace *ws = select_workspace(view); struct sway_node *node = seat_get_focus_inactive(seat, &ws->node); struct sway_container *target_sibling = node->type == N_CONTAINER ? node->sway_container : NULL; From bd4c3957bab5837a9a0b4b8d4690d6b50ba5763e Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Wed, 10 Apr 2019 23:31:50 -0400 Subject: [PATCH 115/191] cmd_focus: raise floating for ` focus` Floaters are currently raised for `focus `. This extends the same functionality to ` focus`. --- sway/commands/focus.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sway/commands/focus.c b/sway/commands/focus.c index 33d014050..14e909551 100644 --- a/sway/commands/focus.c +++ b/sway/commands/focus.c @@ -317,6 +317,7 @@ struct cmd_results *cmd_focus(int argc, char **argv) { } seat_set_focus_container(seat, container); seat_consider_warp_to_focus(seat); + container_raise_floating(container); return cmd_results_new(CMD_SUCCESS, NULL); } From 090e1015812e8b26d008fb570152804b506b48f7 Mon Sep 17 00:00:00 2001 From: Martin Michlmayr Date: Sat, 13 Apr 2019 01:39:14 +0700 Subject: [PATCH 116/191] sway-input.5: add xkeyboard-config(7) to "see also" Since xkeyboard-config(7) is mentioned in the man page, add a reference to the "see also" section. --- sway/sway-input.5.scd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sway/sway-input.5.scd b/sway/sway-input.5.scd index 8c3a8225f..1a8062fb9 100644 --- a/sway/sway-input.5.scd +++ b/sway/sway-input.5.scd @@ -193,4 +193,4 @@ correct seat. # SEE ALSO -*sway*(5) *sway-output*(5) +*sway*(5) *sway-output*(5) *xkeyboard-config*(7) From 27f51360b7cabc990258544ed6a74013db76e9df Mon Sep 17 00:00:00 2001 From: Ashkan Kiani Date: Fri, 12 Apr 2019 00:54:09 -0700 Subject: [PATCH 117/191] Fix scratchpad logic for floating windows When a tiled window is sent to the scratchpad, we want to use sane defaults, which is to center it and resize it to the default. For floating windows, we want to use their existing geometry. --- sway/tree/root.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/sway/tree/root.c b/sway/tree/root.c index c7c5e1df3..a9d306a43 100644 --- a/sway/tree/root.c +++ b/sway/tree/root.c @@ -61,9 +61,14 @@ void root_scratchpad_add_container(struct sway_container *con) { struct sway_container *parent = con->parent; struct sway_workspace *workspace = con->workspace; - container_set_floating(con, true); - container_floating_set_default_size(con); - container_floating_move_to_center(con); + + // When a tiled window is sent to scratchpad, center and resize it. + if (!container_is_floating(con)) { + container_set_floating(con, true); + container_floating_set_default_size(con); + container_floating_move_to_center(con); + } + container_detach(con); con->scratchpad = true; list_add(root->scratchpad, con); From e7d6b8ec3e88564617c6c05656548a37b5dc6d27 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sat, 13 Apr 2019 11:41:40 +0300 Subject: [PATCH 118/191] swaybg: add manpage --- meson.build | 1 + swaybg/swaybg.1.scd | 44 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+) create mode 100644 swaybg/swaybg.1.scd diff --git a/meson.build b/meson.build index 02b5d606c..3ed0c5515 100644 --- a/meson.build +++ b/meson.build @@ -107,6 +107,7 @@ if scdoc.found() 'sway/sway-input.5.scd', 'sway/sway-ipc.7.scd', 'sway/sway-output.5.scd', + 'swaybg/swaybg.1.scd', 'swaymsg/swaymsg.1.scd', 'swaynag/swaynag.1.scd', 'swaynag/swaynag.5.scd', diff --git a/swaybg/swaybg.1.scd b/swaybg/swaybg.1.scd new file mode 100644 index 000000000..027e5d631 --- /dev/null +++ b/swaybg/swaybg.1.scd @@ -0,0 +1,44 @@ +swaybg(1) + +# NAME + +swaybg - Background for Wayland + +# SYNOPSIS + +*swaybg* [options...] + +Displays a background image on all outputs of your Wayland session. + +Without an output specified, appearance options apply to all outputs. +Per-output appearance options can be set by passing _-o, --output_ followed by +these options. + +# OPTIONS + +*-c, --color* + Set the background color. + +*-h, --help* + Show help message and quit. + +*-i, --image* + Set the background image. + +*-m, --mode* + Scaling mode for images: _stretch_, _fill_, _fit_, _center_, or _tile_. Use + the additional mode _solid\_color_ to display only the background color, + even if a background image is specified. + +*-o, --output* + Select an output to configure. Subsequent appearance options will only + apply to this output. The special value _\*_ selects all outputs. + +*-v, --version* + Show the version number and quit. + +# AUTHORS + +Maintained by Drew DeVault , who is assisted by other open +source contributors. For more information about swaybg development, see +https://github.com/swaywm/sway. From 913445e112b3ceca4ece731a6e57b19cab9d0c6a Mon Sep 17 00:00:00 2001 From: Ashkan Kiani Date: Sat, 13 Apr 2019 03:33:07 -0700 Subject: [PATCH 119/191] Fix potential null accesses --- sway/commands/move.c | 90 +++++++++++++++++++++++--------------------- sway/input/seat.c | 12 +++--- 2 files changed, 54 insertions(+), 48 deletions(-) diff --git a/sway/commands/move.c b/sway/commands/move.c index 926b2e8e9..f642f0231 100644 --- a/sway/commands/move.c +++ b/sway/commands/move.c @@ -311,37 +311,39 @@ static bool container_move_in_direction(struct sway_container *container, while (current) { list_t *siblings = container_get_siblings(current); - enum sway_container_layout layout = container_parent_layout(current); - int index = list_find(siblings, current); - int desired = index + offs; + if (siblings) { + enum sway_container_layout layout = container_parent_layout(current); + int index = list_find(siblings, current); + int desired = index + offs; - // Don't allow containers to move out of their - // fullscreen or floating parent - if (current->fullscreen_mode || container_is_floating(current)) { - return false; - } + // Don't allow containers to move out of their + // fullscreen or floating parent + if (current->fullscreen_mode || container_is_floating(current)) { + return false; + } - if (is_parallel(layout, move_dir)) { - if (desired == -1 || desired == siblings->length) { - if (current->parent == container->parent) { - current = current->parent; - continue; - } else { - // Reparenting - if (current->parent) { - container_insert_child(current->parent, container, - index + (offs < 0 ? 0 : 1)); + if (is_parallel(layout, move_dir)) { + if (desired == -1 || desired == siblings->length) { + if (current->parent == container->parent) { + current = current->parent; + continue; } else { - workspace_insert_tiling(current->workspace, container, - index + (offs < 0 ? 0 : 1)); + // Reparenting + if (current->parent) { + container_insert_child(current->parent, container, + index + (offs < 0 ? 0 : 1)); + } else { + workspace_insert_tiling(current->workspace, container, + index + (offs < 0 ? 0 : 1)); + } + return true; } + } else { + // Container can move within its siblings + container_move_to_container_from_direction(container, + siblings->items[desired], move_dir); return true; } - } else { - // Container can move within its siblings - container_move_to_container_from_direction(container, - siblings->items[desired], move_dir); - return true; } } @@ -350,26 +352,28 @@ static bool container_move_in_direction(struct sway_container *container, // Maybe rejigger the workspace struct sway_workspace *ws = container->workspace; - if (!is_parallel(ws->layout, move_dir)) { - workspace_rejigger(ws, container, move_dir); - return true; - } else if (ws->layout == L_TABBED || ws->layout == L_STACKED) { - workspace_rejigger(ws, container, move_dir); - return true; - } - - // Try adjacent output - struct sway_output *output = - output_get_in_direction(container->workspace->output, move_dir); - if (output) { - struct sway_workspace *ws = output_get_active_workspace(output); - if (!sway_assert(ws, "Expected output to have a workspace")) { - return false; + if (ws) { + if (!is_parallel(ws->layout, move_dir)) { + workspace_rejigger(ws, container, move_dir); + return true; + } else if (ws->layout == L_TABBED || ws->layout == L_STACKED) { + workspace_rejigger(ws, container, move_dir); + return true; } - container_move_to_workspace_from_direction(container, ws, move_dir); - return true; + + // Try adjacent output + struct sway_output *output = + output_get_in_direction(container->workspace->output, move_dir); + if (output) { + struct sway_workspace *ws = output_get_active_workspace(output); + if (!sway_assert(ws, "Expected output to have a workspace")) { + return false; + } + container_move_to_workspace_from_direction(container, ws, move_dir); + return true; + } + sway_log(SWAY_DEBUG, "Hit edge of output, nowhere else to go"); } - sway_log(SWAY_DEBUG, "Hit edge of output, nowhere else to go"); return false; } diff --git a/sway/input/seat.c b/sway/input/seat.c index d58ff9e6f..bdab8b81c 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -1194,11 +1194,13 @@ void seat_consider_warp_to_focus(struct sway_seat *seat) { } if (config->mouse_warping == WARP_OUTPUT) { struct sway_output *output = node_get_output(focus); - struct wlr_box box; - output_get_box(output, &box); - if (wlr_box_contains_point(&box, - seat->cursor->cursor->x, seat->cursor->cursor->y)) { - return; + if (output) { + struct wlr_box box; + output_get_box(output, &box); + if (wlr_box_contains_point(&box, + seat->cursor->cursor->x, seat->cursor->cursor->y)) { + return; + } } } From 69a1a0ff99171f15c7842bfde23ed90f09a37256 Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Sun, 31 Mar 2019 23:27:18 -0400 Subject: [PATCH 120/191] Fix scratchpad fullscreen behavior and crash When setting fullscreen on a hidden scratchpad container, there was a check to see if there was an existing fullscreen container on the workspace so it could be fullscreen disabled first. Since the workspace is NULL, it would cause a SIGSEGV. This adds a NULL check to avoid the crash. This also changes the behavior of how fullscreen is handled when adding a container to the scratchpad or changing visibility of a scratchpad container to match i3's. The behavior is as follows: - When adding a container to the scratchpad or hiding a container back into the scratchpad, there is an implicit fullscreen disable - When setting fullscreen on a container that is hidden in the scratchpad, it will be fullscreen when shown (and fullscreen disabled when hidden as stated above) - When setting fullscreen global on a container that is hidden in the scratchpad, it will be shown immediately as fullscreen global. The container is not moved to a workspace and remains in the scratchpad. The container will be visible until fullscreen disabled or killed. Since the container is in the scratchpad, running `scratchpad show` or `move container to scratchpad` will have no effect This also changes `container_replace` to transfer fullscreen and scratchpad status. --- include/sway/input/seat.h | 5 +++ sway/commands/floating.c | 5 +++ sway/commands/focus.c | 4 ++ sway/commands/fullscreen.c | 7 +++ sway/commands/layout.c | 6 ++- sway/commands/move.c | 9 ++++ sway/commands/split.c | 3 +- sway/desktop/output.c | 9 ++-- sway/desktop/render.c | 3 -- sway/input/seat.c | 50 ++++++++++++++++++--- sway/tree/container.c | 92 ++++++++++++++++++++++++++++++-------- sway/tree/node.c | 8 ++++ sway/tree/root.c | 14 ++++++ sway/tree/view.c | 62 ++++++++++++++++--------- 14 files changed, 220 insertions(+), 57 deletions(-) diff --git a/include/sway/input/seat.h b/include/sway/input/seat.h index f2af1066b..eb6c09a1d 100644 --- a/include/sway/input/seat.h +++ b/include/sway/input/seat.h @@ -143,6 +143,11 @@ struct sway_node *seat_get_focus(struct sway_seat *seat); struct sway_workspace *seat_get_focused_workspace(struct sway_seat *seat); +// If a scratchpad container is fullscreen global, this can be used to try to +// determine the last focused workspace. Otherwise, this should yield the same +// results as seat_get_focused_workspace. +struct sway_workspace *seat_get_last_known_workspace(struct sway_seat *seat); + struct sway_container *seat_get_focused_container(struct sway_seat *seat); /** diff --git a/sway/commands/floating.c b/sway/commands/floating.c index 5df9b1bfb..ce1233454 100644 --- a/sway/commands/floating.c +++ b/sway/commands/floating.c @@ -32,6 +32,11 @@ struct cmd_results *cmd_floating(int argc, char **argv) { seat_set_focus_container(config->handler_context.seat, container); } + if (container_is_scratchpad_hidden(container)) { + return cmd_results_new(CMD_INVALID, + "Can't change floating on hidden scratchpad container"); + } + // If the container is in a floating split container, // operate on the split container instead of the child. if (container_is_floating_or_child(container)) { diff --git a/sway/commands/focus.c b/sway/commands/focus.c index 14e909551..8baa616dd 100644 --- a/sway/commands/focus.c +++ b/sway/commands/focus.c @@ -182,6 +182,10 @@ static struct sway_node *node_get_in_direction_floating( double closest_distance = DBL_MAX; struct sway_container *closest_con = NULL; + if (!con->workspace) { + return NULL; + } + for (int i = 0; i < con->workspace->floating->length; i++) { struct sway_container *floater = con->workspace->floating->items[i]; if (floater == con) { diff --git a/sway/commands/fullscreen.c b/sway/commands/fullscreen.c index 52248ce4a..a268ba034 100644 --- a/sway/commands/fullscreen.c +++ b/sway/commands/fullscreen.c @@ -26,6 +26,13 @@ struct cmd_results *cmd_fullscreen(int argc, char **argv) { "Can't fullscreen an empty workspace"); } + // If in the scratchpad, operate on the highest container + if (container && !container->workspace) { + while (container->parent) { + container = container->parent; + } + } + bool is_fullscreen = container && container->fullscreen_mode != FULLSCREEN_NONE; bool global = false; diff --git a/sway/commands/layout.c b/sway/commands/layout.c index 7d61c3be3..32f8fb520 100644 --- a/sway/commands/layout.c +++ b/sway/commands/layout.c @@ -147,7 +147,11 @@ struct cmd_results *cmd_layout(int argc, char **argv) { workspace->layout = new_layout; workspace_update_representation(workspace); } - arrange_workspace(workspace); + if (root->fullscreen_global) { + arrange_root(); + } else { + arrange_workspace(workspace); + } } return cmd_results_new(CMD_SUCCESS, NULL); diff --git a/sway/commands/move.c b/sway/commands/move.c index f642f0231..4ebc949bf 100644 --- a/sway/commands/move.c +++ b/sway/commands/move.c @@ -395,6 +395,11 @@ static struct cmd_results *cmd_move_container(int argc, char **argv) { container = workspace_wrap_children(workspace); } + if (container->fullscreen_mode == FULLSCREEN_GLOBAL) { + return cmd_results_new(CMD_FAILURE, + "Can't move fullscreen global container"); + } + bool no_auto_back_and_forth = false; while (strcasecmp(argv[0], "--no-auto-back-and-forth") == 0) { no_auto_back_and_forth = true; @@ -646,6 +651,10 @@ static struct cmd_results *cmd_move_workspace(int argc, char **argv) { } struct sway_workspace *workspace = config->handler_context.workspace; + if (!workspace) { + return cmd_results_new(CMD_FAILURE, "No workspace to move"); + } + struct sway_output *old_output = workspace->output; int center_x = workspace->width / 2 + workspace->x, center_y = workspace->height / 2 + workspace->y; diff --git a/sway/commands/split.c b/sway/commands/split.c index e96707228..8702f39e6 100644 --- a/sway/commands/split.c +++ b/sway/commands/split.c @@ -13,7 +13,8 @@ static struct cmd_results *do_split(int layout) { struct sway_container *con = config->handler_context.container; struct sway_workspace *ws = config->handler_context.workspace; if (con) { - if (container_is_scratchpad_hidden(con)) { + if (container_is_scratchpad_hidden(con) && + con->fullscreen_mode != FULLSCREEN_GLOBAL) { return cmd_results_new(CMD_FAILURE, "Cannot split a hidden scratchpad container"); } diff --git a/sway/desktop/output.c b/sway/desktop/output.c index 0b3e1edb9..b1e3464a7 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -251,14 +251,11 @@ static void output_for_each_surface(struct sway_output *output, }; struct sway_workspace *workspace = output_get_active_workspace(output); - if (!workspace) { - return; - } struct sway_container *fullscreen_con = root->fullscreen_global; - if (fullscreen_con && container_is_scratchpad_hidden(fullscreen_con)) { - fullscreen_con = NULL; - } if (!fullscreen_con) { + if (!workspace) { + return; + } fullscreen_con = workspace->current.fullscreen; } if (fullscreen_con) { diff --git a/sway/desktop/render.c b/sway/desktop/render.c index 7216e30bb..771bd9082 100644 --- a/sway/desktop/render.c +++ b/sway/desktop/render.c @@ -997,9 +997,6 @@ void output_render(struct sway_output *output, struct timespec *when, } struct sway_container *fullscreen_con = root->fullscreen_global; - if (fullscreen_con && container_is_scratchpad_hidden(fullscreen_con)) { - fullscreen_con = NULL; - } if (!fullscreen_con) { fullscreen_con = workspace->current.fullscreen; } diff --git a/sway/input/seat.c b/sway/input/seat.c index bdab8b81c..ce009d7e4 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -172,14 +172,14 @@ static void handle_seat_node_destroy(struct wl_listener *listener, void *data) { seat_node_destroy(seat_node); - if (!parent) { + if (!parent && !needs_new_focus) { // Destroying a container that is no longer in the tree return; } // Find new focus_inactive (ie. sibling, or workspace if no siblings left) struct sway_node *next_focus = NULL; - while (next_focus == NULL) { + while (next_focus == NULL && parent != NULL) { struct sway_container *con = seat_get_focus_inactive_view(seat, parent); next_focus = con ? &con->node : NULL; @@ -192,6 +192,16 @@ static void handle_seat_node_destroy(struct wl_listener *listener, void *data) { parent = node_get_parent(parent); } + if (!next_focus) { + struct sway_workspace *ws = seat_get_last_known_workspace(seat); + if (!ws) { + return; + } + struct sway_container *con = + seat_get_focus_inactive_view(seat, &ws->node); + next_focus = con ? &(con->node) : &(ws->node); + } + if (next_focus->type == N_WORKSPACE && !workspace_is_visible(next_focus->sway_workspace)) { // Do not change focus to a non-visible workspace @@ -199,6 +209,10 @@ static void handle_seat_node_destroy(struct wl_listener *listener, void *data) { } if (needs_new_focus) { + // Make sure the workspace IPC event gets sent + if (node->type == N_CONTAINER && node->sway_container->scratchpad) { + seat_set_focus(seat, NULL); + } // The structure change might have caused it to move up to the top of // the focus stack without sending focus notifications to the view seat_send_focus(next_focus, seat); @@ -207,7 +221,7 @@ static void handle_seat_node_destroy(struct wl_listener *listener, void *data) { // Setting focus_inactive focus = seat_get_focus_inactive(seat, &root->node); seat_set_raw_focus(seat, next_focus); - if (focus->type == N_CONTAINER) { + if (focus->type == N_CONTAINER && focus->sway_container->workspace) { seat_set_raw_focus(seat, &focus->sway_container->workspace->node); } seat_set_raw_focus(seat, focus); @@ -795,7 +809,13 @@ void seat_set_raw_focus(struct sway_seat *seat, struct sway_node *node) { wl_list_remove(&seat_node->link); wl_list_insert(&seat->focus_stack, &seat_node->link); node_set_dirty(node); - node_set_dirty(node_get_parent(node)); + + // If focusing a scratchpad container that is fullscreen global, parent + // will be NULL + struct sway_node *parent = node_get_parent(node); + if (parent) { + node_set_dirty(parent); + } } void seat_set_focus(struct sway_seat *seat, struct sway_node *node) { @@ -850,7 +870,8 @@ void seat_set_focus(struct sway_seat *seat, struct sway_node *node) { } } - struct sway_output *new_output = new_workspace->output; + struct sway_output *new_output = + new_workspace ? new_workspace->output : NULL; if (last_workspace != new_workspace && new_output) { node_set_dirty(&new_output->node); @@ -894,7 +915,8 @@ void seat_set_focus(struct sway_seat *seat, struct sway_node *node) { } // Move sticky containers to new workspace - if (new_output_last_ws && new_workspace != new_output_last_ws) { + if (new_workspace && new_output_last_ws + && new_workspace != new_output_last_ws) { for (int i = 0; i < new_output_last_ws->floating->length; ++i) { struct sway_container *floater = new_output_last_ws->floating->items[i]; @@ -940,7 +962,7 @@ void seat_set_focus(struct sway_seat *seat, struct sway_node *node) { seat->has_focus = true; - if (config->smart_gaps) { + if (config->smart_gaps && new_workspace) { // When smart gaps is on, gaps may change when the focus changes so // the workspace needs to be arranged arrange_workspace(new_workspace); @@ -1134,6 +1156,20 @@ struct sway_workspace *seat_get_focused_workspace(struct sway_seat *seat) { return NULL; // output doesn't have a workspace yet } +struct sway_workspace *seat_get_last_known_workspace(struct sway_seat *seat) { + struct sway_seat_node *current; + wl_list_for_each(current, &seat->focus_stack, link) { + struct sway_node *node = current->node; + if (node->type == N_CONTAINER && + node->sway_container->workspace) { + return node->sway_container->workspace; + } else if (node->type == N_WORKSPACE) { + return node->sway_workspace; + } + } + return NULL; +} + struct sway_container *seat_get_focused_container(struct sway_seat *seat) { struct sway_node *focus = seat_get_focus(seat); if (focus && focus->type == N_CONTAINER) { diff --git a/sway/tree/container.c b/sway/tree/container.c index 02b4d1b0e..11ed4f98d 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -95,6 +95,10 @@ void container_begin_destroy(struct sway_container *con) { if (con->fullscreen_mode == FULLSCREEN_WORKSPACE && con->workspace) { con->workspace->fullscreen = NULL; } + if (con->scratchpad && con->fullscreen_mode == FULLSCREEN_GLOBAL) { + container_fullscreen_disable(con); + } + wl_signal_emit(&con->node.events.destroy, &con->node); container_end_mouse_operation(con); @@ -128,7 +132,9 @@ void container_reap_empty(struct sway_container *con) { container_begin_destroy(con); con = parent; } - workspace_consider_destroy(ws); + if (ws) { + workspace_consider_destroy(ws); + } } struct sway_container *container_flatten(struct sway_container *container) { @@ -954,18 +960,20 @@ static void container_fullscreen_workspace(struct sway_container *con) { set_fullscreen_iterator(con, &enable); container_for_each_child(con, set_fullscreen_iterator, &enable); - con->workspace->fullscreen = con; con->saved_x = con->x; con->saved_y = con->y; con->saved_width = con->width; con->saved_height = con->height; - struct sway_seat *seat; - struct sway_workspace *focus_ws; - wl_list_for_each(seat, &server.input->seats, link) { - focus_ws = seat_get_focused_workspace(seat); - if (focus_ws == con->workspace) { - seat_set_focus_container(seat, con); + if (con->workspace) { + con->workspace->fullscreen = con; + struct sway_seat *seat; + struct sway_workspace *focus_ws; + wl_list_for_each(seat, &server.input->seats, link) { + focus_ws = seat_get_focused_workspace(seat); + if (focus_ws == con->workspace) { + seat_set_focus_container(seat, con); + } } } @@ -1019,11 +1027,14 @@ void container_fullscreen_disable(struct sway_container *con) { con->height = con->saved_height; if (con->fullscreen_mode == FULLSCREEN_WORKSPACE) { - con->workspace->fullscreen = NULL; - if (container_is_floating(con)) { - struct sway_output *output = container_floating_find_output(con); - if (con->workspace->output != output) { - container_floating_move_to_center(con); + if (con->workspace) { + con->workspace->fullscreen = NULL; + if (container_is_floating(con)) { + struct sway_output *output = + container_floating_find_output(con); + if (con->workspace->output != output) { + container_floating_move_to_center(con); + } } } } else { @@ -1040,6 +1051,17 @@ void container_fullscreen_disable(struct sway_container *con) { con->fullscreen_mode = FULLSCREEN_NONE; container_end_mouse_operation(con); ipc_event_window(con, "fullscreen_mode"); + + if (con->scratchpad) { + struct sway_seat *seat; + wl_list_for_each(seat, &server.input->seats, link) { + struct sway_container *focus = seat_get_focused_container(seat); + if (focus == con || container_has_ancestor(focus, con)) { + seat_set_focus(seat, + seat_get_focus_inactive(seat, &root->node)); + } + } + } } void container_set_fullscreen(struct sway_container *con, @@ -1056,7 +1078,7 @@ void container_set_fullscreen(struct sway_container *con, if (root->fullscreen_global) { container_fullscreen_disable(root->fullscreen_global); } - if (con->workspace->fullscreen) { + if (con->workspace && con->workspace->fullscreen) { container_fullscreen_disable(con->workspace->fullscreen); } container_fullscreen_workspace(con); @@ -1171,6 +1193,11 @@ void container_add_gaps(struct sway_container *c) { c->current_gaps.bottom > 0 || c->current_gaps.left > 0) { return; } + // Fullscreen global scratchpad containers cannot have gaps + struct sway_workspace *ws = c->workspace; + if (!ws) { + return; + } // Linear containers don't have gaps because it'd create double gaps if (!c->view && c->layout != L_TABBED && c->layout != L_STACKED) { return; @@ -1199,8 +1226,6 @@ void container_add_gaps(struct sway_container *c) { } } - struct sway_workspace *ws = c->workspace; - c->current_gaps.top = c->y == ws->y ? ws->gaps_inner : 0; c->current_gaps.right = ws->gaps_inner; c->current_gaps.bottom = ws->gaps_inner; @@ -1308,6 +1333,10 @@ void container_add_child(struct sway_container *parent, child->parent = parent; child->workspace = parent->workspace; container_for_each_child(child, set_workspace, NULL); + bool fullscreen = child->fullscreen_mode != FULLSCREEN_NONE || + parent->fullscreen_mode != FULLSCREEN_NONE; + set_fullscreen_iterator(child, &fullscreen); + container_for_each_child(child, set_fullscreen_iterator, &fullscreen); container_handle_fullscreen_reparent(child); container_update_representation(parent); node_set_dirty(&child->node); @@ -1347,8 +1376,31 @@ void container_detach(struct sway_container *child) { void container_replace(struct sway_container *container, struct sway_container *replacement) { + enum sway_fullscreen_mode fullscreen = container->fullscreen_mode; + bool scratchpad = container->scratchpad; + if (fullscreen != FULLSCREEN_NONE) { + container_fullscreen_disable(container); + } + if (scratchpad) { + root_scratchpad_show(container); + root_scratchpad_remove_container(container); + } container_add_sibling(container, replacement, 1); container_detach(container); + if (scratchpad) { + root_scratchpad_add_container(replacement); + } + switch (fullscreen) { + case FULLSCREEN_WORKSPACE: + container_fullscreen_workspace(replacement); + break; + case FULLSCREEN_GLOBAL: + container_fullscreen_global(replacement); + break; + case FULLSCREEN_NONE: + // noop + break; + } } struct sway_container *container_split(struct sway_container *child, @@ -1369,7 +1421,11 @@ struct sway_container *container_split(struct sway_container *child, if (set_focus) { seat_set_raw_focus(seat, &cont->node); - seat_set_raw_focus(seat, &child->node); + if (cont->fullscreen_mode == FULLSCREEN_GLOBAL) { + seat_set_focus(seat, &child->node); + } else { + seat_set_raw_focus(seat, &child->node); + } } return cont; @@ -1529,7 +1585,7 @@ void container_raise_floating(struct sway_container *con) { while (floater->parent) { floater = floater->parent; } - if (container_is_floating(floater)) { + if (container_is_floating(floater) && floater->workspace) { list_move_to_end(floater->workspace->floating, floater); node_set_dirty(&floater->workspace->node); } diff --git a/sway/tree/node.c b/sway/tree/node.c index dcab1c9b6..ffa7f2cc8 100644 --- a/sway/tree/node.c +++ b/sway/tree/node.c @@ -142,11 +142,19 @@ list_t *node_get_children(struct sway_node *node) { } bool node_has_ancestor(struct sway_node *node, struct sway_node *ancestor) { + if (ancestor->type == N_ROOT && node->type == N_CONTAINER && + node->sway_container->fullscreen_mode == FULLSCREEN_GLOBAL) { + return true; + } struct sway_node *parent = node_get_parent(node); while (parent) { if (parent == ancestor) { return true; } + if (ancestor->type == N_ROOT && parent->type == N_CONTAINER && + parent->sway_container->fullscreen_mode == FULLSCREEN_GLOBAL) { + return true; + } parent = node_get_parent(parent); } return false; diff --git a/sway/tree/root.c b/sway/tree/root.c index a9d306a43..1dabc2870 100644 --- a/sway/tree/root.c +++ b/sway/tree/root.c @@ -62,6 +62,11 @@ void root_scratchpad_add_container(struct sway_container *con) { struct sway_container *parent = con->parent; struct sway_workspace *workspace = con->workspace; + // Clear the fullscreen mode when sending to the scratchpad + if (con->fullscreen_mode != FULLSCREEN_NONE) { + container_fullscreen_disable(con); + } + // When a tiled window is sent to scratchpad, center and resize it. if (!container_is_floating(con)) { container_set_floating(con, true); @@ -143,6 +148,15 @@ void root_scratchpad_hide(struct sway_container *con) { struct sway_node *focus = seat_get_focus_inactive(seat, &root->node); struct sway_workspace *ws = con->workspace; + if (con->fullscreen_mode == FULLSCREEN_GLOBAL && !con->workspace) { + // If the container was made fullscreen global while in the scratchpad, + // it should be shown until fullscreen has been disabled + return; + } + + if (con->fullscreen_mode != FULLSCREEN_NONE) { + container_fullscreen_disable(con); + } container_detach(con); arrange_workspace(ws); if (&con->node == focus || node_has_ancestor(focus, &con->node)) { diff --git a/sway/tree/view.c b/sway/tree/view.c index 2c8839f59..c241b2b3a 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -197,10 +197,11 @@ static bool gaps_to_edge(struct sway_view *view) { void view_autoconfigure(struct sway_view *view) { struct sway_container *con = view->container; - if (container_is_scratchpad_hidden(con)) { + if (container_is_scratchpad_hidden(con) && + con->fullscreen_mode != FULLSCREEN_GLOBAL) { return; } - struct sway_output *output = con->workspace->output; + struct sway_output *output = con->workspace ? con->workspace->output : NULL; if (con->fullscreen_mode == FULLSCREEN_WORKSPACE) { con->content_x = output->lx; @@ -226,19 +227,21 @@ void view_autoconfigure(struct sway_view *view) { con->border_top = con->border_bottom = true; con->border_left = con->border_right = true; - if (config->hide_edge_borders == E_BOTH - || config->hide_edge_borders == E_VERTICAL - || (smart && !other_views && no_gaps)) { - con->border_left = con->x - con->current_gaps.left != ws->x; - int right_x = con->x + con->width + con->current_gaps.right; - con->border_right = right_x != ws->x + ws->width; - } - if (config->hide_edge_borders == E_BOTH - || config->hide_edge_borders == E_HORIZONTAL - || (smart && !other_views && no_gaps)) { - con->border_top = con->y - con->current_gaps.top != ws->y; - int bottom_y = con->y + con->height + con->current_gaps.bottom; - con->border_bottom = bottom_y != ws->y + ws->height; + if (ws) { + if (config->hide_edge_borders == E_BOTH + || config->hide_edge_borders == E_VERTICAL + || (smart && !other_views && no_gaps)) { + con->border_left = con->x - con->current_gaps.left != ws->x; + int right_x = con->x + con->width + con->current_gaps.right; + con->border_right = right_x != ws->x + ws->width; + } + if (config->hide_edge_borders == E_BOTH + || config->hide_edge_borders == E_HORIZONTAL + || (smart && !other_views && no_gaps)) { + con->border_top = con->y - con->current_gaps.top != ws->y; + int bottom_y = con->y + con->height + con->current_gaps.bottom; + con->border_bottom = bottom_y != ws->y + ws->height; + } } double y_offset = 0; @@ -247,7 +250,8 @@ void view_autoconfigure(struct sway_view *view) { // title area. We have to offset the surface y by the height of the title, // bar, and disable any top border because we'll always have the title bar. list_t *siblings = container_get_siblings(con); - bool show_titlebar = siblings->length > 1 || !config->hide_lone_tab; + bool show_titlebar = (siblings && siblings->length > 1) + || !config->hide_lone_tab; if (show_titlebar && !container_is_floating(con)) { enum sway_container_layout layout = container_parent_layout(con); if (layout == L_TABBED) { @@ -538,6 +542,10 @@ static bool should_focus(struct sway_view *view) { struct sway_workspace *prev_ws = seat_get_focused_workspace(seat); struct sway_workspace *map_ws = view->container->workspace; + if (view->container->fullscreen_mode == FULLSCREEN_GLOBAL) { + return true; + } + // Views can only take focus if they are mapped into the active workspace if (prev_ws != map_ws) { return false; @@ -581,7 +589,8 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface, } struct sway_seat *seat = input_manager_current_seat(); - struct sway_node *node = seat_get_focus_inactive(seat, &ws->node); + struct sway_node *node = ws ? seat_get_focus_inactive(seat, &ws->node) + : seat_get_focus_inactive(seat, &root->node); struct sway_container *target_sibling = node->type == N_CONTAINER ? node->sway_container : NULL; @@ -589,12 +598,13 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface, // launch it as a tiled view in the root of the workspace instead. if (target_sibling && container_is_floating(target_sibling)) { target_sibling = NULL; + ws = seat_get_last_known_workspace(seat); } view->container = container_create(view); if (target_sibling) { container_add_sibling(target_sibling, view->container, 1); - } else { + } else if (ws) { workspace_add_tiling(ws, view->container); } ipc_event_window(view->container, "new"); @@ -1032,8 +1042,18 @@ bool view_is_visible(struct sway_view *view) { return false; } struct sway_workspace *workspace = view->container->workspace; - if (!workspace) { - return false; + if (!workspace && view->container->fullscreen_mode != FULLSCREEN_GLOBAL) { + bool fs_global_descendant = false; + struct sway_container *parent = view->container->parent; + while (parent) { + if (parent->fullscreen_mode == FULLSCREEN_GLOBAL) { + fs_global_descendant = true; + } + parent = parent->parent; + } + if (!fs_global_descendant) { + return false; + } } // Determine if view is nested inside a floating container which is sticky struct sway_container *floater = view->container; @@ -1041,7 +1061,7 @@ bool view_is_visible(struct sway_view *view) { floater = floater->parent; } bool is_sticky = container_is_floating(floater) && floater->is_sticky; - if (!is_sticky && !workspace_is_visible(workspace)) { + if (!is_sticky && workspace && !workspace_is_visible(workspace)) { return false; } // Check view isn't in a tabbed or stacked container on an inactive tab From 8c69da11bbe70caa2f5db1665e9d60387604c5e4 Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Sun, 14 Apr 2019 01:06:09 -0400 Subject: [PATCH 121/191] swaynag: fix pointer management Currently on master, swaynag will retrieve a pointer instance whenever the capabilities change and WL_SEAT_CAPBILITY_POINTER is set. The pointer instances were never being destroyed so swaynag received events multiple times due to having several instances of the pointer. This fixes it so if there is already a pointer instance, swaynag does not attempt to retrieve another. Additionally, if the pointer capability is removed, the pointer instance is destroyed. --- swaynag/swaynag.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/swaynag/swaynag.c b/swaynag/swaynag.c index f0c6a3c54..eb31da57e 100644 --- a/swaynag/swaynag.c +++ b/swaynag/swaynag.c @@ -239,10 +239,14 @@ static struct wl_pointer_listener pointer_listener = { static void seat_handle_capabilities(void *data, struct wl_seat *wl_seat, enum wl_seat_capability caps) { struct swaynag *swaynag = data; - if ((caps & WL_SEAT_CAPABILITY_POINTER)) { + bool cap_pointer = caps & WL_SEAT_CAPABILITY_POINTER; + if (cap_pointer && !swaynag->pointer.pointer) { swaynag->pointer.pointer = wl_seat_get_pointer(wl_seat); wl_pointer_add_listener(swaynag->pointer.pointer, &pointer_listener, swaynag); + } else if (!cap_pointer && swaynag->pointer.pointer) { + wl_pointer_destroy(swaynag->pointer.pointer); + swaynag->pointer.pointer = NULL; } } From 6961bf2e4ce2c116e41a8db158691f6c993707ce Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Sun, 14 Apr 2019 00:27:47 -0400 Subject: [PATCH 122/191] Spawn swaynag as a wayland client This spawns swaynag as a wayland client similar to how swaybar and swaybg are already done --- common/util.c | 19 +++++++ include/sway/swaynag.h | 8 +-- include/util.h | 2 + sway/config.c | 21 +++---- sway/config/output.c | 19 ------- sway/main.c | 2 +- sway/swaynag.c | 124 +++++++++++++++++++++++++++++++---------- swaynag/swaynag.c | 23 ++++++-- 8 files changed, 147 insertions(+), 71 deletions(-) diff --git a/common/util.c b/common/util.c index c43c5ddf6..3a807edba 100644 --- a/common/util.c +++ b/common/util.c @@ -1,5 +1,6 @@ #define _POSIX_C_SOURCE 200809L #include +#include #include #include #include @@ -75,3 +76,21 @@ const char *sway_wl_output_subpixel_to_string(enum wl_output_subpixel subpixel) sway_assert(false, "Unknown value for wl_output_subpixel."); return NULL; } + +bool set_cloexec(int fd, bool cloexec) { + int flags = fcntl(fd, F_GETFD); + if (flags == -1) { + sway_log_errno(SWAY_ERROR, "fcntl failed"); + return false; + } + if (cloexec) { + flags = flags | FD_CLOEXEC; + } else { + flags = flags & ~FD_CLOEXEC; + } + if (fcntl(fd, F_SETFD, flags) == -1) { + sway_log_errno(SWAY_ERROR, "fcntl failed"); + return false; + } + return true; +} diff --git a/include/sway/swaynag.h b/include/sway/swaynag.h index 5a1787392..74d9ea18c 100644 --- a/include/sway/swaynag.h +++ b/include/sway/swaynag.h @@ -1,9 +1,12 @@ #ifndef _SWAY_SWAYNAG_H #define _SWAY_SWAYNAG_H +#include struct swaynag_instance { + struct wl_client *client; + struct wl_listener client_destroy; + const char *args; - pid_t pid; int fd[2]; bool detailed; }; @@ -15,9 +18,6 @@ struct swaynag_instance { bool swaynag_spawn(const char *swaynag_command, struct swaynag_instance *swaynag); -// Kill the swaynag instance -void swaynag_kill(struct swaynag_instance *swaynag); - // Write a log message to swaynag->fd[1]. This will fail when swaynag->detailed // is false. void swaynag_log(const char *swaynag_command, struct swaynag_instance *swaynag, diff --git a/include/util.h b/include/util.h index 6a668fd65..6d9454e05 100644 --- a/include/util.h +++ b/include/util.h @@ -32,4 +32,6 @@ float parse_float(const char *value); const char *sway_wl_output_subpixel_to_string(enum wl_output_subpixel subpixel); +bool set_cloexec(int fd, bool cloexec); + #endif diff --git a/sway/config.c b/sway/config.c index d5bfe105f..4944ec024 100644 --- a/sway/config.c +++ b/sway/config.c @@ -175,15 +175,13 @@ static void set_color(float dest[static 4], uint32_t color) { static void config_defaults(struct sway_config *config) { if (!(config->swaynag_command = strdup("swaynag"))) goto cleanup; - config->swaynag_config_errors = (struct swaynag_instance){ - .args = "--type error " + config->swaynag_config_errors = (struct swaynag_instance){0}; + config->swaynag_config_errors.args = "--type error " "--message 'There are errors in your config file' " "--detailed-message " - "--button 'Exit sway' 'swaymsg exit' " - "--button 'Reload sway' 'swaymsg reload'", - .pid = -1, - .detailed = true, - }; + "--button-no-terminal 'Exit sway' 'swaymsg exit' " + "--button-no-terminal 'Reload sway' 'swaymsg reload'"; + config->swaynag_config_errors.detailed = true; if (!(config->symbols = create_list())) goto cleanup; if (!(config->modes = create_list())) goto cleanup; @@ -411,10 +409,9 @@ bool load_main_config(const char *file, bool is_active, bool validating) { config->reloading = true; config->active = true; - swaynag_kill(&old_config->swaynag_config_errors); - memcpy(&config->swaynag_config_errors, - &old_config->swaynag_config_errors, - sizeof(struct swaynag_instance)); + if (old_config->swaynag_config_errors.client != NULL) { + wl_client_destroy(old_config->swaynag_config_errors.client); + } input_manager_reset_all_inputs(); } @@ -486,7 +483,7 @@ bool load_main_config(const char *file, bool is_active, bool validating) { spawn_swaybg(); config->reloading = false; - if (config->swaynag_config_errors.pid > 0) { + if (config->swaynag_config_errors.client != NULL) { swaynag_show(&config->swaynag_config_errors); } diff --git a/sway/config/output.c b/sway/config/output.c index 747ab28b7..fb8a9ee52 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -1,6 +1,5 @@ #define _POSIX_C_SOURCE 200809L #include -#include #include #include #include @@ -488,24 +487,6 @@ static void handle_swaybg_client_destroy(struct wl_listener *listener, config->swaybg_client = NULL; } -static bool set_cloexec(int fd, bool cloexec) { - int flags = fcntl(fd, F_GETFD); - if (flags == -1) { - sway_log_errno(SWAY_ERROR, "fcntl failed"); - return false; - } - if (cloexec) { - flags = flags | FD_CLOEXEC; - } else { - flags = flags & ~FD_CLOEXEC; - } - if (fcntl(fd, F_SETFD, flags) == -1) { - sway_log_errno(SWAY_ERROR, "fcntl failed"); - return false; - } - return true; -} - static bool _spawn_swaybg(char **command) { if (config->swaybg_client != NULL) { wl_client_destroy(config->swaybg_client); diff --git a/sway/main.c b/sway/main.c index ba4e25624..96f67b36b 100644 --- a/sway/main.c +++ b/sway/main.c @@ -391,7 +391,7 @@ int main(int argc, char **argv) { load_swaybars(); run_deferred_commands(); - if (config->swaynag_config_errors.pid > 0) { + if (config->swaynag_config_errors.client != NULL) { swaynag_show(&config->swaynag_config_errors); } diff --git a/sway/swaynag.c b/sway/swaynag.c index 49027f5d5..0fca6c710 100644 --- a/sway/swaynag.c +++ b/sway/swaynag.c @@ -1,16 +1,32 @@ #define _POSIX_C_SOURCE 200809L -#include #include #include #include #include +#include #include +#include #include #include "log.h" +#include "sway/server.h" #include "sway/swaynag.h" +#include "util.h" + +static void handle_swaynag_client_destroy(struct wl_listener *listener, + void *data) { + struct swaynag_instance *swaynag = + wl_container_of(listener, swaynag, client_destroy); + wl_list_remove(&swaynag->client_destroy.link); + wl_list_init(&swaynag->client_destroy.link); + swaynag->client = NULL; +} bool swaynag_spawn(const char *swaynag_command, struct swaynag_instance *swaynag) { + if (swaynag->client != NULL) { + wl_client_destroy(swaynag->client); + } + if (!swaynag_command) { return true; } @@ -20,44 +36,94 @@ bool swaynag_spawn(const char *swaynag_command, sway_log(SWAY_ERROR, "Failed to create pipe for swaynag"); return false; } - fcntl(swaynag->fd[1], F_SETFD, FD_CLOEXEC); + if (!set_cloexec(swaynag->fd[1], true)) { + goto failed; + } } - pid_t pid; - if ((pid = fork()) == 0) { - if (swaynag->detailed) { - close(swaynag->fd[1]); - dup2(swaynag->fd[0], STDIN_FILENO); - close(swaynag->fd[0]); - } + int sockets[2]; + if (socketpair(AF_UNIX, SOCK_STREAM, 0, sockets) != 0) { + sway_log_errno(SWAY_ERROR, "socketpair failed"); + goto failed; + } + if (!set_cloexec(sockets[0], true) || !set_cloexec(sockets[1], true)) { + goto failed; + } - size_t length = strlen(swaynag_command) + strlen(swaynag->args) + 2; - char *cmd = malloc(length); - snprintf(cmd, length, "%s %s", swaynag_command, swaynag->args); - execl("/bin/sh", "/bin/sh", "-c", cmd, NULL); - _exit(0); - } else if (pid < 0) { + swaynag->client = wl_client_create(server.wl_display, sockets[0]); + if (swaynag->client == NULL) { + sway_log_errno(SWAY_ERROR, "wl_client_create failed"); + goto failed; + } + + swaynag->client_destroy.notify = handle_swaynag_client_destroy; + wl_client_add_destroy_listener(swaynag->client, &swaynag->client_destroy); + + pid_t pid = fork(); + if (pid < 0) { sway_log(SWAY_ERROR, "Failed to create fork for swaynag"); - if (swaynag->detailed) { - close(swaynag->fd[0]); - close(swaynag->fd[1]); + goto failed; + } else if (pid == 0) { + pid = fork(); + if (pid < 0) { + sway_log_errno(SWAY_ERROR, "fork failed"); + _exit(EXIT_FAILURE); + } else if (pid == 0) { + if (!set_cloexec(sockets[1], false)) { + _exit(EXIT_FAILURE); + } + + if (swaynag->detailed) { + close(swaynag->fd[1]); + dup2(swaynag->fd[0], STDIN_FILENO); + close(swaynag->fd[0]); + } + + char wayland_socket_str[16]; + snprintf(wayland_socket_str, sizeof(wayland_socket_str), + "%d", sockets[1]); + setenv("WAYLAND_SOCKET", wayland_socket_str, true); + + size_t length = strlen(swaynag_command) + strlen(swaynag->args) + 2; + char *cmd = malloc(length); + snprintf(cmd, length, "%s %s", swaynag_command, swaynag->args); + execl("/bin/sh", "/bin/sh", "-c", cmd, NULL); + sway_log_errno(SWAY_ERROR, "execl failed"); + _exit(EXIT_FAILURE); } - return false; + _exit(EXIT_SUCCESS); } if (swaynag->detailed) { - close(swaynag->fd[0]); + if (close(swaynag->fd[0]) != 0) { + sway_log_errno(SWAY_ERROR, "close failed"); + return false; + } } - swaynag->pid = pid; + + if (close(sockets[1]) != 0) { + sway_log_errno(SWAY_ERROR, "close failed"); + return false; + } + + if (waitpid(pid, NULL, 0) < 0) { + sway_log_errno(SWAY_ERROR, "waitpid failed"); + return false; + } + return true; -} - -void swaynag_kill(struct swaynag_instance *swaynag) { - if (swaynag->pid > 0) { - kill(swaynag->pid, SIGTERM); - swaynag->pid = -1; +failed: + if (swaynag->detailed) { + if (close(swaynag->fd[0]) != 0) { + sway_log_errno(SWAY_ERROR, "close failed"); + return false; + } + if (close(swaynag->fd[1]) != 0) { + sway_log_errno(SWAY_ERROR, "close failed"); + } } + return false; } void swaynag_log(const char *swaynag_command, struct swaynag_instance *swaynag, @@ -71,7 +137,7 @@ void swaynag_log(const char *swaynag_command, struct swaynag_instance *swaynag, return; } - if (swaynag->pid <= 0 && !swaynag_spawn(swaynag_command, swaynag)) { + if (swaynag->client == NULL && !swaynag_spawn(swaynag_command, swaynag)) { return; } @@ -96,7 +162,7 @@ void swaynag_log(const char *swaynag_command, struct swaynag_instance *swaynag, } void swaynag_show(struct swaynag_instance *swaynag) { - if (swaynag->detailed && swaynag->pid > 0) { + if (swaynag->detailed && swaynag->client != NULL) { close(swaynag->fd[1]); } } diff --git a/swaynag/swaynag.c b/swaynag/swaynag.c index eb31da57e..26411ab31 100644 --- a/swaynag/swaynag.c +++ b/swaynag/swaynag.c @@ -45,17 +45,24 @@ static void swaynag_button_execute(struct swaynag *swaynag, swaynag->details.visible = !swaynag->details.visible; render_frame(swaynag); } else { - if (fork() == 0) { + pid_t pid = fork(); + if (pid < 0) { + sway_log_errno(SWAY_DEBUG, "Failed to fork"); + return; + } else if (pid == 0) { // Child process. Will be used to prevent zombie processes - setsid(); - if (fork() == 0) { + pid = fork(); + if (pid < 0) { + sway_log_errno(SWAY_DEBUG, "Failed to fork"); + return; + } else if (pid == 0) { // Child of the child. Will be reparented to the init process char *terminal = getenv("TERMINAL"); if (button->terminal && terminal && strlen(terminal)) { sway_log(SWAY_DEBUG, "Found $TERMINAL: %s", terminal); if (!terminal_execute(terminal, button->action)) { swaynag_destroy(swaynag); - exit(EXIT_FAILURE); + _exit(EXIT_FAILURE); } } else { if (button->terminal) { @@ -63,12 +70,16 @@ static void swaynag_button_execute(struct swaynag *swaynag, "$TERMINAL not found. Running directly"); } execl("/bin/sh", "/bin/sh", "-c", button->action, NULL); + sway_log_errno(SWAY_DEBUG, "execl failed"); + _exit(EXIT_FAILURE); } } - exit(EXIT_SUCCESS); + _exit(EXIT_SUCCESS); + } + if (waitpid(pid, NULL, 0) < 0) { + sway_log_errno(SWAY_DEBUG, "waitpid failed"); } } - wait(0); } static void layer_surface_configure(void *data, From 6737b90cb93d0231bbbc6045adf8a2443bc4e21c Mon Sep 17 00:00:00 2001 From: Benjamin Cheng Date: Tue, 12 Mar 2019 13:17:47 -0400 Subject: [PATCH 123/191] Add heuristics to differentiate touchpads Use libinput_device_config_tap_get_finger_count to determine whether a pointer is a touchpad. swaymsg is also updated to reflect the new touchpad type. --- include/sway/input/input-manager.h | 2 ++ sway/input/input-manager.c | 34 ++++++++++++++++++++++++++++++ sway/ipc-json.c | 20 +----------------- swaymsg/main.c | 1 + 4 files changed, 38 insertions(+), 19 deletions(-) diff --git a/include/sway/input/input-manager.h b/include/sway/input/input-manager.h index e166a2377..8d4a5b005 100644 --- a/include/sway/input/input-manager.h +++ b/include/sway/input/input-manager.h @@ -62,4 +62,6 @@ struct input_config *input_device_get_config(struct sway_input_device *device); char *input_device_get_identifier(struct wlr_input_device *device); +const char *input_device_get_type(struct sway_input_device *device); + #endif diff --git a/sway/input/input-manager.c b/sway/input/input-manager.c index adb36af93..0c5254bdf 100644 --- a/sway/input/input-manager.c +++ b/sway/input/input-manager.c @@ -71,6 +71,40 @@ char *input_device_get_identifier(struct wlr_input_device *device) { return identifier; } +static bool device_is_touchpad(struct sway_input_device *device) { + if (device->wlr_device->type != WLR_INPUT_DEVICE_POINTER || + !wlr_input_device_is_libinput(device->wlr_device)) { + return false; + } + + struct libinput_device *libinput_device = + wlr_libinput_get_device_handle(device->wlr_device); + + return libinput_device_config_tap_get_finger_count(libinput_device) > 0; +} + +const char *input_device_get_type(struct sway_input_device *device) { + switch (device->wlr_device->type) { + case WLR_INPUT_DEVICE_POINTER: + if (device_is_touchpad(device)) { + return "touchpad"; + } else { + return "pointer"; + } + case WLR_INPUT_DEVICE_KEYBOARD: + return "keyboard"; + case WLR_INPUT_DEVICE_TOUCH: + return "touch"; + case WLR_INPUT_DEVICE_TABLET_TOOL: + return "tablet_tool"; + case WLR_INPUT_DEVICE_TABLET_PAD: + return "tablet_pad"; + case WLR_INPUT_DEVICE_SWITCH: + return "switch"; + } + return "unknown"; +} + static struct sway_input_device *input_sway_device_from_wlr( struct wlr_input_device *device) { struct sway_input_device *input_device = NULL; diff --git a/sway/ipc-json.c b/sway/ipc-json.c index c320d9586..4ccf6dfd3 100644 --- a/sway/ipc-json.c +++ b/sway/ipc-json.c @@ -85,24 +85,6 @@ static const char *ipc_json_output_transform_description(enum wl_output_transfor return NULL; } -static const char *ipc_json_device_type_description(struct sway_input_device *device) { - switch (device->wlr_device->type) { - case WLR_INPUT_DEVICE_POINTER: - return "pointer"; - case WLR_INPUT_DEVICE_KEYBOARD: - return "keyboard"; - case WLR_INPUT_DEVICE_SWITCH: - return "switch"; - case WLR_INPUT_DEVICE_TOUCH: - return "touch"; - case WLR_INPUT_DEVICE_TABLET_TOOL: - return "tablet_tool"; - case WLR_INPUT_DEVICE_TABLET_PAD: - return "tablet_pad"; - } - return "unknown"; -} - json_object *ipc_json_get_version(void) { int major = 0, minor = 0, patch = 0; json_object *version = json_object_new_object(); @@ -819,7 +801,7 @@ json_object *ipc_json_describe_input(struct sway_input_device *device) { json_object_new_int(device->wlr_device->product)); json_object_object_add(object, "type", json_object_new_string( - ipc_json_device_type_description(device))); + input_device_get_type(device))); if (device->wlr_device->type == WLR_INPUT_DEVICE_KEYBOARD) { struct wlr_keyboard *keyboard = device->wlr_device->keyboard; diff --git a/swaymsg/main.c b/swaymsg/main.c index f86000a42..e2c43445c 100644 --- a/swaymsg/main.c +++ b/swaymsg/main.c @@ -98,6 +98,7 @@ static const char *pretty_type_name(const char *name) { } type_names[] = { { "keyboard", "Keyboard" }, { "pointer", "Mouse" }, + { "touchpad", "Touchpad" }, { "tablet_pad", "Tablet pad" }, { "tablet_tool", "Tablet tool" }, { "touch", "Touch" }, From bd3720585e91ae0dfcc4be30149ae4f8f5218174 Mon Sep 17 00:00:00 2001 From: Benjamin Cheng Date: Mon, 25 Mar 2019 22:05:49 -0400 Subject: [PATCH 124/191] Implement input type configs (#3784) Add support for configurations that apply to a type of inputs (i.e. natural scrolling on all touchpads). A type config is differentiated by a `type:` prefix followed by the type it corresponds to. When new devices appear, the device config is merged on top of its type config (if it exists). New type configs are applied on top of existing configs. --- include/sway/config.h | 2 ++ sway/config.c | 10 +++++++- sway/config/input.c | 39 ++++++++++++++++++++++++++++--- sway/input/input-manager.c | 47 +++++++++++++++++++++++++++++++++++++- sway/sway-input.5.scd | 17 +++++++++++++- 5 files changed, 109 insertions(+), 6 deletions(-) diff --git a/include/sway/config.h b/include/sway/config.h index fe06fb9de..864105442 100644 --- a/include/sway/config.h +++ b/include/sway/config.h @@ -105,6 +105,7 @@ struct input_config_mapped_from_region { */ struct input_config { char *identifier; + const char *input_type; int accel_profile; int click_method; @@ -418,6 +419,7 @@ struct sway_config { list_t *workspace_configs; list_t *output_configs; list_t *input_configs; + list_t *input_type_configs; list_t *seat_configs; list_t *criteria; list_t *no_focus; diff --git a/sway/config.c b/sway/config.c index 4944ec024..e14ea83a8 100644 --- a/sway/config.c +++ b/sway/config.c @@ -113,6 +113,12 @@ void free_config(struct sway_config *config) { } list_free(config->input_configs); } + if (config->input_type_configs) { + for (int i = 0; i < config->input_type_configs->length; i++) { + free_input_config(config->input_type_configs->items[i]); + } + list_free(config->input_type_configs); + } if (config->seat_configs) { for (int i = 0; i < config->seat_configs->length; i++) { free_seat_config(config->seat_configs->items[i]); @@ -189,10 +195,12 @@ static void config_defaults(struct sway_config *config) { if (!(config->workspace_configs = create_list())) goto cleanup; if (!(config->criteria = create_list())) goto cleanup; if (!(config->no_focus = create_list())) goto cleanup; - if (!(config->input_configs = create_list())) goto cleanup; if (!(config->seat_configs = create_list())) goto cleanup; if (!(config->output_configs = create_list())) goto cleanup; + if (!(config->input_type_configs = create_list())) goto cleanup; + if (!(config->input_configs = create_list())) goto cleanup; + if (!(config->cmd_queue = create_list())) goto cleanup; if (!(config->current_mode = malloc(sizeof(struct sway_mode)))) diff --git a/sway/config/input.c b/sway/config/input.c index 63c28635b..aa5814313 100644 --- a/sway/config/input.c +++ b/sway/config/input.c @@ -18,6 +18,7 @@ struct input_config *new_input_config(const char* identifier) { return NULL; } + input->input_type = NULL; input->tap = INT_MIN; input->tap_button_map = INT_MIN; input->drag = INT_MIN; @@ -140,6 +141,30 @@ static void merge_wildcard_on_all(struct input_config *wildcard) { merge_input_config(ic, wildcard); } } + + for (int i = 0; i < config->input_type_configs->length; i++) { + struct input_config *ic = config->input_type_configs->items[i]; + if (strcmp(wildcard->identifier, ic->identifier) != 0) { + sway_log(SWAY_DEBUG, "Merging input * config on %s", ic->identifier); + merge_input_config(ic, wildcard); + } + } +} + +static void merge_type_on_existing(struct input_config *type_wildcard) { + for (int i = 0; i < config->input_configs->length; i++) { + struct input_config *ic = config->input_configs->items[i]; + if (ic->input_type == NULL) { + continue; + } + + if (strcmp(ic->input_type, type_wildcard->identifier + 5) == 0) { + sway_log(SWAY_DEBUG, "Merging %s top of %s", + type_wildcard->identifier, + ic->identifier); + merge_input_config(ic, type_wildcard); + } + } } struct input_config *store_input_config(struct input_config *ic) { @@ -148,11 +173,19 @@ struct input_config *store_input_config(struct input_config *ic) { merge_wildcard_on_all(ic); } - int i = list_seq_find(config->input_configs, input_identifier_cmp, + list_t *config_list = NULL; + if (strncmp(ic->identifier, "type:", 5) == 0) { + config_list = config->input_type_configs; + merge_type_on_existing(ic); + } else { + config_list = config->input_configs; + } + + int i = list_seq_find(config_list, input_identifier_cmp, ic->identifier); if (i >= 0) { sway_log(SWAY_DEBUG, "Merging on top of existing input config"); - struct input_config *current = config->input_configs->items[i]; + struct input_config *current = config_list->items[i]; merge_input_config(current, ic); free_input_config(ic); ic = current; @@ -167,7 +200,7 @@ struct input_config *store_input_config(struct input_config *ic) { free_input_config(ic); ic = current; } - list_add(config->input_configs, ic); + list_add(config_list, ic); } else { // New wildcard config. Just add it sway_log(SWAY_DEBUG, "Adding input * config"); diff --git a/sway/input/input-manager.c b/sway/input/input-manager.c index 0c5254bdf..a2a1e2740 100644 --- a/sway/input/input-manager.c +++ b/sway/input/input-manager.c @@ -105,6 +105,37 @@ const char *input_device_get_type(struct sway_input_device *device) { return "unknown"; } +static void apply_input_type_config(struct sway_input_device *input_device) { + const char *device_type = input_device_get_type(input_device); + struct input_config *type_config = NULL; + for (int i = 0; i < config->input_type_configs->length; i++) { + struct input_config *ic = config->input_type_configs->items[i]; + if (strcmp(ic->identifier + 5, device_type) == 0) { + type_config = ic; + break; + } + } + if (type_config == NULL) { + return; + } + + for (int i = 0; i < config->input_configs->length; i++) { + struct input_config *ic = config->input_configs->items[i]; + if (strcmp(input_device->identifier, ic->identifier) == 0) { + struct input_config *current = new_input_config(ic->identifier); + merge_input_config(current, type_config); + merge_input_config(current, ic); + + current->input_type = device_type; + config->input_configs->items[i] = current; + free_input_config(ic); + ic = NULL; + + break; + } + } +} + static struct sway_input_device *input_sway_device_from_wlr( struct wlr_input_device *device) { struct sway_input_device *input_device = NULL; @@ -541,6 +572,8 @@ static void handle_new_input(struct wl_listener *listener, void *data) { sway_log(SWAY_DEBUG, "adding device: '%s'", input_device->identifier); + apply_input_type_config(input_device); + if (input_device->wlr_device->type == WLR_INPUT_DEVICE_POINTER || input_device->wlr_device->type == WLR_INPUT_DEVICE_TABLET_TOOL) { input_manager_libinput_config_pointer(input_device); @@ -693,9 +726,13 @@ void input_manager_set_focus(struct sway_node *node) { void input_manager_apply_input_config(struct input_config *input_config) { struct sway_input_device *input_device = NULL; bool wildcard = strcmp(input_config->identifier, "*") == 0; + bool type_wildcard = strncmp(input_config->identifier, "type:", 5) == 0; wl_list_for_each(input_device, &server.input->devices, link) { + bool type_matches = type_wildcard && + strcmp(input_device_get_type(input_device), input_config->identifier + 5) == 0; if (strcmp(input_device->identifier, input_config->identifier) == 0 - || wildcard) { + || wildcard + || type_matches) { if (input_device->wlr_device->type == WLR_INPUT_DEVICE_POINTER || input_device->wlr_device->type == WLR_INPUT_DEVICE_TABLET_TOOL) { input_manager_libinput_config_pointer(input_device); @@ -829,5 +866,13 @@ struct input_config *input_device_get_config(struct sway_input_device *device) { } } + const char *device_type = input_device_get_type(device); + for (int i = 0; i < config->input_type_configs->length; ++i) { + input_config = config->input_type_configs->items[i]; + if (strcmp(input_config->identifier + 5, device_type) == 0) { + return input_config; + } + } + return wildcard_config; } diff --git a/sway/sway-input.5.scd b/sway/sway-input.5.scd index 1a8062fb9..efd3d1afc 100644 --- a/sway/sway-input.5.scd +++ b/sway/sway-input.5.scd @@ -9,13 +9,28 @@ sway-input - input configuration file and commands Sway allows for configuration of devices within the sway configuration file. To obtain a list of available device identifiers, run *swaymsg -t get_inputs*. Settings can also be applied to all input devices by using the wildcard, _\*_, -in place of _\_ in the commands below. +in place of _\_ in the commands below. In addition, the settings +can be applied to a type of device, by using _type:\_ in place +of _\_. Tip: If the configuration settings do not appear to be taking effect, you could try using _\*_ instead of _\_. If it works with the wildcard, try using a different identifier from *swaymsg -t get_inputs* until you find the correct input device. +Current available input types are: + +- touchpad +- pointer +- keyboard +- touch +- tablet_tool +- tablet_pad +- switch + +Note: The type configurations are applied as the devices appear and get applied +on top of the existing device configurations. + # INPUT COMMANDS ## KEYBOARD CONFIGURATION From b2cfcebef61087f819b55d2f815e62aa4a6f1a52 Mon Sep 17 00:00:00 2001 From: Connor E <38229097+c-edw@users.noreply.github.com> Date: Mon, 15 Apr 2019 01:16:35 +0100 Subject: [PATCH 125/191] Add deprecation warnings for new_float, new_window, and force_focus_wrapping. --- include/sway/commands.h | 2 ++ sway/commands.c | 4 ++-- sway/commands/force_focus_wrapping.c | 9 ++++++++- sway/commands/new_float.c | 13 +++++++++++++ sway/commands/new_window.c | 13 +++++++++++++ sway/meson.build | 2 ++ sway/sway.5.scd | 6 ------ 7 files changed, 40 insertions(+), 9 deletions(-) create mode 100644 sway/commands/new_float.c create mode 100644 sway/commands/new_window.c diff --git a/include/sway/commands.h b/include/sway/commands.h index 9bd0f1cb5..2b66904c6 100644 --- a/include/sway/commands.h +++ b/include/sway/commands.h @@ -147,6 +147,8 @@ sway_cmd cmd_mark; sway_cmd cmd_mode; sway_cmd cmd_mouse_warping; sway_cmd cmd_move; +sway_cmd cmd_new_float; +sway_cmd cmd_new_window; sway_cmd cmd_nop; sway_cmd cmd_opacity; sway_cmd cmd_new_float; diff --git a/sway/commands.c b/sway/commands.c index abdaa3b87..d0bf402bd 100644 --- a/sway/commands.c +++ b/sway/commands.c @@ -77,8 +77,8 @@ static struct cmd_handler handlers[] = { { "input", cmd_input }, { "mode", cmd_mode }, { "mouse_warping", cmd_mouse_warping }, - { "new_float", cmd_default_floating_border }, - { "new_window", cmd_default_border }, + { "new_float", cmd_new_float }, + { "new_window", cmd_new_window }, { "no_focus", cmd_no_focus }, { "output", cmd_output }, { "popup_during_fullscreen", cmd_popup_during_fullscreen }, diff --git a/sway/commands/force_focus_wrapping.c b/sway/commands/force_focus_wrapping.c index e646ae9b7..fafc1c3ef 100644 --- a/sway/commands/force_focus_wrapping.c +++ b/sway/commands/force_focus_wrapping.c @@ -1,9 +1,16 @@ -#include #include "sway/commands.h" #include "sway/config.h" +#include "log.h" #include "util.h" struct cmd_results *cmd_force_focus_wrapping(int argc, char **argv) { + sway_log(SWAY_INFO, "Warning: force_focus_wrapping is deprecated. " + "Use focus_wrapping instead."); + if (config->reading) { + config_add_swaynag_warning("force_focus_wrapping is deprecated. " + "Use focus_wrapping instead."); + } + struct cmd_results *error = checkarg(argc, "force_focus_wrapping", EXPECTED_EQUAL_TO, 1); if (error) { diff --git a/sway/commands/new_float.c b/sway/commands/new_float.c new file mode 100644 index 000000000..4fedb4cb8 --- /dev/null +++ b/sway/commands/new_float.c @@ -0,0 +1,13 @@ +#include "log.h" +#include "sway/commands.h" +#include "sway/config.h" + +struct cmd_results *cmd_new_float(int argc, char **argv) { + sway_log(SWAY_INFO, "Warning: new_float is deprecated. " + "Use default_floating_border instead."); + if (config->reading) { + config_add_swaynag_warning("new_float is deprecated. " + "Use default_floating_border instead."); + } + return cmd_default_floating_border(argc, argv); +} diff --git a/sway/commands/new_window.c b/sway/commands/new_window.c new file mode 100644 index 000000000..e8caa4873 --- /dev/null +++ b/sway/commands/new_window.c @@ -0,0 +1,13 @@ +#include "log.h" +#include "sway/commands.h" +#include "sway/config.h" + +struct cmd_results *cmd_new_window(int argc, char **argv) { + sway_log(SWAY_INFO, "Warning: new_window is deprecated. " + "Use default_border instead."); + if (config->reading) { + config_add_swaynag_warning("new_window is deprecated. " + "Use default_border instead."); + } + return cmd_default_border(argc, argv); +} diff --git a/sway/meson.build b/sway/meson.build index 12b86efb8..8254fb5c3 100644 --- a/sway/meson.build +++ b/sway/meson.build @@ -73,6 +73,8 @@ sway_sources = files( 'commands/mode.c', 'commands/mouse_warping.c', 'commands/move.c', + 'commands/new_float.c', + 'commands/new_window.c', 'commands/no_focus.c', 'commands/nop.c', 'commands/output.c', diff --git a/sway/sway.5.scd b/sway/sway.5.scd index 1650cd60c..b484607a2 100644 --- a/sway/sway.5.scd +++ b/sway/sway.5.scd @@ -529,12 +529,6 @@ The default colors are: Whenever a window that matches _criteria_ appears, run list of commands. See *CRITERIA* for more details. -*force_focus_wrapping* yes|no - This option is a wrapper to support i3's legacy syntax. _no_ is equivalent - to _focus_wrapping yes_ and _yes_ is equivalent to - _focus_wrapping force_. This is only available for convenience. Please - use _focus_wrapping_ instead when possible. - *gaps* inner|outer|horizontal|vertical|top|right|bottom|left Sets default _amount_ pixels of _inner_ or _outer_ gap, where the inner affects spacing around each view and outer affects the spacing around each From fd6903673b084ef16f18e228405cb50adf28550d Mon Sep 17 00:00:00 2001 From: Ranieri Althoff Date: Mon, 15 Apr 2019 21:46:50 -0300 Subject: [PATCH 126/191] Send disabled output available modes on IPC get_outputs - Also fix missing trailing newline on pretty print Signed-off-by: Ranieri Althoff <1993083+ranisalt@users.noreply.github.com> --- sway/ipc-json.c | 16 +++++++++++++++- swaymsg/main.c | 2 +- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/sway/ipc-json.c b/sway/ipc-json.c index 4ccf6dfd3..4af8d34bc 100644 --- a/sway/ipc-json.c +++ b/sway/ipc-json.c @@ -240,7 +240,21 @@ json_object *ipc_json_describe_disabled_output(struct sway_output *output) { json_object_new_string(wlr_output->model)); json_object_object_add(object, "serial", json_object_new_string(wlr_output->serial)); - json_object_object_add(object, "modes", json_object_new_array()); + + json_object *modes_array = json_object_new_array(); + struct wlr_output_mode *mode; + wl_list_for_each(mode, &wlr_output->modes, link) { + json_object *mode_object = json_object_new_object(); + json_object_object_add(mode_object, "width", + json_object_new_int(mode->width)); + json_object_object_add(mode_object, "height", + json_object_new_int(mode->height)); + json_object_object_add(mode_object, "refresh", + json_object_new_int(mode->refresh)); + json_object_array_add(modes_array, mode_object); + } + + json_object_object_add(object, "modes", modes_array); json_object_object_add(object, "current_workspace", NULL); diff --git a/swaymsg/main.c b/swaymsg/main.c index e2c43445c..ccc847f5c 100644 --- a/swaymsg/main.c +++ b/swaymsg/main.c @@ -230,7 +230,7 @@ static void pretty_print_output(json_object *o) { ); } else { printf( - "Output %s '%s %s %s' (inactive)", + "Output %s '%s %s %s' (inactive)\n", json_object_get_string(name), json_object_get_string(make), json_object_get_string(model), From 2f9eed659d290c1a3a008bf104e2a94b6e031915 Mon Sep 17 00:00:00 2001 From: Ashkan Kiani Date: Tue, 16 Apr 2019 02:30:19 -0700 Subject: [PATCH 127/191] Add documentation for rename workspace --- sway/sway.5.scd | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sway/sway.5.scd b/sway/sway.5.scd index b484607a2..1de06d3e5 100644 --- a/sway/sway.5.scd +++ b/sway/sway.5.scd @@ -223,6 +223,9 @@ set|plus|minus *reload* Reloads the sway config file and applies any changes. +*rename workspace* [] to + Rename either or the focused workspace to the + *resize* shrink|grow width|height [ [px|ppt]] Resizes the currently focused container by _amount_, specified in pixels or percentage points. If the units are omitted, floating containers are resized From e9e1fbc5011b8eaaaab73be21266ee1d388d38a7 Mon Sep 17 00:00:00 2001 From: mwenzkowski <29407878+mwenzkowski@users.noreply.github.com> Date: Tue, 16 Apr 2019 21:11:35 +0200 Subject: [PATCH 128/191] view.c: refactor view_autoconfigure() --- sway/tree/view.c | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/sway/tree/view.c b/sway/tree/view.c index c241b2b3a..404326619 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -197,11 +197,13 @@ static bool gaps_to_edge(struct sway_view *view) { void view_autoconfigure(struct sway_view *view) { struct sway_container *con = view->container; + struct sway_workspace *ws = con->workspace; + if (container_is_scratchpad_hidden(con) && con->fullscreen_mode != FULLSCREEN_GLOBAL) { return; } - struct sway_output *output = con->workspace ? con->workspace->output : NULL; + struct sway_output *output = ws ? ws->output : NULL; if (con->fullscreen_mode == FULLSCREEN_WORKSPACE) { con->content_x = output->lx; @@ -217,27 +219,23 @@ void view_autoconfigure(struct sway_view *view) { return; } - struct sway_workspace *ws = view->container->workspace; - - bool smart = config->hide_edge_borders == E_SMART || - config->hide_edge_borders == E_SMART_NO_GAPS; - bool other_views = smart && !view_is_only_visible(view); - bool no_gaps = config->hide_edge_borders != E_SMART_NO_GAPS - || !gaps_to_edge(view); - con->border_top = con->border_bottom = true; con->border_left = con->border_right = true; + if (ws) { + bool smart = config->hide_edge_borders == E_SMART || + (config->hide_edge_borders == E_SMART_NO_GAPS && + !gaps_to_edge(view)); + bool hide_smart = smart && view_is_only_visible(view); + if (config->hide_edge_borders == E_BOTH - || config->hide_edge_borders == E_VERTICAL - || (smart && !other_views && no_gaps)) { + || config->hide_edge_borders == E_VERTICAL || hide_smart) { con->border_left = con->x - con->current_gaps.left != ws->x; int right_x = con->x + con->width + con->current_gaps.right; con->border_right = right_x != ws->x + ws->width; } if (config->hide_edge_borders == E_BOTH - || config->hide_edge_borders == E_HORIZONTAL - || (smart && !other_views && no_gaps)) { + || config->hide_edge_borders == E_HORIZONTAL || hide_smart) { con->border_top = con->y - con->current_gaps.top != ws->y; int bottom_y = con->y + con->height + con->current_gaps.bottom; con->border_bottom = bottom_y != ws->y + ws->height; From 9ac2342a673e899af87b8f8406820f2cc40bb185 Mon Sep 17 00:00:00 2001 From: mwenzkowski <29407878+mwenzkowski@users.noreply.github.com> Date: Tue, 16 Apr 2019 21:20:48 +0200 Subject: [PATCH 129/191] Don't apply hide_edge_borders to floating windows This change matches i3's behavior. --- sway/tree/view.c | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/sway/tree/view.c b/sway/tree/view.c index 404326619..e8f5a2999 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -221,8 +221,10 @@ void view_autoconfigure(struct sway_view *view) { con->border_top = con->border_bottom = true; con->border_left = con->border_right = true; + double y_offset = 0; + + if (!container_is_floating(con) && ws) { - if (ws) { bool smart = config->hide_edge_borders == E_SMART || (config->hide_edge_borders == E_SMART_NO_GAPS && !gaps_to_edge(view)); @@ -240,24 +242,22 @@ void view_autoconfigure(struct sway_view *view) { int bottom_y = con->y + con->height + con->current_gaps.bottom; con->border_bottom = bottom_y != ws->y + ws->height; } - } - double y_offset = 0; - - // In a tabbed or stacked container, the container's y is the top of the - // title area. We have to offset the surface y by the height of the title, - // bar, and disable any top border because we'll always have the title bar. - list_t *siblings = container_get_siblings(con); - bool show_titlebar = (siblings && siblings->length > 1) - || !config->hide_lone_tab; - if (show_titlebar && !container_is_floating(con)) { - enum sway_container_layout layout = container_parent_layout(con); - if (layout == L_TABBED) { - y_offset = container_titlebar_height(); - con->border_top = false; - } else if (layout == L_STACKED) { - y_offset = container_titlebar_height() * siblings->length; - con->border_top = false; + // In a tabbed or stacked container, the container's y is the top of the + // title area. We have to offset the surface y by the height of the title, + // bar, and disable any top border because we'll always have the title bar. + list_t *siblings = container_get_siblings(con); + bool show_titlebar = (siblings && siblings->length > 1) + || !config->hide_lone_tab; + if (show_titlebar) { + enum sway_container_layout layout = container_parent_layout(con); + if (layout == L_TABBED) { + y_offset = container_titlebar_height(); + con->border_top = false; + } else if (layout == L_STACKED) { + y_offset = container_titlebar_height() * siblings->length; + con->border_top = false; + } } } From aa4deef8a86ccddbd0d9a7d894e72ac329d8960d Mon Sep 17 00:00:00 2001 From: Ashkan Kiani Date: Tue, 16 Apr 2019 17:35:39 -0700 Subject: [PATCH 130/191] Fix the payload type returned by IPC If a client is subscribed and sends a subsequent ipc command which causes event updates, then those event updates override the `client->current_command` and send the incorrect type for the payload associated with the command. Example: SUBSCRIBE {window} RUN_COMMAND focus -> PAYLOAD_TYPE is 0x80000002 for window events Therefore, we decouple the `client->current_command` by passing it as an argument to the ipc_send_reply function, avoiding a data race. The same is done for the `client->payload_length` as a precautionary measure for the same reason. --- sway/ipc-server.c | 136 ++++++++++++++++++++++++---------------------- 1 file changed, 72 insertions(+), 64 deletions(-) diff --git a/sway/ipc-server.c b/sway/ipc-server.c index e133a5bf1..ca1c1b120 100644 --- a/sway/ipc-server.c +++ b/sway/ipc-server.c @@ -47,13 +47,14 @@ struct ipc_client { struct wl_event_source *writable_event_source; struct sway_server *server; int fd; - uint32_t payload_length; uint32_t security_policy; - enum ipc_command_type current_command; enum ipc_command_type subscribed_events; size_t write_buffer_len; size_t write_buffer_size; char *write_buffer; + // The following are for storing data between event_loop calls + uint32_t pending_length; + enum ipc_command_type pending_type; }; struct sockaddr_un *ipc_user_sockaddr(void); @@ -61,8 +62,10 @@ int ipc_handle_connection(int fd, uint32_t mask, void *data); int ipc_client_handle_readable(int client_fd, uint32_t mask, void *data); int ipc_client_handle_writable(int client_fd, uint32_t mask, void *data); 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); +void ipc_client_handle_command(struct ipc_client *client, uint32_t payload_length, + enum ipc_command_type payload_type); +bool ipc_send_reply(struct ipc_client *client, enum ipc_command_type payload_type, + const char *payload, uint32_t payload_length); static void handle_display_destroy(struct wl_listener *listener, void *data) { if (ipc_event_source) { @@ -178,7 +181,7 @@ int ipc_handle_connection(int fd, uint32_t mask, void *data) { return 0; } client->server = server; - client->payload_length = 0; + client->pending_length = 0; client->fd = client_fd; client->subscribed_events = 0; client->event_source = wl_event_loop_add_fd(server->wl_event_loop, @@ -224,9 +227,13 @@ int ipc_client_handle_readable(int client_fd, uint32_t mask, void *data) { } // Wait for the rest of the command payload in case the header has already been read - if (client->payload_length > 0) { - if ((uint32_t)read_available >= client->payload_length) { - ipc_client_handle_command(client); + if (client->pending_length > 0) { + if ((uint32_t)read_available >= client->pending_length) { + // Reset pending values. + uint32_t pending_length = client->pending_length; + enum ipc_command_type pending_type = client->pending_type; + client->pending_length = 0; + ipc_client_handle_command(client, pending_length, pending_type); } return 0; } @@ -251,11 +258,15 @@ int ipc_client_handle_readable(int client_fd, uint32_t mask, void *data) { return 0; } - memcpy(&client->payload_length, &buf32[0], sizeof(buf32[0])); - memcpy(&client->current_command, &buf32[1], sizeof(buf32[1])); + memcpy(&client->pending_length, &buf32[0], sizeof(buf32[0])); + memcpy(&client->pending_type, &buf32[1], sizeof(buf32[1])); - if (read_available - received >= (long)client->payload_length) { - ipc_client_handle_command(client); + if (read_available - received >= (long)client->pending_length) { + // Reset pending values. + uint32_t pending_length = client->pending_length; + enum ipc_command_type pending_type = client->pending_type; + client->pending_length = 0; + ipc_client_handle_command(client, pending_length, pending_type); } return 0; @@ -278,8 +289,8 @@ static void ipc_send_event(const char *json_string, enum ipc_command_type event) if ((client->subscribed_events & event_mask(event)) == 0) { continue; } - client->current_command = event; - if (!ipc_send_reply(client, json_string, (uint32_t) strlen(json_string))) { + if (!ipc_send_reply(client, event, json_string, + (uint32_t)strlen(json_string))) { sway_log_errno(SWAY_INFO, "Unable to send reply to IPC client"); /* ipc_send_reply destroys client on error, which also * removes it from the list, so we need to process @@ -567,20 +578,21 @@ static void ipc_get_marks_callback(struct sway_container *con, void *data) { } } -void ipc_client_handle_command(struct ipc_client *client) { +void ipc_client_handle_command(struct ipc_client *client, uint32_t payload_length, + enum ipc_command_type payload_type) { if (!sway_assert(client != NULL, "client != NULL")) { return; } - char *buf = malloc(client->payload_length + 1); + char *buf = malloc(payload_length + 1); if (!buf) { sway_log_errno(SWAY_INFO, "Unable to allocate IPC payload"); ipc_client_disconnect(client); return; } - if (client->payload_length > 0) { + if (payload_length > 0) { // Payload should be fully available - ssize_t received = recv(client->fd, buf, client->payload_length, 0); + ssize_t received = recv(client->fd, buf, payload_length, 0); if (received == -1) { sway_log_errno(SWAY_INFO, "Unable to receive payload from IPC client"); @@ -589,16 +601,15 @@ void ipc_client_handle_command(struct ipc_client *client) { return; } } - buf[client->payload_length] = '\0'; + buf[payload_length] = '\0'; - bool client_valid = true; - switch (client->current_command) { + switch (payload_type) { case IPC_COMMAND: { char *line = strtok(buf, "\n"); while (line) { size_t line_length = strlen(line); - if (line + line_length >= buf + client->payload_length) { + if (line + line_length >= buf + payload_length) { break; } line[line_length] = ';'; @@ -609,7 +620,7 @@ void ipc_client_handle_command(struct ipc_client *client) { transaction_commit_dirty(); char *json = cmd_results_to_json(res_list); int length = strlen(json); - client_valid = ipc_send_reply(client, json, (uint32_t)length); + ipc_send_reply(client, payload_type, json, (uint32_t)length); free(json); while (res_list->length) { struct cmd_results *results = res_list->items[0]; @@ -623,7 +634,7 @@ void ipc_client_handle_command(struct ipc_client *client) { case IPC_SEND_TICK: { ipc_event_tick(buf); - ipc_send_reply(client, "{\"success\": true}", 17); + ipc_send_reply(client, payload_type, "{\"success\": true}", 17); goto exit_cleanup; } @@ -656,8 +667,8 @@ void ipc_client_handle_command(struct ipc_client *client) { } } const char *json_string = json_object_to_json_string(outputs); - client_valid = - ipc_send_reply(client, json_string, (uint32_t)strlen(json_string)); + ipc_send_reply(client, payload_type, json_string, + (uint32_t)strlen(json_string)); json_object_put(outputs); // free goto exit_cleanup; } @@ -667,8 +678,8 @@ void ipc_client_handle_command(struct ipc_client *client) { json_object *workspaces = json_object_new_array(); root_for_each_workspace(ipc_get_workspaces_callback, workspaces); const char *json_string = json_object_to_json_string(workspaces); - client_valid = - ipc_send_reply(client, json_string, (uint32_t)strlen(json_string)); + ipc_send_reply(client, payload_type, json_string, + (uint32_t)strlen(json_string)); json_object_put(workspaces); // free goto exit_cleanup; } @@ -679,7 +690,7 @@ void ipc_client_handle_command(struct ipc_client *client) { struct json_object *request = json_tokener_parse(buf); if (request == NULL || !json_object_is_type(request, json_type_array)) { const char msg[] = "{\"success\": false}"; - client_valid = ipc_send_reply(client, msg, strlen(msg)); + ipc_send_reply(client, payload_type, msg, strlen(msg)); sway_log(SWAY_INFO, "Failed to parse subscribe request"); goto exit_cleanup; } @@ -707,7 +718,7 @@ void ipc_client_handle_command(struct ipc_client *client) { is_tick = true; } else { const char msg[] = "{\"success\": false}"; - client_valid = ipc_send_reply(client, msg, strlen(msg)); + ipc_send_reply(client, payload_type, msg, strlen(msg)); json_object_put(request); sway_log(SWAY_INFO, "Unsupported event type in subscribe request"); goto exit_cleanup; @@ -716,11 +727,11 @@ void ipc_client_handle_command(struct ipc_client *client) { json_object_put(request); const char msg[] = "{\"success\": true}"; - client_valid = ipc_send_reply(client, msg, strlen(msg)); + ipc_send_reply(client, payload_type, msg, strlen(msg)); if (is_tick) { - client->current_command = IPC_EVENT_TICK; const char tickmsg[] = "{\"first\": true, \"payload\": \"\"}"; - ipc_send_reply(client, tickmsg, strlen(tickmsg)); + ipc_send_reply(client, IPC_EVENT_TICK, tickmsg, + strlen(tickmsg)); } goto exit_cleanup; } @@ -733,8 +744,8 @@ void ipc_client_handle_command(struct ipc_client *client) { json_object_array_add(inputs, ipc_json_describe_input(device)); } const char *json_string = json_object_to_json_string(inputs); - client_valid = - ipc_send_reply(client, json_string, (uint32_t)strlen(json_string)); + ipc_send_reply(client, payload_type, json_string, + (uint32_t)strlen(json_string)); json_object_put(inputs); // free goto exit_cleanup; } @@ -747,8 +758,8 @@ void ipc_client_handle_command(struct ipc_client *client) { json_object_array_add(seats, ipc_json_describe_seat(seat)); } const char *json_string = json_object_to_json_string(seats); - client_valid = - ipc_send_reply(client, json_string, (uint32_t)strlen(json_string)); + ipc_send_reply(client, payload_type, json_string, + (uint32_t)strlen(json_string)); json_object_put(seats); // free goto exit_cleanup; } @@ -757,8 +768,8 @@ void ipc_client_handle_command(struct ipc_client *client) { { json_object *tree = ipc_json_describe_node_recursive(&root->node); const char *json_string = json_object_to_json_string(tree); - client_valid = - ipc_send_reply(client, json_string, (uint32_t) strlen(json_string)); + ipc_send_reply(client, payload_type, json_string, + (uint32_t)strlen(json_string)); json_object_put(tree); goto exit_cleanup; } @@ -768,8 +779,8 @@ void ipc_client_handle_command(struct ipc_client *client) { json_object *marks = json_object_new_array(); root_for_each_container(ipc_get_marks_callback, marks); const char *json_string = json_object_to_json_string(marks); - client_valid = - ipc_send_reply(client, json_string, (uint32_t)strlen(json_string)); + ipc_send_reply(client, payload_type, json_string, + (uint32_t)strlen(json_string)); json_object_put(marks); goto exit_cleanup; } @@ -778,8 +789,8 @@ void ipc_client_handle_command(struct ipc_client *client) { { json_object *version = ipc_json_get_version(); const char *json_string = json_object_to_json_string(version); - client_valid = - ipc_send_reply(client, json_string, (uint32_t)strlen(json_string)); + ipc_send_reply(client, payload_type, json_string, + (uint32_t)strlen(json_string)); json_object_put(version); // free goto exit_cleanup; } @@ -794,9 +805,8 @@ void ipc_client_handle_command(struct ipc_client *client) { json_object_array_add(bars, json_object_new_string(bar->id)); } const char *json_string = json_object_to_json_string(bars); - client_valid = - ipc_send_reply(client, json_string, - (uint32_t)strlen(json_string)); + ipc_send_reply(client, payload_type, json_string, + (uint32_t)strlen(json_string)); json_object_put(bars); // free } else { // Send particular bar's details @@ -810,15 +820,14 @@ void ipc_client_handle_command(struct ipc_client *client) { } if (!bar) { const char *error = "{ \"success\": false, \"error\": \"No bar with that ID\" }"; - client_valid = - ipc_send_reply(client, error, (uint32_t)strlen(error)); + ipc_send_reply(client, payload_type, error, + (uint32_t)strlen(error)); goto exit_cleanup; } json_object *json = ipc_json_describe_bar_config(bar); const char *json_string = json_object_to_json_string(json); - client_valid = - ipc_send_reply(client, json_string, - (uint32_t)strlen(json_string)); + ipc_send_reply(client, payload_type, json_string, + (uint32_t)strlen(json_string)); json_object_put(json); // free } goto exit_cleanup; @@ -832,8 +841,8 @@ void ipc_client_handle_command(struct ipc_client *client) { json_object_array_add(modes, json_object_new_string(mode->name)); } const char *json_string = json_object_to_json_string(modes); - client_valid = - ipc_send_reply(client, json_string, (uint32_t)strlen(json_string)); + ipc_send_reply(client, payload_type, json_string, + (uint32_t)strlen(json_string)); json_object_put(modes); // free goto exit_cleanup; } @@ -843,34 +852,32 @@ void ipc_client_handle_command(struct ipc_client *client) { 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)); + ipc_send_reply(client, payload_type, json_string, + (uint32_t)strlen(json_string)); json_object_put(json); // free goto exit_cleanup; - } + } case IPC_SYNC: { // It was decided sway will not support this, just return success:false const char msg[] = "{\"success\": false}"; - ipc_send_reply(client, msg, strlen(msg)); + ipc_send_reply(client, payload_type, msg, strlen(msg)); goto exit_cleanup; } default: - sway_log(SWAY_INFO, "Unknown IPC command type %i", client->current_command); + sway_log(SWAY_INFO, "Unknown IPC command type %x", payload_type); goto exit_cleanup; } exit_cleanup: - if (client_valid) { - client->payload_length = 0; - } free(buf); return; } -bool ipc_send_reply(struct ipc_client *client, const char *payload, uint32_t payload_length) { +bool ipc_send_reply(struct ipc_client *client, enum ipc_command_type payload_type, + const char *payload, uint32_t payload_length) { assert(payload); char data[IPC_HEADER_SIZE]; @@ -878,7 +885,7 @@ bool ipc_send_reply(struct ipc_client *client, const char *payload, uint32_t pay memcpy(data, ipc_magic, sizeof(ipc_magic)); memcpy(&data32[0], &payload_length, sizeof(payload_length)); - memcpy(&data32[1], &client->current_command, sizeof(client->current_command)); + memcpy(&data32[1], &payload_type, sizeof(payload_type)); while (client->write_buffer_len + IPC_HEADER_SIZE + payload_length >= client->write_buffer_size) { @@ -910,6 +917,7 @@ bool ipc_send_reply(struct ipc_client *client, const char *payload, uint32_t pay ipc_client_handle_writable, client); } - sway_log(SWAY_DEBUG, "Added IPC reply to client %d queue: %s", client->fd, payload); + sway_log(SWAY_DEBUG, "Added IPC reply of type 0x%x to client %d queue: %s", + payload_type, client->fd, payload); return true; } From d7d29fe546ab0acb332013eb380f8193e1d5b9fc Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Wed, 17 Apr 2019 01:57:34 -0400 Subject: [PATCH 131/191] swaymsg: add timeout and type checks This adds a 3 second timeout to the initial reply in swaymsg. This prevents swaymsg from hanging when `swaymsg -t get_{inputs,seats}` is used in i3. The timeout is removed when waiting for a subscribed event or monitoring for subscribed events. This also adds type checks to commands where i3 does not reply with all of the properties that sway does (such as `modes` in `get_outputs`). This is mostly just a behavioral adjustment since swaymsg should run on i3. When running under i3, some command reply's (such as the one for `get_outputs) may have more useful information in the raw json than the pretty printed version. --- common/ipc-client.c | 10 ++++++++++ include/ipc-client.h | 6 ++++++ swaymsg/main.c | 11 ++++++++++- 3 files changed, 26 insertions(+), 1 deletion(-) diff --git a/common/ipc-client.c b/common/ipc-client.c index 13e2dfa32..b6c03d132 100644 --- a/common/ipc-client.c +++ b/common/ipc-client.c @@ -69,6 +69,16 @@ int ipc_open_socket(const char *socket_path) { return socketfd; } +bool ipc_set_recv_timeout(int socketfd, struct timeval tv) { + if (setsockopt(socketfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) == -1) { + sway_log_errno(SWAY_ERROR, "Failed to set ipc recv timeout"); + return false; + } + sway_log(SWAY_DEBUG, "ipc recv timeout set to %ld.%06ld", + tv.tv_sec, tv.tv_usec); + return true; +} + struct ipc_response *ipc_recv_response(int socketfd) { char data[IPC_HEADER_SIZE]; uint32_t *data32 = (uint32_t *)(data + sizeof(ipc_magic)); diff --git a/include/ipc-client.h b/include/ipc-client.h index c9f5b3441..d3895023f 100644 --- a/include/ipc-client.h +++ b/include/ipc-client.h @@ -1,7 +1,9 @@ #ifndef _SWAY_IPC_CLIENT_H #define _SWAY_IPC_CLIENT_H +#include #include +#include #include "ipc.h" @@ -36,5 +38,9 @@ struct ipc_response *ipc_recv_response(int socketfd); * Free ipc_response struct */ void free_ipc_response(struct ipc_response *response); +/** + * Sets the receive timeout for the IPC socket + */ +bool ipc_set_recv_timeout(int socketfd, struct timeval tv); #endif diff --git a/swaymsg/main.c b/swaymsg/main.c index ccc847f5c..518993af7 100644 --- a/swaymsg/main.c +++ b/swaymsg/main.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -238,7 +239,8 @@ static void pretty_print_output(json_object *o) { ); } - size_t modes_len = json_object_array_length(modes); + size_t modes_len = json_object_is_type(modes, json_type_array) + ? json_object_array_length(modes) : 0; if (modes_len > 0) { printf(" Available modes:\n"); for (size_t i = 0; i < modes_len; ++i) { @@ -448,6 +450,8 @@ int main(int argc, char **argv) { int ret = 0; int socketfd = ipc_open_socket(socket_path); + struct timeval timeout = {.tv_sec = 3, .tv_usec = 0}; + ipc_set_recv_timeout(socketfd, timeout); uint32_t len = strlen(command); char *resp = ipc_single_command(socketfd, type, command, &len); if (!quiet) { @@ -478,6 +482,11 @@ int main(int argc, char **argv) { free(resp); if (type == IPC_SUBSCRIBE && ret == 0) { + // Remove the timeout for subscribed events + timeout.tv_sec = 0; + timeout.tv_usec = 0; + ipc_set_recv_timeout(socketfd, timeout); + do { struct ipc_response *reply = ipc_recv_response(socketfd); if (!reply) { From 852f1f0b349b63fe3c71e39ab1d3e87642a5c2d4 Mon Sep 17 00:00:00 2001 From: Alex Maese Date: Fri, 12 Apr 2019 11:26:23 -0500 Subject: [PATCH 132/191] Create unbindsym and unbindcode commands --- include/sway/commands.h | 2 + sway/commands.c | 2 + sway/commands/bind.c | 114 ++++++++++++++++++++++++++++------------ sway/sway.5.scd | 8 +++ 4 files changed, 91 insertions(+), 35 deletions(-) diff --git a/include/sway/commands.h b/include/sway/commands.h index 2b66904c6..96d68e9e2 100644 --- a/include/sway/commands.h +++ b/include/sway/commands.h @@ -181,6 +181,8 @@ sway_cmd cmd_title_align; sway_cmd cmd_title_format; sway_cmd cmd_titlebar_border_thickness; sway_cmd cmd_titlebar_padding; +sway_cmd cmd_unbindcode; +sway_cmd cmd_unbindsym; sway_cmd cmd_unmark; sway_cmd cmd_urgent; sway_cmd cmd_workspace; diff --git a/sway/commands.c b/sway/commands.c index d0bf402bd..267c7de81 100644 --- a/sway/commands.c +++ b/sway/commands.c @@ -92,6 +92,8 @@ static struct cmd_handler handlers[] = { { "title_align", cmd_title_align }, { "titlebar_border_thickness", cmd_titlebar_border_thickness }, { "titlebar_padding", cmd_titlebar_padding }, + { "unbindcode", cmd_unbindcode }, + { "unbindsym", cmd_unbindsym }, { "workspace", cmd_workspace }, { "workspace_auto_back_and_forth", cmd_ws_auto_back_and_forth }, }; diff --git a/sway/commands/bind.c b/sway/commands/bind.c index 9a937c611..fad7f8500 100644 --- a/sway/commands/bind.c +++ b/sway/commands/bind.c @@ -196,12 +196,70 @@ static struct cmd_results *identify_key(const char* name, bool first_key, return NULL; } +static struct cmd_results *binding_add(struct sway_binding *binding, + list_t *mode_bindings, const char *bindtype, + const char *keycombo, bool warn) { + // overwrite the binding if it already exists + bool overwritten = false; + for (int i = 0; i < mode_bindings->length; ++i) { + struct sway_binding *config_binding = mode_bindings->items[i]; + if (binding_key_compare(binding, config_binding)) { + sway_log(SWAY_INFO, "Overwriting binding '%s' for device '%s' " + "to `%s` from `%s`", keycombo, binding->input, + binding->command, config_binding->command); + if (warn) { + config_add_swaynag_warning("Overwriting binding" + "'%s' for device '%s' to `%s` from `%s`", + keycombo, binding->input, binding->command, + config_binding->command); + } + free_sway_binding(config_binding); + mode_bindings->items[i] = binding; + overwritten = true; + } + } + + if (!overwritten) { + list_add(mode_bindings, binding); + sway_log(SWAY_DEBUG, "%s - Bound %s to command `%s` for device '%s'", + bindtype, keycombo, binding->command, binding->input); + } + + return cmd_results_new(CMD_SUCCESS, NULL); +} + +static struct cmd_results *binding_remove(struct sway_binding *binding, + list_t *mode_bindings, const char *bindtype, + const char *keycombo) { + for (int i = 0; i < mode_bindings->length; ++i) { + struct sway_binding *config_binding = mode_bindings->items[i]; + if (binding_key_compare(binding, config_binding)) { + sway_log(SWAY_DEBUG, "%s - Unbound `%s` from device '%s'", + bindtype, keycombo, binding->input); + free_sway_binding(config_binding); + free_sway_binding(binding); + list_del(mode_bindings, i); + return cmd_results_new(CMD_SUCCESS, NULL); + } + } + free_sway_binding(binding); + return cmd_results_new(CMD_FAILURE, "Could not find binding `%s` " + "for the given flags", keycombo); +} + static struct cmd_results *cmd_bindsym_or_bindcode(int argc, char **argv, - bool bindcode) { - const char *bindtype = bindcode ? "bindcode" : "bindsym"; + bool bindcode, bool unbind) { + const char *bindtype; + int minargs = 2; + if (unbind) { + bindtype = bindcode ? "unbindcode" : "unbindsym"; + minargs--; + } else { + bindtype = bindcode ? "bindcode": "bindsym"; + } struct cmd_results *error = NULL; - if ((error = checkarg(argc, bindtype, EXPECTED_AT_LEAST, 2))) { + if ((error = checkarg(argc, bindtype, EXPECTED_AT_LEAST, minargs))) { return error; } @@ -248,15 +306,14 @@ static struct cmd_results *cmd_bindsym_or_bindcode(int argc, char **argv, BINDING_MOUSECODE : BINDING_MOUSESYM; } - if (argc < 2) { + if (argc < minargs) { free_sway_binding(binding); return cmd_results_new(CMD_FAILURE, "Invalid %s command " - "(expected at least 2 non-option arguments, got %d)", bindtype, argc); + "(expected at least %d non-option arguments, got %d)", + bindtype, minargs, argc); } - binding->command = join_args(argv + 1, argc - 1); - list_t *split = split_string(argv[0], "+"); for (int i = 0; i < split->length; ++i) { // Check for a modifier key @@ -287,7 +344,6 @@ static struct cmd_results *cmd_bindsym_or_bindcode(int argc, char **argv, list_add(binding->keys, key); } list_free_items_and_destroy(split); - binding->order = binding_order++; // refine region of interest for mouse binding once we are certain // that this is one @@ -310,41 +366,29 @@ static struct cmd_results *cmd_bindsym_or_bindcode(int argc, char **argv, mode_bindings = config->current_mode->mouse_bindings; } - // overwrite the binding if it already exists - bool overwritten = false; - for (int i = 0; i < mode_bindings->length; ++i) { - struct sway_binding *config_binding = mode_bindings->items[i]; - if (binding_key_compare(binding, config_binding)) { - sway_log(SWAY_INFO, "Overwriting binding '%s' for device '%s' " - "to `%s` from `%s`", argv[0], binding->input, - binding->command, config_binding->command); - if (warn) { - config_add_swaynag_warning("Overwriting binding" - "'%s' for device '%s' to `%s` from `%s`", - argv[0], binding->input, binding->command, - config_binding->command); - } - free_sway_binding(config_binding); - mode_bindings->items[i] = binding; - overwritten = true; - } + if (unbind) { + return binding_remove(binding, mode_bindings, bindtype, argv[0]); } - if (!overwritten) { - list_add(mode_bindings, binding); - } - - sway_log(SWAY_DEBUG, "%s - Bound %s to command `%s` for device '%s'", - bindtype, argv[0], binding->command, binding->input); - return cmd_results_new(CMD_SUCCESS, NULL); + binding->command = join_args(argv + 1, argc - 1); + binding->order = binding_order++; + return binding_add(binding, mode_bindings, bindtype, argv[0], warn); } struct cmd_results *cmd_bindsym(int argc, char **argv) { - return cmd_bindsym_or_bindcode(argc, argv, false); + return cmd_bindsym_or_bindcode(argc, argv, false, false); } struct cmd_results *cmd_bindcode(int argc, char **argv) { - return cmd_bindsym_or_bindcode(argc, argv, true); + return cmd_bindsym_or_bindcode(argc, argv, true, false); +} + +struct cmd_results *cmd_unbindsym(int argc, char **argv) { + return cmd_bindsym_or_bindcode(argc, argv, false, true); +} + +struct cmd_results *cmd_unbindcode(int argc, char **argv) { + return cmd_bindsym_or_bindcode(argc, argv, true, true); } struct cmd_results *cmd_bindswitch(int argc, char **argv) { diff --git a/sway/sway.5.scd b/sway/sway.5.scd index 1de06d3e5..5d94ecedb 100644 --- a/sway/sway.5.scd +++ b/sway/sway.5.scd @@ -644,6 +644,14 @@ The default colors are: to _yes_, the marks will be shown on the _left_ side instead of the _right_ side. +*unbindsym* [--whole-window] [--border] [--exclude-titlebar] [--release] [--input-device=] + Removes the binding for _key combo_ that was previously bound with the + given flags. If _input-device_ is given, only the binding for that + input device will be unbound. + + *unbindcode* [--whole-window] [--border] [--exclude-titlebar] [--release] [input-device=] + is also available for unbinding with key/button codes instead of key/button names. + *unmark* [] *unmark* will remove _identifier_ from the list of current marks on a window. If _identifier_ is omitted, all marks are removed. From 8d4f8aea4654c6071e243900b5dbf18e1cd62fd6 Mon Sep 17 00:00:00 2001 From: Alex Maese Date: Fri, 12 Apr 2019 11:28:48 -0500 Subject: [PATCH 133/191] Add unbindswitch command --- include/sway/commands.h | 1 + sway/commands.c | 1 + sway/commands/bind.c | 152 ++++++++++++++++++++++++++-------------- sway/sway.5.scd | 3 + 4 files changed, 103 insertions(+), 54 deletions(-) diff --git a/include/sway/commands.h b/include/sway/commands.h index 96d68e9e2..0347ad5e3 100644 --- a/include/sway/commands.h +++ b/include/sway/commands.h @@ -182,6 +182,7 @@ sway_cmd cmd_title_format; sway_cmd cmd_titlebar_border_thickness; sway_cmd cmd_titlebar_padding; sway_cmd cmd_unbindcode; +sway_cmd cmd_unbindswitch; sway_cmd cmd_unbindsym; sway_cmd cmd_unmark; sway_cmd cmd_urgent; diff --git a/sway/commands.c b/sway/commands.c index 267c7de81..237bfc281 100644 --- a/sway/commands.c +++ b/sway/commands.c @@ -93,6 +93,7 @@ static struct cmd_handler handlers[] = { { "titlebar_border_thickness", cmd_titlebar_border_thickness }, { "titlebar_padding", cmd_titlebar_padding }, { "unbindcode", cmd_unbindcode }, + { "unbindswitch", cmd_unbindswitch }, { "unbindsym", cmd_unbindsym }, { "workspace", cmd_workspace }, { "workspace_auto_back_and_forth", cmd_ws_auto_back_and_forth }, diff --git a/sway/commands/bind.c b/sway/commands/bind.c index fad7f8500..dc7e0b190 100644 --- a/sway/commands/bind.c +++ b/sway/commands/bind.c @@ -196,6 +196,59 @@ static struct cmd_results *identify_key(const char* name, bool first_key, return NULL; } +static struct cmd_results *switch_binding_add( + struct sway_switch_binding *binding, const char *bindtype, + const char *switchcombo, bool warn) { + list_t *mode_bindings = config->current_mode->switch_bindings; + // overwrite the binding if it already exists + bool overwritten = false; + for (int i = 0; i < mode_bindings->length; ++i) { + struct sway_switch_binding *config_binding = mode_bindings->items[i]; + if (binding_switch_compare(binding, config_binding)) { + sway_log(SWAY_INFO, "Overwriting binding '%s' to `%s` from `%s`", + switchcombo, binding->command, config_binding->command); + if (warn) { + config_add_swaynag_warning("Overwriting binding" + "'%s' to `%s` from `%s`", + switchcombo, binding->command, + config_binding->command); + } + free_switch_binding(config_binding); + mode_bindings->items[i] = binding; + overwritten = true; + } + } + + if (!overwritten) { + list_add(mode_bindings, binding); + sway_log(SWAY_DEBUG, "%s - Bound %s to command `%s`", + bindtype, switchcombo, binding->command); + } + + return cmd_results_new(CMD_SUCCESS, NULL); +} + +static struct cmd_results *switch_binding_remove( + struct sway_switch_binding *binding, const char *bindtype, + const char *switchcombo) { + list_t *mode_bindings = config->current_mode->switch_bindings; + for (int i = 0; i < mode_bindings->length; ++i) { + struct sway_switch_binding *config_binding = mode_bindings->items[i]; + if (binding_switch_compare(binding, config_binding)) { + free_switch_binding(config_binding); + free_switch_binding(binding); + list_del(mode_bindings, i); + sway_log(SWAY_DEBUG, "%s - Unbound %s switch", + bindtype, switchcombo); + return cmd_results_new(CMD_SUCCESS, NULL); + } + } + + free_switch_binding(binding); + return cmd_results_new(CMD_FAILURE, "Could not find switch binding `%s`", + switchcombo); +} + static struct cmd_results *binding_add(struct sway_binding *binding, list_t *mode_bindings, const char *bindtype, const char *keycombo, bool warn) { @@ -375,26 +428,17 @@ static struct cmd_results *cmd_bindsym_or_bindcode(int argc, char **argv, return binding_add(binding, mode_bindings, bindtype, argv[0], warn); } -struct cmd_results *cmd_bindsym(int argc, char **argv) { - return cmd_bindsym_or_bindcode(argc, argv, false, false); -} - -struct cmd_results *cmd_bindcode(int argc, char **argv) { - return cmd_bindsym_or_bindcode(argc, argv, true, false); -} - -struct cmd_results *cmd_unbindsym(int argc, char **argv) { - return cmd_bindsym_or_bindcode(argc, argv, false, true); -} - -struct cmd_results *cmd_unbindcode(int argc, char **argv) { - return cmd_bindsym_or_bindcode(argc, argv, true, true); -} - -struct cmd_results *cmd_bindswitch(int argc, char **argv) { +struct cmd_results *cmd_bind_or_unbind_switch(int argc, char **argv, + bool unbind) { + int minargs = 2; + char *bindtype = "bindswitch"; + if (unbind) { + minargs--; + bindtype = "unbindswitch"; + } struct cmd_results *error = NULL; - if ((error = checkarg(argc, "bindswitch", EXPECTED_AT_LEAST, 2))) { + if ((error = checkarg(argc, bindtype, EXPECTED_AT_LEAST, minargs))) { return error; } struct sway_switch_binding *binding = calloc(1, sizeof(struct sway_switch_binding)); @@ -417,20 +461,19 @@ struct cmd_results *cmd_bindswitch(int argc, char **argv) { argc--; } - if (argc < 2) { + if (argc < minargs) { free(binding); return cmd_results_new(CMD_FAILURE, - "Invalid bindswitch command (expected at least 2 " - "non-option arguments, got %d)", argc); + "Invalid %s command (expected at least %d " + "non-option arguments, got %d)", bindtype, minargs, argc); } - binding->command = join_args(argv + 1, argc - 1); list_t *split = split_string(argv[0], ":"); if (split->length != 2) { free_switch_binding(binding); return cmd_results_new(CMD_FAILURE, - "Invalid bindswitch command (expected two arguments with " - "format : , got %d)", argc); + "Invalid %s command (expected binding with the form " + ":)", bindtype, argc); } if (strcmp(split->items[0], "tablet") == 0) { binding->type = WLR_SWITCH_TYPE_TABLET_MODE; @@ -439,8 +482,8 @@ struct cmd_results *cmd_bindswitch(int argc, char **argv) { } else { free_switch_binding(binding); return cmd_results_new(CMD_FAILURE, - "Invalid bindswitch command (expected switch binding: " - "unknown switch %s)", split->items[0]); + "Invalid %s command (expected switch binding: " + "unknown switch %s)", bindtype, split->items[0]); } if (strcmp(split->items[1], "on") == 0) { binding->state = WLR_SWITCH_STATE_ON; @@ -451,40 +494,41 @@ struct cmd_results *cmd_bindswitch(int argc, char **argv) { } else { free_switch_binding(binding); return cmd_results_new(CMD_FAILURE, - "Invalid bindswitch command " - "(expected switch state: unknown state %d)", - split->items[0]); + "Invalid %s command " + "(expected switch state: unknown state %d)", + bindtype, split->items[0]); } list_free_items_and_destroy(split); - list_t *mode_bindings = config->current_mode->switch_bindings; - - // overwrite the binding if it already exists - bool overwritten = false; - for (int i = 0; i < mode_bindings->length; ++i) { - struct sway_switch_binding *config_binding = mode_bindings->items[i]; - if (binding_switch_compare(binding, config_binding)) { - sway_log(SWAY_INFO, "Overwriting binding '%s' to `%s` from `%s`", - argv[0], binding->command, config_binding->command); - if (warn) { - config_add_swaynag_warning("Overwriting binding" - "'%s' to `%s` from `%s`", - argv[0], binding->command, - config_binding->command); - } - free_switch_binding(config_binding); - mode_bindings->items[i] = binding; - overwritten = true; - } + if (unbind) { + return switch_binding_remove(binding, bindtype, argv[0]); } + binding->command = join_args(argv + 1, argc - 1); + return switch_binding_add(binding, bindtype, argv[0], warn); +} - if (!overwritten) { - list_add(mode_bindings, binding); - } +struct cmd_results *cmd_bindsym(int argc, char **argv) { + return cmd_bindsym_or_bindcode(argc, argv, false, false); +} - sway_log(SWAY_DEBUG, "bindswitch - Bound %s to command `%s`", - argv[0], binding->command); - return cmd_results_new(CMD_SUCCESS, NULL); +struct cmd_results *cmd_bindcode(int argc, char **argv) { + return cmd_bindsym_or_bindcode(argc, argv, true, false); +} + +struct cmd_results *cmd_unbindsym(int argc, char **argv) { + return cmd_bindsym_or_bindcode(argc, argv, false, true); +} + +struct cmd_results *cmd_unbindcode(int argc, char **argv) { + return cmd_bindsym_or_bindcode(argc, argv, true, true); +} + +struct cmd_results *cmd_bindswitch(int argc, char **argv) { + return cmd_bind_or_unbind_switch(argc, argv, false); +} + +struct cmd_results *cmd_unbindswitch(int argc, char **argv) { + return cmd_bind_or_unbind_switch(argc, argv, true); } /** diff --git a/sway/sway.5.scd b/sway/sway.5.scd index 5d94ecedb..801936519 100644 --- a/sway/sway.5.scd +++ b/sway/sway.5.scd @@ -644,6 +644,9 @@ The default colors are: to _yes_, the marks will be shown on the _left_ side instead of the _right_ side. +*unbindswitch* : + Removes a binding for when changes to . + *unbindsym* [--whole-window] [--border] [--exclude-titlebar] [--release] [--input-device=] Removes the binding for _key combo_ that was previously bound with the given flags. If _input-device_ is given, only the binding for that From 35ddd9aab3c386345176d7ea1f5814f152ba16bd Mon Sep 17 00:00:00 2001 From: Alex Maese Date: Fri, 12 Apr 2019 11:30:36 -0500 Subject: [PATCH 134/191] Add unbindsym/unbindcode command for swaybar --- include/sway/commands.h | 2 + sway/commands/bar.c | 2 + sway/commands/bar/bind.c | 108 ++++++++++++++++++++++++++++----------- sway/sway-bar.5.scd | 6 +++ 4 files changed, 88 insertions(+), 30 deletions(-) diff --git a/include/sway/commands.h b/include/sway/commands.h index 0347ad5e3..cfe2aa079 100644 --- a/include/sway/commands.h +++ b/include/sway/commands.h @@ -217,6 +217,8 @@ sway_cmd bar_cmd_tray_bindcode; sway_cmd bar_cmd_tray_bindsym; sway_cmd bar_cmd_tray_output; sway_cmd bar_cmd_tray_padding; +sway_cmd bar_cmd_unbindcode; +sway_cmd bar_cmd_unbindsym; sway_cmd bar_cmd_wrap_scroll; sway_cmd bar_cmd_workspace_buttons; diff --git a/sway/commands/bar.c b/sway/commands/bar.c index 82441f9e4..73be70406 100644 --- a/sway/commands/bar.c +++ b/sway/commands/bar.c @@ -32,6 +32,8 @@ static struct cmd_handler bar_handlers[] = { { "tray_bindsym", bar_cmd_tray_bindsym }, { "tray_output", bar_cmd_tray_output }, { "tray_padding", bar_cmd_tray_padding }, + { "unbindcode", bar_cmd_unbindcode }, + { "unbindsym", bar_cmd_unbindsym }, { "workspace_buttons", bar_cmd_workspace_buttons }, { "wrap_scroll", bar_cmd_wrap_scroll }, }; diff --git a/sway/commands/bar/bind.c b/sway/commands/bar/bind.c index 17030a05f..f94b48118 100644 --- a/sway/commands/bar/bind.c +++ b/sway/commands/bar/bind.c @@ -9,10 +9,70 @@ #include "log.h" #include "stringop.h" -static struct cmd_results *bar_cmd_bind(int argc, char **argv, bool code) { - const char *command = code ? "bar bindcode" : "bar bindsym"; +static struct cmd_results *binding_add(struct bar_binding *binding, + list_t *mode_bindings) { + const char *name = get_mouse_button_name(binding->button); + bool overwritten = false; + for (int i = 0; i < mode_bindings->length; i++) { + struct bar_binding *other = mode_bindings->items[i]; + if (other->button == binding->button && + other->release == binding->release) { + overwritten = true; + mode_bindings->items[i] = binding; + free_bar_binding(other); + sway_log(SWAY_DEBUG, "[bar %s] Updated binding for %u (%s)%s", + config->current_bar->id, binding->button, name, + binding->release ? " - release" : ""); + break; + } + } + if (!overwritten) { + list_add(mode_bindings, binding); + sway_log(SWAY_DEBUG, "[bar %s] Added binding for %u (%s)%s", + config->current_bar->id, binding->button, name, + binding->release ? " - release" : ""); + } + return cmd_results_new(CMD_SUCCESS, NULL); +} + +static struct cmd_results *binding_remove(struct bar_binding *binding, + list_t *mode_bindings) { + const char *name = get_mouse_button_name(binding->button); + for (int i = 0; i < mode_bindings->length; i++) { + struct bar_binding *other = mode_bindings->items[i]; + if (other->button == binding->button && + other->release == binding->release) { + sway_log(SWAY_DEBUG, "[bar %s] Unbound binding for %u (%s)%s", + config->current_bar->id, binding->button, name, + binding->release ? " - release" : ""); + free_bar_binding(other); + free_bar_binding(binding); + list_del(mode_bindings, i); + return cmd_results_new(CMD_SUCCESS, NULL); + } + } + + struct cmd_results *error = cmd_results_new(CMD_FAILURE, "Could not " + "find binding for [bar %s]" " Button %u (%s)%s", + config->current_bar->id, binding->button, name, + binding->release ? " - release" : ""); + free_bar_binding(binding); + return error; +} + +static struct cmd_results *bar_cmd_bind(int argc, char **argv, bool code, + bool unbind) { + int minargs = 2; + const char *command; + if (unbind) { + minargs--; + command = code ? "bar unbindcode" : "bar unbindsym"; + } else { + command = code ? "bar bindcode" : "bar bindsym"; + } + struct cmd_results *error = NULL; - if ((error = checkarg(argc, command, EXPECTED_AT_LEAST, 2))) { + if ((error = checkarg(argc, command, EXPECTED_AT_LEAST, minargs))) { return error; } if (!config->current_bar) { @@ -46,39 +106,27 @@ static struct cmd_results *bar_cmd_bind(int argc, char **argv, bool code) { free_bar_binding(binding); return cmd_results_new(CMD_INVALID, "Unknown button %s", argv[0]); } - const char *name = get_mouse_button_name(binding->button); + list_t *bindings = config->current_bar->bindings; + if (unbind) { + return binding_remove(binding, bindings); + } binding->command = join_args(argv + 1, argc - 1); - - list_t *bindings = config->current_bar->bindings; - bool overwritten = false; - for (int i = 0; i < bindings->length; i++) { - struct bar_binding *other = bindings->items[i]; - if (other->button == binding->button && - other->release == binding->release) { - overwritten = true; - bindings->items[i] = binding; - free_bar_binding(other); - sway_log(SWAY_DEBUG, "[bar %s] Updated binding for %u (%s)%s", - config->current_bar->id, binding->button, name, - binding->release ? " - release" : ""); - break; - } - } - if (!overwritten) { - list_add(bindings, binding); - sway_log(SWAY_DEBUG, "[bar %s] Added binding for %u (%s)%s", - config->current_bar->id, binding->button, name, - binding->release ? " - release" : ""); - } - - return cmd_results_new(CMD_SUCCESS, NULL); + return binding_add(binding, bindings); } struct cmd_results *bar_cmd_bindcode(int argc, char **argv) { - return bar_cmd_bind(argc, argv, true); + return bar_cmd_bind(argc, argv, true, false); } struct cmd_results *bar_cmd_bindsym(int argc, char **argv) { - return bar_cmd_bind(argc, argv, false); + return bar_cmd_bind(argc, argv, false, false); +} + +struct cmd_results *bar_cmd_unbindcode(int argc, char **argv) { + return bar_cmd_bind(argc, argv, true, true); +} + +struct cmd_results *bar_cmd_unbindsym(int argc, char **argv) { + return bar_cmd_bind(argc, argv, false, true); } diff --git a/sway/sway-bar.5.scd b/sway/sway-bar.5.scd index 1f4ceaf2f..a3df11601 100644 --- a/sway/sway-bar.5.scd +++ b/sway/sway-bar.5.scd @@ -112,6 +112,12 @@ Sway allows configuring swaybar in the sway configuration file. the bar. This value will be multiplied by the output scale. The default is _3_. +*unbindcode* [--release] + Removes the binding with the given . + +*unbindsym* [--release] button[1-9]| + Removes the binding with the given