aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--client/pool-buffer.c1
-rw-r--r--common/gesture.c1
-rw-r--r--common/ipc-client.c1
-rw-r--r--common/log.c1
-rw-r--r--common/loop.c1
-rw-r--r--common/stringop.c1
-rw-r--r--common/util.c1
-rw-r--r--include/sway/config.h29
-rw-r--r--include/sway/desktop/launcher.h4
-rw-r--r--include/sway/desktop/transaction.h3
-rw-r--r--include/sway/input/cursor.h2
-rw-r--r--include/sway/input/input-manager.h3
-rw-r--r--include/sway/input/seat.h7
-rw-r--r--include/sway/input/text_input.h3
-rw-r--r--include/sway/input/text_input_popup.h20
-rw-r--r--include/sway/layers.h5
-rw-r--r--include/sway/output.h2
-rw-r--r--include/sway/server.h19
-rw-r--r--include/sway/tree/view.h17
-rw-r--r--meson.build1
-rwxr-xr-xrelease.sh31
-rw-r--r--sway/commands.c3
-rw-r--r--sway/commands/assign.c1
-rw-r--r--sway/commands/bar.c1
-rw-r--r--sway/commands/bar/font.c1
-rw-r--r--sway/commands/bar/hidden_state.c1
-rw-r--r--sway/commands/bar/icon_theme.c1
-rw-r--r--sway/commands/bar/id.c1
-rw-r--r--sway/commands/bar/mode.c1
-rw-r--r--sway/commands/bar/output.c1
-rw-r--r--sway/commands/bar/position.c1
-rw-r--r--sway/commands/bar/separator_symbol.c1
-rw-r--r--sway/commands/bar/tray_output.c1
-rw-r--r--sway/commands/bind.c1
-rw-r--r--sway/commands/exec_always.c1
-rw-r--r--sway/commands/font.c1
-rw-r--r--sway/commands/gesture.c1
-rw-r--r--sway/commands/input/calibration_matrix.c1
-rw-r--r--sway/commands/input/map_from_region.c1
-rw-r--r--sway/commands/input/map_to_output.c1
-rw-r--r--sway/commands/input/map_to_region.c1
-rw-r--r--sway/commands/input/xkb_file.c1
-rw-r--r--sway/commands/input/xkb_layout.c1
-rw-r--r--sway/commands/input/xkb_model.c1
-rw-r--r--sway/commands/input/xkb_numlock.c1
-rw-r--r--sway/commands/input/xkb_options.c1
-rw-r--r--sway/commands/input/xkb_rules.c1
-rw-r--r--sway/commands/input/xkb_switch_layout.c1
-rw-r--r--sway/commands/input/xkb_variant.c1
-rw-r--r--sway/commands/mark.c1
-rw-r--r--sway/commands/mode.c1
-rw-r--r--sway/commands/move.c10
-rw-r--r--sway/commands/output.c9
-rw-r--r--sway/commands/output/background.c1
-rw-r--r--sway/commands/output/toggle.c2
-rw-r--r--sway/commands/primary_selection.c4
-rw-r--r--sway/commands/reload.c1
-rw-r--r--sway/commands/seat/attach.c1
-rw-r--r--sway/commands/seat/cursor.c15
-rw-r--r--sway/commands/seat/hide_cursor.c1
-rw-r--r--sway/commands/seat/idle.c1
-rw-r--r--sway/commands/seat/xcursor_theme.c1
-rw-r--r--sway/commands/set.c1
-rw-r--r--sway/commands/show_marks.c1
-rw-r--r--sway/commands/swap.c1
-rw-r--r--sway/commands/title_format.c1
-rw-r--r--sway/commands/unmark.c1
-rw-r--r--sway/commands/workspace.c1
-rw-r--r--sway/commands/xwayland.c2
-rw-r--r--sway/config.c103
-rw-r--r--sway/config/bar.c1
-rw-r--r--sway/config/input.c1
-rw-r--r--sway/config/output.c788
-rw-r--r--sway/config/seat.c1
-rw-r--r--sway/criteria.c1
-rw-r--r--sway/desktop/launcher.c16
-rw-r--r--sway/desktop/layer_shell.c29
-rw-r--r--sway/desktop/output.c139
-rw-r--r--sway/desktop/transaction.c21
-rw-r--r--sway/desktop/xdg_shell.c19
-rw-r--r--sway/desktop/xwayland.c1
-rw-r--r--sway/input/cursor.c26
-rw-r--r--sway/input/input-manager.c39
-rw-r--r--sway/input/keyboard.c1
-rw-r--r--sway/input/seat.c26
-rw-r--r--sway/input/seatop_default.c35
-rw-r--r--sway/input/seatop_down.c9
-rw-r--r--sway/input/seatop_move_floating.c3
-rw-r--r--sway/input/seatop_move_tiling.c3
-rw-r--r--sway/input/seatop_resize_floating.c3
-rw-r--r--sway/input/seatop_resize_tiling.c3
-rw-r--r--sway/input/tablet.c1
-rw-r--r--sway/input/text_input.c227
-rw-r--r--sway/ipc-json.c21
-rw-r--r--sway/ipc-server.c1
-rw-r--r--sway/lock.c2
-rw-r--r--sway/main.c1
-rw-r--r--sway/server.c70
-rw-r--r--sway/sway-ipc.7.scd8
-rw-r--r--sway/sway-output.5.scd12
-rw-r--r--sway/sway.5.scd6
-rw-r--r--sway/sway_text_node.c15
-rw-r--r--sway/swaynag.c1
-rw-r--r--sway/tree/arrange.c1
-rw-r--r--sway/tree/container.c6
-rw-r--r--sway/tree/node.c1
-rw-r--r--sway/tree/output.c1
-rw-r--r--sway/tree/root.c1
-rw-r--r--sway/tree/view.c49
-rw-r--r--sway/tree/workspace.c1
-rw-r--r--sway/xdg_activation_v1.c25
-rw-r--r--swaybar/bar.c1
-rw-r--r--swaybar/config.c1
-rw-r--r--swaybar/i3bar.c1
-rw-r--r--swaybar/ipc.c1
-rw-r--r--swaybar/main.c1
-rw-r--r--swaybar/render.c1
-rw-r--r--swaybar/status_line.c1
-rw-r--r--swaybar/tray/host.c1
-rw-r--r--swaybar/tray/icon.c1
-rw-r--r--swaybar/tray/item.c1
-rw-r--r--swaybar/tray/watcher.c1
-rw-r--r--swaymsg/main.c1
-rw-r--r--swaynag/config.c1
-rw-r--r--swaynag/main.c1
-rw-r--r--swaynag/swaynag.c1
-rw-r--r--swaynag/types.c1
127 files changed, 1327 insertions, 646 deletions
diff --git a/client/pool-buffer.c b/client/pool-buffer.c
index 3546b897..c47c40eb 100644
--- a/client/pool-buffer.c
+++ b/client/pool-buffer.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809
2#include <assert.h> 1#include <assert.h>
3#include <cairo.h> 2#include <cairo.h>
4#include <errno.h> 3#include <errno.h>
diff --git a/common/gesture.c b/common/gesture.c
index 58170443..272aa837 100644
--- a/common/gesture.c
+++ b/common/gesture.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include "gesture.h" 1#include "gesture.h"
3 2
4#include <math.h> 3#include <math.h>
diff --git a/common/ipc-client.c b/common/ipc-client.c
index d30212d2..a0be2b2d 100644
--- a/common/ipc-client.c
+++ b/common/ipc-client.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <stdio.h> 1#include <stdio.h>
3#include <stdint.h> 2#include <stdint.h>
4#include <stdlib.h> 3#include <stdlib.h>
diff --git a/common/log.c b/common/log.c
index 483420e7..3eacdb34 100644
--- a/common/log.c
+++ b/common/log.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200112L
2#include <signal.h> 1#include <signal.h>
3#include <stdarg.h> 2#include <stdarg.h>
4#include <stdio.h> 3#include <stdio.h>
diff --git a/common/loop.c b/common/loop.c
index 80fe18ea..b99c6d55 100644
--- a/common/loop.c
+++ b/common/loop.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200112L
2#include <limits.h> 1#include <limits.h>
3#include <string.h> 2#include <string.h>
4#include <stdbool.h> 3#include <stdbool.h>
diff --git a/common/stringop.c b/common/stringop.c
index c503143a..16d04917 100644
--- a/common/stringop.c
+++ b/common/stringop.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <ctype.h> 1#include <ctype.h>
3#include <stdarg.h> 2#include <stdarg.h>
4#include <stdbool.h> 3#include <stdbool.h>
diff --git a/common/util.c b/common/util.c
index 5d4c0673..7c492bcb 100644
--- a/common/util.c
+++ b/common/util.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <ctype.h> 1#include <ctype.h>
3#include <fcntl.h> 2#include <fcntl.h>
4#include <math.h> 3#include <math.h>
diff --git a/include/sway/config.h b/include/sway/config.h
index f9da1967..5ccc3e77 100644
--- a/include/sway/config.h
+++ b/include/sway/config.h
@@ -292,6 +292,14 @@ struct output_config {
292}; 292};
293 293
294/** 294/**
295 * An output config pre-matched to an output
296 */
297struct matched_output_config {
298 struct sway_output *output;
299 struct output_config *config;
300};
301
302/**
295 * Stores size of gaps for each side 303 * Stores size of gaps for each side
296 */ 304 */
297struct side_gaps { 305struct side_gaps {
@@ -680,20 +688,25 @@ const char *sway_output_scale_filter_to_string(enum scale_filter_mode scale_filt
680 688
681struct output_config *new_output_config(const char *name); 689struct output_config *new_output_config(const char *name);
682 690
683void merge_output_config(struct output_config *dst, struct output_config *src); 691bool apply_output_configs(struct matched_output_config *configs,
692 size_t configs_len, bool test_only, bool degrade_to_off);
684 693
685bool apply_output_config(struct output_config *oc, struct sway_output *output); 694void apply_all_output_configs(void);
686 695
687bool test_output_config(struct output_config *oc, struct sway_output *output); 696void sort_output_configs_by_priority(struct matched_output_config *configs,
697 size_t configs_len);
688 698
689struct output_config *store_output_config(struct output_config *oc); 699/**
700 * store_output_config stores a new output config. An output may be matched by
701 * three different config types, in order of precedence: Identifier, name and
702 * wildcard. When storing a config type of lower precedence, assume that the
703 * user wants the config to take immediate effect by superseding (clearing) the
704 * same values from higher presedence configuration.
705 */
706void store_output_config(struct output_config *oc);
690 707
691struct output_config *find_output_config(struct sway_output *output); 708struct output_config *find_output_config(struct sway_output *output);
692 709
693void apply_output_config_to_outputs(struct output_config *oc);
694
695void reset_outputs(void);
696
697void free_output_config(struct output_config *oc); 710void free_output_config(struct output_config *oc);
698 711
699bool spawn_swaybg(void); 712bool spawn_swaybg(void);
diff --git a/include/sway/desktop/launcher.h b/include/sway/desktop/launcher.h
index b7716e82..412068a9 100644
--- a/include/sway/desktop/launcher.h
+++ b/include/sway/desktop/launcher.h
@@ -3,14 +3,18 @@
3 3
4#include <stdlib.h> 4#include <stdlib.h>
5#include <wayland-server-core.h> 5#include <wayland-server-core.h>
6#include "sway/input/seat.h"
6 7
7struct launcher_ctx { 8struct launcher_ctx {
8 pid_t pid; 9 pid_t pid;
9 char *fallback_name; 10 char *fallback_name;
10 struct wlr_xdg_activation_token_v1 *token; 11 struct wlr_xdg_activation_token_v1 *token;
11 struct wl_listener token_destroy; 12 struct wl_listener token_destroy;
13 struct sway_seat *seat;
14 struct wl_listener seat_destroy;
12 15
13 bool activated; 16 bool activated;
17 bool had_focused_surface;
14 18
15 struct sway_node *node; 19 struct sway_node *node;
16 struct wl_listener node_destroy; 20 struct wl_listener node_destroy;
diff --git a/include/sway/desktop/transaction.h b/include/sway/desktop/transaction.h
index 17d41fa3..dd7edb7a 100644
--- a/include/sway/desktop/transaction.h
+++ b/include/sway/desktop/transaction.h
@@ -2,6 +2,7 @@
2#define _SWAY_TRANSACTION_H 2#define _SWAY_TRANSACTION_H
3#include <stdint.h> 3#include <stdint.h>
4#include <stdbool.h> 4#include <stdbool.h>
5#include <wlr/types/wlr_scene.h>
5 6
6/** 7/**
7 * Transactions enable us to perform atomic layout updates. 8 * Transactions enable us to perform atomic layout updates.
@@ -58,4 +59,6 @@ bool transaction_notify_view_ready_by_serial(struct sway_view *view,
58bool transaction_notify_view_ready_by_geometry(struct sway_view *view, 59bool transaction_notify_view_ready_by_geometry(struct sway_view *view,
59 double x, double y, int width, int height); 60 double x, double y, int width, int height);
60 61
62void arrange_popups(struct wlr_scene_tree *popups);
63
61#endif 64#endif
diff --git a/include/sway/input/cursor.h b/include/sway/input/cursor.h
index 1e21c66f..527d0350 100644
--- a/include/sway/input/cursor.h
+++ b/include/sway/input/cursor.h
@@ -114,7 +114,7 @@ void pointer_motion(struct sway_cursor *cursor, uint32_t time_msec,
114 114
115void dispatch_cursor_button(struct sway_cursor *cursor, 115void dispatch_cursor_button(struct sway_cursor *cursor,
116 struct wlr_input_device *device, uint32_t time_msec, uint32_t button, 116 struct wlr_input_device *device, uint32_t time_msec, uint32_t button,
117 enum wlr_button_state state); 117 enum wl_pointer_button_state state);
118 118
119void dispatch_cursor_axis(struct sway_cursor *cursor, 119void dispatch_cursor_axis(struct sway_cursor *cursor,
120 struct wlr_pointer_axis_event *event); 120 struct wlr_pointer_axis_event *event);
diff --git a/include/sway/input/input-manager.h b/include/sway/input/input-manager.h
index 145edd4b..45c75199 100644
--- a/include/sway/input/input-manager.h
+++ b/include/sway/input/input-manager.h
@@ -4,6 +4,7 @@
4#include <wlr/types/wlr_keyboard_shortcuts_inhibit_v1.h> 4#include <wlr/types/wlr_keyboard_shortcuts_inhibit_v1.h>
5#include <wlr/types/wlr_virtual_keyboard_v1.h> 5#include <wlr/types/wlr_virtual_keyboard_v1.h>
6#include <wlr/types/wlr_virtual_pointer_v1.h> 6#include <wlr/types/wlr_virtual_pointer_v1.h>
7#include <wlr/types/wlr_transient_seat_v1.h>
7#include "sway/server.h" 8#include "sway/server.h"
8#include "sway/config.h" 9#include "sway/config.h"
9#include "list.h" 10#include "list.h"
@@ -24,6 +25,7 @@ struct sway_input_manager {
24 struct wlr_virtual_keyboard_manager_v1 *virtual_keyboard; 25 struct wlr_virtual_keyboard_manager_v1 *virtual_keyboard;
25 struct wlr_virtual_pointer_manager_v1 *virtual_pointer; 26 struct wlr_virtual_pointer_manager_v1 *virtual_pointer;
26 struct wlr_pointer_gestures_v1 *pointer_gestures; 27 struct wlr_pointer_gestures_v1 *pointer_gestures;
28 struct wlr_transient_seat_manager_v1 *transient_seat_manager;
27 29
28 struct wl_listener new_input; 30 struct wl_listener new_input;
29 struct wl_listener inhibit_activate; 31 struct wl_listener inhibit_activate;
@@ -31,6 +33,7 @@ struct sway_input_manager {
31 struct wl_listener keyboard_shortcuts_inhibit_new_inhibitor; 33 struct wl_listener keyboard_shortcuts_inhibit_new_inhibitor;
32 struct wl_listener virtual_keyboard_new; 34 struct wl_listener virtual_keyboard_new;
33 struct wl_listener virtual_pointer_new; 35 struct wl_listener virtual_pointer_new;
36 struct wl_listener transient_seat_create;
34}; 37};
35 38
36struct sway_input_manager *input_manager_create(struct sway_server *server); 39struct sway_input_manager *input_manager_create(struct sway_server *server);
diff --git a/include/sway/input/seat.h b/include/sway/input/seat.h
index e5aa8478..428f9679 100644
--- a/include/sway/input/seat.h
+++ b/include/sway/input/seat.h
@@ -17,7 +17,7 @@ struct sway_seat;
17struct sway_seatop_impl { 17struct sway_seatop_impl {
18 void (*button)(struct sway_seat *seat, uint32_t time_msec, 18 void (*button)(struct sway_seat *seat, uint32_t time_msec,
19 struct wlr_input_device *device, uint32_t button, 19 struct wlr_input_device *device, uint32_t button,
20 enum wlr_button_state state); 20 enum wl_pointer_button_state state);
21 void (*pointer_motion)(struct sway_seat *seat, uint32_t time_msec); 21 void (*pointer_motion)(struct sway_seat *seat, uint32_t time_msec);
22 void (*pointer_axis)(struct sway_seat *seat, 22 void (*pointer_axis)(struct sway_seat *seat,
23 struct wlr_pointer_axis_event *event); 23 struct wlr_pointer_axis_event *event);
@@ -124,6 +124,7 @@ struct sway_seat {
124 struct wl_listener start_drag; 124 struct wl_listener start_drag;
125 struct wl_listener request_set_selection; 125 struct wl_listener request_set_selection;
126 struct wl_listener request_set_primary_selection; 126 struct wl_listener request_set_primary_selection;
127 struct wl_listener destroy;
127 128
128 struct wl_list devices; // sway_seat_device::link 129 struct wl_list devices; // sway_seat_device::link
129 struct wl_list keyboard_groups; // sway_keyboard_group::link 130 struct wl_list keyboard_groups; // sway_keyboard_group::link
@@ -286,13 +287,13 @@ struct sway_container *seat_get_focus_inactive_floating(struct sway_seat *seat,
286 struct sway_workspace *workspace); 287 struct sway_workspace *workspace);
287 288
288void seat_pointer_notify_button(struct sway_seat *seat, uint32_t time_msec, 289void seat_pointer_notify_button(struct sway_seat *seat, uint32_t time_msec,
289 uint32_t button, enum wlr_button_state state); 290 uint32_t button, enum wl_pointer_button_state state);
290 291
291void seat_consider_warp_to_focus(struct sway_seat *seat); 292void seat_consider_warp_to_focus(struct sway_seat *seat);
292 293
293void seatop_button(struct sway_seat *seat, uint32_t time_msec, 294void seatop_button(struct sway_seat *seat, uint32_t time_msec,
294 struct wlr_input_device *device, uint32_t button, 295 struct wlr_input_device *device, uint32_t button,
295 enum wlr_button_state state); 296 enum wl_pointer_button_state state);
296 297
297void seatop_pointer_motion(struct sway_seat *seat, uint32_t time_msec); 298void seatop_pointer_motion(struct sway_seat *seat, uint32_t time_msec);
298 299
diff --git a/include/sway/input/text_input.h b/include/sway/input/text_input.h
index 214e61d1..1993f928 100644
--- a/include/sway/input/text_input.h
+++ b/include/sway/input/text_input.h
@@ -21,18 +21,21 @@ struct sway_input_method_relay {
21 struct sway_seat *seat; 21 struct sway_seat *seat;
22 22
23 struct wl_list text_inputs; // sway_text_input::link 23 struct wl_list text_inputs; // sway_text_input::link
24 struct wl_list input_popups; // sway_input_popup::link
24 struct wlr_input_method_v2 *input_method; // doesn't have to be present 25 struct wlr_input_method_v2 *input_method; // doesn't have to be present
25 26
26 struct wl_listener text_input_new; 27 struct wl_listener text_input_new;
27 28
28 struct wl_listener input_method_new; 29 struct wl_listener input_method_new;
29 struct wl_listener input_method_commit; 30 struct wl_listener input_method_commit;
31 struct wl_listener input_method_new_popup_surface;
30 struct wl_listener input_method_grab_keyboard; 32 struct wl_listener input_method_grab_keyboard;
31 struct wl_listener input_method_destroy; 33 struct wl_listener input_method_destroy;
32 34
33 struct wl_listener input_method_keyboard_grab_destroy; 35 struct wl_listener input_method_keyboard_grab_destroy;
34}; 36};
35 37
38
36struct sway_text_input { 39struct sway_text_input {
37 struct sway_input_method_relay *relay; 40 struct sway_input_method_relay *relay;
38 41
diff --git a/include/sway/input/text_input_popup.h b/include/sway/input/text_input_popup.h
new file mode 100644
index 00000000..e5f6ab8b
--- /dev/null
+++ b/include/sway/input/text_input_popup.h
@@ -0,0 +1,20 @@
1#ifndef _SWAY_INPUT_TEXT_INPUT_POPUP_H
2#define _SWAY_INPUT_TEXT_INPUT_POPUP_H
3
4#include "sway/tree/view.h"
5
6struct sway_input_popup {
7 struct sway_input_method_relay *relay;
8
9 struct wlr_scene_tree *scene_tree;
10 struct sway_popup_desc desc;
11 struct wlr_input_popup_surface_v2 *popup_surface;
12
13 struct wl_list link;
14
15 struct wl_listener popup_destroy;
16 struct wl_listener popup_surface_commit;
17
18 struct wl_listener focused_surface_unmap;
19};
20#endif
diff --git a/include/sway/layers.h b/include/sway/layers.h
index a7afb900..fd6384e0 100644
--- a/include/sway/layers.h
+++ b/include/sway/layers.h
@@ -3,6 +3,7 @@
3#include <stdbool.h> 3#include <stdbool.h>
4#include <wlr/types/wlr_compositor.h> 4#include <wlr/types/wlr_compositor.h>
5#include <wlr/types/wlr_layer_shell_v1.h> 5#include <wlr/types/wlr_layer_shell_v1.h>
6#include "sway/tree/view.h"
6 7
7struct sway_layer_surface { 8struct sway_layer_surface {
8 struct wl_listener map; 9 struct wl_listener map;
@@ -14,10 +15,12 @@ struct sway_layer_surface {
14 15
15 bool mapped; 16 bool mapped;
16 17
18 struct wlr_scene_tree *popups;
19 struct sway_popup_desc desc;
20
17 struct sway_output *output; 21 struct sway_output *output;
18 struct wlr_scene_layer_surface_v1 *scene; 22 struct wlr_scene_layer_surface_v1 *scene;
19 struct wlr_scene_tree *tree; 23 struct wlr_scene_tree *tree;
20 struct wlr_scene_tree *popups;
21 struct wlr_layer_surface_v1 *layer_surface; 24 struct wlr_layer_surface_v1 *layer_surface;
22}; 25};
23 26
diff --git a/include/sway/output.h b/include/sway/output.h
index 30595f54..d546d488 100644
--- a/include/sway/output.h
+++ b/include/sway/output.h
@@ -50,7 +50,7 @@ struct sway_output {
50 enum wl_output_subpixel detected_subpixel; 50 enum wl_output_subpixel detected_subpixel;
51 enum scale_filter_mode scale_filter; 51 enum scale_filter_mode scale_filter;
52 52
53 bool enabling, enabled; 53 bool enabled;
54 list_t *workspaces; 54 list_t *workspaces;
55 55
56 struct sway_output_state current; 56 struct sway_output_state current;
diff --git a/include/sway/server.h b/include/sway/server.h
index adb62cda..c71851f6 100644
--- a/include/sway/server.h
+++ b/include/sway/server.h
@@ -2,23 +2,6 @@
2#define _SWAY_SERVER_H 2#define _SWAY_SERVER_H
3#include <stdbool.h> 3#include <stdbool.h>
4#include <wayland-server-core.h> 4#include <wayland-server-core.h>
5#include <wlr/backend.h>
6#include <wlr/render/allocator.h>
7#include <wlr/render/wlr_renderer.h>
8#include <wlr/types/wlr_compositor.h>
9#include <wlr/types/wlr_data_device.h>
10#include <wlr/types/wlr_input_method_v2.h>
11#include <wlr/types/wlr_foreign_toplevel_management_v1.h>
12#include <wlr/types/wlr_drm_lease_v1.h>
13#include <wlr/types/wlr_layer_shell_v1.h>
14#include <wlr/types/wlr_output_management_v1.h>
15#include <wlr/types/wlr_output_power_management_v1.h>
16#include <wlr/types/wlr_presentation_time.h>
17#include <wlr/types/wlr_relative_pointer_v1.h>
18#include <wlr/types/wlr_session_lock_v1.h>
19#include <wlr/types/wlr_server_decoration.h>
20#include <wlr/types/wlr_text_input_v3.h>
21#include <wlr/types/wlr_xdg_shell.h>
22#include "config.h" 5#include "config.h"
23#include "list.h" 6#include "list.h"
24#include "sway/desktop/idle_inhibit_v1.h" 7#include "sway/desktop/idle_inhibit_v1.h"
@@ -63,6 +46,7 @@ struct sway_server {
63 46
64 struct wl_listener new_output; 47 struct wl_listener new_output;
65 struct wl_listener output_layout_change; 48 struct wl_listener output_layout_change;
49 struct wl_listener renderer_lost;
66 50
67 struct wlr_idle_notifier_v1 *idle_notifier_v1; 51 struct wlr_idle_notifier_v1 *idle_notifier_v1;
68 struct sway_idle_inhibit_manager_v1 idle_inhibit_manager_v1; 52 struct sway_idle_inhibit_manager_v1 idle_inhibit_manager_v1;
@@ -116,6 +100,7 @@ struct sway_server {
116 struct wl_listener output_power_manager_set_mode; 100 struct wl_listener output_power_manager_set_mode;
117 struct wlr_input_method_manager_v2 *input_method; 101 struct wlr_input_method_manager_v2 *input_method;
118 struct wlr_text_input_manager_v3 *text_input; 102 struct wlr_text_input_manager_v3 *text_input;
103 struct wlr_ext_foreign_toplevel_list_v1 *foreign_toplevel_list;
119 struct wlr_foreign_toplevel_manager_v1 *foreign_toplevel_manager; 104 struct wlr_foreign_toplevel_manager_v1 *foreign_toplevel_manager;
120 struct wlr_content_type_manager_v1 *content_type_manager_v1; 105 struct wlr_content_type_manager_v1 *content_type_manager_v1;
121 struct wlr_data_control_manager_v1 *data_control_manager_v1; 106 struct wlr_data_control_manager_v1 *data_control_manager_v1;
diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h
index 3e5a9bfe..7faacdcc 100644
--- a/include/sway/tree/view.h
+++ b/include/sway/tree/view.h
@@ -84,6 +84,8 @@ struct sway_view {
84 // transaction state. Updated on every commit. 84 // transaction state. Updated on every commit.
85 struct wlr_box geometry; 85 struct wlr_box geometry;
86 86
87 struct wlr_ext_foreign_toplevel_handle_v1 *ext_foreign_toplevel;
88
87 struct wlr_foreign_toplevel_handle_v1 *foreign_toplevel; 89 struct wlr_foreign_toplevel_handle_v1 *foreign_toplevel;
88 struct wl_listener foreign_activate_request; 90 struct wl_listener foreign_activate_request;
89 struct wl_listener foreign_fullscreen_request; 91 struct wl_listener foreign_fullscreen_request;
@@ -174,6 +176,11 @@ struct sway_xwayland_unmanaged {
174}; 176};
175#endif 177#endif
176 178
179struct sway_popup_desc {
180 struct wlr_scene_node *relative;
181 struct sway_view *view;
182};
183
177struct sway_xdg_popup { 184struct sway_xdg_popup {
178 struct sway_view *view; 185 struct sway_view *view;
179 186
@@ -181,8 +188,11 @@ struct sway_xdg_popup {
181 struct wlr_scene_tree *xdg_surface_tree; 188 struct wlr_scene_tree *xdg_surface_tree;
182 struct wlr_xdg_popup *wlr_xdg_popup; 189 struct wlr_xdg_popup *wlr_xdg_popup;
183 190
191 struct sway_popup_desc desc;
192
184 struct wl_listener surface_commit; 193 struct wl_listener surface_commit;
185 struct wl_listener new_popup; 194 struct wl_listener new_popup;
195 struct wl_listener reposition;
186 struct wl_listener destroy; 196 struct wl_listener destroy;
187}; 197};
188 198
@@ -233,6 +243,11 @@ void view_set_activated(struct sway_view *view, bool activated);
233 */ 243 */
234void view_request_activate(struct sway_view *view, struct sway_seat *seat); 244void view_request_activate(struct sway_view *view, struct sway_seat *seat);
235 245
246/*
247 * Called when the view requests urgent state
248 */
249void view_request_urgent(struct sway_view *view);
250
236/** 251/**
237 * If possible, instructs the client to change their decoration mode. 252 * If possible, instructs the client to change their decoration mode.
238 */ 253 */
@@ -284,6 +299,8 @@ struct sway_view *view_from_wlr_xwayland_surface(
284#endif 299#endif
285struct sway_view *view_from_wlr_surface(struct wlr_surface *surface); 300struct sway_view *view_from_wlr_surface(struct wlr_surface *surface);
286 301
302void view_update_app_id(struct sway_view *view);
303
287/** 304/**
288 * Re-read the view's title property and update any relevant title bars. 305 * Re-read the view's title property and update any relevant title bars.
289 * The force argument makes it recreate the title bars even if the title hasn't 306 * The force argument makes it recreate the title bars even if the title hasn't
diff --git a/meson.build b/meson.build
index f8bf4f80..1043e4ba 100644
--- a/meson.build
+++ b/meson.build
@@ -14,6 +14,7 @@ project(
14add_project_arguments( 14add_project_arguments(
15 [ 15 [
16 '-DWLR_USE_UNSTABLE', 16 '-DWLR_USE_UNSTABLE',
17 '-D_POSIX_C_SOURCE=200809L',
17 18
18 '-Wno-unused-parameter', 19 '-Wno-unused-parameter',
19 '-Wno-unused-result', 20 '-Wno-unused-result',
diff --git a/release.sh b/release.sh
new file mode 100755
index 00000000..62baf415
--- /dev/null
+++ b/release.sh
@@ -0,0 +1,31 @@
1#!/bin/sh -eu
2
3prev=$(git describe --tags --abbrev=0)
4next=$(meson rewrite kwargs info project / 2>&1 >/dev/null | jq -r '.kwargs["project#/"].version')
5
6case "$next" in
7*-dev)
8 echo "This is a development version"
9 exit 1
10 ;;
11esac
12
13if [ "$prev" = "$next" ]; then
14 echo "Version not bumped in meson.build"
15 exit 1
16fi
17
18if ! git diff-index --quiet HEAD -- meson.build; then
19 echo "meson.build not committed"
20 exit 1
21fi
22
23shortlog="$(git shortlog --no-merges "$prev..")"
24(echo "sway $next"; echo ""; echo "$shortlog") | git tag "$next" -ase -F -
25
26prefix=sway-$next
27archive=$prefix.tar.gz
28git archive --prefix="$prefix/" -o "$archive" "$next"
29gpg --output "$archive".sig --detach-sig "$archive"
30
31gh release create "sway $next" -t "$next" -n "" -d "$archive" "$archive.sig"
diff --git a/sway/commands.c b/sway/commands.c
index 55eda183..8d003dfa 100644
--- a/sway/commands.c
+++ b/sway/commands.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809
2#include <ctype.h> 1#include <ctype.h>
3#include <stdarg.h> 2#include <stdarg.h>
4#include <stdlib.h> 3#include <stdlib.h>
@@ -82,7 +81,6 @@ static const struct cmd_handler handlers[] = {
82 { "no_focus", cmd_no_focus }, 81 { "no_focus", cmd_no_focus },
83 { "output", cmd_output }, 82 { "output", cmd_output },
84 { "popup_during_fullscreen", cmd_popup_during_fullscreen }, 83 { "popup_during_fullscreen", cmd_popup_during_fullscreen },
85 { "primary_selection", cmd_primary_selection },
86 { "seat", cmd_seat }, 84 { "seat", cmd_seat },
87 { "set", cmd_set }, 85 { "set", cmd_set },
88 { "show_marks", cmd_show_marks }, 86 { "show_marks", cmd_show_marks },
@@ -105,6 +103,7 @@ static const struct cmd_handler handlers[] = {
105static const struct cmd_handler config_handlers[] = { 103static const struct cmd_handler config_handlers[] = {
106 { "default_orientation", cmd_default_orientation }, 104 { "default_orientation", cmd_default_orientation },
107 { "include", cmd_include }, 105 { "include", cmd_include },
106 { "primary_selection", cmd_primary_selection },
108 { "swaybg_command", cmd_swaybg_command }, 107 { "swaybg_command", cmd_swaybg_command },
109 { "swaynag_command", cmd_swaynag_command }, 108 { "swaynag_command", cmd_swaynag_command },
110 { "workspace_layout", cmd_workspace_layout }, 109 { "workspace_layout", cmd_workspace_layout },
diff --git a/sway/commands/assign.c b/sway/commands/assign.c
index f7d911f7..bf95cf00 100644
--- a/sway/commands/assign.c
+++ b/sway/commands/assign.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <stdio.h> 1#include <stdio.h>
3#include <string.h> 2#include <string.h>
4#include "sway/commands.h" 3#include "sway/commands.h"
diff --git a/sway/commands/bar.c b/sway/commands/bar.c
index 22756acb..635e895b 100644
--- a/sway/commands/bar.c
+++ b/sway/commands/bar.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809
2#include <stdio.h> 1#include <stdio.h>
3#include <string.h> 2#include <string.h>
4#include <strings.h> 3#include <strings.h>
diff --git a/sway/commands/bar/font.c b/sway/commands/bar/font.c
index 891c87af..0c074679 100644
--- a/sway/commands/bar/font.c
+++ b/sway/commands/bar/font.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <string.h> 1#include <string.h>
3#include "sway/commands.h" 2#include "sway/commands.h"
4#include "log.h" 3#include "log.h"
diff --git a/sway/commands/bar/hidden_state.c b/sway/commands/bar/hidden_state.c
index 8b661e3a..7b38831e 100644
--- a/sway/commands/bar/hidden_state.c
+++ b/sway/commands/bar/hidden_state.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <string.h> 1#include <string.h>
3#include <strings.h> 2#include <strings.h>
4#include "sway/commands.h" 3#include "sway/commands.h"
diff --git a/sway/commands/bar/icon_theme.c b/sway/commands/bar/icon_theme.c
index 6ac07843..fee21709 100644
--- a/sway/commands/bar/icon_theme.c
+++ b/sway/commands/bar/icon_theme.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <string.h> 1#include <string.h>
3#include "config.h" 2#include "config.h"
4#include "sway/commands.h" 3#include "sway/commands.h"
diff --git a/sway/commands/bar/id.c b/sway/commands/bar/id.c
index a9a61743..46cf4ca9 100644
--- a/sway/commands/bar/id.c
+++ b/sway/commands/bar/id.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <string.h> 1#include <string.h>
3#include "sway/commands.h" 2#include "sway/commands.h"
4#include "log.h" 3#include "log.h"
diff --git a/sway/commands/bar/mode.c b/sway/commands/bar/mode.c
index 7c2f423b..d69e910b 100644
--- a/sway/commands/bar/mode.c
+++ b/sway/commands/bar/mode.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <string.h> 1#include <string.h>
3#include <strings.h> 2#include <strings.h>
4#include "sway/commands.h" 3#include "sway/commands.h"
diff --git a/sway/commands/bar/output.c b/sway/commands/bar/output.c
index cac1d056..51730176 100644
--- a/sway/commands/bar/output.c
+++ b/sway/commands/bar/output.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <stdbool.h> 1#include <stdbool.h>
3#include <string.h> 2#include <string.h>
4#include "sway/commands.h" 3#include "sway/commands.h"
diff --git a/sway/commands/bar/position.c b/sway/commands/bar/position.c
index b207de0b..94f530ec 100644
--- a/sway/commands/bar/position.c
+++ b/sway/commands/bar/position.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <string.h> 1#include <string.h>
3#include <strings.h> 2#include <strings.h>
4#include "sway/commands.h" 3#include "sway/commands.h"
diff --git a/sway/commands/bar/separator_symbol.c b/sway/commands/bar/separator_symbol.c
index 6737d4d2..50e9a873 100644
--- a/sway/commands/bar/separator_symbol.c
+++ b/sway/commands/bar/separator_symbol.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <string.h> 1#include <string.h>
3#include "sway/commands.h" 2#include "sway/commands.h"
4#include "log.h" 3#include "log.h"
diff --git a/sway/commands/bar/tray_output.c b/sway/commands/bar/tray_output.c
index eb3b486e..679facf7 100644
--- a/sway/commands/bar/tray_output.c
+++ b/sway/commands/bar/tray_output.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <string.h> 1#include <string.h>
3#include "config.h" 2#include "config.h"
4#include "sway/commands.h" 3#include "sway/commands.h"
diff --git a/sway/commands/bind.c b/sway/commands/bind.c
index 979e178f..268f2855 100644
--- a/sway/commands/bind.c
+++ b/sway/commands/bind.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <libevdev/libevdev.h> 1#include <libevdev/libevdev.h>
3#include <linux/input-event-codes.h> 2#include <linux/input-event-codes.h>
4#include <string.h> 3#include <string.h>
diff --git a/sway/commands/exec_always.c b/sway/commands/exec_always.c
index 8fca1909..8bc1048c 100644
--- a/sway/commands/exec_always.c
+++ b/sway/commands/exec_always.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <stdlib.h> 1#include <stdlib.h>
3#include <stdint.h> 2#include <stdint.h>
4#include <string.h> 3#include <string.h>
diff --git a/sway/commands/font.c b/sway/commands/font.c
index 74bb6b9f..9920d03e 100644
--- a/sway/commands/font.c
+++ b/sway/commands/font.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <string.h> 1#include <string.h>
3#include "sway/commands.h" 2#include "sway/commands.h"
4#include "sway/config.h" 3#include "sway/config.h"
diff --git a/sway/commands/gesture.c b/sway/commands/gesture.c
index d4442cc3..90a20716 100644
--- a/sway/commands/gesture.c
+++ b/sway/commands/gesture.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include "sway/config.h" 1#include "sway/config.h"
3 2
4#include "gesture.h" 3#include "gesture.h"
diff --git a/sway/commands/input/calibration_matrix.c b/sway/commands/input/calibration_matrix.c
index 38749fbb..53fe2c35 100644
--- a/sway/commands/input/calibration_matrix.c
+++ b/sway/commands/input/calibration_matrix.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <string.h> 1#include <string.h>
3#include <strings.h> 2#include <strings.h>
4#include "sway/config.h" 3#include "sway/config.h"
diff --git a/sway/commands/input/map_from_region.c b/sway/commands/input/map_from_region.c
index 4400e111..2f8f753d 100644
--- a/sway/commands/input/map_from_region.c
+++ b/sway/commands/input/map_from_region.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <stdbool.h> 1#include <stdbool.h>
3#include <string.h> 2#include <string.h>
4#include <strings.h> 3#include <strings.h>
diff --git a/sway/commands/input/map_to_output.c b/sway/commands/input/map_to_output.c
index f60fb7d5..a7266baa 100644
--- a/sway/commands/input/map_to_output.c
+++ b/sway/commands/input/map_to_output.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <string.h> 1#include <string.h>
3#include <strings.h> 2#include <strings.h>
4#include "sway/config.h" 3#include "sway/config.h"
diff --git a/sway/commands/input/map_to_region.c b/sway/commands/input/map_to_region.c
index ad535db2..9087c589 100644
--- a/sway/commands/input/map_to_region.c
+++ b/sway/commands/input/map_to_region.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <stdlib.h> 1#include <stdlib.h>
3#include <string.h> 2#include <string.h>
4#include "sway/commands.h" 3#include "sway/commands.h"
diff --git a/sway/commands/input/xkb_file.c b/sway/commands/input/xkb_file.c
index 493f94fb..056f00e5 100644
--- a/sway/commands/input/xkb_file.c
+++ b/sway/commands/input/xkb_file.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <unistd.h> 1#include <unistd.h>
3#include <errno.h> 2#include <errno.h>
4#include "sway/config.h" 3#include "sway/config.h"
diff --git a/sway/commands/input/xkb_layout.c b/sway/commands/input/xkb_layout.c
index 22626517..1d01886c 100644
--- a/sway/commands/input/xkb_layout.c
+++ b/sway/commands/input/xkb_layout.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include "sway/config.h" 1#include "sway/config.h"
3#include "sway/commands.h" 2#include "sway/commands.h"
4#include "log.h" 3#include "log.h"
diff --git a/sway/commands/input/xkb_model.c b/sway/commands/input/xkb_model.c
index f4a33de3..a9144a8a 100644
--- a/sway/commands/input/xkb_model.c
+++ b/sway/commands/input/xkb_model.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include "sway/config.h" 1#include "sway/config.h"
3#include "sway/commands.h" 2#include "sway/commands.h"
4#include "log.h" 3#include "log.h"
diff --git a/sway/commands/input/xkb_numlock.c b/sway/commands/input/xkb_numlock.c
index 87d3e60c..bbe848fe 100644
--- a/sway/commands/input/xkb_numlock.c
+++ b/sway/commands/input/xkb_numlock.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include "sway/config.h" 1#include "sway/config.h"
3#include "sway/commands.h" 2#include "sway/commands.h"
4#include "util.h" 3#include "util.h"
diff --git a/sway/commands/input/xkb_options.c b/sway/commands/input/xkb_options.c
index d609293f..7ca20777 100644
--- a/sway/commands/input/xkb_options.c
+++ b/sway/commands/input/xkb_options.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include "sway/config.h" 1#include "sway/config.h"
3#include "sway/commands.h" 2#include "sway/commands.h"
4#include "log.h" 3#include "log.h"
diff --git a/sway/commands/input/xkb_rules.c b/sway/commands/input/xkb_rules.c
index 3b59622c..8fbd26fb 100644
--- a/sway/commands/input/xkb_rules.c
+++ b/sway/commands/input/xkb_rules.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include "sway/config.h" 1#include "sway/config.h"
3#include "sway/commands.h" 2#include "sway/commands.h"
4#include "log.h" 3#include "log.h"
diff --git a/sway/commands/input/xkb_switch_layout.c b/sway/commands/input/xkb_switch_layout.c
index 3cce4ec8..ecac8e6c 100644
--- a/sway/commands/input/xkb_switch_layout.c
+++ b/sway/commands/input/xkb_switch_layout.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <assert.h> 1#include <assert.h>
3#include <wlr/interfaces/wlr_keyboard.h> 2#include <wlr/interfaces/wlr_keyboard.h>
4#include "sway/config.h" 3#include "sway/config.h"
diff --git a/sway/commands/input/xkb_variant.c b/sway/commands/input/xkb_variant.c
index d0e21d77..2d14ea9c 100644
--- a/sway/commands/input/xkb_variant.c
+++ b/sway/commands/input/xkb_variant.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include "sway/config.h" 1#include "sway/config.h"
3#include "sway/commands.h" 2#include "sway/commands.h"
4#include "log.h" 3#include "log.h"
diff --git a/sway/commands/mark.c b/sway/commands/mark.c
index 30cf458c..2bfc86b3 100644
--- a/sway/commands/mark.c
+++ b/sway/commands/mark.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <string.h> 1#include <string.h>
3#include "sway/commands.h" 2#include "sway/commands.h"
4#include "sway/config.h" 3#include "sway/config.h"
diff --git a/sway/commands/mode.c b/sway/commands/mode.c
index 7263efcb..b3216967 100644
--- a/sway/commands/mode.c
+++ b/sway/commands/mode.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <stdbool.h> 1#include <stdbool.h>
3#include <string.h> 2#include <string.h>
4#include "sway/commands.h" 3#include "sway/commands.h"
diff --git a/sway/commands/move.c b/sway/commands/move.c
index 69ed06c0..8addf26e 100644
--- a/sway/commands/move.c
+++ b/sway/commands/move.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <ctype.h> 1#include <ctype.h>
3#include <math.h> 2#include <math.h>
4#include <stdbool.h> 3#include <stdbool.h>
@@ -770,15 +769,6 @@ static struct cmd_results *cmd_move_in_direction(
770 ipc_event_window(container, "move"); 769 ipc_event_window(container, "move");
771 } 770 }
772 771
773 // Hack to re-focus container
774 seat_set_raw_focus(config->handler_context.seat, &new_ws->node);
775 seat_set_focus_container(config->handler_context.seat, container);
776
777 if (old_ws != new_ws) {
778 ipc_event_workspace(old_ws, new_ws, "focus");
779 workspace_detect_urgent(old_ws);
780 workspace_detect_urgent(new_ws);
781 }
782 container_end_mouse_operation(container); 772 container_end_mouse_operation(container);
783 773
784 return cmd_results_new(CMD_SUCCESS, NULL); 774 return cmd_results_new(CMD_SUCCESS, NULL);
diff --git a/sway/commands/output.c b/sway/commands/output.c
index df32c673..5e5d31b3 100644
--- a/sway/commands/output.c
+++ b/sway/commands/output.c
@@ -103,15 +103,18 @@ struct cmd_results *cmd_output(int argc, char **argv) {
103 103
104 bool background = output->background; 104 bool background = output->background;
105 105
106 output = store_output_config(output); 106 store_output_config(output);
107 107
108 // If reloading, the output configs will be applied after reading the 108 // If reloading, the output configs will be applied after reading the
109 // entire config and before the deferred commands so that an auto generated 109 // entire config and before the deferred commands so that an auto generated
110 // workspace name is not given to re-enabled outputs. 110 // workspace name is not given to re-enabled outputs.
111 if (!config->reloading && !config->validating) { 111 if (!config->reloading && !config->validating) {
112 apply_output_config_to_outputs(output); 112 apply_all_output_configs();
113 if (background) { 113 if (background) {
114 spawn_swaybg(); 114 if (!spawn_swaybg()) {
115 return cmd_results_new(CMD_FAILURE,
116 "Failed to apply background configuration");
117 }
115 } 118 }
116 } 119 }
117 120
diff --git a/sway/commands/output/background.c b/sway/commands/output/background.c
index d691295f..55bd7671 100644
--- a/sway/commands/output/background.c
+++ b/sway/commands/output/background.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <libgen.h> 1#include <libgen.h>
3#include <stdio.h> 2#include <stdio.h>
4#include <string.h> 3#include <string.h>
diff --git a/sway/commands/output/toggle.c b/sway/commands/output/toggle.c
index 6342d526..c6b72845 100644
--- a/sway/commands/output/toggle.c
+++ b/sway/commands/output/toggle.c
@@ -29,7 +29,7 @@ struct cmd_results *output_cmd_toggle(int argc, char **argv) {
29 config->handler_context.output_config->enabled = 1; 29 config->handler_context.output_config->enabled = 1;
30 } 30 }
31 31
32 free(oc); 32 free_output_config(oc);
33 config->handler_context.leftovers.argc = argc; 33 config->handler_context.leftovers.argc = argc;
34 config->handler_context.leftovers.argv = argv; 34 config->handler_context.leftovers.argv = argv;
35 return NULL; 35 return NULL;
diff --git a/sway/commands/primary_selection.c b/sway/commands/primary_selection.c
index 585b079d..9e2689c2 100644
--- a/sway/commands/primary_selection.c
+++ b/sway/commands/primary_selection.c
@@ -12,12 +12,14 @@ struct cmd_results *cmd_primary_selection(int argc, char **argv) {
12 12
13 bool primary_selection = parse_boolean(argv[0], true); 13 bool primary_selection = parse_boolean(argv[0], true);
14 14
15 // config->primary_selection is reset to the previous value on reload in
16 // load_main_config()
15 if (config->reloading && config->primary_selection != primary_selection) { 17 if (config->reloading && config->primary_selection != primary_selection) {
16 return cmd_results_new(CMD_FAILURE, 18 return cmd_results_new(CMD_FAILURE,
17 "primary_selection can only be enabled/disabled at launch"); 19 "primary_selection can only be enabled/disabled at launch");
18 } 20 }
19 21
20 config->primary_selection = parse_boolean(argv[0], true); 22 config->primary_selection = primary_selection;
21 23
22 return cmd_results_new(CMD_SUCCESS, NULL); 24 return cmd_results_new(CMD_SUCCESS, NULL);
23} 25}
diff --git a/sway/commands/reload.c b/sway/commands/reload.c
index 82967ca7..6c0aac26 100644
--- a/sway/commands/reload.c
+++ b/sway/commands/reload.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <string.h> 1#include <string.h>
3#include "sway/commands.h" 2#include "sway/commands.h"
4#include "sway/config.h" 3#include "sway/config.h"
diff --git a/sway/commands/seat/attach.c b/sway/commands/seat/attach.c
index 00bfdab6..47d18546 100644
--- a/sway/commands/seat/attach.c
+++ b/sway/commands/seat/attach.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <string.h> 1#include <string.h>
3#include "sway/commands.h" 2#include "sway/commands.h"
4#include "sway/config.h" 3#include "sway/config.h"
diff --git a/sway/commands/seat/cursor.c b/sway/commands/seat/cursor.c
index 5a8a3bc8..df7c379d 100644
--- a/sway/commands/seat/cursor.c
+++ b/sway/commands/seat/cursor.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <linux/input-event-codes.h> 1#include <linux/input-event-codes.h>
3 2
4#include <strings.h> 3#include <strings.h>
@@ -85,12 +84,12 @@ struct cmd_results *seat_cmd_cursor(int argc, char **argv) {
85 84
86static struct cmd_results *press_or_release(struct sway_cursor *cursor, 85static struct cmd_results *press_or_release(struct sway_cursor *cursor,
87 char *action, char *button_str) { 86 char *action, char *button_str) {
88 enum wlr_button_state state; 87 enum wl_pointer_button_state state;
89 uint32_t button; 88 uint32_t button;
90 if (strcasecmp(action, "press") == 0) { 89 if (strcasecmp(action, "press") == 0) {
91 state = WLR_BUTTON_PRESSED; 90 state = WL_POINTER_BUTTON_STATE_PRESSED;
92 } else if (strcasecmp(action, "release") == 0) { 91 } else if (strcasecmp(action, "release") == 0) {
93 state = WLR_BUTTON_RELEASED; 92 state = WL_POINTER_BUTTON_STATE_RELEASED;
94 } else { 93 } else {
95 return cmd_results_new(CMD_INVALID, "%s", expected_syntax); 94 return cmd_results_new(CMD_INVALID, "%s", expected_syntax);
96 } 95 }
@@ -105,16 +104,16 @@ static struct cmd_results *press_or_release(struct sway_cursor *cursor,
105 } else if (button == SWAY_SCROLL_UP || button == SWAY_SCROLL_DOWN 104 } else if (button == SWAY_SCROLL_UP || button == SWAY_SCROLL_DOWN
106 || button == SWAY_SCROLL_LEFT || button == SWAY_SCROLL_RIGHT) { 105 || button == SWAY_SCROLL_LEFT || button == SWAY_SCROLL_RIGHT) {
107 // Dispatch axis event 106 // Dispatch axis event
108 enum wlr_axis_orientation orientation = 107 enum wl_pointer_axis orientation =
109 (button == SWAY_SCROLL_UP || button == SWAY_SCROLL_DOWN) 108 (button == SWAY_SCROLL_UP || button == SWAY_SCROLL_DOWN)
110 ? WLR_AXIS_ORIENTATION_VERTICAL 109 ? WL_POINTER_AXIS_VERTICAL_SCROLL
111 : WLR_AXIS_ORIENTATION_HORIZONTAL; 110 : WL_POINTER_AXIS_HORIZONTAL_SCROLL;
112 double delta = (button == SWAY_SCROLL_UP || button == SWAY_SCROLL_LEFT) 111 double delta = (button == SWAY_SCROLL_UP || button == SWAY_SCROLL_LEFT)
113 ? -1 : 1; 112 ? -1 : 1;
114 struct wlr_pointer_axis_event event = { 113 struct wlr_pointer_axis_event event = {
115 .pointer = NULL, 114 .pointer = NULL,
116 .time_msec = 0, 115 .time_msec = 0,
117 .source = WLR_AXIS_SOURCE_WHEEL, 116 .source = WL_POINTER_AXIS_SOURCE_WHEEL,
118 .orientation = orientation, 117 .orientation = orientation,
119 .delta = delta * 15, 118 .delta = delta * 15,
120 .delta_discrete = delta 119 .delta_discrete = delta
diff --git a/sway/commands/seat/hide_cursor.c b/sway/commands/seat/hide_cursor.c
index e09b82d9..f5177a47 100644
--- a/sway/commands/seat/hide_cursor.c
+++ b/sway/commands/seat/hide_cursor.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <string.h> 1#include <string.h>
3#include "sway/commands.h" 2#include "sway/commands.h"
4#include "sway/config.h" 3#include "sway/config.h"
diff --git a/sway/commands/seat/idle.c b/sway/commands/seat/idle.c
index 62b94db2..2974453e 100644
--- a/sway/commands/seat/idle.c
+++ b/sway/commands/seat/idle.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <limits.h> 1#include <limits.h>
3#include <string.h> 2#include <string.h>
4#include <strings.h> 3#include <strings.h>
diff --git a/sway/commands/seat/xcursor_theme.c b/sway/commands/seat/xcursor_theme.c
index 202f35b9..61322a57 100644
--- a/sway/commands/seat/xcursor_theme.c
+++ b/sway/commands/seat/xcursor_theme.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <string.h> 1#include <string.h>
3#include "sway/commands.h" 2#include "sway/commands.h"
4#include "sway/config.h" 3#include "sway/config.h"
diff --git a/sway/commands/set.c b/sway/commands/set.c
index c539e9fc..ba384c7c 100644
--- a/sway/commands/set.c
+++ b/sway/commands/set.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <stdio.h> 1#include <stdio.h>
3#include <string.h> 2#include <string.h>
4#include <strings.h> 3#include <strings.h>
diff --git a/sway/commands/show_marks.c b/sway/commands/show_marks.c
index fecb5ade..60cef9fa 100644
--- a/sway/commands/show_marks.c
+++ b/sway/commands/show_marks.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <string.h> 1#include <string.h>
3#include "sway/commands.h" 2#include "sway/commands.h"
4#include "sway/config.h" 3#include "sway/config.h"
diff --git a/sway/commands/swap.c b/sway/commands/swap.c
index d44eb006..e142eede 100644
--- a/sway/commands/swap.c
+++ b/sway/commands/swap.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <strings.h> 1#include <strings.h>
3#include "config.h" 2#include "config.h"
4#include "log.h" 3#include "log.h"
diff --git a/sway/commands/title_format.c b/sway/commands/title_format.c
index a2446b7e..0b2ea265 100644
--- a/sway/commands/title_format.c
+++ b/sway/commands/title_format.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <string.h> 1#include <string.h>
3#include "sway/commands.h" 2#include "sway/commands.h"
4#include "sway/config.h" 3#include "sway/config.h"
diff --git a/sway/commands/unmark.c b/sway/commands/unmark.c
index c3a6ac4b..4aba5bae 100644
--- a/sway/commands/unmark.c
+++ b/sway/commands/unmark.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <string.h> 1#include <string.h>
3#include "sway/commands.h" 2#include "sway/commands.h"
4#include "sway/config.h" 3#include "sway/config.h"
diff --git a/sway/commands/workspace.c b/sway/commands/workspace.c
index 8536929e..37a201b4 100644
--- a/sway/commands/workspace.c
+++ b/sway/commands/workspace.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <ctype.h> 1#include <ctype.h>
3#include <limits.h> 2#include <limits.h>
4#include <string.h> 3#include <string.h>
diff --git a/sway/commands/xwayland.c b/sway/commands/xwayland.c
index 6ca26923..584a8e3a 100644
--- a/sway/commands/xwayland.c
+++ b/sway/commands/xwayland.c
@@ -20,6 +20,8 @@ struct cmd_results *cmd_xwayland(int argc, char **argv) {
20 xwayland = XWAYLAND_MODE_DISABLED; 20 xwayland = XWAYLAND_MODE_DISABLED;
21 } 21 }
22 22
23 // config->xwayland is reset to the previous value on reload in
24 // load_main_config()
23 if (config->reloading && config->xwayland != xwayland) { 25 if (config->reloading && config->xwayland != xwayland) {
24 return cmd_results_new(CMD_FAILURE, 26 return cmd_results_new(CMD_FAILURE,
25 "xwayland can only be enabled/disabled at launch"); 27 "xwayland can only be enabled/disabled at launch");
diff --git a/sway/config.c b/sway/config.c
index 4b51dc73..f9131e0f 100644
--- a/sway/config.c
+++ b/sway/config.c
@@ -1,3 +1,4 @@
1#undef _POSIX_C_SOURCE
1#define _XOPEN_SOURCE 700 // for realpath 2#define _XOPEN_SOURCE 700 // for realpath
2#include <stdio.h> 3#include <stdio.h>
3#include <stdbool.h> 4#include <stdbool.h>
@@ -36,19 +37,26 @@
36struct sway_config *config = NULL; 37struct sway_config *config = NULL;
37 38
38static struct xkb_state *keysym_translation_state_create( 39static struct xkb_state *keysym_translation_state_create(
39 struct xkb_rule_names rules) { 40 struct xkb_rule_names rules, uint32_t context_flags) {
40 struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_SECURE_GETENV); 41 struct xkb_context *context = xkb_context_new(context_flags | XKB_CONTEXT_NO_SECURE_GETENV);
41 struct xkb_keymap *xkb_keymap = xkb_keymap_new_from_names( 42 struct xkb_keymap *xkb_keymap = xkb_keymap_new_from_names(
42 context, 43 context,
43 &rules, 44 &rules,
44 XKB_KEYMAP_COMPILE_NO_FLAGS); 45 XKB_KEYMAP_COMPILE_NO_FLAGS);
45
46 xkb_context_unref(context); 46 xkb_context_unref(context);
47 if (xkb_keymap == NULL) {
48 sway_log(SWAY_ERROR, "Failed to compile keysym translation XKB keymap");
49 return NULL;
50 }
51
47 return xkb_state_new(xkb_keymap); 52 return xkb_state_new(xkb_keymap);
48} 53}
49 54
50static void keysym_translation_state_destroy( 55static void keysym_translation_state_destroy(
51 struct xkb_state *state) { 56 struct xkb_state *state) {
57 if (state == NULL) {
58 return;
59 }
52 xkb_keymap_unref(xkb_state_get_keymap(state)); 60 xkb_keymap_unref(xkb_state_get_keymap(state));
53 xkb_state_unref(state); 61 xkb_state_unref(state);
54} 62}
@@ -336,8 +344,14 @@ static void config_defaults(struct sway_config *config) {
336 344
337 // The keysym to keycode translation 345 // The keysym to keycode translation
338 struct xkb_rule_names rules = {0}; 346 struct xkb_rule_names rules = {0};
339 config->keysym_translation_state = 347 config->keysym_translation_state = keysym_translation_state_create(rules, 0);
340 keysym_translation_state_create(rules); 348 if (config->keysym_translation_state == NULL) {
349 config->keysym_translation_state = keysym_translation_state_create(rules,
350 XKB_CONTEXT_NO_ENVIRONMENT_NAMES);
351 }
352 if (config->keysym_translation_state == NULL) {
353 goto cleanup;
354 }
341 355
342 return; 356 return;
343cleanup: 357cleanup:
@@ -352,13 +366,7 @@ static char *config_path(const char *prefix, const char *config_folder) {
352 if (!prefix || !prefix[0] || !config_folder || !config_folder[0]) { 366 if (!prefix || !prefix[0] || !config_folder || !config_folder[0]) {
353 return NULL; 367 return NULL;
354 } 368 }
355 369 return format_str("%s/%s/config", prefix, config_folder);
356 const char *filename = "config";
357
358 size_t size = 3 + strlen(prefix) + strlen(config_folder) + strlen(filename);
359 char *path = calloc(size, sizeof(char));
360 snprintf(path, size, "%s/%s/%s", prefix, config_folder, filename);
361 return path;
362} 370}
363 371
364static char *get_config_path(void) { 372static char *get_config_path(void) {
@@ -368,10 +376,7 @@ static char *get_config_path(void) {
368 376
369 const char *config_home = getenv("XDG_CONFIG_HOME"); 377 const char *config_home = getenv("XDG_CONFIG_HOME");
370 if ((config_home == NULL || config_home[0] == '\0') && home != NULL) { 378 if ((config_home == NULL || config_home[0] == '\0') && home != NULL) {
371 size_t size_fallback = 1 + strlen(home) + strlen("/.config"); 379 config_home_fallback = format_str("%s/.config", home);
372 config_home_fallback = calloc(size_fallback, sizeof(char));
373 if (config_home_fallback != NULL)
374 snprintf(config_home_fallback, size_fallback, "%s/.config", home);
375 config_home = config_home_fallback; 380 config_home = config_home_fallback;
376 } 381 }
377 382
@@ -475,6 +480,11 @@ bool load_main_config(const char *file, bool is_active, bool validating) {
475 old_config->xwayland ? "enabled" : "disabled"); 480 old_config->xwayland ? "enabled" : "disabled");
476 config->xwayland = old_config->xwayland; 481 config->xwayland = old_config->xwayland;
477 482
483 // primary_selection can only be enabled/disabled at launch
484 sway_log(SWAY_DEBUG, "primary_selection will remain %s",
485 old_config->primary_selection ? "enabled" : "disabled");
486 config->primary_selection = old_config->primary_selection;
487
478 if (!config->validating) { 488 if (!config->validating) {
479 if (old_config->swaybg_client != NULL) { 489 if (old_config->swaybg_client != NULL) {
480 wl_client_destroy(old_config->swaybg_client); 490 wl_client_destroy(old_config->swaybg_client);
@@ -494,56 +504,7 @@ bool load_main_config(const char *file, bool is_active, bool validating) {
494 504
495 config->reading = true; 505 config->reading = true;
496 506
497 // Read security configs 507 bool success = load_config(path, config, &config->swaynag_config_errors);
498 // TODO: Security
499 bool success = true;
500 /*
501 DIR *dir = opendir(SYSCONFDIR "/sway/security.d");
502 if (!dir) {
503 sway_log(SWAY_ERROR,
504 "%s does not exist, sway will have no security configuration"
505 " and will probably be broken", SYSCONFDIR "/sway/security.d");
506 } else {
507 list_t *secconfigs = create_list();
508 char *base = SYSCONFDIR "/sway/security.d/";
509 struct dirent *ent = readdir(dir);
510 struct stat s;
511 while (ent != NULL) {
512 char *_path = malloc(strlen(ent->d_name) + strlen(base) + 1);
513 strcpy(_path, base);
514 strcat(_path, ent->d_name);
515 lstat(_path, &s);
516 if (S_ISREG(s.st_mode) && ent->d_name[0] != '.') {
517 list_add(secconfigs, _path);
518 }
519 else {
520 free(_path);
521 }
522 ent = readdir(dir);
523 }
524 closedir(dir);
525
526 list_qsort(secconfigs, qstrcmp);
527 for (int i = 0; i < secconfigs->length; ++i) {
528 char *_path = secconfigs->items[i];
529 if (stat(_path, &s) || s.st_uid != 0 || s.st_gid != 0 ||
530 (((s.st_mode & 0777) != 0644) &&
531 (s.st_mode & 0777) != 0444)) {
532 sway_log(SWAY_ERROR,
533 "Refusing to load %s - it must be owned by root "
534 "and mode 644 or 444", _path);
535 success = false;
536 } else {
537 success = success && load_config(_path, config);
538 }
539 }
540
541 list_free_items_and_destroy(secconfigs);
542 }
543 */
544
545 success = success && load_config(path, config,
546 &config->swaynag_config_errors);
547 508
548 if (validating) { 509 if (validating) {
549 free_config(config); 510 free_config(config);
@@ -571,7 +532,7 @@ bool load_main_config(const char *file, bool is_active, bool validating) {
571 } 532 }
572 sway_switch_retrigger_bindings_for_all(); 533 sway_switch_retrigger_bindings_for_all();
573 534
574 reset_outputs(); 535 apply_all_output_configs();
575 spawn_swaybg(); 536 spawn_swaybg();
576 537
577 config->reloading = false; 538 config->reloading = false;
@@ -1037,8 +998,12 @@ void translate_keysyms(struct input_config *input_config) {
1037 998
1038 struct xkb_rule_names rules = {0}; 999 struct xkb_rule_names rules = {0};
1039 input_config_fill_rule_names(input_config, &rules); 1000 input_config_fill_rule_names(input_config, &rules);
1040 config->keysym_translation_state = 1001 config->keysym_translation_state = keysym_translation_state_create(rules, 0);
1041 keysym_translation_state_create(rules); 1002 if (config->keysym_translation_state == NULL) {
1003 sway_log(SWAY_ERROR, "Failed to create keysym translation XKB state "
1004 "for device '%s'", input_config->identifier);
1005 return;
1006 }
1042 1007
1043 for (int i = 0; i < config->modes->length; ++i) { 1008 for (int i = 0; i < config->modes->length; ++i) {
1044 struct sway_mode *mode = config->modes->items[i]; 1009 struct sway_mode *mode = config->modes->items[i];
diff --git a/sway/config/bar.c b/sway/config/bar.c
index a8389244..908b2865 100644
--- a/sway/config/bar.c
+++ b/sway/config/bar.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <signal.h> 1#include <signal.h>
3#include <stdbool.h> 2#include <stdbool.h>
4#include <stdio.h> 3#include <stdio.h>
diff --git a/sway/config/input.c b/sway/config/input.c
index 44c2be28..de3b21ed 100644
--- a/sway/config/input.c
+++ b/sway/config/input.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <stdlib.h> 1#include <stdlib.h>
3#include <limits.h> 2#include <limits.h>
4#include <float.h> 3#include <float.h>
diff --git a/sway/config/output.c b/sway/config/output.c
index 1a5215fe..fb1956df 100644
--- a/sway/config/output.c
+++ b/sway/config/output.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <assert.h> 1#include <assert.h>
3#include <drm_fourcc.h> 2#include <drm_fourcc.h>
4#include <stdbool.h> 3#include <stdbool.h>
@@ -10,6 +9,7 @@
10#include <wlr/types/wlr_cursor.h> 9#include <wlr/types/wlr_cursor.h>
11#include <wlr/types/wlr_output_layout.h> 10#include <wlr/types/wlr_output_layout.h>
12#include <wlr/types/wlr_output.h> 11#include <wlr/types/wlr_output.h>
12#include <wlr/types/wlr_output_swapchain_manager.h>
13#include "sway/config.h" 13#include "sway/config.h"
14#include "sway/input/cursor.h" 14#include "sway/input/cursor.h"
15#include "sway/output.h" 15#include "sway/output.h"
@@ -79,7 +79,72 @@ struct output_config *new_output_config(const char *name) {
79 return oc; 79 return oc;
80} 80}
81 81
82void merge_output_config(struct output_config *dst, struct output_config *src) { 82// supersede_output_config clears all fields in dst that were set in src
83static void supersede_output_config(struct output_config *dst, struct output_config *src) {
84 if (src->enabled != -1) {
85 dst->enabled = -1;
86 }
87 if (src->width != -1) {
88 dst->width = -1;
89 }
90 if (src->height != -1) {
91 dst->height = -1;
92 }
93 if (src->x != -1) {
94 dst->x = -1;
95 }
96 if (src->y != -1) {
97 dst->y = -1;
98 }
99 if (src->scale != -1) {
100 dst->scale = -1;
101 }
102 if (src->scale_filter != SCALE_FILTER_DEFAULT) {
103 dst->scale_filter = SCALE_FILTER_DEFAULT;
104 }
105 if (src->subpixel != WL_OUTPUT_SUBPIXEL_UNKNOWN) {
106 dst->subpixel = WL_OUTPUT_SUBPIXEL_UNKNOWN;
107 }
108 if (src->refresh_rate != -1) {
109 dst->refresh_rate = -1;
110 }
111 if (src->custom_mode != -1) {
112 dst->custom_mode = -1;
113 }
114 if (src->drm_mode.type != (uint32_t) -1) {
115 dst->drm_mode.type = -1;
116 }
117 if (src->transform != -1) {
118 dst->transform = -1;
119 }
120 if (src->max_render_time != -1) {
121 dst->max_render_time = -1;
122 }
123 if (src->adaptive_sync != -1) {
124 dst->adaptive_sync = -1;
125 }
126 if (src->render_bit_depth != RENDER_BIT_DEPTH_DEFAULT) {
127 dst->render_bit_depth = RENDER_BIT_DEPTH_DEFAULT;
128 }
129 if (src->background) {
130 free(dst->background);
131 dst->background = NULL;
132 }
133 if (src->background_option) {
134 free(dst->background_option);
135 dst->background_option = NULL;
136 }
137 if (src->background_fallback) {
138 free(dst->background_fallback);
139 dst->background_fallback = NULL;
140 }
141 if (src->power != -1) {
142 dst->power = -1;
143 }
144}
145
146// merge_output_config sets all fields in dst that were set in src
147static void merge_output_config(struct output_config *dst, struct output_config *src) {
83 if (src->enabled != -1) { 148 if (src->enabled != -1) {
84 dst->enabled = src->enabled; 149 dst->enabled = src->enabled;
85 } 150 }
@@ -142,94 +207,42 @@ void merge_output_config(struct output_config *dst, struct output_config *src) {
142 } 207 }
143} 208}
144 209
145static void merge_wildcard_on_all(struct output_config *wildcard) { 210void store_output_config(struct output_config *oc) {
146 for (int i = 0; i < config->output_configs->length; i++) { 211 bool merged = false;
147 struct output_config *oc = config->output_configs->items[i]; 212 bool wildcard = strcmp(oc->name, "*") == 0;
148 if (strcmp(wildcard->name, oc->name) != 0) { 213 struct sway_output *output = wildcard ? NULL : all_output_by_name_or_id(oc->name);
149 sway_log(SWAY_DEBUG, "Merging output * config on %s", oc->name);
150 merge_output_config(oc, wildcard);
151 }
152 }
153}
154
155static void merge_id_on_name(struct output_config *oc) {
156 struct sway_output *output = all_output_by_name_or_id(oc->name);
157 if (output == NULL) {
158 return;
159 }
160 214
161 const char *name = output->wlr_output->name;
162 char id[128]; 215 char id[128];
163 output_get_identifier(id, sizeof(id), output); 216 if (output) {
164 217 output_get_identifier(id, sizeof(id), output);
165 char *id_on_name = format_str("%s on %s", id, name);
166 if (!id_on_name) {
167 return;
168 } 218 }
169 219
170 int i = list_seq_find(config->output_configs, output_name_cmp, id_on_name); 220 for (int i = 0; i < config->output_configs->length; i++) {
171 if (i >= 0) { 221 struct output_config *old = config->output_configs->items[i];
172 sway_log(SWAY_DEBUG, "Merging on top of existing id on name config"); 222
173 merge_output_config(config->output_configs->items[i], oc); 223 // If the old config matches the new config's name, regardless of
174 } else { 224 // whether it was name or identifier, merge on top of the existing
175 // If both a name and identifier config, exist generate an id on name 225 // config. If the new config is a wildcard, this also merges on top of
176 int ni = list_seq_find(config->output_configs, output_name_cmp, name); 226 // old wildcard configs.
177 int ii = list_seq_find(config->output_configs, output_name_cmp, id); 227 if (strcmp(old->name, oc->name) == 0) {
178 if ((ni >= 0 && ii >= 0) || (ni >= 0 && strcmp(oc->name, id) == 0) 228 merge_output_config(old, oc);
179 || (ii >= 0 && strcmp(oc->name, name) == 0)) { 229 merged = true;
180 struct output_config *ion_oc = new_output_config(id_on_name); 230 continue;
181 if (ni >= 0) {
182 merge_output_config(ion_oc, config->output_configs->items[ni]);
183 }
184 if (ii >= 0) {
185 merge_output_config(ion_oc, config->output_configs->items[ii]);
186 }
187 merge_output_config(ion_oc, oc);
188 list_add(config->output_configs, ion_oc);
189 sway_log(SWAY_DEBUG, "Generated id on name output config \"%s\""
190 " (enabled: %d) (%dx%d@%fHz position %d,%d scale %f "
191 "transform %d) (bg %s %s) (power %d) (max render time: %d)",
192 ion_oc->name, ion_oc->enabled, ion_oc->width, ion_oc->height,
193 ion_oc->refresh_rate, ion_oc->x, ion_oc->y, ion_oc->scale,
194 ion_oc->transform, ion_oc->background,
195 ion_oc->background_option, ion_oc->power,
196 ion_oc->max_render_time);
197 } 231 }
198 }
199 free(id_on_name);
200}
201 232
202struct output_config *store_output_config(struct output_config *oc) { 233 // If the new config is a wildcard config we supersede all non-wildcard
203 bool wildcard = strcmp(oc->name, "*") == 0; 234 // configs. Old wildcard configs have already been handled above.
204 if (wildcard) { 235 if (wildcard) {
205 merge_wildcard_on_all(oc); 236 supersede_output_config(old, oc);
206 } else { 237 continue;
207 merge_id_on_name(oc); 238 }
208 }
209 239
210 int i = list_seq_find(config->output_configs, output_name_cmp, oc->name); 240 // If the new config matches an output's name, and the old config
211 if (i >= 0) { 241 // matches on that output's identifier, supersede it.
212 sway_log(SWAY_DEBUG, "Merging on top of existing output config"); 242 if (output && strcmp(old->name, id) == 0 &&
213 struct output_config *current = config->output_configs->items[i]; 243 strcmp(oc->name, output->wlr_output->name) == 0) {
214 merge_output_config(current, oc); 244 supersede_output_config(old, oc);
215 free_output_config(oc);
216 oc = current;
217 } else if (!wildcard) {
218 sway_log(SWAY_DEBUG, "Adding non-wildcard output config");
219 i = list_seq_find(config->output_configs, output_name_cmp, "*");
220 if (i >= 0) {
221 sway_log(SWAY_DEBUG, "Merging on top of output * config");
222 struct output_config *current = new_output_config(oc->name);
223 merge_output_config(current, config->output_configs->items[i]);
224 merge_output_config(current, oc);
225 free_output_config(oc);
226 oc = current;
227 } 245 }
228 list_add(config->output_configs, oc);
229 } else {
230 // New wildcard config. Just add it
231 sway_log(SWAY_DEBUG, "Adding output * config");
232 list_add(config->output_configs, oc);
233 } 246 }
234 247
235 sway_log(SWAY_DEBUG, "Config stored for output %s (enabled: %d) (%dx%d@%fHz " 248 sway_log(SWAY_DEBUG, "Config stored for output %s (enabled: %d) (%dx%d@%fHz "
@@ -240,7 +253,13 @@ struct output_config *store_output_config(struct output_config *oc) {
240 oc->transform, oc->background, oc->background_option, oc->power, 253 oc->transform, oc->background, oc->background_option, oc->power,
241 oc->max_render_time); 254 oc->max_render_time);
242 255
243 return oc; 256 // If the configuration was not merged into an existing configuration, add
257 // it to the list. Otherwise we're done with it and can free it.
258 if (!merged) {
259 list_add(config->output_configs, oc);
260 } else {
261 free_output_config(oc);
262 }
244} 263}
245 264
246static void set_mode(struct wlr_output *output, struct wlr_output_state *pending, 265static void set_mode(struct wlr_output *output, struct wlr_output_state *pending,
@@ -367,22 +386,18 @@ static int compute_default_scale(struct wlr_output *output,
367 return 2; 386 return 2;
368} 387}
369 388
370/* Lists of formats to try, in order, when a specific render bit depth has 389static bool render_format_is_10bit(uint32_t render_format) {
371 * been asked for. The second to last format in each list should always 390 return render_format == DRM_FORMAT_XRGB2101010 ||
372 * be XRGB8888, as a reliable backup in case the others are not available; 391 render_format == DRM_FORMAT_XBGR2101010;
373 * the last should be DRM_FORMAT_INVALID, to indicate the end of the list. */ 392}
374static const uint32_t *bit_depth_preferences[] = { 393
375 [RENDER_BIT_DEPTH_8] = (const uint32_t []){ 394static bool render_format_is_bgr(uint32_t fmt) {
376 DRM_FORMAT_XRGB8888, 395 return fmt == DRM_FORMAT_XBGR2101010 || fmt == DRM_FORMAT_XBGR8888;
377 DRM_FORMAT_INVALID, 396}
378 }, 397
379 [RENDER_BIT_DEPTH_10] = (const uint32_t []){ 398static bool output_config_is_disabling(struct output_config *oc) {
380 DRM_FORMAT_XRGB2101010, 399 return oc && (!oc->enabled || oc->power == 0);
381 DRM_FORMAT_XBGR2101010, 400}
382 DRM_FORMAT_XRGB8888,
383 DRM_FORMAT_INVALID,
384 },
385};
386 401
387static void queue_output_config(struct output_config *oc, 402static void queue_output_config(struct output_config *oc,
388 struct sway_output *output, struct wlr_output_state *pending) { 403 struct sway_output *output, struct wlr_output_state *pending) {
@@ -392,7 +407,7 @@ static void queue_output_config(struct output_config *oc,
392 407
393 struct wlr_output *wlr_output = output->wlr_output; 408 struct wlr_output *wlr_output = output->wlr_output;
394 409
395 if (oc && (!oc->enabled || oc->power == 0)) { 410 if (output_config_is_disabling(oc)) {
396 sway_log(SWAY_DEBUG, "Turning off output %s", wlr_output->name); 411 sway_log(SWAY_DEBUG, "Turning off output %s", wlr_output->name);
397 wlr_output_state_set_enabled(pending, false); 412 wlr_output_state_set_enabled(pending, false);
398 return; 413 return;
@@ -415,22 +430,6 @@ static void queue_output_config(struct output_config *oc,
415 struct wlr_output_mode *preferred_mode = 430 struct wlr_output_mode *preferred_mode =
416 wlr_output_preferred_mode(wlr_output); 431 wlr_output_preferred_mode(wlr_output);
417 wlr_output_state_set_mode(pending, preferred_mode); 432 wlr_output_state_set_mode(pending, preferred_mode);
418
419 if (!wlr_output_test_state(wlr_output, pending)) {
420 sway_log(SWAY_DEBUG, "Preferred mode rejected, "
421 "falling back to another mode");
422 struct wlr_output_mode *mode;
423 wl_list_for_each(mode, &wlr_output->modes, link) {
424 if (mode == preferred_mode) {
425 continue;
426 }
427
428 wlr_output_state_set_mode(pending, mode);
429 if (wlr_output_test_state(wlr_output, pending)) {
430 break;
431 }
432 }
433 }
434 } 433 }
435 434
436 if (oc && (oc->subpixel != WL_OUTPUT_SUBPIXEL_UNKNOWN || config->reloading)) { 435 if (oc && (oc->subpixel != WL_OUTPUT_SUBPIXEL_UNKNOWN || config->reloading)) {
@@ -481,54 +480,27 @@ static void queue_output_config(struct output_config *oc,
481 sway_log(SWAY_DEBUG, "Set %s adaptive sync to %d", wlr_output->name, 480 sway_log(SWAY_DEBUG, "Set %s adaptive sync to %d", wlr_output->name,
482 oc->adaptive_sync); 481 oc->adaptive_sync);
483 wlr_output_state_set_adaptive_sync_enabled(pending, oc->adaptive_sync == 1); 482 wlr_output_state_set_adaptive_sync_enabled(pending, oc->adaptive_sync == 1);
484 if (oc->adaptive_sync == 1 && !wlr_output_test_state(wlr_output, pending)) {
485 sway_log(SWAY_DEBUG, "Adaptive sync failed, ignoring");
486 wlr_output_state_set_adaptive_sync_enabled(pending, false);
487 }
488 } 483 }
489 484
490 if (oc && oc->render_bit_depth != RENDER_BIT_DEPTH_DEFAULT) { 485 if (oc && oc->render_bit_depth != RENDER_BIT_DEPTH_DEFAULT) {
491 const uint32_t *fmts = bit_depth_preferences[oc->render_bit_depth]; 486 if (oc->render_bit_depth == RENDER_BIT_DEPTH_10 &&
492 assert(fmts); 487 render_format_is_10bit(output->wlr_output->render_format)) {
493 488 // 10-bit was set successfully before, try to save some tests by reusing the format
494 for (size_t i = 0; fmts[i] != DRM_FORMAT_INVALID; i++) { 489 wlr_output_state_set_render_format(pending, output->wlr_output->render_format);
495 wlr_output_state_set_render_format(pending, fmts[i]); 490 } else if (oc->render_bit_depth == RENDER_BIT_DEPTH_10) {
496 if (wlr_output_test_state(wlr_output, pending)) { 491 wlr_output_state_set_render_format(pending, DRM_FORMAT_XRGB2101010);
497 break; 492 } else {
498 } 493 wlr_output_state_set_render_format(pending, DRM_FORMAT_XRGB8888);
499
500 sway_log(SWAY_DEBUG, "Preferred output format 0x%08x "
501 "failed to work, falling back to next in "
502 "list, 0x%08x", fmts[i], fmts[i + 1]);
503 } 494 }
504 } 495 }
505} 496}
506 497
507bool apply_output_config(struct output_config *oc, struct sway_output *output) { 498static bool finalize_output_config(struct output_config *oc, struct sway_output *output) {
508 if (output == root->fallback_output) { 499 if (output == root->fallback_output) {
509 return false; 500 return false;
510 } 501 }
511 502
512 struct wlr_output *wlr_output = output->wlr_output; 503 struct wlr_output *wlr_output = output->wlr_output;
513
514 // Flag to prevent the output mode event handler from calling us
515 output->enabling = (!oc || oc->enabled);
516
517 struct wlr_output_state pending = {0};
518 queue_output_config(oc, output, &pending);
519
520 sway_log(SWAY_DEBUG, "Committing output %s", wlr_output->name);
521 if (!wlr_output_commit_state(wlr_output, &pending)) {
522 // Failed to commit output changes, maybe the output is missing a CRTC.
523 // Leave the output disabled for now and try again when the output gets
524 // the mode we asked for.
525 sway_log(SWAY_ERROR, "Failed to commit output %s", wlr_output->name);
526 output->enabling = false;
527 return false;
528 }
529
530 output->enabling = false;
531
532 if (oc && !oc->enabled) { 504 if (oc && !oc->enabled) {
533 sway_log(SWAY_DEBUG, "Disabling output %s", oc->name); 505 sway_log(SWAY_DEBUG, "Disabling output %s", oc->name);
534 if (output->enabled) { 506 if (output->enabled) {
@@ -584,25 +556,9 @@ bool apply_output_config(struct output_config *oc, struct sway_output *output) {
584 output->max_render_time = oc->max_render_time; 556 output->max_render_time = oc->max_render_time;
585 } 557 }
586 558
587 // Reconfigure all devices, since input config may have been applied before
588 // this output came online, and some config items (like map_to_output) are
589 // dependent on an output being present.
590 input_manager_configure_all_input_mappings();
591 // Reconfigure the cursor images, since the scale may have changed.
592 input_manager_configure_xcursor();
593 return true; 559 return true;
594} 560}
595 561
596bool test_output_config(struct output_config *oc, struct sway_output *output) {
597 if (output == root->fallback_output) {
598 return false;
599 }
600
601 struct wlr_output_state pending = {0};
602 queue_output_config(oc, output, &pending);
603 return wlr_output_test_state(output->wlr_output, &pending);
604}
605
606static void default_output_config(struct output_config *oc, 562static void default_output_config(struct output_config *oc,
607 struct wlr_output *wlr_output) { 563 struct wlr_output *wlr_output) {
608 oc->enabled = 1; 564 oc->enabled = 1;
@@ -622,140 +578,415 @@ static void default_output_config(struct output_config *oc,
622 oc->max_render_time = 0; 578 oc->max_render_time = 0;
623} 579}
624 580
625static struct output_config *get_output_config(char *identifier, 581// find_output_config returns a merged output_config containing all stored
626 struct sway_output *sway_output) { 582// configuration that applies to the specified output.
583struct output_config *find_output_config(struct sway_output *sway_output) {
627 const char *name = sway_output->wlr_output->name; 584 const char *name = sway_output->wlr_output->name;
585 struct output_config *oc = NULL;
628 586
629 struct output_config *oc_id_on_name = NULL; 587 struct output_config *result = new_output_config(name);
630 struct output_config *oc_name = NULL; 588 if (config->reloading) {
631 struct output_config *oc_id = NULL; 589 default_output_config(result, sway_output->wlr_output);
590 }
632 591
633 char *id_on_name = format_str("%s on %s", identifier, name); 592 char id[128];
634 int i = list_seq_find(config->output_configs, output_name_cmp, id_on_name); 593 output_get_identifier(id, sizeof(id), sway_output);
635 if (i >= 0) { 594
636 oc_id_on_name = config->output_configs->items[i]; 595 int i;
637 } else { 596 bool match = false;
638 i = list_seq_find(config->output_configs, output_name_cmp, name); 597 if ((i = list_seq_find(config->output_configs, output_name_cmp, "*")) >= 0) {
639 if (i >= 0) { 598 match = true;
640 oc_name = config->output_configs->items[i]; 599 oc = config->output_configs->items[i];
600 merge_output_config(result, oc);
601 }
602 if ((i = list_seq_find(config->output_configs, output_name_cmp, name)) >= 0) {
603 match = true;
604 oc = config->output_configs->items[i];
605 merge_output_config(result, oc);
606 }
607 if ((i = list_seq_find(config->output_configs, output_name_cmp, id)) >= 0) {
608 match = true;
609 oc = config->output_configs->items[i];
610 merge_output_config(result, oc);
611 }
612
613 if (!match && !config->reloading) {
614 // No name, identifier, or wildcard config. Since we are not
615 // reloading with defaults, the output config will be empty, so
616 // just return NULL
617 free_output_config(result);
618 return NULL;
619 }
620
621 return result;
622}
623
624static bool config_has_auto_mode(struct output_config *oc) {
625 if (!oc) {
626 return true;
627 }
628 if (oc->drm_mode.type != 0 && oc->drm_mode.type != (uint32_t)-1) {
629 return true;
630 } else if (oc->width > 0 && oc->height > 0) {
631 return true;
632 }
633 return false;
634}
635
636struct search_context {
637 struct wlr_output_swapchain_manager *swapchain_mgr;
638 struct wlr_backend_output_state *states;
639 struct matched_output_config *configs;
640 size_t configs_len;
641 bool degrade_to_off;
642};
643
644static void dump_output_state(struct wlr_output *wlr_output, struct wlr_output_state *state) {
645 sway_log(SWAY_DEBUG, "Output state for %s", wlr_output->name);
646 if (state->committed & WLR_OUTPUT_STATE_ENABLED) {
647 sway_log(SWAY_DEBUG, " enabled: %s", state->enabled ? "yes" : "no");
648 }
649 if (state->committed & WLR_OUTPUT_STATE_RENDER_FORMAT) {
650 sway_log(SWAY_DEBUG, " render_format: %d", state->render_format);
651 }
652 if (state->committed & WLR_OUTPUT_STATE_MODE) {
653 if (state->mode_type == WLR_OUTPUT_STATE_MODE_CUSTOM) {
654 sway_log(SWAY_DEBUG, " custom mode: %dx%d@%dmHz",
655 state->custom_mode.width, state->custom_mode.height, state->custom_mode.refresh);
656 } else {
657 sway_log(SWAY_DEBUG, " mode: %dx%d@%dmHz%s",
658 state->mode->width, state->mode->height, state->mode->refresh,
659 state->mode->preferred ? " (preferred)" : "");
660 }
661 }
662 if (state->committed & WLR_OUTPUT_STATE_ADAPTIVE_SYNC_ENABLED) {
663 sway_log(SWAY_DEBUG, " adaptive_sync: %s",
664 state->adaptive_sync_enabled ? "enabled": "disabled");
665 }
666}
667
668static bool search_valid_config(struct search_context *ctx, size_t output_idx);
669
670static void reset_output_state(struct wlr_output_state *state) {
671 wlr_output_state_finish(state);
672 wlr_output_state_init(state);
673 state->committed = 0;
674}
675
676static void clear_later_output_states(struct wlr_backend_output_state *states,
677 size_t configs_len, size_t output_idx) {
678
679 // Clear and disable all output states after this one to avoid conflict
680 // with previous tests.
681 for (size_t idx = output_idx+1; idx < configs_len; idx++) {
682 struct wlr_backend_output_state *backend_state = &states[idx];
683 struct wlr_output_state *state = &backend_state->base;
684
685 reset_output_state(state);
686 wlr_output_state_set_enabled(state, false);
687 }
688}
689
690static bool search_finish(struct search_context *ctx, size_t output_idx) {
691 struct wlr_backend_output_state *backend_state = &ctx->states[output_idx];
692 struct wlr_output_state *state = &backend_state->base;
693 struct wlr_output *wlr_output = backend_state->output;
694
695 clear_later_output_states(ctx->states, ctx->configs_len, output_idx);
696 dump_output_state(wlr_output, state);
697 return wlr_output_swapchain_manager_prepare(ctx->swapchain_mgr, ctx->states, ctx->configs_len) &&
698 search_valid_config(ctx, output_idx+1);
699}
700
701static bool search_adaptive_sync(struct search_context *ctx, size_t output_idx) {
702 struct matched_output_config *cfg = &ctx->configs[output_idx];
703 struct wlr_backend_output_state *backend_state = &ctx->states[output_idx];
704 struct wlr_output_state *state = &backend_state->base;
705
706 if (cfg->config && cfg->config->adaptive_sync == 1) {
707 wlr_output_state_set_adaptive_sync_enabled(state, true);
708 if (search_finish(ctx, output_idx)) {
709 return true;
710 }
711 }
712 if (!cfg->config || cfg->config->adaptive_sync != -1) {
713 wlr_output_state_set_adaptive_sync_enabled(state, false);
714 if (search_finish(ctx, output_idx)) {
715 return true;
641 } 716 }
717 }
718 // If adaptive sync has not been set, or fallback in case we are on a
719 // backend that cannot disable adaptive sync such as the wayland backend.
720 state->committed &= ~WLR_OUTPUT_STATE_ADAPTIVE_SYNC_ENABLED;
721 return search_finish(ctx, output_idx);
722}
723
724static bool search_mode(struct search_context *ctx, size_t output_idx) {
725 struct matched_output_config *cfg = &ctx->configs[output_idx];
726 struct wlr_backend_output_state *backend_state = &ctx->states[output_idx];
727 struct wlr_output_state *state = &backend_state->base;
728 struct wlr_output *wlr_output = backend_state->output;
729
730 if (!config_has_auto_mode(cfg->config)) {
731 return search_adaptive_sync(ctx, output_idx);
732 }
642 733
643 i = list_seq_find(config->output_configs, output_name_cmp, identifier); 734 struct wlr_output_mode *preferred_mode = wlr_output_preferred_mode(wlr_output);
644 if (i >= 0) { 735 if (preferred_mode) {
645 oc_id = config->output_configs->items[i]; 736 wlr_output_state_set_mode(state, preferred_mode);
737 if (search_adaptive_sync(ctx, output_idx)) {
738 return true;
646 } 739 }
647 } 740 }
648 741
649 struct output_config *result = new_output_config("temp"); 742 if (wl_list_empty(&wlr_output->modes)) {
650 if (config->reloading) { 743 state->committed &= ~WLR_OUTPUT_STATE_MODE;
651 default_output_config(result, sway_output->wlr_output); 744 return search_adaptive_sync(ctx, output_idx);
652 } 745 }
653 if (oc_id_on_name) { 746
654 // Already have an identifier on name config, use that 747 struct wlr_output_mode *mode;
655 free(result->name); 748 wl_list_for_each(mode, &backend_state->output->modes, link) {
656 result->name = strdup(id_on_name); 749 if (mode == preferred_mode) {
657 merge_output_config(result, oc_id_on_name); 750 continue;
658 } else if (oc_name && oc_id) { 751 }
659 // Generate a config named `<identifier> on <name>` which contains a 752 wlr_output_state_set_mode(state, mode);
660 // merged copy of the identifier on name. This will make sure that both 753 if (search_adaptive_sync(ctx, output_idx)) {
661 // identifier and name configs are respected, with identifier getting 754 return true;
662 // priority
663 struct output_config *temp = new_output_config(id_on_name);
664 merge_output_config(temp, oc_name);
665 merge_output_config(temp, oc_id);
666 list_add(config->output_configs, temp);
667
668 free(result->name);
669 result->name = strdup(id_on_name);
670 merge_output_config(result, temp);
671
672 sway_log(SWAY_DEBUG, "Generated output config \"%s\" (enabled: %d)"
673 " (%dx%d@%fHz position %d,%d scale %f transform %d) (bg %s %s)"
674 " (power %d) (max render time: %d)", result->name, result->enabled,
675 result->width, result->height, result->refresh_rate,
676 result->x, result->y, result->scale, result->transform,
677 result->background, result->background_option, result->power,
678 result->max_render_time);
679 } else if (oc_name) {
680 // No identifier config, just return a copy of the name config
681 free(result->name);
682 result->name = strdup(name);
683 merge_output_config(result, oc_name);
684 } else if (oc_id) {
685 // No name config, just return a copy of the identifier config
686 free(result->name);
687 result->name = strdup(identifier);
688 merge_output_config(result, oc_id);
689 } else {
690 i = list_seq_find(config->output_configs, output_name_cmp, "*");
691 if (i >= 0) {
692 // No name or identifier config, but there is a wildcard config
693 free(result->name);
694 result->name = strdup("*");
695 merge_output_config(result, config->output_configs->items[i]);
696 } else if (!config->reloading) {
697 // No name, identifier, or wildcard config. Since we are not
698 // reloading with defaults, the output config will be empty, so
699 // just return NULL
700 free_output_config(result);
701 result = NULL;
702 } 755 }
703 } 756 }
704 757
705 free(id_on_name); 758 return false;
706 return result;
707} 759}
708 760
709struct output_config *find_output_config(struct sway_output *output) { 761static bool search_render_format(struct search_context *ctx, size_t output_idx) {
710 char id[128]; 762 struct matched_output_config *cfg = &ctx->configs[output_idx];
711 output_get_identifier(id, sizeof(id), output); 763 struct wlr_backend_output_state *backend_state = &ctx->states[output_idx];
712 return get_output_config(id, output); 764 struct wlr_output_state *state = &backend_state->base;
765 struct wlr_output *wlr_output = backend_state->output;
766
767 uint32_t fmts[] = {
768 DRM_FORMAT_XRGB2101010,
769 DRM_FORMAT_XBGR2101010,
770 DRM_FORMAT_XRGB8888,
771 DRM_FORMAT_INVALID,
772 };
773 if (render_format_is_bgr(wlr_output->render_format)) {
774 // Start with BGR in the unlikely event that we previously required it.
775 fmts[0] = DRM_FORMAT_XBGR2101010;
776 fmts[1] = DRM_FORMAT_XRGB2101010;
777 }
778
779 const struct wlr_drm_format_set *primary_formats =
780 wlr_output_get_primary_formats(wlr_output, WLR_BUFFER_CAP_DMABUF);
781 bool need_10bit = cfg->config && cfg->config->render_bit_depth == RENDER_BIT_DEPTH_10;
782 for (size_t idx = 0; fmts[idx] != DRM_FORMAT_INVALID; idx++) {
783 if (!need_10bit && render_format_is_10bit(fmts[idx])) {
784 continue;
785 }
786 if (!wlr_drm_format_set_get(primary_formats, fmts[idx])) {
787 // This is not a supported format for this output
788 continue;
789 }
790 wlr_output_state_set_render_format(state, fmts[idx]);
791 if (search_mode(ctx, output_idx)) {
792 return true;
793 }
794 }
795 return false;
713} 796}
714 797
715void apply_output_config_to_outputs(struct output_config *oc) { 798static bool search_valid_config(struct search_context *ctx, size_t output_idx) {
716 // Try to find the output container and apply configuration now. If 799 if (output_idx >= ctx->configs_len) {
717 // this is during startup then there will be no container and config 800 // We reached the end of the search, all good!
718 // will be applied during normal "new output" event from wlroots. 801 return true;
719 bool wildcard = strcmp(oc->name, "*") == 0; 802 }
720 struct sway_output *sway_output, *tmp;
721 wl_list_for_each_safe(sway_output, tmp, &root->all_outputs, link) {
722 if (output_match_name_or_id(sway_output, oc->name)) {
723 char id[128];
724 output_get_identifier(id, sizeof(id), sway_output);
725 struct output_config *current = get_output_config(id, sway_output);
726 if (!current) {
727 // No stored output config matched, apply oc directly
728 sway_log(SWAY_DEBUG, "Applying oc directly");
729 current = new_output_config(oc->name);
730 merge_output_config(current, oc);
731 }
732 apply_output_config(current, sway_output);
733 free_output_config(current);
734 803
735 if (!wildcard) { 804 struct matched_output_config *cfg = &ctx->configs[output_idx];
736 // Stop looking if the output config isn't applicable to all 805 struct wlr_backend_output_state *backend_state = &ctx->states[output_idx];
737 // outputs 806 struct wlr_output_state *state = &backend_state->base;
738 break; 807 struct wlr_output *wlr_output = backend_state->output;
739 } 808
809 if (!output_config_is_disabling(cfg->config)) {
810 // Search through our possible configurations, doing a depth-first
811 // through render_format, modes, adaptive_sync and the next output's
812 // config.
813 queue_output_config(cfg->config, cfg->output, &backend_state->base);
814 if (search_render_format(ctx, output_idx)) {
815 return true;
816 } else if (!ctx->degrade_to_off) {
817 return false;
818 }
819 // We could not get anything to work, try to disable this output to see
820 // if we can at least make the outputs before us work.
821 sway_log(SWAY_DEBUG, "Unable to find valid config with output %s, disabling",
822 wlr_output->name);
823 reset_output_state(state);
824 }
825
826 wlr_output_state_set_enabled(state, false);
827 return search_finish(ctx, output_idx);
828}
829
830static int compare_matched_output_config_priority(const void *a, const void *b) {
831
832 const struct matched_output_config *amc = a;
833 const struct matched_output_config *bmc = b;
834 bool a_disabling = output_config_is_disabling(amc->config);
835 bool b_disabling = output_config_is_disabling(bmc->config);
836 bool a_enabled = amc->output->enabled;
837 bool b_enabled = bmc->output->enabled;
838
839 // We want to give priority to existing enabled outputs. To do so, we want
840 // the configuration order to be:
841 // 1. Existing, enabled outputs
842 // 2. Outputs that need to be enabled
843 // 3. Disabled or disabling outputs
844 if (a_enabled && !a_disabling) {
845 return -1;
846 } else if (b_enabled && !b_disabling) {
847 return 1;
848 } else if (b_disabling && !a_disabling) {
849 return -1;
850 } else if (a_disabling && !b_disabling) {
851 return 1;
852 }
853 return 0;
854}
855
856void sort_output_configs_by_priority(struct matched_output_config *configs,
857 size_t configs_len) {
858 qsort(configs, configs_len, sizeof(*configs), compare_matched_output_config_priority);
859}
860
861bool apply_output_configs(struct matched_output_config *configs,
862 size_t configs_len, bool test_only, bool degrade_to_off) {
863 struct wlr_backend_output_state *states = calloc(configs_len, sizeof(*states));
864 if (!states) {
865 return false;
866 }
867
868 sway_log(SWAY_DEBUG, "Committing %zd outputs", configs_len);
869 for (size_t idx = 0; idx < configs_len; idx++) {
870 struct matched_output_config *cfg = &configs[idx];
871 struct wlr_backend_output_state *backend_state = &states[idx];
872
873 backend_state->output = cfg->output->wlr_output;
874 wlr_output_state_init(&backend_state->base);
875
876 sway_log(SWAY_DEBUG, "Preparing config for %s",
877 cfg->output->wlr_output->name);
878 queue_output_config(cfg->config, cfg->output, &backend_state->base);
879 }
880
881 struct wlr_output_swapchain_manager swapchain_mgr;
882 wlr_output_swapchain_manager_init(&swapchain_mgr, server.backend);
883
884 bool ok = wlr_output_swapchain_manager_prepare(&swapchain_mgr, states, configs_len);
885 if (!ok) {
886 sway_log(SWAY_ERROR, "Requested backend configuration failed, searching for valid fallbacks");
887 struct search_context ctx = {
888 .swapchain_mgr = &swapchain_mgr,
889 .states = states,
890 .configs = configs,
891 .configs_len = configs_len,
892 .degrade_to_off = degrade_to_off,
893 };
894 if (!search_valid_config(&ctx, 0)) {
895 sway_log(SWAY_ERROR, "Search for valid config failed");
896 goto out;
897 }
898 }
899
900 if (test_only) {
901 // The swapchain manager already did a test for us
902 goto out;
903 }
904
905 for (size_t idx = 0; idx < configs_len; idx++) {
906 struct matched_output_config *cfg = &configs[idx];
907 struct wlr_backend_output_state *backend_state = &states[idx];
908
909 struct wlr_scene_output_state_options opts = {
910 .swapchain = wlr_output_swapchain_manager_get_swapchain(
911 &swapchain_mgr, backend_state->output),
912 };
913 struct wlr_scene_output *scene_output = cfg->output->scene_output;
914 struct wlr_output_state *state = &backend_state->base;
915 if (!wlr_scene_output_build_state(scene_output, state, &opts)) {
916 sway_log(SWAY_ERROR, "Building output state for '%s' failed",
917 backend_state->output->name);
918 goto out;
740 } 919 }
741 } 920 }
742 921
922 ok = wlr_backend_commit(server.backend, states, configs_len);
923 if (!ok) {
924 sway_log(SWAY_ERROR, "Backend commit failed");
925 goto out;
926 }
927
928 sway_log(SWAY_DEBUG, "Commit of %zd outputs succeeded", configs_len);
929
930 wlr_output_swapchain_manager_apply(&swapchain_mgr);
931
932 for (size_t idx = 0; idx < configs_len; idx++) {
933 struct matched_output_config *cfg = &configs[idx];
934 sway_log(SWAY_DEBUG, "Finalizing config for %s",
935 cfg->output->wlr_output->name);
936 finalize_output_config(cfg->config, cfg->output);
937 }
938
939out:
940 wlr_output_swapchain_manager_finish(&swapchain_mgr);
941 for (size_t idx = 0; idx < configs_len; idx++) {
942 struct wlr_backend_output_state *backend_state = &states[idx];
943 wlr_output_state_finish(&backend_state->base);
944 }
945 free(states);
946
947 // Reconfigure all devices, since input config may have been applied before
948 // this output came online, and some config items (like map_to_output) are
949 // dependent on an output being present.
950 input_manager_configure_all_input_mappings();
951 // Reconfigure the cursor images, since the scale may have changed.
952 input_manager_configure_xcursor();
953
743 struct sway_seat *seat; 954 struct sway_seat *seat;
744 wl_list_for_each(seat, &server.input->seats, link) { 955 wl_list_for_each(seat, &server.input->seats, link) {
745 wlr_seat_pointer_notify_clear_focus(seat->wlr_seat); 956 wlr_seat_pointer_notify_clear_focus(seat->wlr_seat);
746 cursor_rebase(seat->cursor); 957 cursor_rebase(seat->cursor);
747 } 958 }
959
960 return ok;
748} 961}
749 962
750void reset_outputs(void) { 963void apply_all_output_configs(void) {
751 struct output_config *oc = NULL; 964 size_t configs_len = wl_list_length(&root->all_outputs);
752 int i = list_seq_find(config->output_configs, output_name_cmp, "*"); 965 struct matched_output_config *configs = calloc(configs_len, sizeof(*configs));
753 if (i >= 0) { 966 if (!configs) {
754 oc = config->output_configs->items[i]; 967 return;
755 } else {
756 oc = store_output_config(new_output_config("*"));
757 } 968 }
758 apply_output_config_to_outputs(oc); 969
970 int config_idx = 0;
971 struct sway_output *sway_output;
972 wl_list_for_each(sway_output, &root->all_outputs, link) {
973 if (sway_output == root->fallback_output) {
974 configs_len--;
975 continue;
976 }
977
978 struct matched_output_config *config = &configs[config_idx++];
979 config->output = sway_output;
980 config->config = find_output_config(sway_output);
981 }
982
983 sort_output_configs_by_priority(configs, configs_len);
984 apply_output_configs(configs, configs_len, false, true);
985 for (size_t idx = 0; idx < configs_len; idx++) {
986 struct matched_output_config *cfg = &configs[idx];
987 free_output_config(cfg->config);
988 }
989 free(configs);
759} 990}
760 991
761void free_output_config(struct output_config *oc) { 992void free_output_config(struct output_config *oc) {
@@ -822,7 +1053,9 @@ static bool _spawn_swaybg(char **command) {
822 setenv("WAYLAND_SOCKET", wayland_socket_str, true); 1053 setenv("WAYLAND_SOCKET", wayland_socket_str, true);
823 1054
824 execvp(command[0], command); 1055 execvp(command[0], command);
825 sway_log_errno(SWAY_ERROR, "execvp failed"); 1056 sway_log_errno(SWAY_ERROR, "failed to execute '%s' "
1057 "(background configuration probably not applied)",
1058 command[0]);
826 _exit(EXIT_FAILURE); 1059 _exit(EXIT_FAILURE);
827 } 1060 }
828 _exit(EXIT_SUCCESS); 1061 _exit(EXIT_SUCCESS);
@@ -832,12 +1065,13 @@ static bool _spawn_swaybg(char **command) {
832 sway_log_errno(SWAY_ERROR, "close failed"); 1065 sway_log_errno(SWAY_ERROR, "close failed");
833 return false; 1066 return false;
834 } 1067 }
835 if (waitpid(pid, NULL, 0) < 0) { 1068 int fork_status = 0;
1069 if (waitpid(pid, &fork_status, 0) < 0) {
836 sway_log_errno(SWAY_ERROR, "waitpid failed"); 1070 sway_log_errno(SWAY_ERROR, "waitpid failed");
837 return false; 1071 return false;
838 } 1072 }
839 1073
840 return true; 1074 return WIFEXITED(fork_status) && WEXITSTATUS(fork_status) == EXIT_SUCCESS;
841} 1075}
842 1076
843bool spawn_swaybg(void) { 1077bool spawn_swaybg(void) {
diff --git a/sway/config/seat.c b/sway/config/seat.c
index 6d5d91ae..f2326189 100644
--- a/sway/config/seat.c
+++ b/sway/config/seat.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <limits.h> 1#include <limits.h>
3#include <stdlib.h> 2#include <stdlib.h>
4#include <string.h> 3#include <string.h>
diff --git a/sway/criteria.c b/sway/criteria.c
index 78ea8b8a..e16b4fa8 100644
--- a/sway/criteria.c
+++ b/sway/criteria.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <stdlib.h> 1#include <stdlib.h>
3#include <stdio.h> 2#include <stdio.h>
4#include <stdbool.h> 3#include <stdbool.h>
diff --git a/sway/desktop/launcher.c b/sway/desktop/launcher.c
index 00a7e38a..28043d19 100644
--- a/sway/desktop/launcher.c
+++ b/sway/desktop/launcher.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <stdlib.h> 1#include <stdlib.h>
3#include <string.h> 2#include <string.h>
4#include <wlr/types/wlr_xdg_activation_v1.h> 3#include <wlr/types/wlr_xdg_activation_v1.h>
@@ -67,6 +66,9 @@ void launcher_ctx_destroy(struct launcher_ctx *ctx) {
67 } 66 }
68 wl_list_remove(&ctx->node_destroy.link); 67 wl_list_remove(&ctx->node_destroy.link);
69 wl_list_remove(&ctx->token_destroy.link); 68 wl_list_remove(&ctx->token_destroy.link);
69 if (ctx->seat) {
70 wl_list_remove(&ctx->seat_destroy.link);
71 }
70 wl_list_remove(&ctx->link); 72 wl_list_remove(&ctx->link);
71 wlr_xdg_activation_token_v1_destroy(ctx->token); 73 wlr_xdg_activation_token_v1_destroy(ctx->token);
72 free(ctx->fallback_name); 74 free(ctx->fallback_name);
@@ -213,6 +215,8 @@ struct launcher_ctx *launcher_ctx_create(struct wlr_xdg_activation_token_v1 *tok
213 ctx->fallback_name = strdup(fallback_name); 215 ctx->fallback_name = strdup(fallback_name);
214 ctx->token = token; 216 ctx->token = token;
215 ctx->node = node; 217 ctx->node = node;
218 // Having surface set means that the focus check in wlroots has passed
219 ctx->had_focused_surface = token->surface != NULL;
216 220
217 ctx->node_destroy.notify = ctx_handle_node_destroy; 221 ctx->node_destroy.notify = ctx_handle_node_destroy;
218 wl_signal_add(&ctx->node->events.destroy, &ctx->node_destroy); 222 wl_signal_add(&ctx->node->events.destroy, &ctx->node_destroy);
@@ -227,6 +231,12 @@ struct launcher_ctx *launcher_ctx_create(struct wlr_xdg_activation_token_v1 *tok
227 return ctx; 231 return ctx;
228} 232}
229 233
234static void launch_ctx_handle_seat_destroy(struct wl_listener *listener, void *data) {
235 struct launcher_ctx *ctx = wl_container_of(listener, ctx, seat_destroy);
236 ctx->seat = NULL;
237 wl_list_remove(&ctx->seat_destroy.link);
238}
239
230// Creates a context with a new token for the internal launcher 240// Creates a context with a new token for the internal launcher
231struct launcher_ctx *launcher_ctx_create_internal(void) { 241struct launcher_ctx *launcher_ctx_create_internal(void) {
232 struct sway_seat *seat = input_manager_current_seat(); 242 struct sway_seat *seat = input_manager_current_seat();
@@ -238,13 +248,15 @@ struct launcher_ctx *launcher_ctx_create_internal(void) {
238 248
239 struct wlr_xdg_activation_token_v1 *token = 249 struct wlr_xdg_activation_token_v1 *token =
240 wlr_xdg_activation_token_v1_create(server.xdg_activation_v1); 250 wlr_xdg_activation_token_v1_create(server.xdg_activation_v1);
241 token->seat = seat->wlr_seat;
242 251
243 struct launcher_ctx *ctx = launcher_ctx_create(token, &ws->node); 252 struct launcher_ctx *ctx = launcher_ctx_create(token, &ws->node);
244 if (!ctx) { 253 if (!ctx) {
245 wlr_xdg_activation_token_v1_destroy(token); 254 wlr_xdg_activation_token_v1_destroy(token);
246 return NULL; 255 return NULL;
247 } 256 }
257 ctx->seat = seat;
258 ctx->seat_destroy.notify = launch_ctx_handle_seat_destroy;
259 wl_signal_add(&seat->wlr_seat->events.destroy, &ctx->seat_destroy);
248 260
249 return ctx; 261 return ctx;
250} 262}
diff --git a/sway/desktop/layer_shell.c b/sway/desktop/layer_shell.c
index c71abce7..6221b7b9 100644
--- a/sway/desktop/layer_shell.c
+++ b/sway/desktop/layer_shell.c
@@ -2,9 +2,12 @@
2#include <stdlib.h> 2#include <stdlib.h>
3#include <string.h> 3#include <string.h>
4#include <wayland-server-core.h> 4#include <wayland-server-core.h>
5#include <wlr/types/wlr_fractional_scale_v1.h>
5#include <wlr/types/wlr_layer_shell_v1.h> 6#include <wlr/types/wlr_layer_shell_v1.h>
6#include <wlr/types/wlr_output.h> 7#include <wlr/types/wlr_output.h>
8#include <wlr/types/wlr_scene.h>
7#include <wlr/types/wlr_subcompositor.h> 9#include <wlr/types/wlr_subcompositor.h>
10#include <wlr/types/wlr_xdg_shell.h>
8#include "log.h" 11#include "log.h"
9#include "sway/scene_descriptor.h" 12#include "sway/scene_descriptor.h"
10#include "sway/desktop/transaction.h" 13#include "sway/desktop/transaction.h"
@@ -16,7 +19,6 @@
16#include "sway/server.h" 19#include "sway/server.h"
17#include "sway/tree/arrange.h" 20#include "sway/tree/arrange.h"
18#include "sway/tree/workspace.h" 21#include "sway/tree/workspace.h"
19#include <wlr/types/wlr_scene.h>
20 22
21struct wlr_layer_surface_v1 *toplevel_layer_surface_from_surface( 23struct wlr_layer_surface_v1 *toplevel_layer_surface_from_surface(
22 struct wlr_surface *surface) { 24 struct wlr_surface *surface) {
@@ -85,6 +87,8 @@ void arrange_layers(struct sway_output *output) {
85 sway_log(SWAY_DEBUG, "Usable area changed, rearranging output"); 87 sway_log(SWAY_DEBUG, "Usable area changed, rearranging output");
86 output->usable_area = usable_area; 88 output->usable_area = usable_area;
87 arrange_output(output); 89 arrange_output(output);
90 } else {
91 arrange_popups(root->layers.popup);
88 } 92 }
89} 93}
90 94
@@ -120,10 +124,21 @@ static struct sway_layer_surface *sway_layer_surface_create(
120 return NULL; 124 return NULL;
121 } 125 }
122 126
127 surface->desc.relative = &scene->tree->node;
128
129 if (!scene_descriptor_assign(&popups->node,
130 SWAY_SCENE_DESC_POPUP, &surface->desc)) {
131 sway_log(SWAY_ERROR, "Failed to allocate a popup scene descriptor");
132 wlr_scene_node_destroy(&popups->node);
133 free(surface);
134 return NULL;
135 }
136
123 surface->tree = scene->tree; 137 surface->tree = scene->tree;
124 surface->scene = scene; 138 surface->scene = scene;
125 surface->layer_surface = scene->layer_surface; 139 surface->layer_surface = scene->layer_surface;
126 surface->popups = popups; 140 surface->popups = popups;
141 surface->layer_surface->data = surface;
127 142
128 return surface; 143 return surface;
129} 144}
@@ -197,6 +212,8 @@ static void handle_node_destroy(struct wl_listener *listener, void *data) {
197 wl_list_remove(&layer->node_destroy.link); 212 wl_list_remove(&layer->node_destroy.link);
198 wl_list_remove(&layer->output_destroy.link); 213 wl_list_remove(&layer->output_destroy.link);
199 214
215 layer->layer_surface->data = NULL;
216
200 free(layer); 217 free(layer);
201} 218}
202 219
@@ -222,10 +239,6 @@ static void handle_surface_commit(struct wl_listener *listener, void *data) {
222 arrange_layers(surface->output); 239 arrange_layers(surface->output);
223 transaction_commit_dirty(); 240 transaction_commit_dirty();
224 } 241 }
225
226 int lx, ly;
227 wlr_scene_node_coords(&surface->scene->tree->node, &lx, &ly);
228 wlr_scene_node_set_position(&surface->popups->node, lx, ly);
229} 242}
230 243
231static void handle_map(struct wl_listener *listener, void *data) { 244static void handle_map(struct wl_listener *listener, void *data) {
@@ -420,6 +433,12 @@ void handle_layer_shell_surface(struct wl_listener *listener, void *data) {
420 433
421 surface->output = output; 434 surface->output = output;
422 435
436 // now that the surface's output is known, we can advertise its scale
437 wlr_fractional_scale_v1_notify_scale(surface->layer_surface->surface,
438 layer_surface->output->scale);
439 wlr_surface_set_preferred_buffer_scale(surface->layer_surface->surface,
440 ceil(layer_surface->output->scale));
441
423 surface->surface_commit.notify = handle_surface_commit; 442 surface->surface_commit.notify = handle_surface_commit;
424 wl_signal_add(&layer_surface->surface->events.commit, 443 wl_signal_add(&layer_surface->surface->events.commit,
425 &surface->surface_commit); 444 &surface->surface_commit);
diff --git a/sway/desktop/output.c b/sway/desktop/output.c
index 9c4baafd..2722e556 100644
--- a/sway/desktop/output.c
+++ b/sway/desktop/output.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <assert.h> 1#include <assert.h>
3#include <stdlib.h> 2#include <stdlib.h>
4#include <strings.h> 3#include <strings.h>
@@ -12,6 +11,8 @@
12#include <wlr/types/wlr_gamma_control_v1.h> 11#include <wlr/types/wlr_gamma_control_v1.h>
13#include <wlr/types/wlr_matrix.h> 12#include <wlr/types/wlr_matrix.h>
14#include <wlr/types/wlr_output_layout.h> 13#include <wlr/types/wlr_output_layout.h>
14#include <wlr/types/wlr_output_management_v1.h>
15#include <wlr/types/wlr_output_power_management_v1.h>
15#include <wlr/types/wlr_output.h> 16#include <wlr/types/wlr_output.h>
16#include <wlr/types/wlr_presentation_time.h> 17#include <wlr/types/wlr_presentation_time.h>
17#include <wlr/types/wlr_compositor.h> 18#include <wlr/types/wlr_compositor.h>
@@ -182,7 +183,15 @@ static void send_frame_done_iterator(struct wlr_scene_buffer *buffer,
182 } 183 }
183} 184}
184 185
185static enum wlr_scale_filter_mode get_scale_filter(struct sway_output *output) { 186static enum wlr_scale_filter_mode get_scale_filter(struct sway_output *output,
187 struct wlr_scene_buffer *buffer) {
188 // if we are scaling down, we should always choose linear
189 if (buffer->dst_width > 0 && buffer->dst_height > 0 && (
190 buffer->dst_width < buffer->buffer_width ||
191 buffer->dst_height < buffer->buffer_height)) {
192 return WLR_SCALE_FILTER_BILINEAR;
193 }
194
186 switch (output->scale_filter) { 195 switch (output->scale_filter) {
187 case SCALE_FILTER_LINEAR: 196 case SCALE_FILTER_LINEAR:
188 return WLR_SCALE_FILTER_BILINEAR; 197 return WLR_SCALE_FILTER_BILINEAR;
@@ -211,7 +220,7 @@ static void output_configure_scene(struct sway_output *output,
211 // hack: don't call the scene setter because that will damage all outputs 220 // hack: don't call the scene setter because that will damage all outputs
212 // We don't want to damage outputs that aren't our current output that 221 // We don't want to damage outputs that aren't our current output that
213 // we're configuring 222 // we're configuring
214 buffer->filter_mode = get_scale_filter(output); 223 buffer->filter_mode = get_scale_filter(output, buffer);
215 224
216 wlr_scene_buffer_set_opacity(buffer, opacity); 225 wlr_scene_buffer_set_opacity(buffer, opacity);
217 } else if (node->type == WLR_SCENE_NODE_TREE) { 226 } else if (node->type == WLR_SCENE_NODE_TREE) {
@@ -512,9 +521,7 @@ void handle_new_output(struct wl_listener *listener, void *data) {
512 sway_session_lock_add_output(server->session_lock.lock, output); 521 sway_session_lock_add_output(server->session_lock.lock, output);
513 } 522 }
514 523
515 struct output_config *oc = find_output_config(output); 524 apply_all_output_configs();
516 apply_output_config(oc, output);
517 free_output_config(oc);
518 525
519 transaction_commit_dirty(); 526 transaction_commit_dirty();
520 527
@@ -543,63 +550,89 @@ void handle_gamma_control_set_gamma(struct wl_listener *listener, void *data) {
543 wlr_output_schedule_frame(output->wlr_output); 550 wlr_output_schedule_frame(output->wlr_output);
544} 551}
545 552
553static struct output_config *output_config_for_config_head(
554 struct wlr_output_configuration_head_v1 *config_head,
555 struct sway_output *output) {
556 struct output_config *oc = new_output_config(output->wlr_output->name);
557 oc->enabled = config_head->state.enabled;
558 if (!oc->enabled) {
559 return oc;
560 }
561
562 if (config_head->state.mode != NULL) {
563 struct wlr_output_mode *mode = config_head->state.mode;
564 oc->width = mode->width;
565 oc->height = mode->height;
566 oc->refresh_rate = mode->refresh / 1000.f;
567 } else {
568 oc->width = config_head->state.custom_mode.width;
569 oc->height = config_head->state.custom_mode.height;
570 oc->refresh_rate =
571 config_head->state.custom_mode.refresh / 1000.f;
572 }
573 oc->x = config_head->state.x;
574 oc->y = config_head->state.y;
575 oc->transform = config_head->state.transform;
576 oc->scale = config_head->state.scale;
577 oc->adaptive_sync = config_head->state.adaptive_sync_enabled;
578 return oc;
579}
580
546static void output_manager_apply(struct sway_server *server, 581static void output_manager_apply(struct sway_server *server,
547 struct wlr_output_configuration_v1 *config, bool test_only) { 582 struct wlr_output_configuration_v1 *config, bool test_only) {
548 // TODO: perform atomic tests on the whole backend atomically 583 size_t configs_len = wl_list_length(&root->all_outputs);
549 584 struct matched_output_config *configs = calloc(configs_len, sizeof(*configs));
550 struct wlr_output_configuration_head_v1 *config_head; 585 if (!configs) {
551 // First disable outputs we need to disable 586 return;
552 bool ok = true; 587 }
553 wl_list_for_each(config_head, &config->heads, link) { 588
554 struct wlr_output *wlr_output = config_head->state.output; 589 int config_idx = 0;
555 struct sway_output *output = wlr_output->data; 590 struct sway_output *sway_output;
556 if (!output->enabled || config_head->state.enabled) { 591 wl_list_for_each(sway_output, &root->all_outputs, link) {
592 if (sway_output == root->fallback_output) {
593 configs_len--;
557 continue; 594 continue;
558 } 595 }
559 struct output_config *oc = new_output_config(output->wlr_output->name);
560 oc->enabled = false;
561 596
562 if (test_only) { 597 struct matched_output_config *cfg = &configs[config_idx++];
563 ok &= test_output_config(oc, output); 598 cfg->output = sway_output;
564 } else { 599
565 oc = store_output_config(oc); 600 struct wlr_output_configuration_head_v1 *config_head;
566 ok &= apply_output_config(oc, output); 601 wl_list_for_each(config_head, &config->heads, link) {
602 if (config_head->state.output == sway_output->wlr_output) {
603 cfg->config = output_config_for_config_head(config_head, sway_output);
604 break;
605 }
606 }
607 if (!cfg->config) {
608 cfg->config = find_output_config(sway_output);
567 } 609 }
568 } 610 }
569 611
570 // Then enable outputs that need to 612 sort_output_configs_by_priority(configs, configs_len);
571 wl_list_for_each(config_head, &config->heads, link) { 613 bool ok = apply_output_configs(configs, configs_len, test_only, false);
572 struct wlr_output *wlr_output = config_head->state.output; 614 for (size_t idx = 0; idx < configs_len; idx++) {
573 struct sway_output *output = wlr_output->data; 615 struct matched_output_config *cfg = &configs[idx];
574 if (!config_head->state.enabled) { 616
575 continue; 617 // Only store new configs for successful non-test commits. Old configs,
576 } 618 // test-only and failed commits just get freed.
577 struct output_config *oc = new_output_config(output->wlr_output->name); 619 bool store_config = false;
578 oc->enabled = true; 620 if (!test_only && ok) {
579 if (config_head->state.mode != NULL) { 621 struct wlr_output_configuration_head_v1 *config_head;
580 struct wlr_output_mode *mode = config_head->state.mode; 622 wl_list_for_each(config_head, &config->heads, link) {
581 oc->width = mode->width; 623 if (config_head->state.output == cfg->output->wlr_output) {
582 oc->height = mode->height; 624 store_config = true;
583 oc->refresh_rate = mode->refresh / 1000.f; 625 break;
584 } else { 626 }
585 oc->width = config_head->state.custom_mode.width; 627 }
586 oc->height = config_head->state.custom_mode.height;
587 oc->refresh_rate =
588 config_head->state.custom_mode.refresh / 1000.f;
589 } 628 }
590 oc->x = config_head->state.x; 629 if (store_config) {
591 oc->y = config_head->state.y; 630 store_output_config(cfg->config);
592 oc->transform = config_head->state.transform;
593 oc->scale = config_head->state.scale;
594 oc->adaptive_sync = config_head->state.adaptive_sync_enabled;
595
596 if (test_only) {
597 ok &= test_output_config(oc, output);
598 } else { 631 } else {
599 oc = store_output_config(oc); 632 free_output_config(cfg->config);
600 ok &= apply_output_config(oc, output);
601 } 633 }
602 } 634 }
635 free(configs);
603 636
604 if (ok) { 637 if (ok) {
605 wlr_output_configuration_v1_send_succeeded(config); 638 wlr_output_configuration_v1_send_succeeded(config);
@@ -643,6 +676,6 @@ void handle_output_power_manager_set_mode(struct wl_listener *listener,
643 oc->power = 1; 676 oc->power = 1;
644 break; 677 break;
645 } 678 }
646 oc = store_output_config(oc); 679 store_output_config(oc);
647 apply_output_config(oc, output); 680 apply_all_output_configs();
648} 681}
diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c
index acc3e3f9..042141ab 100644
--- a/sway/desktop/transaction.c
+++ b/sway/desktop/transaction.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <stdbool.h> 1#include <stdbool.h>
3#include <stdlib.h> 2#include <stdlib.h>
4#include <string.h> 3#include <string.h>
@@ -606,21 +605,15 @@ static void arrange_output(struct sway_output *output, int width, int height) {
606 } 605 }
607} 606}
608 607
609static void arrange_popup(struct wlr_scene_tree *popup) { 608void arrange_popups(struct wlr_scene_tree *popups) {
610 struct wlr_scene_node *node; 609 struct wlr_scene_node *node;
611 wl_list_for_each(node, &popup->children, link) { 610 wl_list_for_each(node, &popups->children, link) {
612 struct sway_xdg_popup *popup = scene_descriptor_try_get(node, 611 struct sway_popup_desc *popup = scene_descriptor_try_get(node,
613 SWAY_SCENE_DESC_POPUP); 612 SWAY_SCENE_DESC_POPUP);
614 613
615 // the popup layer may have popups from layer_shell surfaces, in this 614 int lx, ly;
616 // case those don't have a scene descriptor, so lets skip those here. 615 wlr_scene_node_coords(popup->relative, &lx, &ly);
617 if (popup) { 616 wlr_scene_node_set_position(node, lx, ly);
618 struct wlr_scene_tree *tree = popup->view->content_tree;
619
620 int lx, ly;
621 wlr_scene_node_coords(&tree->node, &lx, &ly);
622 wlr_scene_node_set_position(&popup->scene_tree->node, lx, ly);
623 }
624 } 617 }
625} 618}
626 619
@@ -679,7 +672,7 @@ static void arrange_root(struct sway_root *root) {
679 } 672 }
680 } 673 }
681 674
682 arrange_popup(root->layers.popup); 675 arrange_popups(root->layers.popup);
683} 676}
684 677
685/** 678/**
diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c
index 7cdd97c8..7c417891 100644
--- a/sway/desktop/xdg_shell.c
+++ b/sway/desktop/xdg_shell.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 199309L
2#include <float.h> 1#include <float.h>
3#include <stdbool.h> 2#include <stdbool.h>
4#include <stdlib.h> 3#include <stdlib.h>
@@ -36,6 +35,7 @@ static void popup_handle_destroy(struct wl_listener *listener, void *data) {
36 wl_list_remove(&popup->new_popup.link); 35 wl_list_remove(&popup->new_popup.link);
37 wl_list_remove(&popup->destroy.link); 36 wl_list_remove(&popup->destroy.link);
38 wl_list_remove(&popup->surface_commit.link); 37 wl_list_remove(&popup->surface_commit.link);
38 wl_list_remove(&popup->reposition.link);
39 wlr_scene_node_destroy(&popup->scene_tree->node); 39 wlr_scene_node_destroy(&popup->scene_tree->node);
40 free(popup); 40 free(popup);
41} 41}
@@ -71,6 +71,11 @@ static void popup_handle_surface_commit(struct wl_listener *listener, void *data
71 } 71 }
72} 72}
73 73
74static void popup_handle_reposition(struct wl_listener *listener, void *data) {
75 struct sway_xdg_popup *popup = wl_container_of(listener, popup, reposition);
76 popup_unconstrain(popup);
77}
78
74static struct sway_xdg_popup *popup_create(struct wlr_xdg_popup *wlr_popup, 79static struct sway_xdg_popup *popup_create(struct wlr_xdg_popup *wlr_popup,
75 struct sway_view *view, struct wlr_scene_tree *parent) { 80 struct sway_view *view, struct wlr_scene_tree *parent) {
76 struct wlr_xdg_surface *xdg_surface = wlr_popup->base; 81 struct wlr_xdg_surface *xdg_surface = wlr_popup->base;
@@ -97,8 +102,11 @@ static struct sway_xdg_popup *popup_create(struct wlr_xdg_popup *wlr_popup,
97 return NULL; 102 return NULL;
98 } 103 }
99 104
105 popup->desc.relative = &view->content_tree->node;
106 popup->desc.view = view;
107
100 if (!scene_descriptor_assign(&popup->scene_tree->node, 108 if (!scene_descriptor_assign(&popup->scene_tree->node,
101 SWAY_SCENE_DESC_POPUP, popup)) { 109 SWAY_SCENE_DESC_POPUP, &popup->desc)) {
102 sway_log(SWAY_ERROR, "Failed to allocate a popup scene descriptor"); 110 sway_log(SWAY_ERROR, "Failed to allocate a popup scene descriptor");
103 wlr_scene_node_destroy(&popup->scene_tree->node); 111 wlr_scene_node_destroy(&popup->scene_tree->node);
104 free(popup); 112 free(popup);
@@ -114,6 +122,8 @@ static struct sway_xdg_popup *popup_create(struct wlr_xdg_popup *wlr_popup,
114 popup->surface_commit.notify = popup_handle_surface_commit; 122 popup->surface_commit.notify = popup_handle_surface_commit;
115 wl_signal_add(&xdg_surface->events.new_popup, &popup->new_popup); 123 wl_signal_add(&xdg_surface->events.new_popup, &popup->new_popup);
116 popup->new_popup.notify = popup_handle_new_popup; 124 popup->new_popup.notify = popup_handle_new_popup;
125 wl_signal_add(&wlr_popup->events.reposition, &popup->reposition);
126 popup->reposition.notify = popup_handle_reposition;
117 wl_signal_add(&wlr_popup->events.destroy, &popup->destroy); 127 wl_signal_add(&wlr_popup->events.destroy, &popup->destroy);
118 popup->destroy.notify = popup_handle_destroy; 128 popup->destroy.notify = popup_handle_destroy;
119 129
@@ -279,6 +289,7 @@ static void handle_commit(struct wl_listener *listener, void *data) {
279 } 289 }
280 // XXX: https://github.com/swaywm/sway/issues/2176 290 // XXX: https://github.com/swaywm/sway/issues/2176
281 wlr_xdg_surface_schedule_configure(xdg_surface); 291 wlr_xdg_surface_schedule_configure(xdg_surface);
292 // TODO: wlr_xdg_toplevel_set_bounds()
282 return; 293 return;
283 } 294 }
284 295
@@ -337,6 +348,7 @@ static void handle_set_app_id(struct wl_listener *listener, void *data) {
337 struct sway_xdg_shell_view *xdg_shell_view = 348 struct sway_xdg_shell_view *xdg_shell_view =
338 wl_container_of(listener, xdg_shell_view, set_app_id); 349 wl_container_of(listener, xdg_shell_view, set_app_id);
339 struct sway_view *view = &xdg_shell_view->view; 350 struct sway_view *view = &xdg_shell_view->view;
351 view_update_app_id(view);
340 view_execute_criteria(view); 352 view_execute_criteria(view);
341} 353}
342 354
@@ -563,4 +575,7 @@ void handle_xdg_shell_toplevel(struct wl_listener *listener, void *data) {
563 wlr_scene_xdg_surface_create(xdg_shell_view->view.content_tree, xdg_toplevel->base); 575 wlr_scene_xdg_surface_create(xdg_shell_view->view.content_tree, xdg_toplevel->base);
564 576
565 xdg_toplevel->base->data = xdg_shell_view; 577 xdg_toplevel->base->data = xdg_shell_view;
578
579 wlr_xdg_toplevel_set_wm_capabilities(xdg_toplevel,
580 XDG_TOPLEVEL_WM_CAPABILITIES_FULLSCREEN);
566} 581}
diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c
index 9f3f4d5f..270cf08f 100644
--- a/sway/desktop/xwayland.c
+++ b/sway/desktop/xwayland.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 199309L
2#include <float.h> 1#include <float.h>
3#include <stdbool.h> 2#include <stdbool.h>
4#include <stdlib.h> 3#include <stdlib.h>
diff --git a/sway/input/cursor.c b/sway/input/cursor.c
index 404c1eed..3d04826c 100644
--- a/sway/input/cursor.c
+++ b/sway/input/cursor.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <assert.h> 1#include <assert.h>
3#include <math.h> 2#include <math.h>
4#include <libevdev/libevdev.h> 3#include <libevdev/libevdev.h>
@@ -9,6 +8,7 @@
9#include <wlr/types/wlr_cursor.h> 8#include <wlr/types/wlr_cursor.h>
10#include <wlr/types/wlr_cursor_shape_v1.h> 9#include <wlr/types/wlr_cursor_shape_v1.h>
11#include <wlr/types/wlr_pointer.h> 10#include <wlr/types/wlr_pointer.h>
11#include <wlr/types/wlr_relative_pointer_v1.h>
12#include <wlr/types/wlr_touch.h> 12#include <wlr/types/wlr_touch.h>
13#include <wlr/types/wlr_tablet_v2.h> 13#include <wlr/types/wlr_tablet_v2.h>
14#include <wlr/types/wlr_tablet_pad.h> 14#include <wlr/types/wlr_tablet_pad.h>
@@ -90,9 +90,9 @@ struct sway_node *node_at_coords(
90 } 90 }
91 91
92 if (!con) { 92 if (!con) {
93 struct sway_xdg_popup *popup = 93 struct sway_popup_desc *popup =
94 scene_descriptor_try_get(current, SWAY_SCENE_DESC_POPUP); 94 scene_descriptor_try_get(current, SWAY_SCENE_DESC_POPUP);
95 if (popup) { 95 if (popup && popup->view) {
96 con = popup->view->container; 96 con = popup->view->container;
97 } 97 }
98 } 98 }
@@ -243,7 +243,7 @@ static enum sway_input_idle_source idle_source_from_device(
243 return IDLE_SOURCE_POINTER; 243 return IDLE_SOURCE_POINTER;
244 case WLR_INPUT_DEVICE_TOUCH: 244 case WLR_INPUT_DEVICE_TOUCH:
245 return IDLE_SOURCE_TOUCH; 245 return IDLE_SOURCE_TOUCH;
246 case WLR_INPUT_DEVICE_TABLET_TOOL: 246 case WLR_INPUT_DEVICE_TABLET:
247 return IDLE_SOURCE_TABLET_TOOL; 247 return IDLE_SOURCE_TABLET_TOOL;
248 case WLR_INPUT_DEVICE_TABLET_PAD: 248 case WLR_INPUT_DEVICE_TABLET_PAD:
249 return IDLE_SOURCE_TABLET_PAD; 249 return IDLE_SOURCE_TABLET_PAD;
@@ -356,7 +356,7 @@ static void handle_pointer_motion_absolute(
356 356
357void dispatch_cursor_button(struct sway_cursor *cursor, 357void dispatch_cursor_button(struct sway_cursor *cursor,
358 struct wlr_input_device *device, uint32_t time_msec, uint32_t button, 358 struct wlr_input_device *device, uint32_t time_msec, uint32_t button,
359 enum wlr_button_state state) { 359 enum wl_pointer_button_state state) {
360 if (time_msec == 0) { 360 if (time_msec == 0) {
361 time_msec = get_current_time_msec(); 361 time_msec = get_current_time_msec();
362 } 362 }
@@ -368,7 +368,7 @@ static void handle_pointer_button(struct wl_listener *listener, void *data) {
368 struct sway_cursor *cursor = wl_container_of(listener, cursor, button); 368 struct sway_cursor *cursor = wl_container_of(listener, cursor, button);
369 struct wlr_pointer_button_event *event = data; 369 struct wlr_pointer_button_event *event = data;
370 370
371 if (event->state == WLR_BUTTON_PRESSED) { 371 if (event->state == WL_POINTER_BUTTON_STATE_PRESSED) {
372 cursor->pressed_button_count++; 372 cursor->pressed_button_count++;
373 } else { 373 } else {
374 if (cursor->pressed_button_count > 0) { 374 if (cursor->pressed_button_count > 0) {
@@ -430,7 +430,7 @@ static void handle_touch_up(struct wl_listener *listener, void *data) {
430 if (cursor->pointer_touch_id == cursor->seat->touch_id) { 430 if (cursor->pointer_touch_id == cursor->seat->touch_id) {
431 cursor->pointer_touch_up = true; 431 cursor->pointer_touch_up = true;
432 dispatch_cursor_button(cursor, &event->touch->base, 432 dispatch_cursor_button(cursor, &event->touch->base,
433 event->time_msec, BTN_LEFT, WLR_BUTTON_RELEASED); 433 event->time_msec, BTN_LEFT, WL_POINTER_BUTTON_STATE_RELEASED);
434 } 434 }
435 } else { 435 } else {
436 seatop_touch_up(seat, event); 436 seatop_touch_up(seat, event);
@@ -448,7 +448,7 @@ static void handle_touch_cancel(struct wl_listener *listener, void *data) {
448 if (cursor->pointer_touch_id == cursor->seat->touch_id) { 448 if (cursor->pointer_touch_id == cursor->seat->touch_id) {
449 cursor->pointer_touch_up = true; 449 cursor->pointer_touch_up = true;
450 dispatch_cursor_button(cursor, &event->touch->base, 450 dispatch_cursor_button(cursor, &event->touch->base,
451 event->time_msec, BTN_LEFT, WLR_BUTTON_RELEASED); 451 event->time_msec, BTN_LEFT, WL_POINTER_BUTTON_STATE_RELEASED);
452 } 452 }
453 } else { 453 } else {
454 seatop_touch_cancel(seat, event); 454 seatop_touch_cancel(seat, event);
@@ -518,7 +518,7 @@ static void apply_mapping_from_region(struct wlr_input_device *device,
518 double x1 = region->x1, x2 = region->x2; 518 double x1 = region->x1, x2 = region->x2;
519 double y1 = region->y1, y2 = region->y2; 519 double y1 = region->y1, y2 = region->y2;
520 520
521 if (region->mm && device->type == WLR_INPUT_DEVICE_TABLET_TOOL) { 521 if (region->mm && device->type == WLR_INPUT_DEVICE_TABLET) {
522 struct wlr_tablet *tablet = wlr_tablet_from_input_device(device); 522 struct wlr_tablet *tablet = wlr_tablet_from_input_device(device);
523 if (tablet->width_mm == 0 || tablet->height_mm == 0) { 523 if (tablet->width_mm == 0 || tablet->height_mm == 0) {
524 return; 524 return;
@@ -661,7 +661,7 @@ static void handle_tool_tip(struct wl_listener *listener, void *data) {
661 event->state == WLR_TABLET_TOOL_TIP_UP) { 661 event->state == WLR_TABLET_TOOL_TIP_UP) {
662 cursor->simulating_pointer_from_tool_tip = false; 662 cursor->simulating_pointer_from_tool_tip = false;
663 dispatch_cursor_button(cursor, &event->tablet->base, event->time_msec, 663 dispatch_cursor_button(cursor, &event->tablet->base, event->time_msec,
664 BTN_LEFT, WLR_BUTTON_RELEASED); 664 BTN_LEFT, WL_POINTER_BUTTON_STATE_RELEASED);
665 wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); 665 wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat);
666 } else if (!surface || !wlr_surface_accepts_tablet_v2(tablet_v2, surface)) { 666 } else if (!surface || !wlr_surface_accepts_tablet_v2(tablet_v2, surface)) {
667 // If we started holding the tool tip down on a surface that accepts 667 // If we started holding the tool tip down on a surface that accepts
@@ -673,7 +673,7 @@ static void handle_tool_tip(struct wl_listener *listener, void *data) {
673 } else { 673 } else {
674 cursor->simulating_pointer_from_tool_tip = true; 674 cursor->simulating_pointer_from_tool_tip = true;
675 dispatch_cursor_button(cursor, &event->tablet->base, 675 dispatch_cursor_button(cursor, &event->tablet->base,
676 event->time_msec, BTN_LEFT, WLR_BUTTON_PRESSED); 676 event->time_msec, BTN_LEFT, WL_POINTER_BUTTON_STATE_PRESSED);
677 wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); 677 wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat);
678 } 678 }
679 } else { 679 } else {
@@ -776,13 +776,13 @@ static void handle_tool_button(struct wl_listener *listener, void *data) {
776 case WLR_BUTTON_PRESSED: 776 case WLR_BUTTON_PRESSED:
777 if (cursor->tool_buttons == 0) { 777 if (cursor->tool_buttons == 0) {
778 dispatch_cursor_button(cursor, &event->tablet->base, 778 dispatch_cursor_button(cursor, &event->tablet->base,
779 event->time_msec, BTN_RIGHT, event->state); 779 event->time_msec, BTN_RIGHT, WL_POINTER_BUTTON_STATE_PRESSED);
780 } 780 }
781 break; 781 break;
782 case WLR_BUTTON_RELEASED: 782 case WLR_BUTTON_RELEASED:
783 if (cursor->tool_buttons <= 1) { 783 if (cursor->tool_buttons <= 1) {
784 dispatch_cursor_button(cursor, &event->tablet->base, 784 dispatch_cursor_button(cursor, &event->tablet->base,
785 event->time_msec, BTN_RIGHT, event->state); 785 event->time_msec, BTN_RIGHT, WL_POINTER_BUTTON_STATE_RELEASED);
786 } 786 }
787 break; 787 break;
788 } 788 }
diff --git a/sway/input/input-manager.c b/sway/input/input-manager.c
index c1bbdde0..248ca34e 100644
--- a/sway/input/input-manager.c
+++ b/sway/input/input-manager.c
@@ -1,9 +1,10 @@
1#define _POSIX_C_SOURCE 200809L
2#include <ctype.h> 1#include <ctype.h>
3#include <stdio.h> 2#include <stdio.h>
4#include <string.h> 3#include <string.h>
5#include <math.h> 4#include <math.h>
5#include <assert.h>
6#include <wlr/config.h> 6#include <wlr/config.h>
7#include <wlr/backend/libinput.h>
7#include <wlr/types/wlr_cursor.h> 8#include <wlr/types/wlr_cursor.h>
8#include <wlr/types/wlr_keyboard_group.h> 9#include <wlr/types/wlr_keyboard_group.h>
9#include <wlr/types/wlr_virtual_keyboard_v1.h> 10#include <wlr/types/wlr_virtual_keyboard_v1.h>
@@ -66,8 +67,15 @@ struct sway_seat *input_manager_sway_seat_from_wlr_seat(struct wlr_seat *wlr_sea
66} 67}
67 68
68char *input_device_get_identifier(struct wlr_input_device *device) { 69char *input_device_get_identifier(struct wlr_input_device *device) {
69 int vendor = device->vendor; 70 int vendor = 0, product = 0;
70 int product = device->product; 71#if WLR_HAS_LIBINPUT_BACKEND
72 if (wlr_input_device_is_libinput(device)) {
73 struct libinput_device *libinput_dev = wlr_libinput_get_device_handle(device);
74 vendor = libinput_device_get_id_vendor(libinput_dev);
75 product = libinput_device_get_id_product(libinput_dev);
76 }
77#endif
78
71 char *name = strdup(device->name ? device->name : ""); 79 char *name = strdup(device->name ? device->name : "");
72 strip_whitespace(name); 80 strip_whitespace(name);
73 81
@@ -112,7 +120,7 @@ const char *input_device_get_type(struct sway_input_device *device) {
112 return "keyboard"; 120 return "keyboard";
113 case WLR_INPUT_DEVICE_TOUCH: 121 case WLR_INPUT_DEVICE_TOUCH:
114 return "touch"; 122 return "touch";
115 case WLR_INPUT_DEVICE_TABLET_TOOL: 123 case WLR_INPUT_DEVICE_TABLET:
116 return "tablet_tool"; 124 return "tablet_tool";
117 case WLR_INPUT_DEVICE_TABLET_PAD: 125 case WLR_INPUT_DEVICE_TABLET_PAD:
118 return "tablet_pad"; 126 return "tablet_pad";
@@ -425,6 +433,20 @@ void handle_virtual_pointer(struct wl_listener *listener, void *data) {
425 } 433 }
426} 434}
427 435
436static void handle_transient_seat_manager_create_seat(
437 struct wl_listener *listener, void *data) {
438 struct wlr_transient_seat_v1 *transient_seat = data;
439 static uint64_t i;
440 char name[256];
441 snprintf(name, sizeof(name), "transient-%"PRIx64, i++);
442 struct sway_seat *seat = seat_create(name);
443 if (seat && seat->wlr_seat) {
444 wlr_transient_seat_v1_ready(transient_seat, seat->wlr_seat);
445 } else {
446 wlr_transient_seat_v1_deny(transient_seat);
447 }
448}
449
428struct sway_input_manager *input_manager_create(struct sway_server *server) { 450struct sway_input_manager *input_manager_create(struct sway_server *server) {
429 struct sway_input_manager *input = 451 struct sway_input_manager *input =
430 calloc(1, sizeof(struct sway_input_manager)); 452 calloc(1, sizeof(struct sway_input_manager));
@@ -460,6 +482,15 @@ struct sway_input_manager *input_manager_create(struct sway_server *server) {
460 482
461 input->pointer_gestures = wlr_pointer_gestures_v1_create(server->wl_display); 483 input->pointer_gestures = wlr_pointer_gestures_v1_create(server->wl_display);
462 484
485 input->transient_seat_manager =
486 wlr_transient_seat_manager_v1_create(server->wl_display);
487 assert(input->transient_seat_manager);
488
489 input->transient_seat_create.notify =
490 handle_transient_seat_manager_create_seat;
491 wl_signal_add(&input->transient_seat_manager->events.create_seat,
492 &input->transient_seat_create);
493
463 return input; 494 return input;
464} 495}
465 496
diff --git a/sway/input/keyboard.c b/sway/input/keyboard.c
index b97f0152..f74d0658 100644
--- a/sway/input/keyboard.c
+++ b/sway/input/keyboard.c
@@ -32,6 +32,7 @@ static struct modifier_key {
32 { XKB_MOD_NAME_NUM, WLR_MODIFIER_MOD2 }, 32 { XKB_MOD_NAME_NUM, WLR_MODIFIER_MOD2 },
33 { "Mod3", WLR_MODIFIER_MOD3 }, 33 { "Mod3", WLR_MODIFIER_MOD3 },
34 { XKB_MOD_NAME_LOGO, WLR_MODIFIER_LOGO }, 34 { XKB_MOD_NAME_LOGO, WLR_MODIFIER_LOGO },
35 { "Super", WLR_MODIFIER_LOGO },
35 { "Mod5", WLR_MODIFIER_MOD5 }, 36 { "Mod5", WLR_MODIFIER_MOD5 },
36}; 37};
37 38
diff --git a/sway/input/seat.c b/sway/input/seat.c
index 75fea484..0c5672bc 100644
--- a/sway/input/seat.c
+++ b/sway/input/seat.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <assert.h> 1#include <assert.h>
3#include <linux/input-event-codes.h> 2#include <linux/input-event-codes.h>
4#include <string.h> 3#include <string.h>
@@ -68,6 +67,12 @@ static void seat_node_destroy(struct sway_seat_node *seat_node) {
68} 67}
69 68
70void seat_destroy(struct sway_seat *seat) { 69void seat_destroy(struct sway_seat *seat) {
70 wlr_seat_destroy(seat->wlr_seat);
71}
72
73static void handle_seat_destroy(struct wl_listener *listener, void *data) {
74 struct sway_seat *seat = wl_container_of(listener, seat, destroy);
75
71 if (seat == config->handler_context.seat) { 76 if (seat == config->handler_context.seat) {
72 config->handler_context.seat = input_manager_get_default_seat(); 77 config->handler_context.seat = input_manager_get_default_seat();
73 } 78 }
@@ -88,7 +93,7 @@ void seat_destroy(struct sway_seat *seat) {
88 wl_list_remove(&seat->request_set_selection.link); 93 wl_list_remove(&seat->request_set_selection.link);
89 wl_list_remove(&seat->request_set_primary_selection.link); 94 wl_list_remove(&seat->request_set_primary_selection.link);
90 wl_list_remove(&seat->link); 95 wl_list_remove(&seat->link);
91 wlr_seat_destroy(seat->wlr_seat); 96 wl_list_remove(&seat->destroy.link);
92 for (int i = 0; i < seat->deferred_bindings->length; i++) { 97 for (int i = 0; i < seat->deferred_bindings->length; i++) {
93 free_sway_binding(seat->deferred_bindings->items[i]); 98 free_sway_binding(seat->deferred_bindings->items[i]);
94 } 99 }
@@ -535,6 +540,9 @@ struct sway_seat *seat_create(const char *seat_name) {
535 return NULL; 540 return NULL;
536 } 541 }
537 542
543 seat->destroy.notify = handle_seat_destroy;
544 wl_signal_add(&seat->wlr_seat->events.destroy, &seat->destroy);
545
538 seat->idle_inhibit_sources = seat->idle_wake_sources = 546 seat->idle_inhibit_sources = seat->idle_wake_sources =
539 IDLE_SOURCE_KEYBOARD | 547 IDLE_SOURCE_KEYBOARD |
540 IDLE_SOURCE_POINTER | 548 IDLE_SOURCE_POINTER |
@@ -608,7 +616,7 @@ static void seat_update_capabilities(struct sway_seat *seat) {
608 case WLR_INPUT_DEVICE_TOUCH: 616 case WLR_INPUT_DEVICE_TOUCH:
609 caps |= WL_SEAT_CAPABILITY_TOUCH; 617 caps |= WL_SEAT_CAPABILITY_TOUCH;
610 break; 618 break;
611 case WLR_INPUT_DEVICE_TABLET_TOOL: 619 case WLR_INPUT_DEVICE_TABLET:
612 caps |= WL_SEAT_CAPABILITY_POINTER; 620 caps |= WL_SEAT_CAPABILITY_POINTER;
613 break; 621 break;
614 case WLR_INPUT_DEVICE_SWITCH: 622 case WLR_INPUT_DEVICE_SWITCH:
@@ -666,7 +674,7 @@ static const char *get_builtin_output_name(void) {
666static bool is_touch_or_tablet_tool(struct sway_seat_device *seat_device) { 674static bool is_touch_or_tablet_tool(struct sway_seat_device *seat_device) {
667 switch (seat_device->input_device->wlr_device->type) { 675 switch (seat_device->input_device->wlr_device->type) {
668 case WLR_INPUT_DEVICE_TOUCH: 676 case WLR_INPUT_DEVICE_TOUCH:
669 case WLR_INPUT_DEVICE_TABLET_TOOL: 677 case WLR_INPUT_DEVICE_TABLET:
670 return true; 678 return true;
671 default: 679 default:
672 return false; 680 return false;
@@ -681,7 +689,7 @@ static void seat_apply_input_mapping(struct sway_seat *seat,
681 switch (sway_device->input_device->wlr_device->type) { 689 switch (sway_device->input_device->wlr_device->type) {
682 case WLR_INPUT_DEVICE_POINTER: 690 case WLR_INPUT_DEVICE_POINTER:
683 case WLR_INPUT_DEVICE_TOUCH: 691 case WLR_INPUT_DEVICE_TOUCH:
684 case WLR_INPUT_DEVICE_TABLET_TOOL: 692 case WLR_INPUT_DEVICE_TABLET:
685 break; 693 break;
686 default: 694 default:
687 return; // these devices don't support mappings 695 return; // these devices don't support mappings
@@ -874,7 +882,7 @@ void seat_configure_device(struct sway_seat *seat,
874 case WLR_INPUT_DEVICE_TOUCH: 882 case WLR_INPUT_DEVICE_TOUCH:
875 seat_configure_touch(seat, seat_device); 883 seat_configure_touch(seat, seat_device);
876 break; 884 break;
877 case WLR_INPUT_DEVICE_TABLET_TOOL: 885 case WLR_INPUT_DEVICE_TABLET:
878 seat_configure_tablet_tool(seat, seat_device); 886 seat_configure_tablet_tool(seat, seat_device);
879 break; 887 break;
880 case WLR_INPUT_DEVICE_TABLET_PAD: 888 case WLR_INPUT_DEVICE_TABLET_PAD:
@@ -913,7 +921,7 @@ void seat_reset_device(struct sway_seat *seat,
913 case WLR_INPUT_DEVICE_TOUCH: 921 case WLR_INPUT_DEVICE_TOUCH:
914 seat_reset_input_config(seat, seat_device); 922 seat_reset_input_config(seat, seat_device);
915 break; 923 break;
916 case WLR_INPUT_DEVICE_TABLET_TOOL: 924 case WLR_INPUT_DEVICE_TABLET:
917 seat_reset_input_config(seat, seat_device); 925 seat_reset_input_config(seat, seat_device);
918 break; 926 break;
919 case WLR_INPUT_DEVICE_TABLET_PAD: 927 case WLR_INPUT_DEVICE_TABLET_PAD:
@@ -1521,7 +1529,7 @@ struct seat_config *seat_get_config_by_name(const char *name) {
1521} 1529}
1522 1530
1523void seat_pointer_notify_button(struct sway_seat *seat, uint32_t time_msec, 1531void seat_pointer_notify_button(struct sway_seat *seat, uint32_t time_msec,
1524 uint32_t button, enum wlr_button_state state) { 1532 uint32_t button, enum wl_pointer_button_state state) {
1525 seat->last_button_serial = wlr_seat_pointer_notify_button(seat->wlr_seat, 1533 seat->last_button_serial = wlr_seat_pointer_notify_button(seat->wlr_seat,
1526 time_msec, button, state); 1534 time_msec, button, state);
1527} 1535}
@@ -1558,7 +1566,7 @@ void seatop_unref(struct sway_seat *seat, struct sway_container *con) {
1558 1566
1559void seatop_button(struct sway_seat *seat, uint32_t time_msec, 1567void seatop_button(struct sway_seat *seat, uint32_t time_msec,
1560 struct wlr_input_device *device, uint32_t button, 1568 struct wlr_input_device *device, uint32_t button,
1561 enum wlr_button_state state) { 1569 enum wl_pointer_button_state state) {
1562 if (seat->seatop_impl->button) { 1570 if (seat->seatop_impl->button) {
1563 seat->seatop_impl->button(seat, time_msec, device, button, state); 1571 seat->seatop_impl->button(seat, time_msec, device, button, state);
1564 } 1572 }
diff --git a/sway/input/seatop_default.c b/sway/input/seatop_default.c
index c56330fd..0c6f7c5e 100644
--- a/sway/input/seatop_default.c
+++ b/sway/input/seatop_default.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <float.h> 1#include <float.h>
3#include <libevdev/libevdev.h> 2#include <libevdev/libevdev.h>
4#include <wlr/types/wlr_cursor.h> 3#include <wlr/types/wlr_cursor.h>
@@ -291,7 +290,7 @@ static void handle_tablet_tool_tip(struct sway_seat *seat,
291 290
292static bool trigger_pointer_button_binding(struct sway_seat *seat, 291static bool trigger_pointer_button_binding(struct sway_seat *seat,
293 struct wlr_input_device *device, uint32_t button, 292 struct wlr_input_device *device, uint32_t button,
294 enum wlr_button_state state, uint32_t modifiers, 293 enum wl_pointer_button_state state, uint32_t modifiers,
295 bool on_titlebar, bool on_border, bool on_contents, bool on_workspace) { 294 bool on_titlebar, bool on_border, bool on_contents, bool on_workspace) {
296 // We can reach this for non-pointer devices if we're currently emulating 295 // We can reach this for non-pointer devices if we're currently emulating
297 // pointer input for one. Emulated input should not trigger bindings. The 296 // pointer input for one. Emulated input should not trigger bindings. The
@@ -305,7 +304,7 @@ static bool trigger_pointer_button_binding(struct sway_seat *seat,
305 char *device_identifier = device ? input_device_get_identifier(device) 304 char *device_identifier = device ? input_device_get_identifier(device)
306 : strdup("*"); 305 : strdup("*");
307 struct sway_binding *binding = NULL; 306 struct sway_binding *binding = NULL;
308 if (state == WLR_BUTTON_PRESSED) { 307 if (state == WL_POINTER_BUTTON_STATE_PRESSED) {
309 state_add_button(e, button); 308 state_add_button(e, button);
310 binding = get_active_mouse_binding(e, 309 binding = get_active_mouse_binding(e,
311 config->current_mode->mouse_bindings, modifiers, false, 310 config->current_mode->mouse_bindings, modifiers, false,
@@ -330,7 +329,7 @@ static bool trigger_pointer_button_binding(struct sway_seat *seat,
330 329
331static void handle_button(struct sway_seat *seat, uint32_t time_msec, 330static void handle_button(struct sway_seat *seat, uint32_t time_msec,
332 struct wlr_input_device *device, uint32_t button, 331 struct wlr_input_device *device, uint32_t button,
333 enum wlr_button_state state) { 332 enum wl_pointer_button_state state) {
334 struct sway_cursor *cursor = seat->cursor; 333 struct sway_cursor *cursor = seat->cursor;
335 334
336 // Determine what's under the cursor 335 // Determine what's under the cursor
@@ -363,7 +362,7 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec,
363 362
364 // Handle clicking an empty workspace 363 // Handle clicking an empty workspace
365 if (node && node->type == N_WORKSPACE) { 364 if (node && node->type == N_WORKSPACE) {
366 if (state == WLR_BUTTON_PRESSED) { 365 if (state == WL_POINTER_BUTTON_STATE_PRESSED) {
367 seat_set_focus(seat, node); 366 seat_set_focus(seat, node);
368 transaction_commit_dirty(); 367 transaction_commit_dirty();
369 } 368 }
@@ -378,7 +377,7 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec,
378 seat_set_focus_layer(seat, layer); 377 seat_set_focus_layer(seat, layer);
379 transaction_commit_dirty(); 378 transaction_commit_dirty();
380 } 379 }
381 if (state == WLR_BUTTON_PRESSED) { 380 if (state == WL_POINTER_BUTTON_STATE_PRESSED) {
382 seatop_begin_down_on_surface(seat, surface, sx, sy); 381 seatop_begin_down_on_surface(seat, surface, sx, sy);
383 } 382 }
384 seat_pointer_notify_button(seat, time_msec, button, state); 383 seat_pointer_notify_button(seat, time_msec, button, state);
@@ -387,7 +386,7 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec,
387 386
388 // Handle tiling resize via border 387 // Handle tiling resize via border
389 if (cont && resize_edge && button == BTN_LEFT && 388 if (cont && resize_edge && button == BTN_LEFT &&
390 state == WLR_BUTTON_PRESSED && !is_floating) { 389 state == WL_POINTER_BUTTON_STATE_PRESSED && !is_floating) {
391 // If a resize is triggered on a tabbed or stacked container, change 390 // If a resize is triggered on a tabbed or stacked container, change
392 // focus to the tab which already had inactive focus -- otherwise, we'd 391 // focus to the tab which already had inactive focus -- otherwise, we'd
393 // change the active tab when the user probably just wanted to resize. 392 // change the active tab when the user probably just wanted to resize.
@@ -405,7 +404,7 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec,
405 // Handle tiling resize via mod 404 // Handle tiling resize via mod
406 bool mod_pressed = modifiers & config->floating_mod; 405 bool mod_pressed = modifiers & config->floating_mod;
407 if (cont && !is_floating_or_child && mod_pressed && 406 if (cont && !is_floating_or_child && mod_pressed &&
408 state == WLR_BUTTON_PRESSED) { 407 state == WL_POINTER_BUTTON_STATE_PRESSED) {
409 uint32_t btn_resize = config->floating_mod_inverse ? 408 uint32_t btn_resize = config->floating_mod_inverse ?
410 BTN_LEFT : BTN_RIGHT; 409 BTN_LEFT : BTN_RIGHT;
411 if (button == btn_resize) { 410 if (button == btn_resize) {
@@ -433,7 +432,7 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec,
433 } 432 }
434 433
435 // Handle changing focus when clicking on a container 434 // Handle changing focus when clicking on a container
436 if (cont && state == WLR_BUTTON_PRESSED) { 435 if (cont && state == WL_POINTER_BUTTON_STATE_PRESSED) {
437 // Default case: focus the container that was just clicked. 436 // Default case: focus the container that was just clicked.
438 node = &cont->node; 437 node = &cont->node;
439 438
@@ -454,7 +453,7 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec,
454 453
455 // Handle beginning floating move 454 // Handle beginning floating move
456 if (cont && is_floating_or_child && !is_fullscreen_or_child && 455 if (cont && is_floating_or_child && !is_fullscreen_or_child &&
457 state == WLR_BUTTON_PRESSED) { 456 state == WL_POINTER_BUTTON_STATE_PRESSED) {
458 uint32_t btn_move = config->floating_mod_inverse ? BTN_RIGHT : BTN_LEFT; 457 uint32_t btn_move = config->floating_mod_inverse ? BTN_RIGHT : BTN_LEFT;
459 if (button == btn_move && (mod_pressed || on_titlebar)) { 458 if (button == btn_move && (mod_pressed || on_titlebar)) {
460 seatop_begin_move_floating(seat, container_toplevel_ancestor(cont)); 459 seatop_begin_move_floating(seat, container_toplevel_ancestor(cont));
@@ -464,7 +463,7 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec,
464 463
465 // Handle beginning floating resize 464 // Handle beginning floating resize
466 if (cont && is_floating_or_child && !is_fullscreen_or_child && 465 if (cont && is_floating_or_child && !is_fullscreen_or_child &&
467 state == WLR_BUTTON_PRESSED) { 466 state == WL_POINTER_BUTTON_STATE_PRESSED) {
468 // Via border 467 // Via border
469 if (button == BTN_LEFT && resize_edge != WLR_EDGE_NONE) { 468 if (button == BTN_LEFT && resize_edge != WLR_EDGE_NONE) {
470 seat_set_focus_container(seat, cont); 469 seat_set_focus_container(seat, cont);
@@ -490,7 +489,7 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec,
490 489
491 // Handle moving a tiling container 490 // Handle moving a tiling container
492 if (config->tiling_drag && (mod_pressed || on_titlebar) && 491 if (config->tiling_drag && (mod_pressed || on_titlebar) &&
493 state == WLR_BUTTON_PRESSED && !is_floating_or_child && 492 state == WL_POINTER_BUTTON_STATE_PRESSED && !is_floating_or_child &&
494 cont && cont->pending.fullscreen_mode == FULLSCREEN_NONE) { 493 cont && cont->pending.fullscreen_mode == FULLSCREEN_NONE) {
495 // If moving a container by its title bar, use a threshold for the drag 494 // If moving a container by its title bar, use a threshold for the drag
496 if (!mod_pressed && config->tiling_drag_threshold > 0) { 495 if (!mod_pressed && config->tiling_drag_threshold > 0) {
@@ -503,14 +502,14 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec,
503 } 502 }
504 503
505 // Handle mousedown on a container surface 504 // Handle mousedown on a container surface
506 if (surface && cont && state == WLR_BUTTON_PRESSED) { 505 if (surface && cont && state == WL_POINTER_BUTTON_STATE_PRESSED) {
507 seatop_begin_down(seat, cont, sx, sy); 506 seatop_begin_down(seat, cont, sx, sy);
508 seat_pointer_notify_button(seat, time_msec, button, WLR_BUTTON_PRESSED); 507 seat_pointer_notify_button(seat, time_msec, button, WL_POINTER_BUTTON_STATE_PRESSED);
509 return; 508 return;
510 } 509 }
511 510
512 // Handle clicking a container surface or decorations 511 // Handle clicking a container surface or decorations
513 if (cont && state == WLR_BUTTON_PRESSED) { 512 if (cont && state == WL_POINTER_BUTTON_STATE_PRESSED) {
514 seat_pointer_notify_button(seat, time_msec, button, state); 513 seat_pointer_notify_button(seat, time_msec, button, state);
515 return; 514 return;
516 } 515 }
@@ -685,7 +684,7 @@ static void handle_touch_down(struct sway_seat *seat,
685 pointer_motion(cursor, event->time_msec, &event->touch->base, dx, dy, 684 pointer_motion(cursor, event->time_msec, &event->touch->base, dx, dy,
686 dx, dy); 685 dx, dy);
687 dispatch_cursor_button(cursor, &event->touch->base, event->time_msec, 686 dispatch_cursor_button(cursor, &event->touch->base, event->time_msec,
688 BTN_LEFT, WLR_BUTTON_PRESSED); 687 BTN_LEFT, WL_POINTER_BUTTON_STATE_PRESSED);
689 } 688 }
690} 689}
691 690
@@ -695,9 +694,9 @@ static void handle_touch_down(struct sway_seat *seat,
695 694
696static uint32_t wl_axis_to_button(struct wlr_pointer_axis_event *event) { 695static uint32_t wl_axis_to_button(struct wlr_pointer_axis_event *event) {
697 switch (event->orientation) { 696 switch (event->orientation) {
698 case WLR_AXIS_ORIENTATION_VERTICAL: 697 case WL_POINTER_AXIS_VERTICAL_SCROLL:
699 return event->delta < 0 ? SWAY_SCROLL_UP : SWAY_SCROLL_DOWN; 698 return event->delta < 0 ? SWAY_SCROLL_UP : SWAY_SCROLL_DOWN;
700 case WLR_AXIS_ORIENTATION_HORIZONTAL: 699 case WL_POINTER_AXIS_HORIZONTAL_SCROLL:
701 return event->delta < 0 ? SWAY_SCROLL_LEFT : SWAY_SCROLL_RIGHT; 700 return event->delta < 0 ? SWAY_SCROLL_LEFT : SWAY_SCROLL_RIGHT;
702 default: 701 default:
703 sway_log(SWAY_DEBUG, "Unknown axis orientation"); 702 sway_log(SWAY_DEBUG, "Unknown axis orientation");
diff --git a/sway/input/seatop_down.c b/sway/input/seatop_down.c
index b4421fe6..340e334b 100644
--- a/sway/input/seatop_down.c
+++ b/sway/input/seatop_down.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <float.h> 1#include <float.h>
3#include <wlr/types/wlr_cursor.h> 2#include <wlr/types/wlr_cursor.h>
4#include <wlr/types/wlr_tablet_v2.h> 3#include <wlr/types/wlr_tablet_v2.h>
@@ -118,7 +117,11 @@ static void handle_touch_cancel(struct sway_seat *seat,
118 } 117 }
119 118
120 if (e->surface) { 119 if (e->surface) {
121 wlr_seat_touch_notify_cancel(seat->wlr_seat, e->surface); 120 struct wl_client *client = wl_resource_get_client(e->surface->resource);
121 struct wlr_seat_client *seat_client = wlr_seat_client_for_wl_client(seat->wlr_seat, client);
122 if (seat_client != NULL) {
123 wlr_seat_touch_notify_cancel(seat->wlr_seat, seat_client);
124 }
122 } 125 }
123 126
124 if (wl_list_empty(&e->point_events)) { 127 if (wl_list_empty(&e->point_events)) {
@@ -143,7 +146,7 @@ static void handle_pointer_axis(struct sway_seat *seat,
143 146
144static void handle_button(struct sway_seat *seat, uint32_t time_msec, 147static void handle_button(struct sway_seat *seat, uint32_t time_msec,
145 struct wlr_input_device *device, uint32_t button, 148 struct wlr_input_device *device, uint32_t button,
146 enum wlr_button_state state) { 149 enum wl_pointer_button_state state) {
147 seat_pointer_notify_button(seat, time_msec, button, state); 150 seat_pointer_notify_button(seat, time_msec, button, state);
148 151
149 if (seat->cursor->pressed_button_count == 0) { 152 if (seat->cursor->pressed_button_count == 0) {
diff --git a/sway/input/seatop_move_floating.c b/sway/input/seatop_move_floating.c
index 21d048ce..83668d88 100644
--- a/sway/input/seatop_move_floating.c
+++ b/sway/input/seatop_move_floating.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <wlr/types/wlr_cursor.h> 1#include <wlr/types/wlr_cursor.h>
3#include "sway/desktop/transaction.h" 2#include "sway/desktop/transaction.h"
4#include "sway/input/cursor.h" 3#include "sway/input/cursor.h"
@@ -22,7 +21,7 @@ static void finalize_move(struct sway_seat *seat) {
22 21
23static void handle_button(struct sway_seat *seat, uint32_t time_msec, 22static void handle_button(struct sway_seat *seat, uint32_t time_msec,
24 struct wlr_input_device *device, uint32_t button, 23 struct wlr_input_device *device, uint32_t button,
25 enum wlr_button_state state) { 24 enum wl_pointer_button_state state) {
26 if (seat->cursor->pressed_button_count == 0) { 25 if (seat->cursor->pressed_button_count == 0) {
27 finalize_move(seat); 26 finalize_move(seat);
28 } 27 }
diff --git a/sway/input/seatop_move_tiling.c b/sway/input/seatop_move_tiling.c
index 7de39ff6..c525b77a 100644
--- a/sway/input/seatop_move_tiling.c
+++ b/sway/input/seatop_move_tiling.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <limits.h> 1#include <limits.h>
3#include <wlr/types/wlr_cursor.h> 2#include <wlr/types/wlr_cursor.h>
4#include <wlr/util/edges.h> 3#include <wlr/util/edges.h>
@@ -406,7 +405,7 @@ static void finalize_move(struct sway_seat *seat) {
406 405
407static void handle_button(struct sway_seat *seat, uint32_t time_msec, 406static void handle_button(struct sway_seat *seat, uint32_t time_msec,
408 struct wlr_input_device *device, uint32_t button, 407 struct wlr_input_device *device, uint32_t button,
409 enum wlr_button_state state) { 408 enum wl_pointer_button_state state) {
410 if (seat->cursor->pressed_button_count == 0) { 409 if (seat->cursor->pressed_button_count == 0) {
411 finalize_move(seat); 410 finalize_move(seat);
412 } 411 }
diff --git a/sway/input/seatop_resize_floating.c b/sway/input/seatop_resize_floating.c
index df683026..bec86e33 100644
--- a/sway/input/seatop_resize_floating.c
+++ b/sway/input/seatop_resize_floating.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <limits.h> 1#include <limits.h>
3#include <wlr/types/wlr_cursor.h> 2#include <wlr/types/wlr_cursor.h>
4#include <wlr/types/wlr_xcursor_manager.h> 3#include <wlr/types/wlr_xcursor_manager.h>
@@ -21,7 +20,7 @@ struct seatop_resize_floating_event {
21 20
22static void handle_button(struct sway_seat *seat, uint32_t time_msec, 21static void handle_button(struct sway_seat *seat, uint32_t time_msec,
23 struct wlr_input_device *device, uint32_t button, 22 struct wlr_input_device *device, uint32_t button,
24 enum wlr_button_state state) { 23 enum wl_pointer_button_state state) {
25 struct seatop_resize_floating_event *e = seat->seatop_data; 24 struct seatop_resize_floating_event *e = seat->seatop_data;
26 struct sway_container *con = e->con; 25 struct sway_container *con = e->con;
27 26
diff --git a/sway/input/seatop_resize_tiling.c b/sway/input/seatop_resize_tiling.c
index 869d11b5..15fd333b 100644
--- a/sway/input/seatop_resize_tiling.c
+++ b/sway/input/seatop_resize_tiling.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <wlr/types/wlr_cursor.h> 1#include <wlr/types/wlr_cursor.h>
3#include <wlr/util/edges.h> 2#include <wlr/util/edges.h>
4#include "sway/commands.h" 3#include "sway/commands.h"
@@ -46,7 +45,7 @@ static struct sway_container *container_get_resize_sibling(
46 45
47static void handle_button(struct sway_seat *seat, uint32_t time_msec, 46static void handle_button(struct sway_seat *seat, uint32_t time_msec,
48 struct wlr_input_device *device, uint32_t button, 47 struct wlr_input_device *device, uint32_t button,
49 enum wlr_button_state state) { 48 enum wl_pointer_button_state state) {
50 struct seatop_resize_tiling_event *e = seat->seatop_data; 49 struct seatop_resize_tiling_event *e = seat->seatop_data;
51 50
52 if (seat->cursor->pressed_button_count == 0) { 51 if (seat->cursor->pressed_button_count == 0) {
diff --git a/sway/input/tablet.c b/sway/input/tablet.c
index 902cb7ed..2863642a 100644
--- a/sway/input/tablet.c
+++ b/sway/input/tablet.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <stdlib.h> 1#include <stdlib.h>
3#include <wlr/config.h> 2#include <wlr/config.h>
4#include <wlr/types/wlr_tablet_v2.h> 3#include <wlr/types/wlr_tablet_v2.h>
diff --git a/sway/input/text_input.c b/sway/input/text_input.c
index 58911c2d..c38a3bb2 100644
--- a/sway/input/text_input.c
+++ b/sway/input/text_input.c
@@ -2,7 +2,14 @@
2#include <stdlib.h> 2#include <stdlib.h>
3#include "log.h" 3#include "log.h"
4#include "sway/input/seat.h" 4#include "sway/input/seat.h"
5#include "sway/scene_descriptor.h"
6#include "sway/tree/root.h"
7#include "sway/tree/view.h"
8#include "sway/output.h"
5#include "sway/input/text_input.h" 9#include "sway/input/text_input.h"
10#include "sway/input/text_input_popup.h"
11#include "sway/layers.h"
12static void input_popup_update(struct sway_input_popup *popup);
6 13
7static struct sway_text_input *relay_get_focusable_text_input( 14static struct sway_text_input *relay_get_focusable_text_input(
8 struct sway_input_method_relay *relay) { 15 struct sway_input_method_relay *relay) {
@@ -102,6 +109,10 @@ static void handle_im_destroy(struct wl_listener *listener, void *data) {
102 input_method_destroy); 109 input_method_destroy);
103 struct wlr_input_method_v2 *context = data; 110 struct wlr_input_method_v2 *context = data;
104 assert(context == relay->input_method); 111 assert(context == relay->input_method);
112 wl_list_remove(&relay->input_method_commit.link);
113 wl_list_remove(&relay->input_method_grab_keyboard.link);
114 wl_list_remove(&relay->input_method_destroy.link);
115 wl_list_remove(&relay->input_method_new_popup_surface.link);
105 relay->input_method = NULL; 116 relay->input_method = NULL;
106 struct sway_text_input *text_input = relay_get_focused_text_input(relay); 117 struct sway_text_input *text_input = relay_get_focused_text_input(relay);
107 if (text_input) { 118 if (text_input) {
@@ -133,6 +144,11 @@ static void relay_send_im_state(struct sway_input_method_relay *relay,
133 input->current.content_type.hint, 144 input->current.content_type.hint,
134 input->current.content_type.purpose); 145 input->current.content_type.purpose);
135 } 146 }
147 struct sway_input_popup *popup;
148 wl_list_for_each(popup, &relay->input_popups, link) {
149 // send_text_input_rectangle is called in this function
150 input_popup_update(popup);
151 }
136 wlr_input_method_v2_send_done(input_method); 152 wlr_input_method_v2_send_done(input_method);
137 // TODO: pass intent, display popup size 153 // TODO: pass intent, display popup size
138} 154}
@@ -255,6 +271,211 @@ static void relay_handle_text_input(struct wl_listener *listener,
255 sway_text_input_create(relay, wlr_text_input); 271 sway_text_input_create(relay, wlr_text_input);
256} 272}
257 273
274static void input_popup_update(struct sway_input_popup *popup) {
275 struct sway_text_input *text_input =
276 relay_get_focused_text_input(popup->relay);
277
278 if (text_input == NULL || text_input->input->focused_surface == NULL) {
279 return;
280 }
281
282 if (popup->scene_tree != NULL) {
283 wlr_scene_node_destroy(&popup->scene_tree->node);
284 popup->scene_tree = NULL;
285 }
286 if (popup->desc.relative != NULL) {
287 wlr_scene_node_destroy(popup->desc.relative);
288 popup->desc.relative = NULL;
289 }
290 popup->desc.view = NULL;
291
292 if (!popup->popup_surface->surface->mapped) {
293 return;
294 }
295
296 bool cursor_rect = text_input->input->current.features
297 & WLR_TEXT_INPUT_V3_FEATURE_CURSOR_RECTANGLE;
298 struct wlr_surface *focused_surface = text_input->input->focused_surface;
299 struct wlr_box cursor_area = text_input->input->current.cursor_rectangle;
300
301 struct wlr_box output_box;
302 struct wlr_box parent = {0};
303 struct wlr_layer_surface_v1 *layer_surface =
304 wlr_layer_surface_v1_try_from_wlr_surface(focused_surface);
305 struct wlr_scene_tree *relative_parent;
306
307 struct wlr_box geo = {0};
308
309 popup->scene_tree = wlr_scene_subsurface_tree_create(root->layers.popup, popup->popup_surface->surface);
310 if (layer_surface != NULL) {
311 struct sway_layer_surface *layer =
312 layer_surface->data;
313 if (layer == NULL) {
314 return;
315 }
316
317 relative_parent = layer->scene->tree;
318 struct wlr_output *output = layer->layer_surface->output;
319 wlr_output_layout_get_box(root->output_layout, output, &output_box);
320 int lx, ly;
321 wlr_scene_node_coords(&layer->tree->node, &lx, &ly);
322 parent.x = lx;
323 parent.y = ly;
324 popup->desc.view = NULL;
325 } else {
326 struct sway_view *view = view_from_wlr_surface(focused_surface);
327 relative_parent = view->scene_tree;
328 geo = view->geometry;
329 int lx, ly;
330 wlr_scene_node_coords(&view->scene_tree->node, &lx, &ly);
331 struct wlr_output *output = wlr_output_layout_output_at(root->output_layout,
332 view->container->pending.content_x + view->geometry.x,
333 view->container->pending.content_y + view->geometry.y);
334 wlr_output_layout_get_box(root->output_layout, output, &output_box);
335 parent.x = lx;
336 parent.y = ly;
337
338 parent.width = view->geometry.width;
339 parent.height = view->geometry.height;
340 popup->desc.view = view;
341 }
342
343 struct wlr_scene_tree *relative = wlr_scene_tree_create(relative_parent);
344
345 popup->desc.relative = &relative->node;
346 if (!scene_descriptor_assign(&popup->scene_tree->node,
347 SWAY_SCENE_DESC_POPUP, &popup->desc)) {
348 wlr_scene_node_destroy(&popup->scene_tree->node);
349 popup->scene_tree = NULL;
350 return;
351 }
352
353 if (!cursor_rect) {
354 cursor_area.x = 0;
355 cursor_area.y = 0;
356 cursor_area.width = parent.width;
357 cursor_area.height = parent.height;
358 }
359
360 int popup_width = popup->popup_surface->surface->current.width;
361 int popup_height = popup->popup_surface->surface->current.height;
362 int x1 = parent.x + cursor_area.x;
363 int x2 = parent.x + cursor_area.x + cursor_area.width;
364 int y1 = parent.y + cursor_area.y;
365 int y2 = parent.y + cursor_area.y + cursor_area.height;
366 int x = x1;
367 int y = y2;
368
369 int available_right = output_box.x + output_box.width - x1;
370 int available_left = x2 - output_box.x;
371 if (available_right < popup_width && available_left > available_right) {
372 x = x2 - popup_width;
373 }
374
375 int available_down = output_box.y + output_box.height - y2;
376 int available_up = y1 - output_box.y;
377 if (available_down < popup_height && available_up > available_down) {
378 y = y1 - popup_height;
379 }
380
381 wlr_scene_node_set_position(&relative->node, x - parent.x - geo.x, y - parent.y - geo.y);
382 if (cursor_rect) {
383 struct wlr_box box = {
384 .x = x1 - x,
385 .y = y1 - y,
386 .width = cursor_area.width,
387 .height = cursor_area.height,
388 };
389 wlr_input_popup_surface_v2_send_text_input_rectangle(
390 popup->popup_surface, &box);
391 }
392 wlr_scene_node_set_position(&popup->scene_tree->node, x - geo.x, y - geo.y);
393}
394
395static void input_popup_set_focus(struct sway_input_popup *popup,
396 struct wlr_surface *surface) {
397 wl_list_remove(&popup->focused_surface_unmap.link);
398
399 if (surface == NULL) {
400 wl_list_init(&popup->focused_surface_unmap.link);
401 input_popup_update(popup);
402 return;
403 }
404 struct wlr_layer_surface_v1 *layer_surface =
405 wlr_layer_surface_v1_try_from_wlr_surface(surface);
406 if (layer_surface != NULL) {
407 wl_signal_add(
408 &layer_surface->surface->events.unmap, &popup->focused_surface_unmap);
409 input_popup_update(popup);
410 return;
411 }
412
413 struct sway_view *view = view_from_wlr_surface(surface);
414 wl_signal_add(&view->events.unmap, &popup->focused_surface_unmap);
415}
416
417static void handle_im_popup_destroy(struct wl_listener *listener, void *data) {
418 struct sway_input_popup *popup =
419 wl_container_of(listener, popup, popup_destroy);
420 wl_list_remove(&popup->focused_surface_unmap.link);
421 wl_list_remove(&popup->popup_surface_commit.link);
422 wl_list_remove(&popup->popup_destroy.link);
423 wl_list_remove(&popup->link);
424
425 free(popup);
426}
427
428static void handle_im_popup_surface_commit(struct wl_listener *listener,
429 void *data) {
430 struct sway_input_popup *popup =
431 wl_container_of(listener, popup, popup_surface_commit);
432 input_popup_update(popup);
433}
434
435static void handle_im_focused_surface_unmap(
436 struct wl_listener *listener, void *data) {
437 struct sway_input_popup *popup =
438 wl_container_of(listener, popup, focused_surface_unmap);
439 input_popup_update(popup);
440}
441
442static void handle_im_new_popup_surface(struct wl_listener *listener,
443 void *data) {
444 struct sway_input_method_relay *relay = wl_container_of(listener, relay,
445 input_method_new_popup_surface);
446 struct sway_input_popup *popup = calloc(1, sizeof(*popup));
447 popup->relay = relay;
448 popup->popup_surface = data;
449 popup->popup_surface->data = popup;
450
451 wl_signal_add(
452 &popup->popup_surface->events.destroy, &popup->popup_destroy);
453 popup->popup_destroy.notify = handle_im_popup_destroy;
454 wl_signal_add(&popup->popup_surface->surface->events.commit,
455 &popup->popup_surface_commit);
456 popup->popup_surface_commit.notify = handle_im_popup_surface_commit;
457 wl_list_init(&popup->focused_surface_unmap.link);
458 popup->focused_surface_unmap.notify = handle_im_focused_surface_unmap;
459
460 struct sway_text_input *text_input = relay_get_focused_text_input(relay);
461 if (text_input != NULL) {
462 input_popup_set_focus(popup, text_input->input->focused_surface);
463 } else {
464 input_popup_set_focus(popup, NULL);
465 }
466
467 wl_list_insert(&relay->input_popups, &popup->link);
468}
469
470static void text_input_send_enter(struct sway_text_input *text_input,
471 struct wlr_surface *surface) {
472 wlr_text_input_v3_send_enter(text_input->input, surface);
473 struct sway_input_popup *popup;
474 wl_list_for_each(popup, &text_input->relay->input_popups, link) {
475 input_popup_set_focus(popup, surface);
476 }
477}
478
258static void relay_handle_input_method(struct wl_listener *listener, 479static void relay_handle_input_method(struct wl_listener *listener,
259 void *data) { 480 void *data) {
260 struct sway_input_method_relay *relay = wl_container_of(listener, relay, 481 struct sway_input_method_relay *relay = wl_container_of(listener, relay,
@@ -280,10 +501,13 @@ static void relay_handle_input_method(struct wl_listener *listener,
280 wl_signal_add(&relay->input_method->events.destroy, 501 wl_signal_add(&relay->input_method->events.destroy,
281 &relay->input_method_destroy); 502 &relay->input_method_destroy);
282 relay->input_method_destroy.notify = handle_im_destroy; 503 relay->input_method_destroy.notify = handle_im_destroy;
504 wl_signal_add(&relay->input_method->events.new_popup_surface,
505 &relay->input_method_new_popup_surface);
506 relay->input_method_new_popup_surface.notify = handle_im_new_popup_surface;
283 507
284 struct sway_text_input *text_input = relay_get_focusable_text_input(relay); 508 struct sway_text_input *text_input = relay_get_focusable_text_input(relay);
285 if (text_input) { 509 if (text_input) {
286 wlr_text_input_v3_send_enter(text_input->input, 510 text_input_send_enter(text_input,
287 text_input->pending_focused_surface); 511 text_input->pending_focused_surface);
288 text_input_set_pending_focused_surface(text_input, NULL); 512 text_input_set_pending_focused_surface(text_input, NULL);
289 } 513 }
@@ -293,6 +517,7 @@ void sway_input_method_relay_init(struct sway_seat *seat,
293 struct sway_input_method_relay *relay) { 517 struct sway_input_method_relay *relay) {
294 relay->seat = seat; 518 relay->seat = seat;
295 wl_list_init(&relay->text_inputs); 519 wl_list_init(&relay->text_inputs);
520 wl_list_init(&relay->input_popups);
296 521
297 relay->text_input_new.notify = relay_handle_text_input; 522 relay->text_input_new.notify = relay_handle_text_input;
298 wl_signal_add(&server.text_input->events.text_input, 523 wl_signal_add(&server.text_input->events.text_input,
diff --git a/sway/ipc-json.c b/sway/ipc-json.c
index 58356d4e..81ca3483 100644
--- a/sway/ipc-json.c
+++ b/sway/ipc-json.c
@@ -288,6 +288,8 @@ static json_object *ipc_json_create_node(int id, const char* type, char *name,
288 json_object_object_add(object, "focus", focus); 288 json_object_object_add(object, "focus", focus);
289 json_object_object_add(object, "fullscreen_mode", json_object_new_int(0)); 289 json_object_object_add(object, "fullscreen_mode", json_object_new_int(0));
290 json_object_object_add(object, "sticky", json_object_new_boolean(false)); 290 json_object_object_add(object, "sticky", json_object_new_boolean(false));
291 json_object_object_add(object, "floating", NULL);
292 json_object_object_add(object, "scratchpad_state", NULL);
291 293
292 return object; 294 return object;
293} 295}
@@ -675,7 +677,8 @@ static void ipc_json_describe_view(struct sway_container *c, json_object *object
675static void ipc_json_describe_container(struct sway_container *c, json_object *object) { 677static void ipc_json_describe_container(struct sway_container *c, json_object *object) {
676 json_object_object_add(object, "name", 678 json_object_object_add(object, "name",
677 c->title ? json_object_new_string(c->title) : NULL); 679 c->title ? json_object_new_string(c->title) : NULL);
678 if (container_is_floating(c)) { 680 bool floating = container_is_floating(c);
681 if (floating) {
679 json_object_object_add(object, "type", 682 json_object_object_add(object, "type",
680 json_object_new_string("floating_con")); 683 json_object_new_string("floating_con"));
681 } 684 }
@@ -693,9 +696,17 @@ static void ipc_json_describe_container(struct sway_container *c, json_object *o
693 json_object_object_add(object, "urgent", json_object_new_boolean(urgent)); 696 json_object_object_add(object, "urgent", json_object_new_boolean(urgent));
694 json_object_object_add(object, "sticky", json_object_new_boolean(c->is_sticky)); 697 json_object_object_add(object, "sticky", json_object_new_boolean(c->is_sticky));
695 698
699 // sway doesn't track the floating reason, so we can't use "auto_on" or "user_off"
700 json_object_object_add(object, "floating",
701 json_object_new_string(floating ? "user_on" : "auto_off"));
702
696 json_object_object_add(object, "fullscreen_mode", 703 json_object_object_add(object, "fullscreen_mode",
697 json_object_new_int(c->pending.fullscreen_mode)); 704 json_object_new_int(c->pending.fullscreen_mode));
698 705
706 // sway doesn't track if window was resized in scratchpad, so we can't use "changed"
707 json_object_object_add(object, "scratchpad_state",
708 json_object_new_string(!c->scratchpad ? "none" : "fresh"));
709
699 struct sway_node *parent = node_get_parent(&c->node); 710 struct sway_node *parent = node_get_parent(&c->node);
700 struct wlr_box parent_box = {0, 0, 0, 0}; 711 struct wlr_box parent_box = {0, 0, 0, 0};
701 712
@@ -1086,10 +1097,6 @@ json_object *ipc_json_describe_input(struct sway_input_device *device) {
1086 json_object_new_string(device->identifier)); 1097 json_object_new_string(device->identifier));
1087 json_object_object_add(object, "name", 1098 json_object_object_add(object, "name",
1088 json_object_new_string(device->wlr_device->name)); 1099 json_object_new_string(device->wlr_device->name));
1089 json_object_object_add(object, "vendor",
1090 json_object_new_int(device->wlr_device->vendor));
1091 json_object_object_add(object, "product",
1092 json_object_new_int(device->wlr_device->product));
1093 json_object_object_add(object, "type", 1100 json_object_object_add(object, "type",
1094 json_object_new_string( 1101 json_object_new_string(
1095 input_device_get_type(device))); 1102 input_device_get_type(device)));
@@ -1143,6 +1150,10 @@ json_object *ipc_json_describe_input(struct sway_input_device *device) {
1143 libinput_dev = wlr_libinput_get_device_handle(device->wlr_device); 1150 libinput_dev = wlr_libinput_get_device_handle(device->wlr_device);
1144 json_object_object_add(object, "libinput", 1151 json_object_object_add(object, "libinput",
1145 describe_libinput_device(libinput_dev)); 1152 describe_libinput_device(libinput_dev));
1153 json_object_object_add(object, "vendor",
1154 json_object_new_int(libinput_device_get_id_vendor(libinput_dev)));
1155 json_object_object_add(object, "product",
1156 json_object_new_int(libinput_device_get_id_product(libinput_dev)));
1146 } 1157 }
1147#endif 1158#endif
1148 1159
diff --git a/sway/ipc-server.c b/sway/ipc-server.c
index 9692a77f..7f353c0e 100644
--- a/sway/ipc-server.c
+++ b/sway/ipc-server.c
@@ -1,5 +1,4 @@
1// See https://i3wm.org/docs/ipc.html for protocol information 1// See https://i3wm.org/docs/ipc.html for protocol information
2#define _POSIX_C_SOURCE 200112L
3#include <linux/input-event-codes.h> 2#include <linux/input-event-codes.h>
4#include <assert.h> 3#include <assert.h>
5#include <errno.h> 4#include <errno.h>
diff --git a/sway/lock.c b/sway/lock.c
index 8ad9c3f6..289e8ca4 100644
--- a/sway/lock.c
+++ b/sway/lock.c
@@ -1,6 +1,6 @@
1#define _POSIX_C_SOURCE 200809L
2#include <assert.h> 1#include <assert.h>
3#include <wlr/types/wlr_scene.h> 2#include <wlr/types/wlr_scene.h>
3#include <wlr/types/wlr_session_lock_v1.h>
4#include "log.h" 4#include "log.h"
5#include "sway/input/cursor.h" 5#include "sway/input/cursor.h"
6#include "sway/input/keyboard.h" 6#include "sway/input/keyboard.h"
diff --git a/sway/main.c b/sway/main.c
index 73254dc2..1c4939aa 100644
--- a/sway/main.c
+++ b/sway/main.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <getopt.h> 1#include <getopt.h>
3#include <pango/pangocairo.h> 2#include <pango/pangocairo.h>
4#include <signal.h> 3#include <signal.h>
diff --git a/sway/server.c b/sway/server.c
index cc20e89d..180d3a6b 100644
--- a/sway/server.c
+++ b/sway/server.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <assert.h> 1#include <assert.h>
3#include <stdbool.h> 2#include <stdbool.h>
4#include <stdlib.h> 3#include <stdlib.h>
@@ -8,24 +7,32 @@
8#include <wlr/backend/headless.h> 7#include <wlr/backend/headless.h>
9#include <wlr/backend/multi.h> 8#include <wlr/backend/multi.h>
10#include <wlr/config.h> 9#include <wlr/config.h>
10#include <wlr/render/allocator.h>
11#include <wlr/render/wlr_renderer.h> 11#include <wlr/render/wlr_renderer.h>
12#include <wlr/types/wlr_compositor.h> 12#include <wlr/types/wlr_compositor.h>
13#include <wlr/types/wlr_content_type_v1.h> 13#include <wlr/types/wlr_content_type_v1.h>
14#include <wlr/types/wlr_cursor_shape_v1.h> 14#include <wlr/types/wlr_cursor_shape_v1.h>
15#include <wlr/types/wlr_data_control_v1.h> 15#include <wlr/types/wlr_data_control_v1.h>
16#include <wlr/types/wlr_data_device.h>
16#include <wlr/types/wlr_drm.h> 17#include <wlr/types/wlr_drm.h>
17#include <wlr/types/wlr_export_dmabuf_v1.h> 18#include <wlr/types/wlr_export_dmabuf_v1.h>
19#include <wlr/types/wlr_ext_foreign_toplevel_list_v1.h>
20#include <wlr/types/wlr_foreign_toplevel_management_v1.h>
18#include <wlr/types/wlr_fractional_scale_v1.h> 21#include <wlr/types/wlr_fractional_scale_v1.h>
19#include <wlr/types/wlr_gamma_control_v1.h> 22#include <wlr/types/wlr_gamma_control_v1.h>
20#include <wlr/types/wlr_idle_notify_v1.h> 23#include <wlr/types/wlr_idle_notify_v1.h>
21#include <wlr/types/wlr_layer_shell_v1.h> 24#include <wlr/types/wlr_layer_shell_v1.h>
22#include <wlr/types/wlr_linux_dmabuf_v1.h> 25#include <wlr/types/wlr_linux_dmabuf_v1.h>
26#include <wlr/types/wlr_output_management_v1.h>
27#include <wlr/types/wlr_output_power_management_v1.h>
23#include <wlr/types/wlr_pointer_constraints_v1.h> 28#include <wlr/types/wlr_pointer_constraints_v1.h>
29#include <wlr/types/wlr_presentation_time.h>
24#include <wlr/types/wlr_primary_selection_v1.h> 30#include <wlr/types/wlr_primary_selection_v1.h>
25#include <wlr/types/wlr_relative_pointer_v1.h> 31#include <wlr/types/wlr_relative_pointer_v1.h>
26#include <wlr/types/wlr_screencopy_v1.h> 32#include <wlr/types/wlr_screencopy_v1.h>
27#include <wlr/types/wlr_security_context_v1.h> 33#include <wlr/types/wlr_security_context_v1.h>
28#include <wlr/types/wlr_server_decoration.h> 34#include <wlr/types/wlr_server_decoration.h>
35#include <wlr/types/wlr_session_lock_v1.h>
29#include <wlr/types/wlr_single_pixel_buffer_v1.h> 36#include <wlr/types/wlr_single_pixel_buffer_v1.h>
30#include <wlr/types/wlr_subcompositor.h> 37#include <wlr/types/wlr_subcompositor.h>
31#include <wlr/types/wlr_tablet_v2.h> 38#include <wlr/types/wlr_tablet_v2.h>
@@ -58,8 +65,9 @@
58#include <wlr/types/wlr_drm_lease_v1.h> 65#include <wlr/types/wlr_drm_lease_v1.h>
59#endif 66#endif
60 67
61#define SWAY_XDG_SHELL_VERSION 2 68#define SWAY_XDG_SHELL_VERSION 5
62#define SWAY_LAYER_SHELL_VERSION 4 69#define SWAY_LAYER_SHELL_VERSION 4
70#define SWAY_FOREIGN_TOPLEVEL_LIST_VERSION 1
63 71
64bool allow_unsupported_gpu = false; 72bool allow_unsupported_gpu = false;
65 73
@@ -93,6 +101,7 @@ static bool is_privileged(const struct wl_global *global) {
93 global == server.output_manager_v1->global || 101 global == server.output_manager_v1->global ||
94 global == server.output_power_manager_v1->global || 102 global == server.output_power_manager_v1->global ||
95 global == server.input_method->global || 103 global == server.input_method->global ||
104 global == server.foreign_toplevel_list->global ||
96 global == server.foreign_toplevel_manager->global || 105 global == server.foreign_toplevel_manager->global ||
97 global == server.data_control_manager_v1->global || 106 global == server.data_control_manager_v1->global ||
98 global == server.screencopy_manager_v1->global || 107 global == server.screencopy_manager_v1->global ||
@@ -103,7 +112,8 @@ static bool is_privileged(const struct wl_global *global) {
103 global == server.session_lock.manager->global || 112 global == server.session_lock.manager->global ||
104 global == server.input->keyboard_shortcuts_inhibit->global || 113 global == server.input->keyboard_shortcuts_inhibit->global ||
105 global == server.input->virtual_keyboard->global || 114 global == server.input->virtual_keyboard->global ||
106 global == server.input->virtual_pointer->global; 115 global == server.input->virtual_pointer->global ||
116 global == server.input->transient_seat_manager->global;
107} 117}
108 118
109static bool filter_global(const struct wl_client *client, 119static bool filter_global(const struct wl_client *client,
@@ -163,6 +173,45 @@ static void detect_proprietary(struct wlr_backend *backend, void *data) {
163 drmFreeVersion(version); 173 drmFreeVersion(version);
164} 174}
165 175
176static void handle_renderer_lost(struct wl_listener *listener, void *data) {
177 struct sway_server *server = wl_container_of(listener, server, renderer_lost);
178
179 sway_log(SWAY_INFO, "Re-creating renderer after GPU reset");
180
181 struct wlr_renderer *renderer = wlr_renderer_autocreate(server->backend);
182 if (renderer == NULL) {
183 sway_log(SWAY_ERROR, "Unable to create renderer");
184 return;
185 }
186
187 struct wlr_allocator *allocator =
188 wlr_allocator_autocreate(server->backend, renderer);
189 if (allocator == NULL) {
190 sway_log(SWAY_ERROR, "Unable to create allocator");
191 wlr_renderer_destroy(renderer);
192 return;
193 }
194
195 struct wlr_renderer *old_renderer = server->renderer;
196 struct wlr_allocator *old_allocator = server->allocator;
197 server->renderer = renderer;
198 server->allocator = allocator;
199
200 wl_list_remove(&server->renderer_lost.link);
201 wl_signal_add(&server->renderer->events.lost, &server->renderer_lost);
202
203 wlr_compositor_set_renderer(server->compositor, renderer);
204
205 for (int i = 0; i < root->outputs->length; ++i) {
206 struct sway_output *output = root->outputs->items[i];
207 wlr_output_init_render(output->wlr_output,
208 server->allocator, server->renderer);
209 }
210
211 wlr_allocator_destroy(old_allocator);
212 wlr_renderer_destroy(old_renderer);
213}
214
166bool server_init(struct sway_server *server) { 215bool server_init(struct sway_server *server) {
167 sway_log(SWAY_DEBUG, "Initializing Wayland server"); 216 sway_log(SWAY_DEBUG, "Initializing Wayland server");
168 server->wl_display = wl_display_create(); 217 server->wl_display = wl_display_create();
@@ -186,15 +235,17 @@ bool server_init(struct sway_server *server) {
186 return false; 235 return false;
187 } 236 }
188 237
238 server->renderer_lost.notify = handle_renderer_lost;
239 wl_signal_add(&server->renderer->events.lost, &server->renderer_lost);
240
189 wlr_renderer_init_wl_shm(server->renderer, server->wl_display); 241 wlr_renderer_init_wl_shm(server->renderer, server->wl_display);
190 242
191 if (wlr_renderer_get_dmabuf_texture_formats(server->renderer) != NULL) { 243 if (wlr_renderer_get_texture_formats(server->renderer, WLR_BUFFER_CAP_DMABUF) != NULL) {
192 server->linux_dmabuf_v1 = wlr_linux_dmabuf_v1_create_with_renderer( 244 server->linux_dmabuf_v1 = wlr_linux_dmabuf_v1_create_with_renderer(
193 server->wl_display, 4, server->renderer); 245 server->wl_display, 4, server->renderer);
194 } 246 if (debug.legacy_wl_drm) {
195 if (wlr_renderer_get_dmabuf_texture_formats(server->renderer) != NULL && 247 wlr_drm_create(server->wl_display, server->renderer);
196 debug.legacy_wl_drm) { 248 }
197 wlr_drm_create(server->wl_display, server->renderer);
198 } 249 }
199 250
200 server->allocator = wlr_allocator_autocreate(server->backend, 251 server->allocator = wlr_allocator_autocreate(server->backend,
@@ -289,6 +340,8 @@ bool server_init(struct sway_server *server) {
289 &server->output_power_manager_set_mode); 340 &server->output_power_manager_set_mode);
290 server->input_method = wlr_input_method_manager_v2_create(server->wl_display); 341 server->input_method = wlr_input_method_manager_v2_create(server->wl_display);
291 server->text_input = wlr_text_input_manager_v3_create(server->wl_display); 342 server->text_input = wlr_text_input_manager_v3_create(server->wl_display);
343 server->foreign_toplevel_list =
344 wlr_ext_foreign_toplevel_list_v1_create(server->wl_display, SWAY_FOREIGN_TOPLEVEL_LIST_VERSION);
292 server->foreign_toplevel_manager = 345 server->foreign_toplevel_manager =
293 wlr_foreign_toplevel_manager_v1_create(server->wl_display); 346 wlr_foreign_toplevel_manager_v1_create(server->wl_display);
294 347
@@ -388,6 +441,7 @@ void server_fini(struct sway_server *server) {
388 wlr_xwayland_destroy(server->xwayland.wlr_xwayland); 441 wlr_xwayland_destroy(server->xwayland.wlr_xwayland);
389#endif 442#endif
390 wl_display_destroy_clients(server->wl_display); 443 wl_display_destroy_clients(server->wl_display);
444 wlr_backend_destroy(server->backend);
391 wl_display_destroy(server->wl_display); 445 wl_display_destroy(server->wl_display);
392 list_free(server->dirty_nodes); 446 list_free(server->dirty_nodes);
393} 447}
diff --git a/sway/sway-ipc.7.scd b/sway/sway-ipc.7.scd
index f4a5ccff..2f697248 100644
--- a/sway/sway-ipc.7.scd
+++ b/sway/sway-ipc.7.scd
@@ -376,6 +376,12 @@ node and will have the following properties:
376: integer 376: integer
377: (Only containers and views) The fullscreen mode of the node. 0 means none, 1 means 377: (Only containers and views) The fullscreen mode of the node. 0 means none, 1 means
378 full workspace, and 2 means global fullscreen 378 full workspace, and 2 means global fullscreen
379|- floating
380: string
381: Floating state of container. Can be either "auto_off" or "user_on"
382|- scratchpad_state
383: string
384: Whether the window is in the scratchpad. Can be either "none" or "fresh"
379|- app_id 385|- app_id
380: string 386: string
381: (Only views) For an xdg-shell view, the name of the application, if set. 387: (Only views) For an xdg-shell view, the name of the application, if set.
@@ -1040,7 +1046,7 @@ An object with a single string property containing the contents of the config
1040*Example Reply:* 1046*Example Reply:*
1041``` 1047```
1042{ 1048{
1043 "config": "set $mod Mod4\nbindsym $mod+q exit\n" 1049 "config": "set $mod Mod4\\nbindsym $mod+q exit\\n"
1044} 1050}
1045``` 1051```
1046 1052
diff --git a/sway/sway-output.5.scd b/sway/sway-output.5.scd
index 028cb7ab..7d088d5d 100644
--- a/sway/sway-output.5.scd
+++ b/sway/sway-output.5.scd
@@ -72,13 +72,11 @@ must be separated by one space. For example:
72 72
73*output* <name> scale <factor> 73*output* <name> scale <factor>
74 Scales the specified output by the specified scale _factor_. An integer is 74 Scales the specified output by the specified scale _factor_. An integer is
75 recommended, but fractional values are also supported. If a fractional 75 recommended, but fractional values are also supported. You may be better
76 value are specified, be warned that it is not possible to faithfully 76 served by setting an integer scale factor and adjusting the font size of
77 represent the contents of your windows - they will be rendered at the next 77 your applications to taste. HiDPI isn't supported with Xwayland clients
78 highest integer scale factor and downscaled. You may be better served by 78 (windows will blur). A fractional scale may be slightly adjusted to match
79 setting an integer scale factor and adjusting the font size of your 79 requirements of the protocol.
80 applications to taste. HiDPI isn't supported with Xwayland clients (windows
81 will blur).
82 80
83*output* <name> scale_filter linear|nearest|smart 81*output* <name> scale_filter linear|nearest|smart
84 Indicates how to scale application buffers that are rendered at a scale 82 Indicates how to scale application buffers that are rendered at a scale
diff --git a/sway/sway.5.scd b/sway/sway.5.scd
index 7e58b528..9f823947 100644
--- a/sway/sway.5.scd
+++ b/sway/sway.5.scd
@@ -400,6 +400,12 @@ runtime.
400 only be available for that group. By default, if you overwrite a binding, 400 only be available for that group. By default, if you overwrite a binding,
401 swaynag will give you a warning. To silence this, use the _--no-warn_ flag. 401 swaynag will give you a warning. To silence this, use the _--no-warn_ flag.
402 402
403 For specifying modifier keys, you can use the XKB modifier names _Shift_,
404 _Lock_ (for Caps Lock), _Control_, _Mod1_ (for Alt), _Mod2_ (for Num Lock),
405 _Mod3_ (for XKB modifier Mod3), _Mod4_ (for the Logo key), and _Mod5_ (for
406 AltGr). In addition, you can use the aliases _Ctrl_ (for Control), _Alt_
407 (for Alt), and _Super_ (for the Logo key).
408
403 Unless the flag _--locked_ is set, the command will not be run when a 409 Unless the flag _--locked_ is set, the command will not be run when a
404 screen locking program is active. If there is a matching binding with 410 screen locking program is active. If there is a matching binding with
405 and without _--locked_, the one with will be preferred when locked and the 411 and without _--locked_, the one with will be preferred when locked and the
diff --git a/sway/sway_text_node.c b/sway/sway_text_node.c
index b9a77d94..4b7ee999 100644
--- a/sway/sway_text_node.c
+++ b/sway/sway_text_node.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <drm_fourcc.h> 1#include <drm_fourcc.h>
3#include <stdio.h> 2#include <stdio.h>
4#include <stdlib.h> 3#include <stdlib.h>
@@ -58,11 +57,11 @@ struct text_buffer {
58}; 57};
59 58
60static int get_text_width(struct sway_text_node *props) { 59static int get_text_width(struct sway_text_node *props) {
61 if (props->max_width) { 60 int width = props->width;
62 return MIN(props->max_width, props->width); 61 if (props->max_width >= 0) {
62 width = MIN(width, props->max_width);
63 } 63 }
64 64 return MAX(width, 0);
65 return props->width;
66} 65}
67 66
68static void update_source_box(struct text_buffer *buffer) { 67static void update_source_box(struct text_buffer *buffer) {
@@ -82,6 +81,11 @@ static void render_backing_buffer(struct text_buffer *buffer) {
82 return; 81 return;
83 } 82 }
84 83
84 if (buffer->props.max_width == 0) {
85 wlr_scene_buffer_set_buffer(buffer->buffer_node, NULL);
86 return;
87 }
88
85 float scale = buffer->scale; 89 float scale = buffer->scale;
86 int width = ceil(buffer->props.width * scale); 90 int width = ceil(buffer->props.width * scale);
87 int height = ceil(buffer->props.height * scale); 91 int height = ceil(buffer->props.height * scale);
@@ -237,6 +241,7 @@ struct sway_text_node *sway_text_node_create(struct wlr_scene_tree *parent,
237 241
238 buffer->buffer_node = node; 242 buffer->buffer_node = node;
239 buffer->props.node = &node->node; 243 buffer->props.node = &node->node;
244 buffer->props.max_width = -1;
240 buffer->text = strdup(text); 245 buffer->text = strdup(text);
241 if (!buffer->text) { 246 if (!buffer->text) {
242 free(buffer); 247 free(buffer);
diff --git a/sway/swaynag.c b/sway/swaynag.c
index 6031174d..bc5e23ea 100644
--- a/sway/swaynag.c
+++ b/sway/swaynag.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <signal.h> 1#include <signal.h>
3#include <stdbool.h> 2#include <stdbool.h>
4#include <stdlib.h> 3#include <stdlib.h>
diff --git a/sway/tree/arrange.c b/sway/tree/arrange.c
index af925d05..d4003fe6 100644
--- a/sway/tree/arrange.c
+++ b/sway/tree/arrange.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <ctype.h> 1#include <ctype.h>
3#include <stdbool.h> 2#include <stdbool.h>
4#include <stdlib.h> 3#include <stdlib.h>
diff --git a/sway/tree/container.c b/sway/tree/container.c
index 30cb97ba..80ef34fe 100644
--- a/sway/tree/container.c
+++ b/sway/tree/container.c
@@ -1,9 +1,9 @@
1#define _POSIX_C_SOURCE 200809L
2#include <assert.h> 1#include <assert.h>
3#include <drm_fourcc.h> 2#include <drm_fourcc.h>
4#include <stdint.h> 3#include <stdint.h>
5#include <stdlib.h> 4#include <stdlib.h>
6#include <wayland-server-core.h> 5#include <wayland-server-core.h>
6#include <wlr/types/wlr_foreign_toplevel_management_v1.h>
7#include <wlr/types/wlr_linux_dmabuf_v1.h> 7#include <wlr/types/wlr_linux_dmabuf_v1.h>
8#include <wlr/types/wlr_output_layout.h> 8#include <wlr/types/wlr_output_layout.h>
9#include <wlr/types/wlr_subcompositor.h> 9#include <wlr/types/wlr_subcompositor.h>
@@ -352,6 +352,8 @@ void container_arrange_title_bar(struct sway_container *con) {
352 352
353 int alloc_width = MIN((int)node->width, 353 int alloc_width = MIN((int)node->width,
354 width - h_padding - config->titlebar_h_padding); 354 width - h_padding - config->titlebar_h_padding);
355 alloc_width = MAX(alloc_width, 0);
356
355 sway_text_node_set_max_width(node, alloc_width); 357 sway_text_node_set_max_width(node, alloc_width);
356 wlr_scene_node_set_position(node->node, 358 wlr_scene_node_set_position(node->node,
357 h_padding, (height - node->height) >> 1); 359 h_padding, (height - node->height) >> 1);
@@ -376,6 +378,8 @@ void container_arrange_title_bar(struct sway_container *con) {
376 378
377 int alloc_width = MIN((int) node->width, 379 int alloc_width = MIN((int) node->width,
378 width - h_padding - config->titlebar_h_padding); 380 width - h_padding - config->titlebar_h_padding);
381 alloc_width = MAX(alloc_width, 0);
382
379 sway_text_node_set_max_width(node, alloc_width); 383 sway_text_node_set_max_width(node, alloc_width);
380 wlr_scene_node_set_position(node->node, 384 wlr_scene_node_set_position(node->node,
381 h_padding, (height - node->height) >> 1); 385 h_padding, (height - node->height) >> 1);
diff --git a/sway/tree/node.c b/sway/tree/node.c
index 213cf0a6..7aaf9762 100644
--- a/sway/tree/node.c
+++ b/sway/tree/node.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include "sway/output.h" 1#include "sway/output.h"
3#include "sway/server.h" 2#include "sway/server.h"
4#include "sway/tree/container.h" 3#include "sway/tree/container.h"
diff --git a/sway/tree/output.c b/sway/tree/output.c
index cd7bf0c2..2d11195e 100644
--- a/sway/tree/output.c
+++ b/sway/tree/output.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <assert.h> 1#include <assert.h>
3#include <ctype.h> 2#include <ctype.h>
4#include <string.h> 3#include <string.h>
diff --git a/sway/tree/root.c b/sway/tree/root.c
index e9cea5e2..ae3c3cb2 100644
--- a/sway/tree/root.c
+++ b/sway/tree/root.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <stdbool.h> 1#include <stdbool.h>
3#include <stdlib.h> 2#include <stdlib.h>
4#include <string.h> 3#include <string.h>
diff --git a/sway/tree/view.c b/sway/tree/view.c
index d6984178..35b4b73f 100644
--- a/sway/tree/view.c
+++ b/sway/tree/view.c
@@ -1,9 +1,10 @@
1#define _POSIX_C_SOURCE 200809L
2#include <stdlib.h> 1#include <stdlib.h>
3#include <strings.h> 2#include <strings.h>
4#include <wayland-server-core.h> 3#include <wayland-server-core.h>
5#include <wlr/render/wlr_renderer.h> 4#include <wlr/render/wlr_renderer.h>
6#include <wlr/types/wlr_buffer.h> 5#include <wlr/types/wlr_buffer.h>
6#include <wlr/types/wlr_ext_foreign_toplevel_list_v1.h>
7#include <wlr/types/wlr_foreign_toplevel_management_v1.h>
7#include <wlr/types/wlr_output_layout.h> 8#include <wlr/types/wlr_output_layout.h>
8#include <wlr/types/wlr_server_decoration.h> 9#include <wlr/types/wlr_server_decoration.h>
9#include <wlr/types/wlr_subcompositor.h> 10#include <wlr/types/wlr_subcompositor.h>
@@ -410,6 +411,12 @@ void view_request_activate(struct sway_view *view, struct sway_seat *seat) {
410 transaction_commit_dirty(); 411 transaction_commit_dirty();
411} 412}
412 413
414void view_request_urgent(struct sway_view *view) {
415 if (config->focus_on_window_activation != FOWA_NONE) {
416 view_set_urgent(view, true);
417 }
418}
419
413void view_set_csd_from_server(struct sway_view *view, bool enabled) { 420void view_set_csd_from_server(struct sway_view *view, bool enabled) {
414 sway_log(SWAY_DEBUG, "Telling view %p to set CSD to %i", view, enabled); 421 sway_log(SWAY_DEBUG, "Telling view %p to set CSD to %i", view, enabled);
415 if (view->xdg_decoration) { 422 if (view->xdg_decoration) {
@@ -582,6 +589,14 @@ static struct sway_workspace *select_workspace(struct sway_view *view) {
582 return NULL; 589 return NULL;
583} 590}
584 591
592static void update_ext_foreign_toplevel(struct sway_view *view) {
593 struct wlr_ext_foreign_toplevel_handle_v1_state toplevel_state = {
594 .app_id = view_get_app_id(view),
595 .title = view_get_title(view),
596 };
597 wlr_ext_foreign_toplevel_handle_v1_update_state(view->ext_foreign_toplevel, &toplevel_state);
598}
599
585static bool should_focus(struct sway_view *view) { 600static bool should_focus(struct sway_view *view) {
586 struct sway_seat *seat = input_manager_current_seat(); 601 struct sway_seat *seat = input_manager_current_seat();
587 struct sway_container *prev_con = seat_get_focused_container(seat); 602 struct sway_container *prev_con = seat_get_focused_container(seat);
@@ -751,6 +766,13 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface,
751 } 766 }
752 } 767 }
753 768
769 struct wlr_ext_foreign_toplevel_handle_v1_state foreign_toplevel_state = {
770 .app_id = view_get_app_id(view),
771 .title = view_get_title(view),
772 };
773 view->ext_foreign_toplevel =
774 wlr_ext_foreign_toplevel_handle_v1_create(server.foreign_toplevel_list, &foreign_toplevel_state);
775
754 view->foreign_toplevel = 776 view->foreign_toplevel =
755 wlr_foreign_toplevel_handle_v1_create(server.foreign_toplevel_manager); 777 wlr_foreign_toplevel_handle_v1_create(server.foreign_toplevel_manager);
756 view->foreign_activate_request.notify = handle_foreign_activate_request; 778 view->foreign_activate_request.notify = handle_foreign_activate_request;
@@ -828,6 +850,10 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface,
828 input_manager_set_focus(&view->container->node); 850 input_manager_set_focus(&view->container->node);
829 } 851 }
830 852
853 if (view->ext_foreign_toplevel) {
854 update_ext_foreign_toplevel(view);
855 }
856
831 const char *app_id; 857 const char *app_id;
832 const char *class; 858 const char *class;
833 if ((app_id = view_get_app_id(view)) != NULL) { 859 if ((app_id = view_get_app_id(view)) != NULL) {
@@ -847,6 +873,11 @@ void view_unmap(struct sway_view *view) {
847 view->urgent_timer = NULL; 873 view->urgent_timer = NULL;
848 } 874 }
849 875
876 if (view->ext_foreign_toplevel) {
877 wlr_ext_foreign_toplevel_handle_v1_destroy(view->ext_foreign_toplevel);
878 view->ext_foreign_toplevel = NULL;
879 }
880
850 if (view->foreign_toplevel) { 881 if (view->foreign_toplevel) {
851 wlr_foreign_toplevel_handle_v1_destroy(view->foreign_toplevel); 882 wlr_foreign_toplevel_handle_v1_destroy(view->foreign_toplevel);
852 view->foreign_toplevel = NULL; 883 view->foreign_toplevel = NULL;
@@ -1014,6 +1045,18 @@ static size_t parse_title_format(struct sway_view *view, char *buffer) {
1014 return len; 1045 return len;
1015} 1046}
1016 1047
1048void view_update_app_id(struct sway_view *view) {
1049 const char *app_id = view_get_app_id(view);
1050
1051 if (view->foreign_toplevel && app_id) {
1052 wlr_foreign_toplevel_handle_v1_set_app_id(view->foreign_toplevel, app_id);
1053 }
1054
1055 if (view->ext_foreign_toplevel) {
1056 update_ext_foreign_toplevel(view);
1057 }
1058}
1059
1017void view_update_title(struct sway_view *view, bool force) { 1060void view_update_title(struct sway_view *view, bool force) {
1018 const char *title = view_get_title(view); 1061 const char *title = view_get_title(view);
1019 1062
@@ -1060,6 +1103,10 @@ void view_update_title(struct sway_view *view, bool force) {
1060 if (view->foreign_toplevel && title) { 1103 if (view->foreign_toplevel && title) {
1061 wlr_foreign_toplevel_handle_v1_set_title(view->foreign_toplevel, title); 1104 wlr_foreign_toplevel_handle_v1_set_title(view->foreign_toplevel, title);
1062 } 1105 }
1106
1107 if (view->ext_foreign_toplevel) {
1108 update_ext_foreign_toplevel(view);
1109 }
1063} 1110}
1064 1111
1065bool view_is_visible(struct sway_view *view) { 1112bool view_is_visible(struct sway_view *view) {
diff --git a/sway/tree/workspace.c b/sway/tree/workspace.c
index 40d33435..a68dc927 100644
--- a/sway/tree/workspace.c
+++ b/sway/tree/workspace.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809
2#include <ctype.h> 1#include <ctype.h>
3#include <limits.h> 2#include <limits.h>
4#include <stdbool.h> 3#include <stdbool.h>
diff --git a/sway/xdg_activation_v1.c b/sway/xdg_activation_v1.c
index c26ee19a..b7c80dd4 100644
--- a/sway/xdg_activation_v1.c
+++ b/sway/xdg_activation_v1.c
@@ -1,4 +1,5 @@
1#include <wlr/types/wlr_xdg_activation_v1.h> 1#include <wlr/types/wlr_xdg_activation_v1.h>
2#include <wlr/types/wlr_xdg_shell.h>
2#include "sway/desktop/launcher.h" 3#include "sway/desktop/launcher.h"
3#include "sway/tree/view.h" 4#include "sway/tree/view.h"
4#include "sway/tree/workspace.h" 5#include "sway/tree/workspace.h"
@@ -17,11 +18,15 @@ void xdg_activation_v1_handle_request_activate(struct wl_listener *listener,
17 return; 18 return;
18 } 19 }
19 20
21 struct launcher_ctx *ctx = event->token->data;
22 if (ctx == NULL) {
23 return;
24 }
25
20 if (!xdg_surface->surface->mapped) { 26 if (!xdg_surface->surface->mapped) {
21 // This is a startup notification. If we are tracking it, the data 27 // This is a startup notification. If we are tracking it, the data
22 // field is a launcher_ctx. 28 // field is a launcher_ctx.
23 struct launcher_ctx *ctx = event->token->data; 29 if (ctx->activated) {
24 if (!ctx || ctx->activated) {
25 // This ctx has already been activated and cannot be used again 30 // This ctx has already been activated and cannot be used again
26 // for a startup notification. It will be destroyed 31 // for a startup notification. It will be destroyed
27 return; 32 return;
@@ -32,9 +37,19 @@ void xdg_activation_v1_handle_request_activate(struct wl_listener *listener,
32 return; 37 return;
33 } 38 }
34 39
35 struct wlr_seat *wlr_seat = event->token->seat; 40 // This is an activation request. If this context is internal we have ctx->seat.
36 struct sway_seat *seat = wlr_seat ? wlr_seat->data : NULL; 41 struct sway_seat *seat = ctx->seat;
37 view_request_activate(view, seat); 42 if (!seat) {
43 // Otherwise, use the seat indicated by the launcher client in set_serial
44 seat = ctx->token->seat ? ctx->token->seat->data : NULL;
45 }
46
47 if (seat && ctx->had_focused_surface) {
48 view_request_activate(view, seat);
49 } else {
50 // The token is valid, but cannot be used to activate a window
51 view_request_urgent(view);
52 }
38} 53}
39 54
40void xdg_activation_v1_handle_new_token(struct wl_listener *listener, void *data) { 55void xdg_activation_v1_handle_new_token(struct wl_listener *listener, void *data) {
diff --git a/swaybar/bar.c b/swaybar/bar.c
index 021fc3bd..5b1213a8 100644
--- a/swaybar/bar.c
+++ b/swaybar/bar.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <assert.h> 1#include <assert.h>
3#include <errno.h> 2#include <errno.h>
4#include <fcntl.h> 3#include <fcntl.h>
diff --git a/swaybar/config.c b/swaybar/config.c
index 5e828773..55bfcb72 100644
--- a/swaybar/config.c
+++ b/swaybar/config.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <stdlib.h> 1#include <stdlib.h>
3#include <string.h> 2#include <string.h>
4#include "swaybar/config.h" 3#include "swaybar/config.h"
diff --git a/swaybar/i3bar.c b/swaybar/i3bar.c
index ccd5a076..62c22d43 100644
--- a/swaybar/i3bar.c
+++ b/swaybar/i3bar.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <json.h> 1#include <json.h>
3#include <linux/input-event-codes.h> 2#include <linux/input-event-codes.h>
4#include <ctype.h> 3#include <ctype.h>
diff --git a/swaybar/ipc.c b/swaybar/ipc.c
index 33ae6544..03500bdf 100644
--- a/swaybar/ipc.c
+++ b/swaybar/ipc.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809
2#include <limits.h> 1#include <limits.h>
3#include <poll.h> 2#include <poll.h>
4#include <stdio.h> 3#include <stdio.h>
diff --git a/swaybar/main.c b/swaybar/main.c
index a44c1e63..3dc67233 100644
--- a/swaybar/main.c
+++ b/swaybar/main.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <stdio.h> 1#include <stdio.h>
3#include <stdlib.h> 2#include <stdlib.h>
4#include <string.h> 3#include <string.h>
diff --git a/swaybar/render.c b/swaybar/render.c
index 1113ca44..879a4e42 100644
--- a/swaybar/render.c
+++ b/swaybar/render.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <assert.h> 1#include <assert.h>
3#include <linux/input-event-codes.h> 2#include <linux/input-event-codes.h>
4#include <limits.h> 3#include <limits.h>
diff --git a/swaybar/status_line.c b/swaybar/status_line.c
index 2e9bb7f1..e542e606 100644
--- a/swaybar/status_line.c
+++ b/swaybar/status_line.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <assert.h> 1#include <assert.h>
3#include <fcntl.h> 2#include <fcntl.h>
4#include <sys/ioctl.h> 3#include <sys/ioctl.h>
diff --git a/swaybar/tray/host.c b/swaybar/tray/host.c
index eea2caa5..79b54606 100644
--- a/swaybar/tray/host.c
+++ b/swaybar/tray/host.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <stdbool.h> 1#include <stdbool.h>
3#include <stdio.h> 2#include <stdio.h>
4#include <stdlib.h> 3#include <stdlib.h>
diff --git a/swaybar/tray/icon.c b/swaybar/tray/icon.c
index b513dca5..659edd86 100644
--- a/swaybar/tray/icon.c
+++ b/swaybar/tray/icon.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <ctype.h> 1#include <ctype.h>
3#include <dirent.h> 2#include <dirent.h>
4#include <stdbool.h> 3#include <stdbool.h>
diff --git a/swaybar/tray/item.c b/swaybar/tray/item.c
index d5fe50b1..ca6c03ad 100644
--- a/swaybar/tray/item.c
+++ b/swaybar/tray/item.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <arpa/inet.h> 1#include <arpa/inet.h>
3#include <cairo.h> 2#include <cairo.h>
4#include <limits.h> 3#include <limits.h>
diff --git a/swaybar/tray/watcher.c b/swaybar/tray/watcher.c
index 2458a8c2..3cfea8d8 100644
--- a/swaybar/tray/watcher.c
+++ b/swaybar/tray/watcher.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <stdbool.h> 1#include <stdbool.h>
3#include <stddef.h> 2#include <stddef.h>
4#include <stdio.h> 3#include <stdio.h>
diff --git a/swaymsg/main.c b/swaymsg/main.c
index db9346c4..573a7b16 100644
--- a/swaymsg/main.c
+++ b/swaymsg/main.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2 1
3#include <limits.h> 2#include <limits.h>
4#include <stdio.h> 3#include <stdio.h>
diff --git a/swaynag/config.c b/swaynag/config.c
index cff3930f..efd71ce7 100644
--- a/swaynag/config.c
+++ b/swaynag/config.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <getopt.h> 1#include <getopt.h>
3#include <stdio.h> 2#include <stdio.h>
4#include <stdlib.h> 3#include <stdlib.h>
diff --git a/swaynag/main.c b/swaynag/main.c
index 20390207..634bddbf 100644
--- a/swaynag/main.c
+++ b/swaynag/main.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <stdlib.h> 1#include <stdlib.h>
3#include <signal.h> 2#include <signal.h>
4#include "log.h" 3#include "log.h"
diff --git a/swaynag/swaynag.c b/swaynag/swaynag.c
index 6ea739e3..50eea148 100644
--- a/swaynag/swaynag.c
+++ b/swaynag/swaynag.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <stdlib.h> 1#include <stdlib.h>
3#include <assert.h> 2#include <assert.h>
4#include <sys/stat.h> 3#include <sys/stat.h>
diff --git a/swaynag/types.c b/swaynag/types.c
index 409cc668..821e5b21 100644
--- a/swaynag/types.c
+++ b/swaynag/types.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <getopt.h> 1#include <getopt.h>
3#include <stdbool.h> 2#include <stdbool.h>
4#include <stdlib.h> 3#include <stdlib.h>