aboutsummaryrefslogtreecommitdiffstats
path: root/sway
diff options
context:
space:
mode:
Diffstat (limited to 'sway')
-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/events.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.c2
-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.c11
-rw-r--r--sway/commands/output.c10
-rw-r--r--sway/commands/output/background.c1
-rw-r--r--sway/commands/output/color_profile.c101
-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.c16
-rw-r--r--sway/commands/seat/hide_cursor.c1
-rw-r--r--sway/commands/seat/idle.c1
-rw-r--r--sway/commands/seat/pointer_constraint.c1
-rw-r--r--sway/commands/seat/shortcuts_inhibitor.c1
-rw-r--r--sway/commands/seat/xcursor_theme.c1
-rw-r--r--sway/commands/set.c1
-rw-r--r--sway/commands/shortcuts_inhibitor.c1
-rw-r--r--sway/commands/show_marks.c1
-rw-r--r--sway/commands/swap.c5
-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.c4
-rw-r--r--sway/config.c104
-rw-r--r--sway/config/bar.c2
-rw-r--r--sway/config/input.c2
-rw-r--r--sway/config/output.c810
-rw-r--r--sway/config/seat.c1
-rw-r--r--sway/criteria.c20
-rw-r--r--sway/desktop/launcher.c17
-rw-r--r--sway/desktop/layer_shell.c29
-rw-r--r--sway/desktop/output.c147
-rw-r--r--sway/desktop/transaction.c24
-rw-r--r--sway/desktop/xdg_shell.c19
-rw-r--r--sway/desktop/xwayland.c1
-rw-r--r--sway/input/cursor.c29
-rw-r--r--sway/input/input-manager.c39
-rw-r--r--sway/input/keyboard.c2
-rw-r--r--sway/input/seat.c30
-rw-r--r--sway/input/seatop_default.c44
-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/switch.c1
-rw-r--r--sway/input/tablet.c2
-rw-r--r--sway/input/text_input.c233
-rw-r--r--sway/ipc-json.c26
-rw-r--r--sway/ipc-server.c1
-rw-r--r--sway/lock.c2
-rw-r--r--sway/main.c1
-rw-r--r--sway/meson.build3
-rw-r--r--sway/server.c82
-rw-r--r--sway/sway-ipc.7.scd8
-rw-r--r--sway/sway-output.5.scd24
-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.c2
-rw-r--r--sway/tree/root.c3
-rw-r--r--sway/tree/view.c92
-rw-r--r--sway/tree/workspace.c7
-rw-r--r--sway/xdg_activation_v1.c25
97 files changed, 1441 insertions, 643 deletions
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/events.c b/sway/commands/input/events.c
index 08d99bf0..3cea026e 100644
--- a/sway/commands/input/events.c
+++ b/sway/commands/input/events.c
@@ -5,6 +5,7 @@
5#include "sway/config.h" 5#include "sway/config.h"
6#include "sway/commands.h" 6#include "sway/commands.h"
7#include "sway/input/input-manager.h" 7#include "sway/input/input-manager.h"
8#include "sway/server.h"
8#include "log.h" 9#include "log.h"
9 10
10#if WLR_HAS_LIBINPUT_BACKEND 11#if WLR_HAS_LIBINPUT_BACKEND
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..8d600fca 100644
--- a/sway/commands/input/xkb_switch_layout.c
+++ b/sway/commands/input/xkb_switch_layout.c
@@ -1,9 +1,9 @@
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"
5#include "sway/commands.h" 4#include "sway/commands.h"
6#include "sway/input/input-manager.h" 5#include "sway/input/input-manager.h"
6#include "sway/server.h"
7#include "log.h" 7#include "log.h"
8 8
9struct xkb_switch_layout_action { 9struct xkb_switch_layout_action {
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..ff656cfb 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>
@@ -12,6 +11,7 @@
12#include "sway/input/seat.h" 11#include "sway/input/seat.h"
13#include "sway/ipc-server.h" 12#include "sway/ipc-server.h"
14#include "sway/output.h" 13#include "sway/output.h"
14#include "sway/server.h"
15#include "sway/tree/arrange.h" 15#include "sway/tree/arrange.h"
16#include "sway/tree/container.h" 16#include "sway/tree/container.h"
17#include "sway/tree/root.h" 17#include "sway/tree/root.h"
@@ -770,15 +770,6 @@ static struct cmd_results *cmd_move_in_direction(
770 ipc_event_window(container, "move"); 770 ipc_event_window(container, "move");
771 } 771 }
772 772
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); 773 container_end_mouse_operation(container);
783 774
784 return cmd_results_new(CMD_SUCCESS, NULL); 775 return cmd_results_new(CMD_SUCCESS, NULL);
diff --git a/sway/commands/output.c b/sway/commands/output.c
index df32c673..b822e770 100644
--- a/sway/commands/output.c
+++ b/sway/commands/output.c
@@ -10,6 +10,7 @@ static const struct cmd_handler output_handlers[] = {
10 { "adaptive_sync", output_cmd_adaptive_sync }, 10 { "adaptive_sync", output_cmd_adaptive_sync },
11 { "background", output_cmd_background }, 11 { "background", output_cmd_background },
12 { "bg", output_cmd_background }, 12 { "bg", output_cmd_background },
13 { "color_profile", output_cmd_color_profile },
13 { "disable", output_cmd_disable }, 14 { "disable", output_cmd_disable },
14 { "dpms", output_cmd_dpms }, 15 { "dpms", output_cmd_dpms },
15 { "enable", output_cmd_enable }, 16 { "enable", output_cmd_enable },
@@ -103,15 +104,18 @@ struct cmd_results *cmd_output(int argc, char **argv) {
103 104
104 bool background = output->background; 105 bool background = output->background;
105 106
106 output = store_output_config(output); 107 store_output_config(output);
107 108
108 // If reloading, the output configs will be applied after reading the 109 // If reloading, the output configs will be applied after reading the
109 // entire config and before the deferred commands so that an auto generated 110 // entire config and before the deferred commands so that an auto generated
110 // workspace name is not given to re-enabled outputs. 111 // workspace name is not given to re-enabled outputs.
111 if (!config->reloading && !config->validating) { 112 if (!config->reloading && !config->validating) {
112 apply_output_config_to_outputs(output); 113 apply_all_output_configs();
113 if (background) { 114 if (background) {
114 spawn_swaybg(); 115 if (!spawn_swaybg()) {
116 return cmd_results_new(CMD_FAILURE,
117 "Failed to apply background configuration");
118 }
115 } 119 }
116 } 120 }
117 121
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/color_profile.c b/sway/commands/output/color_profile.c
new file mode 100644
index 00000000..792bd55f
--- /dev/null
+++ b/sway/commands/output/color_profile.c
@@ -0,0 +1,101 @@
1#include <fcntl.h>
2#include <strings.h>
3#include <sys/stat.h>
4#include <unistd.h>
5#include <wlr/render/color.h>
6#include "sway/commands.h"
7#include "sway/config.h"
8
9static bool read_file_into_buf(const char *path, void **buf, size_t *size) {
10 /* Why not use fopen/fread directly? glibc will succesfully open directories,
11 * not just files, and supports seeking on them. Instead, we directly
12 * work with file descriptors and use the more consistent open/fstat/read. */
13 int fd = open(path, O_RDONLY | O_NOCTTY | O_CLOEXEC);
14 if (fd == -1) {
15 return false;
16 }
17 char *b = NULL;
18 struct stat info;
19 if (fstat(fd, &info) == -1) {
20 goto fail;
21 }
22 // only regular files, to avoid issues with e.g. opening pipes
23 if (!S_ISREG(info.st_mode)) {
24 goto fail;
25 }
26 off_t s = info.st_size;
27 if (s <= 0) {
28 goto fail;
29 }
30 b = calloc(1, s);
31 if (!b) {
32 goto fail;
33 }
34 size_t nread = 0;
35 while (nread < (size_t)s) {
36 size_t to_read = (size_t)s - nread;
37 ssize_t r = read(fd, b + nread, to_read);
38 if ((r == -1 && errno != EINTR) || r == 0) {
39 goto fail;
40 }
41 nread += (size_t)r;
42 }
43 close(fd);
44 *buf = b;
45 *size = (size_t)s;
46 return true; // success
47fail:
48 free(b);
49 close(fd);
50 return false;
51}
52
53struct cmd_results *output_cmd_color_profile(int argc, char **argv) {
54 if (!config->handler_context.output_config) {
55 return cmd_results_new(CMD_FAILURE, "Missing output config");
56 }
57 if (!argc) {
58 return cmd_results_new(CMD_INVALID, "Missing color_profile first argument.");
59 }
60
61 if (strcmp(*argv, "srgb") == 0) {
62 wlr_color_transform_unref(config->handler_context.output_config->color_transform);
63 config->handler_context.output_config->color_transform = NULL;
64 config->handler_context.output_config->set_color_transform = true;
65
66 config->handler_context.leftovers.argc = argc - 1;
67 config->handler_context.leftovers.argv = argv + 1;
68 } else if (strcmp(*argv, "icc") == 0) {
69 if (argc < 2) {
70 return cmd_results_new(CMD_INVALID,
71 "Invalid color profile specification: icc type requires a file");
72 }
73 void *data = NULL;
74 size_t size = 0;
75 if (!read_file_into_buf(argv[1], &data, &size)) {
76 return cmd_results_new(CMD_FAILURE,
77 "Failed to load color profile: could not read ICC file");
78 }
79
80 struct wlr_color_transform *tmp =
81 wlr_color_transform_init_linear_to_icc(data, size);
82 if (!tmp) {
83 free(data);
84 return cmd_results_new(CMD_FAILURE,
85 "Failed to load color profile: failed to initialize transform from ICC");
86 }
87 free(data);
88
89 wlr_color_transform_unref(config->handler_context.output_config->color_transform);
90 config->handler_context.output_config->color_transform = tmp;
91 config->handler_context.output_config->set_color_transform = true;
92
93 config->handler_context.leftovers.argc = argc - 2;
94 config->handler_context.leftovers.argv = argv + 2;
95 } else {
96 return cmd_results_new(CMD_INVALID,
97 "Invalid color profile specification: first argument should be icc|srgb");
98 }
99
100 return NULL;
101}
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..434e6bbb 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>
@@ -6,6 +5,7 @@
6#include <wlr/types/wlr_pointer.h> 5#include <wlr/types/wlr_pointer.h>
7#include "sway/commands.h" 6#include "sway/commands.h"
8#include "sway/input/cursor.h" 7#include "sway/input/cursor.h"
8#include "sway/server.h"
9 9
10static struct cmd_results *press_or_release(struct sway_cursor *cursor, 10static struct cmd_results *press_or_release(struct sway_cursor *cursor,
11 char *action, char *button_str); 11 char *action, char *button_str);
@@ -85,12 +85,12 @@ struct cmd_results *seat_cmd_cursor(int argc, char **argv) {
85 85
86static struct cmd_results *press_or_release(struct sway_cursor *cursor, 86static struct cmd_results *press_or_release(struct sway_cursor *cursor,
87 char *action, char *button_str) { 87 char *action, char *button_str) {
88 enum wlr_button_state state; 88 enum wl_pointer_button_state state;
89 uint32_t button; 89 uint32_t button;
90 if (strcasecmp(action, "press") == 0) { 90 if (strcasecmp(action, "press") == 0) {
91 state = WLR_BUTTON_PRESSED; 91 state = WL_POINTER_BUTTON_STATE_PRESSED;
92 } else if (strcasecmp(action, "release") == 0) { 92 } else if (strcasecmp(action, "release") == 0) {
93 state = WLR_BUTTON_RELEASED; 93 state = WL_POINTER_BUTTON_STATE_RELEASED;
94 } else { 94 } else {
95 return cmd_results_new(CMD_INVALID, "%s", expected_syntax); 95 return cmd_results_new(CMD_INVALID, "%s", expected_syntax);
96 } 96 }
@@ -105,16 +105,16 @@ static struct cmd_results *press_or_release(struct sway_cursor *cursor,
105 } else if (button == SWAY_SCROLL_UP || button == SWAY_SCROLL_DOWN 105 } else if (button == SWAY_SCROLL_UP || button == SWAY_SCROLL_DOWN
106 || button == SWAY_SCROLL_LEFT || button == SWAY_SCROLL_RIGHT) { 106 || button == SWAY_SCROLL_LEFT || button == SWAY_SCROLL_RIGHT) {
107 // Dispatch axis event 107 // Dispatch axis event
108 enum wlr_axis_orientation orientation = 108 enum wl_pointer_axis orientation =
109 (button == SWAY_SCROLL_UP || button == SWAY_SCROLL_DOWN) 109 (button == SWAY_SCROLL_UP || button == SWAY_SCROLL_DOWN)
110 ? WLR_AXIS_ORIENTATION_VERTICAL 110 ? WL_POINTER_AXIS_VERTICAL_SCROLL
111 : WLR_AXIS_ORIENTATION_HORIZONTAL; 111 : WL_POINTER_AXIS_HORIZONTAL_SCROLL;
112 double delta = (button == SWAY_SCROLL_UP || button == SWAY_SCROLL_LEFT) 112 double delta = (button == SWAY_SCROLL_UP || button == SWAY_SCROLL_LEFT)
113 ? -1 : 1; 113 ? -1 : 1;
114 struct wlr_pointer_axis_event event = { 114 struct wlr_pointer_axis_event event = {
115 .pointer = NULL, 115 .pointer = NULL,
116 .time_msec = 0, 116 .time_msec = 0,
117 .source = WLR_AXIS_SOURCE_WHEEL, 117 .source = WL_POINTER_AXIS_SOURCE_WHEEL,
118 .orientation = orientation, 118 .orientation = orientation,
119 .delta = delta * 15, 119 .delta = delta * 15,
120 .delta_discrete = delta 120 .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/pointer_constraint.c b/sway/commands/seat/pointer_constraint.c
index 3890ebde..38f85bcd 100644
--- a/sway/commands/seat/pointer_constraint.c
+++ b/sway/commands/seat/pointer_constraint.c
@@ -4,6 +4,7 @@
4#include "sway/config.h" 4#include "sway/config.h"
5#include "sway/input/cursor.h" 5#include "sway/input/cursor.h"
6#include "sway/input/seat.h" 6#include "sway/input/seat.h"
7#include "sway/server.h"
7 8
8enum operation { 9enum operation {
9 OP_ENABLE, 10 OP_ENABLE,
diff --git a/sway/commands/seat/shortcuts_inhibitor.c b/sway/commands/seat/shortcuts_inhibitor.c
index 7c7f99cf..df68618d 100644
--- a/sway/commands/seat/shortcuts_inhibitor.c
+++ b/sway/commands/seat/shortcuts_inhibitor.c
@@ -2,6 +2,7 @@
2#include "sway/commands.h" 2#include "sway/commands.h"
3#include "sway/input/seat.h" 3#include "sway/input/seat.h"
4#include "sway/input/input-manager.h" 4#include "sway/input/input-manager.h"
5#include "sway/server.h"
5#include "util.h" 6#include "util.h"
6 7
7static struct cmd_results *handle_action(struct seat_config *sc, 8static struct cmd_results *handle_action(struct seat_config *sc,
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/shortcuts_inhibitor.c b/sway/commands/shortcuts_inhibitor.c
index ffa1a5c9..2dfd1b9f 100644
--- a/sway/commands/shortcuts_inhibitor.c
+++ b/sway/commands/shortcuts_inhibitor.c
@@ -3,6 +3,7 @@
3#include "sway/commands.h" 3#include "sway/commands.h"
4#include "sway/config.h" 4#include "sway/config.h"
5#include "sway/input/seat.h" 5#include "sway/input/seat.h"
6#include "sway/server.h"
6#include "sway/tree/container.h" 7#include "sway/tree/container.h"
7#include "sway/tree/view.h" 8#include "sway/tree/view.h"
8 9
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..c0b0d0b9 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"
@@ -19,7 +18,7 @@ static bool test_con_id(struct sway_container *container, void *data) {
19 return container->node.id == *con_id; 18 return container->node.id == *con_id;
20} 19}
21 20
22#if HAVE_XWAYLAND 21#if WLR_HAS_XWAYLAND
23static bool test_id(struct sway_container *container, void *data) { 22static bool test_id(struct sway_container *container, void *data) {
24 xcb_window_t *wid = data; 23 xcb_window_t *wid = data;
25 return (container->view && container->view->type == SWAY_VIEW_XWAYLAND 24 return (container->view && container->view->type == SWAY_VIEW_XWAYLAND
@@ -54,7 +53,7 @@ struct cmd_results *cmd_swap(int argc, char **argv) {
54 53
55 char *value = join_args(argv + 3, argc - 3); 54 char *value = join_args(argv + 3, argc - 3);
56 if (strcasecmp(argv[2], "id") == 0) { 55 if (strcasecmp(argv[2], "id") == 0) {
57#if HAVE_XWAYLAND 56#if WLR_HAS_XWAYLAND
58 xcb_window_t id = strtol(value, NULL, 0); 57 xcb_window_t id = strtol(value, NULL, 0);
59 other = root_find_container(test_id, &id); 58 other = root_find_container(test_id, &id);
60#endif 59#endif
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..c0b175fc 100644
--- a/sway/commands/xwayland.c
+++ b/sway/commands/xwayland.c
@@ -10,7 +10,7 @@ struct cmd_results *cmd_xwayland(int argc, char **argv) {
10 return error; 10 return error;
11 } 11 }
12 12
13#ifdef HAVE_XWAYLAND 13#ifdef WLR_HAS_XWAYLAND
14 enum xwayland_mode xwayland; 14 enum xwayland_mode xwayland;
15 if (strcmp(argv[0], "force") == 0) { 15 if (strcmp(argv[0], "force") == 0) {
16 xwayland = XWAYLAND_MODE_IMMEDIATE; 16 xwayland = XWAYLAND_MODE_IMMEDIATE;
@@ -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..5058efcc 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>
@@ -22,6 +23,7 @@
22#include "sway/config.h" 23#include "sway/config.h"
23#include "sway/criteria.h" 24#include "sway/criteria.h"
24#include "sway/desktop/transaction.h" 25#include "sway/desktop/transaction.h"
26#include "sway/server.h"
25#include "sway/swaynag.h" 27#include "sway/swaynag.h"
26#include "sway/tree/arrange.h" 28#include "sway/tree/arrange.h"
27#include "sway/tree/root.h" 29#include "sway/tree/root.h"
@@ -36,19 +38,26 @@
36struct sway_config *config = NULL; 38struct sway_config *config = NULL;
37 39
38static struct xkb_state *keysym_translation_state_create( 40static struct xkb_state *keysym_translation_state_create(
39 struct xkb_rule_names rules) { 41 struct xkb_rule_names rules, uint32_t context_flags) {
40 struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_SECURE_GETENV); 42 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( 43 struct xkb_keymap *xkb_keymap = xkb_keymap_new_from_names(
42 context, 44 context,
43 &rules, 45 &rules,
44 XKB_KEYMAP_COMPILE_NO_FLAGS); 46 XKB_KEYMAP_COMPILE_NO_FLAGS);
45
46 xkb_context_unref(context); 47 xkb_context_unref(context);
48 if (xkb_keymap == NULL) {
49 sway_log(SWAY_ERROR, "Failed to compile keysym translation XKB keymap");
50 return NULL;
51 }
52
47 return xkb_state_new(xkb_keymap); 53 return xkb_state_new(xkb_keymap);
48} 54}
49 55
50static void keysym_translation_state_destroy( 56static void keysym_translation_state_destroy(
51 struct xkb_state *state) { 57 struct xkb_state *state) {
58 if (state == NULL) {
59 return;
60 }
52 xkb_keymap_unref(xkb_state_get_keymap(state)); 61 xkb_keymap_unref(xkb_state_get_keymap(state));
53 xkb_state_unref(state); 62 xkb_state_unref(state);
54} 63}
@@ -336,8 +345,14 @@ static void config_defaults(struct sway_config *config) {
336 345
337 // The keysym to keycode translation 346 // The keysym to keycode translation
338 struct xkb_rule_names rules = {0}; 347 struct xkb_rule_names rules = {0};
339 config->keysym_translation_state = 348 config->keysym_translation_state = keysym_translation_state_create(rules, 0);
340 keysym_translation_state_create(rules); 349 if (config->keysym_translation_state == NULL) {
350 config->keysym_translation_state = keysym_translation_state_create(rules,
351 XKB_CONTEXT_NO_ENVIRONMENT_NAMES);
352 }
353 if (config->keysym_translation_state == NULL) {
354 goto cleanup;
355 }
341 356
342 return; 357 return;
343cleanup: 358cleanup:
@@ -352,13 +367,7 @@ static char *config_path(const char *prefix, const char *config_folder) {
352 if (!prefix || !prefix[0] || !config_folder || !config_folder[0]) { 367 if (!prefix || !prefix[0] || !config_folder || !config_folder[0]) {
353 return NULL; 368 return NULL;
354 } 369 }
355 370 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} 371}
363 372
364static char *get_config_path(void) { 373static char *get_config_path(void) {
@@ -368,10 +377,7 @@ static char *get_config_path(void) {
368 377
369 const char *config_home = getenv("XDG_CONFIG_HOME"); 378 const char *config_home = getenv("XDG_CONFIG_HOME");
370 if ((config_home == NULL || config_home[0] == '\0') && home != NULL) { 379 if ((config_home == NULL || config_home[0] == '\0') && home != NULL) {
371 size_t size_fallback = 1 + strlen(home) + strlen("/.config"); 380 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; 381 config_home = config_home_fallback;
376 } 382 }
377 383
@@ -475,6 +481,11 @@ bool load_main_config(const char *file, bool is_active, bool validating) {
475 old_config->xwayland ? "enabled" : "disabled"); 481 old_config->xwayland ? "enabled" : "disabled");
476 config->xwayland = old_config->xwayland; 482 config->xwayland = old_config->xwayland;
477 483
484 // primary_selection can only be enabled/disabled at launch
485 sway_log(SWAY_DEBUG, "primary_selection will remain %s",
486 old_config->primary_selection ? "enabled" : "disabled");
487 config->primary_selection = old_config->primary_selection;
488
478 if (!config->validating) { 489 if (!config->validating) {
479 if (old_config->swaybg_client != NULL) { 490 if (old_config->swaybg_client != NULL) {
480 wl_client_destroy(old_config->swaybg_client); 491 wl_client_destroy(old_config->swaybg_client);
@@ -494,56 +505,7 @@ bool load_main_config(const char *file, bool is_active, bool validating) {
494 505
495 config->reading = true; 506 config->reading = true;
496 507
497 // Read security configs 508 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 509
548 if (validating) { 510 if (validating) {
549 free_config(config); 511 free_config(config);
@@ -571,7 +533,7 @@ bool load_main_config(const char *file, bool is_active, bool validating) {
571 } 533 }
572 sway_switch_retrigger_bindings_for_all(); 534 sway_switch_retrigger_bindings_for_all();
573 535
574 reset_outputs(); 536 apply_all_output_configs();
575 spawn_swaybg(); 537 spawn_swaybg();
576 538
577 config->reloading = false; 539 config->reloading = false;
@@ -1037,8 +999,12 @@ void translate_keysyms(struct input_config *input_config) {
1037 999
1038 struct xkb_rule_names rules = {0}; 1000 struct xkb_rule_names rules = {0};
1039 input_config_fill_rule_names(input_config, &rules); 1001 input_config_fill_rule_names(input_config, &rules);
1040 config->keysym_translation_state = 1002 config->keysym_translation_state = keysym_translation_state_create(rules, 0);
1041 keysym_translation_state_create(rules); 1003 if (config->keysym_translation_state == NULL) {
1004 sway_log(SWAY_ERROR, "Failed to create keysym translation XKB state "
1005 "for device '%s'", input_config->identifier);
1006 return;
1007 }
1042 1008
1043 for (int i = 0; i < config->modes->length; ++i) { 1009 for (int i = 0; i < config->modes->length; ++i) {
1044 struct sway_mode *mode = config->modes->items[i]; 1010 struct sway_mode *mode = config->modes->items[i];
diff --git a/sway/config/bar.c b/sway/config/bar.c
index a8389244..ecefb61a 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>
@@ -13,6 +12,7 @@
13#include "sway/config.h" 12#include "sway/config.h"
14#include "sway/input/keyboard.h" 13#include "sway/input/keyboard.h"
15#include "sway/output.h" 14#include "sway/output.h"
15#include "sway/server.h"
16#include "config.h" 16#include "config.h"
17#include "list.h" 17#include "list.h"
18#include "log.h" 18#include "log.h"
diff --git a/sway/config/input.c b/sway/config/input.c
index 44c2be28..613270df 100644
--- a/sway/config/input.c
+++ b/sway/config/input.c
@@ -1,9 +1,9 @@
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>
5#include "sway/config.h" 4#include "sway/config.h"
6#include "sway/input/keyboard.h" 5#include "sway/input/keyboard.h"
6#include "sway/server.h"
7#include "log.h" 7#include "log.h"
8 8
9struct input_config *new_input_config(const char* identifier) { 9struct input_config *new_input_config(const char* identifier) {
diff --git a/sway/config/output.c b/sway/config/output.c
index 1a5215fe..16be49c8 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,9 +9,11 @@
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"
16#include "sway/server.h"
16#include "sway/tree/root.h" 17#include "sway/tree/root.h"
17#include "log.h" 18#include "log.h"
18#include "util.h" 19#include "util.h"
@@ -75,11 +76,78 @@ struct output_config *new_output_config(const char *name) {
75 oc->max_render_time = -1; 76 oc->max_render_time = -1;
76 oc->adaptive_sync = -1; 77 oc->adaptive_sync = -1;
77 oc->render_bit_depth = RENDER_BIT_DEPTH_DEFAULT; 78 oc->render_bit_depth = RENDER_BIT_DEPTH_DEFAULT;
79 oc->set_color_transform = false;
80 oc->color_transform = NULL;
78 oc->power = -1; 81 oc->power = -1;
79 return oc; 82 return oc;
80} 83}
81 84
82void merge_output_config(struct output_config *dst, struct output_config *src) { 85// supersede_output_config clears all fields in dst that were set in src
86static void supersede_output_config(struct output_config *dst, struct output_config *src) {
87 if (src->enabled != -1) {
88 dst->enabled = -1;
89 }
90 if (src->width != -1) {
91 dst->width = -1;
92 }
93 if (src->height != -1) {
94 dst->height = -1;
95 }
96 if (src->x != -1) {
97 dst->x = -1;
98 }
99 if (src->y != -1) {
100 dst->y = -1;
101 }
102 if (src->scale != -1) {
103 dst->scale = -1;
104 }
105 if (src->scale_filter != SCALE_FILTER_DEFAULT) {
106 dst->scale_filter = SCALE_FILTER_DEFAULT;
107 }
108 if (src->subpixel != WL_OUTPUT_SUBPIXEL_UNKNOWN) {
109 dst->subpixel = WL_OUTPUT_SUBPIXEL_UNKNOWN;
110 }
111 if (src->refresh_rate != -1) {
112 dst->refresh_rate = -1;
113 }
114 if (src->custom_mode != -1) {
115 dst->custom_mode = -1;
116 }
117 if (src->drm_mode.type != (uint32_t) -1) {
118 dst->drm_mode.type = -1;
119 }
120 if (src->transform != -1) {
121 dst->transform = -1;
122 }
123 if (src->max_render_time != -1) {
124 dst->max_render_time = -1;
125 }
126 if (src->adaptive_sync != -1) {
127 dst->adaptive_sync = -1;
128 }
129 if (src->render_bit_depth != RENDER_BIT_DEPTH_DEFAULT) {
130 dst->render_bit_depth = RENDER_BIT_DEPTH_DEFAULT;
131 }
132 if (src->background) {
133 free(dst->background);
134 dst->background = NULL;
135 }
136 if (src->background_option) {
137 free(dst->background_option);
138 dst->background_option = NULL;
139 }
140 if (src->background_fallback) {
141 free(dst->background_fallback);
142 dst->background_fallback = NULL;
143 }
144 if (src->power != -1) {
145 dst->power = -1;
146 }
147}
148
149// merge_output_config sets all fields in dst that were set in src
150static void merge_output_config(struct output_config *dst, struct output_config *src) {
83 if (src->enabled != -1) { 151 if (src->enabled != -1) {
84 dst->enabled = src->enabled; 152 dst->enabled = src->enabled;
85 } 153 }
@@ -125,6 +193,14 @@ void merge_output_config(struct output_config *dst, struct output_config *src) {
125 if (src->render_bit_depth != RENDER_BIT_DEPTH_DEFAULT) { 193 if (src->render_bit_depth != RENDER_BIT_DEPTH_DEFAULT) {
126 dst->render_bit_depth = src->render_bit_depth; 194 dst->render_bit_depth = src->render_bit_depth;
127 } 195 }
196 if (src->set_color_transform) {
197 if (src->color_transform) {
198 wlr_color_transform_ref(src->color_transform);
199 }
200 wlr_color_transform_unref(dst->color_transform);
201 dst->set_color_transform = true;
202 dst->color_transform = src->color_transform;
203 }
128 if (src->background) { 204 if (src->background) {
129 free(dst->background); 205 free(dst->background);
130 dst->background = strdup(src->background); 206 dst->background = strdup(src->background);
@@ -142,94 +218,42 @@ void merge_output_config(struct output_config *dst, struct output_config *src) {
142 } 218 }
143} 219}
144 220
145static void merge_wildcard_on_all(struct output_config *wildcard) { 221void store_output_config(struct output_config *oc) {
146 for (int i = 0; i < config->output_configs->length; i++) { 222 bool merged = false;
147 struct output_config *oc = config->output_configs->items[i]; 223 bool wildcard = strcmp(oc->name, "*") == 0;
148 if (strcmp(wildcard->name, oc->name) != 0) { 224 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 225
161 const char *name = output->wlr_output->name;
162 char id[128]; 226 char id[128];
163 output_get_identifier(id, sizeof(id), output); 227 if (output) {
164 228 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 } 229 }
169 230
170 int i = list_seq_find(config->output_configs, output_name_cmp, id_on_name); 231 for (int i = 0; i < config->output_configs->length; i++) {
171 if (i >= 0) { 232 struct output_config *old = config->output_configs->items[i];
172 sway_log(SWAY_DEBUG, "Merging on top of existing id on name config"); 233
173 merge_output_config(config->output_configs->items[i], oc); 234 // If the old config matches the new config's name, regardless of
174 } else { 235 // 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 236 // 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); 237 // old wildcard configs.
177 int ii = list_seq_find(config->output_configs, output_name_cmp, id); 238 if (strcmp(old->name, oc->name) == 0) {
178 if ((ni >= 0 && ii >= 0) || (ni >= 0 && strcmp(oc->name, id) == 0) 239 merge_output_config(old, oc);
179 || (ii >= 0 && strcmp(oc->name, name) == 0)) { 240 merged = true;
180 struct output_config *ion_oc = new_output_config(id_on_name); 241 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 } 242 }
198 }
199 free(id_on_name);
200}
201 243
202struct output_config *store_output_config(struct output_config *oc) { 244 // If the new config is a wildcard config we supersede all non-wildcard
203 bool wildcard = strcmp(oc->name, "*") == 0; 245 // configs. Old wildcard configs have already been handled above.
204 if (wildcard) { 246 if (wildcard) {
205 merge_wildcard_on_all(oc); 247 supersede_output_config(old, oc);
206 } else { 248 continue;
207 merge_id_on_name(oc); 249 }
208 }
209 250
210 int i = list_seq_find(config->output_configs, output_name_cmp, oc->name); 251 // If the new config matches an output's name, and the old config
211 if (i >= 0) { 252 // matches on that output's identifier, supersede it.
212 sway_log(SWAY_DEBUG, "Merging on top of existing output config"); 253 if (output && strcmp(old->name, id) == 0 &&
213 struct output_config *current = config->output_configs->items[i]; 254 strcmp(oc->name, output->wlr_output->name) == 0) {
214 merge_output_config(current, oc); 255 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 } 256 }
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 } 257 }
234 258
235 sway_log(SWAY_DEBUG, "Config stored for output %s (enabled: %d) (%dx%d@%fHz " 259 sway_log(SWAY_DEBUG, "Config stored for output %s (enabled: %d) (%dx%d@%fHz "
@@ -240,7 +264,13 @@ struct output_config *store_output_config(struct output_config *oc) {
240 oc->transform, oc->background, oc->background_option, oc->power, 264 oc->transform, oc->background, oc->background_option, oc->power,
241 oc->max_render_time); 265 oc->max_render_time);
242 266
243 return oc; 267 // If the configuration was not merged into an existing configuration, add
268 // it to the list. Otherwise we're done with it and can free it.
269 if (!merged) {
270 list_add(config->output_configs, oc);
271 } else {
272 free_output_config(oc);
273 }
244} 274}
245 275
246static void set_mode(struct wlr_output *output, struct wlr_output_state *pending, 276static void set_mode(struct wlr_output *output, struct wlr_output_state *pending,
@@ -367,22 +397,18 @@ static int compute_default_scale(struct wlr_output *output,
367 return 2; 397 return 2;
368} 398}
369 399
370/* Lists of formats to try, in order, when a specific render bit depth has 400static bool render_format_is_10bit(uint32_t render_format) {
371 * been asked for. The second to last format in each list should always 401 return render_format == DRM_FORMAT_XRGB2101010 ||
372 * be XRGB8888, as a reliable backup in case the others are not available; 402 render_format == DRM_FORMAT_XBGR2101010;
373 * the last should be DRM_FORMAT_INVALID, to indicate the end of the list. */ 403}
374static const uint32_t *bit_depth_preferences[] = { 404
375 [RENDER_BIT_DEPTH_8] = (const uint32_t []){ 405static bool render_format_is_bgr(uint32_t fmt) {
376 DRM_FORMAT_XRGB8888, 406 return fmt == DRM_FORMAT_XBGR2101010 || fmt == DRM_FORMAT_XBGR8888;
377 DRM_FORMAT_INVALID, 407}
378 }, 408
379 [RENDER_BIT_DEPTH_10] = (const uint32_t []){ 409static bool output_config_is_disabling(struct output_config *oc) {
380 DRM_FORMAT_XRGB2101010, 410 return oc && (!oc->enabled || oc->power == 0);
381 DRM_FORMAT_XBGR2101010, 411}
382 DRM_FORMAT_XRGB8888,
383 DRM_FORMAT_INVALID,
384 },
385};
386 412
387static void queue_output_config(struct output_config *oc, 413static void queue_output_config(struct output_config *oc,
388 struct sway_output *output, struct wlr_output_state *pending) { 414 struct sway_output *output, struct wlr_output_state *pending) {
@@ -392,7 +418,7 @@ static void queue_output_config(struct output_config *oc,
392 418
393 struct wlr_output *wlr_output = output->wlr_output; 419 struct wlr_output *wlr_output = output->wlr_output;
394 420
395 if (oc && (!oc->enabled || oc->power == 0)) { 421 if (output_config_is_disabling(oc)) {
396 sway_log(SWAY_DEBUG, "Turning off output %s", wlr_output->name); 422 sway_log(SWAY_DEBUG, "Turning off output %s", wlr_output->name);
397 wlr_output_state_set_enabled(pending, false); 423 wlr_output_state_set_enabled(pending, false);
398 return; 424 return;
@@ -415,22 +441,6 @@ static void queue_output_config(struct output_config *oc,
415 struct wlr_output_mode *preferred_mode = 441 struct wlr_output_mode *preferred_mode =
416 wlr_output_preferred_mode(wlr_output); 442 wlr_output_preferred_mode(wlr_output);
417 wlr_output_state_set_mode(pending, preferred_mode); 443 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 } 444 }
435 445
436 if (oc && (oc->subpixel != WL_OUTPUT_SUBPIXEL_UNKNOWN || config->reloading)) { 446 if (oc && (oc->subpixel != WL_OUTPUT_SUBPIXEL_UNKNOWN || config->reloading)) {
@@ -449,7 +459,7 @@ static void queue_output_config(struct output_config *oc,
449#endif 459#endif
450 } 460 }
451 if (wlr_output->transform != tr) { 461 if (wlr_output->transform != tr) {
452 sway_log(SWAY_DEBUG, "Set %s transform to %d", oc->name, tr); 462 sway_log(SWAY_DEBUG, "Set %s transform to %d", wlr_output->name, tr);
453 wlr_output_state_set_transform(pending, tr); 463 wlr_output_state_set_transform(pending, tr);
454 } 464 }
455 465
@@ -481,54 +491,27 @@ static void queue_output_config(struct output_config *oc,
481 sway_log(SWAY_DEBUG, "Set %s adaptive sync to %d", wlr_output->name, 491 sway_log(SWAY_DEBUG, "Set %s adaptive sync to %d", wlr_output->name,
482 oc->adaptive_sync); 492 oc->adaptive_sync);
483 wlr_output_state_set_adaptive_sync_enabled(pending, oc->adaptive_sync == 1); 493 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 } 494 }
489 495
490 if (oc && oc->render_bit_depth != RENDER_BIT_DEPTH_DEFAULT) { 496 if (oc && oc->render_bit_depth != RENDER_BIT_DEPTH_DEFAULT) {
491 const uint32_t *fmts = bit_depth_preferences[oc->render_bit_depth]; 497 if (oc->render_bit_depth == RENDER_BIT_DEPTH_10 &&
492 assert(fmts); 498 render_format_is_10bit(output->wlr_output->render_format)) {
493 499 // 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++) { 500 wlr_output_state_set_render_format(pending, output->wlr_output->render_format);
495 wlr_output_state_set_render_format(pending, fmts[i]); 501 } else if (oc->render_bit_depth == RENDER_BIT_DEPTH_10) {
496 if (wlr_output_test_state(wlr_output, pending)) { 502 wlr_output_state_set_render_format(pending, DRM_FORMAT_XRGB2101010);
497 break; 503 } else {
498 } 504 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 } 505 }
504 } 506 }
505} 507}
506 508
507bool apply_output_config(struct output_config *oc, struct sway_output *output) { 509static bool finalize_output_config(struct output_config *oc, struct sway_output *output) {
508 if (output == root->fallback_output) { 510 if (output == root->fallback_output) {
509 return false; 511 return false;
510 } 512 }
511 513
512 struct wlr_output *wlr_output = output->wlr_output; 514 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) { 515 if (oc && !oc->enabled) {
533 sway_log(SWAY_DEBUG, "Disabling output %s", oc->name); 516 sway_log(SWAY_DEBUG, "Disabling output %s", oc->name);
534 if (output->enabled) { 517 if (output->enabled) {
@@ -584,23 +567,14 @@ bool apply_output_config(struct output_config *oc, struct sway_output *output) {
584 output->max_render_time = oc->max_render_time; 567 output->max_render_time = oc->max_render_time;
585 } 568 }
586 569
587 // Reconfigure all devices, since input config may have been applied before 570 if (oc && oc->set_color_transform) {
588 // this output came online, and some config items (like map_to_output) are 571 if (oc->color_transform) {
589 // dependent on an output being present. 572 wlr_color_transform_ref(oc->color_transform);
590 input_manager_configure_all_input_mappings(); 573 }
591 // Reconfigure the cursor images, since the scale may have changed. 574 wlr_color_transform_unref(output->color_transform);
592 input_manager_configure_xcursor(); 575 output->color_transform = oc->color_transform;
593 return true;
594}
595
596bool test_output_config(struct output_config *oc, struct sway_output *output) {
597 if (output == root->fallback_output) {
598 return false;
599 } 576 }
600 577 return true;
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} 578}
605 579
606static void default_output_config(struct output_config *oc, 580static void default_output_config(struct output_config *oc,
@@ -622,140 +596,416 @@ static void default_output_config(struct output_config *oc,
622 oc->max_render_time = 0; 596 oc->max_render_time = 0;
623} 597}
624 598
625static struct output_config *get_output_config(char *identifier, 599// find_output_config returns a merged output_config containing all stored
626 struct sway_output *sway_output) { 600// configuration that applies to the specified output.
601struct output_config *find_output_config(struct sway_output *sway_output) {
627 const char *name = sway_output->wlr_output->name; 602 const char *name = sway_output->wlr_output->name;
603 struct output_config *oc = NULL;
604
605 struct output_config *result = new_output_config(name);
606 if (config->reloading) {
607 default_output_config(result, sway_output->wlr_output);
608 }
628 609
629 struct output_config *oc_id_on_name = NULL; 610 char id[128];
630 struct output_config *oc_name = NULL; 611 output_get_identifier(id, sizeof(id), sway_output);
631 struct output_config *oc_id = NULL;
632 612
633 char *id_on_name = format_str("%s on %s", identifier, name); 613 int i;
634 int i = list_seq_find(config->output_configs, output_name_cmp, id_on_name); 614 bool match = false;
635 if (i >= 0) { 615 if ((i = list_seq_find(config->output_configs, output_name_cmp, "*")) >= 0) {
636 oc_id_on_name = config->output_configs->items[i]; 616 match = true;
637 } else { 617 oc = config->output_configs->items[i];
638 i = list_seq_find(config->output_configs, output_name_cmp, name); 618 merge_output_config(result, oc);
639 if (i >= 0) { 619 }
640 oc_name = config->output_configs->items[i]; 620 if ((i = list_seq_find(config->output_configs, output_name_cmp, name)) >= 0) {
621 match = true;
622 oc = config->output_configs->items[i];
623 merge_output_config(result, oc);
624 }
625 if ((i = list_seq_find(config->output_configs, output_name_cmp, id)) >= 0) {
626 match = true;
627 oc = config->output_configs->items[i];
628 merge_output_config(result, oc);
629 }
630
631 if (!match && !config->reloading) {
632 // No name, identifier, or wildcard config. Since we are not
633 // reloading with defaults, the output config will be empty, so
634 // just return NULL
635 free_output_config(result);
636 return NULL;
637 }
638
639 return result;
640}
641
642static bool config_has_auto_mode(struct output_config *oc) {
643 if (!oc) {
644 return true;
645 }
646 if (oc->drm_mode.type != 0 && oc->drm_mode.type != (uint32_t)-1) {
647 return true;
648 } else if (oc->width > 0 && oc->height > 0) {
649 return true;
650 }
651 return false;
652}
653
654struct search_context {
655 struct wlr_output_swapchain_manager *swapchain_mgr;
656 struct wlr_backend_output_state *states;
657 struct matched_output_config *configs;
658 size_t configs_len;
659 bool degrade_to_off;
660};
661
662static void dump_output_state(struct wlr_output *wlr_output, struct wlr_output_state *state) {
663 sway_log(SWAY_DEBUG, "Output state for %s", wlr_output->name);
664 if (state->committed & WLR_OUTPUT_STATE_ENABLED) {
665 sway_log(SWAY_DEBUG, " enabled: %s", state->enabled ? "yes" : "no");
666 }
667 if (state->committed & WLR_OUTPUT_STATE_RENDER_FORMAT) {
668 sway_log(SWAY_DEBUG, " render_format: %d", state->render_format);
669 }
670 if (state->committed & WLR_OUTPUT_STATE_MODE) {
671 if (state->mode_type == WLR_OUTPUT_STATE_MODE_CUSTOM) {
672 sway_log(SWAY_DEBUG, " custom mode: %dx%d@%dmHz",
673 state->custom_mode.width, state->custom_mode.height, state->custom_mode.refresh);
674 } else {
675 sway_log(SWAY_DEBUG, " mode: %dx%d@%dmHz%s",
676 state->mode->width, state->mode->height, state->mode->refresh,
677 state->mode->preferred ? " (preferred)" : "");
641 } 678 }
679 }
680 if (state->committed & WLR_OUTPUT_STATE_ADAPTIVE_SYNC_ENABLED) {
681 sway_log(SWAY_DEBUG, " adaptive_sync: %s",
682 state->adaptive_sync_enabled ? "enabled": "disabled");
683 }
684}
685
686static bool search_valid_config(struct search_context *ctx, size_t output_idx);
687
688static void reset_output_state(struct wlr_output_state *state) {
689 wlr_output_state_finish(state);
690 wlr_output_state_init(state);
691 state->committed = 0;
692}
693
694static void clear_later_output_states(struct wlr_backend_output_state *states,
695 size_t configs_len, size_t output_idx) {
642 696
643 i = list_seq_find(config->output_configs, output_name_cmp, identifier); 697 // Clear and disable all output states after this one to avoid conflict
644 if (i >= 0) { 698 // with previous tests.
645 oc_id = config->output_configs->items[i]; 699 for (size_t idx = output_idx+1; idx < configs_len; idx++) {
700 struct wlr_backend_output_state *backend_state = &states[idx];
701 struct wlr_output_state *state = &backend_state->base;
702
703 reset_output_state(state);
704 wlr_output_state_set_enabled(state, false);
705 }
706}
707
708static bool search_finish(struct search_context *ctx, size_t output_idx) {
709 struct wlr_backend_output_state *backend_state = &ctx->states[output_idx];
710 struct wlr_output_state *state = &backend_state->base;
711 struct wlr_output *wlr_output = backend_state->output;
712
713 clear_later_output_states(ctx->states, ctx->configs_len, output_idx);
714 dump_output_state(wlr_output, state);
715 return wlr_output_swapchain_manager_prepare(ctx->swapchain_mgr, ctx->states, ctx->configs_len) &&
716 search_valid_config(ctx, output_idx+1);
717}
718
719static bool search_adaptive_sync(struct search_context *ctx, size_t output_idx) {
720 struct matched_output_config *cfg = &ctx->configs[output_idx];
721 struct wlr_backend_output_state *backend_state = &ctx->states[output_idx];
722 struct wlr_output_state *state = &backend_state->base;
723
724 if (cfg->config && cfg->config->adaptive_sync == 1) {
725 wlr_output_state_set_adaptive_sync_enabled(state, true);
726 if (search_finish(ctx, output_idx)) {
727 return true;
646 } 728 }
647 } 729 }
730 if (!cfg->config || cfg->config->adaptive_sync != -1) {
731 wlr_output_state_set_adaptive_sync_enabled(state, false);
732 if (search_finish(ctx, output_idx)) {
733 return true;
734 }
735 }
736 // If adaptive sync has not been set, or fallback in case we are on a
737 // backend that cannot disable adaptive sync such as the wayland backend.
738 state->committed &= ~WLR_OUTPUT_STATE_ADAPTIVE_SYNC_ENABLED;
739 return search_finish(ctx, output_idx);
740}
648 741
649 struct output_config *result = new_output_config("temp"); 742static bool search_mode(struct search_context *ctx, size_t output_idx) {
650 if (config->reloading) { 743 struct matched_output_config *cfg = &ctx->configs[output_idx];
651 default_output_config(result, sway_output->wlr_output); 744 struct wlr_backend_output_state *backend_state = &ctx->states[output_idx];
745 struct wlr_output_state *state = &backend_state->base;
746 struct wlr_output *wlr_output = backend_state->output;
747
748 if (!config_has_auto_mode(cfg->config)) {
749 return search_adaptive_sync(ctx, output_idx);
652 } 750 }
653 if (oc_id_on_name) { 751
654 // Already have an identifier on name config, use that 752 struct wlr_output_mode *preferred_mode = wlr_output_preferred_mode(wlr_output);
655 free(result->name); 753 if (preferred_mode) {
656 result->name = strdup(id_on_name); 754 wlr_output_state_set_mode(state, preferred_mode);
657 merge_output_config(result, oc_id_on_name); 755 if (search_adaptive_sync(ctx, output_idx)) {
658 } else if (oc_name && oc_id) { 756 return true;
659 // Generate a config named `<identifier> on <name>` which contains a
660 // merged copy of the identifier on name. This will make sure that both
661 // identifier and name configs are respected, with identifier getting
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 } 757 }
703 } 758 }
704 759
705 free(id_on_name); 760 if (wl_list_empty(&wlr_output->modes)) {
706 return result; 761 state->committed &= ~WLR_OUTPUT_STATE_MODE;
762 return search_adaptive_sync(ctx, output_idx);
763 }
764
765 struct wlr_output_mode *mode;
766 wl_list_for_each(mode, &backend_state->output->modes, link) {
767 if (mode == preferred_mode) {
768 continue;
769 }
770 wlr_output_state_set_mode(state, mode);
771 if (search_adaptive_sync(ctx, output_idx)) {
772 return true;
773 }
774 }
775
776 return false;
707} 777}
708 778
709struct output_config *find_output_config(struct sway_output *output) { 779static bool search_render_format(struct search_context *ctx, size_t output_idx) {
710 char id[128]; 780 struct matched_output_config *cfg = &ctx->configs[output_idx];
711 output_get_identifier(id, sizeof(id), output); 781 struct wlr_backend_output_state *backend_state = &ctx->states[output_idx];
712 return get_output_config(id, output); 782 struct wlr_output_state *state = &backend_state->base;
783 struct wlr_output *wlr_output = backend_state->output;
784
785 uint32_t fmts[] = {
786 DRM_FORMAT_XRGB2101010,
787 DRM_FORMAT_XBGR2101010,
788 DRM_FORMAT_XRGB8888,
789 DRM_FORMAT_INVALID,
790 };
791 if (render_format_is_bgr(wlr_output->render_format)) {
792 // Start with BGR in the unlikely event that we previously required it.
793 fmts[0] = DRM_FORMAT_XBGR2101010;
794 fmts[1] = DRM_FORMAT_XRGB2101010;
795 }
796
797 const struct wlr_drm_format_set *primary_formats =
798 wlr_output_get_primary_formats(wlr_output, WLR_BUFFER_CAP_DMABUF);
799 bool need_10bit = cfg->config && cfg->config->render_bit_depth == RENDER_BIT_DEPTH_10;
800 for (size_t idx = 0; fmts[idx] != DRM_FORMAT_INVALID; idx++) {
801 if (!need_10bit && render_format_is_10bit(fmts[idx])) {
802 continue;
803 }
804 if (!wlr_drm_format_set_get(primary_formats, fmts[idx])) {
805 // This is not a supported format for this output
806 continue;
807 }
808 wlr_output_state_set_render_format(state, fmts[idx]);
809 if (search_mode(ctx, output_idx)) {
810 return true;
811 }
812 }
813 return false;
713} 814}
714 815
715void apply_output_config_to_outputs(struct output_config *oc) { 816static bool search_valid_config(struct search_context *ctx, size_t output_idx) {
716 // Try to find the output container and apply configuration now. If 817 if (output_idx >= ctx->configs_len) {
717 // this is during startup then there will be no container and config 818 // We reached the end of the search, all good!
718 // will be applied during normal "new output" event from wlroots. 819 return true;
719 bool wildcard = strcmp(oc->name, "*") == 0; 820 }
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 821
735 if (!wildcard) { 822 struct matched_output_config *cfg = &ctx->configs[output_idx];
736 // Stop looking if the output config isn't applicable to all 823 struct wlr_backend_output_state *backend_state = &ctx->states[output_idx];
737 // outputs 824 struct wlr_output_state *state = &backend_state->base;
738 break; 825 struct wlr_output *wlr_output = backend_state->output;
739 } 826
827 if (!output_config_is_disabling(cfg->config)) {
828 // Search through our possible configurations, doing a depth-first
829 // through render_format, modes, adaptive_sync and the next output's
830 // config.
831 queue_output_config(cfg->config, cfg->output, &backend_state->base);
832 if (search_render_format(ctx, output_idx)) {
833 return true;
834 } else if (!ctx->degrade_to_off) {
835 return false;
740 } 836 }
837 // We could not get anything to work, try to disable this output to see
838 // if we can at least make the outputs before us work.
839 sway_log(SWAY_DEBUG, "Unable to find valid config with output %s, disabling",
840 wlr_output->name);
841 reset_output_state(state);
842 }
843
844 wlr_output_state_set_enabled(state, false);
845 return search_finish(ctx, output_idx);
846}
847
848static int compare_matched_output_config_priority(const void *a, const void *b) {
849
850 const struct matched_output_config *amc = a;
851 const struct matched_output_config *bmc = b;
852 bool a_disabling = output_config_is_disabling(amc->config);
853 bool b_disabling = output_config_is_disabling(bmc->config);
854 bool a_enabled = amc->output->enabled;
855 bool b_enabled = bmc->output->enabled;
856
857 // We want to give priority to existing enabled outputs. To do so, we want
858 // the configuration order to be:
859 // 1. Existing, enabled outputs
860 // 2. Outputs that need to be enabled
861 // 3. Disabled or disabling outputs
862 if (a_enabled && !a_disabling) {
863 return -1;
864 } else if (b_enabled && !b_disabling) {
865 return 1;
866 } else if (b_disabling && !a_disabling) {
867 return -1;
868 } else if (a_disabling && !b_disabling) {
869 return 1;
870 }
871 return 0;
872}
873
874void sort_output_configs_by_priority(struct matched_output_config *configs,
875 size_t configs_len) {
876 qsort(configs, configs_len, sizeof(*configs), compare_matched_output_config_priority);
877}
878
879bool apply_output_configs(struct matched_output_config *configs,
880 size_t configs_len, bool test_only, bool degrade_to_off) {
881 struct wlr_backend_output_state *states = calloc(configs_len, sizeof(*states));
882 if (!states) {
883 return false;
884 }
885
886 sway_log(SWAY_DEBUG, "Committing %zd outputs", configs_len);
887 for (size_t idx = 0; idx < configs_len; idx++) {
888 struct matched_output_config *cfg = &configs[idx];
889 struct wlr_backend_output_state *backend_state = &states[idx];
890
891 backend_state->output = cfg->output->wlr_output;
892 wlr_output_state_init(&backend_state->base);
893
894 sway_log(SWAY_DEBUG, "Preparing config for %s",
895 cfg->output->wlr_output->name);
896 queue_output_config(cfg->config, cfg->output, &backend_state->base);
897 }
898
899 struct wlr_output_swapchain_manager swapchain_mgr;
900 wlr_output_swapchain_manager_init(&swapchain_mgr, server.backend);
901
902 bool ok = wlr_output_swapchain_manager_prepare(&swapchain_mgr, states, configs_len);
903 if (!ok) {
904 sway_log(SWAY_ERROR, "Requested backend configuration failed, searching for valid fallbacks");
905 struct search_context ctx = {
906 .swapchain_mgr = &swapchain_mgr,
907 .states = states,
908 .configs = configs,
909 .configs_len = configs_len,
910 .degrade_to_off = degrade_to_off,
911 };
912 if (!search_valid_config(&ctx, 0)) {
913 sway_log(SWAY_ERROR, "Search for valid config failed");
914 goto out;
915 }
916 }
917
918 if (test_only) {
919 // The swapchain manager already did a test for us
920 goto out;
741 } 921 }
742 922
923 for (size_t idx = 0; idx < configs_len; idx++) {
924 struct matched_output_config *cfg = &configs[idx];
925 struct wlr_backend_output_state *backend_state = &states[idx];
926
927 struct wlr_scene_output_state_options opts = {
928 .swapchain = wlr_output_swapchain_manager_get_swapchain(
929 &swapchain_mgr, backend_state->output),
930 .color_transform = cfg->output->color_transform,
931 };
932 struct wlr_scene_output *scene_output = cfg->output->scene_output;
933 struct wlr_output_state *state = &backend_state->base;
934 if (!wlr_scene_output_build_state(scene_output, state, &opts)) {
935 sway_log(SWAY_ERROR, "Building output state for '%s' failed",
936 backend_state->output->name);
937 goto out;
938 }
939 }
940
941 ok = wlr_backend_commit(server.backend, states, configs_len);
942 if (!ok) {
943 sway_log(SWAY_ERROR, "Backend commit failed");
944 goto out;
945 }
946
947 sway_log(SWAY_DEBUG, "Commit of %zd outputs succeeded", configs_len);
948
949 wlr_output_swapchain_manager_apply(&swapchain_mgr);
950
951 for (size_t idx = 0; idx < configs_len; idx++) {
952 struct matched_output_config *cfg = &configs[idx];
953 sway_log(SWAY_DEBUG, "Finalizing config for %s",
954 cfg->output->wlr_output->name);
955 finalize_output_config(cfg->config, cfg->output);
956 }
957
958out:
959 wlr_output_swapchain_manager_finish(&swapchain_mgr);
960 for (size_t idx = 0; idx < configs_len; idx++) {
961 struct wlr_backend_output_state *backend_state = &states[idx];
962 wlr_output_state_finish(&backend_state->base);
963 }
964 free(states);
965
966 // Reconfigure all devices, since input config may have been applied before
967 // this output came online, and some config items (like map_to_output) are
968 // dependent on an output being present.
969 input_manager_configure_all_input_mappings();
970 // Reconfigure the cursor images, since the scale may have changed.
971 input_manager_configure_xcursor();
972
743 struct sway_seat *seat; 973 struct sway_seat *seat;
744 wl_list_for_each(seat, &server.input->seats, link) { 974 wl_list_for_each(seat, &server.input->seats, link) {
745 wlr_seat_pointer_notify_clear_focus(seat->wlr_seat); 975 wlr_seat_pointer_notify_clear_focus(seat->wlr_seat);
746 cursor_rebase(seat->cursor); 976 cursor_rebase(seat->cursor);
747 } 977 }
978
979 return ok;
748} 980}
749 981
750void reset_outputs(void) { 982void apply_all_output_configs(void) {
751 struct output_config *oc = NULL; 983 size_t configs_len = wl_list_length(&root->all_outputs);
752 int i = list_seq_find(config->output_configs, output_name_cmp, "*"); 984 struct matched_output_config *configs = calloc(configs_len, sizeof(*configs));
753 if (i >= 0) { 985 if (!configs) {
754 oc = config->output_configs->items[i]; 986 return;
755 } else {
756 oc = store_output_config(new_output_config("*"));
757 } 987 }
758 apply_output_config_to_outputs(oc); 988
989 int config_idx = 0;
990 struct sway_output *sway_output;
991 wl_list_for_each(sway_output, &root->all_outputs, link) {
992 if (sway_output == root->fallback_output) {
993 configs_len--;
994 continue;
995 }
996
997 struct matched_output_config *config = &configs[config_idx++];
998 config->output = sway_output;
999 config->config = find_output_config(sway_output);
1000 }
1001
1002 sort_output_configs_by_priority(configs, configs_len);
1003 apply_output_configs(configs, configs_len, false, true);
1004 for (size_t idx = 0; idx < configs_len; idx++) {
1005 struct matched_output_config *cfg = &configs[idx];
1006 free_output_config(cfg->config);
1007 }
1008 free(configs);
759} 1009}
760 1010
761void free_output_config(struct output_config *oc) { 1011void free_output_config(struct output_config *oc) {
@@ -765,6 +1015,7 @@ void free_output_config(struct output_config *oc) {
765 free(oc->name); 1015 free(oc->name);
766 free(oc->background); 1016 free(oc->background);
767 free(oc->background_option); 1017 free(oc->background_option);
1018 wlr_color_transform_unref(oc->color_transform);
768 free(oc); 1019 free(oc);
769} 1020}
770 1021
@@ -822,7 +1073,9 @@ static bool _spawn_swaybg(char **command) {
822 setenv("WAYLAND_SOCKET", wayland_socket_str, true); 1073 setenv("WAYLAND_SOCKET", wayland_socket_str, true);
823 1074
824 execvp(command[0], command); 1075 execvp(command[0], command);
825 sway_log_errno(SWAY_ERROR, "execvp failed"); 1076 sway_log_errno(SWAY_ERROR, "failed to execute '%s' "
1077 "(background configuration probably not applied)",
1078 command[0]);
826 _exit(EXIT_FAILURE); 1079 _exit(EXIT_FAILURE);
827 } 1080 }
828 _exit(EXIT_SUCCESS); 1081 _exit(EXIT_SUCCESS);
@@ -832,12 +1085,13 @@ static bool _spawn_swaybg(char **command) {
832 sway_log_errno(SWAY_ERROR, "close failed"); 1085 sway_log_errno(SWAY_ERROR, "close failed");
833 return false; 1086 return false;
834 } 1087 }
835 if (waitpid(pid, NULL, 0) < 0) { 1088 int fork_status = 0;
1089 if (waitpid(pid, &fork_status, 0) < 0) {
836 sway_log_errno(SWAY_ERROR, "waitpid failed"); 1090 sway_log_errno(SWAY_ERROR, "waitpid failed");
837 return false; 1091 return false;
838 } 1092 }
839 1093
840 return true; 1094 return WIFEXITED(fork_status) && WEXITSTATUS(fork_status) == EXIT_SUCCESS;
841} 1095}
842 1096
843bool spawn_swaybg(void) { 1097bool 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..2b7290c0 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>
@@ -8,6 +7,7 @@
8#include "sway/criteria.h" 7#include "sway/criteria.h"
9#include "sway/tree/container.h" 8#include "sway/tree/container.h"
10#include "sway/config.h" 9#include "sway/config.h"
10#include "sway/server.h"
11#include "sway/tree/root.h" 11#include "sway/tree/root.h"
12#include "sway/tree/view.h" 12#include "sway/tree/view.h"
13#include "sway/tree/workspace.h" 13#include "sway/tree/workspace.h"
@@ -23,7 +23,7 @@ bool criteria_is_empty(struct criteria *criteria) {
23 && !criteria->app_id 23 && !criteria->app_id
24 && !criteria->con_mark 24 && !criteria->con_mark
25 && !criteria->con_id 25 && !criteria->con_id
26#if HAVE_XWAYLAND 26#if WLR_HAS_XWAYLAND
27 && !criteria->class 27 && !criteria->class
28 && !criteria->id 28 && !criteria->id
29 && !criteria->instance 29 && !criteria->instance
@@ -91,7 +91,7 @@ void criteria_destroy(struct criteria *criteria) {
91 pattern_destroy(criteria->title); 91 pattern_destroy(criteria->title);
92 pattern_destroy(criteria->shell); 92 pattern_destroy(criteria->shell);
93 pattern_destroy(criteria->app_id); 93 pattern_destroy(criteria->app_id);
94#if HAVE_XWAYLAND 94#if WLR_HAS_XWAYLAND
95 pattern_destroy(criteria->class); 95 pattern_destroy(criteria->class);
96 pattern_destroy(criteria->instance); 96 pattern_destroy(criteria->instance);
97 pattern_destroy(criteria->window_role); 97 pattern_destroy(criteria->window_role);
@@ -111,7 +111,7 @@ static int regex_cmp(const char *item, const pcre2_code *regex) {
111 return result; 111 return result;
112} 112}
113 113
114#if HAVE_XWAYLAND 114#if WLR_HAS_XWAYLAND
115static bool view_has_window_type(struct sway_view *view, enum atom_name name) { 115static bool view_has_window_type(struct sway_view *view, enum atom_name name) {
116 if (view->type != SWAY_VIEW_XWAYLAND) { 116 if (view->type != SWAY_VIEW_XWAYLAND) {
117 return false; 117 return false;
@@ -252,7 +252,7 @@ static bool criteria_matches_view(struct criteria *criteria,
252 return false; 252 return false;
253 } 253 }
254 254
255#if HAVE_XWAYLAND 255#if WLR_HAS_XWAYLAND
256 if (criteria->id) { // X11 window ID 256 if (criteria->id) { // X11 window ID
257 uint32_t x11_window_id = view_get_x11_window_id(view); 257 uint32_t x11_window_id = view_get_x11_window_id(view);
258 if (!x11_window_id || x11_window_id != criteria->id) { 258 if (!x11_window_id || x11_window_id != criteria->id) {
@@ -429,7 +429,7 @@ list_t *criteria_get_containers(struct criteria *criteria) {
429 return matches; 429 return matches;
430} 430}
431 431
432#if HAVE_XWAYLAND 432#if WLR_HAS_XWAYLAND
433static enum atom_name parse_window_type(const char *type) { 433static enum atom_name parse_window_type(const char *type) {
434 if (strcasecmp(type, "normal") == 0) { 434 if (strcasecmp(type, "normal") == 0) {
435 return NET_WM_WINDOW_TYPE_NORMAL; 435 return NET_WM_WINDOW_TYPE_NORMAL;
@@ -462,7 +462,7 @@ enum criteria_token {
462 T_CON_ID, 462 T_CON_ID,
463 T_CON_MARK, 463 T_CON_MARK,
464 T_FLOATING, 464 T_FLOATING,
465#if HAVE_XWAYLAND 465#if WLR_HAS_XWAYLAND
466 T_CLASS, 466 T_CLASS,
467 T_ID, 467 T_ID,
468 T_INSTANCE, 468 T_INSTANCE,
@@ -488,7 +488,7 @@ static enum criteria_token token_from_name(char *name) {
488 return T_CON_ID; 488 return T_CON_ID;
489 } else if (strcmp(name, "con_mark") == 0) { 489 } else if (strcmp(name, "con_mark") == 0) {
490 return T_CON_MARK; 490 return T_CON_MARK;
491#if HAVE_XWAYLAND 491#if WLR_HAS_XWAYLAND
492 } else if (strcmp(name, "class") == 0) { 492 } else if (strcmp(name, "class") == 0) {
493 return T_CLASS; 493 return T_CLASS;
494 } else if (strcmp(name, "id") == 0) { 494 } else if (strcmp(name, "id") == 0) {
@@ -567,7 +567,7 @@ static bool parse_token(struct criteria *criteria, char *name, char *value) {
567 case T_CON_MARK: 567 case T_CON_MARK:
568 pattern_create(&criteria->con_mark, value); 568 pattern_create(&criteria->con_mark, value);
569 break; 569 break;
570#if HAVE_XWAYLAND 570#if WLR_HAS_XWAYLAND
571 case T_CLASS: 571 case T_CLASS:
572 pattern_create(&criteria->class, value); 572 pattern_create(&criteria->class, value);
573 break; 573 break;
@@ -675,7 +675,7 @@ struct criteria *criteria_parse(char *raw, char **error_arg) {
675 ++head; 675 ++head;
676 676
677 struct criteria *criteria = calloc(1, sizeof(struct criteria)); 677 struct criteria *criteria = calloc(1, sizeof(struct criteria));
678#if HAVE_XWAYLAND 678#if WLR_HAS_XWAYLAND
679 criteria->window_type = ATOM_LAST; // default value 679 criteria->window_type = ATOM_LAST; // default value
680#endif 680#endif
681 char *name = NULL, *value = NULL; 681 char *name = NULL, *value = NULL;
diff --git a/sway/desktop/launcher.c b/sway/desktop/launcher.c
index 00a7e38a..2362e1ba 100644
--- a/sway/desktop/launcher.c
+++ b/sway/desktop/launcher.c
@@ -1,10 +1,10 @@
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>
5#include "sway/input/seat.h" 4#include "sway/input/seat.h"
6#include "sway/output.h" 5#include "sway/output.h"
7#include "sway/desktop/launcher.h" 6#include "sway/desktop/launcher.h"
7#include "sway/server.h"
8#include "sway/tree/node.h" 8#include "sway/tree/node.h"
9#include "sway/tree/container.h" 9#include "sway/tree/container.h"
10#include "sway/tree/workspace.h" 10#include "sway/tree/workspace.h"
@@ -67,6 +67,9 @@ void launcher_ctx_destroy(struct launcher_ctx *ctx) {
67 } 67 }
68 wl_list_remove(&ctx->node_destroy.link); 68 wl_list_remove(&ctx->node_destroy.link);
69 wl_list_remove(&ctx->token_destroy.link); 69 wl_list_remove(&ctx->token_destroy.link);
70 if (ctx->seat) {
71 wl_list_remove(&ctx->seat_destroy.link);
72 }
70 wl_list_remove(&ctx->link); 73 wl_list_remove(&ctx->link);
71 wlr_xdg_activation_token_v1_destroy(ctx->token); 74 wlr_xdg_activation_token_v1_destroy(ctx->token);
72 free(ctx->fallback_name); 75 free(ctx->fallback_name);
@@ -213,6 +216,8 @@ struct launcher_ctx *launcher_ctx_create(struct wlr_xdg_activation_token_v1 *tok
213 ctx->fallback_name = strdup(fallback_name); 216 ctx->fallback_name = strdup(fallback_name);
214 ctx->token = token; 217 ctx->token = token;
215 ctx->node = node; 218 ctx->node = node;
219 // Having surface set means that the focus check in wlroots has passed
220 ctx->had_focused_surface = token->surface != NULL;
216 221
217 ctx->node_destroy.notify = ctx_handle_node_destroy; 222 ctx->node_destroy.notify = ctx_handle_node_destroy;
218 wl_signal_add(&ctx->node->events.destroy, &ctx->node_destroy); 223 wl_signal_add(&ctx->node->events.destroy, &ctx->node_destroy);
@@ -227,6 +232,12 @@ struct launcher_ctx *launcher_ctx_create(struct wlr_xdg_activation_token_v1 *tok
227 return ctx; 232 return ctx;
228} 233}
229 234
235static void launch_ctx_handle_seat_destroy(struct wl_listener *listener, void *data) {
236 struct launcher_ctx *ctx = wl_container_of(listener, ctx, seat_destroy);
237 ctx->seat = NULL;
238 wl_list_remove(&ctx->seat_destroy.link);
239}
240
230// Creates a context with a new token for the internal launcher 241// Creates a context with a new token for the internal launcher
231struct launcher_ctx *launcher_ctx_create_internal(void) { 242struct launcher_ctx *launcher_ctx_create_internal(void) {
232 struct sway_seat *seat = input_manager_current_seat(); 243 struct sway_seat *seat = input_manager_current_seat();
@@ -238,13 +249,15 @@ struct launcher_ctx *launcher_ctx_create_internal(void) {
238 249
239 struct wlr_xdg_activation_token_v1 *token = 250 struct wlr_xdg_activation_token_v1 *token =
240 wlr_xdg_activation_token_v1_create(server.xdg_activation_v1); 251 wlr_xdg_activation_token_v1_create(server.xdg_activation_v1);
241 token->seat = seat->wlr_seat;
242 252
243 struct launcher_ctx *ctx = launcher_ctx_create(token, &ws->node); 253 struct launcher_ctx *ctx = launcher_ctx_create(token, &ws->node);
244 if (!ctx) { 254 if (!ctx) {
245 wlr_xdg_activation_token_v1_destroy(token); 255 wlr_xdg_activation_token_v1_destroy(token);
246 return NULL; 256 return NULL;
247 } 257 }
258 ctx->seat = seat;
259 ctx->seat_destroy.notify = launch_ctx_handle_seat_destroy;
260 wl_signal_add(&seat->wlr_seat->events.destroy, &ctx->seat_destroy);
248 261
249 return ctx; 262 return ctx;
250} 263}
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..6bf77d17 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) {
@@ -234,10 +243,14 @@ static int output_repaint_timer_handler(void *data) {
234 243
235 output_configure_scene(output, &root->root_scene->tree.node, 1.0f); 244 output_configure_scene(output, &root->root_scene->tree.node, 1.0f);
236 245
246 struct wlr_scene_output_state_options opts = {
247 .color_transform = output->color_transform,
248 };
249
237 if (output->gamma_lut_changed) { 250 if (output->gamma_lut_changed) {
238 struct wlr_output_state pending; 251 struct wlr_output_state pending;
239 wlr_output_state_init(&pending); 252 wlr_output_state_init(&pending);
240 if (!wlr_scene_output_build_state(output->scene_output, &pending, NULL)) { 253 if (!wlr_scene_output_build_state(output->scene_output, &pending, &opts)) {
241 return 0; 254 return 0;
242 } 255 }
243 256
@@ -260,7 +273,7 @@ static int output_repaint_timer_handler(void *data) {
260 return 0; 273 return 0;
261 } 274 }
262 275
263 wlr_scene_output_commit(output->scene_output, NULL); 276 wlr_scene_output_commit(output->scene_output, &opts);
264 return 0; 277 return 0;
265} 278}
266 279
@@ -512,9 +525,7 @@ void handle_new_output(struct wl_listener *listener, void *data) {
512 sway_session_lock_add_output(server->session_lock.lock, output); 525 sway_session_lock_add_output(server->session_lock.lock, output);
513 } 526 }
514 527
515 struct output_config *oc = find_output_config(output); 528 apply_all_output_configs();
516 apply_output_config(oc, output);
517 free_output_config(oc);
518 529
519 transaction_commit_dirty(); 530 transaction_commit_dirty();
520 531
@@ -543,63 +554,89 @@ void handle_gamma_control_set_gamma(struct wl_listener *listener, void *data) {
543 wlr_output_schedule_frame(output->wlr_output); 554 wlr_output_schedule_frame(output->wlr_output);
544} 555}
545 556
557static struct output_config *output_config_for_config_head(
558 struct wlr_output_configuration_head_v1 *config_head,
559 struct sway_output *output) {
560 struct output_config *oc = new_output_config(output->wlr_output->name);
561 oc->enabled = config_head->state.enabled;
562 if (!oc->enabled) {
563 return oc;
564 }
565
566 if (config_head->state.mode != NULL) {
567 struct wlr_output_mode *mode = config_head->state.mode;
568 oc->width = mode->width;
569 oc->height = mode->height;
570 oc->refresh_rate = mode->refresh / 1000.f;
571 } else {
572 oc->width = config_head->state.custom_mode.width;
573 oc->height = config_head->state.custom_mode.height;
574 oc->refresh_rate =
575 config_head->state.custom_mode.refresh / 1000.f;
576 }
577 oc->x = config_head->state.x;
578 oc->y = config_head->state.y;
579 oc->transform = config_head->state.transform;
580 oc->scale = config_head->state.scale;
581 oc->adaptive_sync = config_head->state.adaptive_sync_enabled;
582 return oc;
583}
584
546static void output_manager_apply(struct sway_server *server, 585static void output_manager_apply(struct sway_server *server,
547 struct wlr_output_configuration_v1 *config, bool test_only) { 586 struct wlr_output_configuration_v1 *config, bool test_only) {
548 // TODO: perform atomic tests on the whole backend atomically 587 size_t configs_len = wl_list_length(&root->all_outputs);
549 588 struct matched_output_config *configs = calloc(configs_len, sizeof(*configs));
550 struct wlr_output_configuration_head_v1 *config_head; 589 if (!configs) {
551 // First disable outputs we need to disable 590 return;
552 bool ok = true; 591 }
553 wl_list_for_each(config_head, &config->heads, link) { 592
554 struct wlr_output *wlr_output = config_head->state.output; 593 int config_idx = 0;
555 struct sway_output *output = wlr_output->data; 594 struct sway_output *sway_output;
556 if (!output->enabled || config_head->state.enabled) { 595 wl_list_for_each(sway_output, &root->all_outputs, link) {
596 if (sway_output == root->fallback_output) {
597 configs_len--;
557 continue; 598 continue;
558 } 599 }
559 struct output_config *oc = new_output_config(output->wlr_output->name);
560 oc->enabled = false;
561 600
562 if (test_only) { 601 struct matched_output_config *cfg = &configs[config_idx++];
563 ok &= test_output_config(oc, output); 602 cfg->output = sway_output;
564 } else { 603
565 oc = store_output_config(oc); 604 struct wlr_output_configuration_head_v1 *config_head;
566 ok &= apply_output_config(oc, output); 605 wl_list_for_each(config_head, &config->heads, link) {
606 if (config_head->state.output == sway_output->wlr_output) {
607 cfg->config = output_config_for_config_head(config_head, sway_output);
608 break;
609 }
610 }
611 if (!cfg->config) {
612 cfg->config = find_output_config(sway_output);
567 } 613 }
568 } 614 }
569 615
570 // Then enable outputs that need to 616 sort_output_configs_by_priority(configs, configs_len);
571 wl_list_for_each(config_head, &config->heads, link) { 617 bool ok = apply_output_configs(configs, configs_len, test_only, false);
572 struct wlr_output *wlr_output = config_head->state.output; 618 for (size_t idx = 0; idx < configs_len; idx++) {
573 struct sway_output *output = wlr_output->data; 619 struct matched_output_config *cfg = &configs[idx];
574 if (!config_head->state.enabled) { 620
575 continue; 621 // Only store new configs for successful non-test commits. Old configs,
576 } 622 // test-only and failed commits just get freed.
577 struct output_config *oc = new_output_config(output->wlr_output->name); 623 bool store_config = false;
578 oc->enabled = true; 624 if (!test_only && ok) {
579 if (config_head->state.mode != NULL) { 625 struct wlr_output_configuration_head_v1 *config_head;
580 struct wlr_output_mode *mode = config_head->state.mode; 626 wl_list_for_each(config_head, &config->heads, link) {
581 oc->width = mode->width; 627 if (config_head->state.output == cfg->output->wlr_output) {
582 oc->height = mode->height; 628 store_config = true;
583 oc->refresh_rate = mode->refresh / 1000.f; 629 break;
584 } else { 630 }
585 oc->width = config_head->state.custom_mode.width; 631 }
586 oc->height = config_head->state.custom_mode.height;
587 oc->refresh_rate =
588 config_head->state.custom_mode.refresh / 1000.f;
589 } 632 }
590 oc->x = config_head->state.x; 633 if (store_config) {
591 oc->y = config_head->state.y; 634 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 { 635 } else {
599 oc = store_output_config(oc); 636 free_output_config(cfg->config);
600 ok &= apply_output_config(oc, output);
601 } 637 }
602 } 638 }
639 free(configs);
603 640
604 if (ok) { 641 if (ok) {
605 wlr_output_configuration_v1_send_succeeded(config); 642 wlr_output_configuration_v1_send_succeeded(config);
@@ -643,6 +680,6 @@ void handle_output_power_manager_set_mode(struct wl_listener *listener,
643 oc->power = 1; 680 oc->power = 1;
644 break; 681 break;
645 } 682 }
646 oc = store_output_config(oc); 683 store_output_config(oc);
647 apply_output_config(oc, output); 684 apply_all_output_configs();
648} 685}
diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c
index acc3e3f9..d1898843 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>
@@ -11,6 +10,7 @@
11#include "sway/input/cursor.h" 10#include "sway/input/cursor.h"
12#include "sway/input/input-manager.h" 11#include "sway/input/input-manager.h"
13#include "sway/output.h" 12#include "sway/output.h"
13#include "sway/server.h"
14#include "sway/tree/container.h" 14#include "sway/tree/container.h"
15#include "sway/tree/node.h" 15#include "sway/tree/node.h"
16#include "sway/tree/view.h" 16#include "sway/tree/view.h"
@@ -606,21 +606,15 @@ static void arrange_output(struct sway_output *output, int width, int height) {
606 } 606 }
607} 607}
608 608
609static void arrange_popup(struct wlr_scene_tree *popup) { 609void arrange_popups(struct wlr_scene_tree *popups) {
610 struct wlr_scene_node *node; 610 struct wlr_scene_node *node;
611 wl_list_for_each(node, &popup->children, link) { 611 wl_list_for_each(node, &popups->children, link) {
612 struct sway_xdg_popup *popup = scene_descriptor_try_get(node, 612 struct sway_popup_desc *popup = scene_descriptor_try_get(node,
613 SWAY_SCENE_DESC_POPUP); 613 SWAY_SCENE_DESC_POPUP);
614 614
615 // the popup layer may have popups from layer_shell surfaces, in this 615 int lx, ly;
616 // case those don't have a scene descriptor, so lets skip those here. 616 wlr_scene_node_coords(popup->relative, &lx, &ly);
617 if (popup) { 617 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 } 618 }
625} 619}
626 620
@@ -679,7 +673,7 @@ static void arrange_root(struct sway_root *root) {
679 } 673 }
680 } 674 }
681 675
682 arrange_popup(root->layers.popup); 676 arrange_popups(root->layers.popup);
683} 677}
684 678
685/** 679/**
@@ -768,7 +762,7 @@ static bool should_configure(struct sway_node *node,
768 } 762 }
769 struct sway_container_state *cstate = &node->sway_container->current; 763 struct sway_container_state *cstate = &node->sway_container->current;
770 struct sway_container_state *istate = &instruction->container_state; 764 struct sway_container_state *istate = &instruction->container_state;
771#if HAVE_XWAYLAND 765#if WLR_HAS_XWAYLAND
772 // Xwayland views are position-aware and need to be reconfigured 766 // Xwayland views are position-aware and need to be reconfigured
773 // when their position changes. 767 // when their position changes.
774 if (node->sway_container->view->type == SWAY_VIEW_XWAYLAND) { 768 if (node->sway_container->view->type == SWAY_VIEW_XWAYLAND) {
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..235951d4 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>
@@ -25,6 +25,7 @@
25#include "sway/layers.h" 25#include "sway/layers.h"
26#include "sway/output.h" 26#include "sway/output.h"
27#include "sway/scene_descriptor.h" 27#include "sway/scene_descriptor.h"
28#include "sway/server.h"
28#include "sway/tree/container.h" 29#include "sway/tree/container.h"
29#include "sway/tree/root.h" 30#include "sway/tree/root.h"
30#include "sway/tree/view.h" 31#include "sway/tree/view.h"
@@ -90,9 +91,9 @@ struct sway_node *node_at_coords(
90 } 91 }
91 92
92 if (!con) { 93 if (!con) {
93 struct sway_xdg_popup *popup = 94 struct sway_popup_desc *popup =
94 scene_descriptor_try_get(current, SWAY_SCENE_DESC_POPUP); 95 scene_descriptor_try_get(current, SWAY_SCENE_DESC_POPUP);
95 if (popup) { 96 if (popup && popup->view) {
96 con = popup->view->container; 97 con = popup->view->container;
97 } 98 }
98 } 99 }
@@ -107,7 +108,7 @@ struct sway_node *node_at_coords(
107 return NULL; 108 return NULL;
108 } 109 }
109 110
110#if HAVE_XWAYLAND 111#if WLR_HAS_XWAYLAND
111 if (scene_descriptor_try_get(current, SWAY_SCENE_DESC_XWAYLAND_UNMANAGED)) { 112 if (scene_descriptor_try_get(current, SWAY_SCENE_DESC_XWAYLAND_UNMANAGED)) {
112 return NULL; 113 return NULL;
113 } 114 }
@@ -243,7 +244,7 @@ static enum sway_input_idle_source idle_source_from_device(
243 return IDLE_SOURCE_POINTER; 244 return IDLE_SOURCE_POINTER;
244 case WLR_INPUT_DEVICE_TOUCH: 245 case WLR_INPUT_DEVICE_TOUCH:
245 return IDLE_SOURCE_TOUCH; 246 return IDLE_SOURCE_TOUCH;
246 case WLR_INPUT_DEVICE_TABLET_TOOL: 247 case WLR_INPUT_DEVICE_TABLET:
247 return IDLE_SOURCE_TABLET_TOOL; 248 return IDLE_SOURCE_TABLET_TOOL;
248 case WLR_INPUT_DEVICE_TABLET_PAD: 249 case WLR_INPUT_DEVICE_TABLET_PAD:
249 return IDLE_SOURCE_TABLET_PAD; 250 return IDLE_SOURCE_TABLET_PAD;
@@ -356,7 +357,7 @@ static void handle_pointer_motion_absolute(
356 357
357void dispatch_cursor_button(struct sway_cursor *cursor, 358void dispatch_cursor_button(struct sway_cursor *cursor,
358 struct wlr_input_device *device, uint32_t time_msec, uint32_t button, 359 struct wlr_input_device *device, uint32_t time_msec, uint32_t button,
359 enum wlr_button_state state) { 360 enum wl_pointer_button_state state) {
360 if (time_msec == 0) { 361 if (time_msec == 0) {
361 time_msec = get_current_time_msec(); 362 time_msec = get_current_time_msec();
362 } 363 }
@@ -368,7 +369,7 @@ static void handle_pointer_button(struct wl_listener *listener, void *data) {
368 struct sway_cursor *cursor = wl_container_of(listener, cursor, button); 369 struct sway_cursor *cursor = wl_container_of(listener, cursor, button);
369 struct wlr_pointer_button_event *event = data; 370 struct wlr_pointer_button_event *event = data;
370 371
371 if (event->state == WLR_BUTTON_PRESSED) { 372 if (event->state == WL_POINTER_BUTTON_STATE_PRESSED) {
372 cursor->pressed_button_count++; 373 cursor->pressed_button_count++;
373 } else { 374 } else {
374 if (cursor->pressed_button_count > 0) { 375 if (cursor->pressed_button_count > 0) {
@@ -430,7 +431,7 @@ static void handle_touch_up(struct wl_listener *listener, void *data) {
430 if (cursor->pointer_touch_id == cursor->seat->touch_id) { 431 if (cursor->pointer_touch_id == cursor->seat->touch_id) {
431 cursor->pointer_touch_up = true; 432 cursor->pointer_touch_up = true;
432 dispatch_cursor_button(cursor, &event->touch->base, 433 dispatch_cursor_button(cursor, &event->touch->base,
433 event->time_msec, BTN_LEFT, WLR_BUTTON_RELEASED); 434 event->time_msec, BTN_LEFT, WL_POINTER_BUTTON_STATE_RELEASED);
434 } 435 }
435 } else { 436 } else {
436 seatop_touch_up(seat, event); 437 seatop_touch_up(seat, event);
@@ -448,7 +449,7 @@ static void handle_touch_cancel(struct wl_listener *listener, void *data) {
448 if (cursor->pointer_touch_id == cursor->seat->touch_id) { 449 if (cursor->pointer_touch_id == cursor->seat->touch_id) {
449 cursor->pointer_touch_up = true; 450 cursor->pointer_touch_up = true;
450 dispatch_cursor_button(cursor, &event->touch->base, 451 dispatch_cursor_button(cursor, &event->touch->base,
451 event->time_msec, BTN_LEFT, WLR_BUTTON_RELEASED); 452 event->time_msec, BTN_LEFT, WL_POINTER_BUTTON_STATE_RELEASED);
452 } 453 }
453 } else { 454 } else {
454 seatop_touch_cancel(seat, event); 455 seatop_touch_cancel(seat, event);
@@ -518,7 +519,7 @@ static void apply_mapping_from_region(struct wlr_input_device *device,
518 double x1 = region->x1, x2 = region->x2; 519 double x1 = region->x1, x2 = region->x2;
519 double y1 = region->y1, y2 = region->y2; 520 double y1 = region->y1, y2 = region->y2;
520 521
521 if (region->mm && device->type == WLR_INPUT_DEVICE_TABLET_TOOL) { 522 if (region->mm && device->type == WLR_INPUT_DEVICE_TABLET) {
522 struct wlr_tablet *tablet = wlr_tablet_from_input_device(device); 523 struct wlr_tablet *tablet = wlr_tablet_from_input_device(device);
523 if (tablet->width_mm == 0 || tablet->height_mm == 0) { 524 if (tablet->width_mm == 0 || tablet->height_mm == 0) {
524 return; 525 return;
@@ -661,7 +662,7 @@ static void handle_tool_tip(struct wl_listener *listener, void *data) {
661 event->state == WLR_TABLET_TOOL_TIP_UP) { 662 event->state == WLR_TABLET_TOOL_TIP_UP) {
662 cursor->simulating_pointer_from_tool_tip = false; 663 cursor->simulating_pointer_from_tool_tip = false;
663 dispatch_cursor_button(cursor, &event->tablet->base, event->time_msec, 664 dispatch_cursor_button(cursor, &event->tablet->base, event->time_msec,
664 BTN_LEFT, WLR_BUTTON_RELEASED); 665 BTN_LEFT, WL_POINTER_BUTTON_STATE_RELEASED);
665 wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); 666 wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat);
666 } else if (!surface || !wlr_surface_accepts_tablet_v2(tablet_v2, surface)) { 667 } 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 668 // If we started holding the tool tip down on a surface that accepts
@@ -673,7 +674,7 @@ static void handle_tool_tip(struct wl_listener *listener, void *data) {
673 } else { 674 } else {
674 cursor->simulating_pointer_from_tool_tip = true; 675 cursor->simulating_pointer_from_tool_tip = true;
675 dispatch_cursor_button(cursor, &event->tablet->base, 676 dispatch_cursor_button(cursor, &event->tablet->base,
676 event->time_msec, BTN_LEFT, WLR_BUTTON_PRESSED); 677 event->time_msec, BTN_LEFT, WL_POINTER_BUTTON_STATE_PRESSED);
677 wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); 678 wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat);
678 } 679 }
679 } else { 680 } else {
@@ -776,13 +777,13 @@ static void handle_tool_button(struct wl_listener *listener, void *data) {
776 case WLR_BUTTON_PRESSED: 777 case WLR_BUTTON_PRESSED:
777 if (cursor->tool_buttons == 0) { 778 if (cursor->tool_buttons == 0) {
778 dispatch_cursor_button(cursor, &event->tablet->base, 779 dispatch_cursor_button(cursor, &event->tablet->base,
779 event->time_msec, BTN_RIGHT, event->state); 780 event->time_msec, BTN_RIGHT, WL_POINTER_BUTTON_STATE_PRESSED);
780 } 781 }
781 break; 782 break;
782 case WLR_BUTTON_RELEASED: 783 case WLR_BUTTON_RELEASED:
783 if (cursor->tool_buttons <= 1) { 784 if (cursor->tool_buttons <= 1) {
784 dispatch_cursor_button(cursor, &event->tablet->base, 785 dispatch_cursor_button(cursor, &event->tablet->base,
785 event->time_msec, BTN_RIGHT, event->state); 786 event->time_msec, BTN_RIGHT, WL_POINTER_BUTTON_STATE_RELEASED);
786 } 787 }
787 break; 788 break;
788 } 789 }
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..9ac21664 100644
--- a/sway/input/keyboard.c
+++ b/sway/input/keyboard.c
@@ -13,6 +13,7 @@
13#include "sway/input/seat.h" 13#include "sway/input/seat.h"
14#include "sway/input/cursor.h" 14#include "sway/input/cursor.h"
15#include "sway/ipc-server.h" 15#include "sway/ipc-server.h"
16#include "sway/server.h"
16#include "log.h" 17#include "log.h"
17 18
18#if WLR_HAS_SESSION 19#if WLR_HAS_SESSION
@@ -32,6 +33,7 @@ static struct modifier_key {
32 { XKB_MOD_NAME_NUM, WLR_MODIFIER_MOD2 }, 33 { XKB_MOD_NAME_NUM, WLR_MODIFIER_MOD2 },
33 { "Mod3", WLR_MODIFIER_MOD3 }, 34 { "Mod3", WLR_MODIFIER_MOD3 },
34 { XKB_MOD_NAME_LOGO, WLR_MODIFIER_LOGO }, 35 { XKB_MOD_NAME_LOGO, WLR_MODIFIER_LOGO },
36 { "Super", WLR_MODIFIER_LOGO },
35 { "Mod5", WLR_MODIFIER_MOD5 }, 37 { "Mod5", WLR_MODIFIER_MOD5 },
36}; 38};
37 39
diff --git a/sway/input/seat.c b/sway/input/seat.c
index 75fea484..da4bb12a 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 }
@@ -185,7 +190,7 @@ static void seat_send_focus(struct sway_node *node, struct sway_seat *seat) {
185 node->sway_container->view : NULL; 190 node->sway_container->view : NULL;
186 191
187 if (view && seat_is_input_allowed(seat, view->surface)) { 192 if (view && seat_is_input_allowed(seat, view->surface)) {
188#if HAVE_XWAYLAND 193#if WLR_HAS_XWAYLAND
189 if (view->type == SWAY_VIEW_XWAYLAND) { 194 if (view->type == SWAY_VIEW_XWAYLAND) {
190 struct wlr_xwayland *xwayland = server.xwayland.wlr_xwayland; 195 struct wlr_xwayland *xwayland = server.xwayland.wlr_xwayland;
191 wlr_xwayland_set_seat(xwayland, seat->wlr_seat); 196 wlr_xwayland_set_seat(xwayland, seat->wlr_seat);
@@ -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:
@@ -994,7 +1002,7 @@ void seat_configure_xcursor(struct sway_seat *seat) {
994 setenv("XCURSOR_THEME", cursor_theme, 1); 1002 setenv("XCURSOR_THEME", cursor_theme, 1);
995 } 1003 }
996 1004
997#if HAVE_XWAYLAND 1005#if WLR_HAS_XWAYLAND
998 if (server.xwayland.wlr_xwayland && (!server.xwayland.xcursor_manager || 1006 if (server.xwayland.wlr_xwayland && (!server.xwayland.xcursor_manager ||
999 !xcursor_manager_is_named(server.xwayland.xcursor_manager, 1007 !xcursor_manager_is_named(server.xwayland.xcursor_manager,
1000 cursor_theme) || 1008 cursor_theme) ||
@@ -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..f4a0f463 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>
@@ -12,11 +11,12 @@
12#include "sway/input/tablet.h" 11#include "sway/input/tablet.h"
13#include "sway/layers.h" 12#include "sway/layers.h"
14#include "sway/output.h" 13#include "sway/output.h"
14#include "sway/server.h"
15#include "sway/scene_descriptor.h" 15#include "sway/scene_descriptor.h"
16#include "sway/tree/view.h" 16#include "sway/tree/view.h"
17#include "sway/tree/workspace.h" 17#include "sway/tree/workspace.h"
18#include "log.h" 18#include "log.h"
19#if HAVE_XWAYLAND 19#if WLR_HAS_XWAYLAND
20#include "sway/xwayland.h" 20#include "sway/xwayland.h"
21#endif 21#endif
22 22
@@ -235,7 +235,7 @@ static void handle_tablet_tool_tip(struct sway_seat *seat,
235 node->sway_container : NULL; 235 node->sway_container : NULL;
236 236
237 struct wlr_layer_surface_v1 *layer; 237 struct wlr_layer_surface_v1 *layer;
238#if HAVE_XWAYLAND 238#if WLR_HAS_XWAYLAND
239 struct wlr_xwayland_surface *xsurface; 239 struct wlr_xwayland_surface *xsurface;
240#endif 240#endif
241 if ((layer = wlr_layer_surface_v1_try_from_wlr_surface(surface)) && 241 if ((layer = wlr_layer_surface_v1_try_from_wlr_surface(surface)) &&
@@ -269,7 +269,7 @@ static void handle_tablet_tool_tip(struct sway_seat *seat,
269 seat_set_focus_container(seat, cont); 269 seat_set_focus_container(seat, cont);
270 seatop_begin_down(seat, node->sway_container, sx, sy); 270 seatop_begin_down(seat, node->sway_container, sx, sy);
271 } 271 }
272#if HAVE_XWAYLAND 272#if WLR_HAS_XWAYLAND
273 // Handle tapping on an xwayland unmanaged view 273 // Handle tapping on an xwayland unmanaged view
274 else if ((xsurface = wlr_xwayland_surface_try_from_wlr_surface(surface)) && 274 else if ((xsurface = wlr_xwayland_surface_try_from_wlr_surface(surface)) &&
275 xsurface->override_redirect && 275 xsurface->override_redirect &&
@@ -291,7 +291,7 @@ static void handle_tablet_tool_tip(struct sway_seat *seat,
291 291
292static bool trigger_pointer_button_binding(struct sway_seat *seat, 292static bool trigger_pointer_button_binding(struct sway_seat *seat,
293 struct wlr_input_device *device, uint32_t button, 293 struct wlr_input_device *device, uint32_t button,
294 enum wlr_button_state state, uint32_t modifiers, 294 enum wl_pointer_button_state state, uint32_t modifiers,
295 bool on_titlebar, bool on_border, bool on_contents, bool on_workspace) { 295 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 296 // 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 297 // pointer input for one. Emulated input should not trigger bindings. The
@@ -305,7 +305,7 @@ static bool trigger_pointer_button_binding(struct sway_seat *seat,
305 char *device_identifier = device ? input_device_get_identifier(device) 305 char *device_identifier = device ? input_device_get_identifier(device)
306 : strdup("*"); 306 : strdup("*");
307 struct sway_binding *binding = NULL; 307 struct sway_binding *binding = NULL;
308 if (state == WLR_BUTTON_PRESSED) { 308 if (state == WL_POINTER_BUTTON_STATE_PRESSED) {
309 state_add_button(e, button); 309 state_add_button(e, button);
310 binding = get_active_mouse_binding(e, 310 binding = get_active_mouse_binding(e,
311 config->current_mode->mouse_bindings, modifiers, false, 311 config->current_mode->mouse_bindings, modifiers, false,
@@ -330,7 +330,7 @@ static bool trigger_pointer_button_binding(struct sway_seat *seat,
330 330
331static void handle_button(struct sway_seat *seat, uint32_t time_msec, 331static void handle_button(struct sway_seat *seat, uint32_t time_msec,
332 struct wlr_input_device *device, uint32_t button, 332 struct wlr_input_device *device, uint32_t button,
333 enum wlr_button_state state) { 333 enum wl_pointer_button_state state) {
334 struct sway_cursor *cursor = seat->cursor; 334 struct sway_cursor *cursor = seat->cursor;
335 335
336 // Determine what's under the cursor 336 // Determine what's under the cursor
@@ -363,7 +363,7 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec,
363 363
364 // Handle clicking an empty workspace 364 // Handle clicking an empty workspace
365 if (node && node->type == N_WORKSPACE) { 365 if (node && node->type == N_WORKSPACE) {
366 if (state == WLR_BUTTON_PRESSED) { 366 if (state == WL_POINTER_BUTTON_STATE_PRESSED) {
367 seat_set_focus(seat, node); 367 seat_set_focus(seat, node);
368 transaction_commit_dirty(); 368 transaction_commit_dirty();
369 } 369 }
@@ -378,7 +378,7 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec,
378 seat_set_focus_layer(seat, layer); 378 seat_set_focus_layer(seat, layer);
379 transaction_commit_dirty(); 379 transaction_commit_dirty();
380 } 380 }
381 if (state == WLR_BUTTON_PRESSED) { 381 if (state == WL_POINTER_BUTTON_STATE_PRESSED) {
382 seatop_begin_down_on_surface(seat, surface, sx, sy); 382 seatop_begin_down_on_surface(seat, surface, sx, sy);
383 } 383 }
384 seat_pointer_notify_button(seat, time_msec, button, state); 384 seat_pointer_notify_button(seat, time_msec, button, state);
@@ -387,7 +387,7 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec,
387 387
388 // Handle tiling resize via border 388 // Handle tiling resize via border
389 if (cont && resize_edge && button == BTN_LEFT && 389 if (cont && resize_edge && button == BTN_LEFT &&
390 state == WLR_BUTTON_PRESSED && !is_floating) { 390 state == WL_POINTER_BUTTON_STATE_PRESSED && !is_floating) {
391 // If a resize is triggered on a tabbed or stacked container, change 391 // 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 392 // 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. 393 // change the active tab when the user probably just wanted to resize.
@@ -405,7 +405,7 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec,
405 // Handle tiling resize via mod 405 // Handle tiling resize via mod
406 bool mod_pressed = modifiers & config->floating_mod; 406 bool mod_pressed = modifiers & config->floating_mod;
407 if (cont && !is_floating_or_child && mod_pressed && 407 if (cont && !is_floating_or_child && mod_pressed &&
408 state == WLR_BUTTON_PRESSED) { 408 state == WL_POINTER_BUTTON_STATE_PRESSED) {
409 uint32_t btn_resize = config->floating_mod_inverse ? 409 uint32_t btn_resize = config->floating_mod_inverse ?
410 BTN_LEFT : BTN_RIGHT; 410 BTN_LEFT : BTN_RIGHT;
411 if (button == btn_resize) { 411 if (button == btn_resize) {
@@ -433,7 +433,7 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec,
433 } 433 }
434 434
435 // Handle changing focus when clicking on a container 435 // Handle changing focus when clicking on a container
436 if (cont && state == WLR_BUTTON_PRESSED) { 436 if (cont && state == WL_POINTER_BUTTON_STATE_PRESSED) {
437 // Default case: focus the container that was just clicked. 437 // Default case: focus the container that was just clicked.
438 node = &cont->node; 438 node = &cont->node;
439 439
@@ -454,7 +454,7 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec,
454 454
455 // Handle beginning floating move 455 // Handle beginning floating move
456 if (cont && is_floating_or_child && !is_fullscreen_or_child && 456 if (cont && is_floating_or_child && !is_fullscreen_or_child &&
457 state == WLR_BUTTON_PRESSED) { 457 state == WL_POINTER_BUTTON_STATE_PRESSED) {
458 uint32_t btn_move = config->floating_mod_inverse ? BTN_RIGHT : BTN_LEFT; 458 uint32_t btn_move = config->floating_mod_inverse ? BTN_RIGHT : BTN_LEFT;
459 if (button == btn_move && (mod_pressed || on_titlebar)) { 459 if (button == btn_move && (mod_pressed || on_titlebar)) {
460 seatop_begin_move_floating(seat, container_toplevel_ancestor(cont)); 460 seatop_begin_move_floating(seat, container_toplevel_ancestor(cont));
@@ -464,7 +464,7 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec,
464 464
465 // Handle beginning floating resize 465 // Handle beginning floating resize
466 if (cont && is_floating_or_child && !is_fullscreen_or_child && 466 if (cont && is_floating_or_child && !is_fullscreen_or_child &&
467 state == WLR_BUTTON_PRESSED) { 467 state == WL_POINTER_BUTTON_STATE_PRESSED) {
468 // Via border 468 // Via border
469 if (button == BTN_LEFT && resize_edge != WLR_EDGE_NONE) { 469 if (button == BTN_LEFT && resize_edge != WLR_EDGE_NONE) {
470 seat_set_focus_container(seat, cont); 470 seat_set_focus_container(seat, cont);
@@ -490,7 +490,7 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec,
490 490
491 // Handle moving a tiling container 491 // Handle moving a tiling container
492 if (config->tiling_drag && (mod_pressed || on_titlebar) && 492 if (config->tiling_drag && (mod_pressed || on_titlebar) &&
493 state == WLR_BUTTON_PRESSED && !is_floating_or_child && 493 state == WL_POINTER_BUTTON_STATE_PRESSED && !is_floating_or_child &&
494 cont && cont->pending.fullscreen_mode == FULLSCREEN_NONE) { 494 cont && cont->pending.fullscreen_mode == FULLSCREEN_NONE) {
495 // If moving a container by its title bar, use a threshold for the drag 495 // If moving a container by its title bar, use a threshold for the drag
496 if (!mod_pressed && config->tiling_drag_threshold > 0) { 496 if (!mod_pressed && config->tiling_drag_threshold > 0) {
@@ -503,19 +503,19 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec,
503 } 503 }
504 504
505 // Handle mousedown on a container surface 505 // Handle mousedown on a container surface
506 if (surface && cont && state == WLR_BUTTON_PRESSED) { 506 if (surface && cont && state == WL_POINTER_BUTTON_STATE_PRESSED) {
507 seatop_begin_down(seat, cont, sx, sy); 507 seatop_begin_down(seat, cont, sx, sy);
508 seat_pointer_notify_button(seat, time_msec, button, WLR_BUTTON_PRESSED); 508 seat_pointer_notify_button(seat, time_msec, button, WL_POINTER_BUTTON_STATE_PRESSED);
509 return; 509 return;
510 } 510 }
511 511
512 // Handle clicking a container surface or decorations 512 // Handle clicking a container surface or decorations
513 if (cont && state == WLR_BUTTON_PRESSED) { 513 if (cont && state == WL_POINTER_BUTTON_STATE_PRESSED) {
514 seat_pointer_notify_button(seat, time_msec, button, state); 514 seat_pointer_notify_button(seat, time_msec, button, state);
515 return; 515 return;
516 } 516 }
517 517
518#if HAVE_XWAYLAND 518#if WLR_HAS_XWAYLAND
519 // Handle clicking on xwayland unmanaged view 519 // Handle clicking on xwayland unmanaged view
520 struct wlr_xwayland_surface *xsurface; 520 struct wlr_xwayland_surface *xsurface;
521 if (surface && 521 if (surface &&
@@ -685,7 +685,7 @@ static void handle_touch_down(struct sway_seat *seat,
685 pointer_motion(cursor, event->time_msec, &event->touch->base, dx, dy, 685 pointer_motion(cursor, event->time_msec, &event->touch->base, dx, dy,
686 dx, dy); 686 dx, dy);
687 dispatch_cursor_button(cursor, &event->touch->base, event->time_msec, 687 dispatch_cursor_button(cursor, &event->touch->base, event->time_msec,
688 BTN_LEFT, WLR_BUTTON_PRESSED); 688 BTN_LEFT, WL_POINTER_BUTTON_STATE_PRESSED);
689 } 689 }
690} 690}
691 691
@@ -695,9 +695,9 @@ static void handle_touch_down(struct sway_seat *seat,
695 695
696static uint32_t wl_axis_to_button(struct wlr_pointer_axis_event *event) { 696static uint32_t wl_axis_to_button(struct wlr_pointer_axis_event *event) {
697 switch (event->orientation) { 697 switch (event->orientation) {
698 case WLR_AXIS_ORIENTATION_VERTICAL: 698 case WL_POINTER_AXIS_VERTICAL_SCROLL:
699 return event->delta < 0 ? SWAY_SCROLL_UP : SWAY_SCROLL_DOWN; 699 return event->delta < 0 ? SWAY_SCROLL_UP : SWAY_SCROLL_DOWN;
700 case WLR_AXIS_ORIENTATION_HORIZONTAL: 700 case WL_POINTER_AXIS_HORIZONTAL_SCROLL:
701 return event->delta < 0 ? SWAY_SCROLL_LEFT : SWAY_SCROLL_RIGHT; 701 return event->delta < 0 ? SWAY_SCROLL_LEFT : SWAY_SCROLL_RIGHT;
702 default: 702 default:
703 sway_log(SWAY_DEBUG, "Unknown axis orientation"); 703 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/switch.c b/sway/input/switch.c
index 831f4dbf..6aab4ad0 100644
--- a/sway/input/switch.c
+++ b/sway/input/switch.c
@@ -1,5 +1,6 @@
1#include "sway/config.h" 1#include "sway/config.h"
2#include "sway/input/switch.h" 2#include "sway/input/switch.h"
3#include "sway/server.h"
3#include "log.h" 4#include "log.h"
4 5
5struct sway_switch *sway_switch_create(struct sway_seat *seat, 6struct sway_switch *sway_switch_create(struct sway_seat *seat,
diff --git a/sway/input/tablet.c b/sway/input/tablet.c
index 902cb7ed..ec1e4f68 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>
@@ -8,6 +7,7 @@
8#include "sway/input/cursor.h" 7#include "sway/input/cursor.h"
9#include "sway/input/seat.h" 8#include "sway/input/seat.h"
10#include "sway/input/tablet.h" 9#include "sway/input/tablet.h"
10#include "sway/server.h"
11 11
12#if WLR_HAS_LIBINPUT_BACKEND 12#if WLR_HAS_LIBINPUT_BACKEND
13#include <wlr/backend/libinput.h> 13#include <wlr/backend/libinput.h>
diff --git a/sway/input/text_input.c b/sway/input/text_input.c
index 58911c2d..580a9f54 100644
--- a/sway/input/text_input.c
+++ b/sway/input/text_input.c
@@ -2,7 +2,16 @@
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"
12#include "sway/server.h"
13
14static void input_popup_update(struct sway_input_popup *popup);
6 15
7static struct sway_text_input *relay_get_focusable_text_input( 16static struct sway_text_input *relay_get_focusable_text_input(
8 struct sway_input_method_relay *relay) { 17 struct sway_input_method_relay *relay) {
@@ -59,11 +68,13 @@ static void handle_im_keyboard_grab_destroy(struct wl_listener *listener, void *
59 struct sway_input_method_relay *relay = wl_container_of(listener, relay, 68 struct sway_input_method_relay *relay = wl_container_of(listener, relay,
60 input_method_keyboard_grab_destroy); 69 input_method_keyboard_grab_destroy);
61 struct wlr_input_method_keyboard_grab_v2 *keyboard_grab = data; 70 struct wlr_input_method_keyboard_grab_v2 *keyboard_grab = data;
71 struct wlr_seat *wlr_seat = keyboard_grab->input_method->seat;
62 wl_list_remove(&relay->input_method_keyboard_grab_destroy.link); 72 wl_list_remove(&relay->input_method_keyboard_grab_destroy.link);
63 73
64 if (keyboard_grab->keyboard) { 74 if (keyboard_grab->keyboard) {
65 // send modifier state to original client 75 // send modifier state to original client
66 wlr_seat_keyboard_notify_modifiers(keyboard_grab->input_method->seat, 76 wlr_seat_set_keyboard(wlr_seat, keyboard_grab->keyboard);
77 wlr_seat_keyboard_notify_modifiers(wlr_seat,
67 &keyboard_grab->keyboard->modifiers); 78 &keyboard_grab->keyboard->modifiers);
68 } 79 }
69} 80}
@@ -102,6 +113,10 @@ static void handle_im_destroy(struct wl_listener *listener, void *data) {
102 input_method_destroy); 113 input_method_destroy);
103 struct wlr_input_method_v2 *context = data; 114 struct wlr_input_method_v2 *context = data;
104 assert(context == relay->input_method); 115 assert(context == relay->input_method);
116 wl_list_remove(&relay->input_method_commit.link);
117 wl_list_remove(&relay->input_method_grab_keyboard.link);
118 wl_list_remove(&relay->input_method_destroy.link);
119 wl_list_remove(&relay->input_method_new_popup_surface.link);
105 relay->input_method = NULL; 120 relay->input_method = NULL;
106 struct sway_text_input *text_input = relay_get_focused_text_input(relay); 121 struct sway_text_input *text_input = relay_get_focused_text_input(relay);
107 if (text_input) { 122 if (text_input) {
@@ -133,6 +148,11 @@ static void relay_send_im_state(struct sway_input_method_relay *relay,
133 input->current.content_type.hint, 148 input->current.content_type.hint,
134 input->current.content_type.purpose); 149 input->current.content_type.purpose);
135 } 150 }
151 struct sway_input_popup *popup;
152 wl_list_for_each(popup, &relay->input_popups, link) {
153 // send_text_input_rectangle is called in this function
154 input_popup_update(popup);
155 }
136 wlr_input_method_v2_send_done(input_method); 156 wlr_input_method_v2_send_done(input_method);
137 // TODO: pass intent, display popup size 157 // TODO: pass intent, display popup size
138} 158}
@@ -255,6 +275,211 @@ static void relay_handle_text_input(struct wl_listener *listener,
255 sway_text_input_create(relay, wlr_text_input); 275 sway_text_input_create(relay, wlr_text_input);
256} 276}
257 277
278static void input_popup_update(struct sway_input_popup *popup) {
279 struct sway_text_input *text_input =
280 relay_get_focused_text_input(popup->relay);
281
282 if (text_input == NULL || text_input->input->focused_surface == NULL) {
283 return;
284 }
285
286 if (popup->scene_tree != NULL) {
287 wlr_scene_node_destroy(&popup->scene_tree->node);
288 popup->scene_tree = NULL;
289 }
290 if (popup->desc.relative != NULL) {
291 wlr_scene_node_destroy(popup->desc.relative);
292 popup->desc.relative = NULL;
293 }
294 popup->desc.view = NULL;
295
296 if (!popup->popup_surface->surface->mapped) {
297 return;
298 }
299
300 bool cursor_rect = text_input->input->current.features
301 & WLR_TEXT_INPUT_V3_FEATURE_CURSOR_RECTANGLE;
302 struct wlr_surface *focused_surface = text_input->input->focused_surface;
303 struct wlr_box cursor_area = text_input->input->current.cursor_rectangle;
304
305 struct wlr_box output_box;
306 struct wlr_box parent = {0};
307 struct wlr_layer_surface_v1 *layer_surface =
308 wlr_layer_surface_v1_try_from_wlr_surface(focused_surface);
309 struct wlr_scene_tree *relative_parent;
310
311 struct wlr_box geo = {0};
312
313 popup->scene_tree = wlr_scene_subsurface_tree_create(root->layers.popup, popup->popup_surface->surface);
314 if (layer_surface != NULL) {
315 struct sway_layer_surface *layer =
316 layer_surface->data;
317 if (layer == NULL) {
318 return;
319 }
320
321 relative_parent = layer->scene->tree;
322 struct wlr_output *output = layer->layer_surface->output;
323 wlr_output_layout_get_box(root->output_layout, output, &output_box);
324 int lx, ly;
325 wlr_scene_node_coords(&layer->tree->node, &lx, &ly);
326 parent.x = lx;
327 parent.y = ly;
328 popup->desc.view = NULL;
329 } else {
330 struct sway_view *view = view_from_wlr_surface(focused_surface);
331 relative_parent = view->scene_tree;
332 geo = view->geometry;
333 int lx, ly;
334 wlr_scene_node_coords(&view->scene_tree->node, &lx, &ly);
335 struct wlr_output *output = wlr_output_layout_output_at(root->output_layout,
336 view->container->pending.content_x + view->geometry.x,
337 view->container->pending.content_y + view->geometry.y);
338 wlr_output_layout_get_box(root->output_layout, output, &output_box);
339 parent.x = lx;
340 parent.y = ly;
341
342 parent.width = view->geometry.width;
343 parent.height = view->geometry.height;
344 popup->desc.view = view;
345 }
346
347 struct wlr_scene_tree *relative = wlr_scene_tree_create(relative_parent);
348
349 popup->desc.relative = &relative->node;
350 if (!scene_descriptor_assign(&popup->scene_tree->node,
351 SWAY_SCENE_DESC_POPUP, &popup->desc)) {
352 wlr_scene_node_destroy(&popup->scene_tree->node);
353 popup->scene_tree = NULL;
354 return;
355 }
356
357 if (!cursor_rect) {
358 cursor_area.x = 0;
359 cursor_area.y = 0;
360 cursor_area.width = parent.width;
361 cursor_area.height = parent.height;
362 }
363
364 int popup_width = popup->popup_surface->surface->current.width;
365 int popup_height = popup->popup_surface->surface->current.height;
366 int x1 = parent.x + cursor_area.x;
367 int x2 = parent.x + cursor_area.x + cursor_area.width;
368 int y1 = parent.y + cursor_area.y;
369 int y2 = parent.y + cursor_area.y + cursor_area.height;
370 int x = x1;
371 int y = y2;
372
373 int available_right = output_box.x + output_box.width - x1;
374 int available_left = x2 - output_box.x;
375 if (available_right < popup_width && available_left > available_right) {
376 x = x2 - popup_width;
377 }
378
379 int available_down = output_box.y + output_box.height - y2;
380 int available_up = y1 - output_box.y;
381 if (available_down < popup_height && available_up > available_down) {
382 y = y1 - popup_height;
383 }
384
385 wlr_scene_node_set_position(&relative->node, x - parent.x - geo.x, y - parent.y - geo.y);
386 if (cursor_rect) {
387 struct wlr_box box = {
388 .x = x1 - x,
389 .y = y1 - y,
390 .width = cursor_area.width,
391 .height = cursor_area.height,
392 };
393 wlr_input_popup_surface_v2_send_text_input_rectangle(
394 popup->popup_surface, &box);
395 }
396 wlr_scene_node_set_position(&popup->scene_tree->node, x - geo.x, y - geo.y);
397}
398
399static void input_popup_set_focus(struct sway_input_popup *popup,
400 struct wlr_surface *surface) {
401 wl_list_remove(&popup->focused_surface_unmap.link);
402
403 if (surface == NULL) {
404 wl_list_init(&popup->focused_surface_unmap.link);
405 input_popup_update(popup);
406 return;
407 }
408 struct wlr_layer_surface_v1 *layer_surface =
409 wlr_layer_surface_v1_try_from_wlr_surface(surface);
410 if (layer_surface != NULL) {
411 wl_signal_add(
412 &layer_surface->surface->events.unmap, &popup->focused_surface_unmap);
413 input_popup_update(popup);
414 return;
415 }
416
417 struct sway_view *view = view_from_wlr_surface(surface);
418 wl_signal_add(&view->events.unmap, &popup->focused_surface_unmap);
419}
420
421static void handle_im_popup_destroy(struct wl_listener *listener, void *data) {
422 struct sway_input_popup *popup =
423 wl_container_of(listener, popup, popup_destroy);
424 wl_list_remove(&popup->focused_surface_unmap.link);
425 wl_list_remove(&popup->popup_surface_commit.link);
426 wl_list_remove(&popup->popup_destroy.link);
427 wl_list_remove(&popup->link);
428
429 free(popup);
430}
431
432static void handle_im_popup_surface_commit(struct wl_listener *listener,
433 void *data) {
434 struct sway_input_popup *popup =
435 wl_container_of(listener, popup, popup_surface_commit);
436 input_popup_update(popup);
437}
438
439static void handle_im_focused_surface_unmap(
440 struct wl_listener *listener, void *data) {
441 struct sway_input_popup *popup =
442 wl_container_of(listener, popup, focused_surface_unmap);
443 input_popup_update(popup);
444}
445
446static void handle_im_new_popup_surface(struct wl_listener *listener,
447 void *data) {
448 struct sway_input_method_relay *relay = wl_container_of(listener, relay,
449 input_method_new_popup_surface);
450 struct sway_input_popup *popup = calloc(1, sizeof(*popup));
451 popup->relay = relay;
452 popup->popup_surface = data;
453 popup->popup_surface->data = popup;
454
455 wl_signal_add(
456 &popup->popup_surface->events.destroy, &popup->popup_destroy);
457 popup->popup_destroy.notify = handle_im_popup_destroy;
458 wl_signal_add(&popup->popup_surface->surface->events.commit,
459 &popup->popup_surface_commit);
460 popup->popup_surface_commit.notify = handle_im_popup_surface_commit;
461 wl_list_init(&popup->focused_surface_unmap.link);
462 popup->focused_surface_unmap.notify = handle_im_focused_surface_unmap;
463
464 struct sway_text_input *text_input = relay_get_focused_text_input(relay);
465 if (text_input != NULL) {
466 input_popup_set_focus(popup, text_input->input->focused_surface);
467 } else {
468 input_popup_set_focus(popup, NULL);
469 }
470
471 wl_list_insert(&relay->input_popups, &popup->link);
472}
473
474static void text_input_send_enter(struct sway_text_input *text_input,
475 struct wlr_surface *surface) {
476 wlr_text_input_v3_send_enter(text_input->input, surface);
477 struct sway_input_popup *popup;
478 wl_list_for_each(popup, &text_input->relay->input_popups, link) {
479 input_popup_set_focus(popup, surface);
480 }
481}
482
258static void relay_handle_input_method(struct wl_listener *listener, 483static void relay_handle_input_method(struct wl_listener *listener,
259 void *data) { 484 void *data) {
260 struct sway_input_method_relay *relay = wl_container_of(listener, relay, 485 struct sway_input_method_relay *relay = wl_container_of(listener, relay,
@@ -280,10 +505,13 @@ static void relay_handle_input_method(struct wl_listener *listener,
280 wl_signal_add(&relay->input_method->events.destroy, 505 wl_signal_add(&relay->input_method->events.destroy,
281 &relay->input_method_destroy); 506 &relay->input_method_destroy);
282 relay->input_method_destroy.notify = handle_im_destroy; 507 relay->input_method_destroy.notify = handle_im_destroy;
508 wl_signal_add(&relay->input_method->events.new_popup_surface,
509 &relay->input_method_new_popup_surface);
510 relay->input_method_new_popup_surface.notify = handle_im_new_popup_surface;
283 511
284 struct sway_text_input *text_input = relay_get_focusable_text_input(relay); 512 struct sway_text_input *text_input = relay_get_focusable_text_input(relay);
285 if (text_input) { 513 if (text_input) {
286 wlr_text_input_v3_send_enter(text_input->input, 514 text_input_send_enter(text_input,
287 text_input->pending_focused_surface); 515 text_input->pending_focused_surface);
288 text_input_set_pending_focused_surface(text_input, NULL); 516 text_input_set_pending_focused_surface(text_input, NULL);
289 } 517 }
@@ -293,6 +521,7 @@ void sway_input_method_relay_init(struct sway_seat *seat,
293 struct sway_input_method_relay *relay) { 521 struct sway_input_method_relay *relay) {
294 relay->seat = seat; 522 relay->seat = seat;
295 wl_list_init(&relay->text_inputs); 523 wl_list_init(&relay->text_inputs);
524 wl_list_init(&relay->input_popups);
296 525
297 relay->text_input_new.notify = relay_handle_text_input; 526 relay->text_input_new.notify = relay_handle_text_input;
298 wl_signal_add(&server.text_input->events.text_input, 527 wl_signal_add(&server.text_input->events.text_input,
diff --git a/sway/ipc-json.c b/sway/ipc-json.c
index 58356d4e..1ee39124 100644
--- a/sway/ipc-json.c
+++ b/sway/ipc-json.c
@@ -11,6 +11,7 @@
11#include "log.h" 11#include "log.h"
12#include "sway/config.h" 12#include "sway/config.h"
13#include "sway/ipc-json.h" 13#include "sway/ipc-json.h"
14#include "sway/server.h"
14#include "sway/tree/container.h" 15#include "sway/tree/container.h"
15#include "sway/tree/view.h" 16#include "sway/tree/view.h"
16#include "sway/tree/workspace.h" 17#include "sway/tree/workspace.h"
@@ -154,7 +155,7 @@ static json_object *ipc_json_output_mode_description(
154 return mode_object; 155 return mode_object;
155} 156}
156 157
157#if HAVE_XWAYLAND 158#if WLR_HAS_XWAYLAND
158static const char *ipc_json_xwindow_type_description(struct sway_view *view) { 159static const char *ipc_json_xwindow_type_description(struct sway_view *view) {
159 struct wlr_xwayland_surface *surface = view->wlr_xwayland_surface; 160 struct wlr_xwayland_surface *surface = view->wlr_xwayland_surface;
160 struct sway_xwayland *xwayland = &server.xwayland; 161 struct sway_xwayland *xwayland = &server.xwayland;
@@ -288,6 +289,8 @@ static json_object *ipc_json_create_node(int id, const char* type, char *name,
288 json_object_object_add(object, "focus", focus); 289 json_object_object_add(object, "focus", focus);
289 json_object_object_add(object, "fullscreen_mode", json_object_new_int(0)); 290 json_object_object_add(object, "fullscreen_mode", json_object_new_int(0));
290 json_object_object_add(object, "sticky", json_object_new_boolean(false)); 291 json_object_object_add(object, "sticky", json_object_new_boolean(false));
292 json_object_object_add(object, "floating", NULL);
293 json_object_object_add(object, "scratchpad_state", NULL);
291 294
292 return object; 295 return object;
293} 296}
@@ -631,7 +634,7 @@ static void ipc_json_describe_view(struct sway_container *c, json_object *object
631 json_object_new_string(ipc_json_content_type_description(content_type))); 634 json_object_new_string(ipc_json_content_type_description(content_type)));
632 } 635 }
633 636
634#if HAVE_XWAYLAND 637#if WLR_HAS_XWAYLAND
635 if (c->view->type == SWAY_VIEW_XWAYLAND) { 638 if (c->view->type == SWAY_VIEW_XWAYLAND) {
636 json_object_object_add(object, "window", 639 json_object_object_add(object, "window",
637 json_object_new_int(view_get_x11_window_id(c->view))); 640 json_object_new_int(view_get_x11_window_id(c->view)));
@@ -675,7 +678,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) { 678static void ipc_json_describe_container(struct sway_container *c, json_object *object) {
676 json_object_object_add(object, "name", 679 json_object_object_add(object, "name",
677 c->title ? json_object_new_string(c->title) : NULL); 680 c->title ? json_object_new_string(c->title) : NULL);
678 if (container_is_floating(c)) { 681 bool floating = container_is_floating(c);
682 if (floating) {
679 json_object_object_add(object, "type", 683 json_object_object_add(object, "type",
680 json_object_new_string("floating_con")); 684 json_object_new_string("floating_con"));
681 } 685 }
@@ -693,9 +697,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)); 697 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)); 698 json_object_object_add(object, "sticky", json_object_new_boolean(c->is_sticky));
695 699
700 // sway doesn't track the floating reason, so we can't use "auto_on" or "user_off"
701 json_object_object_add(object, "floating",
702 json_object_new_string(floating ? "user_on" : "auto_off"));
703
696 json_object_object_add(object, "fullscreen_mode", 704 json_object_object_add(object, "fullscreen_mode",
697 json_object_new_int(c->pending.fullscreen_mode)); 705 json_object_new_int(c->pending.fullscreen_mode));
698 706
707 // sway doesn't track if window was resized in scratchpad, so we can't use "changed"
708 json_object_object_add(object, "scratchpad_state",
709 json_object_new_string(!c->scratchpad ? "none" : "fresh"));
710
699 struct sway_node *parent = node_get_parent(&c->node); 711 struct sway_node *parent = node_get_parent(&c->node);
700 struct wlr_box parent_box = {0, 0, 0, 0}; 712 struct wlr_box parent_box = {0, 0, 0, 0};
701 713
@@ -1086,10 +1098,6 @@ json_object *ipc_json_describe_input(struct sway_input_device *device) {
1086 json_object_new_string(device->identifier)); 1098 json_object_new_string(device->identifier));
1087 json_object_object_add(object, "name", 1099 json_object_object_add(object, "name",
1088 json_object_new_string(device->wlr_device->name)); 1100 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", 1101 json_object_object_add(object, "type",
1094 json_object_new_string( 1102 json_object_new_string(
1095 input_device_get_type(device))); 1103 input_device_get_type(device)));
@@ -1143,6 +1151,10 @@ json_object *ipc_json_describe_input(struct sway_input_device *device) {
1143 libinput_dev = wlr_libinput_get_device_handle(device->wlr_device); 1151 libinput_dev = wlr_libinput_get_device_handle(device->wlr_device);
1144 json_object_object_add(object, "libinput", 1152 json_object_object_add(object, "libinput",
1145 describe_libinput_device(libinput_dev)); 1153 describe_libinput_device(libinput_dev));
1154 json_object_object_add(object, "vendor",
1155 json_object_new_int(libinput_device_get_id_vendor(libinput_dev)));
1156 json_object_object_add(object, "product",
1157 json_object_new_int(libinput_device_get_id_product(libinput_dev)));
1146 } 1158 }
1147#endif 1159#endif
1148 1160
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/meson.build b/sway/meson.build
index d937e425..a189fe9a 100644
--- a/sway/meson.build
+++ b/sway/meson.build
@@ -202,6 +202,7 @@ sway_sources = files(
202 'commands/output/toggle.c', 202 'commands/output/toggle.c',
203 'commands/output/transform.c', 203 'commands/output/transform.c',
204 'commands/output/unplug.c', 204 'commands/output/unplug.c',
205 'commands/output/color_profile.c',
205 206
206 'tree/arrange.c', 207 'tree/arrange.c',
207 'tree/container.c', 208 'tree/container.c',
@@ -231,7 +232,7 @@ sway_deps = [
231 xcb_icccm, 232 xcb_icccm,
232] 233]
233 234
234if have_xwayland 235if wlroots_features['xwayland']
235 sway_sources += 'desktop/xwayland.c' 236 sway_sources += 'desktop/xwayland.c'
236endif 237endif
237 238
diff --git a/sway/server.c b/sway/server.c
index cc20e89d..edbc1a4b 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>
@@ -49,7 +56,7 @@
49#include "sway/input/cursor.h" 56#include "sway/input/cursor.h"
50#include "sway/tree/root.h" 57#include "sway/tree/root.h"
51 58
52#if HAVE_XWAYLAND 59#if WLR_HAS_XWAYLAND
53#include <wlr/xwayland/shell.h> 60#include <wlr/xwayland/shell.h>
54#include "sway/xwayland.h" 61#include "sway/xwayland.h"
55#endif 62#endif
@@ -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,12 +112,14 @@ 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 ||
117 global == server.xdg_output_manager_v1->global;
107} 118}
108 119
109static bool filter_global(const struct wl_client *client, 120static bool filter_global(const struct wl_client *client,
110 const struct wl_global *global, void *data) { 121 const struct wl_global *global, void *data) {
111#if HAVE_XWAYLAND 122#if WLR_HAS_XWAYLAND
112 struct wlr_xwayland *xwayland = server.xwayland.wlr_xwayland; 123 struct wlr_xwayland *xwayland = server.xwayland.wlr_xwayland;
113 if (xwayland && global == xwayland->shell_v1->global) { 124 if (xwayland && global == xwayland->shell_v1->global) {
114 return xwayland->server != NULL && client == xwayland->server->client; 125 return xwayland->server != NULL && client == xwayland->server->client;
@@ -163,6 +174,45 @@ static void detect_proprietary(struct wlr_backend *backend, void *data) {
163 drmFreeVersion(version); 174 drmFreeVersion(version);
164} 175}
165 176
177static void handle_renderer_lost(struct wl_listener *listener, void *data) {
178 struct sway_server *server = wl_container_of(listener, server, renderer_lost);
179
180 sway_log(SWAY_INFO, "Re-creating renderer after GPU reset");
181
182 struct wlr_renderer *renderer = wlr_renderer_autocreate(server->backend);
183 if (renderer == NULL) {
184 sway_log(SWAY_ERROR, "Unable to create renderer");
185 return;
186 }
187
188 struct wlr_allocator *allocator =
189 wlr_allocator_autocreate(server->backend, renderer);
190 if (allocator == NULL) {
191 sway_log(SWAY_ERROR, "Unable to create allocator");
192 wlr_renderer_destroy(renderer);
193 return;
194 }
195
196 struct wlr_renderer *old_renderer = server->renderer;
197 struct wlr_allocator *old_allocator = server->allocator;
198 server->renderer = renderer;
199 server->allocator = allocator;
200
201 wl_list_remove(&server->renderer_lost.link);
202 wl_signal_add(&server->renderer->events.lost, &server->renderer_lost);
203
204 wlr_compositor_set_renderer(server->compositor, renderer);
205
206 for (int i = 0; i < root->outputs->length; ++i) {
207 struct sway_output *output = root->outputs->items[i];
208 wlr_output_init_render(output->wlr_output,
209 server->allocator, server->renderer);
210 }
211
212 wlr_allocator_destroy(old_allocator);
213 wlr_renderer_destroy(old_renderer);
214}
215
166bool server_init(struct sway_server *server) { 216bool server_init(struct sway_server *server) {
167 sway_log(SWAY_DEBUG, "Initializing Wayland server"); 217 sway_log(SWAY_DEBUG, "Initializing Wayland server");
168 server->wl_display = wl_display_create(); 218 server->wl_display = wl_display_create();
@@ -186,15 +236,17 @@ bool server_init(struct sway_server *server) {
186 return false; 236 return false;
187 } 237 }
188 238
239 server->renderer_lost.notify = handle_renderer_lost;
240 wl_signal_add(&server->renderer->events.lost, &server->renderer_lost);
241
189 wlr_renderer_init_wl_shm(server->renderer, server->wl_display); 242 wlr_renderer_init_wl_shm(server->renderer, server->wl_display);
190 243
191 if (wlr_renderer_get_dmabuf_texture_formats(server->renderer) != NULL) { 244 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( 245 server->linux_dmabuf_v1 = wlr_linux_dmabuf_v1_create_with_renderer(
193 server->wl_display, 4, server->renderer); 246 server->wl_display, 4, server->renderer);
194 } 247 if (debug.legacy_wl_drm) {
195 if (wlr_renderer_get_dmabuf_texture_formats(server->renderer) != NULL && 248 wlr_drm_create(server->wl_display, server->renderer);
196 debug.legacy_wl_drm) { 249 }
197 wlr_drm_create(server->wl_display, server->renderer);
198 } 250 }
199 251
200 server->allocator = wlr_allocator_autocreate(server->backend, 252 server->allocator = wlr_allocator_autocreate(server->backend,
@@ -224,7 +276,8 @@ bool server_init(struct sway_server *server) {
224 wl_signal_add(&root->output_layout->events.change, 276 wl_signal_add(&root->output_layout->events.change,
225 &server->output_layout_change); 277 &server->output_layout_change);
226 278
227 wlr_xdg_output_manager_v1_create(server->wl_display, root->output_layout); 279 server->xdg_output_manager_v1 =
280 wlr_xdg_output_manager_v1_create(server->wl_display, root->output_layout);
228 281
229 server->idle_notifier_v1 = wlr_idle_notifier_v1_create(server->wl_display); 282 server->idle_notifier_v1 = wlr_idle_notifier_v1_create(server->wl_display);
230 sway_idle_inhibit_manager_v1_init(); 283 sway_idle_inhibit_manager_v1_init();
@@ -289,6 +342,8 @@ bool server_init(struct sway_server *server) {
289 &server->output_power_manager_set_mode); 342 &server->output_power_manager_set_mode);
290 server->input_method = wlr_input_method_manager_v2_create(server->wl_display); 343 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); 344 server->text_input = wlr_text_input_manager_v3_create(server->wl_display);
345 server->foreign_toplevel_list =
346 wlr_ext_foreign_toplevel_list_v1_create(server->wl_display, SWAY_FOREIGN_TOPLEVEL_LIST_VERSION);
292 server->foreign_toplevel_manager = 347 server->foreign_toplevel_manager =
293 wlr_foreign_toplevel_manager_v1_create(server->wl_display); 348 wlr_foreign_toplevel_manager_v1_create(server->wl_display);
294 349
@@ -384,16 +439,17 @@ bool server_init(struct sway_server *server) {
384 439
385void server_fini(struct sway_server *server) { 440void server_fini(struct sway_server *server) {
386 // TODO: free sway-specific resources 441 // TODO: free sway-specific resources
387#if HAVE_XWAYLAND 442#if WLR_HAS_XWAYLAND
388 wlr_xwayland_destroy(server->xwayland.wlr_xwayland); 443 wlr_xwayland_destroy(server->xwayland.wlr_xwayland);
389#endif 444#endif
390 wl_display_destroy_clients(server->wl_display); 445 wl_display_destroy_clients(server->wl_display);
446 wlr_backend_destroy(server->backend);
391 wl_display_destroy(server->wl_display); 447 wl_display_destroy(server->wl_display);
392 list_free(server->dirty_nodes); 448 list_free(server->dirty_nodes);
393} 449}
394 450
395bool server_start(struct sway_server *server) { 451bool server_start(struct sway_server *server) {
396#if HAVE_XWAYLAND 452#if WLR_HAS_XWAYLAND
397 if (config->xwayland != XWAYLAND_MODE_DISABLED) { 453 if (config->xwayland != XWAYLAND_MODE_DISABLED) {
398 sway_log(SWAY_DEBUG, "Initializing Xwayland (lazy=%d)", 454 sway_log(SWAY_DEBUG, "Initializing Xwayland (lazy=%d)",
399 config->xwayland == XWAYLAND_MODE_LAZY); 455 config->xwayland == XWAYLAND_MODE_LAZY);
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..6d7c0860 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
@@ -180,6 +178,18 @@ must be separated by one space. For example:
180 updated to work with different bit depths. This command is experimental, 178 updated to work with different bit depths. This command is experimental,
181 and may be removed or changed in the future. 179 and may be removed or changed in the future.
182 180
181*output* <name> color_profile srgb|[icc <file>]
182 Sets the color profile for an output. The default is _srgb_. <file> should be a
183 path to a display ICC profile.
184
185 Not all renderers support this feature; currently it only works with the
186 the Vulkan renderer. Even where supported, the application of the color
187 profile may be inaccurate.
188
189 This command is experimental, and may be removed or changed in the future. It
190 may have no effect or produce unexpected output when used together with future
191 HDR support features.
192
183# SEE ALSO 193# SEE ALSO
184 194
185*sway*(5) *sway-input*(5) 195*sway*(5) *sway-input*(5)
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..6c8dd6dc 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>
@@ -280,6 +279,7 @@ void output_destroy(struct sway_output *output) {
280 list_free(output->workspaces); 279 list_free(output->workspaces);
281 list_free(output->current.workspaces); 280 list_free(output->current.workspaces);
282 wl_event_source_remove(output->repaint_timer); 281 wl_event_source_remove(output->repaint_timer);
282 wlr_color_transform_unref(output->color_transform);
283 free(output); 283 free(output);
284} 284}
285 285
diff --git a/sway/tree/root.c b/sway/tree/root.c
index e9cea5e2..20fcfa59 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>
@@ -54,7 +53,7 @@ struct sway_root *root_create(struct wl_display *wl_display) {
54 root->layers.shell_top = alloc_scene_tree(root->layer_tree, &failed); 53 root->layers.shell_top = alloc_scene_tree(root->layer_tree, &failed);
55 root->layers.fullscreen = alloc_scene_tree(root->layer_tree, &failed); 54 root->layers.fullscreen = alloc_scene_tree(root->layer_tree, &failed);
56 root->layers.fullscreen_global = alloc_scene_tree(root->layer_tree, &failed); 55 root->layers.fullscreen_global = alloc_scene_tree(root->layer_tree, &failed);
57#if HAVE_XWAYLAND 56#if WLR_HAS_XWAYLAND
58 root->layers.unmanaged = alloc_scene_tree(root->layer_tree, &failed); 57 root->layers.unmanaged = alloc_scene_tree(root->layer_tree, &failed);
59#endif 58#endif
60 root->layers.shell_overlay = alloc_scene_tree(root->layer_tree, &failed); 59 root->layers.shell_overlay = alloc_scene_tree(root->layer_tree, &failed);
diff --git a/sway/tree/view.c b/sway/tree/view.c
index d6984178..086a8ff3 100644
--- a/sway/tree/view.c
+++ b/sway/tree/view.c
@@ -1,15 +1,16 @@
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>
4#include <wlr/config.h>
5#include <wlr/render/wlr_renderer.h> 5#include <wlr/render/wlr_renderer.h>
6#include <wlr/types/wlr_buffer.h> 6#include <wlr/types/wlr_buffer.h>
7#include <wlr/types/wlr_ext_foreign_toplevel_list_v1.h>
8#include <wlr/types/wlr_foreign_toplevel_management_v1.h>
7#include <wlr/types/wlr_output_layout.h> 9#include <wlr/types/wlr_output_layout.h>
8#include <wlr/types/wlr_server_decoration.h> 10#include <wlr/types/wlr_server_decoration.h>
9#include <wlr/types/wlr_subcompositor.h> 11#include <wlr/types/wlr_subcompositor.h>
10#include <wlr/types/wlr_xdg_decoration_v1.h> 12#include <wlr/types/wlr_xdg_decoration_v1.h>
11#include "config.h" 13#if WLR_HAS_XWAYLAND
12#if HAVE_XWAYLAND
13#include <wlr/xwayland.h> 14#include <wlr/xwayland.h>
14#endif 15#endif
15#include "list.h" 16#include "list.h"
@@ -125,7 +126,7 @@ const char *view_get_instance(struct sway_view *view) {
125 } 126 }
126 return NULL; 127 return NULL;
127} 128}
128#if HAVE_XWAYLAND 129#if WLR_HAS_XWAYLAND
129uint32_t view_get_x11_window_id(struct sway_view *view) { 130uint32_t view_get_x11_window_id(struct sway_view *view) {
130 if (view->impl->get_int_prop) { 131 if (view->impl->get_int_prop) {
131 return view->impl->get_int_prop(view, VIEW_PROP_X11_WINDOW_ID); 132 return view->impl->get_int_prop(view, VIEW_PROP_X11_WINDOW_ID);
@@ -158,7 +159,7 @@ const char *view_get_shell(struct sway_view *view) {
158 switch(view->type) { 159 switch(view->type) {
159 case SWAY_VIEW_XDG_SHELL: 160 case SWAY_VIEW_XDG_SHELL:
160 return "xdg_shell"; 161 return "xdg_shell";
161#if HAVE_XWAYLAND 162#if WLR_HAS_XWAYLAND
162 case SWAY_VIEW_XWAYLAND: 163 case SWAY_VIEW_XWAYLAND:
163 return "xwayland"; 164 return "xwayland";
164#endif 165#endif
@@ -172,9 +173,9 @@ void view_get_constraints(struct sway_view *view, double *min_width,
172 view->impl->get_constraints(view, 173 view->impl->get_constraints(view,
173 min_width, max_width, min_height, max_height); 174 min_width, max_width, min_height, max_height);
174 } else { 175 } else {
175 *min_width = DBL_MIN; 176 *min_width = 1;
176 *max_width = DBL_MAX; 177 *max_width = DBL_MAX;
177 *min_height = DBL_MIN; 178 *min_height = 1;
178 *max_height = DBL_MAX; 179 *max_height = DBL_MAX;
179 } 180 }
180} 181}
@@ -364,8 +365,8 @@ void view_autoconfigure(struct sway_view *view) {
364 365
365 con->pending.content_x = x; 366 con->pending.content_x = x;
366 con->pending.content_y = y; 367 con->pending.content_y = y;
367 con->pending.content_width = width; 368 con->pending.content_width = fmax(width, 1);
368 con->pending.content_height = height; 369 con->pending.content_height = fmax(height, 1);
369} 370}
370 371
371void view_set_activated(struct sway_view *view, bool activated) { 372void view_set_activated(struct sway_view *view, bool activated) {
@@ -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) {
@@ -492,7 +499,7 @@ void view_execute_criteria(struct sway_view *view) {
492static void view_populate_pid(struct sway_view *view) { 499static void view_populate_pid(struct sway_view *view) {
493 pid_t pid; 500 pid_t pid;
494 switch (view->type) { 501 switch (view->type) {
495#if HAVE_XWAYLAND 502#if WLR_HAS_XWAYLAND
496 case SWAY_VIEW_XWAYLAND:; 503 case SWAY_VIEW_XWAYLAND:;
497 struct wlr_xwayland_surface *surf = 504 struct wlr_xwayland_surface *surf =
498 wlr_xwayland_surface_try_from_wlr_surface(view->surface); 505 wlr_xwayland_surface_try_from_wlr_surface(view->surface);
@@ -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;
@@ -816,7 +838,7 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface,
816 838
817 bool set_focus = should_focus(view); 839 bool set_focus = should_focus(view);
818 840
819#if HAVE_XWAYLAND 841#if WLR_HAS_XWAYLAND
820 struct wlr_xwayland_surface *xsurface; 842 struct wlr_xwayland_surface *xsurface;
821 if ((xsurface = wlr_xwayland_surface_try_from_wlr_surface(wlr_surface))) { 843 if ((xsurface = wlr_xwayland_surface_try_from_wlr_surface(wlr_surface))) {
822 set_focus &= wlr_xwayland_icccm_input_model(xsurface) != 844 set_focus &= wlr_xwayland_icccm_input_model(xsurface) !=
@@ -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;
@@ -896,11 +927,14 @@ void view_update_size(struct sway_view *view) {
896void view_center_and_clip_surface(struct sway_view *view) { 927void view_center_and_clip_surface(struct sway_view *view) {
897 struct sway_container *con = view->container; 928 struct sway_container *con = view->container;
898 929
930 bool clip_to_geometry = true;
931
899 if (container_is_floating(con)) { 932 if (container_is_floating(con)) {
900 // We always center the current coordinates rather than the next, as the 933 // We always center the current coordinates rather than the next, as the
901 // geometry immediately affects the currently active rendering. 934 // geometry immediately affects the currently active rendering.
902 int x = (int) fmax(0, (con->current.content_width - view->geometry.width) / 2); 935 int x = (int) fmax(0, (con->current.content_width - view->geometry.width) / 2);
903 int y = (int) fmax(0, (con->current.content_height - view->geometry.height) / 2); 936 int y = (int) fmax(0, (con->current.content_height - view->geometry.height) / 2);
937 clip_to_geometry = !view->using_csd;
904 938
905 wlr_scene_node_set_position(&view->content_tree->node, x, y); 939 wlr_scene_node_set_position(&view->content_tree->node, x, y);
906 } else { 940 } else {
@@ -909,12 +943,16 @@ void view_center_and_clip_surface(struct sway_view *view) {
909 943
910 // only make sure to clip the content if there is content to clip 944 // only make sure to clip the content if there is content to clip
911 if (!wl_list_empty(&con->view->content_tree->children)) { 945 if (!wl_list_empty(&con->view->content_tree->children)) {
912 wlr_scene_subsurface_tree_set_clip(&con->view->content_tree->node, &(struct wlr_box){ 946 struct wlr_box clip = {0};
913 .x = con->view->geometry.x, 947 if (clip_to_geometry) {
914 .y = con->view->geometry.y, 948 clip = (struct wlr_box){
915 .width = con->current.content_width, 949 .x = con->view->geometry.x,
916 .height = con->current.content_height, 950 .y = con->view->geometry.y,
917 }); 951 .width = con->current.content_width,
952 .height = con->current.content_height,
953 };
954 }
955 wlr_scene_subsurface_tree_set_clip(&con->view->content_tree->node, &clip);
918 } 956 }
919} 957}
920 958
@@ -923,7 +961,7 @@ struct sway_view *view_from_wlr_surface(struct wlr_surface *wlr_surface) {
923 if ((xdg_surface = wlr_xdg_surface_try_from_wlr_surface(wlr_surface))) { 961 if ((xdg_surface = wlr_xdg_surface_try_from_wlr_surface(wlr_surface))) {
924 return view_from_wlr_xdg_surface(xdg_surface); 962 return view_from_wlr_xdg_surface(xdg_surface);
925 } 963 }
926#if HAVE_XWAYLAND 964#if WLR_HAS_XWAYLAND
927 struct wlr_xwayland_surface *xsurface; 965 struct wlr_xwayland_surface *xsurface;
928 if ((xsurface = wlr_xwayland_surface_try_from_wlr_surface(wlr_surface))) { 966 if ((xsurface = wlr_xwayland_surface_try_from_wlr_surface(wlr_surface))) {
929 return view_from_wlr_xwayland_surface(xsurface); 967 return view_from_wlr_xwayland_surface(xsurface);
@@ -1014,6 +1052,18 @@ static size_t parse_title_format(struct sway_view *view, char *buffer) {
1014 return len; 1052 return len;
1015} 1053}
1016 1054
1055void view_update_app_id(struct sway_view *view) {
1056 const char *app_id = view_get_app_id(view);
1057
1058 if (view->foreign_toplevel && app_id) {
1059 wlr_foreign_toplevel_handle_v1_set_app_id(view->foreign_toplevel, app_id);
1060 }
1061
1062 if (view->ext_foreign_toplevel) {
1063 update_ext_foreign_toplevel(view);
1064 }
1065}
1066
1017void view_update_title(struct sway_view *view, bool force) { 1067void view_update_title(struct sway_view *view, bool force) {
1018 const char *title = view_get_title(view); 1068 const char *title = view_get_title(view);
1019 1069
@@ -1060,6 +1110,10 @@ void view_update_title(struct sway_view *view, bool force) {
1060 if (view->foreign_toplevel && title) { 1110 if (view->foreign_toplevel && title) {
1061 wlr_foreign_toplevel_handle_v1_set_title(view->foreign_toplevel, title); 1111 wlr_foreign_toplevel_handle_v1_set_title(view->foreign_toplevel, title);
1062 } 1112 }
1113
1114 if (view->ext_foreign_toplevel) {
1115 update_ext_foreign_toplevel(view);
1116 }
1063} 1117}
1064 1118
1065bool view_is_visible(struct sway_view *view) { 1119bool view_is_visible(struct sway_view *view) {
@@ -1131,7 +1185,7 @@ void view_set_urgent(struct sway_view *view, bool enable) {
1131 1185
1132 ipc_event_window(view->container, "urgent"); 1186 ipc_event_window(view->container, "urgent");
1133 1187
1134 if (!container_is_scratchpad_hidden(view->container)) { 1188 if (!container_is_scratchpad_hidden_or_child(view->container)) {
1135 workspace_detect_urgent(view->container->pending.workspace); 1189 workspace_detect_urgent(view->container->pending.workspace);
1136 } 1190 }
1137} 1191}
diff --git a/sway/tree/workspace.c b/sway/tree/workspace.c
index 40d33435..f8709a4c 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>
@@ -11,6 +10,7 @@
11#include "sway/input/seat.h" 10#include "sway/input/seat.h"
12#include "sway/ipc-server.h" 11#include "sway/ipc-server.h"
13#include "sway/output.h" 12#include "sway/output.h"
13#include "sway/server.h"
14#include "sway/tree/arrange.h" 14#include "sway/tree/arrange.h"
15#include "sway/tree/container.h" 15#include "sway/tree/container.h"
16#include "sway/tree/node.h" 16#include "sway/tree/node.h"
@@ -708,6 +708,11 @@ void workspace_for_each_container(struct sway_workspace *ws,
708struct sway_container *workspace_find_container(struct sway_workspace *ws, 708struct sway_container *workspace_find_container(struct sway_workspace *ws,
709 bool (*test)(struct sway_container *con, void *data), void *data) { 709 bool (*test)(struct sway_container *con, void *data), void *data) {
710 struct sway_container *result = NULL; 710 struct sway_container *result = NULL;
711 if (ws == NULL){
712 sway_log(SWAY_ERROR, "Cannot find container with no workspace.");
713 return NULL;
714 }
715
711 // Tiling 716 // Tiling
712 for (int i = 0; i < ws->tiling->length; ++i) { 717 for (int i = 0; i < ws->tiling->length; ++i) {
713 struct sway_container *child = ws->tiling->items[i]; 718 struct sway_container *child = ws->tiling->items[i];
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) {