diff options
Diffstat (limited to 'sway/desktop')
-rw-r--r-- | sway/desktop/desktop.c | 39 | ||||
-rw-r--r-- | sway/desktop/idle_inhibit_v1.c | 62 | ||||
-rw-r--r-- | sway/desktop/launcher.c | 267 | ||||
-rw-r--r-- | sway/desktop/layer_shell.c | 778 | ||||
-rw-r--r-- | sway/desktop/output.c | 1087 | ||||
-rw-r--r-- | sway/desktop/render.c | 1125 | ||||
-rw-r--r-- | sway/desktop/surface.c | 46 | ||||
-rw-r--r-- | sway/desktop/transaction.c | 733 | ||||
-rw-r--r-- | sway/desktop/xdg_shell.c | 401 | ||||
-rw-r--r-- | sway/desktop/xwayland.c | 340 |
10 files changed, 1932 insertions, 2946 deletions
diff --git a/sway/desktop/desktop.c b/sway/desktop/desktop.c deleted file mode 100644 index ec45d80a..00000000 --- a/sway/desktop/desktop.c +++ /dev/null | |||
@@ -1,39 +0,0 @@ | |||
1 | #include "sway/tree/container.h" | ||
2 | #include "sway/desktop.h" | ||
3 | #include "sway/output.h" | ||
4 | |||
5 | void desktop_damage_surface(struct wlr_surface *surface, double lx, double ly, | ||
6 | bool whole) { | ||
7 | for (int i = 0; i < root->outputs->length; ++i) { | ||
8 | struct sway_output *output = root->outputs->items[i]; | ||
9 | struct wlr_box *output_box = wlr_output_layout_get_box( | ||
10 | root->output_layout, output->wlr_output); | ||
11 | output_damage_surface(output, lx - output_box->x, | ||
12 | ly - output_box->y, surface, whole); | ||
13 | } | ||
14 | } | ||
15 | |||
16 | void desktop_damage_whole_container(struct sway_container *con) { | ||
17 | for (int i = 0; i < root->outputs->length; ++i) { | ||
18 | struct sway_output *output = root->outputs->items[i]; | ||
19 | output_damage_whole_container(output, con); | ||
20 | } | ||
21 | } | ||
22 | |||
23 | void desktop_damage_box(struct wlr_box *box) { | ||
24 | for (int i = 0; i < root->outputs->length; ++i) { | ||
25 | struct sway_output *output = root->outputs->items[i]; | ||
26 | output_damage_box(output, box); | ||
27 | } | ||
28 | } | ||
29 | |||
30 | void desktop_damage_view(struct sway_view *view) { | ||
31 | desktop_damage_whole_container(view->container); | ||
32 | struct wlr_box box = { | ||
33 | .x = view->container->current.content_x - view->geometry.x, | ||
34 | .y = view->container->current.content_y - view->geometry.y, | ||
35 | .width = view->surface->current.width, | ||
36 | .height = view->surface->current.height, | ||
37 | }; | ||
38 | desktop_damage_box(&box); | ||
39 | } | ||
diff --git a/sway/desktop/idle_inhibit_v1.c b/sway/desktop/idle_inhibit_v1.c index a5cfd5b2..f3af7aa1 100644 --- a/sway/desktop/idle_inhibit_v1.c +++ b/sway/desktop/idle_inhibit_v1.c | |||
@@ -1,5 +1,5 @@ | |||
1 | #include <stdlib.h> | 1 | #include <stdlib.h> |
2 | #include <wlr/types/wlr_idle.h> | 2 | #include <wlr/types/wlr_idle_notify_v1.h> |
3 | #include "log.h" | 3 | #include "log.h" |
4 | #include "sway/desktop/idle_inhibit_v1.h" | 4 | #include "sway/desktop/idle_inhibit_v1.h" |
5 | #include "sway/input/seat.h" | 5 | #include "sway/input/seat.h" |
@@ -11,7 +11,7 @@ | |||
11 | static void destroy_inhibitor(struct sway_idle_inhibitor_v1 *inhibitor) { | 11 | static void destroy_inhibitor(struct sway_idle_inhibitor_v1 *inhibitor) { |
12 | wl_list_remove(&inhibitor->link); | 12 | wl_list_remove(&inhibitor->link); |
13 | wl_list_remove(&inhibitor->destroy.link); | 13 | wl_list_remove(&inhibitor->destroy.link); |
14 | sway_idle_inhibit_v1_check_active(inhibitor->manager); | 14 | sway_idle_inhibit_v1_check_active(); |
15 | free(inhibitor); | 15 | free(inhibitor); |
16 | } | 16 | } |
17 | 17 | ||
@@ -34,43 +34,43 @@ void handle_idle_inhibitor_v1(struct wl_listener *listener, void *data) { | |||
34 | return; | 34 | return; |
35 | } | 35 | } |
36 | 36 | ||
37 | inhibitor->manager = manager; | ||
38 | inhibitor->mode = INHIBIT_IDLE_APPLICATION; | 37 | inhibitor->mode = INHIBIT_IDLE_APPLICATION; |
39 | inhibitor->view = view_from_wlr_surface(wlr_inhibitor->surface); | 38 | inhibitor->wlr_inhibitor = wlr_inhibitor; |
40 | wl_list_insert(&manager->inhibitors, &inhibitor->link); | 39 | wl_list_insert(&manager->inhibitors, &inhibitor->link); |
41 | 40 | ||
42 | inhibitor->destroy.notify = handle_destroy; | 41 | inhibitor->destroy.notify = handle_destroy; |
43 | wl_signal_add(&wlr_inhibitor->events.destroy, &inhibitor->destroy); | 42 | wl_signal_add(&wlr_inhibitor->events.destroy, &inhibitor->destroy); |
44 | 43 | ||
45 | sway_idle_inhibit_v1_check_active(manager); | 44 | sway_idle_inhibit_v1_check_active(); |
46 | } | 45 | } |
47 | 46 | ||
48 | void sway_idle_inhibit_v1_user_inhibitor_register(struct sway_view *view, | 47 | void sway_idle_inhibit_v1_user_inhibitor_register(struct sway_view *view, |
49 | enum sway_idle_inhibit_mode mode) { | 48 | enum sway_idle_inhibit_mode mode) { |
49 | struct sway_idle_inhibit_manager_v1 *manager = &server.idle_inhibit_manager_v1; | ||
50 | |||
50 | struct sway_idle_inhibitor_v1 *inhibitor = | 51 | struct sway_idle_inhibitor_v1 *inhibitor = |
51 | calloc(1, sizeof(struct sway_idle_inhibitor_v1)); | 52 | calloc(1, sizeof(struct sway_idle_inhibitor_v1)); |
52 | if (!inhibitor) { | 53 | if (!inhibitor) { |
53 | return; | 54 | return; |
54 | } | 55 | } |
55 | 56 | ||
56 | inhibitor->manager = server.idle_inhibit_manager_v1; | ||
57 | inhibitor->mode = mode; | 57 | inhibitor->mode = mode; |
58 | inhibitor->view = view; | 58 | inhibitor->view = view; |
59 | wl_list_insert(&inhibitor->manager->inhibitors, &inhibitor->link); | 59 | wl_list_insert(&manager->inhibitors, &inhibitor->link); |
60 | 60 | ||
61 | inhibitor->destroy.notify = handle_destroy; | 61 | inhibitor->destroy.notify = handle_destroy; |
62 | wl_signal_add(&view->events.unmap, &inhibitor->destroy); | 62 | wl_signal_add(&view->events.unmap, &inhibitor->destroy); |
63 | 63 | ||
64 | sway_idle_inhibit_v1_check_active(inhibitor->manager); | 64 | sway_idle_inhibit_v1_check_active(); |
65 | } | 65 | } |
66 | 66 | ||
67 | struct sway_idle_inhibitor_v1 *sway_idle_inhibit_v1_user_inhibitor_for_view( | 67 | struct sway_idle_inhibitor_v1 *sway_idle_inhibit_v1_user_inhibitor_for_view( |
68 | struct sway_view *view) { | 68 | struct sway_view *view) { |
69 | struct sway_idle_inhibit_manager_v1 *manager = &server.idle_inhibit_manager_v1; | ||
69 | struct sway_idle_inhibitor_v1 *inhibitor; | 70 | struct sway_idle_inhibitor_v1 *inhibitor; |
70 | wl_list_for_each(inhibitor, &server.idle_inhibit_manager_v1->inhibitors, | 71 | wl_list_for_each(inhibitor, &manager->inhibitors, link) { |
71 | link) { | 72 | if (inhibitor->mode != INHIBIT_IDLE_APPLICATION && |
72 | if (inhibitor->view == view && | 73 | inhibitor->view == view) { |
73 | inhibitor->mode != INHIBIT_IDLE_APPLICATION) { | ||
74 | return inhibitor; | 74 | return inhibitor; |
75 | } | 75 | } |
76 | } | 76 | } |
@@ -79,11 +79,11 @@ struct sway_idle_inhibitor_v1 *sway_idle_inhibit_v1_user_inhibitor_for_view( | |||
79 | 79 | ||
80 | struct sway_idle_inhibitor_v1 *sway_idle_inhibit_v1_application_inhibitor_for_view( | 80 | struct sway_idle_inhibitor_v1 *sway_idle_inhibit_v1_application_inhibitor_for_view( |
81 | struct sway_view *view) { | 81 | struct sway_view *view) { |
82 | struct sway_idle_inhibit_manager_v1 *manager = &server.idle_inhibit_manager_v1; | ||
82 | struct sway_idle_inhibitor_v1 *inhibitor; | 83 | struct sway_idle_inhibitor_v1 *inhibitor; |
83 | wl_list_for_each(inhibitor, &server.idle_inhibit_manager_v1->inhibitors, | 84 | wl_list_for_each(inhibitor, &manager->inhibitors, link) { |
84 | link) { | 85 | if (inhibitor->mode == INHIBIT_IDLE_APPLICATION && |
85 | if (inhibitor->view == view && | 86 | view_from_wlr_surface(inhibitor->wlr_inhibitor->surface) == view) { |
86 | inhibitor->mode == INHIBIT_IDLE_APPLICATION) { | ||
87 | return inhibitor; | 87 | return inhibitor; |
88 | } | 88 | } |
89 | } | 89 | } |
@@ -104,10 +104,10 @@ void sway_idle_inhibit_v1_user_inhibitor_destroy( | |||
104 | 104 | ||
105 | bool sway_idle_inhibit_v1_is_active(struct sway_idle_inhibitor_v1 *inhibitor) { | 105 | bool sway_idle_inhibit_v1_is_active(struct sway_idle_inhibitor_v1 *inhibitor) { |
106 | switch (inhibitor->mode) { | 106 | switch (inhibitor->mode) { |
107 | case INHIBIT_IDLE_APPLICATION: | 107 | case INHIBIT_IDLE_APPLICATION:; |
108 | // If there is no view associated with the inhibitor, assume visible | 108 | // If there is no view associated with the inhibitor, assume visible |
109 | return !inhibitor->view || !inhibitor->view->container || | 109 | struct sway_view *view = view_from_wlr_surface(inhibitor->wlr_inhibitor->surface); |
110 | view_is_visible(inhibitor->view); | 110 | return !view || !view->container || view_is_visible(view); |
111 | case INHIBIT_IDLE_FOCUS:; | 111 | case INHIBIT_IDLE_FOCUS:; |
112 | struct sway_seat *seat = NULL; | 112 | struct sway_seat *seat = NULL; |
113 | wl_list_for_each(seat, &server.input->seats, link) { | 113 | wl_list_for_each(seat, &server.input->seats, link) { |
@@ -130,8 +130,8 @@ bool sway_idle_inhibit_v1_is_active(struct sway_idle_inhibitor_v1 *inhibitor) { | |||
130 | return false; | 130 | return false; |
131 | } | 131 | } |
132 | 132 | ||
133 | void sway_idle_inhibit_v1_check_active( | 133 | void sway_idle_inhibit_v1_check_active(void) { |
134 | struct sway_idle_inhibit_manager_v1 *manager) { | 134 | struct sway_idle_inhibit_manager_v1 *manager = &server.idle_inhibit_manager_v1; |
135 | struct sway_idle_inhibitor_v1 *inhibitor; | 135 | struct sway_idle_inhibitor_v1 *inhibitor; |
136 | bool inhibited = false; | 136 | bool inhibited = false; |
137 | wl_list_for_each(inhibitor, &manager->inhibitors, link) { | 137 | wl_list_for_each(inhibitor, &manager->inhibitors, link) { |
@@ -139,27 +139,21 @@ void sway_idle_inhibit_v1_check_active( | |||
139 | break; | 139 | break; |
140 | } | 140 | } |
141 | } | 141 | } |
142 | wlr_idle_set_enabled(manager->idle, NULL, !inhibited); | 142 | wlr_idle_notifier_v1_set_inhibited(server.idle_notifier_v1, inhibited); |
143 | } | 143 | } |
144 | 144 | ||
145 | struct sway_idle_inhibit_manager_v1 *sway_idle_inhibit_manager_v1_create( | 145 | bool sway_idle_inhibit_manager_v1_init(void) { |
146 | struct wl_display *wl_display, struct wlr_idle *idle) { | 146 | struct sway_idle_inhibit_manager_v1 *manager = &server.idle_inhibit_manager_v1; |
147 | struct sway_idle_inhibit_manager_v1 *manager = | ||
148 | calloc(1, sizeof(struct sway_idle_inhibit_manager_v1)); | ||
149 | if (!manager) { | ||
150 | return NULL; | ||
151 | } | ||
152 | 147 | ||
153 | manager->wlr_manager = wlr_idle_inhibit_v1_create(wl_display); | 148 | manager->wlr_manager = wlr_idle_inhibit_v1_create(server.wl_display); |
154 | if (!manager->wlr_manager) { | 149 | if (!manager->wlr_manager) { |
155 | free(manager); | 150 | return false; |
156 | return NULL; | ||
157 | } | 151 | } |
158 | manager->idle = idle; | 152 | |
159 | wl_signal_add(&manager->wlr_manager->events.new_inhibitor, | 153 | wl_signal_add(&manager->wlr_manager->events.new_inhibitor, |
160 | &manager->new_idle_inhibitor_v1); | 154 | &manager->new_idle_inhibitor_v1); |
161 | manager->new_idle_inhibitor_v1.notify = handle_idle_inhibitor_v1; | 155 | manager->new_idle_inhibitor_v1.notify = handle_idle_inhibitor_v1; |
162 | wl_list_init(&manager->inhibitors); | 156 | wl_list_init(&manager->inhibitors); |
163 | 157 | ||
164 | return manager; | 158 | return true; |
165 | } | 159 | } |
diff --git a/sway/desktop/launcher.c b/sway/desktop/launcher.c new file mode 100644 index 00000000..28043d19 --- /dev/null +++ b/sway/desktop/launcher.c | |||
@@ -0,0 +1,267 @@ | |||
1 | #include <stdlib.h> | ||
2 | #include <string.h> | ||
3 | #include <wlr/types/wlr_xdg_activation_v1.h> | ||
4 | #include "sway/input/seat.h" | ||
5 | #include "sway/output.h" | ||
6 | #include "sway/desktop/launcher.h" | ||
7 | #include "sway/tree/node.h" | ||
8 | #include "sway/tree/container.h" | ||
9 | #include "sway/tree/workspace.h" | ||
10 | #include "sway/tree/root.h" | ||
11 | #include "log.h" | ||
12 | |||
13 | /** | ||
14 | * Get the pid of a parent process given the pid of a child process. | ||
15 | * | ||
16 | * Returns the parent pid or NULL if the parent pid cannot be determined. | ||
17 | */ | ||
18 | static pid_t get_parent_pid(pid_t child) { | ||
19 | pid_t parent = -1; | ||
20 | char file_name[100]; | ||
21 | char *buffer = NULL; | ||
22 | const char *sep = " "; | ||
23 | FILE *stat = NULL; | ||
24 | size_t buf_size = 0; | ||
25 | |||
26 | snprintf(file_name, sizeof(file_name), "/proc/%d/stat", child); | ||
27 | |||
28 | if ((stat = fopen(file_name, "r"))) { | ||
29 | if (getline(&buffer, &buf_size, stat) != -1) { | ||
30 | strtok(buffer, sep); // pid | ||
31 | strtok(NULL, sep); // executable name | ||
32 | strtok(NULL, sep); // state | ||
33 | char *token = strtok(NULL, sep); // parent pid | ||
34 | parent = strtol(token, NULL, 10); | ||
35 | } | ||
36 | free(buffer); | ||
37 | fclose(stat); | ||
38 | } | ||
39 | |||
40 | if (parent) { | ||
41 | return (parent == child) ? -1 : parent; | ||
42 | } | ||
43 | |||
44 | return -1; | ||
45 | } | ||
46 | |||
47 | void launcher_ctx_consume(struct launcher_ctx *ctx) { | ||
48 | // The view is now responsible for destroying this ctx | ||
49 | wl_list_remove(&ctx->token_destroy.link); | ||
50 | wl_list_init(&ctx->token_destroy.link); | ||
51 | |||
52 | if (!ctx->activated) { | ||
53 | // An unactivated token hasn't been destroyed yet | ||
54 | wlr_xdg_activation_token_v1_destroy(ctx->token); | ||
55 | } | ||
56 | ctx->token = NULL; | ||
57 | |||
58 | // Prevent additional matches | ||
59 | wl_list_remove(&ctx->link); | ||
60 | wl_list_init(&ctx->link); | ||
61 | } | ||
62 | |||
63 | void launcher_ctx_destroy(struct launcher_ctx *ctx) { | ||
64 | if (ctx == NULL) { | ||
65 | return; | ||
66 | } | ||
67 | wl_list_remove(&ctx->node_destroy.link); | ||
68 | wl_list_remove(&ctx->token_destroy.link); | ||
69 | if (ctx->seat) { | ||
70 | wl_list_remove(&ctx->seat_destroy.link); | ||
71 | } | ||
72 | wl_list_remove(&ctx->link); | ||
73 | wlr_xdg_activation_token_v1_destroy(ctx->token); | ||
74 | free(ctx->fallback_name); | ||
75 | free(ctx); | ||
76 | } | ||
77 | |||
78 | struct launcher_ctx *launcher_ctx_find_pid(pid_t pid) { | ||
79 | if (wl_list_empty(&server.pending_launcher_ctxs)) { | ||
80 | return NULL; | ||
81 | } | ||
82 | |||
83 | struct launcher_ctx *ctx = NULL; | ||
84 | sway_log(SWAY_DEBUG, "Looking up workspace for pid %d", pid); | ||
85 | |||
86 | do { | ||
87 | struct launcher_ctx *_ctx = NULL; | ||
88 | wl_list_for_each(_ctx, &server.pending_launcher_ctxs, link) { | ||
89 | if (pid == _ctx->pid) { | ||
90 | ctx = _ctx; | ||
91 | sway_log(SWAY_DEBUG, | ||
92 | "found %s match for pid %d: %s", | ||
93 | node_type_to_str(ctx->node->type), pid, node_get_name(ctx->node)); | ||
94 | break; | ||
95 | } | ||
96 | } | ||
97 | pid = get_parent_pid(pid); | ||
98 | } while (pid > 1); | ||
99 | |||
100 | return ctx; | ||
101 | } | ||
102 | |||
103 | struct sway_workspace *launcher_ctx_get_workspace( | ||
104 | struct launcher_ctx *ctx) { | ||
105 | struct sway_workspace *ws = NULL; | ||
106 | struct sway_output *output = NULL; | ||
107 | |||
108 | switch (ctx->node->type) { | ||
109 | case N_CONTAINER: | ||
110 | // Unimplemented | ||
111 | // TODO: add container matching? | ||
112 | ws = ctx->node->sway_container->pending.workspace; | ||
113 | break; | ||
114 | case N_WORKSPACE: | ||
115 | ws = ctx->node->sway_workspace; | ||
116 | break; | ||
117 | case N_OUTPUT: | ||
118 | output = ctx->node->sway_output; | ||
119 | ws = workspace_by_name(ctx->fallback_name); | ||
120 | if (!ws) { | ||
121 | sway_log(SWAY_DEBUG, | ||
122 | "Creating workspace %s for pid %d because it disappeared", | ||
123 | ctx->fallback_name, ctx->pid); | ||
124 | if (!output->enabled) { | ||
125 | sway_log(SWAY_DEBUG, | ||
126 | "Workspace output %s is disabled, trying another one", | ||
127 | output->wlr_output->name); | ||
128 | output = NULL; | ||
129 | } | ||
130 | ws = workspace_create(output, ctx->fallback_name); | ||
131 | } | ||
132 | break; | ||
133 | case N_ROOT: | ||
134 | ws = workspace_create(NULL, ctx->fallback_name); | ||
135 | break; | ||
136 | } | ||
137 | |||
138 | return ws; | ||
139 | } | ||
140 | |||
141 | static void ctx_handle_node_destroy(struct wl_listener *listener, void *data) { | ||
142 | struct launcher_ctx *ctx = wl_container_of(listener, ctx, node_destroy); | ||
143 | switch (ctx->node->type) { | ||
144 | case N_CONTAINER: | ||
145 | // Unimplemented | ||
146 | break; | ||
147 | case N_WORKSPACE:; | ||
148 | struct sway_workspace *ws = ctx->node->sway_workspace; | ||
149 | wl_list_remove(&ctx->node_destroy.link); | ||
150 | wl_list_init(&ctx->node_destroy.link); | ||
151 | // We want to save this ws name to recreate later, hopefully on the | ||
152 | // same output | ||
153 | free(ctx->fallback_name); | ||
154 | ctx->fallback_name = strdup(ws->name); | ||
155 | if (!ws->output || ws->output->node.destroying) { | ||
156 | // If the output is being destroyed it would be pointless to track | ||
157 | // If the output is being disabled, we'll find out if it's still | ||
158 | // disabled when we try to match it. | ||
159 | ctx->node = &root->node; | ||
160 | break; | ||
161 | } | ||
162 | ctx->node = &ws->output->node; | ||
163 | wl_signal_add(&ctx->node->events.destroy, &ctx->node_destroy); | ||
164 | break; | ||
165 | case N_OUTPUT: | ||
166 | wl_list_remove(&ctx->node_destroy.link); | ||
167 | wl_list_init(&ctx->node_destroy.link); | ||
168 | // We'll make the ws ctx->name somewhere else | ||
169 | ctx->node = &root->node; | ||
170 | break; | ||
171 | case N_ROOT: | ||
172 | // Unreachable | ||
173 | break; | ||
174 | } | ||
175 | } | ||
176 | |||
177 | static void token_handle_destroy(struct wl_listener *listener, void *data) { | ||
178 | struct launcher_ctx *ctx = wl_container_of(listener, ctx, token_destroy); | ||
179 | ctx->token = NULL; | ||
180 | launcher_ctx_destroy(ctx); | ||
181 | } | ||
182 | |||
183 | struct launcher_ctx *launcher_ctx_create(struct wlr_xdg_activation_token_v1 *token, | ||
184 | struct sway_node *node) { | ||
185 | struct launcher_ctx *ctx = calloc(1, sizeof(struct launcher_ctx)); | ||
186 | |||
187 | const char *fallback_name = NULL; | ||
188 | struct sway_workspace *ws = NULL; | ||
189 | switch (node->type) { | ||
190 | case N_CONTAINER: | ||
191 | // Unimplemented | ||
192 | free(ctx); | ||
193 | return NULL; | ||
194 | case N_WORKSPACE: | ||
195 | ws = node->sway_workspace; | ||
196 | fallback_name = ws->name; | ||
197 | break; | ||
198 | case N_OUTPUT:; | ||
199 | struct sway_output *output = node->sway_output; | ||
200 | ws = output_get_active_workspace(output); | ||
201 | fallback_name = ws ? ws->name : NULL; | ||
202 | break; | ||
203 | case N_ROOT: | ||
204 | // Unimplemented | ||
205 | free(ctx); | ||
206 | return NULL; | ||
207 | } | ||
208 | |||
209 | if (!fallback_name) { | ||
210 | // TODO: implement a better fallback. | ||
211 | free(ctx); | ||
212 | return NULL; | ||
213 | } | ||
214 | |||
215 | ctx->fallback_name = strdup(fallback_name); | ||
216 | ctx->token = token; | ||
217 | ctx->node = node; | ||
218 | // Having surface set means that the focus check in wlroots has passed | ||
219 | ctx->had_focused_surface = token->surface != NULL; | ||
220 | |||
221 | ctx->node_destroy.notify = ctx_handle_node_destroy; | ||
222 | wl_signal_add(&ctx->node->events.destroy, &ctx->node_destroy); | ||
223 | |||
224 | ctx->token_destroy.notify = token_handle_destroy; | ||
225 | wl_signal_add(&token->events.destroy, &ctx->token_destroy); | ||
226 | |||
227 | wl_list_init(&ctx->link); | ||
228 | wl_list_insert(&server.pending_launcher_ctxs, &ctx->link); | ||
229 | |||
230 | token->data = ctx; | ||
231 | return ctx; | ||
232 | } | ||
233 | |||
234 | static void launch_ctx_handle_seat_destroy(struct wl_listener *listener, void *data) { | ||
235 | struct launcher_ctx *ctx = wl_container_of(listener, ctx, seat_destroy); | ||
236 | ctx->seat = NULL; | ||
237 | wl_list_remove(&ctx->seat_destroy.link); | ||
238 | } | ||
239 | |||
240 | // Creates a context with a new token for the internal launcher | ||
241 | struct launcher_ctx *launcher_ctx_create_internal(void) { | ||
242 | struct sway_seat *seat = input_manager_current_seat(); | ||
243 | struct sway_workspace *ws = seat_get_focused_workspace(seat); | ||
244 | if (!ws) { | ||
245 | sway_log(SWAY_DEBUG, "Failed to create launch context. No workspace."); | ||
246 | return NULL; | ||
247 | } | ||
248 | |||
249 | struct wlr_xdg_activation_token_v1 *token = | ||
250 | wlr_xdg_activation_token_v1_create(server.xdg_activation_v1); | ||
251 | |||
252 | struct launcher_ctx *ctx = launcher_ctx_create(token, &ws->node); | ||
253 | if (!ctx) { | ||
254 | wlr_xdg_activation_token_v1_destroy(token); | ||
255 | return NULL; | ||
256 | } | ||
257 | ctx->seat = seat; | ||
258 | ctx->seat_destroy.notify = launch_ctx_handle_seat_destroy; | ||
259 | wl_signal_add(&seat->wlr_seat->events.destroy, &ctx->seat_destroy); | ||
260 | |||
261 | return ctx; | ||
262 | } | ||
263 | |||
264 | const char *launcher_ctx_get_token_name(struct launcher_ctx *ctx) { | ||
265 | const char *token = wlr_xdg_activation_token_v1_get_name(ctx->token); | ||
266 | return token; | ||
267 | } | ||
diff --git a/sway/desktop/layer_shell.c b/sway/desktop/layer_shell.c index d4ca4fb4..6221b7b9 100644 --- a/sway/desktop/layer_shell.c +++ b/sway/desktop/layer_shell.c | |||
@@ -2,11 +2,14 @@ | |||
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_box.h> | 5 | #include <wlr/types/wlr_fractional_scale_v1.h> |
6 | #include <wlr/types/wlr_layer_shell_v1.h> | 6 | #include <wlr/types/wlr_layer_shell_v1.h> |
7 | #include <wlr/types/wlr_output_damage.h> | ||
8 | #include <wlr/types/wlr_output.h> | 7 | #include <wlr/types/wlr_output.h> |
8 | #include <wlr/types/wlr_scene.h> | ||
9 | #include <wlr/types/wlr_subcompositor.h> | ||
10 | #include <wlr/types/wlr_xdg_shell.h> | ||
9 | #include "log.h" | 11 | #include "log.h" |
12 | #include "sway/scene_descriptor.h" | ||
10 | #include "sway/desktop/transaction.h" | 13 | #include "sway/desktop/transaction.h" |
11 | #include "sway/input/cursor.h" | 14 | #include "sway/input/cursor.h" |
12 | #include "sway/input/input-manager.h" | 15 | #include "sway/input/input-manager.h" |
@@ -17,155 +20,55 @@ | |||
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 | 22 | ||
20 | static void apply_exclusive(struct wlr_box *usable_area, | 23 | struct wlr_layer_surface_v1 *toplevel_layer_surface_from_surface( |
21 | uint32_t anchor, int32_t exclusive, | 24 | struct wlr_surface *surface) { |
22 | int32_t margin_top, int32_t margin_right, | 25 | struct wlr_layer_surface_v1 *layer; |
23 | int32_t margin_bottom, int32_t margin_left) { | 26 | do { |
24 | if (exclusive <= 0) { | 27 | if (!surface) { |
25 | return; | 28 | return NULL; |
26 | } | 29 | } |
27 | struct { | 30 | // Topmost layer surface |
28 | uint32_t singular_anchor; | 31 | if ((layer = wlr_layer_surface_v1_try_from_wlr_surface(surface))) { |
29 | uint32_t anchor_triplet; | 32 | return layer; |
30 | int *positive_axis; | 33 | } |
31 | int *negative_axis; | 34 | // Layer subsurface |
32 | int margin; | 35 | if (wlr_subsurface_try_from_wlr_surface(surface)) { |
33 | } edges[] = { | 36 | surface = wlr_surface_get_root_surface(surface); |
34 | // Top | 37 | continue; |
35 | { | 38 | } |
36 | .singular_anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP, | 39 | |
37 | .anchor_triplet = | 40 | // Layer surface popup |
38 | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | | 41 | struct wlr_xdg_surface *xdg_surface = NULL; |
39 | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT | | 42 | if ((xdg_surface = wlr_xdg_surface_try_from_wlr_surface(surface)) && |
40 | ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP, | 43 | xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP && xdg_surface->popup != NULL) { |
41 | .positive_axis = &usable_area->y, | 44 | if (!xdg_surface->popup->parent) { |
42 | .negative_axis = &usable_area->height, | 45 | return NULL; |
43 | .margin = margin_top, | ||
44 | }, | ||
45 | // Bottom | ||
46 | { | ||
47 | .singular_anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM, | ||
48 | .anchor_triplet = | ||
49 | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | | ||
50 | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT | | ||
51 | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM, | ||
52 | .positive_axis = NULL, | ||
53 | .negative_axis = &usable_area->height, | ||
54 | .margin = margin_bottom, | ||
55 | }, | ||
56 | // Left | ||
57 | { | ||
58 | .singular_anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT, | ||
59 | .anchor_triplet = | ||
60 | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | | ||
61 | ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | | ||
62 | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM, | ||
63 | .positive_axis = &usable_area->x, | ||
64 | .negative_axis = &usable_area->width, | ||
65 | .margin = margin_left, | ||
66 | }, | ||
67 | // Right | ||
68 | { | ||
69 | .singular_anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT, | ||
70 | .anchor_triplet = | ||
71 | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT | | ||
72 | ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | | ||
73 | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM, | ||
74 | .positive_axis = NULL, | ||
75 | .negative_axis = &usable_area->width, | ||
76 | .margin = margin_right, | ||
77 | }, | ||
78 | }; | ||
79 | for (size_t i = 0; i < sizeof(edges) / sizeof(edges[0]); ++i) { | ||
80 | if ((anchor == edges[i].singular_anchor || anchor == edges[i].anchor_triplet) | ||
81 | && exclusive + edges[i].margin > 0) { | ||
82 | if (edges[i].positive_axis) { | ||
83 | *edges[i].positive_axis += exclusive + edges[i].margin; | ||
84 | } | ||
85 | if (edges[i].negative_axis) { | ||
86 | *edges[i].negative_axis -= exclusive + edges[i].margin; | ||
87 | } | 46 | } |
88 | break; | 47 | surface = wlr_surface_get_root_surface(xdg_surface->popup->parent); |
48 | continue; | ||
89 | } | 49 | } |
90 | } | 50 | |
51 | // Return early if the surface is not a layer/xdg_popup/sub surface | ||
52 | return NULL; | ||
53 | } while (true); | ||
91 | } | 54 | } |
92 | 55 | ||
93 | static void arrange_layer(struct sway_output *output, struct wl_list *list, | 56 | static void arrange_surface(struct sway_output *output, const struct wlr_box *full_area, |
94 | struct wlr_box *usable_area, bool exclusive) { | 57 | struct wlr_box *usable_area, struct wlr_scene_tree *tree) { |
95 | struct sway_layer_surface *sway_layer; | 58 | struct wlr_scene_node *node; |
96 | struct wlr_box full_area = { 0 }; | 59 | wl_list_for_each(node, &tree->children, link) { |
97 | wlr_output_effective_resolution(output->wlr_output, | 60 | struct sway_layer_surface *surface = scene_descriptor_try_get(node, |
98 | &full_area.width, &full_area.height); | 61 | SWAY_SCENE_DESC_LAYER_SHELL); |
99 | wl_list_for_each(sway_layer, list, link) { | 62 | // surface could be null during destruction |
100 | struct wlr_layer_surface_v1 *layer = sway_layer->layer_surface; | 63 | if (!surface) { |
101 | struct wlr_layer_surface_v1_state *state = &layer->current; | ||
102 | if (exclusive != (state->exclusive_zone > 0)) { | ||
103 | continue; | 64 | continue; |
104 | } | 65 | } |
105 | struct wlr_box bounds; | 66 | |
106 | if (state->exclusive_zone == -1) { | 67 | if (!surface->scene->layer_surface->initialized) { |
107 | bounds = full_area; | ||
108 | } else { | ||
109 | bounds = *usable_area; | ||
110 | } | ||
111 | struct wlr_box box = { | ||
112 | .width = state->desired_width, | ||
113 | .height = state->desired_height | ||
114 | }; | ||
115 | // Horizontal axis | ||
116 | const uint32_t both_horiz = ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | ||
117 | | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT; | ||
118 | if ((state->anchor & both_horiz) && box.width == 0) { | ||
119 | box.x = bounds.x; | ||
120 | box.width = bounds.width; | ||
121 | } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT)) { | ||
122 | box.x = bounds.x; | ||
123 | } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT)) { | ||
124 | box.x = bounds.x + (bounds.width - box.width); | ||
125 | } else { | ||
126 | box.x = bounds.x + ((bounds.width / 2) - (box.width / 2)); | ||
127 | } | ||
128 | // Vertical axis | ||
129 | const uint32_t both_vert = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | ||
130 | | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM; | ||
131 | if ((state->anchor & both_vert) && box.height == 0) { | ||
132 | box.y = bounds.y; | ||
133 | box.height = bounds.height; | ||
134 | } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP)) { | ||
135 | box.y = bounds.y; | ||
136 | } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM)) { | ||
137 | box.y = bounds.y + (bounds.height - box.height); | ||
138 | } else { | ||
139 | box.y = bounds.y + ((bounds.height / 2) - (box.height / 2)); | ||
140 | } | ||
141 | // Margin | ||
142 | if ((state->anchor & both_horiz) == both_horiz) { | ||
143 | box.x += state->margin.left; | ||
144 | box.width -= state->margin.left + state->margin.right; | ||
145 | } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT)) { | ||
146 | box.x += state->margin.left; | ||
147 | } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT)) { | ||
148 | box.x -= state->margin.right; | ||
149 | } | ||
150 | if ((state->anchor & both_vert) == both_vert) { | ||
151 | box.y += state->margin.top; | ||
152 | box.height -= state->margin.top + state->margin.bottom; | ||
153 | } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP)) { | ||
154 | box.y += state->margin.top; | ||
155 | } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM)) { | ||
156 | box.y -= state->margin.bottom; | ||
157 | } | ||
158 | if (box.width < 0 || box.height < 0) { | ||
159 | // TODO: Bubble up a protocol error? | ||
160 | wlr_layer_surface_v1_close(layer); | ||
161 | continue; | 68 | continue; |
162 | } | 69 | } |
163 | // Apply | 70 | |
164 | sway_layer->geo = box; | 71 | wlr_scene_layer_surface_v1_configure(surface->scene, full_area, usable_area); |
165 | apply_exclusive(usable_area, state->anchor, state->exclusive_zone, | ||
166 | state->margin.top, state->margin.right, | ||
167 | state->margin.bottom, state->margin.left); | ||
168 | wlr_layer_surface_v1_configure(layer, box.width, box.height); | ||
169 | } | 72 | } |
170 | } | 73 | } |
171 | 74 | ||
@@ -173,81 +76,94 @@ void arrange_layers(struct sway_output *output) { | |||
173 | struct wlr_box usable_area = { 0 }; | 76 | struct wlr_box usable_area = { 0 }; |
174 | wlr_output_effective_resolution(output->wlr_output, | 77 | wlr_output_effective_resolution(output->wlr_output, |
175 | &usable_area.width, &usable_area.height); | 78 | &usable_area.width, &usable_area.height); |
79 | const struct wlr_box full_area = usable_area; | ||
80 | |||
81 | arrange_surface(output, &full_area, &usable_area, output->layers.shell_background); | ||
82 | arrange_surface(output, &full_area, &usable_area, output->layers.shell_bottom); | ||
83 | arrange_surface(output, &full_area, &usable_area, output->layers.shell_top); | ||
84 | arrange_surface(output, &full_area, &usable_area, output->layers.shell_overlay); | ||
176 | 85 | ||
177 | // Arrange exclusive surfaces from top->bottom | 86 | if (!wlr_box_equal(&usable_area, &output->usable_area)) { |
178 | arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], | ||
179 | &usable_area, true); | ||
180 | arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP], | ||
181 | &usable_area, true); | ||
182 | arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM], | ||
183 | &usable_area, true); | ||
184 | arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND], | ||
185 | &usable_area, true); | ||
186 | |||
187 | if (memcmp(&usable_area, &output->usable_area, | ||
188 | sizeof(struct wlr_box)) != 0) { | ||
189 | sway_log(SWAY_DEBUG, "Usable area changed, rearranging output"); | 87 | sway_log(SWAY_DEBUG, "Usable area changed, rearranging output"); |
190 | memcpy(&output->usable_area, &usable_area, sizeof(struct wlr_box)); | 88 | output->usable_area = usable_area; |
191 | arrange_output(output); | 89 | arrange_output(output); |
90 | } else { | ||
91 | arrange_popups(root->layers.popup); | ||
192 | } | 92 | } |
93 | } | ||
193 | 94 | ||
194 | // Arrange non-exlusive surfaces from top->bottom | 95 | static struct wlr_scene_tree *sway_layer_get_scene(struct sway_output *output, |
195 | arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], | 96 | enum zwlr_layer_shell_v1_layer type) { |
196 | &usable_area, false); | 97 | switch (type) { |
197 | arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP], | 98 | case ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND: |
198 | &usable_area, false); | 99 | return output->layers.shell_background; |
199 | arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM], | 100 | case ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM: |
200 | &usable_area, false); | 101 | return output->layers.shell_bottom; |
201 | arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND], | 102 | case ZWLR_LAYER_SHELL_V1_LAYER_TOP: |
202 | &usable_area, false); | 103 | return output->layers.shell_top; |
203 | 104 | case ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY: | |
204 | // Find topmost keyboard interactive layer, if such a layer exists | 105 | return output->layers.shell_overlay; |
205 | uint32_t layers_above_shell[] = { | ||
206 | ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY, | ||
207 | ZWLR_LAYER_SHELL_V1_LAYER_TOP, | ||
208 | }; | ||
209 | size_t nlayers = sizeof(layers_above_shell) / sizeof(layers_above_shell[0]); | ||
210 | struct sway_layer_surface *layer, *topmost = NULL; | ||
211 | for (size_t i = 0; i < nlayers; ++i) { | ||
212 | wl_list_for_each_reverse(layer, | ||
213 | &output->layers[layers_above_shell[i]], link) { | ||
214 | if (layer->layer_surface->current.keyboard_interactive && | ||
215 | layer->layer_surface->mapped) { | ||
216 | topmost = layer; | ||
217 | break; | ||
218 | } | ||
219 | } | ||
220 | if (topmost != NULL) { | ||
221 | break; | ||
222 | } | ||
223 | } | 106 | } |
224 | 107 | ||
225 | struct sway_seat *seat; | 108 | sway_assert(false, "unreachable"); |
226 | wl_list_for_each(seat, &server.input->seats, link) { | 109 | return NULL; |
227 | if (topmost != NULL) { | 110 | } |
228 | seat_set_focus_layer(seat, topmost->layer_surface); | 111 | |
229 | } else if (seat->focused_layer && | 112 | static struct sway_layer_surface *sway_layer_surface_create( |
230 | !seat->focused_layer->current.keyboard_interactive) { | 113 | struct wlr_scene_layer_surface_v1 *scene) { |
231 | seat_set_focus_layer(seat, NULL); | 114 | struct sway_layer_surface *surface = calloc(1, sizeof(*surface)); |
232 | } | 115 | if (!surface) { |
116 | sway_log(SWAY_ERROR, "Could not allocate a scene_layer surface"); | ||
117 | return NULL; | ||
118 | } | ||
119 | |||
120 | struct wlr_scene_tree *popups = wlr_scene_tree_create(root->layers.popup); | ||
121 | if (!popups) { | ||
122 | sway_log(SWAY_ERROR, "Could not allocate a scene_layer popup node"); | ||
123 | free(surface); | ||
124 | return NULL; | ||
125 | } | ||
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; | ||
233 | } | 135 | } |
136 | |||
137 | surface->tree = scene->tree; | ||
138 | surface->scene = scene; | ||
139 | surface->layer_surface = scene->layer_surface; | ||
140 | surface->popups = popups; | ||
141 | surface->layer_surface->data = surface; | ||
142 | |||
143 | return surface; | ||
234 | } | 144 | } |
235 | 145 | ||
236 | static struct sway_layer_surface *find_mapped_layer_by_client( | 146 | static struct sway_layer_surface *find_mapped_layer_by_client( |
237 | struct wl_client *client, struct wlr_output *ignore_output) { | 147 | struct wl_client *client, struct sway_output *ignore_output) { |
238 | for (int i = 0; i < root->outputs->length; ++i) { | 148 | for (int i = 0; i < root->outputs->length; ++i) { |
239 | struct sway_output *output = root->outputs->items[i]; | 149 | struct sway_output *output = root->outputs->items[i]; |
240 | if (output->wlr_output == ignore_output) { | 150 | if (output == ignore_output) { |
241 | continue; | 151 | continue; |
242 | } | 152 | } |
243 | // For now we'll only check the overlay layer | 153 | // For now we'll only check the overlay layer |
244 | struct sway_layer_surface *lsurface; | 154 | struct wlr_scene_node *node; |
245 | wl_list_for_each(lsurface, | 155 | wl_list_for_each (node, &output->layers.shell_overlay->children, link) { |
246 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], link) { | 156 | struct sway_layer_surface *surface = scene_descriptor_try_get(node, |
247 | struct wl_resource *resource = lsurface->layer_surface->resource; | 157 | SWAY_SCENE_DESC_LAYER_SHELL); |
158 | if (!surface) { | ||
159 | continue; | ||
160 | } | ||
161 | |||
162 | struct wlr_layer_surface_v1 *layer_surface = surface->layer_surface; | ||
163 | struct wl_resource *resource = layer_surface->resource; | ||
248 | if (wl_resource_get_client(resource) == client | 164 | if (wl_resource_get_client(resource) == client |
249 | && lsurface->layer_surface->mapped) { | 165 | && layer_surface->surface->mapped) { |
250 | return lsurface; | 166 | return surface; |
251 | } | 167 | } |
252 | } | 168 | } |
253 | } | 169 | } |
@@ -255,280 +171,142 @@ static struct sway_layer_surface *find_mapped_layer_by_client( | |||
255 | } | 171 | } |
256 | 172 | ||
257 | static void handle_output_destroy(struct wl_listener *listener, void *data) { | 173 | static void handle_output_destroy(struct wl_listener *listener, void *data) { |
258 | struct sway_layer_surface *sway_layer = | 174 | struct sway_layer_surface *layer = |
259 | wl_container_of(listener, sway_layer, output_destroy); | 175 | wl_container_of(listener, layer, output_destroy); |
260 | // Determine if this layer is being used by an exclusive client. If it is, | ||
261 | // try and find another layer owned by this client to pass focus to. | ||
262 | struct sway_seat *seat = input_manager_get_default_seat(); | ||
263 | struct wl_client *client = | ||
264 | wl_resource_get_client(sway_layer->layer_surface->resource); | ||
265 | bool set_focus = seat->exclusive_client == client; | ||
266 | |||
267 | wl_list_remove(&sway_layer->output_destroy.link); | ||
268 | wl_list_remove(&sway_layer->link); | ||
269 | wl_list_init(&sway_layer->link); | ||
270 | |||
271 | if (set_focus) { | ||
272 | struct sway_layer_surface *layer = | ||
273 | find_mapped_layer_by_client(client, sway_layer->layer_surface->output); | ||
274 | if (layer) { | ||
275 | seat_set_focus_layer(seat, layer->layer_surface); | ||
276 | } | ||
277 | } | ||
278 | 176 | ||
279 | sway_layer->layer_surface->output = NULL; | 177 | layer->output = NULL; |
280 | wlr_layer_surface_v1_close(sway_layer->layer_surface); | 178 | wlr_scene_node_destroy(&layer->scene->tree->node); |
281 | } | 179 | } |
282 | 180 | ||
283 | static void handle_surface_commit(struct wl_listener *listener, void *data) { | 181 | static void handle_node_destroy(struct wl_listener *listener, void *data) { |
284 | struct sway_layer_surface *layer = | 182 | struct sway_layer_surface *layer = |
285 | wl_container_of(listener, layer, surface_commit); | 183 | wl_container_of(listener, layer, node_destroy); |
286 | struct wlr_layer_surface_v1 *layer_surface = layer->layer_surface; | ||
287 | struct wlr_output *wlr_output = layer_surface->output; | ||
288 | if (wlr_output == NULL) { | ||
289 | return; | ||
290 | } | ||
291 | 184 | ||
292 | struct sway_output *output = wlr_output->data; | 185 | // destroy the scene descriptor straight away if it exists, otherwise |
293 | struct wlr_box old_geo = layer->geo; | 186 | // we will try to reflow still considering the destroyed node. |
294 | arrange_layers(output); | 187 | scene_descriptor_destroy(&layer->tree->node, SWAY_SCENE_DESC_LAYER_SHELL); |
295 | |||
296 | bool geo_changed = | ||
297 | memcmp(&old_geo, &layer->geo, sizeof(struct wlr_box)) != 0; | ||
298 | bool layer_changed = layer->layer != layer_surface->current.layer; | ||
299 | if (layer_changed) { | ||
300 | wl_list_remove(&layer->link); | ||
301 | wl_list_insert(&output->layers[layer_surface->current.layer], | ||
302 | &layer->link); | ||
303 | layer->layer = layer_surface->current.layer; | ||
304 | } | ||
305 | if (geo_changed || layer_changed) { | ||
306 | output_damage_surface(output, old_geo.x, old_geo.y, | ||
307 | layer_surface->surface, true); | ||
308 | output_damage_surface(output, layer->geo.x, layer->geo.y, | ||
309 | layer_surface->surface, true); | ||
310 | } else { | ||
311 | output_damage_surface(output, layer->geo.x, layer->geo.y, | ||
312 | layer_surface->surface, false); | ||
313 | } | ||
314 | |||
315 | transaction_commit_dirty(); | ||
316 | } | ||
317 | 188 | ||
318 | static void unmap(struct sway_layer_surface *sway_layer) { | 189 | // Determine if this layer is being used by an exclusive client. If it is, |
319 | struct sway_seat *seat; | 190 | // try and find another layer owned by this client to pass focus to. |
320 | wl_list_for_each(seat, &server.input->seats, link) { | 191 | struct sway_seat *seat = input_manager_get_default_seat(); |
321 | if (seat->focused_layer == sway_layer->layer_surface) { | 192 | struct wl_client *client = |
322 | seat_set_focus_layer(seat, NULL); | 193 | wl_resource_get_client(layer->layer_surface->resource); |
194 | if (!server.session_lock.lock) { | ||
195 | struct sway_layer_surface *consider_layer = | ||
196 | find_mapped_layer_by_client(client, layer->output); | ||
197 | if (consider_layer) { | ||
198 | seat_set_focus_layer(seat, consider_layer->layer_surface); | ||
323 | } | 199 | } |
324 | } | 200 | } |
325 | 201 | ||
326 | cursor_rebase_all(); | 202 | if (layer->output) { |
327 | 203 | arrange_layers(layer->output); | |
328 | struct wlr_output *wlr_output = sway_layer->layer_surface->output; | 204 | transaction_commit_dirty(); |
329 | if (wlr_output == NULL) { | ||
330 | return; | ||
331 | } | 205 | } |
332 | struct sway_output *output = wlr_output->data; | ||
333 | if (output == NULL) { | ||
334 | return; | ||
335 | } | ||
336 | output_damage_surface(output, sway_layer->geo.x, sway_layer->geo.y, | ||
337 | sway_layer->layer_surface->surface, true); | ||
338 | } | ||
339 | 206 | ||
340 | static void handle_destroy(struct wl_listener *listener, void *data) { | 207 | wlr_scene_node_destroy(&layer->popups->node); |
341 | struct sway_layer_surface *sway_layer = | ||
342 | wl_container_of(listener, sway_layer, destroy); | ||
343 | sway_log(SWAY_DEBUG, "Layer surface destroyed (%s)", | ||
344 | sway_layer->layer_surface->namespace); | ||
345 | if (sway_layer->layer_surface->mapped) { | ||
346 | unmap(sway_layer); | ||
347 | } | ||
348 | wl_list_remove(&sway_layer->link); | ||
349 | wl_list_remove(&sway_layer->destroy.link); | ||
350 | wl_list_remove(&sway_layer->map.link); | ||
351 | wl_list_remove(&sway_layer->unmap.link); | ||
352 | wl_list_remove(&sway_layer->surface_commit.link); | ||
353 | wl_list_remove(&sway_layer->new_popup.link); | ||
354 | wl_list_remove(&sway_layer->new_subsurface.link); | ||
355 | if (sway_layer->layer_surface->output != NULL) { | ||
356 | struct sway_output *output = sway_layer->layer_surface->output->data; | ||
357 | if (output != NULL) { | ||
358 | arrange_layers(output); | ||
359 | transaction_commit_dirty(); | ||
360 | } | ||
361 | wl_list_remove(&sway_layer->output_destroy.link); | ||
362 | sway_layer->layer_surface->output = NULL; | ||
363 | } | ||
364 | free(sway_layer); | ||
365 | } | ||
366 | 208 | ||
367 | static void handle_map(struct wl_listener *listener, void *data) { | 209 | wl_list_remove(&layer->map.link); |
368 | struct sway_layer_surface *sway_layer = wl_container_of(listener, | 210 | wl_list_remove(&layer->unmap.link); |
369 | sway_layer, map); | 211 | wl_list_remove(&layer->surface_commit.link); |
370 | struct sway_output *output = sway_layer->layer_surface->output->data; | 212 | wl_list_remove(&layer->node_destroy.link); |
371 | output_damage_surface(output, sway_layer->geo.x, sway_layer->geo.y, | 213 | wl_list_remove(&layer->output_destroy.link); |
372 | sway_layer->layer_surface->surface, true); | ||
373 | wlr_surface_send_enter(sway_layer->layer_surface->surface, | ||
374 | sway_layer->layer_surface->output); | ||
375 | cursor_rebase_all(); | ||
376 | } | ||
377 | 214 | ||
378 | static void handle_unmap(struct wl_listener *listener, void *data) { | 215 | layer->layer_surface->data = NULL; |
379 | struct sway_layer_surface *sway_layer = wl_container_of( | ||
380 | listener, sway_layer, unmap); | ||
381 | unmap(sway_layer); | ||
382 | } | ||
383 | 216 | ||
384 | static void subsurface_damage(struct sway_layer_subsurface *subsurface, | 217 | free(layer); |
385 | bool whole) { | ||
386 | struct sway_layer_surface *layer = subsurface->layer_surface; | ||
387 | struct wlr_output *wlr_output = layer->layer_surface->output; | ||
388 | if (!wlr_output) { | ||
389 | return; | ||
390 | } | ||
391 | struct sway_output *output = wlr_output->data; | ||
392 | int ox = subsurface->wlr_subsurface->current.x + layer->geo.x; | ||
393 | int oy = subsurface->wlr_subsurface->current.y + layer->geo.y; | ||
394 | output_damage_surface( | ||
395 | output, ox, oy, subsurface->wlr_subsurface->surface, whole); | ||
396 | } | 218 | } |
397 | 219 | ||
398 | static void subsurface_handle_unmap(struct wl_listener *listener, void *data) { | 220 | static void handle_surface_commit(struct wl_listener *listener, void *data) { |
399 | struct sway_layer_subsurface *subsurface = | 221 | struct sway_layer_surface *surface = |
400 | wl_container_of(listener, subsurface, unmap); | 222 | wl_container_of(listener, surface, surface_commit); |
401 | subsurface_damage(subsurface, true); | ||
402 | } | ||
403 | |||
404 | static void subsurface_handle_map(struct wl_listener *listener, void *data) { | ||
405 | struct sway_layer_subsurface *subsurface = | ||
406 | wl_container_of(listener, subsurface, map); | ||
407 | subsurface_damage(subsurface, true); | ||
408 | } | ||
409 | |||
410 | static void subsurface_handle_commit(struct wl_listener *listener, void *data) { | ||
411 | struct sway_layer_subsurface *subsurface = | ||
412 | wl_container_of(listener, subsurface, commit); | ||
413 | subsurface_damage(subsurface, false); | ||
414 | } | ||
415 | |||
416 | static void subsurface_handle_destroy(struct wl_listener *listener, | ||
417 | void *data) { | ||
418 | struct sway_layer_subsurface *subsurface = | ||
419 | wl_container_of(listener, subsurface, destroy); | ||
420 | |||
421 | wl_list_remove(&subsurface->map.link); | ||
422 | wl_list_remove(&subsurface->unmap.link); | ||
423 | wl_list_remove(&subsurface->destroy.link); | ||
424 | wl_list_remove(&subsurface->commit.link); | ||
425 | free(subsurface); | ||
426 | } | ||
427 | 223 | ||
428 | static struct sway_layer_subsurface *create_subsurface( | 224 | struct wlr_layer_surface_v1 *layer_surface = surface->layer_surface; |
429 | struct wlr_subsurface *wlr_subsurface, | 225 | if (!layer_surface->initialized) { |
430 | struct sway_layer_surface *layer_surface) { | 226 | return; |
431 | struct sway_layer_subsurface *subsurface = | ||
432 | calloc(1, sizeof(struct sway_layer_surface)); | ||
433 | if (subsurface == NULL) { | ||
434 | return NULL; | ||
435 | } | 227 | } |
436 | 228 | ||
437 | subsurface->wlr_subsurface = wlr_subsurface; | 229 | uint32_t committed = layer_surface->current.committed; |
438 | subsurface->layer_surface = layer_surface; | 230 | if (committed & WLR_LAYER_SURFACE_V1_STATE_LAYER) { |
439 | 231 | enum zwlr_layer_shell_v1_layer layer_type = layer_surface->current.layer; | |
440 | subsurface->map.notify = subsurface_handle_map; | 232 | struct wlr_scene_tree *output_layer = sway_layer_get_scene( |
441 | wl_signal_add(&wlr_subsurface->events.map, &subsurface->map); | 233 | surface->output, layer_type); |
442 | subsurface->unmap.notify = subsurface_handle_unmap; | 234 | wlr_scene_node_reparent(&surface->scene->tree->node, output_layer); |
443 | wl_signal_add(&wlr_subsurface->events.unmap, &subsurface->unmap); | 235 | } |
444 | subsurface->destroy.notify = subsurface_handle_destroy; | ||
445 | wl_signal_add(&wlr_subsurface->events.destroy, &subsurface->destroy); | ||
446 | subsurface->commit.notify = subsurface_handle_commit; | ||
447 | wl_signal_add(&wlr_subsurface->surface->events.commit, &subsurface->commit); | ||
448 | |||
449 | return subsurface; | ||
450 | } | ||
451 | |||
452 | static void handle_new_subsurface(struct wl_listener *listener, void *data) { | ||
453 | struct sway_layer_surface *sway_layer_surface = | ||
454 | wl_container_of(listener, sway_layer_surface, new_subsurface); | ||
455 | struct wlr_subsurface *wlr_subsurface = data; | ||
456 | create_subsurface(wlr_subsurface, sway_layer_surface); | ||
457 | } | ||
458 | |||
459 | 236 | ||
460 | static struct sway_layer_surface *popup_get_layer( | 237 | if (layer_surface->initial_commit || committed || layer_surface->surface->mapped != surface->mapped) { |
461 | struct sway_layer_popup *popup) { | 238 | surface->mapped = layer_surface->surface->mapped; |
462 | while (popup->parent_type == LAYER_PARENT_POPUP) { | 239 | arrange_layers(surface->output); |
463 | popup = popup->parent_popup; | 240 | transaction_commit_dirty(); |
464 | } | 241 | } |
465 | return popup->parent_layer; | ||
466 | } | 242 | } |
467 | 243 | ||
468 | static void popup_damage(struct sway_layer_popup *layer_popup, bool whole) { | 244 | static void handle_map(struct wl_listener *listener, void *data) { |
469 | struct wlr_xdg_popup *popup = layer_popup->wlr_popup; | 245 | struct sway_layer_surface *surface = wl_container_of(listener, |
470 | struct wlr_surface *surface = popup->base->surface; | 246 | surface, map); |
471 | int popup_sx = popup->geometry.x - popup->base->geometry.x; | 247 | |
472 | int popup_sy = popup->geometry.y - popup->base->geometry.y; | 248 | struct wlr_layer_surface_v1 *layer_surface = |
473 | int ox = popup_sx, oy = popup_sy; | 249 | surface->scene->layer_surface; |
474 | struct sway_layer_surface *layer; | 250 | |
475 | while (true) { | 251 | // focus on new surface |
476 | if (layer_popup->parent_type == LAYER_PARENT_POPUP) { | 252 | if (layer_surface->current.keyboard_interactive && |
477 | layer_popup = layer_popup->parent_popup; | 253 | (layer_surface->current.layer == ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY || |
478 | ox += layer_popup->wlr_popup->geometry.x; | 254 | layer_surface->current.layer == ZWLR_LAYER_SHELL_V1_LAYER_TOP)) { |
479 | oy += layer_popup->wlr_popup->geometry.y; | 255 | struct sway_seat *seat; |
480 | } else { | 256 | wl_list_for_each(seat, &server.input->seats, link) { |
481 | layer = layer_popup->parent_layer; | 257 | // but only if the currently focused layer has a lower precedence |
482 | ox += layer->geo.x; | 258 | if (!seat->focused_layer || |
483 | oy += layer->geo.y; | 259 | seat->focused_layer->current.layer >= layer_surface->current.layer) { |
484 | break; | 260 | seat_set_focus_layer(seat, layer_surface); |
261 | } | ||
485 | } | 262 | } |
263 | arrange_layers(surface->output); | ||
486 | } | 264 | } |
487 | struct wlr_output *wlr_output = layer->layer_surface->output; | ||
488 | struct sway_output *output = wlr_output->data; | ||
489 | output_damage_surface(output, ox, oy, surface, whole); | ||
490 | } | ||
491 | 265 | ||
492 | static void popup_handle_map(struct wl_listener *listener, void *data) { | 266 | cursor_rebase_all(); |
493 | struct sway_layer_popup *popup = wl_container_of(listener, popup, map); | ||
494 | struct sway_layer_surface *layer = popup_get_layer(popup); | ||
495 | struct wlr_output *wlr_output = layer->layer_surface->output; | ||
496 | wlr_surface_send_enter(popup->wlr_popup->base->surface, wlr_output); | ||
497 | popup_damage(popup, true); | ||
498 | } | 267 | } |
499 | 268 | ||
500 | static void popup_handle_unmap(struct wl_listener *listener, void *data) { | 269 | static void handle_unmap(struct wl_listener *listener, void *data) { |
501 | struct sway_layer_popup *popup = wl_container_of(listener, popup, unmap); | 270 | struct sway_layer_surface *surface = wl_container_of( |
502 | popup_damage(popup, true); | 271 | listener, surface, unmap); |
503 | } | 272 | struct sway_seat *seat; |
273 | wl_list_for_each(seat, &server.input->seats, link) { | ||
274 | if (seat->focused_layer == surface->layer_surface) { | ||
275 | seat_set_focus_layer(seat, NULL); | ||
276 | } | ||
277 | } | ||
504 | 278 | ||
505 | static void popup_handle_commit(struct wl_listener *listener, void *data) { | 279 | cursor_rebase_all(); |
506 | struct sway_layer_popup *popup = wl_container_of(listener, popup, commit); | ||
507 | popup_damage(popup, false); | ||
508 | } | 280 | } |
509 | 281 | ||
510 | static void popup_handle_destroy(struct wl_listener *listener, void *data) { | 282 | static void popup_handle_destroy(struct wl_listener *listener, void *data) { |
511 | struct sway_layer_popup *popup = | 283 | struct sway_layer_popup *popup = |
512 | wl_container_of(listener, popup, destroy); | 284 | wl_container_of(listener, popup, destroy); |
513 | 285 | ||
514 | wl_list_remove(&popup->map.link); | ||
515 | wl_list_remove(&popup->unmap.link); | ||
516 | wl_list_remove(&popup->destroy.link); | 286 | wl_list_remove(&popup->destroy.link); |
287 | wl_list_remove(&popup->new_popup.link); | ||
517 | wl_list_remove(&popup->commit.link); | 288 | wl_list_remove(&popup->commit.link); |
518 | free(popup); | 289 | free(popup); |
519 | } | 290 | } |
520 | 291 | ||
521 | static void popup_unconstrain(struct sway_layer_popup *popup) { | 292 | static void popup_unconstrain(struct sway_layer_popup *popup) { |
522 | struct sway_layer_surface *layer = popup_get_layer(popup); | ||
523 | struct wlr_xdg_popup *wlr_popup = popup->wlr_popup; | 293 | struct wlr_xdg_popup *wlr_popup = popup->wlr_popup; |
294 | struct sway_output *output = popup->toplevel->output; | ||
524 | 295 | ||
525 | struct sway_output *output = layer->layer_surface->output->data; | 296 | // if a client tries to create a popup while we are in the process of destroying |
297 | // its output, don't crash. | ||
298 | if (!output) { | ||
299 | return; | ||
300 | } | ||
301 | |||
302 | int lx, ly; | ||
303 | wlr_scene_node_coords(&popup->toplevel->scene->tree->node, &lx, &ly); | ||
526 | 304 | ||
527 | // the output box expressed in the coordinate system of the toplevel parent | 305 | // the output box expressed in the coordinate system of the toplevel parent |
528 | // of the popup | 306 | // of the popup |
529 | struct wlr_box output_toplevel_sx_box = { | 307 | struct wlr_box output_toplevel_sx_box = { |
530 | .x = -layer->geo.x, | 308 | .x = output->lx - lx, |
531 | .y = -layer->geo.y, | 309 | .y = output->ly - ly, |
532 | .width = output->width, | 310 | .width = output->width, |
533 | .height = output->height, | 311 | .height = output->height, |
534 | }; | 312 | }; |
@@ -536,32 +314,38 @@ static void popup_unconstrain(struct sway_layer_popup *popup) { | |||
536 | wlr_xdg_popup_unconstrain_from_box(wlr_popup, &output_toplevel_sx_box); | 314 | wlr_xdg_popup_unconstrain_from_box(wlr_popup, &output_toplevel_sx_box); |
537 | } | 315 | } |
538 | 316 | ||
317 | static void popup_handle_commit(struct wl_listener *listener, void *data) { | ||
318 | struct sway_layer_popup *popup = wl_container_of(listener, popup, commit); | ||
319 | if (popup->wlr_popup->base->initial_commit) { | ||
320 | popup_unconstrain(popup); | ||
321 | } | ||
322 | } | ||
323 | |||
539 | static void popup_handle_new_popup(struct wl_listener *listener, void *data); | 324 | static void popup_handle_new_popup(struct wl_listener *listener, void *data); |
540 | 325 | ||
541 | static struct sway_layer_popup *create_popup(struct wlr_xdg_popup *wlr_popup, | 326 | static struct sway_layer_popup *create_popup(struct wlr_xdg_popup *wlr_popup, |
542 | enum layer_parent parent_type, void *parent) { | 327 | struct sway_layer_surface *toplevel, struct wlr_scene_tree *parent) { |
543 | struct sway_layer_popup *popup = | 328 | struct sway_layer_popup *popup = calloc(1, sizeof(*popup)); |
544 | calloc(1, sizeof(struct sway_layer_popup)); | ||
545 | if (popup == NULL) { | 329 | if (popup == NULL) { |
546 | return NULL; | 330 | return NULL; |
547 | } | 331 | } |
548 | 332 | ||
333 | popup->toplevel = toplevel; | ||
549 | popup->wlr_popup = wlr_popup; | 334 | popup->wlr_popup = wlr_popup; |
550 | popup->parent_type = parent_type; | 335 | popup->scene = wlr_scene_xdg_surface_create(parent, |
551 | popup->parent_layer = parent; | 336 | wlr_popup->base); |
337 | |||
338 | if (!popup->scene) { | ||
339 | free(popup); | ||
340 | return NULL; | ||
341 | } | ||
552 | 342 | ||
553 | popup->map.notify = popup_handle_map; | ||
554 | wl_signal_add(&wlr_popup->base->events.map, &popup->map); | ||
555 | popup->unmap.notify = popup_handle_unmap; | ||
556 | wl_signal_add(&wlr_popup->base->events.unmap, &popup->unmap); | ||
557 | popup->destroy.notify = popup_handle_destroy; | 343 | popup->destroy.notify = popup_handle_destroy; |
558 | wl_signal_add(&wlr_popup->base->events.destroy, &popup->destroy); | 344 | wl_signal_add(&wlr_popup->base->events.destroy, &popup->destroy); |
559 | popup->commit.notify = popup_handle_commit; | ||
560 | wl_signal_add(&wlr_popup->base->surface->events.commit, &popup->commit); | ||
561 | popup->new_popup.notify = popup_handle_new_popup; | 345 | popup->new_popup.notify = popup_handle_new_popup; |
562 | wl_signal_add(&wlr_popup->base->events.new_popup, &popup->new_popup); | 346 | wl_signal_add(&wlr_popup->base->events.new_popup, &popup->new_popup); |
563 | 347 | popup->commit.notify = popup_handle_commit; | |
564 | popup_unconstrain(popup); | 348 | wl_signal_add(&wlr_popup->base->surface->events.commit, &popup->commit); |
565 | 349 | ||
566 | return popup; | 350 | return popup; |
567 | } | 351 | } |
@@ -570,19 +354,14 @@ static void popup_handle_new_popup(struct wl_listener *listener, void *data) { | |||
570 | struct sway_layer_popup *sway_layer_popup = | 354 | struct sway_layer_popup *sway_layer_popup = |
571 | wl_container_of(listener, sway_layer_popup, new_popup); | 355 | wl_container_of(listener, sway_layer_popup, new_popup); |
572 | struct wlr_xdg_popup *wlr_popup = data; | 356 | struct wlr_xdg_popup *wlr_popup = data; |
573 | create_popup(wlr_popup, LAYER_PARENT_POPUP, sway_layer_popup); | 357 | create_popup(wlr_popup, sway_layer_popup->toplevel, sway_layer_popup->scene); |
574 | } | 358 | } |
575 | 359 | ||
576 | static void handle_new_popup(struct wl_listener *listener, void *data) { | 360 | static void handle_new_popup(struct wl_listener *listener, void *data) { |
577 | struct sway_layer_surface *sway_layer_surface = | 361 | struct sway_layer_surface *sway_layer_surface = |
578 | wl_container_of(listener, sway_layer_surface, new_popup); | 362 | wl_container_of(listener, sway_layer_surface, new_popup); |
579 | struct wlr_xdg_popup *wlr_popup = data; | 363 | struct wlr_xdg_popup *wlr_popup = data; |
580 | create_popup(wlr_popup, LAYER_PARENT_LAYER, sway_layer_surface); | 364 | create_popup(wlr_popup, sway_layer_surface, sway_layer_surface->popups); |
581 | } | ||
582 | |||
583 | struct sway_layer_surface *layer_from_wlr_layer_surface_v1( | ||
584 | struct wlr_layer_surface_v1 *layer_surface) { | ||
585 | return layer_surface->data; | ||
586 | } | 365 | } |
587 | 366 | ||
588 | void handle_layer_shell_surface(struct wl_listener *listener, void *data) { | 367 | void handle_layer_shell_surface(struct wl_listener *listener, void *data) { |
@@ -590,14 +369,14 @@ void handle_layer_shell_surface(struct wl_listener *listener, void *data) { | |||
590 | sway_log(SWAY_DEBUG, "new layer surface: namespace %s layer %d anchor %" PRIu32 | 369 | sway_log(SWAY_DEBUG, "new layer surface: namespace %s layer %d anchor %" PRIu32 |
591 | " size %" PRIu32 "x%" PRIu32 " margin %" PRIu32 ",%" PRIu32 ",%" PRIu32 ",%" PRIu32 ",", | 370 | " size %" PRIu32 "x%" PRIu32 " margin %" PRIu32 ",%" PRIu32 ",%" PRIu32 ",%" PRIu32 ",", |
592 | layer_surface->namespace, | 371 | layer_surface->namespace, |
593 | layer_surface->client_pending.layer, | 372 | layer_surface->pending.layer, |
594 | layer_surface->client_pending.anchor, | 373 | layer_surface->pending.anchor, |
595 | layer_surface->client_pending.desired_width, | 374 | layer_surface->pending.desired_width, |
596 | layer_surface->client_pending.desired_height, | 375 | layer_surface->pending.desired_height, |
597 | layer_surface->client_pending.margin.top, | 376 | layer_surface->pending.margin.top, |
598 | layer_surface->client_pending.margin.right, | 377 | layer_surface->pending.margin.right, |
599 | layer_surface->client_pending.margin.bottom, | 378 | layer_surface->pending.margin.bottom, |
600 | layer_surface->client_pending.margin.left); | 379 | layer_surface->pending.margin.left); |
601 | 380 | ||
602 | if (!layer_surface->output) { | 381 | if (!layer_surface->output) { |
603 | // Assign last active output | 382 | // Assign last active output |
@@ -609,12 +388,12 @@ void handle_layer_shell_surface(struct wl_listener *listener, void *data) { | |||
609 | output = ws->output; | 388 | output = ws->output; |
610 | } | 389 | } |
611 | } | 390 | } |
612 | if (!output || output == root->noop_output) { | 391 | if (!output || output == root->fallback_output) { |
613 | if (!root->outputs->length) { | 392 | if (!root->outputs->length) { |
614 | sway_log(SWAY_ERROR, | 393 | sway_log(SWAY_ERROR, |
615 | "no output to auto-assign layer surface '%s' to", | 394 | "no output to auto-assign layer surface '%s' to", |
616 | layer_surface->namespace); | 395 | layer_surface->namespace); |
617 | wlr_layer_surface_v1_close(layer_surface); | 396 | wlr_layer_surface_v1_destroy(layer_surface); |
618 | return; | 397 | return; |
619 | } | 398 | } |
620 | output = root->outputs->items[0]; | 399 | output = root->outputs->items[0]; |
@@ -622,42 +401,57 @@ void handle_layer_shell_surface(struct wl_listener *listener, void *data) { | |||
622 | layer_surface->output = output->wlr_output; | 401 | layer_surface->output = output->wlr_output; |
623 | } | 402 | } |
624 | 403 | ||
625 | struct sway_layer_surface *sway_layer = | 404 | struct sway_output *output = layer_surface->output->data; |
626 | calloc(1, sizeof(struct sway_layer_surface)); | 405 | |
627 | if (!sway_layer) { | 406 | enum zwlr_layer_shell_v1_layer layer_type = layer_surface->pending.layer; |
407 | struct wlr_scene_tree *output_layer = sway_layer_get_scene( | ||
408 | output, layer_type); | ||
409 | struct wlr_scene_layer_surface_v1 *scene_surface = | ||
410 | wlr_scene_layer_surface_v1_create(output_layer, layer_surface); | ||
411 | if (!scene_surface) { | ||
412 | sway_log(SWAY_ERROR, "Could not allocate a layer_surface_v1"); | ||
628 | return; | 413 | return; |
629 | } | 414 | } |
630 | 415 | ||
631 | sway_layer->surface_commit.notify = handle_surface_commit; | 416 | struct sway_layer_surface *surface = |
632 | wl_signal_add(&layer_surface->surface->events.commit, | 417 | sway_layer_surface_create(scene_surface); |
633 | &sway_layer->surface_commit); | 418 | if (!surface) { |
634 | 419 | wlr_layer_surface_v1_destroy(layer_surface); | |
635 | sway_layer->destroy.notify = handle_destroy; | ||
636 | wl_signal_add(&layer_surface->events.destroy, &sway_layer->destroy); | ||
637 | sway_layer->map.notify = handle_map; | ||
638 | wl_signal_add(&layer_surface->events.map, &sway_layer->map); | ||
639 | sway_layer->unmap.notify = handle_unmap; | ||
640 | wl_signal_add(&layer_surface->events.unmap, &sway_layer->unmap); | ||
641 | sway_layer->new_popup.notify = handle_new_popup; | ||
642 | wl_signal_add(&layer_surface->events.new_popup, &sway_layer->new_popup); | ||
643 | sway_layer->new_subsurface.notify = handle_new_subsurface; | ||
644 | wl_signal_add(&layer_surface->surface->events.new_subsurface, | ||
645 | &sway_layer->new_subsurface); | ||
646 | |||
647 | sway_layer->layer_surface = layer_surface; | ||
648 | layer_surface->data = sway_layer; | ||
649 | 420 | ||
650 | struct sway_output *output = layer_surface->output->data; | 421 | sway_log(SWAY_ERROR, "Could not allocate a sway_layer_surface"); |
651 | sway_layer->output_destroy.notify = handle_output_destroy; | 422 | return; |
652 | wl_signal_add(&output->events.destroy, &sway_layer->output_destroy); | 423 | } |
653 | 424 | ||
654 | wl_list_insert(&output->layers[layer_surface->client_pending.layer], | 425 | if (!scene_descriptor_assign(&scene_surface->tree->node, |
655 | &sway_layer->link); | 426 | SWAY_SCENE_DESC_LAYER_SHELL, surface)) { |
656 | 427 | sway_log(SWAY_ERROR, "Failed to allocate a layer surface descriptor"); | |
657 | // Temporarily set the layer's current state to client_pending | 428 | // destroying the layer_surface will also destroy its corresponding |
658 | // So that we can easily arrange it | 429 | // scene node |
659 | struct wlr_layer_surface_v1_state old_state = layer_surface->current; | 430 | wlr_layer_surface_v1_destroy(layer_surface); |
660 | layer_surface->current = layer_surface->client_pending; | 431 | return; |
661 | arrange_layers(output); | 432 | } |
662 | layer_surface->current = old_state; | 433 | |
434 | surface->output = output; | ||
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 | |||
442 | surface->surface_commit.notify = handle_surface_commit; | ||
443 | wl_signal_add(&layer_surface->surface->events.commit, | ||
444 | &surface->surface_commit); | ||
445 | surface->map.notify = handle_map; | ||
446 | wl_signal_add(&layer_surface->surface->events.map, &surface->map); | ||
447 | surface->unmap.notify = handle_unmap; | ||
448 | wl_signal_add(&layer_surface->surface->events.unmap, &surface->unmap); | ||
449 | surface->new_popup.notify = handle_new_popup; | ||
450 | wl_signal_add(&layer_surface->events.new_popup, &surface->new_popup); | ||
451 | |||
452 | surface->output_destroy.notify = handle_output_destroy; | ||
453 | wl_signal_add(&output->events.disable, &surface->output_destroy); | ||
454 | |||
455 | surface->node_destroy.notify = handle_node_destroy; | ||
456 | wl_signal_add(&scene_surface->tree->node.events.destroy, &surface->node_destroy); | ||
663 | } | 457 | } |
diff --git a/sway/desktop/output.c b/sway/desktop/output.c index 5edc8f96..2722e556 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c | |||
@@ -1,42 +1,61 @@ | |||
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> |
5 | #include <time.h> | 4 | #include <time.h> |
6 | #include <wayland-server-core.h> | 5 | #include <wayland-server-core.h> |
6 | #include <wlr/config.h> | ||
7 | #include <wlr/backend/headless.h> | ||
8 | #include <wlr/render/swapchain.h> | ||
7 | #include <wlr/render/wlr_renderer.h> | 9 | #include <wlr/render/wlr_renderer.h> |
8 | #include <wlr/types/wlr_box.h> | ||
9 | #include <wlr/types/wlr_buffer.h> | 10 | #include <wlr/types/wlr_buffer.h> |
11 | #include <wlr/types/wlr_gamma_control_v1.h> | ||
10 | #include <wlr/types/wlr_matrix.h> | 12 | #include <wlr/types/wlr_matrix.h> |
11 | #include <wlr/types/wlr_output_damage.h> | ||
12 | #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> | ||
13 | #include <wlr/types/wlr_output.h> | 16 | #include <wlr/types/wlr_output.h> |
14 | #include <wlr/types/wlr_presentation_time.h> | 17 | #include <wlr/types/wlr_presentation_time.h> |
15 | #include <wlr/types/wlr_surface.h> | 18 | #include <wlr/types/wlr_compositor.h> |
16 | #include <wlr/util/region.h> | 19 | #include <wlr/util/region.h> |
20 | #include <wlr/util/transform.h> | ||
17 | #include "config.h" | 21 | #include "config.h" |
18 | #include "log.h" | 22 | #include "log.h" |
19 | #include "sway/config.h" | 23 | #include "sway/config.h" |
20 | #include "sway/desktop/transaction.h" | 24 | #include "sway/desktop/transaction.h" |
21 | #include "sway/input/input-manager.h" | 25 | #include "sway/input/input-manager.h" |
22 | #include "sway/input/seat.h" | 26 | #include "sway/input/seat.h" |
27 | #include "sway/ipc-server.h" | ||
23 | #include "sway/layers.h" | 28 | #include "sway/layers.h" |
24 | #include "sway/output.h" | 29 | #include "sway/output.h" |
30 | #include "sway/scene_descriptor.h" | ||
25 | #include "sway/server.h" | 31 | #include "sway/server.h" |
26 | #include "sway/surface.h" | ||
27 | #include "sway/tree/arrange.h" | 32 | #include "sway/tree/arrange.h" |
28 | #include "sway/tree/container.h" | 33 | #include "sway/tree/container.h" |
29 | #include "sway/tree/root.h" | 34 | #include "sway/tree/root.h" |
30 | #include "sway/tree/view.h" | 35 | #include "sway/tree/view.h" |
31 | #include "sway/tree/workspace.h" | 36 | #include "sway/tree/workspace.h" |
32 | 37 | ||
38 | #if WLR_HAS_DRM_BACKEND | ||
39 | #include <wlr/backend/drm.h> | ||
40 | #include <wlr/types/wlr_drm_lease_v1.h> | ||
41 | #endif | ||
42 | |||
43 | bool output_match_name_or_id(struct sway_output *output, | ||
44 | const char *name_or_id) { | ||
45 | if (strcmp(name_or_id, "*") == 0) { | ||
46 | return true; | ||
47 | } | ||
48 | |||
49 | char identifier[128]; | ||
50 | output_get_identifier(identifier, sizeof(identifier), output); | ||
51 | return strcasecmp(identifier, name_or_id) == 0 | ||
52 | || strcasecmp(output->wlr_output->name, name_or_id) == 0; | ||
53 | } | ||
54 | |||
33 | struct sway_output *output_by_name_or_id(const char *name_or_id) { | 55 | struct sway_output *output_by_name_or_id(const char *name_or_id) { |
34 | for (int i = 0; i < root->outputs->length; ++i) { | 56 | for (int i = 0; i < root->outputs->length; ++i) { |
35 | struct sway_output *output = root->outputs->items[i]; | 57 | struct sway_output *output = root->outputs->items[i]; |
36 | char identifier[128]; | 58 | if (output_match_name_or_id(output, name_or_id)) { |
37 | output_get_identifier(identifier, sizeof(identifier), output); | ||
38 | if (strcasecmp(identifier, name_or_id) == 0 | ||
39 | || strcasecmp(output->wlr_output->name, name_or_id) == 0) { | ||
40 | return output; | 59 | return output; |
41 | } | 60 | } |
42 | } | 61 | } |
@@ -46,583 +65,217 @@ struct sway_output *output_by_name_or_id(const char *name_or_id) { | |||
46 | struct sway_output *all_output_by_name_or_id(const char *name_or_id) { | 65 | struct sway_output *all_output_by_name_or_id(const char *name_or_id) { |
47 | struct sway_output *output; | 66 | struct sway_output *output; |
48 | wl_list_for_each(output, &root->all_outputs, link) { | 67 | wl_list_for_each(output, &root->all_outputs, link) { |
49 | char identifier[128]; | 68 | if (output_match_name_or_id(output, name_or_id)) { |
50 | output_get_identifier(identifier, sizeof(identifier), output); | ||
51 | if (strcasecmp(identifier, name_or_id) == 0 | ||
52 | || strcasecmp(output->wlr_output->name, name_or_id) == 0) { | ||
53 | return output; | 69 | return output; |
54 | } | 70 | } |
55 | } | 71 | } |
56 | return NULL; | 72 | return NULL; |
57 | } | 73 | } |
58 | 74 | ||
59 | /** | ||
60 | * Rotate a child's position relative to a parent. The parent size is (pw, ph), | ||
61 | * the child position is (*sx, *sy) and its size is (sw, sh). | ||
62 | */ | ||
63 | static void rotate_child_position(double *sx, double *sy, double sw, double sh, | ||
64 | double pw, double ph, float rotation) { | ||
65 | if (rotation == 0.0f) { | ||
66 | return; | ||
67 | } | ||
68 | 75 | ||
69 | // Coordinates relative to the center of the subsurface | 76 | struct sway_workspace *output_get_active_workspace(struct sway_output *output) { |
70 | double ox = *sx - pw/2 + sw/2, | 77 | struct sway_seat *seat = input_manager_current_seat(); |
71 | oy = *sy - ph/2 + sh/2; | 78 | struct sway_node *focus = seat_get_active_tiling_child(seat, &output->node); |
72 | // Rotated coordinates | 79 | if (!focus) { |
73 | double rx = cos(-rotation)*ox - sin(-rotation)*oy, | 80 | if (!output->workspaces->length) { |
74 | ry = cos(-rotation)*oy + sin(-rotation)*ox; | 81 | return NULL; |
75 | *sx = rx + pw/2 - sw/2; | 82 | } |
76 | *sy = ry + ph/2 - sh/2; | 83 | return output->workspaces->items[0]; |
84 | } | ||
85 | return focus->sway_workspace; | ||
77 | } | 86 | } |
78 | 87 | ||
79 | struct surface_iterator_data { | 88 | struct send_frame_done_data { |
80 | sway_surface_iterator_func_t user_iterator; | 89 | struct timespec when; |
81 | void *user_data; | 90 | int msec_until_refresh; |
82 | |||
83 | struct sway_output *output; | 91 | struct sway_output *output; |
84 | struct sway_view *view; | ||
85 | double ox, oy; | ||
86 | int width, height; | ||
87 | float rotation; | ||
88 | }; | 92 | }; |
89 | 93 | ||
90 | static bool get_surface_box(struct surface_iterator_data *data, | 94 | struct buffer_timer { |
91 | struct wlr_surface *surface, int sx, int sy, | 95 | struct wl_listener destroy; |
92 | struct wlr_box *surface_box) { | 96 | struct wl_event_source *frame_done_timer; |
93 | struct sway_output *output = data->output; | 97 | }; |
94 | |||
95 | if (!wlr_surface_has_buffer(surface)) { | ||
96 | return false; | ||
97 | } | ||
98 | |||
99 | int sw = surface->current.width; | ||
100 | int sh = surface->current.height; | ||
101 | |||
102 | double _sx = sx + surface->sx; | ||
103 | double _sy = sy + surface->sy; | ||
104 | rotate_child_position(&_sx, &_sy, sw, sh, data->width, data->height, | ||
105 | data->rotation); | ||
106 | |||
107 | struct wlr_box box = { | ||
108 | .x = data->ox + _sx, | ||
109 | .y = data->oy + _sy, | ||
110 | .width = sw, | ||
111 | .height = sh, | ||
112 | }; | ||
113 | if (surface_box != NULL) { | ||
114 | memcpy(surface_box, &box, sizeof(struct wlr_box)); | ||
115 | } | ||
116 | |||
117 | struct wlr_box rotated_box; | ||
118 | wlr_box_rotated_bounds(&rotated_box, &box, data->rotation); | ||
119 | |||
120 | struct wlr_box output_box = { | ||
121 | .width = output->width, | ||
122 | .height = output->height, | ||
123 | }; | ||
124 | |||
125 | struct wlr_box intersection; | ||
126 | return wlr_box_intersection(&intersection, &output_box, &rotated_box); | ||
127 | } | ||
128 | |||
129 | static void output_for_each_surface_iterator(struct wlr_surface *surface, | ||
130 | int sx, int sy, void *_data) { | ||
131 | struct surface_iterator_data *data = _data; | ||
132 | |||
133 | struct wlr_box box; | ||
134 | bool intersects = get_surface_box(data, surface, sx, sy, &box); | ||
135 | if (!intersects) { | ||
136 | return; | ||
137 | } | ||
138 | 98 | ||
139 | data->user_iterator(data->output, data->view, surface, &box, data->rotation, | 99 | static int handle_buffer_timer(void *data) { |
140 | data->user_data); | 100 | struct wlr_scene_buffer *buffer = data; |
141 | } | ||
142 | 101 | ||
143 | void output_surface_for_each_surface(struct sway_output *output, | 102 | struct timespec now; |
144 | struct wlr_surface *surface, double ox, double oy, | 103 | clock_gettime(CLOCK_MONOTONIC, &now); |
145 | sway_surface_iterator_func_t iterator, void *user_data) { | 104 | wlr_scene_buffer_send_frame_done(buffer, &now); |
146 | struct surface_iterator_data data = { | 105 | return 0; |
147 | .user_iterator = iterator, | ||
148 | .user_data = user_data, | ||
149 | .output = output, | ||
150 | .view = NULL, | ||
151 | .ox = ox, | ||
152 | .oy = oy, | ||
153 | .width = surface->current.width, | ||
154 | .height = surface->current.height, | ||
155 | .rotation = 0, | ||
156 | }; | ||
157 | |||
158 | wlr_surface_for_each_surface(surface, | ||
159 | output_for_each_surface_iterator, &data); | ||
160 | } | 106 | } |
161 | 107 | ||
162 | void output_view_for_each_surface(struct sway_output *output, | 108 | static void handle_buffer_timer_destroy(struct wl_listener *listener, |
163 | struct sway_view *view, sway_surface_iterator_func_t iterator, | 109 | void *data) { |
164 | void *user_data) { | 110 | struct buffer_timer *timer = wl_container_of(listener, timer, destroy); |
165 | struct surface_iterator_data data = { | ||
166 | .user_iterator = iterator, | ||
167 | .user_data = user_data, | ||
168 | .output = output, | ||
169 | .view = view, | ||
170 | .ox = view->container->surface_x - output->lx | ||
171 | - view->geometry.x, | ||
172 | .oy = view->container->surface_y - output->ly | ||
173 | - view->geometry.y, | ||
174 | .width = view->container->current.content_width, | ||
175 | .height = view->container->current.content_height, | ||
176 | .rotation = 0, // TODO | ||
177 | }; | ||
178 | |||
179 | view_for_each_surface(view, output_for_each_surface_iterator, &data); | ||
180 | } | ||
181 | 111 | ||
182 | void output_view_for_each_popup_surface(struct sway_output *output, | 112 | wl_list_remove(&timer->destroy.link); |
183 | struct sway_view *view, sway_surface_iterator_func_t iterator, | 113 | wl_event_source_remove(timer->frame_done_timer); |
184 | void *user_data) { | 114 | free(timer); |
185 | struct surface_iterator_data data = { | ||
186 | .user_iterator = iterator, | ||
187 | .user_data = user_data, | ||
188 | .output = output, | ||
189 | .view = view, | ||
190 | .ox = view->container->surface_x - output->lx | ||
191 | - view->geometry.x, | ||
192 | .oy = view->container->surface_y - output->ly | ||
193 | - view->geometry.y, | ||
194 | .width = view->container->current.content_width, | ||
195 | .height = view->container->current.content_height, | ||
196 | .rotation = 0, // TODO | ||
197 | }; | ||
198 | |||
199 | view_for_each_popup_surface(view, output_for_each_surface_iterator, &data); | ||
200 | } | 115 | } |
201 | 116 | ||
202 | void output_layer_for_each_surface(struct sway_output *output, | 117 | static struct buffer_timer *buffer_timer_get_or_create(struct wlr_scene_buffer *buffer) { |
203 | struct wl_list *layer_surfaces, sway_surface_iterator_func_t iterator, | 118 | struct buffer_timer *timer = |
204 | void *user_data) { | 119 | scene_descriptor_try_get(&buffer->node, SWAY_SCENE_DESC_BUFFER_TIMER); |
205 | struct sway_layer_surface *layer_surface; | 120 | if (timer) { |
206 | wl_list_for_each(layer_surface, layer_surfaces, link) { | 121 | return timer; |
207 | struct wlr_layer_surface_v1 *wlr_layer_surface_v1 = | ||
208 | layer_surface->layer_surface; | ||
209 | output_surface_for_each_surface(output, wlr_layer_surface_v1->surface, | ||
210 | layer_surface->geo.x, layer_surface->geo.y, iterator, | ||
211 | user_data); | ||
212 | |||
213 | struct wlr_xdg_popup *state; | ||
214 | wl_list_for_each(state, &wlr_layer_surface_v1->popups, link) { | ||
215 | struct wlr_xdg_surface *popup = state->base; | ||
216 | if (!popup->configured) { | ||
217 | continue; | ||
218 | } | ||
219 | |||
220 | double popup_sx, popup_sy; | ||
221 | popup_sx = layer_surface->geo.x + | ||
222 | popup->popup->geometry.x - popup->geometry.x; | ||
223 | popup_sy = layer_surface->geo.y + | ||
224 | popup->popup->geometry.y - popup->geometry.y; | ||
225 | |||
226 | struct wlr_surface *surface = popup->surface; | ||
227 | |||
228 | struct surface_iterator_data data = { | ||
229 | .user_iterator = iterator, | ||
230 | .user_data = user_data, | ||
231 | .output = output, | ||
232 | .view = NULL, | ||
233 | .ox = popup_sx, | ||
234 | .oy = popup_sy, | ||
235 | .width = surface->current.width, | ||
236 | .height = surface->current.height, | ||
237 | .rotation = 0, | ||
238 | }; | ||
239 | |||
240 | wlr_xdg_surface_for_each_surface( | ||
241 | popup, output_for_each_surface_iterator, &data); | ||
242 | } | ||
243 | } | 122 | } |
244 | } | ||
245 | 123 | ||
246 | void output_layer_for_each_toplevel_surface(struct sway_output *output, | 124 | timer = calloc(1, sizeof(struct buffer_timer)); |
247 | struct wl_list *layer_surfaces, sway_surface_iterator_func_t iterator, | 125 | if (!timer) { |
248 | void *user_data) { | 126 | return NULL; |
249 | struct sway_layer_surface *layer_surface; | ||
250 | wl_list_for_each(layer_surface, layer_surfaces, link) { | ||
251 | struct wlr_layer_surface_v1 *wlr_layer_surface_v1 = | ||
252 | layer_surface->layer_surface; | ||
253 | output_surface_for_each_surface(output, wlr_layer_surface_v1->surface, | ||
254 | layer_surface->geo.x, layer_surface->geo.y, iterator, | ||
255 | user_data); | ||
256 | } | 127 | } |
257 | } | ||
258 | |||
259 | 128 | ||
260 | void output_layer_for_each_popup_surface(struct sway_output *output, | 129 | timer->frame_done_timer = wl_event_loop_add_timer(server.wl_event_loop, |
261 | struct wl_list *layer_surfaces, sway_surface_iterator_func_t iterator, | 130 | handle_buffer_timer, buffer); |
262 | void *user_data) { | 131 | if (!timer->frame_done_timer) { |
263 | struct sway_layer_surface *layer_surface; | 132 | free(timer); |
264 | wl_list_for_each(layer_surface, layer_surfaces, link) { | 133 | return NULL; |
265 | struct wlr_layer_surface_v1 *wlr_layer_surface_v1 = | ||
266 | layer_surface->layer_surface; | ||
267 | |||
268 | struct wlr_xdg_popup *state; | ||
269 | wl_list_for_each(state, &wlr_layer_surface_v1->popups, link) { | ||
270 | struct wlr_xdg_surface *popup = state->base; | ||
271 | if (!popup->configured) { | ||
272 | continue; | ||
273 | } | ||
274 | |||
275 | double popup_sx, popup_sy; | ||
276 | popup_sx = layer_surface->geo.x + | ||
277 | popup->popup->geometry.x - popup->geometry.x; | ||
278 | popup_sy = layer_surface->geo.y + | ||
279 | popup->popup->geometry.y - popup->geometry.y; | ||
280 | |||
281 | struct wlr_surface *surface = popup->surface; | ||
282 | |||
283 | struct surface_iterator_data data = { | ||
284 | .user_iterator = iterator, | ||
285 | .user_data = user_data, | ||
286 | .output = output, | ||
287 | .view = NULL, | ||
288 | .ox = popup_sx, | ||
289 | .oy = popup_sy, | ||
290 | .width = surface->current.width, | ||
291 | .height = surface->current.height, | ||
292 | .rotation = 0, | ||
293 | }; | ||
294 | |||
295 | wlr_xdg_surface_for_each_surface( | ||
296 | popup, output_for_each_surface_iterator, &data); | ||
297 | } | ||
298 | } | 134 | } |
299 | } | ||
300 | 135 | ||
301 | #if HAVE_XWAYLAND | 136 | scene_descriptor_assign(&buffer->node, SWAY_SCENE_DESC_BUFFER_TIMER, timer); |
302 | void output_unmanaged_for_each_surface(struct sway_output *output, | ||
303 | struct wl_list *unmanaged, sway_surface_iterator_func_t iterator, | ||
304 | void *user_data) { | ||
305 | struct sway_xwayland_unmanaged *unmanaged_surface; | ||
306 | wl_list_for_each(unmanaged_surface, unmanaged, link) { | ||
307 | struct wlr_xwayland_surface *xsurface = | ||
308 | unmanaged_surface->wlr_xwayland_surface; | ||
309 | double ox = unmanaged_surface->lx - output->lx; | ||
310 | double oy = unmanaged_surface->ly - output->ly; | ||
311 | |||
312 | output_surface_for_each_surface(output, xsurface->surface, ox, oy, | ||
313 | iterator, user_data); | ||
314 | } | ||
315 | } | ||
316 | #endif | ||
317 | 137 | ||
318 | void output_drag_icons_for_each_surface(struct sway_output *output, | 138 | timer->destroy.notify = handle_buffer_timer_destroy; |
319 | struct wl_list *drag_icons, sway_surface_iterator_func_t iterator, | 139 | wl_signal_add(&buffer->node.events.destroy, &timer->destroy); |
320 | void *user_data) { | ||
321 | struct sway_drag_icon *drag_icon; | ||
322 | wl_list_for_each(drag_icon, drag_icons, link) { | ||
323 | double ox = drag_icon->x - output->lx; | ||
324 | double oy = drag_icon->y - output->ly; | ||
325 | |||
326 | if (drag_icon->wlr_drag_icon->mapped) { | ||
327 | output_surface_for_each_surface(output, | ||
328 | drag_icon->wlr_drag_icon->surface, ox, oy, | ||
329 | iterator, user_data); | ||
330 | } | ||
331 | } | ||
332 | } | ||
333 | |||
334 | static void for_each_surface_container_iterator(struct sway_container *con, | ||
335 | void *_data) { | ||
336 | if (!con->view || !view_is_visible(con->view)) { | ||
337 | return; | ||
338 | } | ||
339 | 140 | ||
340 | struct surface_iterator_data *data = _data; | 141 | return timer; |
341 | output_view_for_each_surface(data->output, con->view, | ||
342 | data->user_iterator, data->user_data); | ||
343 | } | 142 | } |
344 | 143 | ||
345 | static void output_for_each_surface(struct sway_output *output, | 144 | static void send_frame_done_iterator(struct wlr_scene_buffer *buffer, |
346 | sway_surface_iterator_func_t iterator, void *user_data) { | 145 | int x, int y, void *user_data) { |
347 | if (output_has_opaque_overlay_layer_surface(output)) { | 146 | struct send_frame_done_data *data = user_data; |
348 | goto overlay; | 147 | struct sway_output *output = data->output; |
349 | } | 148 | int view_max_render_time = 0; |
350 | 149 | ||
351 | struct surface_iterator_data data = { | 150 | if (buffer->primary_output != data->output->scene_output) { |
352 | .user_iterator = iterator, | 151 | return; |
353 | .user_data = user_data, | ||
354 | .output = output, | ||
355 | .view = NULL, | ||
356 | }; | ||
357 | |||
358 | struct sway_workspace *workspace = output_get_active_workspace(output); | ||
359 | struct sway_container *fullscreen_con = root->fullscreen_global; | ||
360 | if (!fullscreen_con) { | ||
361 | if (!workspace) { | ||
362 | return; | ||
363 | } | ||
364 | fullscreen_con = workspace->current.fullscreen; | ||
365 | } | ||
366 | if (fullscreen_con) { | ||
367 | for_each_surface_container_iterator(fullscreen_con, &data); | ||
368 | container_for_each_child(fullscreen_con, | ||
369 | for_each_surface_container_iterator, &data); | ||
370 | |||
371 | // TODO: Show transient containers for fullscreen global | ||
372 | if (fullscreen_con == workspace->current.fullscreen) { | ||
373 | for (int i = 0; i < workspace->current.floating->length; ++i) { | ||
374 | struct sway_container *floater = | ||
375 | workspace->current.floating->items[i]; | ||
376 | if (container_is_transient_for(floater, fullscreen_con)) { | ||
377 | for_each_surface_container_iterator(floater, &data); | ||
378 | } | ||
379 | } | ||
380 | } | ||
381 | #if HAVE_XWAYLAND | ||
382 | output_unmanaged_for_each_surface(output, &root->xwayland_unmanaged, | ||
383 | iterator, user_data); | ||
384 | #endif | ||
385 | } else { | ||
386 | output_layer_for_each_surface(output, | ||
387 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND], | ||
388 | iterator, user_data); | ||
389 | output_layer_for_each_surface(output, | ||
390 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM], | ||
391 | iterator, user_data); | ||
392 | |||
393 | workspace_for_each_container(workspace, | ||
394 | for_each_surface_container_iterator, &data); | ||
395 | |||
396 | #if HAVE_XWAYLAND | ||
397 | output_unmanaged_for_each_surface(output, &root->xwayland_unmanaged, | ||
398 | iterator, user_data); | ||
399 | #endif | ||
400 | output_layer_for_each_surface(output, | ||
401 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP], | ||
402 | iterator, user_data); | ||
403 | } | 152 | } |
404 | 153 | ||
405 | overlay: | 154 | struct wlr_scene_node *current = &buffer->node; |
406 | output_layer_for_each_surface(output, | 155 | while (true) { |
407 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], | 156 | struct sway_view *view = scene_descriptor_try_get(current, |
408 | iterator, user_data); | 157 | SWAY_SCENE_DESC_VIEW); |
409 | output_drag_icons_for_each_surface(output, &root->drag_icons, | 158 | if (view) { |
410 | iterator, user_data); | 159 | view_max_render_time = view->max_render_time; |
411 | } | 160 | break; |
412 | |||
413 | static int scale_length(int length, int offset, float scale) { | ||
414 | return round((offset + length) * scale) - round(offset * scale); | ||
415 | } | ||
416 | |||
417 | void scale_box(struct wlr_box *box, float scale) { | ||
418 | box->width = scale_length(box->width, box->x, scale); | ||
419 | box->height = scale_length(box->height, box->y, scale); | ||
420 | box->x = round(box->x * scale); | ||
421 | box->y = round(box->y * scale); | ||
422 | } | ||
423 | |||
424 | struct sway_workspace *output_get_active_workspace(struct sway_output *output) { | ||
425 | struct sway_seat *seat = input_manager_current_seat(); | ||
426 | struct sway_node *focus = seat_get_active_tiling_child(seat, &output->node); | ||
427 | if (!focus) { | ||
428 | if (!output->workspaces->length) { | ||
429 | return NULL; | ||
430 | } | 161 | } |
431 | return output->workspaces->items[0]; | ||
432 | } | ||
433 | return focus->sway_workspace; | ||
434 | } | ||
435 | 162 | ||
436 | bool output_has_opaque_overlay_layer_surface(struct sway_output *output) { | 163 | if (!current->parent) { |
437 | struct sway_layer_surface *sway_layer_surface; | 164 | break; |
438 | wl_list_for_each(sway_layer_surface, | ||
439 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], link) { | ||
440 | struct wlr_surface *wlr_surface = sway_layer_surface->layer_surface->surface; | ||
441 | pixman_box32_t output_box = { | ||
442 | .x2 = output->width, | ||
443 | .y2 = output->height, | ||
444 | }; | ||
445 | pixman_region32_t surface_opaque_box; | ||
446 | pixman_region32_init(&surface_opaque_box); | ||
447 | pixman_region32_copy(&surface_opaque_box, &wlr_surface->opaque_region); | ||
448 | pixman_region32_translate(&surface_opaque_box, | ||
449 | sway_layer_surface->geo.x, sway_layer_surface->geo.y); | ||
450 | pixman_region_overlap_t contains = | ||
451 | pixman_region32_contains_rectangle(&surface_opaque_box, &output_box); | ||
452 | pixman_region32_fini(&surface_opaque_box); | ||
453 | |||
454 | if (contains == PIXMAN_REGION_IN) { | ||
455 | return true; | ||
456 | } | 165 | } |
457 | } | ||
458 | return false; | ||
459 | } | ||
460 | 166 | ||
461 | struct send_frame_done_data { | 167 | current = ¤t->parent->node; |
462 | struct timespec when; | ||
463 | int msec_until_refresh; | ||
464 | }; | ||
465 | |||
466 | static void send_frame_done_iterator(struct sway_output *output, struct sway_view *view, | ||
467 | struct wlr_surface *surface, struct wlr_box *box, float rotation, | ||
468 | void *user_data) { | ||
469 | int view_max_render_time = 0; | ||
470 | if (view != NULL) { | ||
471 | view_max_render_time = view->max_render_time; | ||
472 | } | 168 | } |
473 | 169 | ||
474 | struct send_frame_done_data *data = user_data; | ||
475 | |||
476 | int delay = data->msec_until_refresh - output->max_render_time | 170 | int delay = data->msec_until_refresh - output->max_render_time |
477 | - view_max_render_time; | 171 | - view_max_render_time; |
478 | 172 | ||
479 | if (output->max_render_time == 0 || view_max_render_time == 0 || delay < 1) { | 173 | struct buffer_timer *timer = NULL; |
480 | wlr_surface_send_frame_done(surface, &data->when); | ||
481 | } else { | ||
482 | struct sway_surface *sway_surface = surface->data; | ||
483 | wl_event_source_timer_update(sway_surface->frame_done_timer, delay); | ||
484 | } | ||
485 | } | ||
486 | |||
487 | static void send_frame_done(struct sway_output *output, struct send_frame_done_data *data) { | ||
488 | output_for_each_surface(output, send_frame_done_iterator, data); | ||
489 | } | ||
490 | |||
491 | static void count_surface_iterator(struct sway_output *output, struct sway_view *view, | ||
492 | struct wlr_surface *surface, struct wlr_box *_box, float rotation, | ||
493 | void *data) { | ||
494 | size_t *n = data; | ||
495 | (*n)++; | ||
496 | } | ||
497 | 174 | ||
498 | static bool scan_out_fullscreen_view(struct sway_output *output, | 175 | if (output->max_render_time != 0 && view_max_render_time != 0 && delay > 0) { |
499 | struct sway_view *view) { | 176 | timer = buffer_timer_get_or_create(buffer); |
500 | struct wlr_output *wlr_output = output->wlr_output; | ||
501 | struct sway_workspace *workspace = output->current.active_workspace; | ||
502 | if (!sway_assert(workspace, "Expected an active workspace")) { | ||
503 | return false; | ||
504 | } | 177 | } |
505 | 178 | ||
506 | if (!wl_list_empty(&view->saved_buffers)) { | 179 | if (timer) { |
507 | return false; | 180 | wl_event_source_timer_update(timer->frame_done_timer, delay); |
181 | } else { | ||
182 | wlr_scene_buffer_send_frame_done(buffer, &data->when); | ||
508 | } | 183 | } |
184 | } | ||
509 | 185 | ||
510 | for (int i = 0; i < workspace->current.floating->length; ++i) { | 186 | static enum wlr_scale_filter_mode get_scale_filter(struct sway_output *output, |
511 | struct sway_container *floater = | 187 | struct wlr_scene_buffer *buffer) { |
512 | workspace->current.floating->items[i]; | 188 | // if we are scaling down, we should always choose linear |
513 | if (container_is_transient_for(floater, view->container)) { | 189 | if (buffer->dst_width > 0 && buffer->dst_height > 0 && ( |
514 | return false; | 190 | buffer->dst_width < buffer->buffer_width || |
515 | } | 191 | buffer->dst_height < buffer->buffer_height)) { |
192 | return WLR_SCALE_FILTER_BILINEAR; | ||
516 | } | 193 | } |
517 | 194 | ||
518 | #if HAVE_XWAYLAND | 195 | switch (output->scale_filter) { |
519 | if (!wl_list_empty(&root->xwayland_unmanaged)) { | 196 | case SCALE_FILTER_LINEAR: |
520 | return false; | 197 | return WLR_SCALE_FILTER_BILINEAR; |
198 | case SCALE_FILTER_NEAREST: | ||
199 | return WLR_SCALE_FILTER_NEAREST; | ||
200 | default: | ||
201 | abort(); // unreachable | ||
521 | } | 202 | } |
522 | #endif | 203 | } |
523 | 204 | ||
524 | if (!wl_list_empty(&output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY])) { | 205 | static void output_configure_scene(struct sway_output *output, |
525 | return false; | 206 | struct wlr_scene_node *node, float opacity) { |
526 | } | 207 | if (!node->enabled) { |
527 | if (!wl_list_empty(&root->drag_icons)) { | 208 | return; |
528 | return false; | ||
529 | } | 209 | } |
530 | 210 | ||
531 | struct wlr_surface *surface = view->surface; | 211 | struct sway_container *con = |
532 | if (surface == NULL) { | 212 | scene_descriptor_try_get(node, SWAY_SCENE_DESC_CONTAINER); |
533 | return false; | 213 | if (con) { |
534 | } | 214 | opacity = con->alpha; |
535 | size_t n_surfaces = 0; | ||
536 | output_view_for_each_surface(output, view, | ||
537 | count_surface_iterator, &n_surfaces); | ||
538 | if (n_surfaces != 1) { | ||
539 | return false; | ||
540 | } | 215 | } |
541 | 216 | ||
542 | if (surface->buffer == NULL) { | 217 | if (node->type == WLR_SCENE_NODE_BUFFER) { |
543 | return false; | 218 | struct wlr_scene_buffer *buffer = wlr_scene_buffer_from_node(node); |
544 | } | ||
545 | 219 | ||
546 | if ((float)surface->current.scale != wlr_output->scale || | 220 | // hack: don't call the scene setter because that will damage all outputs |
547 | surface->current.transform != wlr_output->transform) { | 221 | // We don't want to damage outputs that aren't our current output that |
548 | return false; | 222 | // we're configuring |
549 | } | 223 | buffer->filter_mode = get_scale_filter(output, buffer); |
550 | 224 | ||
551 | wlr_output_attach_buffer(wlr_output, &surface->buffer->base); | 225 | wlr_scene_buffer_set_opacity(buffer, opacity); |
552 | if (!wlr_output_test(wlr_output)) { | 226 | } else if (node->type == WLR_SCENE_NODE_TREE) { |
553 | return false; | 227 | struct wlr_scene_tree *tree = wlr_scene_tree_from_node(node); |
228 | struct wlr_scene_node *node; | ||
229 | wl_list_for_each(node, &tree->children, link) { | ||
230 | output_configure_scene(output, node, opacity); | ||
231 | } | ||
554 | } | 232 | } |
555 | |||
556 | wlr_presentation_surface_sampled_on_output(server.presentation, surface, | ||
557 | wlr_output); | ||
558 | |||
559 | return wlr_output_commit(wlr_output); | ||
560 | } | 233 | } |
561 | 234 | ||
562 | static int output_repaint_timer_handler(void *data) { | 235 | static int output_repaint_timer_handler(void *data) { |
563 | struct sway_output *output = data; | 236 | struct sway_output *output = data; |
564 | if (output->wlr_output == NULL) { | ||
565 | return 0; | ||
566 | } | ||
567 | 237 | ||
568 | output->wlr_output->frame_pending = false; | 238 | if (!output->enabled) { |
569 | |||
570 | struct sway_workspace *workspace = output->current.active_workspace; | ||
571 | if (workspace == NULL) { | ||
572 | return 0; | 239 | return 0; |
573 | } | 240 | } |
574 | 241 | ||
575 | struct sway_container *fullscreen_con = root->fullscreen_global; | 242 | output->wlr_output->frame_pending = false; |
576 | if (!fullscreen_con) { | ||
577 | fullscreen_con = workspace->current.fullscreen; | ||
578 | } | ||
579 | 243 | ||
580 | if (fullscreen_con && fullscreen_con->view) { | 244 | output_configure_scene(output, &root->root_scene->tree.node, 1.0f); |
581 | // Try to scan-out the fullscreen view | ||
582 | static bool last_scanned_out = false; | ||
583 | bool scanned_out = | ||
584 | scan_out_fullscreen_view(output, fullscreen_con->view); | ||
585 | 245 | ||
586 | if (scanned_out && !last_scanned_out) { | 246 | if (output->gamma_lut_changed) { |
587 | sway_log(SWAY_DEBUG, "Scanning out fullscreen view on %s", | 247 | struct wlr_output_state pending; |
588 | output->wlr_output->name); | 248 | wlr_output_state_init(&pending); |
249 | if (!wlr_scene_output_build_state(output->scene_output, &pending, NULL)) { | ||
250 | return 0; | ||
589 | } | 251 | } |
590 | if (last_scanned_out && !scanned_out) { | 252 | |
591 | sway_log(SWAY_DEBUG, "Stopping fullscreen view scan out on %s", | 253 | output->gamma_lut_changed = false; |
592 | output->wlr_output->name); | 254 | struct wlr_gamma_control_v1 *gamma_control = |
255 | wlr_gamma_control_manager_v1_get_control( | ||
256 | server.gamma_control_manager_v1, output->wlr_output); | ||
257 | if (!wlr_gamma_control_v1_apply(gamma_control, &pending)) { | ||
258 | wlr_output_state_finish(&pending); | ||
259 | return 0; | ||
593 | } | 260 | } |
594 | last_scanned_out = scanned_out; | ||
595 | 261 | ||
596 | if (scanned_out) { | 262 | if (!wlr_output_commit_state(output->wlr_output, &pending)) { |
263 | wlr_gamma_control_v1_send_failed_and_destroy(gamma_control); | ||
264 | wlr_output_state_finish(&pending); | ||
597 | return 0; | 265 | return 0; |
598 | } | 266 | } |
599 | } | ||
600 | 267 | ||
601 | bool needs_frame; | 268 | wlr_output_state_finish(&pending); |
602 | pixman_region32_t damage; | ||
603 | pixman_region32_init(&damage); | ||
604 | if (!wlr_output_damage_attach_render(output->damage, | ||
605 | &needs_frame, &damage)) { | ||
606 | return 0; | 269 | return 0; |
607 | } | 270 | } |
608 | 271 | ||
609 | if (needs_frame) { | 272 | wlr_scene_output_commit(output->scene_output, NULL); |
610 | struct timespec now; | ||
611 | clock_gettime(CLOCK_MONOTONIC, &now); | ||
612 | |||
613 | output_render(output, &now, &damage); | ||
614 | } else { | ||
615 | wlr_output_rollback(output->wlr_output); | ||
616 | } | ||
617 | |||
618 | pixman_region32_fini(&damage); | ||
619 | |||
620 | return 0; | 273 | return 0; |
621 | } | 274 | } |
622 | 275 | ||
623 | static void damage_handle_frame(struct wl_listener *listener, void *user_data) { | 276 | static void handle_frame(struct wl_listener *listener, void *user_data) { |
624 | struct sway_output *output = | 277 | struct sway_output *output = |
625 | wl_container_of(listener, output, damage_frame); | 278 | wl_container_of(listener, output, frame); |
626 | if (!output->enabled || !output->wlr_output->enabled) { | 279 | if (!output->enabled || !output->wlr_output->enabled) { |
627 | return; | 280 | return; |
628 | } | 281 | } |
@@ -633,9 +286,7 @@ static void damage_handle_frame(struct wl_listener *listener, void *user_data) { | |||
633 | 286 | ||
634 | if (output->max_render_time != 0) { | 287 | if (output->max_render_time != 0) { |
635 | struct timespec now; | 288 | struct timespec now; |
636 | clockid_t presentation_clock | 289 | clock_gettime(CLOCK_MONOTONIC, &now); |
637 | = wlr_backend_get_presentation_clock(server.backend); | ||
638 | clock_gettime(presentation_clock, &now); | ||
639 | 290 | ||
640 | const long NSEC_IN_SECONDS = 1000000000; | 291 | const long NSEC_IN_SECONDS = 1000000000; |
641 | struct timespec predicted_refresh = output->last_presentation; | 292 | struct timespec predicted_refresh = output->last_presentation; |
@@ -682,124 +333,8 @@ static void damage_handle_frame(struct wl_listener *listener, void *user_data) { | |||
682 | struct send_frame_done_data data = {0}; | 333 | struct send_frame_done_data data = {0}; |
683 | clock_gettime(CLOCK_MONOTONIC, &data.when); | 334 | clock_gettime(CLOCK_MONOTONIC, &data.when); |
684 | data.msec_until_refresh = msec_until_refresh; | 335 | data.msec_until_refresh = msec_until_refresh; |
685 | send_frame_done(output, &data); | 336 | data.output = output; |
686 | } | 337 | wlr_scene_output_for_each_buffer(output->scene_output, send_frame_done_iterator, &data); |
687 | |||
688 | void output_damage_whole(struct sway_output *output) { | ||
689 | // The output can exist with no wlr_output if it's just been disconnected | ||
690 | // and the transaction to evacuate it has't completed yet. | ||
691 | if (output && output->wlr_output && output->damage) { | ||
692 | wlr_output_damage_add_whole(output->damage); | ||
693 | } | ||
694 | } | ||
695 | |||
696 | static void damage_surface_iterator(struct sway_output *output, struct sway_view *view, | ||
697 | struct wlr_surface *surface, struct wlr_box *_box, float rotation, | ||
698 | void *_data) { | ||
699 | bool *data = _data; | ||
700 | bool whole = *data; | ||
701 | |||
702 | struct wlr_box box = *_box; | ||
703 | scale_box(&box, output->wlr_output->scale); | ||
704 | |||
705 | int center_x = box.x + box.width/2; | ||
706 | int center_y = box.y + box.height/2; | ||
707 | |||
708 | if (pixman_region32_not_empty(&surface->buffer_damage)) { | ||
709 | pixman_region32_t damage; | ||
710 | pixman_region32_init(&damage); | ||
711 | wlr_surface_get_effective_damage(surface, &damage); | ||
712 | wlr_region_scale(&damage, &damage, output->wlr_output->scale); | ||
713 | if (ceil(output->wlr_output->scale) > surface->current.scale) { | ||
714 | // When scaling up a surface, it'll become blurry so we need to | ||
715 | // expand the damage region | ||
716 | wlr_region_expand(&damage, &damage, | ||
717 | ceil(output->wlr_output->scale) - surface->current.scale); | ||
718 | } | ||
719 | pixman_region32_translate(&damage, box.x, box.y); | ||
720 | wlr_region_rotated_bounds(&damage, &damage, rotation, | ||
721 | center_x, center_y); | ||
722 | wlr_output_damage_add(output->damage, &damage); | ||
723 | pixman_region32_fini(&damage); | ||
724 | } | ||
725 | |||
726 | if (whole) { | ||
727 | wlr_box_rotated_bounds(&box, &box, rotation); | ||
728 | wlr_output_damage_add_box(output->damage, &box); | ||
729 | } | ||
730 | |||
731 | if (!wl_list_empty(&surface->current.frame_callback_list)) { | ||
732 | wlr_output_schedule_frame(output->wlr_output); | ||
733 | } | ||
734 | } | ||
735 | |||
736 | void output_damage_surface(struct sway_output *output, double ox, double oy, | ||
737 | struct wlr_surface *surface, bool whole) { | ||
738 | output_surface_for_each_surface(output, surface, ox, oy, | ||
739 | damage_surface_iterator, &whole); | ||
740 | } | ||
741 | |||
742 | void output_damage_from_view(struct sway_output *output, | ||
743 | struct sway_view *view) { | ||
744 | if (!view_is_visible(view)) { | ||
745 | return; | ||
746 | } | ||
747 | bool whole = false; | ||
748 | output_view_for_each_surface(output, view, damage_surface_iterator, &whole); | ||
749 | } | ||
750 | |||
751 | // Expecting an unscaled box in layout coordinates | ||
752 | void output_damage_box(struct sway_output *output, struct wlr_box *_box) { | ||
753 | struct wlr_box box; | ||
754 | memcpy(&box, _box, sizeof(struct wlr_box)); | ||
755 | box.x -= output->lx; | ||
756 | box.y -= output->ly; | ||
757 | scale_box(&box, output->wlr_output->scale); | ||
758 | wlr_output_damage_add_box(output->damage, &box); | ||
759 | } | ||
760 | |||
761 | static void damage_child_views_iterator(struct sway_container *con, | ||
762 | void *data) { | ||
763 | if (!con->view || !view_is_visible(con->view)) { | ||
764 | return; | ||
765 | } | ||
766 | struct sway_output *output = data; | ||
767 | bool whole = true; | ||
768 | output_view_for_each_surface(output, con->view, damage_surface_iterator, | ||
769 | &whole); | ||
770 | } | ||
771 | |||
772 | void output_damage_whole_container(struct sway_output *output, | ||
773 | struct sway_container *con) { | ||
774 | // Pad the box by 1px, because the width is a double and might be a fraction | ||
775 | struct wlr_box box = { | ||
776 | .x = con->current.x - output->lx - 1, | ||
777 | .y = con->current.y - output->ly - 1, | ||
778 | .width = con->current.width + 2, | ||
779 | .height = con->current.height + 2, | ||
780 | }; | ||
781 | scale_box(&box, output->wlr_output->scale); | ||
782 | wlr_output_damage_add_box(output->damage, &box); | ||
783 | // Damage subsurfaces as well, which may extend outside the box | ||
784 | if (con->view) { | ||
785 | damage_child_views_iterator(con, output); | ||
786 | } else { | ||
787 | container_for_each_child(con, damage_child_views_iterator, output); | ||
788 | } | ||
789 | } | ||
790 | |||
791 | static void damage_handle_destroy(struct wl_listener *listener, void *data) { | ||
792 | struct sway_output *output = | ||
793 | wl_container_of(listener, output, damage_destroy); | ||
794 | if (!output->enabled) { | ||
795 | return; | ||
796 | } | ||
797 | output_disable(output); | ||
798 | |||
799 | wl_list_remove(&output->damage_destroy.link); | ||
800 | wl_list_remove(&output->damage_frame.link); | ||
801 | |||
802 | transaction_commit_dirty(); | ||
803 | } | 338 | } |
804 | 339 | ||
805 | static void update_output_manager_config(struct sway_server *server) { | 340 | static void update_output_manager_config(struct sway_server *server) { |
@@ -808,73 +343,61 @@ static void update_output_manager_config(struct sway_server *server) { | |||
808 | 343 | ||
809 | struct sway_output *output; | 344 | struct sway_output *output; |
810 | wl_list_for_each(output, &root->all_outputs, link) { | 345 | wl_list_for_each(output, &root->all_outputs, link) { |
811 | if (output == root->noop_output) { | 346 | if (output == root->fallback_output) { |
812 | continue; | 347 | continue; |
813 | } | 348 | } |
814 | struct wlr_output_configuration_head_v1 *config_head = | 349 | struct wlr_output_configuration_head_v1 *config_head = |
815 | wlr_output_configuration_head_v1_create(config, output->wlr_output); | 350 | wlr_output_configuration_head_v1_create(config, output->wlr_output); |
816 | struct wlr_box *output_box = wlr_output_layout_get_box( | 351 | struct wlr_box output_box; |
817 | root->output_layout, output->wlr_output); | 352 | wlr_output_layout_get_box(root->output_layout, |
818 | // We mark the output enabled even if it is switched off by DPMS | 353 | output->wlr_output, &output_box); |
819 | config_head->state.enabled = output->enabled; | 354 | // We mark the output enabled when it's switched off but not disabled |
820 | config_head->state.mode = output->current_mode; | 355 | config_head->state.enabled = !wlr_box_empty(&output_box); |
821 | if (output_box) { | 356 | config_head->state.x = output_box.x; |
822 | config_head->state.x = output_box->x; | 357 | config_head->state.y = output_box.y; |
823 | config_head->state.y = output_box->y; | ||
824 | } | ||
825 | } | 358 | } |
826 | 359 | ||
827 | wlr_output_manager_v1_set_configuration(server->output_manager_v1, config); | 360 | wlr_output_manager_v1_set_configuration(server->output_manager_v1, config); |
361 | |||
362 | ipc_event_output(); | ||
828 | } | 363 | } |
829 | 364 | ||
830 | static void handle_destroy(struct wl_listener *listener, void *data) { | 365 | static void begin_destroy(struct sway_output *output) { |
831 | struct sway_output *output = wl_container_of(listener, output, destroy); | ||
832 | struct sway_server *server = output->server; | 366 | struct sway_server *server = output->server; |
833 | wl_signal_emit(&output->events.destroy, output); | ||
834 | 367 | ||
835 | if (output->enabled) { | 368 | if (output->enabled) { |
836 | output_disable(output); | 369 | output_disable(output); |
837 | } | 370 | } |
371 | |||
838 | output_begin_destroy(output); | 372 | output_begin_destroy(output); |
839 | 373 | ||
374 | wl_list_remove(&output->link); | ||
375 | |||
376 | wl_list_remove(&output->layout_destroy.link); | ||
840 | wl_list_remove(&output->destroy.link); | 377 | wl_list_remove(&output->destroy.link); |
841 | wl_list_remove(&output->commit.link); | 378 | wl_list_remove(&output->commit.link); |
842 | wl_list_remove(&output->mode.link); | ||
843 | wl_list_remove(&output->present.link); | 379 | wl_list_remove(&output->present.link); |
380 | wl_list_remove(&output->frame.link); | ||
381 | wl_list_remove(&output->request_state.link); | ||
382 | |||
383 | wlr_scene_output_destroy(output->scene_output); | ||
384 | output->scene_output = NULL; | ||
385 | output->wlr_output->data = NULL; | ||
386 | output->wlr_output = NULL; | ||
844 | 387 | ||
845 | transaction_commit_dirty(); | 388 | transaction_commit_dirty(); |
846 | 389 | ||
847 | update_output_manager_config(server); | 390 | update_output_manager_config(server); |
848 | } | 391 | } |
849 | 392 | ||
850 | static void handle_mode(struct wl_listener *listener, void *data) { | 393 | static void handle_destroy(struct wl_listener *listener, void *data) { |
851 | struct sway_output *output = wl_container_of(listener, output, mode); | 394 | struct sway_output *output = wl_container_of(listener, output, destroy); |
852 | if (!output->enabled && !output->enabling) { | 395 | begin_destroy(output); |
853 | struct output_config *oc = find_output_config(output); | ||
854 | if (output->wlr_output->current_mode != NULL && | ||
855 | (!oc || oc->enabled)) { | ||
856 | // We want to enable this output, but it didn't work last time, | ||
857 | // possibly because we hadn't enough CRTCs. Try again now that the | ||
858 | // output has a mode. | ||
859 | sway_log(SWAY_DEBUG, "Output %s has gained a CRTC, " | ||
860 | "trying to enable it", output->wlr_output->name); | ||
861 | apply_output_config(oc, output); | ||
862 | } | ||
863 | return; | ||
864 | } | ||
865 | if (!output->enabled) { | ||
866 | return; | ||
867 | } | ||
868 | arrange_layers(output); | ||
869 | arrange_output(output); | ||
870 | transaction_commit_dirty(); | ||
871 | |||
872 | update_output_manager_config(output->server); | ||
873 | } | 396 | } |
874 | 397 | ||
875 | static void update_textures(struct sway_container *con, void *data) { | 398 | static void handle_layout_destroy(struct wl_listener *listener, void *data) { |
876 | container_update_title_textures(con); | 399 | struct sway_output *output = wl_container_of(listener, output, layout_destroy); |
877 | container_update_marks_textures(con); | 400 | begin_destroy(output); |
878 | } | 401 | } |
879 | 402 | ||
880 | static void handle_commit(struct wl_listener *listener, void *data) { | 403 | static void handle_commit(struct wl_listener *listener, void *data) { |
@@ -885,24 +408,28 @@ static void handle_commit(struct wl_listener *listener, void *data) { | |||
885 | return; | 408 | return; |
886 | } | 409 | } |
887 | 410 | ||
888 | if (event->committed & WLR_OUTPUT_STATE_SCALE) { | 411 | if (event->state->committed & ( |
889 | output_for_each_container(output, update_textures, NULL); | 412 | WLR_OUTPUT_STATE_MODE | |
890 | } | 413 | WLR_OUTPUT_STATE_TRANSFORM | |
891 | 414 | WLR_OUTPUT_STATE_SCALE)) { | |
892 | if (event->committed & (WLR_OUTPUT_STATE_TRANSFORM | WLR_OUTPUT_STATE_SCALE)) { | ||
893 | arrange_layers(output); | 415 | arrange_layers(output); |
894 | arrange_output(output); | 416 | arrange_output(output); |
895 | transaction_commit_dirty(); | 417 | transaction_commit_dirty(); |
896 | 418 | ||
897 | update_output_manager_config(output->server); | 419 | update_output_manager_config(output->server); |
898 | } | 420 | } |
421 | |||
422 | // Next time the output is enabled, try to re-apply the gamma LUT | ||
423 | if ((event->state->committed & WLR_OUTPUT_STATE_ENABLED) && !output->wlr_output->enabled) { | ||
424 | output->gamma_lut_changed = true; | ||
425 | } | ||
899 | } | 426 | } |
900 | 427 | ||
901 | static void handle_present(struct wl_listener *listener, void *data) { | 428 | static void handle_present(struct wl_listener *listener, void *data) { |
902 | struct sway_output *output = wl_container_of(listener, output, present); | 429 | struct sway_output *output = wl_container_of(listener, output, present); |
903 | struct wlr_output_event_present *output_event = data; | 430 | struct wlr_output_event_present *output_event = data; |
904 | 431 | ||
905 | if (!output->enabled) { | 432 | if (!output->enabled || !output_event->presented) { |
906 | return; | 433 | return; |
907 | } | 434 | } |
908 | 435 | ||
@@ -910,37 +437,91 @@ static void handle_present(struct wl_listener *listener, void *data) { | |||
910 | output->refresh_nsec = output_event->refresh; | 437 | output->refresh_nsec = output_event->refresh; |
911 | } | 438 | } |
912 | 439 | ||
440 | static void handle_request_state(struct wl_listener *listener, void *data) { | ||
441 | struct sway_output *output = | ||
442 | wl_container_of(listener, output, request_state); | ||
443 | const struct wlr_output_event_request_state *event = data; | ||
444 | wlr_output_commit_state(output->wlr_output, event->state); | ||
445 | } | ||
446 | |||
447 | static unsigned int last_headless_num = 0; | ||
448 | |||
913 | void handle_new_output(struct wl_listener *listener, void *data) { | 449 | void handle_new_output(struct wl_listener *listener, void *data) { |
914 | struct sway_server *server = wl_container_of(listener, server, new_output); | 450 | struct sway_server *server = wl_container_of(listener, server, new_output); |
915 | struct wlr_output *wlr_output = data; | 451 | struct wlr_output *wlr_output = data; |
916 | sway_log(SWAY_DEBUG, "New output %p: %s", wlr_output, wlr_output->name); | 452 | |
453 | if (wlr_output == root->fallback_output->wlr_output) { | ||
454 | return; | ||
455 | } | ||
456 | |||
457 | if (wlr_output_is_headless(wlr_output)) { | ||
458 | char name[64]; | ||
459 | snprintf(name, sizeof(name), "HEADLESS-%u", ++last_headless_num); | ||
460 | wlr_output_set_name(wlr_output, name); | ||
461 | } | ||
462 | |||
463 | sway_log(SWAY_DEBUG, "New output %p: %s (non-desktop: %d)", | ||
464 | wlr_output, wlr_output->name, wlr_output->non_desktop); | ||
465 | |||
466 | if (wlr_output->non_desktop) { | ||
467 | sway_log(SWAY_DEBUG, "Not configuring non-desktop output"); | ||
468 | struct sway_output_non_desktop *non_desktop = output_non_desktop_create(wlr_output); | ||
469 | #if WLR_HAS_DRM_BACKEND | ||
470 | if (server->drm_lease_manager) { | ||
471 | wlr_drm_lease_v1_manager_offer_output(server->drm_lease_manager, | ||
472 | wlr_output); | ||
473 | } | ||
474 | #endif | ||
475 | list_add(root->non_desktop_outputs, non_desktop); | ||
476 | return; | ||
477 | } | ||
478 | |||
479 | if (!wlr_output_init_render(wlr_output, server->allocator, | ||
480 | server->renderer)) { | ||
481 | sway_log(SWAY_ERROR, "Failed to init output render"); | ||
482 | return; | ||
483 | } | ||
484 | |||
485 | // Create the scene output here so we're not accidentally creating one for | ||
486 | // the fallback output | ||
487 | struct wlr_scene_output *scene_output = | ||
488 | wlr_scene_output_create(root->root_scene, wlr_output); | ||
489 | if (!scene_output) { | ||
490 | sway_log(SWAY_ERROR, "Failed to create a scene output"); | ||
491 | return; | ||
492 | } | ||
917 | 493 | ||
918 | struct sway_output *output = output_create(wlr_output); | 494 | struct sway_output *output = output_create(wlr_output); |
919 | if (!output) { | 495 | if (!output) { |
496 | sway_log(SWAY_ERROR, "Failed to create a sway output"); | ||
497 | wlr_scene_output_destroy(scene_output); | ||
920 | return; | 498 | return; |
921 | } | 499 | } |
500 | |||
922 | output->server = server; | 501 | output->server = server; |
923 | output->damage = wlr_output_damage_create(wlr_output); | 502 | output->scene_output = scene_output; |
924 | 503 | ||
504 | wl_signal_add(&root->output_layout->events.destroy, &output->layout_destroy); | ||
505 | output->layout_destroy.notify = handle_layout_destroy; | ||
925 | wl_signal_add(&wlr_output->events.destroy, &output->destroy); | 506 | wl_signal_add(&wlr_output->events.destroy, &output->destroy); |
926 | output->destroy.notify = handle_destroy; | 507 | output->destroy.notify = handle_destroy; |
927 | wl_signal_add(&wlr_output->events.commit, &output->commit); | 508 | wl_signal_add(&wlr_output->events.commit, &output->commit); |
928 | output->commit.notify = handle_commit; | 509 | output->commit.notify = handle_commit; |
929 | wl_signal_add(&wlr_output->events.mode, &output->mode); | ||
930 | output->mode.notify = handle_mode; | ||
931 | wl_signal_add(&wlr_output->events.present, &output->present); | 510 | wl_signal_add(&wlr_output->events.present, &output->present); |
932 | output->present.notify = handle_present; | 511 | output->present.notify = handle_present; |
933 | wl_signal_add(&output->damage->events.frame, &output->damage_frame); | 512 | wl_signal_add(&wlr_output->events.frame, &output->frame); |
934 | output->damage_frame.notify = damage_handle_frame; | 513 | output->frame.notify = handle_frame; |
935 | wl_signal_add(&output->damage->events.destroy, &output->damage_destroy); | 514 | wl_signal_add(&wlr_output->events.request_state, &output->request_state); |
936 | output->damage_destroy.notify = damage_handle_destroy; | 515 | output->request_state.notify = handle_request_state; |
937 | 516 | ||
938 | output->repaint_timer = wl_event_loop_add_timer(server->wl_event_loop, | 517 | output->repaint_timer = wl_event_loop_add_timer(server->wl_event_loop, |
939 | output_repaint_timer_handler, output); | 518 | output_repaint_timer_handler, output); |
940 | 519 | ||
941 | struct output_config *oc = find_output_config(output); | 520 | if (server->session_lock.lock) { |
942 | apply_output_config(oc, output); | 521 | sway_session_lock_add_output(server->session_lock.lock, output); |
943 | free_output_config(oc); | 522 | } |
523 | |||
524 | apply_all_output_configs(); | ||
944 | 525 | ||
945 | transaction_commit_dirty(); | 526 | transaction_commit_dirty(); |
946 | 527 | ||
@@ -954,62 +535,104 @@ void handle_output_layout_change(struct wl_listener *listener, | |||
954 | update_output_manager_config(server); | 535 | update_output_manager_config(server); |
955 | } | 536 | } |
956 | 537 | ||
538 | void handle_gamma_control_set_gamma(struct wl_listener *listener, void *data) { | ||
539 | struct sway_server *server = | ||
540 | wl_container_of(listener, server, gamma_control_set_gamma); | ||
541 | const struct wlr_gamma_control_manager_v1_set_gamma_event *event = data; | ||
542 | |||
543 | struct sway_output *output = event->output->data; | ||
544 | |||
545 | if(!output) { | ||
546 | return; | ||
547 | } | ||
548 | |||
549 | output->gamma_lut_changed = true; | ||
550 | wlr_output_schedule_frame(output->wlr_output); | ||
551 | } | ||
552 | |||
553 | static struct output_config *output_config_for_config_head( | ||
554 | struct wlr_output_configuration_head_v1 *config_head, | ||
555 | struct sway_output *output) { | ||
556 | struct output_config *oc = new_output_config(output->wlr_output->name); | ||
557 | oc->enabled = config_head->state.enabled; | ||
558 | if (!oc->enabled) { | ||
559 | return oc; | ||
560 | } | ||
561 | |||
562 | if (config_head->state.mode != NULL) { | ||
563 | struct wlr_output_mode *mode = config_head->state.mode; | ||
564 | oc->width = mode->width; | ||
565 | oc->height = mode->height; | ||
566 | oc->refresh_rate = mode->refresh / 1000.f; | ||
567 | } else { | ||
568 | oc->width = config_head->state.custom_mode.width; | ||
569 | oc->height = config_head->state.custom_mode.height; | ||
570 | oc->refresh_rate = | ||
571 | config_head->state.custom_mode.refresh / 1000.f; | ||
572 | } | ||
573 | oc->x = config_head->state.x; | ||
574 | oc->y = config_head->state.y; | ||
575 | oc->transform = config_head->state.transform; | ||
576 | oc->scale = config_head->state.scale; | ||
577 | oc->adaptive_sync = config_head->state.adaptive_sync_enabled; | ||
578 | return oc; | ||
579 | } | ||
580 | |||
957 | static void output_manager_apply(struct sway_server *server, | 581 | static void output_manager_apply(struct sway_server *server, |
958 | struct wlr_output_configuration_v1 *config, bool test_only) { | 582 | struct wlr_output_configuration_v1 *config, bool test_only) { |
959 | // TODO: perform atomic tests on the whole backend atomically | 583 | size_t configs_len = wl_list_length(&root->all_outputs); |
960 | 584 | struct matched_output_config *configs = calloc(configs_len, sizeof(*configs)); | |
961 | struct wlr_output_configuration_head_v1 *config_head; | 585 | if (!configs) { |
962 | // First disable outputs we need to disable | 586 | return; |
963 | bool ok = true; | 587 | } |
964 | wl_list_for_each(config_head, &config->heads, link) { | 588 | |
965 | struct wlr_output *wlr_output = config_head->state.output; | 589 | int config_idx = 0; |
966 | struct sway_output *output = wlr_output->data; | 590 | struct sway_output *sway_output; |
967 | if (!output->enabled || config_head->state.enabled) { | 591 | wl_list_for_each(sway_output, &root->all_outputs, link) { |
592 | if (sway_output == root->fallback_output) { | ||
593 | configs_len--; | ||
968 | continue; | 594 | continue; |
969 | } | 595 | } |
970 | struct output_config *oc = new_output_config(output->wlr_output->name); | ||
971 | oc->enabled = false; | ||
972 | 596 | ||
973 | if (test_only) { | 597 | struct matched_output_config *cfg = &configs[config_idx++]; |
974 | ok &= test_output_config(oc, output); | 598 | cfg->output = sway_output; |
975 | } else { | ||
976 | oc = store_output_config(oc); | ||
977 | ok &= apply_output_config(oc, output); | ||
978 | } | ||
979 | } | ||
980 | 599 | ||
981 | // Then enable outputs that need to | 600 | struct wlr_output_configuration_head_v1 *config_head; |
982 | wl_list_for_each(config_head, &config->heads, link) { | 601 | wl_list_for_each(config_head, &config->heads, link) { |
983 | struct wlr_output *wlr_output = config_head->state.output; | 602 | if (config_head->state.output == sway_output->wlr_output) { |
984 | struct sway_output *output = wlr_output->data; | 603 | cfg->config = output_config_for_config_head(config_head, sway_output); |
985 | if (!config_head->state.enabled) { | 604 | break; |
986 | continue; | 605 | } |
987 | } | 606 | } |
988 | struct output_config *oc = new_output_config(output->wlr_output->name); | 607 | if (!cfg->config) { |
989 | oc->enabled = true; | 608 | cfg->config = find_output_config(sway_output); |
990 | if (config_head->state.mode != NULL) { | ||
991 | struct wlr_output_mode *mode = config_head->state.mode; | ||
992 | oc->width = mode->width; | ||
993 | oc->height = mode->height; | ||
994 | oc->refresh_rate = mode->refresh / 1000.f; | ||
995 | } else { | ||
996 | oc->width = config_head->state.custom_mode.width; | ||
997 | oc->height = config_head->state.custom_mode.height; | ||
998 | oc->refresh_rate = | ||
999 | config_head->state.custom_mode.refresh / 1000.f; | ||
1000 | } | 609 | } |
1001 | oc->x = config_head->state.x; | 610 | } |
1002 | oc->y = config_head->state.y; | ||
1003 | oc->transform = config_head->state.transform; | ||
1004 | oc->scale = config_head->state.scale; | ||
1005 | 611 | ||
1006 | if (test_only) { | 612 | sort_output_configs_by_priority(configs, configs_len); |
1007 | ok &= test_output_config(oc, output); | 613 | bool ok = apply_output_configs(configs, configs_len, test_only, false); |
614 | for (size_t idx = 0; idx < configs_len; idx++) { | ||
615 | struct matched_output_config *cfg = &configs[idx]; | ||
616 | |||
617 | // Only store new configs for successful non-test commits. Old configs, | ||
618 | // test-only and failed commits just get freed. | ||
619 | bool store_config = false; | ||
620 | if (!test_only && ok) { | ||
621 | struct wlr_output_configuration_head_v1 *config_head; | ||
622 | wl_list_for_each(config_head, &config->heads, link) { | ||
623 | if (config_head->state.output == cfg->output->wlr_output) { | ||
624 | store_config = true; | ||
625 | break; | ||
626 | } | ||
627 | } | ||
628 | } | ||
629 | if (store_config) { | ||
630 | store_output_config(cfg->config); | ||
1008 | } else { | 631 | } else { |
1009 | oc = store_output_config(oc); | 632 | free_output_config(cfg->config); |
1010 | ok &= apply_output_config(oc, output); | ||
1011 | } | 633 | } |
1012 | } | 634 | } |
635 | free(configs); | ||
1013 | 636 | ||
1014 | if (ok) { | 637 | if (ok) { |
1015 | wlr_output_configuration_v1_send_succeeded(config); | 638 | wlr_output_configuration_v1_send_succeeded(config); |
@@ -1047,12 +670,12 @@ void handle_output_power_manager_set_mode(struct wl_listener *listener, | |||
1047 | struct output_config *oc = new_output_config(output->wlr_output->name); | 670 | struct output_config *oc = new_output_config(output->wlr_output->name); |
1048 | switch (event->mode) { | 671 | switch (event->mode) { |
1049 | case ZWLR_OUTPUT_POWER_V1_MODE_OFF: | 672 | case ZWLR_OUTPUT_POWER_V1_MODE_OFF: |
1050 | oc->dpms_state = DPMS_OFF; | 673 | oc->power = 0; |
1051 | break; | 674 | break; |
1052 | case ZWLR_OUTPUT_POWER_V1_MODE_ON: | 675 | case ZWLR_OUTPUT_POWER_V1_MODE_ON: |
1053 | oc->dpms_state = DPMS_ON; | 676 | oc->power = 1; |
1054 | break; | 677 | break; |
1055 | } | 678 | } |
1056 | oc = store_output_config(oc); | 679 | store_output_config(oc); |
1057 | apply_output_config(oc, output); | 680 | apply_all_output_configs(); |
1058 | } | 681 | } |
diff --git a/sway/desktop/render.c b/sway/desktop/render.c deleted file mode 100644 index bd85282c..00000000 --- a/sway/desktop/render.c +++ /dev/null | |||
@@ -1,1125 +0,0 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <assert.h> | ||
3 | #include <GLES2/gl2.h> | ||
4 | #include <stdlib.h> | ||
5 | #include <strings.h> | ||
6 | #include <time.h> | ||
7 | #include <wayland-server-core.h> | ||
8 | #include <wlr/render/gles2.h> | ||
9 | #include <wlr/render/wlr_renderer.h> | ||
10 | #include <wlr/types/wlr_box.h> | ||
11 | #include <wlr/types/wlr_buffer.h> | ||
12 | #include <wlr/types/wlr_matrix.h> | ||
13 | #include <wlr/types/wlr_output_damage.h> | ||
14 | #include <wlr/types/wlr_output_layout.h> | ||
15 | #include <wlr/types/wlr_output.h> | ||
16 | #include <wlr/types/wlr_surface.h> | ||
17 | #include <wlr/util/region.h> | ||
18 | #include "log.h" | ||
19 | #include "config.h" | ||
20 | #include "sway/config.h" | ||
21 | #include "sway/input/input-manager.h" | ||
22 | #include "sway/input/seat.h" | ||
23 | #include "sway/layers.h" | ||
24 | #include "sway/output.h" | ||
25 | #include "sway/server.h" | ||
26 | #include "sway/tree/arrange.h" | ||
27 | #include "sway/tree/container.h" | ||
28 | #include "sway/tree/root.h" | ||
29 | #include "sway/tree/view.h" | ||
30 | #include "sway/tree/workspace.h" | ||
31 | |||
32 | struct render_data { | ||
33 | pixman_region32_t *damage; | ||
34 | float alpha; | ||
35 | }; | ||
36 | |||
37 | /** | ||
38 | * Apply scale to a width or height. | ||
39 | * | ||
40 | * One does not simply multiply the width by the scale. We allow fractional | ||
41 | * scaling, which means the resulting scaled width might be a decimal. | ||
42 | * So we round it. | ||
43 | * | ||
44 | * But even this can produce undesirable results depending on the X or Y offset | ||
45 | * of the box. For example, with a scale of 1.5, a box with width=1 should not | ||
46 | * scale to 2px if its X coordinate is 1, because the X coordinate would have | ||
47 | * scaled to 2px. | ||
48 | */ | ||
49 | static int scale_length(int length, int offset, float scale) { | ||
50 | return round((offset + length) * scale) - round(offset * scale); | ||
51 | } | ||
52 | |||
53 | static void scissor_output(struct wlr_output *wlr_output, | ||
54 | pixman_box32_t *rect) { | ||
55 | struct wlr_renderer *renderer = wlr_backend_get_renderer(wlr_output->backend); | ||
56 | assert(renderer); | ||
57 | |||
58 | struct wlr_box box = { | ||
59 | .x = rect->x1, | ||
60 | .y = rect->y1, | ||
61 | .width = rect->x2 - rect->x1, | ||
62 | .height = rect->y2 - rect->y1, | ||
63 | }; | ||
64 | |||
65 | int ow, oh; | ||
66 | wlr_output_transformed_resolution(wlr_output, &ow, &oh); | ||
67 | |||
68 | enum wl_output_transform transform = | ||
69 | wlr_output_transform_invert(wlr_output->transform); | ||
70 | wlr_box_transform(&box, &box, transform, ow, oh); | ||
71 | |||
72 | wlr_renderer_scissor(renderer, &box); | ||
73 | } | ||
74 | |||
75 | static void set_scale_filter(struct wlr_output *wlr_output, | ||
76 | struct wlr_texture *texture, enum scale_filter_mode scale_filter) { | ||
77 | if (!wlr_texture_is_gles2(texture)) { | ||
78 | return; | ||
79 | } | ||
80 | |||
81 | struct wlr_gles2_texture_attribs attribs; | ||
82 | wlr_gles2_texture_get_attribs(texture, &attribs); | ||
83 | |||
84 | glBindTexture(attribs.target, attribs.tex); | ||
85 | |||
86 | switch (scale_filter) { | ||
87 | case SCALE_FILTER_LINEAR: | ||
88 | glTexParameteri(attribs.target, GL_TEXTURE_MAG_FILTER, GL_LINEAR); | ||
89 | break; | ||
90 | case SCALE_FILTER_NEAREST: | ||
91 | glTexParameteri(attribs.target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); | ||
92 | break; | ||
93 | case SCALE_FILTER_DEFAULT: | ||
94 | case SCALE_FILTER_SMART: | ||
95 | assert(false); // unreachable | ||
96 | } | ||
97 | } | ||
98 | |||
99 | static void render_texture(struct wlr_output *wlr_output, | ||
100 | pixman_region32_t *output_damage, struct wlr_texture *texture, | ||
101 | const struct wlr_fbox *src_box, const struct wlr_box *dst_box, | ||
102 | const float matrix[static 9], float alpha) { | ||
103 | struct wlr_renderer *renderer = | ||
104 | wlr_backend_get_renderer(wlr_output->backend); | ||
105 | struct sway_output *output = wlr_output->data; | ||
106 | |||
107 | struct wlr_gles2_texture_attribs attribs; | ||
108 | wlr_gles2_texture_get_attribs(texture, &attribs); | ||
109 | |||
110 | pixman_region32_t damage; | ||
111 | pixman_region32_init(&damage); | ||
112 | pixman_region32_union_rect(&damage, &damage, dst_box->x, dst_box->y, | ||
113 | dst_box->width, dst_box->height); | ||
114 | pixman_region32_intersect(&damage, &damage, output_damage); | ||
115 | bool damaged = pixman_region32_not_empty(&damage); | ||
116 | if (!damaged) { | ||
117 | goto damage_finish; | ||
118 | } | ||
119 | |||
120 | int nrects; | ||
121 | pixman_box32_t *rects = pixman_region32_rectangles(&damage, &nrects); | ||
122 | for (int i = 0; i < nrects; ++i) { | ||
123 | scissor_output(wlr_output, &rects[i]); | ||
124 | set_scale_filter(wlr_output, texture, output->scale_filter); | ||
125 | if (src_box != NULL) { | ||
126 | wlr_render_subtexture_with_matrix(renderer, texture, src_box, matrix, alpha); | ||
127 | } else { | ||
128 | wlr_render_texture_with_matrix(renderer, texture, matrix, alpha); | ||
129 | } | ||
130 | } | ||
131 | |||
132 | damage_finish: | ||
133 | pixman_region32_fini(&damage); | ||
134 | } | ||
135 | |||
136 | static void render_surface_iterator(struct sway_output *output, struct sway_view *view, | ||
137 | struct wlr_surface *surface, struct wlr_box *_box, float rotation, | ||
138 | void *_data) { | ||
139 | struct render_data *data = _data; | ||
140 | struct wlr_output *wlr_output = output->wlr_output; | ||
141 | pixman_region32_t *output_damage = data->damage; | ||
142 | float alpha = data->alpha; | ||
143 | |||
144 | struct wlr_texture *texture = wlr_surface_get_texture(surface); | ||
145 | if (!texture) { | ||
146 | return; | ||
147 | } | ||
148 | |||
149 | struct wlr_fbox src_box; | ||
150 | wlr_surface_get_buffer_source_box(surface, &src_box); | ||
151 | |||
152 | struct wlr_box dst_box = *_box; | ||
153 | scale_box(&dst_box, wlr_output->scale); | ||
154 | |||
155 | float matrix[9]; | ||
156 | enum wl_output_transform transform = | ||
157 | wlr_output_transform_invert(surface->current.transform); | ||
158 | wlr_matrix_project_box(matrix, &dst_box, transform, rotation, | ||
159 | wlr_output->transform_matrix); | ||
160 | |||
161 | render_texture(wlr_output, output_damage, texture, | ||
162 | &src_box, &dst_box, matrix, alpha); | ||
163 | |||
164 | wlr_presentation_surface_sampled_on_output(server.presentation, surface, | ||
165 | wlr_output); | ||
166 | } | ||
167 | |||
168 | static void render_layer_toplevel(struct sway_output *output, | ||
169 | pixman_region32_t *damage, struct wl_list *layer_surfaces) { | ||
170 | struct render_data data = { | ||
171 | .damage = damage, | ||
172 | .alpha = 1.0f, | ||
173 | }; | ||
174 | output_layer_for_each_toplevel_surface(output, layer_surfaces, | ||
175 | render_surface_iterator, &data); | ||
176 | } | ||
177 | |||
178 | static void render_layer_popups(struct sway_output *output, | ||
179 | pixman_region32_t *damage, struct wl_list *layer_surfaces) { | ||
180 | struct render_data data = { | ||
181 | .damage = damage, | ||
182 | .alpha = 1.0f, | ||
183 | }; | ||
184 | output_layer_for_each_popup_surface(output, layer_surfaces, | ||
185 | render_surface_iterator, &data); | ||
186 | } | ||
187 | |||
188 | #if HAVE_XWAYLAND | ||
189 | static void render_unmanaged(struct sway_output *output, | ||
190 | pixman_region32_t *damage, struct wl_list *unmanaged) { | ||
191 | struct render_data data = { | ||
192 | .damage = damage, | ||
193 | .alpha = 1.0f, | ||
194 | }; | ||
195 | output_unmanaged_for_each_surface(output, unmanaged, | ||
196 | render_surface_iterator, &data); | ||
197 | } | ||
198 | #endif | ||
199 | |||
200 | static void render_drag_icons(struct sway_output *output, | ||
201 | pixman_region32_t *damage, struct wl_list *drag_icons) { | ||
202 | struct render_data data = { | ||
203 | .damage = damage, | ||
204 | .alpha = 1.0f, | ||
205 | }; | ||
206 | output_drag_icons_for_each_surface(output, drag_icons, | ||
207 | render_surface_iterator, &data); | ||
208 | } | ||
209 | |||
210 | // _box.x and .y are expected to be layout-local | ||
211 | // _box.width and .height are expected to be output-buffer-local | ||
212 | void render_rect(struct sway_output *output, | ||
213 | pixman_region32_t *output_damage, const struct wlr_box *_box, | ||
214 | float color[static 4]) { | ||
215 | struct wlr_output *wlr_output = output->wlr_output; | ||
216 | struct wlr_renderer *renderer = | ||
217 | wlr_backend_get_renderer(wlr_output->backend); | ||
218 | |||
219 | struct wlr_box box; | ||
220 | memcpy(&box, _box, sizeof(struct wlr_box)); | ||
221 | box.x -= output->lx * wlr_output->scale; | ||
222 | box.y -= output->ly * wlr_output->scale; | ||
223 | |||
224 | pixman_region32_t damage; | ||
225 | pixman_region32_init(&damage); | ||
226 | pixman_region32_union_rect(&damage, &damage, box.x, box.y, | ||
227 | box.width, box.height); | ||
228 | pixman_region32_intersect(&damage, &damage, output_damage); | ||
229 | bool damaged = pixman_region32_not_empty(&damage); | ||
230 | if (!damaged) { | ||
231 | goto damage_finish; | ||
232 | } | ||
233 | |||
234 | int nrects; | ||
235 | pixman_box32_t *rects = pixman_region32_rectangles(&damage, &nrects); | ||
236 | for (int i = 0; i < nrects; ++i) { | ||
237 | scissor_output(wlr_output, &rects[i]); | ||
238 | wlr_render_rect(renderer, &box, color, | ||
239 | wlr_output->transform_matrix); | ||
240 | } | ||
241 | |||
242 | damage_finish: | ||
243 | pixman_region32_fini(&damage); | ||
244 | } | ||
245 | |||
246 | void premultiply_alpha(float color[4], float opacity) { | ||
247 | color[3] *= opacity; | ||
248 | color[0] *= color[3]; | ||
249 | color[1] *= color[3]; | ||
250 | color[2] *= color[3]; | ||
251 | } | ||
252 | |||
253 | static void render_view_toplevels(struct sway_view *view, | ||
254 | struct sway_output *output, pixman_region32_t *damage, float alpha) { | ||
255 | struct render_data data = { | ||
256 | .damage = damage, | ||
257 | .alpha = alpha, | ||
258 | }; | ||
259 | // Render all toplevels without descending into popups | ||
260 | double ox = view->container->surface_x - | ||
261 | output->lx - view->geometry.x; | ||
262 | double oy = view->container->surface_y - | ||
263 | output->ly - view->geometry.y; | ||
264 | output_surface_for_each_surface(output, view->surface, ox, oy, | ||
265 | render_surface_iterator, &data); | ||
266 | } | ||
267 | |||
268 | static void render_view_popups(struct sway_view *view, | ||
269 | struct sway_output *output, pixman_region32_t *damage, float alpha) { | ||
270 | struct render_data data = { | ||
271 | .damage = damage, | ||
272 | .alpha = alpha, | ||
273 | }; | ||
274 | output_view_for_each_popup_surface(output, view, | ||
275 | render_surface_iterator, &data); | ||
276 | } | ||
277 | |||
278 | static void render_saved_view(struct sway_view *view, | ||
279 | struct sway_output *output, pixman_region32_t *damage, float alpha) { | ||
280 | struct wlr_output *wlr_output = output->wlr_output; | ||
281 | |||
282 | if (wl_list_empty(&view->saved_buffers)) { | ||
283 | return; | ||
284 | } | ||
285 | struct sway_saved_buffer *saved_buf; | ||
286 | wl_list_for_each(saved_buf, &view->saved_buffers, link) { | ||
287 | if (!saved_buf->buffer->texture) { | ||
288 | continue; | ||
289 | } | ||
290 | |||
291 | struct wlr_box box = { | ||
292 | .x = view->container->surface_x - output->lx - | ||
293 | view->saved_geometry.x + saved_buf->x, | ||
294 | .y = view->container->surface_y - output->ly - | ||
295 | view->saved_geometry.y + saved_buf->y, | ||
296 | .width = saved_buf->width, | ||
297 | .height = saved_buf->height, | ||
298 | }; | ||
299 | |||
300 | struct wlr_box output_box = { | ||
301 | .width = output->width, | ||
302 | .height = output->height, | ||
303 | }; | ||
304 | |||
305 | struct wlr_box intersection; | ||
306 | bool intersects = wlr_box_intersection(&intersection, &output_box, &box); | ||
307 | if (!intersects) { | ||
308 | continue; | ||
309 | } | ||
310 | |||
311 | scale_box(&box, wlr_output->scale); | ||
312 | |||
313 | float matrix[9]; | ||
314 | enum wl_output_transform transform = wlr_output_transform_invert(saved_buf->transform); | ||
315 | wlr_matrix_project_box(matrix, &box, transform, 0, | ||
316 | wlr_output->transform_matrix); | ||
317 | |||
318 | render_texture(wlr_output, damage, saved_buf->buffer->texture, | ||
319 | &saved_buf->source_box, &box, matrix, alpha); | ||
320 | } | ||
321 | |||
322 | // FIXME: we should set the surface that this saved buffer originates from | ||
323 | // as sampled here. | ||
324 | // https://github.com/swaywm/sway/pull/4465#discussion_r321082059 | ||
325 | } | ||
326 | |||
327 | /** | ||
328 | * Render a view's surface and left/bottom/right borders. | ||
329 | */ | ||
330 | static void render_view(struct sway_output *output, pixman_region32_t *damage, | ||
331 | struct sway_container *con, struct border_colors *colors) { | ||
332 | struct sway_view *view = con->view; | ||
333 | if (!wl_list_empty(&view->saved_buffers)) { | ||
334 | render_saved_view(view, output, damage, view->container->alpha); | ||
335 | } else if (view->surface) { | ||
336 | render_view_toplevels(view, output, damage, view->container->alpha); | ||
337 | } | ||
338 | |||
339 | if (con->current.border == B_NONE || con->current.border == B_CSD) { | ||
340 | return; | ||
341 | } | ||
342 | |||
343 | struct wlr_box box; | ||
344 | float output_scale = output->wlr_output->scale; | ||
345 | float color[4]; | ||
346 | struct sway_container_state *state = &con->current; | ||
347 | |||
348 | if (state->border_left) { | ||
349 | memcpy(&color, colors->child_border, sizeof(float) * 4); | ||
350 | premultiply_alpha(color, con->alpha); | ||
351 | box.x = state->x; | ||
352 | box.y = state->content_y; | ||
353 | box.width = state->border_thickness; | ||
354 | box.height = state->content_height; | ||
355 | scale_box(&box, output_scale); | ||
356 | render_rect(output, damage, &box, color); | ||
357 | } | ||
358 | |||
359 | list_t *siblings = container_get_current_siblings(con); | ||
360 | enum sway_container_layout layout = | ||
361 | container_current_parent_layout(con); | ||
362 | |||
363 | if (state->border_right) { | ||
364 | if (!container_is_floating(con) && siblings->length == 1 && layout == L_HORIZ) { | ||
365 | memcpy(&color, colors->indicator, sizeof(float) * 4); | ||
366 | } else { | ||
367 | memcpy(&color, colors->child_border, sizeof(float) * 4); | ||
368 | } | ||
369 | premultiply_alpha(color, con->alpha); | ||
370 | box.x = state->content_x + state->content_width; | ||
371 | box.y = state->content_y; | ||
372 | box.width = state->border_thickness; | ||
373 | box.height = state->content_height; | ||
374 | scale_box(&box, output_scale); | ||
375 | render_rect(output, damage, &box, color); | ||
376 | } | ||
377 | |||
378 | if (state->border_bottom) { | ||
379 | if (!container_is_floating(con) && siblings->length == 1 && layout == L_VERT) { | ||
380 | memcpy(&color, colors->indicator, sizeof(float) * 4); | ||
381 | } else { | ||
382 | memcpy(&color, colors->child_border, sizeof(float) * 4); | ||
383 | } | ||
384 | premultiply_alpha(color, con->alpha); | ||
385 | box.x = state->x; | ||
386 | box.y = state->content_y + state->content_height; | ||
387 | box.width = state->width; | ||
388 | box.height = state->border_thickness; | ||
389 | scale_box(&box, output_scale); | ||
390 | render_rect(output, damage, &box, color); | ||
391 | } | ||
392 | } | ||
393 | |||
394 | /** | ||
395 | * Render a titlebar. | ||
396 | * | ||
397 | * Care must be taken not to render over the same pixel multiple times, | ||
398 | * otherwise the colors will be incorrect when using opacity. | ||
399 | * | ||
400 | * The height is: 1px border, 3px padding, font height, 3px padding, 1px border | ||
401 | * The left side is: 1px border, 2px padding, title | ||
402 | */ | ||
403 | static void render_titlebar(struct sway_output *output, | ||
404 | pixman_region32_t *output_damage, struct sway_container *con, | ||
405 | int x, int y, int width, | ||
406 | struct border_colors *colors, struct wlr_texture *title_texture, | ||
407 | struct wlr_texture *marks_texture) { | ||
408 | struct wlr_box box; | ||
409 | float color[4]; | ||
410 | float output_scale = output->wlr_output->scale; | ||
411 | double output_x = output->lx; | ||
412 | double output_y = output->ly; | ||
413 | int titlebar_border_thickness = config->titlebar_border_thickness; | ||
414 | int titlebar_h_padding = config->titlebar_h_padding; | ||
415 | int titlebar_v_padding = config->titlebar_v_padding; | ||
416 | enum alignment title_align = config->title_align; | ||
417 | |||
418 | // Single pixel bar above title | ||
419 | memcpy(&color, colors->border, sizeof(float) * 4); | ||
420 | premultiply_alpha(color, con->alpha); | ||
421 | box.x = x; | ||
422 | box.y = y; | ||
423 | box.width = width; | ||
424 | box.height = titlebar_border_thickness; | ||
425 | scale_box(&box, output_scale); | ||
426 | render_rect(output, output_damage, &box, color); | ||
427 | |||
428 | // Single pixel bar below title | ||
429 | box.x = x; | ||
430 | box.y = y + container_titlebar_height() - titlebar_border_thickness; | ||
431 | box.width = width; | ||
432 | box.height = titlebar_border_thickness; | ||
433 | scale_box(&box, output_scale); | ||
434 | render_rect(output, output_damage, &box, color); | ||
435 | |||
436 | // Single pixel left edge | ||
437 | box.x = x; | ||
438 | box.y = y + titlebar_border_thickness; | ||
439 | box.width = titlebar_border_thickness; | ||
440 | box.height = container_titlebar_height() - titlebar_border_thickness * 2; | ||
441 | scale_box(&box, output_scale); | ||
442 | render_rect(output, output_damage, &box, color); | ||
443 | |||
444 | // Single pixel right edge | ||
445 | box.x = x + width - titlebar_border_thickness; | ||
446 | box.y = y + titlebar_border_thickness; | ||
447 | box.width = titlebar_border_thickness; | ||
448 | box.height = container_titlebar_height() - titlebar_border_thickness * 2; | ||
449 | scale_box(&box, output_scale); | ||
450 | render_rect(output, output_damage, &box, color); | ||
451 | |||
452 | int inner_x = x - output_x + titlebar_h_padding; | ||
453 | int bg_y = y + titlebar_border_thickness; | ||
454 | size_t inner_width = width - titlebar_h_padding * 2; | ||
455 | |||
456 | // output-buffer local | ||
457 | int ob_inner_x = round(inner_x * output_scale); | ||
458 | int ob_inner_width = scale_length(inner_width, inner_x, output_scale); | ||
459 | int ob_bg_height = scale_length( | ||
460 | (titlebar_v_padding - titlebar_border_thickness) * 2 + | ||
461 | config->font_height, bg_y, output_scale); | ||
462 | |||
463 | // Marks | ||
464 | int ob_marks_x = 0; // output-buffer-local | ||
465 | int ob_marks_width = 0; // output-buffer-local | ||
466 | if (config->show_marks && marks_texture) { | ||
467 | struct wlr_box texture_box; | ||
468 | wlr_texture_get_size(marks_texture, | ||
469 | &texture_box.width, &texture_box.height); | ||
470 | ob_marks_width = texture_box.width; | ||
471 | |||
472 | // The marks texture might be shorter than the config->font_height, in | ||
473 | // which case we need to pad it as evenly as possible above and below. | ||
474 | int ob_padding_total = ob_bg_height - texture_box.height; | ||
475 | int ob_padding_above = floor(ob_padding_total / 2.0); | ||
476 | int ob_padding_below = ceil(ob_padding_total / 2.0); | ||
477 | |||
478 | // Render texture. If the title is on the right, the marks will be on | ||
479 | // the left. Otherwise, they will be on the right. | ||
480 | if (title_align == ALIGN_RIGHT || texture_box.width > ob_inner_width) { | ||
481 | texture_box.x = ob_inner_x; | ||
482 | } else { | ||
483 | texture_box.x = ob_inner_x + ob_inner_width - texture_box.width; | ||
484 | } | ||
485 | ob_marks_x = texture_box.x; | ||
486 | |||
487 | texture_box.y = round((bg_y - output_y) * output_scale) + | ||
488 | ob_padding_above; | ||
489 | |||
490 | float matrix[9]; | ||
491 | wlr_matrix_project_box(matrix, &texture_box, | ||
492 | WL_OUTPUT_TRANSFORM_NORMAL, | ||
493 | 0.0, output->wlr_output->transform_matrix); | ||
494 | |||
495 | if (ob_inner_width < texture_box.width) { | ||
496 | texture_box.width = ob_inner_width; | ||
497 | } | ||
498 | render_texture(output->wlr_output, output_damage, marks_texture, | ||
499 | NULL, &texture_box, matrix, con->alpha); | ||
500 | |||
501 | // Padding above | ||
502 | memcpy(&color, colors->background, sizeof(float) * 4); | ||
503 | premultiply_alpha(color, con->alpha); | ||
504 | box.x = texture_box.x + round(output_x * output_scale); | ||
505 | box.y = round((y + titlebar_border_thickness) * output_scale); | ||
506 | box.width = texture_box.width; | ||
507 | box.height = ob_padding_above; | ||
508 | render_rect(output, output_damage, &box, color); | ||
509 | |||
510 | // Padding below | ||
511 | box.y += ob_padding_above + texture_box.height; | ||
512 | box.height = ob_padding_below; | ||
513 | render_rect(output, output_damage, &box, color); | ||
514 | } | ||
515 | |||
516 | // Title text | ||
517 | int ob_title_x = 0; // output-buffer-local | ||
518 | int ob_title_width = 0; // output-buffer-local | ||
519 | if (title_texture) { | ||
520 | struct wlr_box texture_box; | ||
521 | wlr_texture_get_size(title_texture, | ||
522 | &texture_box.width, &texture_box.height); | ||
523 | ob_title_width = texture_box.width; | ||
524 | |||
525 | // The title texture might be shorter than the config->font_height, | ||
526 | // in which case we need to pad it above and below. | ||
527 | int ob_padding_above = round((config->font_baseline - | ||
528 | con->title_baseline + titlebar_v_padding - | ||
529 | titlebar_border_thickness) * output_scale); | ||
530 | int ob_padding_below = ob_bg_height - ob_padding_above - | ||
531 | texture_box.height; | ||
532 | |||
533 | // Render texture | ||
534 | if (texture_box.width > ob_inner_width - ob_marks_width) { | ||
535 | texture_box.x = (title_align == ALIGN_RIGHT && ob_marks_width) | ||
536 | ? ob_marks_x + ob_marks_width : ob_inner_x; | ||
537 | } else if (title_align == ALIGN_LEFT) { | ||
538 | texture_box.x = ob_inner_x; | ||
539 | } else if (title_align == ALIGN_CENTER) { | ||
540 | // If there are marks visible, center between the edge and marks. | ||
541 | // Otherwise, center in the inner area. | ||
542 | if (ob_marks_width) { | ||
543 | texture_box.x = (ob_inner_x + ob_marks_x) / 2 | ||
544 | - texture_box.width / 2; | ||
545 | } else { | ||
546 | texture_box.x = ob_inner_x + ob_inner_width / 2 | ||
547 | - texture_box.width / 2; | ||
548 | } | ||
549 | } else { | ||
550 | texture_box.x = ob_inner_x + ob_inner_width - texture_box.width; | ||
551 | } | ||
552 | ob_title_x = texture_box.x; | ||
553 | |||
554 | texture_box.y = | ||
555 | round((bg_y - output_y) * output_scale) + ob_padding_above; | ||
556 | |||
557 | float matrix[9]; | ||
558 | wlr_matrix_project_box(matrix, &texture_box, | ||
559 | WL_OUTPUT_TRANSFORM_NORMAL, | ||
560 | 0.0, output->wlr_output->transform_matrix); | ||
561 | |||
562 | if (ob_inner_width - ob_marks_width < texture_box.width) { | ||
563 | texture_box.width = ob_inner_width - ob_marks_width; | ||
564 | } | ||
565 | |||
566 | render_texture(output->wlr_output, output_damage, title_texture, | ||
567 | NULL, &texture_box, matrix, con->alpha); | ||
568 | |||
569 | // Padding above | ||
570 | memcpy(&color, colors->background, sizeof(float) * 4); | ||
571 | premultiply_alpha(color, con->alpha); | ||
572 | box.x = texture_box.x + round(output_x * output_scale); | ||
573 | box.y = round((y + titlebar_border_thickness) * output_scale); | ||
574 | box.width = texture_box.width; | ||
575 | box.height = ob_padding_above; | ||
576 | render_rect(output, output_damage, &box, color); | ||
577 | |||
578 | // Padding below | ||
579 | box.y += ob_padding_above + texture_box.height; | ||
580 | box.height = ob_padding_below; | ||
581 | render_rect(output, output_damage, &box, color); | ||
582 | } | ||
583 | |||
584 | // Determine the left + right extends of the textures (output-buffer local) | ||
585 | int ob_left_x, ob_left_width, ob_right_x, ob_right_width; | ||
586 | if (ob_title_width == 0 && ob_marks_width == 0) { | ||
587 | ob_left_x = ob_inner_x; | ||
588 | ob_left_width = 0; | ||
589 | ob_right_x = ob_inner_x; | ||
590 | ob_right_width = 0; | ||
591 | } else if (ob_title_x < ob_marks_x) { | ||
592 | ob_left_x = ob_title_x; | ||
593 | ob_left_width = ob_title_width; | ||
594 | ob_right_x = ob_marks_x; | ||
595 | ob_right_width = ob_marks_width; | ||
596 | } else { | ||
597 | ob_left_x = ob_marks_x; | ||
598 | ob_left_width = ob_marks_width; | ||
599 | ob_right_x = ob_title_x; | ||
600 | ob_right_width = ob_title_width; | ||
601 | } | ||
602 | if (ob_left_x < ob_inner_x) { | ||
603 | ob_left_x = ob_inner_x; | ||
604 | } else if (ob_left_x + ob_left_width > ob_right_x + ob_right_width) { | ||
605 | ob_right_x = ob_left_x; | ||
606 | ob_right_width = ob_left_width; | ||
607 | } | ||
608 | |||
609 | // Filler between title and marks | ||
610 | box.width = ob_right_x - ob_left_x - ob_left_width; | ||
611 | if (box.width > 0) { | ||
612 | box.x = ob_left_x + ob_left_width + round(output_x * output_scale); | ||
613 | box.y = round(bg_y * output_scale); | ||
614 | box.height = ob_bg_height; | ||
615 | render_rect(output, output_damage, &box, color); | ||
616 | } | ||
617 | |||
618 | // Padding on left side | ||
619 | box.x = x + titlebar_border_thickness; | ||
620 | box.y = y + titlebar_border_thickness; | ||
621 | box.width = titlebar_h_padding - titlebar_border_thickness; | ||
622 | box.height = (titlebar_v_padding - titlebar_border_thickness) * 2 + | ||
623 | config->font_height; | ||
624 | scale_box(&box, output_scale); | ||
625 | int left_x = ob_left_x + round(output_x * output_scale); | ||
626 | if (box.x + box.width < left_x) { | ||
627 | box.width += left_x - box.x - box.width; | ||
628 | } | ||
629 | render_rect(output, output_damage, &box, color); | ||
630 | |||
631 | // Padding on right side | ||
632 | box.x = x + width - titlebar_h_padding; | ||
633 | box.y = y + titlebar_border_thickness; | ||
634 | box.width = titlebar_h_padding - titlebar_border_thickness; | ||
635 | box.height = (titlebar_v_padding - titlebar_border_thickness) * 2 + | ||
636 | config->font_height; | ||
637 | scale_box(&box, output_scale); | ||
638 | int right_rx = ob_right_x + ob_right_width + round(output_x * output_scale); | ||
639 | if (right_rx < box.x) { | ||
640 | box.width += box.x - right_rx; | ||
641 | box.x = right_rx; | ||
642 | } | ||
643 | render_rect(output, output_damage, &box, color); | ||
644 | } | ||
645 | |||
646 | /** | ||
647 | * Render the top border line for a view using "border pixel". | ||
648 | */ | ||
649 | static void render_top_border(struct sway_output *output, | ||
650 | pixman_region32_t *output_damage, struct sway_container *con, | ||
651 | struct border_colors *colors) { | ||
652 | struct sway_container_state *state = &con->current; | ||
653 | if (!state->border_top) { | ||
654 | return; | ||
655 | } | ||
656 | struct wlr_box box; | ||
657 | float color[4]; | ||
658 | float output_scale = output->wlr_output->scale; | ||
659 | |||
660 | // Child border - top edge | ||
661 | memcpy(&color, colors->child_border, sizeof(float) * 4); | ||
662 | premultiply_alpha(color, con->alpha); | ||
663 | box.x = state->x; | ||
664 | box.y = state->y; | ||
665 | box.width = state->width; | ||
666 | box.height = state->border_thickness; | ||
667 | scale_box(&box, output_scale); | ||
668 | render_rect(output, output_damage, &box, color); | ||
669 | } | ||
670 | |||
671 | struct parent_data { | ||
672 | enum sway_container_layout layout; | ||
673 | struct wlr_box box; | ||
674 | list_t *children; | ||
675 | bool focused; | ||
676 | struct sway_container *active_child; | ||
677 | }; | ||
678 | |||
679 | static void render_container(struct sway_output *output, | ||
680 | pixman_region32_t *damage, struct sway_container *con, bool parent_focused); | ||
681 | |||
682 | /** | ||
683 | * Render a container's children using a L_HORIZ or L_VERT layout. | ||
684 | * | ||
685 | * Wrap child views in borders and leave child containers borderless because | ||
686 | * they'll apply their own borders to their children. | ||
687 | */ | ||
688 | static void render_containers_linear(struct sway_output *output, | ||
689 | pixman_region32_t *damage, struct parent_data *parent) { | ||
690 | for (int i = 0; i < parent->children->length; ++i) { | ||
691 | struct sway_container *child = parent->children->items[i]; | ||
692 | |||
693 | if (child->view) { | ||
694 | struct sway_view *view = child->view; | ||
695 | struct border_colors *colors; | ||
696 | struct wlr_texture *title_texture; | ||
697 | struct wlr_texture *marks_texture; | ||
698 | struct sway_container_state *state = &child->current; | ||
699 | |||
700 | if (view_is_urgent(view)) { | ||
701 | colors = &config->border_colors.urgent; | ||
702 | title_texture = child->title_urgent; | ||
703 | marks_texture = child->marks_urgent; | ||
704 | } else if (state->focused || parent->focused) { | ||
705 | colors = &config->border_colors.focused; | ||
706 | title_texture = child->title_focused; | ||
707 | marks_texture = child->marks_focused; | ||
708 | } else if (child == parent->active_child) { | ||
709 | colors = &config->border_colors.focused_inactive; | ||
710 | title_texture = child->title_focused_inactive; | ||
711 | marks_texture = child->marks_focused_inactive; | ||
712 | } else { | ||
713 | colors = &config->border_colors.unfocused; | ||
714 | title_texture = child->title_unfocused; | ||
715 | marks_texture = child->marks_unfocused; | ||
716 | } | ||
717 | |||
718 | if (state->border == B_NORMAL) { | ||
719 | render_titlebar(output, damage, child, state->x, | ||
720 | state->y, state->width, colors, | ||
721 | title_texture, marks_texture); | ||
722 | } else if (state->border == B_PIXEL) { | ||
723 | render_top_border(output, damage, child, colors); | ||
724 | } | ||
725 | render_view(output, damage, child, colors); | ||
726 | } else { | ||
727 | render_container(output, damage, child, | ||
728 | parent->focused || child->current.focused); | ||
729 | } | ||
730 | } | ||
731 | } | ||
732 | |||
733 | /** | ||
734 | * Render a container's children using the L_TABBED layout. | ||
735 | */ | ||
736 | static void render_containers_tabbed(struct sway_output *output, | ||
737 | pixman_region32_t *damage, struct parent_data *parent) { | ||
738 | if (!parent->children->length) { | ||
739 | return; | ||
740 | } | ||
741 | struct sway_container *current = parent->active_child; | ||
742 | struct border_colors *current_colors = &config->border_colors.unfocused; | ||
743 | int tab_width = parent->box.width / parent->children->length; | ||
744 | |||
745 | // Render tabs | ||
746 | for (int i = 0; i < parent->children->length; ++i) { | ||
747 | struct sway_container *child = parent->children->items[i]; | ||
748 | struct sway_view *view = child->view; | ||
749 | struct sway_container_state *cstate = &child->current; | ||
750 | struct border_colors *colors; | ||
751 | struct wlr_texture *title_texture; | ||
752 | struct wlr_texture *marks_texture; | ||
753 | bool urgent = view ? | ||
754 | view_is_urgent(view) : container_has_urgent_child(child); | ||
755 | |||
756 | if (urgent) { | ||
757 | colors = &config->border_colors.urgent; | ||
758 | title_texture = child->title_urgent; | ||
759 | marks_texture = child->marks_urgent; | ||
760 | } else if (cstate->focused || parent->focused) { | ||
761 | colors = &config->border_colors.focused; | ||
762 | title_texture = child->title_focused; | ||
763 | marks_texture = child->marks_focused; | ||
764 | } else if (child == parent->active_child) { | ||
765 | colors = &config->border_colors.focused_inactive; | ||
766 | title_texture = child->title_focused_inactive; | ||
767 | marks_texture = child->marks_focused_inactive; | ||
768 | } else { | ||
769 | colors = &config->border_colors.unfocused; | ||
770 | title_texture = child->title_unfocused; | ||
771 | marks_texture = child->marks_unfocused; | ||
772 | } | ||
773 | |||
774 | int x = cstate->x + tab_width * i; | ||
775 | |||
776 | // Make last tab use the remaining width of the parent | ||
777 | if (i == parent->children->length - 1) { | ||
778 | tab_width = parent->box.width - tab_width * i; | ||
779 | } | ||
780 | |||
781 | render_titlebar(output, damage, child, x, parent->box.y, tab_width, | ||
782 | colors, title_texture, marks_texture); | ||
783 | |||
784 | if (child == current) { | ||
785 | current_colors = colors; | ||
786 | } | ||
787 | } | ||
788 | |||
789 | // Render surface and left/right/bottom borders | ||
790 | if (current->view) { | ||
791 | render_view(output, damage, current, current_colors); | ||
792 | } else { | ||
793 | render_container(output, damage, current, | ||
794 | parent->focused || current->current.focused); | ||
795 | } | ||
796 | } | ||
797 | |||
798 | /** | ||
799 | * Render a container's children using the L_STACKED layout. | ||
800 | */ | ||
801 | static void render_containers_stacked(struct sway_output *output, | ||
802 | pixman_region32_t *damage, struct parent_data *parent) { | ||
803 | if (!parent->children->length) { | ||
804 | return; | ||
805 | } | ||
806 | struct sway_container *current = parent->active_child; | ||
807 | struct border_colors *current_colors = &config->border_colors.unfocused; | ||
808 | size_t titlebar_height = container_titlebar_height(); | ||
809 | |||
810 | // Render titles | ||
811 | for (int i = 0; i < parent->children->length; ++i) { | ||
812 | struct sway_container *child = parent->children->items[i]; | ||
813 | struct sway_view *view = child->view; | ||
814 | struct sway_container_state *cstate = &child->current; | ||
815 | struct border_colors *colors; | ||
816 | struct wlr_texture *title_texture; | ||
817 | struct wlr_texture *marks_texture; | ||
818 | bool urgent = view ? | ||
819 | view_is_urgent(view) : container_has_urgent_child(child); | ||
820 | |||
821 | if (urgent) { | ||
822 | colors = &config->border_colors.urgent; | ||
823 | title_texture = child->title_urgent; | ||
824 | marks_texture = child->marks_urgent; | ||
825 | } else if (cstate->focused || parent->focused) { | ||
826 | colors = &config->border_colors.focused; | ||
827 | title_texture = child->title_focused; | ||
828 | marks_texture = child->marks_focused; | ||
829 | } else if (child == parent->active_child) { | ||
830 | colors = &config->border_colors.focused_inactive; | ||
831 | title_texture = child->title_focused_inactive; | ||
832 | marks_texture = child->marks_focused_inactive; | ||
833 | } else { | ||
834 | colors = &config->border_colors.unfocused; | ||
835 | title_texture = child->title_unfocused; | ||
836 | marks_texture = child->marks_unfocused; | ||
837 | } | ||
838 | |||
839 | int y = parent->box.y + titlebar_height * i; | ||
840 | render_titlebar(output, damage, child, parent->box.x, y, | ||
841 | parent->box.width, colors, title_texture, marks_texture); | ||
842 | |||
843 | if (child == current) { | ||
844 | current_colors = colors; | ||
845 | } | ||
846 | } | ||
847 | |||
848 | // Render surface and left/right/bottom borders | ||
849 | if (current->view) { | ||
850 | render_view(output, damage, current, current_colors); | ||
851 | } else { | ||
852 | render_container(output, damage, current, | ||
853 | parent->focused || current->current.focused); | ||
854 | } | ||
855 | } | ||
856 | |||
857 | static void render_containers(struct sway_output *output, | ||
858 | pixman_region32_t *damage, struct parent_data *parent) { | ||
859 | if (config->hide_lone_tab && parent->children->length == 1) { | ||
860 | struct sway_container *child = parent->children->items[0]; | ||
861 | if (child->view) { | ||
862 | render_containers_linear(output,damage, parent); | ||
863 | return; | ||
864 | } | ||
865 | } | ||
866 | |||
867 | switch (parent->layout) { | ||
868 | case L_NONE: | ||
869 | case L_HORIZ: | ||
870 | case L_VERT: | ||
871 | render_containers_linear(output, damage, parent); | ||
872 | break; | ||
873 | case L_STACKED: | ||
874 | render_containers_stacked(output, damage, parent); | ||
875 | break; | ||
876 | case L_TABBED: | ||
877 | render_containers_tabbed(output, damage, parent); | ||
878 | break; | ||
879 | } | ||
880 | } | ||
881 | |||
882 | static void render_container(struct sway_output *output, | ||
883 | pixman_region32_t *damage, struct sway_container *con, bool focused) { | ||
884 | struct parent_data data = { | ||
885 | .layout = con->current.layout, | ||
886 | .box = { | ||
887 | .x = con->current.x, | ||
888 | .y = con->current.y, | ||
889 | .width = con->current.width, | ||
890 | .height = con->current.height, | ||
891 | }, | ||
892 | .children = con->current.children, | ||
893 | .focused = focused, | ||
894 | .active_child = con->current.focused_inactive_child, | ||
895 | }; | ||
896 | render_containers(output, damage, &data); | ||
897 | } | ||
898 | |||
899 | static void render_workspace(struct sway_output *output, | ||
900 | pixman_region32_t *damage, struct sway_workspace *ws, bool focused) { | ||
901 | struct parent_data data = { | ||
902 | .layout = ws->current.layout, | ||
903 | .box = { | ||
904 | .x = ws->current.x, | ||
905 | .y = ws->current.y, | ||
906 | .width = ws->current.width, | ||
907 | .height = ws->current.height, | ||
908 | }, | ||
909 | .children = ws->current.tiling, | ||
910 | .focused = focused, | ||
911 | .active_child = ws->current.focused_inactive_child, | ||
912 | }; | ||
913 | render_containers(output, damage, &data); | ||
914 | } | ||
915 | |||
916 | static void render_floating_container(struct sway_output *soutput, | ||
917 | pixman_region32_t *damage, struct sway_container *con) { | ||
918 | if (con->view) { | ||
919 | struct sway_view *view = con->view; | ||
920 | struct border_colors *colors; | ||
921 | struct wlr_texture *title_texture; | ||
922 | struct wlr_texture *marks_texture; | ||
923 | |||
924 | if (view_is_urgent(view)) { | ||
925 | colors = &config->border_colors.urgent; | ||
926 | title_texture = con->title_urgent; | ||
927 | marks_texture = con->marks_urgent; | ||
928 | } else if (con->current.focused) { | ||
929 | colors = &config->border_colors.focused; | ||
930 | title_texture = con->title_focused; | ||
931 | marks_texture = con->marks_focused; | ||
932 | } else { | ||
933 | colors = &config->border_colors.unfocused; | ||
934 | title_texture = con->title_unfocused; | ||
935 | marks_texture = con->marks_unfocused; | ||
936 | } | ||
937 | |||
938 | if (con->current.border == B_NORMAL) { | ||
939 | render_titlebar(soutput, damage, con, con->current.x, | ||
940 | con->current.y, con->current.width, colors, | ||
941 | title_texture, marks_texture); | ||
942 | } else if (con->current.border == B_PIXEL) { | ||
943 | render_top_border(soutput, damage, con, colors); | ||
944 | } | ||
945 | render_view(soutput, damage, con, colors); | ||
946 | } else { | ||
947 | render_container(soutput, damage, con, con->current.focused); | ||
948 | } | ||
949 | } | ||
950 | |||
951 | static void render_floating(struct sway_output *soutput, | ||
952 | pixman_region32_t *damage) { | ||
953 | for (int i = 0; i < root->outputs->length; ++i) { | ||
954 | struct sway_output *output = root->outputs->items[i]; | ||
955 | for (int j = 0; j < output->current.workspaces->length; ++j) { | ||
956 | struct sway_workspace *ws = output->current.workspaces->items[j]; | ||
957 | if (!workspace_is_visible(ws)) { | ||
958 | continue; | ||
959 | } | ||
960 | for (int k = 0; k < ws->current.floating->length; ++k) { | ||
961 | struct sway_container *floater = ws->current.floating->items[k]; | ||
962 | if (floater->fullscreen_mode != FULLSCREEN_NONE) { | ||
963 | continue; | ||
964 | } | ||
965 | render_floating_container(soutput, damage, floater); | ||
966 | } | ||
967 | } | ||
968 | } | ||
969 | } | ||
970 | |||
971 | static void render_seatops(struct sway_output *output, | ||
972 | pixman_region32_t *damage) { | ||
973 | struct sway_seat *seat; | ||
974 | wl_list_for_each(seat, &server.input->seats, link) { | ||
975 | seatop_render(seat, output, damage); | ||
976 | } | ||
977 | } | ||
978 | |||
979 | void output_render(struct sway_output *output, struct timespec *when, | ||
980 | pixman_region32_t *damage) { | ||
981 | struct wlr_output *wlr_output = output->wlr_output; | ||
982 | |||
983 | struct wlr_renderer *renderer = | ||
984 | wlr_backend_get_renderer(wlr_output->backend); | ||
985 | if (!sway_assert(renderer != NULL, | ||
986 | "expected the output backend to have a renderer")) { | ||
987 | return; | ||
988 | } | ||
989 | |||
990 | struct sway_workspace *workspace = output->current.active_workspace; | ||
991 | if (workspace == NULL) { | ||
992 | return; | ||
993 | } | ||
994 | |||
995 | struct sway_container *fullscreen_con = root->fullscreen_global; | ||
996 | if (!fullscreen_con) { | ||
997 | fullscreen_con = workspace->current.fullscreen; | ||
998 | } | ||
999 | |||
1000 | wlr_renderer_begin(renderer, wlr_output->width, wlr_output->height); | ||
1001 | |||
1002 | if (!pixman_region32_not_empty(damage)) { | ||
1003 | // Output isn't damaged but needs buffer swap | ||
1004 | goto renderer_end; | ||
1005 | } | ||
1006 | |||
1007 | if (debug.damage == DAMAGE_HIGHLIGHT) { | ||
1008 | wlr_renderer_clear(renderer, (float[]){1, 1, 0, 1}); | ||
1009 | } else if (debug.damage == DAMAGE_RERENDER) { | ||
1010 | int width, height; | ||
1011 | wlr_output_transformed_resolution(wlr_output, &width, &height); | ||
1012 | pixman_region32_union_rect(damage, damage, 0, 0, width, height); | ||
1013 | } | ||
1014 | |||
1015 | if (output_has_opaque_overlay_layer_surface(output)) { | ||
1016 | goto render_overlay; | ||
1017 | } | ||
1018 | |||
1019 | if (fullscreen_con) { | ||
1020 | float clear_color[] = {0.0f, 0.0f, 0.0f, 1.0f}; | ||
1021 | |||
1022 | int nrects; | ||
1023 | pixman_box32_t *rects = pixman_region32_rectangles(damage, &nrects); | ||
1024 | for (int i = 0; i < nrects; ++i) { | ||
1025 | scissor_output(wlr_output, &rects[i]); | ||
1026 | wlr_renderer_clear(renderer, clear_color); | ||
1027 | } | ||
1028 | |||
1029 | if (fullscreen_con->view) { | ||
1030 | if (!wl_list_empty(&fullscreen_con->view->saved_buffers)) { | ||
1031 | render_saved_view(fullscreen_con->view, output, damage, 1.0f); | ||
1032 | } else if (fullscreen_con->view->surface) { | ||
1033 | render_view_toplevels(fullscreen_con->view, | ||
1034 | output, damage, 1.0f); | ||
1035 | } | ||
1036 | } else { | ||
1037 | render_container(output, damage, fullscreen_con, | ||
1038 | fullscreen_con->current.focused); | ||
1039 | } | ||
1040 | |||
1041 | for (int i = 0; i < workspace->current.floating->length; ++i) { | ||
1042 | struct sway_container *floater = | ||
1043 | workspace->current.floating->items[i]; | ||
1044 | if (container_is_transient_for(floater, fullscreen_con)) { | ||
1045 | render_floating_container(output, damage, floater); | ||
1046 | } | ||
1047 | } | ||
1048 | #if HAVE_XWAYLAND | ||
1049 | render_unmanaged(output, damage, &root->xwayland_unmanaged); | ||
1050 | #endif | ||
1051 | } else { | ||
1052 | float clear_color[] = {0.25f, 0.25f, 0.25f, 1.0f}; | ||
1053 | |||
1054 | int nrects; | ||
1055 | pixman_box32_t *rects = pixman_region32_rectangles(damage, &nrects); | ||
1056 | for (int i = 0; i < nrects; ++i) { | ||
1057 | scissor_output(wlr_output, &rects[i]); | ||
1058 | wlr_renderer_clear(renderer, clear_color); | ||
1059 | } | ||
1060 | |||
1061 | render_layer_toplevel(output, damage, | ||
1062 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND]); | ||
1063 | render_layer_toplevel(output, damage, | ||
1064 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM]); | ||
1065 | |||
1066 | render_workspace(output, damage, workspace, workspace->current.focused); | ||
1067 | render_floating(output, damage); | ||
1068 | #if HAVE_XWAYLAND | ||
1069 | render_unmanaged(output, damage, &root->xwayland_unmanaged); | ||
1070 | #endif | ||
1071 | render_layer_toplevel(output, damage, | ||
1072 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]); | ||
1073 | |||
1074 | render_layer_popups(output, damage, | ||
1075 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND]); | ||
1076 | render_layer_popups(output, damage, | ||
1077 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM]); | ||
1078 | render_layer_popups(output, damage, | ||
1079 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]); | ||
1080 | } | ||
1081 | |||
1082 | render_seatops(output, damage); | ||
1083 | |||
1084 | struct sway_seat *seat = input_manager_current_seat(); | ||
1085 | struct sway_container *focus = seat_get_focused_container(seat); | ||
1086 | if (focus && focus->view) { | ||
1087 | render_view_popups(focus->view, output, damage, focus->alpha); | ||
1088 | } | ||
1089 | |||
1090 | render_overlay: | ||
1091 | render_layer_toplevel(output, damage, | ||
1092 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]); | ||
1093 | render_layer_popups(output, damage, | ||
1094 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]); | ||
1095 | render_drag_icons(output, damage, &root->drag_icons); | ||
1096 | |||
1097 | renderer_end: | ||
1098 | wlr_renderer_scissor(renderer, NULL); | ||
1099 | wlr_output_render_software_cursors(wlr_output, damage); | ||
1100 | wlr_renderer_end(renderer); | ||
1101 | |||
1102 | int width, height; | ||
1103 | wlr_output_transformed_resolution(wlr_output, &width, &height); | ||
1104 | |||
1105 | pixman_region32_t frame_damage; | ||
1106 | pixman_region32_init(&frame_damage); | ||
1107 | |||
1108 | enum wl_output_transform transform = | ||
1109 | wlr_output_transform_invert(wlr_output->transform); | ||
1110 | wlr_region_transform(&frame_damage, &output->damage->current, | ||
1111 | transform, width, height); | ||
1112 | |||
1113 | if (debug.damage == DAMAGE_HIGHLIGHT) { | ||
1114 | pixman_region32_union_rect(&frame_damage, &frame_damage, | ||
1115 | 0, 0, wlr_output->width, wlr_output->height); | ||
1116 | } | ||
1117 | |||
1118 | wlr_output_set_damage(wlr_output, &frame_damage); | ||
1119 | pixman_region32_fini(&frame_damage); | ||
1120 | |||
1121 | if (!wlr_output_commit(wlr_output)) { | ||
1122 | return; | ||
1123 | } | ||
1124 | output->last_frame = *when; | ||
1125 | } | ||
diff --git a/sway/desktop/surface.c b/sway/desktop/surface.c deleted file mode 100644 index 767b2045..00000000 --- a/sway/desktop/surface.c +++ /dev/null | |||
@@ -1,46 +0,0 @@ | |||
1 | #define _POSIX_C_SOURCE 200112L | ||
2 | #include <stdlib.h> | ||
3 | #include <time.h> | ||
4 | #include <wlr/types/wlr_surface.h> | ||
5 | #include "sway/server.h" | ||
6 | #include "sway/surface.h" | ||
7 | |||
8 | static void handle_destroy(struct wl_listener *listener, void *data) { | ||
9 | struct sway_surface *surface = wl_container_of(listener, surface, destroy); | ||
10 | |||
11 | surface->wlr_surface->data = NULL; | ||
12 | wl_list_remove(&surface->destroy.link); | ||
13 | |||
14 | if (surface->frame_done_timer) { | ||
15 | wl_event_source_remove(surface->frame_done_timer); | ||
16 | } | ||
17 | |||
18 | free(surface); | ||
19 | } | ||
20 | |||
21 | static int surface_frame_done_timer_handler(void *data) { | ||
22 | struct sway_surface *surface = data; | ||
23 | |||
24 | struct timespec now; | ||
25 | clock_gettime(CLOCK_MONOTONIC, &now); | ||
26 | wlr_surface_send_frame_done(surface->wlr_surface, &now); | ||
27 | |||
28 | return 0; | ||
29 | } | ||
30 | |||
31 | void handle_compositor_new_surface(struct wl_listener *listener, void *data) { | ||
32 | struct wlr_surface *wlr_surface = data; | ||
33 | |||
34 | struct sway_surface *surface = calloc(1, sizeof(struct sway_surface)); | ||
35 | surface->wlr_surface = wlr_surface; | ||
36 | wlr_surface->data = surface; | ||
37 | |||
38 | surface->destroy.notify = handle_destroy; | ||
39 | wl_signal_add(&wlr_surface->events.destroy, &surface->destroy); | ||
40 | |||
41 | surface->frame_done_timer = wl_event_loop_add_timer(server.wl_event_loop, | ||
42 | surface_frame_done_timer_handler, surface); | ||
43 | if (!surface->frame_done_timer) { | ||
44 | wl_resource_post_no_memory(wlr_surface->resource); | ||
45 | } | ||
46 | } | ||
diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index eac38991..e464ff1a 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c | |||
@@ -1,11 +1,10 @@ | |||
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> |
5 | #include <time.h> | 4 | #include <time.h> |
6 | #include <wlr/types/wlr_buffer.h> | 5 | #include <wlr/types/wlr_buffer.h> |
7 | #include "sway/config.h" | 6 | #include "sway/config.h" |
8 | #include "sway/desktop.h" | 7 | #include "sway/scene_descriptor.h" |
9 | #include "sway/desktop/idle_inhibit_v1.h" | 8 | #include "sway/desktop/idle_inhibit_v1.h" |
10 | #include "sway/desktop/transaction.h" | 9 | #include "sway/desktop/transaction.h" |
11 | #include "sway/input/cursor.h" | 10 | #include "sway/input/cursor.h" |
@@ -35,6 +34,8 @@ struct sway_transaction_instruction { | |||
35 | struct sway_container_state container_state; | 34 | struct sway_container_state container_state; |
36 | }; | 35 | }; |
37 | uint32_t serial; | 36 | uint32_t serial; |
37 | bool server_request; | ||
38 | bool waiting; | ||
38 | }; | 39 | }; |
39 | 40 | ||
40 | static struct sway_transaction *transaction_create(void) { | 41 | static struct sway_transaction *transaction_create(void) { |
@@ -86,7 +87,11 @@ static void transaction_destroy(struct sway_transaction *transaction) { | |||
86 | static void copy_output_state(struct sway_output *output, | 87 | static void copy_output_state(struct sway_output *output, |
87 | struct sway_transaction_instruction *instruction) { | 88 | struct sway_transaction_instruction *instruction) { |
88 | struct sway_output_state *state = &instruction->output_state; | 89 | struct sway_output_state *state = &instruction->output_state; |
89 | state->workspaces = create_list(); | 90 | if (state->workspaces) { |
91 | state->workspaces->length = 0; | ||
92 | } else { | ||
93 | state->workspaces = create_list(); | ||
94 | } | ||
90 | list_cat(state->workspaces, output->workspaces); | 95 | list_cat(state->workspaces, output->workspaces); |
91 | 96 | ||
92 | state->active_workspace = output_get_active_workspace(output); | 97 | state->active_workspace = output_get_active_workspace(output); |
@@ -104,8 +109,16 @@ static void copy_workspace_state(struct sway_workspace *ws, | |||
104 | state->layout = ws->layout; | 109 | state->layout = ws->layout; |
105 | 110 | ||
106 | state->output = ws->output; | 111 | state->output = ws->output; |
107 | state->floating = create_list(); | 112 | if (state->floating) { |
108 | state->tiling = create_list(); | 113 | state->floating->length = 0; |
114 | } else { | ||
115 | state->floating = create_list(); | ||
116 | } | ||
117 | if (state->tiling) { | ||
118 | state->tiling->length = 0; | ||
119 | } else { | ||
120 | state->tiling = create_list(); | ||
121 | } | ||
109 | list_cat(state->floating, ws->floating); | 122 | list_cat(state->floating, ws->floating); |
110 | list_cat(state->tiling, ws->tiling); | 123 | list_cat(state->tiling, ws->tiling); |
111 | 124 | ||
@@ -115,8 +128,8 @@ static void copy_workspace_state(struct sway_workspace *ws, | |||
115 | // Set focused_inactive_child to the direct tiling child | 128 | // Set focused_inactive_child to the direct tiling child |
116 | struct sway_container *focus = seat_get_focus_inactive_tiling(seat, ws); | 129 | struct sway_container *focus = seat_get_focus_inactive_tiling(seat, ws); |
117 | if (focus) { | 130 | if (focus) { |
118 | while (focus->parent) { | 131 | while (focus->pending.parent) { |
119 | focus = focus->parent; | 132 | focus = focus->pending.parent; |
120 | } | 133 | } |
121 | } | 134 | } |
122 | state->focused_inactive_child = focus; | 135 | state->focused_inactive_child = focus; |
@@ -126,28 +139,19 @@ static void copy_container_state(struct sway_container *container, | |||
126 | struct sway_transaction_instruction *instruction) { | 139 | struct sway_transaction_instruction *instruction) { |
127 | struct sway_container_state *state = &instruction->container_state; | 140 | struct sway_container_state *state = &instruction->container_state; |
128 | 141 | ||
129 | state->layout = container->layout; | 142 | if (state->children) { |
130 | state->x = container->x; | 143 | list_free(state->children); |
131 | state->y = container->y; | 144 | } |
132 | state->width = container->width; | 145 | |
133 | state->height = container->height; | 146 | memcpy(state, &container->pending, sizeof(struct sway_container_state)); |
134 | state->fullscreen_mode = container->fullscreen_mode; | ||
135 | state->parent = container->parent; | ||
136 | state->workspace = container->workspace; | ||
137 | state->border = container->border; | ||
138 | state->border_thickness = container->border_thickness; | ||
139 | state->border_top = container->border_top; | ||
140 | state->border_left = container->border_left; | ||
141 | state->border_right = container->border_right; | ||
142 | state->border_bottom = container->border_bottom; | ||
143 | state->content_x = container->content_x; | ||
144 | state->content_y = container->content_y; | ||
145 | state->content_width = container->content_width; | ||
146 | state->content_height = container->content_height; | ||
147 | 147 | ||
148 | if (!container->view) { | 148 | if (!container->view) { |
149 | // We store a copy of the child list to avoid having it mutated after | ||
150 | // we copy the state. | ||
149 | state->children = create_list(); | 151 | state->children = create_list(); |
150 | list_cat(state->children, container->children); | 152 | list_cat(state->children, container->pending.children); |
153 | } else { | ||
154 | state->children = NULL; | ||
151 | } | 155 | } |
152 | 156 | ||
153 | struct sway_seat *seat = input_manager_current_seat(); | 157 | struct sway_seat *seat = input_manager_current_seat(); |
@@ -161,14 +165,36 @@ static void copy_container_state(struct sway_container *container, | |||
161 | } | 165 | } |
162 | 166 | ||
163 | static void transaction_add_node(struct sway_transaction *transaction, | 167 | static void transaction_add_node(struct sway_transaction *transaction, |
164 | struct sway_node *node) { | 168 | struct sway_node *node, bool server_request) { |
165 | struct sway_transaction_instruction *instruction = | 169 | struct sway_transaction_instruction *instruction = NULL; |
166 | calloc(1, sizeof(struct sway_transaction_instruction)); | 170 | |
167 | if (!sway_assert(instruction, "Unable to allocate instruction")) { | 171 | // Check if we have an instruction for this node already, in which case we |
168 | return; | 172 | // update that instead of creating a new one. |
173 | if (node->ntxnrefs > 0) { | ||
174 | for (int idx = 0; idx < transaction->instructions->length; idx++) { | ||
175 | struct sway_transaction_instruction *other = | ||
176 | transaction->instructions->items[idx]; | ||
177 | if (other->node == node) { | ||
178 | instruction = other; | ||
179 | break; | ||
180 | } | ||
181 | } | ||
182 | } | ||
183 | |||
184 | if (!instruction) { | ||
185 | instruction = calloc(1, sizeof(struct sway_transaction_instruction)); | ||
186 | if (!sway_assert(instruction, "Unable to allocate instruction")) { | ||
187 | return; | ||
188 | } | ||
189 | instruction->transaction = transaction; | ||
190 | instruction->node = node; | ||
191 | instruction->server_request = server_request; | ||
192 | |||
193 | list_add(transaction->instructions, instruction); | ||
194 | node->ntxnrefs++; | ||
195 | } else if (server_request) { | ||
196 | instruction->server_request = true; | ||
169 | } | 197 | } |
170 | instruction->transaction = transaction; | ||
171 | instruction->node = node; | ||
172 | 198 | ||
173 | switch (node->type) { | 199 | switch (node->type) { |
174 | case N_ROOT: | 200 | case N_ROOT: |
@@ -183,46 +209,24 @@ static void transaction_add_node(struct sway_transaction *transaction, | |||
183 | copy_container_state(node->sway_container, instruction); | 209 | copy_container_state(node->sway_container, instruction); |
184 | break; | 210 | break; |
185 | } | 211 | } |
186 | |||
187 | list_add(transaction->instructions, instruction); | ||
188 | node->ntxnrefs++; | ||
189 | } | 212 | } |
190 | 213 | ||
191 | static void apply_output_state(struct sway_output *output, | 214 | static void apply_output_state(struct sway_output *output, |
192 | struct sway_output_state *state) { | 215 | struct sway_output_state *state) { |
193 | output_damage_whole(output); | ||
194 | list_free(output->current.workspaces); | 216 | list_free(output->current.workspaces); |
195 | memcpy(&output->current, state, sizeof(struct sway_output_state)); | 217 | memcpy(&output->current, state, sizeof(struct sway_output_state)); |
196 | output_damage_whole(output); | ||
197 | } | 218 | } |
198 | 219 | ||
199 | static void apply_workspace_state(struct sway_workspace *ws, | 220 | static void apply_workspace_state(struct sway_workspace *ws, |
200 | struct sway_workspace_state *state) { | 221 | struct sway_workspace_state *state) { |
201 | output_damage_whole(ws->current.output); | ||
202 | list_free(ws->current.floating); | 222 | list_free(ws->current.floating); |
203 | list_free(ws->current.tiling); | 223 | list_free(ws->current.tiling); |
204 | memcpy(&ws->current, state, sizeof(struct sway_workspace_state)); | 224 | memcpy(&ws->current, state, sizeof(struct sway_workspace_state)); |
205 | output_damage_whole(ws->current.output); | ||
206 | } | 225 | } |
207 | 226 | ||
208 | static void apply_container_state(struct sway_container *container, | 227 | static void apply_container_state(struct sway_container *container, |
209 | struct sway_container_state *state) { | 228 | struct sway_container_state *state) { |
210 | struct sway_view *view = container->view; | 229 | struct sway_view *view = container->view; |
211 | // Damage the old location | ||
212 | desktop_damage_whole_container(container); | ||
213 | if (view && !wl_list_empty(&view->saved_buffers)) { | ||
214 | struct sway_saved_buffer *saved_buf; | ||
215 | wl_list_for_each(saved_buf, &view->saved_buffers, link) { | ||
216 | struct wlr_box box = { | ||
217 | .x = container->current.content_x - view->saved_geometry.x + saved_buf->x, | ||
218 | .y = container->current.content_y - view->saved_geometry.y + saved_buf->y, | ||
219 | .width = saved_buf->width, | ||
220 | .height = saved_buf->height, | ||
221 | }; | ||
222 | desktop_damage_box(&box); | ||
223 | } | ||
224 | } | ||
225 | |||
226 | // There are separate children lists for each instruction state, the | 230 | // There are separate children lists for each instruction state, the |
227 | // container's current state and the container's pending state | 231 | // container's current state and the container's pending state |
228 | // (ie. con->children). The list itself needs to be freed here. | 232 | // (ie. con->children). The list itself needs to be freed here. |
@@ -232,48 +236,445 @@ static void apply_container_state(struct sway_container *container, | |||
232 | 236 | ||
233 | memcpy(&container->current, state, sizeof(struct sway_container_state)); | 237 | memcpy(&container->current, state, sizeof(struct sway_container_state)); |
234 | 238 | ||
235 | if (view && !wl_list_empty(&view->saved_buffers)) { | 239 | if (view) { |
236 | if (!container->node.destroying || container->node.ntxnrefs == 1) { | 240 | if (view->saved_surface_tree) { |
237 | view_remove_saved_buffer(view); | 241 | if (!container->node.destroying || container->node.ntxnrefs == 1) { |
242 | view_remove_saved_buffer(view); | ||
243 | } | ||
244 | } | ||
245 | |||
246 | // If the view hasn't responded to the configure, center it within | ||
247 | // the container. This is important for fullscreen views which | ||
248 | // refuse to resize to the size of the output. | ||
249 | if (view->surface) { | ||
250 | view_center_and_clip_surface(view); | ||
251 | } | ||
252 | } | ||
253 | } | ||
254 | |||
255 | static void arrange_title_bar(struct sway_container *con, | ||
256 | int x, int y, int width, int height) { | ||
257 | container_update(con); | ||
258 | |||
259 | bool has_title_bar = height > 0; | ||
260 | wlr_scene_node_set_enabled(&con->title_bar.tree->node, has_title_bar); | ||
261 | if (!has_title_bar) { | ||
262 | return; | ||
263 | } | ||
264 | |||
265 | wlr_scene_node_set_position(&con->title_bar.tree->node, x, y); | ||
266 | |||
267 | con->title_width = width; | ||
268 | container_arrange_title_bar(con); | ||
269 | } | ||
270 | |||
271 | static void disable_container(struct sway_container *con) { | ||
272 | if (con->view) { | ||
273 | wlr_scene_node_reparent(&con->view->scene_tree->node, con->content_tree); | ||
274 | } else { | ||
275 | for (int i = 0; i < con->current.children->length; i++) { | ||
276 | struct sway_container *child = con->current.children->items[i]; | ||
277 | |||
278 | wlr_scene_node_reparent(&child->scene_tree->node, con->content_tree); | ||
279 | |||
280 | disable_container(child); | ||
281 | } | ||
282 | } | ||
283 | } | ||
284 | |||
285 | static void arrange_container(struct sway_container *con, | ||
286 | int width, int height, bool title_bar, int gaps); | ||
287 | |||
288 | static void arrange_children(enum sway_container_layout layout, list_t *children, | ||
289 | struct sway_container *active, struct wlr_scene_tree *content, | ||
290 | int width, int height, int gaps) { | ||
291 | int title_bar_height = container_titlebar_height(); | ||
292 | |||
293 | if (layout == L_TABBED) { | ||
294 | struct sway_container *first = children->length == 1 ? | ||
295 | ((struct sway_container *)children->items[0]) : NULL; | ||
296 | if (config->hide_lone_tab && first && first->view && | ||
297 | first->current.border != B_NORMAL) { | ||
298 | title_bar_height = 0; | ||
299 | } | ||
300 | |||
301 | double w = (double) width / children->length; | ||
302 | int title_offset = 0; | ||
303 | for (int i = 0; i < children->length; i++) { | ||
304 | struct sway_container *child = children->items[i]; | ||
305 | bool activated = child == active; | ||
306 | int next_title_offset = round(w * i + w); | ||
307 | |||
308 | arrange_title_bar(child, title_offset, -title_bar_height, | ||
309 | next_title_offset - title_offset, title_bar_height); | ||
310 | wlr_scene_node_set_enabled(&child->border.tree->node, activated); | ||
311 | wlr_scene_node_set_position(&child->scene_tree->node, 0, title_bar_height); | ||
312 | wlr_scene_node_reparent(&child->scene_tree->node, content); | ||
313 | |||
314 | if (activated) { | ||
315 | arrange_container(child, width, height - title_bar_height, | ||
316 | false, 0); | ||
317 | } else { | ||
318 | disable_container(child); | ||
319 | } | ||
320 | |||
321 | title_offset = next_title_offset; | ||
238 | } | 322 | } |
323 | } else if (layout == L_STACKED) { | ||
324 | struct sway_container *first = children->length == 1 ? | ||
325 | ((struct sway_container *)children->items[0]) : NULL; | ||
326 | if (config->hide_lone_tab && first && first->view && | ||
327 | first->current.border != B_NORMAL) { | ||
328 | title_bar_height = 0; | ||
329 | } | ||
330 | |||
331 | int title_height = title_bar_height * children->length; | ||
332 | |||
333 | int y = 0; | ||
334 | for (int i = 0; i < children->length; i++) { | ||
335 | struct sway_container *child = children->items[i]; | ||
336 | bool activated = child == active; | ||
337 | |||
338 | arrange_title_bar(child, 0, y - title_height, width, title_bar_height); | ||
339 | wlr_scene_node_set_enabled(&child->border.tree->node, activated); | ||
340 | wlr_scene_node_set_position(&child->scene_tree->node, 0, title_height); | ||
341 | wlr_scene_node_reparent(&child->scene_tree->node, content); | ||
342 | |||
343 | if (activated) { | ||
344 | arrange_container(child, width, height - title_height, | ||
345 | false, 0); | ||
346 | } else { | ||
347 | disable_container(child); | ||
348 | } | ||
349 | |||
350 | y += title_bar_height; | ||
351 | } | ||
352 | } else if (layout == L_VERT) { | ||
353 | int off = 0; | ||
354 | for (int i = 0; i < children->length; i++) { | ||
355 | struct sway_container *child = children->items[i]; | ||
356 | int cheight = child->current.height; | ||
357 | |||
358 | wlr_scene_node_set_enabled(&child->border.tree->node, true); | ||
359 | wlr_scene_node_set_position(&child->scene_tree->node, 0, off); | ||
360 | wlr_scene_node_reparent(&child->scene_tree->node, content); | ||
361 | arrange_container(child, width, cheight, true, gaps); | ||
362 | off += cheight + gaps; | ||
363 | } | ||
364 | } else if (layout == L_HORIZ) { | ||
365 | int off = 0; | ||
366 | for (int i = 0; i < children->length; i++) { | ||
367 | struct sway_container *child = children->items[i]; | ||
368 | int cwidth = child->current.width; | ||
369 | |||
370 | wlr_scene_node_set_enabled(&child->border.tree->node, true); | ||
371 | wlr_scene_node_set_position(&child->scene_tree->node, off, 0); | ||
372 | wlr_scene_node_reparent(&child->scene_tree->node, content); | ||
373 | arrange_container(child, cwidth, height, true, gaps); | ||
374 | off += cwidth + gaps; | ||
375 | } | ||
376 | } else { | ||
377 | sway_assert(false, "unreachable"); | ||
378 | } | ||
379 | } | ||
380 | |||
381 | static void arrange_container(struct sway_container *con, | ||
382 | int width, int height, bool title_bar, int gaps) { | ||
383 | // this container might have previously been in the scratchpad, | ||
384 | // make sure it's enabled for viewing | ||
385 | wlr_scene_node_set_enabled(&con->scene_tree->node, true); | ||
386 | |||
387 | if (con->output_handler) { | ||
388 | wlr_scene_buffer_set_dest_size(con->output_handler, width, height); | ||
239 | } | 389 | } |
240 | 390 | ||
241 | // Damage the new location | 391 | if (con->view) { |
242 | desktop_damage_whole_container(container); | 392 | int border_top = container_titlebar_height(); |
243 | if (view && view->surface) { | 393 | int border_width = con->current.border_thickness; |
244 | struct wlr_surface *surface = view->surface; | 394 | |
245 | struct wlr_box box = { | 395 | if (title_bar && con->current.border != B_NORMAL) { |
246 | .x = container->current.content_x - view->geometry.x, | 396 | wlr_scene_node_set_enabled(&con->title_bar.tree->node, false); |
247 | .y = container->current.content_y - view->geometry.y, | 397 | wlr_scene_node_set_enabled(&con->border.top->node, true); |
248 | .width = surface->current.width, | ||
249 | .height = surface->current.height, | ||
250 | }; | ||
251 | desktop_damage_box(&box); | ||
252 | } | ||
253 | |||
254 | // If the view hasn't responded to the configure, center it within | ||
255 | // the container. This is important for fullscreen views which | ||
256 | // refuse to resize to the size of the output. | ||
257 | if (view && view->surface) { | ||
258 | if (view->geometry.width < container->current.content_width) { | ||
259 | container->surface_x = container->current.content_x + | ||
260 | (container->current.content_width - view->geometry.width) / 2; | ||
261 | } else { | 398 | } else { |
262 | container->surface_x = container->current.content_x; | 399 | wlr_scene_node_set_enabled(&con->border.top->node, false); |
263 | } | 400 | } |
264 | if (view->geometry.height < container->current.content_height) { | 401 | |
265 | container->surface_y = container->current.content_y + | 402 | if (con->current.border == B_NORMAL) { |
266 | (container->current.content_height - view->geometry.height) / 2; | 403 | if (title_bar) { |
404 | arrange_title_bar(con, 0, 0, width, border_top); | ||
405 | } else { | ||
406 | border_top = 0; | ||
407 | // should be handled by the parent container | ||
408 | } | ||
409 | } else if (con->current.border == B_PIXEL) { | ||
410 | container_update(con); | ||
411 | border_top = title_bar && con->current.border_top ? border_width : 0; | ||
412 | } else if (con->current.border == B_NONE) { | ||
413 | container_update(con); | ||
414 | border_top = 0; | ||
415 | border_width = 0; | ||
416 | } else if (con->current.border == B_CSD) { | ||
417 | border_top = 0; | ||
418 | border_width = 0; | ||
419 | } else { | ||
420 | sway_assert(false, "unreachable"); | ||
421 | } | ||
422 | |||
423 | int border_bottom = con->current.border_bottom ? border_width : 0; | ||
424 | int border_left = con->current.border_left ? border_width : 0; | ||
425 | int border_right = con->current.border_right ? border_width : 0; | ||
426 | |||
427 | wlr_scene_rect_set_size(con->border.top, width, border_top); | ||
428 | wlr_scene_rect_set_size(con->border.bottom, width, border_bottom); | ||
429 | wlr_scene_rect_set_size(con->border.left, | ||
430 | border_left, height - border_top - border_bottom); | ||
431 | wlr_scene_rect_set_size(con->border.right, | ||
432 | border_right, height - border_top - border_bottom); | ||
433 | |||
434 | wlr_scene_node_set_position(&con->border.top->node, 0, 0); | ||
435 | wlr_scene_node_set_position(&con->border.bottom->node, | ||
436 | 0, height - border_bottom); | ||
437 | wlr_scene_node_set_position(&con->border.left->node, | ||
438 | 0, border_top); | ||
439 | wlr_scene_node_set_position(&con->border.right->node, | ||
440 | width - border_right, border_top); | ||
441 | |||
442 | // make sure to reparent, it's possible that the client just came out of | ||
443 | // fullscreen mode where the parent of the surface is not the container | ||
444 | wlr_scene_node_reparent(&con->view->scene_tree->node, con->content_tree); | ||
445 | wlr_scene_node_set_position(&con->view->scene_tree->node, | ||
446 | border_left, border_top); | ||
447 | } else { | ||
448 | // make sure to disable the title bar if the parent is not managing it | ||
449 | if (title_bar) { | ||
450 | wlr_scene_node_set_enabled(&con->title_bar.tree->node, false); | ||
451 | } | ||
452 | |||
453 | arrange_children(con->current.layout, con->current.children, | ||
454 | con->current.focused_inactive_child, con->content_tree, | ||
455 | width, height, gaps); | ||
456 | } | ||
457 | } | ||
458 | |||
459 | static int container_get_gaps(struct sway_container *con) { | ||
460 | struct sway_workspace *ws = con->current.workspace; | ||
461 | struct sway_container *temp = con; | ||
462 | while (temp) { | ||
463 | enum sway_container_layout layout; | ||
464 | if (temp->current.parent) { | ||
465 | layout = temp->current.parent->current.layout; | ||
267 | } else { | 466 | } else { |
268 | container->surface_y = container->current.content_y; | 467 | layout = ws->current.layout; |
269 | } | 468 | } |
469 | if (layout == L_TABBED || layout == L_STACKED) { | ||
470 | return 0; | ||
471 | } | ||
472 | temp = temp->pending.parent; | ||
270 | } | 473 | } |
474 | return ws->gaps_inner; | ||
475 | } | ||
476 | |||
477 | static void arrange_fullscreen(struct wlr_scene_tree *tree, | ||
478 | struct sway_container *fs, struct sway_workspace *ws, | ||
479 | int width, int height) { | ||
480 | struct wlr_scene_node *fs_node; | ||
481 | if (fs->view) { | ||
482 | fs_node = &fs->view->scene_tree->node; | ||
483 | |||
484 | // if we only care about the view, disable any decorations | ||
485 | wlr_scene_node_set_enabled(&fs->scene_tree->node, false); | ||
486 | } else { | ||
487 | fs_node = &fs->scene_tree->node; | ||
488 | arrange_container(fs, width, height, true, container_get_gaps(fs)); | ||
489 | } | ||
490 | |||
491 | wlr_scene_node_reparent(fs_node, tree); | ||
492 | wlr_scene_node_lower_to_bottom(fs_node); | ||
493 | wlr_scene_node_set_position(fs_node, 0, 0); | ||
494 | } | ||
495 | |||
496 | static void arrange_workspace_floating(struct sway_workspace *ws) { | ||
497 | for (int i = 0; i < ws->current.floating->length; i++) { | ||
498 | struct sway_container *floater = ws->current.floating->items[i]; | ||
499 | struct wlr_scene_tree *layer = root->layers.floating; | ||
271 | 500 | ||
272 | if (!container->node.destroying) { | 501 | if (floater->current.fullscreen_mode != FULLSCREEN_NONE) { |
273 | container_discover_outputs(container); | 502 | continue; |
503 | } | ||
504 | |||
505 | if (root->fullscreen_global) { | ||
506 | if (container_is_transient_for(floater, root->fullscreen_global)) { | ||
507 | layer = root->layers.fullscreen_global; | ||
508 | } | ||
509 | } else { | ||
510 | for (int i = 0; i < root->outputs->length; i++) { | ||
511 | struct sway_output *output = root->outputs->items[i]; | ||
512 | struct sway_workspace *active = output->current.active_workspace; | ||
513 | |||
514 | if (active && active->fullscreen && | ||
515 | container_is_transient_for(floater, active->fullscreen)) { | ||
516 | layer = root->layers.fullscreen; | ||
517 | } | ||
518 | } | ||
519 | } | ||
520 | |||
521 | wlr_scene_node_reparent(&floater->scene_tree->node, layer); | ||
522 | wlr_scene_node_set_position(&floater->scene_tree->node, | ||
523 | floater->current.x, floater->current.y); | ||
524 | wlr_scene_node_set_enabled(&floater->scene_tree->node, true); | ||
525 | |||
526 | arrange_container(floater, floater->current.width, floater->current.height, | ||
527 | true, ws->gaps_inner); | ||
274 | } | 528 | } |
275 | } | 529 | } |
276 | 530 | ||
531 | static void arrange_workspace_tiling(struct sway_workspace *ws, | ||
532 | int width, int height) { | ||
533 | arrange_children(ws->current.layout, ws->current.tiling, | ||
534 | ws->current.focused_inactive_child, ws->layers.tiling, | ||
535 | width, height, ws->gaps_inner); | ||
536 | } | ||
537 | |||
538 | static void disable_workspace(struct sway_workspace *ws) { | ||
539 | // if any containers were just moved to a disabled workspace it will | ||
540 | // have the parent of the old workspace. Move the workspace so that it won't | ||
541 | // be shown. | ||
542 | for (int i = 0; i < ws->current.tiling->length; i++) { | ||
543 | struct sway_container *child = ws->current.tiling->items[i]; | ||
544 | |||
545 | wlr_scene_node_reparent(&child->scene_tree->node, ws->layers.tiling); | ||
546 | disable_container(child); | ||
547 | } | ||
548 | |||
549 | for (int i = 0; i < ws->current.floating->length; i++) { | ||
550 | struct sway_container *floater = ws->current.floating->items[i]; | ||
551 | wlr_scene_node_reparent(&floater->scene_tree->node, root->layers.floating); | ||
552 | disable_container(floater); | ||
553 | wlr_scene_node_set_enabled(&floater->scene_tree->node, false); | ||
554 | } | ||
555 | } | ||
556 | |||
557 | static void arrange_output(struct sway_output *output, int width, int height) { | ||
558 | for (int i = 0; i < output->current.workspaces->length; i++) { | ||
559 | struct sway_workspace *child = output->current.workspaces->items[i]; | ||
560 | |||
561 | bool activated = output->current.active_workspace == child; | ||
562 | |||
563 | wlr_scene_node_reparent(&child->layers.tiling->node, output->layers.tiling); | ||
564 | wlr_scene_node_reparent(&child->layers.fullscreen->node, output->layers.fullscreen); | ||
565 | |||
566 | for (int i = 0; i < child->current.floating->length; i++) { | ||
567 | struct sway_container *floater = child->current.floating->items[i]; | ||
568 | wlr_scene_node_reparent(&floater->scene_tree->node, root->layers.floating); | ||
569 | wlr_scene_node_set_enabled(&floater->scene_tree->node, activated); | ||
570 | } | ||
571 | |||
572 | if (activated) { | ||
573 | struct sway_container *fs = child->current.fullscreen; | ||
574 | wlr_scene_node_set_enabled(&child->layers.tiling->node, !fs); | ||
575 | wlr_scene_node_set_enabled(&child->layers.fullscreen->node, fs); | ||
576 | |||
577 | arrange_workspace_floating(child); | ||
578 | |||
579 | wlr_scene_node_set_enabled(&output->layers.shell_background->node, !fs); | ||
580 | wlr_scene_node_set_enabled(&output->layers.shell_bottom->node, !fs); | ||
581 | wlr_scene_node_set_enabled(&output->layers.fullscreen->node, fs); | ||
582 | |||
583 | if (fs) { | ||
584 | wlr_scene_rect_set_size(output->fullscreen_background, width, height); | ||
585 | |||
586 | arrange_fullscreen(child->layers.fullscreen, fs, child, | ||
587 | width, height); | ||
588 | } else { | ||
589 | struct wlr_box *area = &output->usable_area; | ||
590 | struct side_gaps *gaps = &child->current_gaps; | ||
591 | |||
592 | wlr_scene_node_set_position(&child->layers.tiling->node, | ||
593 | gaps->left + area->x, gaps->top + area->y); | ||
594 | |||
595 | arrange_workspace_tiling(child, | ||
596 | area->width - gaps->left - gaps->right, | ||
597 | area->height - gaps->top - gaps->bottom); | ||
598 | } | ||
599 | } else { | ||
600 | wlr_scene_node_set_enabled(&child->layers.tiling->node, false); | ||
601 | wlr_scene_node_set_enabled(&child->layers.fullscreen->node, false); | ||
602 | |||
603 | disable_workspace(child); | ||
604 | } | ||
605 | } | ||
606 | } | ||
607 | |||
608 | void arrange_popups(struct wlr_scene_tree *popups) { | ||
609 | struct wlr_scene_node *node; | ||
610 | wl_list_for_each(node, &popups->children, link) { | ||
611 | struct sway_popup_desc *popup = scene_descriptor_try_get(node, | ||
612 | SWAY_SCENE_DESC_POPUP); | ||
613 | |||
614 | int lx, ly; | ||
615 | wlr_scene_node_coords(popup->relative, &lx, &ly); | ||
616 | wlr_scene_node_set_position(node, lx, ly); | ||
617 | } | ||
618 | } | ||
619 | |||
620 | static void arrange_root(struct sway_root *root) { | ||
621 | struct sway_container *fs = root->fullscreen_global; | ||
622 | |||
623 | wlr_scene_node_set_enabled(&root->layers.shell_background->node, !fs); | ||
624 | wlr_scene_node_set_enabled(&root->layers.shell_bottom->node, !fs); | ||
625 | wlr_scene_node_set_enabled(&root->layers.tiling->node, !fs); | ||
626 | wlr_scene_node_set_enabled(&root->layers.floating->node, !fs); | ||
627 | wlr_scene_node_set_enabled(&root->layers.shell_top->node, !fs); | ||
628 | wlr_scene_node_set_enabled(&root->layers.fullscreen->node, !fs); | ||
629 | |||
630 | // hide all contents in the scratchpad | ||
631 | for (int i = 0; i < root->scratchpad->length; i++) { | ||
632 | struct sway_container *con = root->scratchpad->items[i]; | ||
633 | |||
634 | wlr_scene_node_set_enabled(&con->scene_tree->node, false); | ||
635 | } | ||
636 | |||
637 | if (fs) { | ||
638 | for (int i = 0; i < root->outputs->length; i++) { | ||
639 | struct sway_output *output = root->outputs->items[i]; | ||
640 | struct sway_workspace *ws = output->current.active_workspace; | ||
641 | |||
642 | if (ws) { | ||
643 | arrange_workspace_floating(ws); | ||
644 | } | ||
645 | } | ||
646 | |||
647 | arrange_fullscreen(root->layers.fullscreen_global, fs, NULL, | ||
648 | root->width, root->height); | ||
649 | } else { | ||
650 | for (int i = 0; i < root->outputs->length; i++) { | ||
651 | struct sway_output *output = root->outputs->items[i]; | ||
652 | |||
653 | wlr_scene_output_set_position(output->scene_output, output->lx, output->ly); | ||
654 | |||
655 | wlr_scene_node_reparent(&output->layers.shell_background->node, root->layers.shell_background); | ||
656 | wlr_scene_node_reparent(&output->layers.shell_bottom->node, root->layers.shell_bottom); | ||
657 | wlr_scene_node_reparent(&output->layers.tiling->node, root->layers.tiling); | ||
658 | wlr_scene_node_reparent(&output->layers.shell_top->node, root->layers.shell_top); | ||
659 | wlr_scene_node_reparent(&output->layers.shell_overlay->node, root->layers.shell_overlay); | ||
660 | wlr_scene_node_reparent(&output->layers.fullscreen->node, root->layers.fullscreen); | ||
661 | wlr_scene_node_reparent(&output->layers.session_lock->node, root->layers.session_lock); | ||
662 | |||
663 | wlr_scene_node_set_position(&output->layers.shell_background->node, output->lx, output->ly); | ||
664 | wlr_scene_node_set_position(&output->layers.shell_bottom->node, output->lx, output->ly); | ||
665 | wlr_scene_node_set_position(&output->layers.tiling->node, output->lx, output->ly); | ||
666 | wlr_scene_node_set_position(&output->layers.fullscreen->node, output->lx, output->ly); | ||
667 | wlr_scene_node_set_position(&output->layers.shell_top->node, output->lx, output->ly); | ||
668 | wlr_scene_node_set_position(&output->layers.shell_overlay->node, output->lx, output->ly); | ||
669 | wlr_scene_node_set_position(&output->layers.session_lock->node, output->lx, output->ly); | ||
670 | |||
671 | arrange_output(output, output->width, output->height); | ||
672 | } | ||
673 | } | ||
674 | |||
675 | arrange_popups(root->layers.popup); | ||
676 | } | ||
677 | |||
277 | /** | 678 | /** |
278 | * Apply a transaction to the "current" state of the tree. | 679 | * Apply a transaction to the "current" state of the tree. |
279 | */ | 680 | */ |
@@ -313,74 +714,29 @@ static void transaction_apply(struct sway_transaction *transaction) { | |||
313 | 714 | ||
314 | node->instruction = NULL; | 715 | node->instruction = NULL; |
315 | } | 716 | } |
316 | |||
317 | cursor_rebase_all(); | ||
318 | } | 717 | } |
319 | 718 | ||
320 | static void transaction_commit(struct sway_transaction *transaction); | 719 | static void transaction_commit_pending(void); |
321 | 720 | ||
322 | // Return true if both transactions operate on the same nodes | 721 | static void transaction_progress(void) { |
323 | static bool transaction_same_nodes(struct sway_transaction *a, | 722 | if (!server.queued_transaction) { |
324 | struct sway_transaction *b) { | ||
325 | if (a->instructions->length != b->instructions->length) { | ||
326 | return false; | ||
327 | } | ||
328 | for (int i = 0; i < a->instructions->length; ++i) { | ||
329 | struct sway_transaction_instruction *a_inst = a->instructions->items[i]; | ||
330 | struct sway_transaction_instruction *b_inst = b->instructions->items[i]; | ||
331 | if (a_inst->node != b_inst->node) { | ||
332 | return false; | ||
333 | } | ||
334 | } | ||
335 | return true; | ||
336 | } | ||
337 | |||
338 | static void transaction_progress_queue(void) { | ||
339 | if (!server.transactions->length) { | ||
340 | return; | 723 | return; |
341 | } | 724 | } |
342 | // Only the first transaction in the queue is committed, so that's the one | 725 | if (server.queued_transaction->num_waiting > 0) { |
343 | // we try to process. | ||
344 | struct sway_transaction *transaction = server.transactions->items[0]; | ||
345 | if (transaction->num_waiting) { | ||
346 | return; | 726 | return; |
347 | } | 727 | } |
348 | transaction_apply(transaction); | 728 | transaction_apply(server.queued_transaction); |
349 | transaction_destroy(transaction); | 729 | arrange_root(root); |
350 | list_del(server.transactions, 0); | 730 | cursor_rebase_all(); |
731 | transaction_destroy(server.queued_transaction); | ||
732 | server.queued_transaction = NULL; | ||
351 | 733 | ||
352 | if (server.transactions->length == 0) { | 734 | if (!server.pending_transaction) { |
353 | // The transaction queue is empty, so we're done. | 735 | sway_idle_inhibit_v1_check_active(); |
354 | sway_idle_inhibit_v1_check_active(server.idle_inhibit_manager_v1); | ||
355 | return; | 736 | return; |
356 | } | 737 | } |
357 | 738 | ||
358 | // If there's a bunch of consecutive transactions which all apply to the | 739 | transaction_commit_pending(); |
359 | // same views, skip all except the last one. | ||
360 | while (server.transactions->length >= 2) { | ||
361 | struct sway_transaction *txn = server.transactions->items[0]; | ||
362 | struct sway_transaction *dup = NULL; | ||
363 | |||
364 | for (int i = 1; i < server.transactions->length; i++) { | ||
365 | struct sway_transaction *maybe_dup = server.transactions->items[i]; | ||
366 | if (transaction_same_nodes(txn, maybe_dup)) { | ||
367 | dup = maybe_dup; | ||
368 | break; | ||
369 | } | ||
370 | } | ||
371 | |||
372 | if (dup) { | ||
373 | list_del(server.transactions, 0); | ||
374 | transaction_destroy(txn); | ||
375 | } else { | ||
376 | break; | ||
377 | } | ||
378 | } | ||
379 | |||
380 | // We again commit the first transaction in the queue to process it. | ||
381 | transaction = server.transactions->items[0]; | ||
382 | transaction_commit(transaction); | ||
383 | transaction_progress_queue(); | ||
384 | } | 740 | } |
385 | 741 | ||
386 | static int handle_timeout(void *data) { | 742 | static int handle_timeout(void *data) { |
@@ -388,7 +744,7 @@ static int handle_timeout(void *data) { | |||
388 | sway_log(SWAY_DEBUG, "Transaction %p timed out (%zi waiting)", | 744 | sway_log(SWAY_DEBUG, "Transaction %p timed out (%zi waiting)", |
389 | transaction, transaction->num_waiting); | 745 | transaction, transaction->num_waiting); |
390 | transaction->num_waiting = 0; | 746 | transaction->num_waiting = 0; |
391 | transaction_progress_queue(); | 747 | transaction_progress(); |
392 | return 0; | 748 | return 0; |
393 | } | 749 | } |
394 | 750 | ||
@@ -400,9 +756,12 @@ static bool should_configure(struct sway_node *node, | |||
400 | if (node->destroying) { | 756 | if (node->destroying) { |
401 | return false; | 757 | return false; |
402 | } | 758 | } |
759 | if (!instruction->server_request) { | ||
760 | return false; | ||
761 | } | ||
403 | struct sway_container_state *cstate = &node->sway_container->current; | 762 | struct sway_container_state *cstate = &node->sway_container->current; |
404 | struct sway_container_state *istate = &instruction->container_state; | 763 | struct sway_container_state *istate = &instruction->container_state; |
405 | #if HAVE_XWAYLAND | 764 | #if WLR_HAS_XWAYLAND |
406 | // Xwayland views are position-aware and need to be reconfigured | 765 | // Xwayland views are position-aware and need to be reconfigured |
407 | // when their position changes. | 766 | // when their position changes. |
408 | if (node->sway_container->view->type == SWAY_VIEW_XWAYLAND) { | 767 | if (node->sway_container->view->type == SWAY_VIEW_XWAYLAND) { |
@@ -431,28 +790,24 @@ static void transaction_commit(struct sway_transaction *transaction) { | |||
431 | struct sway_transaction_instruction *instruction = | 790 | struct sway_transaction_instruction *instruction = |
432 | transaction->instructions->items[i]; | 791 | transaction->instructions->items[i]; |
433 | struct sway_node *node = instruction->node; | 792 | struct sway_node *node = instruction->node; |
793 | bool hidden = node_is_view(node) && !node->destroying && | ||
794 | !view_is_visible(node->sway_container->view); | ||
434 | if (should_configure(node, instruction)) { | 795 | if (should_configure(node, instruction)) { |
435 | instruction->serial = view_configure(node->sway_container->view, | 796 | instruction->serial = view_configure(node->sway_container->view, |
436 | instruction->container_state.content_x, | 797 | instruction->container_state.content_x, |
437 | instruction->container_state.content_y, | 798 | instruction->container_state.content_y, |
438 | instruction->container_state.content_width, | 799 | instruction->container_state.content_width, |
439 | instruction->container_state.content_height); | 800 | instruction->container_state.content_height); |
440 | ++transaction->num_waiting; | 801 | if (!hidden) { |
441 | 802 | instruction->waiting = true; | |
442 | // From here on we are rendering a saved buffer of the view, which | 803 | ++transaction->num_waiting; |
443 | // means we can send a frame done event to make the client redraw it | 804 | } |
444 | // as soon as possible. Additionally, this is required if a view is | 805 | |
445 | // mapping and its default geometry doesn't intersect an output. | 806 | view_send_frame_done(node->sway_container->view); |
446 | struct timespec now; | ||
447 | clock_gettime(CLOCK_MONOTONIC, &now); | ||
448 | wlr_surface_send_frame_done( | ||
449 | node->sway_container->view->surface, &now); | ||
450 | } | 807 | } |
451 | if (node_is_view(node) && wl_list_empty(&node->sway_container->view->saved_buffers)) { | 808 | if (!hidden && node_is_view(node) && |
809 | !node->sway_container->view->saved_surface_tree) { | ||
452 | view_save_buffer(node->sway_container->view); | 810 | view_save_buffer(node->sway_container->view); |
453 | memcpy(&node->sway_container->view->saved_geometry, | ||
454 | &node->sway_container->view->geometry, | ||
455 | sizeof(struct wlr_box)); | ||
456 | } | 811 | } |
457 | node->instruction = instruction; | 812 | node->instruction = instruction; |
458 | } | 813 | } |
@@ -483,6 +838,17 @@ static void transaction_commit(struct sway_transaction *transaction) { | |||
483 | } | 838 | } |
484 | } | 839 | } |
485 | 840 | ||
841 | static void transaction_commit_pending(void) { | ||
842 | if (server.queued_transaction) { | ||
843 | return; | ||
844 | } | ||
845 | struct sway_transaction *transaction = server.pending_transaction; | ||
846 | server.pending_transaction = NULL; | ||
847 | server.queued_transaction = transaction; | ||
848 | transaction_commit(transaction); | ||
849 | transaction_progress(); | ||
850 | } | ||
851 | |||
486 | static void set_instruction_ready( | 852 | static void set_instruction_ready( |
487 | struct sway_transaction_instruction *instruction) { | 853 | struct sway_transaction_instruction *instruction) { |
488 | struct sway_transaction *transaction = instruction->transaction; | 854 | struct sway_transaction *transaction = instruction->transaction; |
@@ -501,25 +867,28 @@ static void set_instruction_ready( | |||
501 | } | 867 | } |
502 | 868 | ||
503 | // If the transaction has timed out then its num_waiting will be 0 already. | 869 | // If the transaction has timed out then its num_waiting will be 0 already. |
504 | if (transaction->num_waiting > 0 && --transaction->num_waiting == 0) { | 870 | if (instruction->waiting && transaction->num_waiting > 0 && |
871 | --transaction->num_waiting == 0) { | ||
505 | sway_log(SWAY_DEBUG, "Transaction %p is ready", transaction); | 872 | sway_log(SWAY_DEBUG, "Transaction %p is ready", transaction); |
506 | wl_event_source_timer_update(transaction->timer, 0); | 873 | wl_event_source_timer_update(transaction->timer, 0); |
507 | } | 874 | } |
508 | 875 | ||
509 | instruction->node->instruction = NULL; | 876 | instruction->node->instruction = NULL; |
510 | transaction_progress_queue(); | 877 | transaction_progress(); |
511 | } | 878 | } |
512 | 879 | ||
513 | void transaction_notify_view_ready_by_serial(struct sway_view *view, | 880 | bool transaction_notify_view_ready_by_serial(struct sway_view *view, |
514 | uint32_t serial) { | 881 | uint32_t serial) { |
515 | struct sway_transaction_instruction *instruction = | 882 | struct sway_transaction_instruction *instruction = |
516 | view->container->node.instruction; | 883 | view->container->node.instruction; |
517 | if (instruction != NULL && instruction->serial == serial) { | 884 | if (instruction != NULL && instruction->serial == serial) { |
518 | set_instruction_ready(instruction); | 885 | set_instruction_ready(instruction); |
886 | return true; | ||
519 | } | 887 | } |
888 | return false; | ||
520 | } | 889 | } |
521 | 890 | ||
522 | void transaction_notify_view_ready_by_geometry(struct sway_view *view, | 891 | bool transaction_notify_view_ready_by_geometry(struct sway_view *view, |
523 | double x, double y, int width, int height) { | 892 | double x, double y, int width, int height) { |
524 | struct sway_transaction_instruction *instruction = | 893 | struct sway_transaction_instruction *instruction = |
525 | view->container->node.instruction; | 894 | view->container->node.instruction; |
@@ -529,39 +898,37 @@ void transaction_notify_view_ready_by_geometry(struct sway_view *view, | |||
529 | instruction->container_state.content_width == width && | 898 | instruction->container_state.content_width == width && |
530 | instruction->container_state.content_height == height) { | 899 | instruction->container_state.content_height == height) { |
531 | set_instruction_ready(instruction); | 900 | set_instruction_ready(instruction); |
901 | return true; | ||
532 | } | 902 | } |
903 | return false; | ||
533 | } | 904 | } |
534 | 905 | ||
535 | void transaction_notify_view_ready_immediately(struct sway_view *view) { | 906 | static void _transaction_commit_dirty(bool server_request) { |
536 | struct sway_transaction_instruction *instruction = | ||
537 | view->container->node.instruction; | ||
538 | if (instruction != NULL) { | ||
539 | set_instruction_ready(instruction); | ||
540 | } | ||
541 | } | ||
542 | |||
543 | void transaction_commit_dirty(void) { | ||
544 | if (!server.dirty_nodes->length) { | 907 | if (!server.dirty_nodes->length) { |
545 | return; | 908 | return; |
546 | } | 909 | } |
547 | struct sway_transaction *transaction = transaction_create(); | 910 | |
548 | if (!transaction) { | 911 | if (!server.pending_transaction) { |
549 | return; | 912 | server.pending_transaction = transaction_create(); |
913 | if (!server.pending_transaction) { | ||
914 | return; | ||
915 | } | ||
550 | } | 916 | } |
917 | |||
551 | for (int i = 0; i < server.dirty_nodes->length; ++i) { | 918 | for (int i = 0; i < server.dirty_nodes->length; ++i) { |
552 | struct sway_node *node = server.dirty_nodes->items[i]; | 919 | struct sway_node *node = server.dirty_nodes->items[i]; |
553 | transaction_add_node(transaction, node); | 920 | transaction_add_node(server.pending_transaction, node, server_request); |
554 | node->dirty = false; | 921 | node->dirty = false; |
555 | } | 922 | } |
556 | server.dirty_nodes->length = 0; | 923 | server.dirty_nodes->length = 0; |
557 | 924 | ||
558 | list_add(server.transactions, transaction); | 925 | transaction_commit_pending(); |
926 | } | ||
559 | 927 | ||
560 | // We only commit the first transaction added to the queue. | 928 | void transaction_commit_dirty(void) { |
561 | if (server.transactions->length == 1) { | 929 | _transaction_commit_dirty(true); |
562 | transaction_commit(transaction); | 930 | } |
563 | // Attempting to progress the queue here is useful | 931 | |
564 | // if the transaction has nothing to wait for. | 932 | void transaction_commit_dirty_client(void) { |
565 | transaction_progress_queue(); | 933 | _transaction_commit_dirty(false); |
566 | } | ||
567 | } | 934 | } |
diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c index 667fb9e5..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> |
@@ -7,7 +6,7 @@ | |||
7 | #include <wlr/util/edges.h> | 6 | #include <wlr/util/edges.h> |
8 | #include "log.h" | 7 | #include "log.h" |
9 | #include "sway/decoration.h" | 8 | #include "sway/decoration.h" |
10 | #include "sway/desktop.h" | 9 | #include "sway/scene_descriptor.h" |
11 | #include "sway/desktop/transaction.h" | 10 | #include "sway/desktop/transaction.h" |
12 | #include "sway/input/cursor.h" | 11 | #include "sway/input/cursor.h" |
13 | #include "sway/input/input-manager.h" | 12 | #include "sway/input/input-manager.h" |
@@ -19,64 +18,45 @@ | |||
19 | #include "sway/tree/workspace.h" | 18 | #include "sway/tree/workspace.h" |
20 | #include "sway/xdg_decoration.h" | 19 | #include "sway/xdg_decoration.h" |
21 | 20 | ||
22 | static const struct sway_view_child_impl popup_impl; | ||
23 | |||
24 | static void popup_get_root_coords(struct sway_view_child *child, | ||
25 | int *root_sx, int *root_sy) { | ||
26 | struct sway_xdg_popup *popup = (struct sway_xdg_popup *)child; | ||
27 | struct wlr_xdg_surface *surface = popup->wlr_xdg_surface; | ||
28 | |||
29 | int x_offset = -child->view->geometry.x - surface->geometry.x; | ||
30 | int y_offset = -child->view->geometry.y - surface->geometry.y; | ||
31 | |||
32 | wlr_xdg_popup_get_toplevel_coords(surface->popup, | ||
33 | x_offset + surface->popup->geometry.x, | ||
34 | y_offset + surface->popup->geometry.y, | ||
35 | root_sx, root_sy); | ||
36 | } | ||
37 | |||
38 | static void popup_destroy(struct sway_view_child *child) { | ||
39 | if (!sway_assert(child->impl == &popup_impl, | ||
40 | "Expected an xdg_shell popup")) { | ||
41 | return; | ||
42 | } | ||
43 | struct sway_xdg_popup *popup = (struct sway_xdg_popup *)child; | ||
44 | wl_list_remove(&popup->new_popup.link); | ||
45 | wl_list_remove(&popup->destroy.link); | ||
46 | free(popup); | ||
47 | } | ||
48 | |||
49 | static const struct sway_view_child_impl popup_impl = { | ||
50 | .get_root_coords = popup_get_root_coords, | ||
51 | .destroy = popup_destroy, | ||
52 | }; | ||
53 | |||
54 | static struct sway_xdg_popup *popup_create( | 21 | static struct sway_xdg_popup *popup_create( |
55 | struct wlr_xdg_popup *wlr_popup, struct sway_view *view); | 22 | struct wlr_xdg_popup *wlr_popup, struct sway_view *view, |
23 | struct wlr_scene_tree *parent); | ||
56 | 24 | ||
57 | static void popup_handle_new_popup(struct wl_listener *listener, void *data) { | 25 | static void popup_handle_new_popup(struct wl_listener *listener, void *data) { |
58 | struct sway_xdg_popup *popup = | 26 | struct sway_xdg_popup *popup = |
59 | wl_container_of(listener, popup, new_popup); | 27 | wl_container_of(listener, popup, new_popup); |
60 | struct wlr_xdg_popup *wlr_popup = data; | 28 | struct wlr_xdg_popup *wlr_popup = data; |
61 | popup_create(wlr_popup, popup->child.view); | 29 | popup_create(wlr_popup, popup->view, popup->xdg_surface_tree); |
62 | } | 30 | } |
63 | 31 | ||
64 | static void popup_handle_destroy(struct wl_listener *listener, void *data) { | 32 | static void popup_handle_destroy(struct wl_listener *listener, void *data) { |
65 | struct sway_xdg_popup *popup = wl_container_of(listener, popup, destroy); | 33 | struct sway_xdg_popup *popup = wl_container_of(listener, popup, destroy); |
66 | view_child_destroy(&popup->child); | 34 | |
35 | wl_list_remove(&popup->new_popup.link); | ||
36 | wl_list_remove(&popup->destroy.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); | ||
40 | free(popup); | ||
67 | } | 41 | } |
68 | 42 | ||
69 | static void popup_unconstrain(struct sway_xdg_popup *popup) { | 43 | static void popup_unconstrain(struct sway_xdg_popup *popup) { |
70 | struct sway_view *view = popup->child.view; | 44 | struct sway_view *view = popup->view; |
71 | struct wlr_xdg_popup *wlr_popup = popup->wlr_xdg_surface->popup; | 45 | struct wlr_xdg_popup *wlr_popup = popup->wlr_xdg_popup; |
46 | |||
47 | struct sway_workspace *workspace = view->container->pending.workspace; | ||
48 | if (!workspace) { | ||
49 | // is null if in the scratchpad | ||
50 | return; | ||
51 | } | ||
72 | 52 | ||
73 | struct sway_output *output = view->container->workspace->output; | 53 | struct sway_output *output = workspace->output; |
74 | 54 | ||
75 | // the output box expressed in the coordinate system of the toplevel parent | 55 | // the output box expressed in the coordinate system of the toplevel parent |
76 | // of the popup | 56 | // of the popup |
77 | struct wlr_box output_toplevel_sx_box = { | 57 | struct wlr_box output_toplevel_sx_box = { |
78 | .x = output->lx - view->container->content_x, | 58 | .x = output->lx - view->container->pending.content_x + view->geometry.x, |
79 | .y = output->ly - view->container->content_y, | 59 | .y = output->ly - view->container->pending.content_y + view->geometry.y, |
80 | .width = output->width, | 60 | .width = output->width, |
81 | .height = output->height, | 61 | .height = output->height, |
82 | }; | 62 | }; |
@@ -84,32 +64,72 @@ static void popup_unconstrain(struct sway_xdg_popup *popup) { | |||
84 | wlr_xdg_popup_unconstrain_from_box(wlr_popup, &output_toplevel_sx_box); | 64 | wlr_xdg_popup_unconstrain_from_box(wlr_popup, &output_toplevel_sx_box); |
85 | } | 65 | } |
86 | 66 | ||
87 | static struct sway_xdg_popup *popup_create( | 67 | static void popup_handle_surface_commit(struct wl_listener *listener, void *data) { |
88 | struct wlr_xdg_popup *wlr_popup, struct sway_view *view) { | 68 | struct sway_xdg_popup *popup = wl_container_of(listener, popup, surface_commit); |
69 | if (popup->wlr_xdg_popup->base->initial_commit) { | ||
70 | popup_unconstrain(popup); | ||
71 | } | ||
72 | } | ||
73 | |||
74 | static 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 | |||
79 | static struct sway_xdg_popup *popup_create(struct wlr_xdg_popup *wlr_popup, | ||
80 | struct sway_view *view, struct wlr_scene_tree *parent) { | ||
89 | struct wlr_xdg_surface *xdg_surface = wlr_popup->base; | 81 | struct wlr_xdg_surface *xdg_surface = wlr_popup->base; |
90 | 82 | ||
91 | struct sway_xdg_popup *popup = | 83 | struct sway_xdg_popup *popup = calloc(1, sizeof(struct sway_xdg_popup)); |
92 | calloc(1, sizeof(struct sway_xdg_popup)); | 84 | if (!popup) { |
93 | if (popup == NULL) { | 85 | return NULL; |
86 | } | ||
87 | |||
88 | popup->wlr_xdg_popup = wlr_popup; | ||
89 | popup->view = view; | ||
90 | |||
91 | popup->scene_tree = wlr_scene_tree_create(parent); | ||
92 | if (!popup->scene_tree) { | ||
93 | free(popup); | ||
94 | return NULL; | ||
95 | } | ||
96 | |||
97 | popup->xdg_surface_tree = wlr_scene_xdg_surface_create( | ||
98 | popup->scene_tree, xdg_surface); | ||
99 | if (!popup->xdg_surface_tree) { | ||
100 | wlr_scene_node_destroy(&popup->scene_tree->node); | ||
101 | free(popup); | ||
94 | return NULL; | 102 | return NULL; |
95 | } | 103 | } |
96 | view_child_init(&popup->child, &popup_impl, view, xdg_surface->surface); | ||
97 | popup->wlr_xdg_surface = xdg_surface; | ||
98 | 104 | ||
105 | popup->desc.relative = &view->content_tree->node; | ||
106 | popup->desc.view = view; | ||
107 | |||
108 | if (!scene_descriptor_assign(&popup->scene_tree->node, | ||
109 | SWAY_SCENE_DESC_POPUP, &popup->desc)) { | ||
110 | sway_log(SWAY_ERROR, "Failed to allocate a popup scene descriptor"); | ||
111 | wlr_scene_node_destroy(&popup->scene_tree->node); | ||
112 | free(popup); | ||
113 | return NULL; | ||
114 | } | ||
115 | |||
116 | popup->wlr_xdg_popup = xdg_surface->popup; | ||
117 | struct sway_xdg_shell_view *shell_view = | ||
118 | wl_container_of(view, shell_view, view); | ||
119 | xdg_surface->data = shell_view; | ||
120 | |||
121 | wl_signal_add(&xdg_surface->surface->events.commit, &popup->surface_commit); | ||
122 | popup->surface_commit.notify = popup_handle_surface_commit; | ||
99 | wl_signal_add(&xdg_surface->events.new_popup, &popup->new_popup); | 123 | wl_signal_add(&xdg_surface->events.new_popup, &popup->new_popup); |
100 | popup->new_popup.notify = popup_handle_new_popup; | 124 | popup->new_popup.notify = popup_handle_new_popup; |
101 | wl_signal_add(&xdg_surface->events.destroy, &popup->destroy); | 125 | wl_signal_add(&wlr_popup->events.reposition, &popup->reposition); |
126 | popup->reposition.notify = popup_handle_reposition; | ||
127 | wl_signal_add(&wlr_popup->events.destroy, &popup->destroy); | ||
102 | popup->destroy.notify = popup_handle_destroy; | 128 | popup->destroy.notify = popup_handle_destroy; |
103 | 129 | ||
104 | wl_signal_add(&xdg_surface->events.map, &popup->child.surface_map); | ||
105 | wl_signal_add(&xdg_surface->events.unmap, &popup->child.surface_unmap); | ||
106 | |||
107 | popup_unconstrain(popup); | ||
108 | |||
109 | return popup; | 130 | return popup; |
110 | } | 131 | } |
111 | 132 | ||
112 | |||
113 | static struct sway_xdg_shell_view *xdg_shell_view_from_view( | 133 | static struct sway_xdg_shell_view *xdg_shell_view_from_view( |
114 | struct sway_view *view) { | 134 | struct sway_view *view) { |
115 | if (!sway_assert(view->type == SWAY_VIEW_XDG_SHELL, | 135 | if (!sway_assert(view->type == SWAY_VIEW_XDG_SHELL, |
@@ -122,7 +142,7 @@ static struct sway_xdg_shell_view *xdg_shell_view_from_view( | |||
122 | static void get_constraints(struct sway_view *view, double *min_width, | 142 | static void get_constraints(struct sway_view *view, double *min_width, |
123 | double *max_width, double *min_height, double *max_height) { | 143 | double *max_width, double *min_height, double *max_height) { |
124 | struct wlr_xdg_toplevel_state *state = | 144 | struct wlr_xdg_toplevel_state *state = |
125 | &view->wlr_xdg_surface->toplevel->current; | 145 | &view->wlr_xdg_toplevel->current; |
126 | *min_width = state->min_width > 0 ? state->min_width : DBL_MIN; | 146 | *min_width = state->min_width > 0 ? state->min_width : DBL_MIN; |
127 | *max_width = state->max_width > 0 ? state->max_width : DBL_MAX; | 147 | *max_width = state->max_width > 0 ? state->max_width : DBL_MAX; |
128 | *min_height = state->min_height > 0 ? state->min_height : DBL_MIN; | 148 | *min_height = state->min_height > 0 ? state->min_height : DBL_MIN; |
@@ -136,9 +156,9 @@ static const char *get_string_prop(struct sway_view *view, | |||
136 | } | 156 | } |
137 | switch (prop) { | 157 | switch (prop) { |
138 | case VIEW_PROP_TITLE: | 158 | case VIEW_PROP_TITLE: |
139 | return view->wlr_xdg_surface->toplevel->title; | 159 | return view->wlr_xdg_toplevel->title; |
140 | case VIEW_PROP_APP_ID: | 160 | case VIEW_PROP_APP_ID: |
141 | return view->wlr_xdg_surface->toplevel->app_id; | 161 | return view->wlr_xdg_toplevel->app_id; |
142 | default: | 162 | default: |
143 | return NULL; | 163 | return NULL; |
144 | } | 164 | } |
@@ -151,50 +171,52 @@ static uint32_t configure(struct sway_view *view, double lx, double ly, | |||
151 | if (xdg_shell_view == NULL) { | 171 | if (xdg_shell_view == NULL) { |
152 | return 0; | 172 | return 0; |
153 | } | 173 | } |
154 | return wlr_xdg_toplevel_set_size(view->wlr_xdg_surface, width, height); | 174 | return wlr_xdg_toplevel_set_size(view->wlr_xdg_toplevel, |
175 | width, height); | ||
155 | } | 176 | } |
156 | 177 | ||
157 | static void set_activated(struct sway_view *view, bool activated) { | 178 | static void set_activated(struct sway_view *view, bool activated) { |
158 | if (xdg_shell_view_from_view(view) == NULL) { | 179 | if (xdg_shell_view_from_view(view) == NULL) { |
159 | return; | 180 | return; |
160 | } | 181 | } |
161 | struct wlr_xdg_surface *surface = view->wlr_xdg_surface; | 182 | wlr_xdg_toplevel_set_activated(view->wlr_xdg_toplevel, activated); |
162 | if (surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL) { | ||
163 | wlr_xdg_toplevel_set_activated(surface, activated); | ||
164 | } | ||
165 | } | 183 | } |
166 | 184 | ||
167 | static void set_tiled(struct sway_view *view, bool tiled) { | 185 | static void set_tiled(struct sway_view *view, bool tiled) { |
168 | if (xdg_shell_view_from_view(view) == NULL) { | 186 | if (xdg_shell_view_from_view(view) == NULL) { |
169 | return; | 187 | return; |
170 | } | 188 | } |
171 | struct wlr_xdg_surface *surface = view->wlr_xdg_surface; | 189 | if (wl_resource_get_version(view->wlr_xdg_toplevel->resource) >= |
172 | enum wlr_edges edges = WLR_EDGE_NONE; | 190 | XDG_TOPLEVEL_STATE_TILED_LEFT_SINCE_VERSION) { |
173 | if (tiled) { | 191 | enum wlr_edges edges = WLR_EDGE_NONE; |
174 | edges = WLR_EDGE_LEFT | WLR_EDGE_RIGHT | WLR_EDGE_TOP | | 192 | if (tiled) { |
175 | WLR_EDGE_BOTTOM; | 193 | edges = WLR_EDGE_LEFT | WLR_EDGE_RIGHT | WLR_EDGE_TOP | |
194 | WLR_EDGE_BOTTOM; | ||
195 | } | ||
196 | wlr_xdg_toplevel_set_tiled(view->wlr_xdg_toplevel, edges); | ||
197 | } else { | ||
198 | // The version is too low for the tiled state; configure as maximized instead | ||
199 | // to stop the client from drawing decorations outside of the toplevel geometry. | ||
200 | wlr_xdg_toplevel_set_maximized(view->wlr_xdg_toplevel, tiled); | ||
176 | } | 201 | } |
177 | wlr_xdg_toplevel_set_tiled(surface, edges); | ||
178 | } | 202 | } |
179 | 203 | ||
180 | static void set_fullscreen(struct sway_view *view, bool fullscreen) { | 204 | static void set_fullscreen(struct sway_view *view, bool fullscreen) { |
181 | if (xdg_shell_view_from_view(view) == NULL) { | 205 | if (xdg_shell_view_from_view(view) == NULL) { |
182 | return; | 206 | return; |
183 | } | 207 | } |
184 | struct wlr_xdg_surface *surface = view->wlr_xdg_surface; | 208 | wlr_xdg_toplevel_set_fullscreen(view->wlr_xdg_toplevel, fullscreen); |
185 | wlr_xdg_toplevel_set_fullscreen(surface, fullscreen); | ||
186 | } | 209 | } |
187 | 210 | ||
188 | static void set_resizing(struct sway_view *view, bool resizing) { | 211 | static void set_resizing(struct sway_view *view, bool resizing) { |
189 | if (xdg_shell_view_from_view(view) == NULL) { | 212 | if (xdg_shell_view_from_view(view) == NULL) { |
190 | return; | 213 | return; |
191 | } | 214 | } |
192 | struct wlr_xdg_surface *surface = view->wlr_xdg_surface; | 215 | wlr_xdg_toplevel_set_resizing(view->wlr_xdg_toplevel, resizing); |
193 | wlr_xdg_toplevel_set_resizing(surface, resizing); | ||
194 | } | 216 | } |
195 | 217 | ||
196 | static bool wants_floating(struct sway_view *view) { | 218 | static bool wants_floating(struct sway_view *view) { |
197 | struct wlr_xdg_toplevel *toplevel = view->wlr_xdg_surface->toplevel; | 219 | struct wlr_xdg_toplevel *toplevel = view->wlr_xdg_toplevel; |
198 | struct wlr_xdg_toplevel_state *state = &toplevel->current; | 220 | struct wlr_xdg_toplevel_state *state = &toplevel->current; |
199 | return (state->min_width != 0 && state->min_height != 0 | 221 | return (state->min_width != 0 && state->min_height != 0 |
200 | && (state->min_width == state->max_width | 222 | && (state->min_width == state->max_width |
@@ -202,35 +224,17 @@ static bool wants_floating(struct sway_view *view) { | |||
202 | || toplevel->parent; | 224 | || toplevel->parent; |
203 | } | 225 | } |
204 | 226 | ||
205 | static void for_each_surface(struct sway_view *view, | ||
206 | wlr_surface_iterator_func_t iterator, void *user_data) { | ||
207 | if (xdg_shell_view_from_view(view) == NULL) { | ||
208 | return; | ||
209 | } | ||
210 | wlr_xdg_surface_for_each_surface(view->wlr_xdg_surface, iterator, | ||
211 | user_data); | ||
212 | } | ||
213 | |||
214 | static void for_each_popup_surface(struct sway_view *view, | ||
215 | wlr_surface_iterator_func_t iterator, void *user_data) { | ||
216 | if (xdg_shell_view_from_view(view) == NULL) { | ||
217 | return; | ||
218 | } | ||
219 | wlr_xdg_surface_for_each_popup_surface(view->wlr_xdg_surface, iterator, | ||
220 | user_data); | ||
221 | } | ||
222 | |||
223 | static bool is_transient_for(struct sway_view *child, | 227 | static bool is_transient_for(struct sway_view *child, |
224 | struct sway_view *ancestor) { | 228 | struct sway_view *ancestor) { |
225 | if (xdg_shell_view_from_view(child) == NULL) { | 229 | if (xdg_shell_view_from_view(child) == NULL) { |
226 | return false; | 230 | return false; |
227 | } | 231 | } |
228 | struct wlr_xdg_surface *surface = child->wlr_xdg_surface; | 232 | struct wlr_xdg_toplevel *toplevel = child->wlr_xdg_toplevel; |
229 | while (surface && surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL) { | 233 | while (toplevel) { |
230 | if (surface->toplevel->parent == ancestor->wlr_xdg_surface) { | 234 | if (toplevel->parent == ancestor->wlr_xdg_toplevel) { |
231 | return true; | 235 | return true; |
232 | } | 236 | } |
233 | surface = surface->toplevel->parent; | 237 | toplevel = toplevel->parent; |
234 | } | 238 | } |
235 | return false; | 239 | return false; |
236 | } | 240 | } |
@@ -239,17 +243,13 @@ static void _close(struct sway_view *view) { | |||
239 | if (xdg_shell_view_from_view(view) == NULL) { | 243 | if (xdg_shell_view_from_view(view) == NULL) { |
240 | return; | 244 | return; |
241 | } | 245 | } |
242 | struct wlr_xdg_surface *surface = view->wlr_xdg_surface; | 246 | wlr_xdg_toplevel_send_close(view->wlr_xdg_toplevel); |
243 | if (surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL | ||
244 | && surface->toplevel) { | ||
245 | wlr_xdg_toplevel_send_close(surface); | ||
246 | } | ||
247 | } | 247 | } |
248 | 248 | ||
249 | static void close_popups(struct sway_view *view) { | 249 | static void close_popups(struct sway_view *view) { |
250 | struct wlr_xdg_popup *popup, *tmp; | 250 | struct wlr_xdg_popup *popup, *tmp; |
251 | wl_list_for_each_safe(popup, tmp, &view->wlr_xdg_surface->popups, link) { | 251 | wl_list_for_each_safe(popup, tmp, &view->wlr_xdg_toplevel->base->popups, link) { |
252 | wlr_xdg_popup_destroy(popup->base); | 252 | wlr_xdg_popup_destroy(popup); |
253 | } | 253 | } |
254 | } | 254 | } |
255 | 255 | ||
@@ -271,8 +271,6 @@ static const struct sway_view_impl view_impl = { | |||
271 | .set_fullscreen = set_fullscreen, | 271 | .set_fullscreen = set_fullscreen, |
272 | .set_resizing = set_resizing, | 272 | .set_resizing = set_resizing, |
273 | .wants_floating = wants_floating, | 273 | .wants_floating = wants_floating, |
274 | .for_each_surface = for_each_surface, | ||
275 | .for_each_popup_surface = for_each_popup_surface, | ||
276 | .is_transient_for = is_transient_for, | 274 | .is_transient_for = is_transient_for, |
277 | .close = _close, | 275 | .close = _close, |
278 | .close_popups = close_popups, | 276 | .close_popups = close_popups, |
@@ -283,7 +281,21 @@ static void handle_commit(struct wl_listener *listener, void *data) { | |||
283 | struct sway_xdg_shell_view *xdg_shell_view = | 281 | struct sway_xdg_shell_view *xdg_shell_view = |
284 | wl_container_of(listener, xdg_shell_view, commit); | 282 | wl_container_of(listener, xdg_shell_view, commit); |
285 | struct sway_view *view = &xdg_shell_view->view; | 283 | struct sway_view *view = &xdg_shell_view->view; |
286 | struct wlr_xdg_surface *xdg_surface = view->wlr_xdg_surface; | 284 | struct wlr_xdg_surface *xdg_surface = view->wlr_xdg_toplevel->base; |
285 | |||
286 | if (xdg_surface->initial_commit) { | ||
287 | if (view->xdg_decoration != NULL) { | ||
288 | set_xdg_decoration_mode(view->xdg_decoration); | ||
289 | } | ||
290 | // XXX: https://github.com/swaywm/sway/issues/2176 | ||
291 | wlr_xdg_surface_schedule_configure(xdg_surface); | ||
292 | // TODO: wlr_xdg_toplevel_set_bounds() | ||
293 | return; | ||
294 | } | ||
295 | |||
296 | if (!xdg_surface->surface->mapped) { | ||
297 | return; | ||
298 | } | ||
287 | 299 | ||
288 | struct wlr_box new_geo; | 300 | struct wlr_box new_geo; |
289 | wlr_xdg_surface_get_geometry(xdg_surface, &new_geo); | 301 | wlr_xdg_surface_get_geometry(xdg_surface, &new_geo); |
@@ -293,22 +305,35 @@ static void handle_commit(struct wl_listener *listener, void *data) { | |||
293 | new_geo.y != view->geometry.y; | 305 | new_geo.y != view->geometry.y; |
294 | 306 | ||
295 | if (new_size) { | 307 | if (new_size) { |
296 | // The view has unexpectedly sent a new size | 308 | // The client changed its surface size in this commit. For floating |
297 | desktop_damage_view(view); | 309 | // containers, we resize the container to match. For tiling containers, |
298 | view_update_size(view, new_geo.width, new_geo.height); | 310 | // we only recenter the surface. |
299 | memcpy(&view->geometry, &new_geo, sizeof(struct wlr_box)); | 311 | memcpy(&view->geometry, &new_geo, sizeof(struct wlr_box)); |
300 | desktop_damage_view(view); | 312 | if (container_is_floating(view->container)) { |
301 | transaction_commit_dirty(); | 313 | view_update_size(view); |
314 | // Only set the toplevel size the current container actually has a size. | ||
315 | if (view->container->current.width) { | ||
316 | wlr_xdg_toplevel_set_size(view->wlr_xdg_toplevel, view->geometry.width, | ||
317 | view->geometry.height); | ||
318 | } | ||
319 | transaction_commit_dirty_client(); | ||
320 | } | ||
321 | |||
322 | view_center_and_clip_surface(view); | ||
302 | } | 323 | } |
303 | 324 | ||
304 | if (view->container->node.instruction) { | 325 | if (view->container->node.instruction) { |
305 | transaction_notify_view_ready_by_serial(view, | 326 | bool successful = transaction_notify_view_ready_by_serial(view, |
306 | xdg_surface->configure_serial); | 327 | xdg_surface->current.configure_serial); |
307 | } else if (new_size) { | 328 | |
308 | transaction_notify_view_ready_immediately(view); | 329 | // If we saved the view and this commit isn't what we're looking for |
330 | // that means the user will never actually see the buffers submitted to | ||
331 | // us here. Just send frame done events to these surfaces so they can | ||
332 | // commit another time for us. | ||
333 | if (view->saved_surface_tree && !successful) { | ||
334 | view_send_frame_done(view); | ||
335 | } | ||
309 | } | 336 | } |
310 | |||
311 | view_damage_from(view); | ||
312 | } | 337 | } |
313 | 338 | ||
314 | static void handle_set_title(struct wl_listener *listener, void *data) { | 339 | static void handle_set_title(struct wl_listener *listener, void *data) { |
@@ -323,6 +348,7 @@ static void handle_set_app_id(struct wl_listener *listener, void *data) { | |||
323 | struct sway_xdg_shell_view *xdg_shell_view = | 348 | struct sway_xdg_shell_view *xdg_shell_view = |
324 | wl_container_of(listener, xdg_shell_view, set_app_id); | 349 | wl_container_of(listener, xdg_shell_view, set_app_id); |
325 | struct sway_view *view = &xdg_shell_view->view; | 350 | struct sway_view *view = &xdg_shell_view->view; |
351 | view_update_app_id(view); | ||
326 | view_execute_criteria(view); | 352 | view_execute_criteria(view); |
327 | } | 353 | } |
328 | 354 | ||
@@ -330,31 +356,42 @@ static void handle_new_popup(struct wl_listener *listener, void *data) { | |||
330 | struct sway_xdg_shell_view *xdg_shell_view = | 356 | struct sway_xdg_shell_view *xdg_shell_view = |
331 | wl_container_of(listener, xdg_shell_view, new_popup); | 357 | wl_container_of(listener, xdg_shell_view, new_popup); |
332 | struct wlr_xdg_popup *wlr_popup = data; | 358 | struct wlr_xdg_popup *wlr_popup = data; |
333 | popup_create(wlr_popup, &xdg_shell_view->view); | 359 | |
360 | struct sway_xdg_popup *popup = popup_create(wlr_popup, | ||
361 | &xdg_shell_view->view, root->layers.popup); | ||
362 | if (!popup) { | ||
363 | return; | ||
364 | } | ||
365 | |||
366 | int lx, ly; | ||
367 | wlr_scene_node_coords(&popup->view->content_tree->node, &lx, &ly); | ||
368 | wlr_scene_node_set_position(&popup->scene_tree->node, lx, ly); | ||
369 | } | ||
370 | |||
371 | static void handle_request_maximize(struct wl_listener *listener, void *data) { | ||
372 | struct sway_xdg_shell_view *xdg_shell_view = | ||
373 | wl_container_of(listener, xdg_shell_view, request_maximize); | ||
374 | struct wlr_xdg_toplevel *toplevel = xdg_shell_view->view.wlr_xdg_toplevel; | ||
375 | wlr_xdg_surface_schedule_configure(toplevel->base); | ||
334 | } | 376 | } |
335 | 377 | ||
336 | static void handle_request_fullscreen(struct wl_listener *listener, void *data) { | 378 | static void handle_request_fullscreen(struct wl_listener *listener, void *data) { |
337 | struct sway_xdg_shell_view *xdg_shell_view = | 379 | struct sway_xdg_shell_view *xdg_shell_view = |
338 | wl_container_of(listener, xdg_shell_view, request_fullscreen); | 380 | wl_container_of(listener, xdg_shell_view, request_fullscreen); |
339 | struct wlr_xdg_toplevel_set_fullscreen_event *e = data; | 381 | struct wlr_xdg_toplevel *toplevel = xdg_shell_view->view.wlr_xdg_toplevel; |
340 | struct wlr_xdg_surface *xdg_surface = | ||
341 | xdg_shell_view->view.wlr_xdg_surface; | ||
342 | struct sway_view *view = &xdg_shell_view->view; | 382 | struct sway_view *view = &xdg_shell_view->view; |
343 | 383 | ||
344 | if (!sway_assert(xdg_surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL, | 384 | if (!toplevel->base->surface->mapped) { |
345 | "xdg_shell requested fullscreen of surface with role %i", | ||
346 | xdg_surface->role)) { | ||
347 | return; | ||
348 | } | ||
349 | if (!xdg_surface->mapped) { | ||
350 | return; | 385 | return; |
351 | } | 386 | } |
352 | 387 | ||
353 | struct sway_container *container = view->container; | 388 | struct sway_container *container = view->container; |
354 | if (e->fullscreen && e->output && e->output->data) { | 389 | struct wlr_xdg_toplevel_requested *req = &toplevel->requested; |
355 | struct sway_output *output = e->output->data; | 390 | if (req->fullscreen && req->fullscreen_output && req->fullscreen_output->data) { |
391 | struct sway_output *output = req->fullscreen_output->data; | ||
356 | struct sway_workspace *ws = output_get_active_workspace(output); | 392 | struct sway_workspace *ws = output_get_active_workspace(output); |
357 | if (ws && !container_is_scratchpad_hidden(container)) { | 393 | if (ws && !container_is_scratchpad_hidden(container) && |
394 | container->pending.workspace != ws) { | ||
358 | if (container_is_floating(container)) { | 395 | if (container_is_floating(container)) { |
359 | workspace_add_floating(ws, container); | 396 | workspace_add_floating(ws, container); |
360 | } else { | 397 | } else { |
@@ -363,22 +400,18 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data) | |||
363 | } | 400 | } |
364 | } | 401 | } |
365 | 402 | ||
366 | container_set_fullscreen(container, e->fullscreen); | 403 | container_set_fullscreen(container, req->fullscreen); |
367 | 404 | ||
368 | arrange_root(); | 405 | arrange_root(); |
369 | transaction_commit_dirty(); | 406 | transaction_commit_dirty(); |
370 | } | 407 | } |
371 | 408 | ||
372 | static void handle_request_maximize(struct wl_listener *listener, void *data) { | ||
373 | struct wlr_xdg_surface *surface = data; | ||
374 | wlr_xdg_surface_schedule_configure(surface); | ||
375 | } | ||
376 | |||
377 | static void handle_request_move(struct wl_listener *listener, void *data) { | 409 | static void handle_request_move(struct wl_listener *listener, void *data) { |
378 | struct sway_xdg_shell_view *xdg_shell_view = | 410 | struct sway_xdg_shell_view *xdg_shell_view = |
379 | wl_container_of(listener, xdg_shell_view, request_move); | 411 | wl_container_of(listener, xdg_shell_view, request_move); |
380 | struct sway_view *view = &xdg_shell_view->view; | 412 | struct sway_view *view = &xdg_shell_view->view; |
381 | if (!container_is_floating(view->container)) { | 413 | if (!container_is_floating(view->container) || |
414 | view->container->pending.fullscreen_mode) { | ||
382 | return; | 415 | return; |
383 | } | 416 | } |
384 | struct wlr_xdg_toplevel_move_event *e = data; | 417 | struct wlr_xdg_toplevel_move_event *e = data; |
@@ -413,10 +446,9 @@ static void handle_unmap(struct wl_listener *listener, void *data) { | |||
413 | 446 | ||
414 | view_unmap(view); | 447 | view_unmap(view); |
415 | 448 | ||
416 | wl_list_remove(&xdg_shell_view->commit.link); | ||
417 | wl_list_remove(&xdg_shell_view->new_popup.link); | 449 | wl_list_remove(&xdg_shell_view->new_popup.link); |
418 | wl_list_remove(&xdg_shell_view->request_fullscreen.link); | ||
419 | wl_list_remove(&xdg_shell_view->request_maximize.link); | 450 | wl_list_remove(&xdg_shell_view->request_maximize.link); |
451 | wl_list_remove(&xdg_shell_view->request_fullscreen.link); | ||
420 | wl_list_remove(&xdg_shell_view->request_move.link); | 452 | wl_list_remove(&xdg_shell_view->request_move.link); |
421 | wl_list_remove(&xdg_shell_view->request_resize.link); | 453 | wl_list_remove(&xdg_shell_view->request_resize.link); |
422 | wl_list_remove(&xdg_shell_view->set_title.link); | 454 | wl_list_remove(&xdg_shell_view->set_title.link); |
@@ -427,62 +459,61 @@ static void handle_map(struct wl_listener *listener, void *data) { | |||
427 | struct sway_xdg_shell_view *xdg_shell_view = | 459 | struct sway_xdg_shell_view *xdg_shell_view = |
428 | wl_container_of(listener, xdg_shell_view, map); | 460 | wl_container_of(listener, xdg_shell_view, map); |
429 | struct sway_view *view = &xdg_shell_view->view; | 461 | struct sway_view *view = &xdg_shell_view->view; |
430 | struct wlr_xdg_surface *xdg_surface = view->wlr_xdg_surface; | 462 | struct wlr_xdg_toplevel *toplevel = view->wlr_xdg_toplevel; |
431 | 463 | ||
432 | view->natural_width = view->wlr_xdg_surface->geometry.width; | 464 | view->natural_width = toplevel->base->current.geometry.width; |
433 | view->natural_height = view->wlr_xdg_surface->geometry.height; | 465 | view->natural_height = toplevel->base->current.geometry.height; |
434 | if (!view->natural_width && !view->natural_height) { | 466 | if (!view->natural_width && !view->natural_height) { |
435 | view->natural_width = view->wlr_xdg_surface->surface->current.width; | 467 | view->natural_width = toplevel->base->surface->current.width; |
436 | view->natural_height = view->wlr_xdg_surface->surface->current.height; | 468 | view->natural_height = toplevel->base->surface->current.height; |
437 | } | 469 | } |
438 | 470 | ||
439 | bool csd = false; | 471 | bool csd = false; |
440 | 472 | ||
441 | if (!view->xdg_decoration) { | 473 | if (view->xdg_decoration) { |
474 | enum wlr_xdg_toplevel_decoration_v1_mode mode = | ||
475 | view->xdg_decoration->wlr_xdg_decoration->requested_mode; | ||
476 | csd = mode == WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE; | ||
477 | } else { | ||
442 | struct sway_server_decoration *deco = | 478 | struct sway_server_decoration *deco = |
443 | decoration_from_surface(xdg_surface->surface); | 479 | decoration_from_surface(toplevel->base->surface); |
444 | csd = !deco || deco->wlr_server_decoration->mode == | 480 | csd = !deco || deco->wlr_server_decoration->mode == |
445 | WLR_SERVER_DECORATION_MANAGER_MODE_CLIENT; | 481 | WLR_SERVER_DECORATION_MANAGER_MODE_CLIENT; |
446 | |||
447 | } | 482 | } |
448 | 483 | ||
449 | view_map(view, view->wlr_xdg_surface->surface, | 484 | view_map(view, toplevel->base->surface, |
450 | xdg_surface->toplevel->client_pending.fullscreen, | 485 | toplevel->requested.fullscreen, |
451 | xdg_surface->toplevel->client_pending.fullscreen_output, | 486 | toplevel->requested.fullscreen_output, |
452 | csd); | 487 | csd); |
453 | 488 | ||
454 | transaction_commit_dirty(); | 489 | transaction_commit_dirty(); |
455 | 490 | ||
456 | xdg_shell_view->commit.notify = handle_commit; | ||
457 | wl_signal_add(&xdg_surface->surface->events.commit, | ||
458 | &xdg_shell_view->commit); | ||
459 | |||
460 | xdg_shell_view->new_popup.notify = handle_new_popup; | 491 | xdg_shell_view->new_popup.notify = handle_new_popup; |
461 | wl_signal_add(&xdg_surface->events.new_popup, | 492 | wl_signal_add(&toplevel->base->events.new_popup, |
462 | &xdg_shell_view->new_popup); | 493 | &xdg_shell_view->new_popup); |
463 | 494 | ||
464 | xdg_shell_view->request_fullscreen.notify = handle_request_fullscreen; | ||
465 | wl_signal_add(&xdg_surface->toplevel->events.request_fullscreen, | ||
466 | &xdg_shell_view->request_fullscreen); | ||
467 | |||
468 | xdg_shell_view->request_maximize.notify = handle_request_maximize; | 495 | xdg_shell_view->request_maximize.notify = handle_request_maximize; |
469 | wl_signal_add(&xdg_surface->toplevel->events.request_maximize, | 496 | wl_signal_add(&toplevel->events.request_maximize, |
470 | &xdg_shell_view->request_maximize); | 497 | &xdg_shell_view->request_maximize); |
471 | 498 | ||
499 | xdg_shell_view->request_fullscreen.notify = handle_request_fullscreen; | ||
500 | wl_signal_add(&toplevel->events.request_fullscreen, | ||
501 | &xdg_shell_view->request_fullscreen); | ||
502 | |||
472 | xdg_shell_view->request_move.notify = handle_request_move; | 503 | xdg_shell_view->request_move.notify = handle_request_move; |
473 | wl_signal_add(&xdg_surface->toplevel->events.request_move, | 504 | wl_signal_add(&toplevel->events.request_move, |
474 | &xdg_shell_view->request_move); | 505 | &xdg_shell_view->request_move); |
475 | 506 | ||
476 | xdg_shell_view->request_resize.notify = handle_request_resize; | 507 | xdg_shell_view->request_resize.notify = handle_request_resize; |
477 | wl_signal_add(&xdg_surface->toplevel->events.request_resize, | 508 | wl_signal_add(&toplevel->events.request_resize, |
478 | &xdg_shell_view->request_resize); | 509 | &xdg_shell_view->request_resize); |
479 | 510 | ||
480 | xdg_shell_view->set_title.notify = handle_set_title; | 511 | xdg_shell_view->set_title.notify = handle_set_title; |
481 | wl_signal_add(&xdg_surface->toplevel->events.set_title, | 512 | wl_signal_add(&toplevel->events.set_title, |
482 | &xdg_shell_view->set_title); | 513 | &xdg_shell_view->set_title); |
483 | 514 | ||
484 | xdg_shell_view->set_app_id.notify = handle_set_app_id; | 515 | xdg_shell_view->set_app_id.notify = handle_set_app_id; |
485 | wl_signal_add(&xdg_surface->toplevel->events.set_app_id, | 516 | wl_signal_add(&toplevel->events.set_app_id, |
486 | &xdg_shell_view->set_app_id); | 517 | &xdg_shell_view->set_app_id); |
487 | } | 518 | } |
488 | 519 | ||
@@ -496,7 +527,8 @@ static void handle_destroy(struct wl_listener *listener, void *data) { | |||
496 | wl_list_remove(&xdg_shell_view->destroy.link); | 527 | wl_list_remove(&xdg_shell_view->destroy.link); |
497 | wl_list_remove(&xdg_shell_view->map.link); | 528 | wl_list_remove(&xdg_shell_view->map.link); |
498 | wl_list_remove(&xdg_shell_view->unmap.link); | 529 | wl_list_remove(&xdg_shell_view->unmap.link); |
499 | view->wlr_xdg_surface = NULL; | 530 | wl_list_remove(&xdg_shell_view->commit.link); |
531 | view->wlr_xdg_toplevel = NULL; | ||
500 | if (view->xdg_decoration) { | 532 | if (view->xdg_decoration) { |
501 | view->xdg_decoration->view = NULL; | 533 | view->xdg_decoration->view = NULL; |
502 | } | 534 | } |
@@ -508,17 +540,12 @@ struct sway_view *view_from_wlr_xdg_surface( | |||
508 | return xdg_surface->data; | 540 | return xdg_surface->data; |
509 | } | 541 | } |
510 | 542 | ||
511 | void handle_xdg_shell_surface(struct wl_listener *listener, void *data) { | 543 | void handle_xdg_shell_toplevel(struct wl_listener *listener, void *data) { |
512 | struct wlr_xdg_surface *xdg_surface = data; | 544 | struct wlr_xdg_toplevel *xdg_toplevel = data; |
513 | |||
514 | if (xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP) { | ||
515 | sway_log(SWAY_DEBUG, "New xdg_shell popup"); | ||
516 | return; | ||
517 | } | ||
518 | 545 | ||
519 | sway_log(SWAY_DEBUG, "New xdg_shell toplevel title='%s' app_id='%s'", | 546 | sway_log(SWAY_DEBUG, "New xdg_shell toplevel title='%s' app_id='%s'", |
520 | xdg_surface->toplevel->title, xdg_surface->toplevel->app_id); | 547 | xdg_toplevel->title, xdg_toplevel->app_id); |
521 | wlr_xdg_surface_ping(xdg_surface); | 548 | wlr_xdg_surface_ping(xdg_toplevel->base); |
522 | 549 | ||
523 | struct sway_xdg_shell_view *xdg_shell_view = | 550 | struct sway_xdg_shell_view *xdg_shell_view = |
524 | calloc(1, sizeof(struct sway_xdg_shell_view)); | 551 | calloc(1, sizeof(struct sway_xdg_shell_view)); |
@@ -526,17 +553,29 @@ void handle_xdg_shell_surface(struct wl_listener *listener, void *data) { | |||
526 | return; | 553 | return; |
527 | } | 554 | } |
528 | 555 | ||
529 | view_init(&xdg_shell_view->view, SWAY_VIEW_XDG_SHELL, &view_impl); | 556 | if (!view_init(&xdg_shell_view->view, SWAY_VIEW_XDG_SHELL, &view_impl)) { |
530 | xdg_shell_view->view.wlr_xdg_surface = xdg_surface; | 557 | free(xdg_shell_view); |
558 | return; | ||
559 | } | ||
560 | xdg_shell_view->view.wlr_xdg_toplevel = xdg_toplevel; | ||
531 | 561 | ||
532 | xdg_shell_view->map.notify = handle_map; | 562 | xdg_shell_view->map.notify = handle_map; |
533 | wl_signal_add(&xdg_surface->events.map, &xdg_shell_view->map); | 563 | wl_signal_add(&xdg_toplevel->base->surface->events.map, &xdg_shell_view->map); |
534 | 564 | ||
535 | xdg_shell_view->unmap.notify = handle_unmap; | 565 | xdg_shell_view->unmap.notify = handle_unmap; |
536 | wl_signal_add(&xdg_surface->events.unmap, &xdg_shell_view->unmap); | 566 | wl_signal_add(&xdg_toplevel->base->surface->events.unmap, &xdg_shell_view->unmap); |
567 | |||
568 | xdg_shell_view->commit.notify = handle_commit; | ||
569 | wl_signal_add(&xdg_toplevel->base->surface->events.commit, | ||
570 | &xdg_shell_view->commit); | ||
537 | 571 | ||
538 | xdg_shell_view->destroy.notify = handle_destroy; | 572 | xdg_shell_view->destroy.notify = handle_destroy; |
539 | wl_signal_add(&xdg_surface->events.destroy, &xdg_shell_view->destroy); | 573 | wl_signal_add(&xdg_toplevel->events.destroy, &xdg_shell_view->destroy); |
574 | |||
575 | wlr_scene_xdg_surface_create(xdg_shell_view->view.content_tree, xdg_toplevel->base); | ||
576 | |||
577 | xdg_toplevel->base->data = xdg_shell_view; | ||
540 | 578 | ||
541 | xdg_surface->data = xdg_shell_view; | 579 | wlr_xdg_toplevel_set_wm_capabilities(xdg_toplevel, |
580 | XDG_TOPLEVEL_WM_CAPABILITIES_FULLSCREEN); | ||
542 | } | 581 | } |
diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c index e1a2e463..270cf08f 100644 --- a/sway/desktop/xwayland.c +++ b/sway/desktop/xwayland.c | |||
@@ -1,20 +1,23 @@ | |||
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> |
5 | #include <wayland-server-core.h> | 4 | #include <wayland-server-core.h> |
6 | #include <wlr/types/wlr_output_layout.h> | 5 | #include <wlr/types/wlr_output_layout.h> |
7 | #include <wlr/types/wlr_output.h> | 6 | #include <wlr/types/wlr_output.h> |
7 | #include <wlr/types/wlr_xdg_activation_v1.h> | ||
8 | #include <wlr/types/wlr_scene.h> | ||
8 | #include <wlr/xwayland.h> | 9 | #include <wlr/xwayland.h> |
10 | #include <xcb/xcb_icccm.h> | ||
9 | #include "log.h" | 11 | #include "log.h" |
10 | #include "sway/desktop.h" | ||
11 | #include "sway/desktop/transaction.h" | 12 | #include "sway/desktop/transaction.h" |
12 | #include "sway/input/cursor.h" | 13 | #include "sway/input/cursor.h" |
13 | #include "sway/input/input-manager.h" | 14 | #include "sway/input/input-manager.h" |
14 | #include "sway/input/seat.h" | 15 | #include "sway/input/seat.h" |
15 | #include "sway/output.h" | 16 | #include "sway/output.h" |
17 | #include "sway/scene_descriptor.h" | ||
16 | #include "sway/tree/arrange.h" | 18 | #include "sway/tree/arrange.h" |
17 | #include "sway/tree/container.h" | 19 | #include "sway/tree/container.h" |
20 | #include "sway/server.h" | ||
18 | #include "sway/tree/view.h" | 21 | #include "sway/tree/view.h" |
19 | #include "sway/tree/workspace.h" | 22 | #include "sway/tree/workspace.h" |
20 | 23 | ||
@@ -42,29 +45,12 @@ static void unmanaged_handle_request_configure(struct wl_listener *listener, | |||
42 | ev->width, ev->height); | 45 | ev->width, ev->height); |
43 | } | 46 | } |
44 | 47 | ||
45 | static void unmanaged_handle_commit(struct wl_listener *listener, void *data) { | ||
46 | struct sway_xwayland_unmanaged *surface = | ||
47 | wl_container_of(listener, surface, commit); | ||
48 | struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface; | ||
49 | |||
50 | desktop_damage_surface(xsurface->surface, surface->lx, surface->ly, | ||
51 | false); | ||
52 | } | ||
53 | |||
54 | static void unmanaged_handle_set_geometry(struct wl_listener *listener, void *data) { | 48 | static void unmanaged_handle_set_geometry(struct wl_listener *listener, void *data) { |
55 | struct sway_xwayland_unmanaged *surface = | 49 | struct sway_xwayland_unmanaged *surface = |
56 | wl_container_of(listener, surface, set_geometry); | 50 | wl_container_of(listener, surface, set_geometry); |
57 | struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface; | 51 | struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface; |
58 | 52 | ||
59 | if (xsurface->x != surface->lx || xsurface->y != surface->ly) { | 53 | wlr_scene_node_set_position(&surface->surface_scene->buffer->node, xsurface->x, xsurface->y); |
60 | // Surface has moved | ||
61 | desktop_damage_surface(xsurface->surface, surface->lx, surface->ly, | ||
62 | true); | ||
63 | surface->lx = xsurface->x; | ||
64 | surface->ly = xsurface->y; | ||
65 | desktop_damage_surface(xsurface->surface, surface->lx, surface->ly, | ||
66 | true); | ||
67 | } | ||
68 | } | 54 | } |
69 | 55 | ||
70 | static void unmanaged_handle_map(struct wl_listener *listener, void *data) { | 56 | static void unmanaged_handle_map(struct wl_listener *listener, void *data) { |
@@ -72,17 +58,18 @@ static void unmanaged_handle_map(struct wl_listener *listener, void *data) { | |||
72 | wl_container_of(listener, surface, map); | 58 | wl_container_of(listener, surface, map); |
73 | struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface; | 59 | struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface; |
74 | 60 | ||
75 | wl_list_insert(root->xwayland_unmanaged.prev, &surface->link); | 61 | surface->surface_scene = wlr_scene_surface_create(root->layers.unmanaged, |
76 | 62 | xsurface->surface); | |
77 | wl_signal_add(&xsurface->events.set_geometry, &surface->set_geometry); | ||
78 | surface->set_geometry.notify = unmanaged_handle_set_geometry; | ||
79 | 63 | ||
80 | wl_signal_add(&xsurface->surface->events.commit, &surface->commit); | 64 | if (surface->surface_scene) { |
81 | surface->commit.notify = unmanaged_handle_commit; | 65 | scene_descriptor_assign(&surface->surface_scene->buffer->node, |
66 | SWAY_SCENE_DESC_XWAYLAND_UNMANAGED, surface); | ||
67 | wlr_scene_node_set_position(&surface->surface_scene->buffer->node, | ||
68 | xsurface->x, xsurface->y); | ||
82 | 69 | ||
83 | surface->lx = xsurface->x; | 70 | wl_signal_add(&xsurface->events.set_geometry, &surface->set_geometry); |
84 | surface->ly = xsurface->y; | 71 | surface->set_geometry.notify = unmanaged_handle_set_geometry; |
85 | desktop_damage_surface(xsurface->surface, surface->lx, surface->ly, true); | 72 | } |
86 | 73 | ||
87 | if (wlr_xwayland_or_surface_wants_focus(xsurface)) { | 74 | if (wlr_xwayland_or_surface_wants_focus(xsurface)) { |
88 | struct sway_seat *seat = input_manager_current_seat(); | 75 | struct sway_seat *seat = input_manager_current_seat(); |
@@ -96,23 +83,22 @@ static void unmanaged_handle_unmap(struct wl_listener *listener, void *data) { | |||
96 | struct sway_xwayland_unmanaged *surface = | 83 | struct sway_xwayland_unmanaged *surface = |
97 | wl_container_of(listener, surface, unmap); | 84 | wl_container_of(listener, surface, unmap); |
98 | struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface; | 85 | struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface; |
99 | desktop_damage_surface(xsurface->surface, xsurface->x, xsurface->y, true); | 86 | |
100 | wl_list_remove(&surface->link); | 87 | if (surface->surface_scene) { |
101 | wl_list_remove(&surface->set_geometry.link); | 88 | wl_list_remove(&surface->set_geometry.link); |
102 | wl_list_remove(&surface->commit.link); | 89 | |
90 | wlr_scene_node_destroy(&surface->surface_scene->buffer->node); | ||
91 | surface->surface_scene = NULL; | ||
92 | } | ||
103 | 93 | ||
104 | struct sway_seat *seat = input_manager_current_seat(); | 94 | struct sway_seat *seat = input_manager_current_seat(); |
105 | if (seat->wlr_seat->keyboard_state.focused_surface == xsurface->surface) { | 95 | if (seat->wlr_seat->keyboard_state.focused_surface == xsurface->surface) { |
106 | // This simply returns focus to the parent surface if there's one available. | 96 | // This simply returns focus to the parent surface if there's one available. |
107 | // This seems to handle JetBrains issues. | 97 | // This seems to handle JetBrains issues. |
108 | if (xsurface->parent && xsurface->parent->surface && | 98 | if (xsurface->parent && xsurface->parent->surface |
109 | wlr_surface_is_xwayland_surface(xsurface->parent->surface)) { | 99 | && wlr_xwayland_or_surface_wants_focus(xsurface->parent)) { |
110 | struct wlr_xwayland_surface *next_surface = | 100 | seat_set_focus_surface(seat, xsurface->parent->surface, false); |
111 | wlr_xwayland_surface_from_wlr_surface(xsurface->parent->surface); | 101 | return; |
112 | if (wlr_xwayland_or_surface_wants_focus(next_surface)) { | ||
113 | seat_set_focus_surface(seat, xsurface->parent->surface, false); | ||
114 | return; | ||
115 | } | ||
116 | } | 102 | } |
117 | 103 | ||
118 | // Restore focus | 104 | // Restore focus |
@@ -125,18 +111,53 @@ static void unmanaged_handle_unmap(struct wl_listener *listener, void *data) { | |||
125 | } | 111 | } |
126 | } | 112 | } |
127 | 113 | ||
114 | static void unmanaged_handle_request_activate(struct wl_listener *listener, void *data) { | ||
115 | struct sway_xwayland_unmanaged *surface = | ||
116 | wl_container_of(listener, surface, request_activate); | ||
117 | struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface; | ||
118 | if (xsurface->surface == NULL || !xsurface->surface->mapped) { | ||
119 | return; | ||
120 | } | ||
121 | struct sway_seat *seat = input_manager_current_seat(); | ||
122 | struct sway_container *focus = seat_get_focused_container(seat); | ||
123 | if (focus && focus->view && focus->view->pid != xsurface->pid) { | ||
124 | return; | ||
125 | } | ||
126 | |||
127 | seat_set_focus_surface(seat, xsurface->surface, false); | ||
128 | } | ||
129 | |||
130 | static void unmanaged_handle_associate(struct wl_listener *listener, void *data) { | ||
131 | struct sway_xwayland_unmanaged *surface = | ||
132 | wl_container_of(listener, surface, associate); | ||
133 | struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface; | ||
134 | wl_signal_add(&xsurface->surface->events.map, &surface->map); | ||
135 | surface->map.notify = unmanaged_handle_map; | ||
136 | wl_signal_add(&xsurface->surface->events.unmap, &surface->unmap); | ||
137 | surface->unmap.notify = unmanaged_handle_unmap; | ||
138 | } | ||
139 | |||
140 | static void unmanaged_handle_dissociate(struct wl_listener *listener, void *data) { | ||
141 | struct sway_xwayland_unmanaged *surface = | ||
142 | wl_container_of(listener, surface, dissociate); | ||
143 | wl_list_remove(&surface->map.link); | ||
144 | wl_list_remove(&surface->unmap.link); | ||
145 | } | ||
146 | |||
128 | static void unmanaged_handle_destroy(struct wl_listener *listener, void *data) { | 147 | static void unmanaged_handle_destroy(struct wl_listener *listener, void *data) { |
129 | struct sway_xwayland_unmanaged *surface = | 148 | struct sway_xwayland_unmanaged *surface = |
130 | wl_container_of(listener, surface, destroy); | 149 | wl_container_of(listener, surface, destroy); |
131 | wl_list_remove(&surface->request_configure.link); | 150 | wl_list_remove(&surface->request_configure.link); |
132 | wl_list_remove(&surface->map.link); | 151 | wl_list_remove(&surface->associate.link); |
133 | wl_list_remove(&surface->unmap.link); | 152 | wl_list_remove(&surface->dissociate.link); |
134 | wl_list_remove(&surface->destroy.link); | 153 | wl_list_remove(&surface->destroy.link); |
135 | wl_list_remove(&surface->override_redirect.link); | 154 | wl_list_remove(&surface->override_redirect.link); |
155 | wl_list_remove(&surface->request_activate.link); | ||
136 | free(surface); | 156 | free(surface); |
137 | } | 157 | } |
138 | 158 | ||
139 | static void handle_map(struct wl_listener *listener, void *data); | 159 | static void handle_map(struct wl_listener *listener, void *data); |
160 | static void handle_associate(struct wl_listener *listener, void *data); | ||
140 | 161 | ||
141 | struct sway_xwayland_view *create_xwayland_view(struct wlr_xwayland_surface *xsurface); | 162 | struct sway_xwayland_view *create_xwayland_view(struct wlr_xwayland_surface *xsurface); |
142 | 163 | ||
@@ -145,14 +166,22 @@ static void unmanaged_handle_override_redirect(struct wl_listener *listener, voi | |||
145 | wl_container_of(listener, surface, override_redirect); | 166 | wl_container_of(listener, surface, override_redirect); |
146 | struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface; | 167 | struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface; |
147 | 168 | ||
148 | bool mapped = xsurface->mapped; | 169 | bool associated = xsurface->surface != NULL; |
170 | bool mapped = associated && xsurface->surface->mapped; | ||
149 | if (mapped) { | 171 | if (mapped) { |
150 | unmanaged_handle_unmap(&surface->unmap, NULL); | 172 | unmanaged_handle_unmap(&surface->unmap, NULL); |
151 | } | 173 | } |
174 | if (associated) { | ||
175 | unmanaged_handle_dissociate(&surface->dissociate, NULL); | ||
176 | } | ||
152 | 177 | ||
153 | unmanaged_handle_destroy(&surface->destroy, NULL); | 178 | unmanaged_handle_destroy(&surface->destroy, NULL); |
154 | xsurface->data = NULL; | 179 | xsurface->data = NULL; |
180 | |||
155 | struct sway_xwayland_view *xwayland_view = create_xwayland_view(xsurface); | 181 | struct sway_xwayland_view *xwayland_view = create_xwayland_view(xsurface); |
182 | if (associated) { | ||
183 | handle_associate(&xwayland_view->associate, NULL); | ||
184 | } | ||
156 | if (mapped) { | 185 | if (mapped) { |
157 | handle_map(&xwayland_view->map, xsurface); | 186 | handle_map(&xwayland_view->map, xsurface); |
158 | } | 187 | } |
@@ -172,14 +201,16 @@ static struct sway_xwayland_unmanaged *create_unmanaged( | |||
172 | wl_signal_add(&xsurface->events.request_configure, | 201 | wl_signal_add(&xsurface->events.request_configure, |
173 | &surface->request_configure); | 202 | &surface->request_configure); |
174 | surface->request_configure.notify = unmanaged_handle_request_configure; | 203 | surface->request_configure.notify = unmanaged_handle_request_configure; |
175 | wl_signal_add(&xsurface->events.map, &surface->map); | 204 | wl_signal_add(&xsurface->events.associate, &surface->associate); |
176 | surface->map.notify = unmanaged_handle_map; | 205 | surface->associate.notify = unmanaged_handle_associate; |
177 | wl_signal_add(&xsurface->events.unmap, &surface->unmap); | 206 | wl_signal_add(&xsurface->events.dissociate, &surface->dissociate); |
178 | surface->unmap.notify = unmanaged_handle_unmap; | 207 | surface->dissociate.notify = unmanaged_handle_dissociate; |
179 | wl_signal_add(&xsurface->events.destroy, &surface->destroy); | 208 | wl_signal_add(&xsurface->events.destroy, &surface->destroy); |
180 | surface->destroy.notify = unmanaged_handle_destroy; | 209 | surface->destroy.notify = unmanaged_handle_destroy; |
181 | wl_signal_add(&xsurface->events.set_override_redirect, &surface->override_redirect); | 210 | wl_signal_add(&xsurface->events.set_override_redirect, &surface->override_redirect); |
182 | surface->override_redirect.notify = unmanaged_handle_override_redirect; | 211 | surface->override_redirect.notify = unmanaged_handle_override_redirect; |
212 | wl_signal_add(&xsurface->events.request_activate, &surface->request_activate); | ||
213 | surface->request_activate.notify = unmanaged_handle_request_activate; | ||
183 | 214 | ||
184 | return surface; | 215 | return surface; |
185 | } | 216 | } |
@@ -258,6 +289,7 @@ static void set_activated(struct sway_view *view, bool activated) { | |||
258 | } | 289 | } |
259 | 290 | ||
260 | wlr_xwayland_surface_activate(surface, activated); | 291 | wlr_xwayland_surface_activate(surface, activated); |
292 | wlr_xwayland_surface_restack(surface, NULL, XCB_STACK_MODE_ABOVE); | ||
261 | } | 293 | } |
262 | 294 | ||
263 | static void set_tiled(struct sway_view *view, bool tiled) { | 295 | static void set_tiled(struct sway_view *view, bool tiled) { |
@@ -297,7 +329,7 @@ static bool wants_floating(struct sway_view *view) { | |||
297 | } | 329 | } |
298 | } | 330 | } |
299 | 331 | ||
300 | struct wlr_xwayland_surface_size_hints *size_hints = surface->size_hints; | 332 | xcb_size_hints_t *size_hints = surface->size_hints; |
301 | if (size_hints != NULL && | 333 | if (size_hints != NULL && |
302 | size_hints->min_width > 0 && size_hints->min_height > 0 && | 334 | size_hints->min_width > 0 && size_hints->min_height > 0 && |
303 | (size_hints->max_width == size_hints->min_width || | 335 | (size_hints->max_width == size_hints->min_width || |
@@ -351,7 +383,7 @@ static void destroy(struct sway_view *view) { | |||
351 | static void get_constraints(struct sway_view *view, double *min_width, | 383 | static void get_constraints(struct sway_view *view, double *min_width, |
352 | double *max_width, double *min_height, double *max_height) { | 384 | double *max_width, double *min_height, double *max_height) { |
353 | struct wlr_xwayland_surface *surface = view->wlr_xwayland_surface; | 385 | struct wlr_xwayland_surface *surface = view->wlr_xwayland_surface; |
354 | struct wlr_xwayland_surface_size_hints *size_hints = surface->size_hints; | 386 | xcb_size_hints_t *size_hints = surface->size_hints; |
355 | 387 | ||
356 | if (size_hints == NULL) { | 388 | if (size_hints == NULL) { |
357 | *min_width = DBL_MIN; | 389 | *min_width = DBL_MIN; |
@@ -381,17 +413,6 @@ static const struct sway_view_impl view_impl = { | |||
381 | .destroy = destroy, | 413 | .destroy = destroy, |
382 | }; | 414 | }; |
383 | 415 | ||
384 | static void get_geometry(struct sway_view *view, struct wlr_box *box) { | ||
385 | box->x = box->y = 0; | ||
386 | if (view->surface) { | ||
387 | box->width = view->surface->current.width; | ||
388 | box->height = view->surface->current.height; | ||
389 | } else { | ||
390 | box->width = 0; | ||
391 | box->height = 0; | ||
392 | } | ||
393 | } | ||
394 | |||
395 | static void handle_commit(struct wl_listener *listener, void *data) { | 416 | static void handle_commit(struct wl_listener *listener, void *data) { |
396 | struct sway_xwayland_view *xwayland_view = | 417 | struct sway_xwayland_view *xwayland_view = |
397 | wl_container_of(listener, xwayland_view, commit); | 418 | wl_container_of(listener, xwayland_view, commit); |
@@ -399,33 +420,38 @@ static void handle_commit(struct wl_listener *listener, void *data) { | |||
399 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; | 420 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; |
400 | struct wlr_surface_state *state = &xsurface->surface->current; | 421 | struct wlr_surface_state *state = &xsurface->surface->current; |
401 | 422 | ||
423 | struct wlr_box new_geo = {0}; | ||
424 | new_geo.width = state->width; | ||
425 | new_geo.height = state->height; | ||
426 | |||
427 | bool new_size = new_geo.width != view->geometry.width || | ||
428 | new_geo.height != view->geometry.height; | ||
429 | |||
430 | if (new_size) { | ||
431 | // The client changed its surface size in this commit. For floating | ||
432 | // containers, we resize the container to match. For tiling containers, | ||
433 | // we only recenter the surface. | ||
434 | memcpy(&view->geometry, &new_geo, sizeof(struct wlr_box)); | ||
435 | if (container_is_floating(view->container)) { | ||
436 | view_update_size(view); | ||
437 | transaction_commit_dirty_client(); | ||
438 | } | ||
439 | |||
440 | view_center_and_clip_surface(view); | ||
441 | } | ||
442 | |||
402 | if (view->container->node.instruction) { | 443 | if (view->container->node.instruction) { |
403 | get_geometry(view, &view->geometry); | 444 | bool successful = transaction_notify_view_ready_by_geometry(view, |
404 | transaction_notify_view_ready_by_geometry(view, | ||
405 | xsurface->x, xsurface->y, state->width, state->height); | 445 | xsurface->x, xsurface->y, state->width, state->height); |
406 | } else { | 446 | |
407 | struct wlr_box new_geo; | 447 | // If we saved the view and this commit isn't what we're looking for |
408 | get_geometry(view, &new_geo); | 448 | // that means the user will never actually see the buffers submitted to |
409 | 449 | // us here. Just send frame done events to these surfaces so they can | |
410 | if ((new_geo.width != view->geometry.width || | 450 | // commit another time for us. |
411 | new_geo.height != view->geometry.height || | 451 | if (view->saved_surface_tree && !successful) { |
412 | new_geo.x != view->geometry.x || | 452 | view_send_frame_done(view); |
413 | new_geo.y != view->geometry.y)) { | ||
414 | // The view has unexpectedly sent a new size | ||
415 | // eg. The Firefox "Save As" dialog when downloading a file | ||
416 | desktop_damage_view(view); | ||
417 | view_update_size(view, new_geo.width, new_geo.height); | ||
418 | memcpy(&view->geometry, &new_geo, sizeof(struct wlr_box)); | ||
419 | desktop_damage_view(view); | ||
420 | transaction_commit_dirty(); | ||
421 | transaction_notify_view_ready_by_geometry(view, | ||
422 | xsurface->x, xsurface->y, new_geo.width, new_geo.height); | ||
423 | } else { | ||
424 | memcpy(&view->geometry, &new_geo, sizeof(struct wlr_box)); | ||
425 | } | 453 | } |
426 | } | 454 | } |
427 | |||
428 | view_damage_from(view); | ||
429 | } | 455 | } |
430 | 456 | ||
431 | static void handle_destroy(struct wl_listener *listener, void *data) { | 457 | static void handle_destroy(struct wl_listener *listener, void *data) { |
@@ -438,6 +464,8 @@ static void handle_destroy(struct wl_listener *listener, void *data) { | |||
438 | wl_list_remove(&xwayland_view->commit.link); | 464 | wl_list_remove(&xwayland_view->commit.link); |
439 | } | 465 | } |
440 | 466 | ||
467 | xwayland_view->view.wlr_xwayland_surface = NULL; | ||
468 | |||
441 | wl_list_remove(&xwayland_view->destroy.link); | 469 | wl_list_remove(&xwayland_view->destroy.link); |
442 | wl_list_remove(&xwayland_view->request_configure.link); | 470 | wl_list_remove(&xwayland_view->request_configure.link); |
443 | wl_list_remove(&xwayland_view->request_fullscreen.link); | 471 | wl_list_remove(&xwayland_view->request_fullscreen.link); |
@@ -448,11 +476,12 @@ static void handle_destroy(struct wl_listener *listener, void *data) { | |||
448 | wl_list_remove(&xwayland_view->set_title.link); | 476 | wl_list_remove(&xwayland_view->set_title.link); |
449 | wl_list_remove(&xwayland_view->set_class.link); | 477 | wl_list_remove(&xwayland_view->set_class.link); |
450 | wl_list_remove(&xwayland_view->set_role.link); | 478 | wl_list_remove(&xwayland_view->set_role.link); |
479 | wl_list_remove(&xwayland_view->set_startup_id.link); | ||
451 | wl_list_remove(&xwayland_view->set_window_type.link); | 480 | wl_list_remove(&xwayland_view->set_window_type.link); |
452 | wl_list_remove(&xwayland_view->set_hints.link); | 481 | wl_list_remove(&xwayland_view->set_hints.link); |
453 | wl_list_remove(&xwayland_view->set_decorations.link); | 482 | wl_list_remove(&xwayland_view->set_decorations.link); |
454 | wl_list_remove(&xwayland_view->map.link); | 483 | wl_list_remove(&xwayland_view->associate.link); |
455 | wl_list_remove(&xwayland_view->unmap.link); | 484 | wl_list_remove(&xwayland_view->dissociate.link); |
456 | wl_list_remove(&xwayland_view->override_redirect.link); | 485 | wl_list_remove(&xwayland_view->override_redirect.link); |
457 | view_begin_destroy(&xwayland_view->view); | 486 | view_begin_destroy(&xwayland_view->view); |
458 | } | 487 | } |
@@ -466,16 +495,28 @@ static void handle_unmap(struct wl_listener *listener, void *data) { | |||
466 | return; | 495 | return; |
467 | } | 496 | } |
468 | 497 | ||
498 | wl_list_remove(&xwayland_view->commit.link); | ||
499 | wl_list_remove(&xwayland_view->surface_tree_destroy.link); | ||
500 | |||
501 | if (xwayland_view->surface_tree) { | ||
502 | wlr_scene_node_destroy(&xwayland_view->surface_tree->node); | ||
503 | xwayland_view->surface_tree = NULL; | ||
504 | } | ||
505 | |||
469 | view_unmap(view); | 506 | view_unmap(view); |
507 | } | ||
470 | 508 | ||
471 | wl_list_remove(&xwayland_view->commit.link); | 509 | static void handle_surface_tree_destroy(struct wl_listener *listener, void *data) { |
510 | struct sway_xwayland_view *xwayland_view = wl_container_of(listener, xwayland_view, | ||
511 | surface_tree_destroy); | ||
512 | xwayland_view->surface_tree = NULL; | ||
472 | } | 513 | } |
473 | 514 | ||
474 | static void handle_map(struct wl_listener *listener, void *data) { | 515 | static void handle_map(struct wl_listener *listener, void *data) { |
475 | struct sway_xwayland_view *xwayland_view = | 516 | struct sway_xwayland_view *xwayland_view = |
476 | wl_container_of(listener, xwayland_view, map); | 517 | wl_container_of(listener, xwayland_view, map); |
477 | struct wlr_xwayland_surface *xsurface = data; | ||
478 | struct sway_view *view = &xwayland_view->view; | 518 | struct sway_view *view = &xwayland_view->view; |
519 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; | ||
479 | 520 | ||
480 | view->natural_width = xsurface->width; | 521 | view->natural_width = xsurface->width; |
481 | view->natural_height = xsurface->height; | 522 | view->natural_height = xsurface->height; |
@@ -488,23 +529,42 @@ static void handle_map(struct wl_listener *listener, void *data) { | |||
488 | // Put it back into the tree | 529 | // Put it back into the tree |
489 | view_map(view, xsurface->surface, xsurface->fullscreen, NULL, false); | 530 | view_map(view, xsurface->surface, xsurface->fullscreen, NULL, false); |
490 | 531 | ||
532 | xwayland_view->surface_tree = wlr_scene_subsurface_tree_create( | ||
533 | xwayland_view->view.content_tree, xsurface->surface); | ||
534 | |||
535 | if (xwayland_view->surface_tree) { | ||
536 | xwayland_view->surface_tree_destroy.notify = handle_surface_tree_destroy; | ||
537 | wl_signal_add(&xwayland_view->surface_tree->node.events.destroy, | ||
538 | &xwayland_view->surface_tree_destroy); | ||
539 | } | ||
540 | |||
491 | transaction_commit_dirty(); | 541 | transaction_commit_dirty(); |
492 | } | 542 | } |
493 | 543 | ||
544 | static void handle_dissociate(struct wl_listener *listener, void *data); | ||
545 | |||
494 | static void handle_override_redirect(struct wl_listener *listener, void *data) { | 546 | static void handle_override_redirect(struct wl_listener *listener, void *data) { |
495 | struct sway_xwayland_view *xwayland_view = | 547 | struct sway_xwayland_view *xwayland_view = |
496 | wl_container_of(listener, xwayland_view, override_redirect); | 548 | wl_container_of(listener, xwayland_view, override_redirect); |
497 | struct wlr_xwayland_surface *xsurface = data; | ||
498 | struct sway_view *view = &xwayland_view->view; | 549 | struct sway_view *view = &xwayland_view->view; |
550 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; | ||
499 | 551 | ||
500 | bool mapped = xsurface->mapped; | 552 | bool associated = xsurface->surface != NULL; |
553 | bool mapped = associated && xsurface->surface->mapped; | ||
501 | if (mapped) { | 554 | if (mapped) { |
502 | handle_unmap(&xwayland_view->unmap, NULL); | 555 | handle_unmap(&xwayland_view->unmap, NULL); |
503 | } | 556 | } |
557 | if (associated) { | ||
558 | handle_dissociate(&xwayland_view->dissociate, NULL); | ||
559 | } | ||
504 | 560 | ||
505 | handle_destroy(&xwayland_view->destroy, view); | 561 | handle_destroy(&xwayland_view->destroy, view); |
506 | xsurface->data = NULL; | 562 | xsurface->data = NULL; |
563 | |||
507 | struct sway_xwayland_unmanaged *unmanaged = create_unmanaged(xsurface); | 564 | struct sway_xwayland_unmanaged *unmanaged = create_unmanaged(xsurface); |
565 | if (associated) { | ||
566 | unmanaged_handle_associate(&unmanaged->associate, NULL); | ||
567 | } | ||
508 | if (mapped) { | 568 | if (mapped) { |
509 | unmanaged_handle_map(&unmanaged->map, xsurface); | 569 | unmanaged_handle_map(&unmanaged->map, xsurface); |
510 | } | 570 | } |
@@ -516,7 +576,7 @@ static void handle_request_configure(struct wl_listener *listener, void *data) { | |||
516 | struct wlr_xwayland_surface_configure_event *ev = data; | 576 | struct wlr_xwayland_surface_configure_event *ev = data; |
517 | struct sway_view *view = &xwayland_view->view; | 577 | struct sway_view *view = &xwayland_view->view; |
518 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; | 578 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; |
519 | if (!xsurface->mapped) { | 579 | if (xsurface->surface == NULL || !xsurface->surface->mapped) { |
520 | wlr_xwayland_surface_configure(xsurface, ev->x, ev->y, | 580 | wlr_xwayland_surface_configure(xsurface, ev->x, ev->y, |
521 | ev->width, ev->height); | 581 | ev->width, ev->height); |
522 | return; | 582 | return; |
@@ -527,10 +587,10 @@ static void handle_request_configure(struct wl_listener *listener, void *data) { | |||
527 | view->natural_height = ev->height; | 587 | view->natural_height = ev->height; |
528 | container_floating_resize_and_center(view->container); | 588 | container_floating_resize_and_center(view->container); |
529 | 589 | ||
530 | configure(view, view->container->content_x, | 590 | configure(view, view->container->pending.content_x, |
531 | view->container->content_y, | 591 | view->container->pending.content_y, |
532 | view->container->content_width, | 592 | view->container->pending.content_width, |
533 | view->container->content_height); | 593 | view->container->pending.content_height); |
534 | node_set_dirty(&view->container->node); | 594 | node_set_dirty(&view->container->node); |
535 | } else { | 595 | } else { |
536 | configure(view, view->container->current.content_x, | 596 | configure(view, view->container->current.content_x, |
@@ -545,7 +605,7 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data) | |||
545 | wl_container_of(listener, xwayland_view, request_fullscreen); | 605 | wl_container_of(listener, xwayland_view, request_fullscreen); |
546 | struct sway_view *view = &xwayland_view->view; | 606 | struct sway_view *view = &xwayland_view->view; |
547 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; | 607 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; |
548 | if (!xsurface->mapped) { | 608 | if (xsurface->surface == NULL || !xsurface->surface->mapped) { |
549 | return; | 609 | return; |
550 | } | 610 | } |
551 | container_set_fullscreen(view->container, xsurface->fullscreen); | 611 | container_set_fullscreen(view->container, xsurface->fullscreen); |
@@ -559,7 +619,7 @@ static void handle_request_minimize(struct wl_listener *listener, void *data) { | |||
559 | wl_container_of(listener, xwayland_view, request_minimize); | 619 | wl_container_of(listener, xwayland_view, request_minimize); |
560 | struct sway_view *view = &xwayland_view->view; | 620 | struct sway_view *view = &xwayland_view->view; |
561 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; | 621 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; |
562 | if (!xsurface->mapped) { | 622 | if (xsurface->surface == NULL || !xsurface->surface->mapped) { |
563 | return; | 623 | return; |
564 | } | 624 | } |
565 | 625 | ||
@@ -574,10 +634,11 @@ static void handle_request_move(struct wl_listener *listener, void *data) { | |||
574 | wl_container_of(listener, xwayland_view, request_move); | 634 | wl_container_of(listener, xwayland_view, request_move); |
575 | struct sway_view *view = &xwayland_view->view; | 635 | struct sway_view *view = &xwayland_view->view; |
576 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; | 636 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; |
577 | if (!xsurface->mapped) { | 637 | if (xsurface->surface == NULL || !xsurface->surface->mapped) { |
578 | return; | 638 | return; |
579 | } | 639 | } |
580 | if (!container_is_floating(view->container)) { | 640 | if (!container_is_floating(view->container) || |
641 | view->container->pending.fullscreen_mode) { | ||
581 | return; | 642 | return; |
582 | } | 643 | } |
583 | struct sway_seat *seat = input_manager_current_seat(); | 644 | struct sway_seat *seat = input_manager_current_seat(); |
@@ -589,7 +650,7 @@ static void handle_request_resize(struct wl_listener *listener, void *data) { | |||
589 | wl_container_of(listener, xwayland_view, request_resize); | 650 | wl_container_of(listener, xwayland_view, request_resize); |
590 | struct sway_view *view = &xwayland_view->view; | 651 | struct sway_view *view = &xwayland_view->view; |
591 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; | 652 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; |
592 | if (!xsurface->mapped) { | 653 | if (xsurface->surface == NULL || !xsurface->surface->mapped) { |
593 | return; | 654 | return; |
594 | } | 655 | } |
595 | if (!container_is_floating(view->container)) { | 656 | if (!container_is_floating(view->container)) { |
@@ -605,10 +666,10 @@ static void handle_request_activate(struct wl_listener *listener, void *data) { | |||
605 | wl_container_of(listener, xwayland_view, request_activate); | 666 | wl_container_of(listener, xwayland_view, request_activate); |
606 | struct sway_view *view = &xwayland_view->view; | 667 | struct sway_view *view = &xwayland_view->view; |
607 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; | 668 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; |
608 | if (!xsurface->mapped) { | 669 | if (xsurface->surface == NULL || !xsurface->surface->mapped) { |
609 | return; | 670 | return; |
610 | } | 671 | } |
611 | view_request_activate(view); | 672 | view_request_activate(view, NULL); |
612 | 673 | ||
613 | transaction_commit_dirty(); | 674 | transaction_commit_dirty(); |
614 | } | 675 | } |
@@ -618,7 +679,7 @@ static void handle_set_title(struct wl_listener *listener, void *data) { | |||
618 | wl_container_of(listener, xwayland_view, set_title); | 679 | wl_container_of(listener, xwayland_view, set_title); |
619 | struct sway_view *view = &xwayland_view->view; | 680 | struct sway_view *view = &xwayland_view->view; |
620 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; | 681 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; |
621 | if (!xsurface->mapped) { | 682 | if (xsurface->surface == NULL || !xsurface->surface->mapped) { |
622 | return; | 683 | return; |
623 | } | 684 | } |
624 | view_update_title(view, false); | 685 | view_update_title(view, false); |
@@ -630,7 +691,7 @@ static void handle_set_class(struct wl_listener *listener, void *data) { | |||
630 | wl_container_of(listener, xwayland_view, set_class); | 691 | wl_container_of(listener, xwayland_view, set_class); |
631 | struct sway_view *view = &xwayland_view->view; | 692 | struct sway_view *view = &xwayland_view->view; |
632 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; | 693 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; |
633 | if (!xsurface->mapped) { | 694 | if (xsurface->surface == NULL || !xsurface->surface->mapped) { |
634 | return; | 695 | return; |
635 | } | 696 | } |
636 | view_execute_criteria(view); | 697 | view_execute_criteria(view); |
@@ -641,18 +702,43 @@ static void handle_set_role(struct wl_listener *listener, void *data) { | |||
641 | wl_container_of(listener, xwayland_view, set_role); | 702 | wl_container_of(listener, xwayland_view, set_role); |
642 | struct sway_view *view = &xwayland_view->view; | 703 | struct sway_view *view = &xwayland_view->view; |
643 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; | 704 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; |
644 | if (!xsurface->mapped) { | 705 | if (xsurface->surface == NULL || !xsurface->surface->mapped) { |
645 | return; | 706 | return; |
646 | } | 707 | } |
647 | view_execute_criteria(view); | 708 | view_execute_criteria(view); |
648 | } | 709 | } |
649 | 710 | ||
711 | static void handle_set_startup_id(struct wl_listener *listener, void *data) { | ||
712 | struct sway_xwayland_view *xwayland_view = | ||
713 | wl_container_of(listener, xwayland_view, set_startup_id); | ||
714 | struct sway_view *view = &xwayland_view->view; | ||
715 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; | ||
716 | if (xsurface->startup_id == NULL) { | ||
717 | return; | ||
718 | } | ||
719 | |||
720 | struct wlr_xdg_activation_token_v1 *token = | ||
721 | wlr_xdg_activation_v1_find_token( | ||
722 | server.xdg_activation_v1, xsurface->startup_id); | ||
723 | if (token == NULL) { | ||
724 | // Tried to activate with an unknown or expired token | ||
725 | return; | ||
726 | } | ||
727 | |||
728 | struct launcher_ctx *ctx = token->data; | ||
729 | if (token->data == NULL) { | ||
730 | // TODO: support external launchers in X | ||
731 | return; | ||
732 | } | ||
733 | view_assign_ctx(view, ctx); | ||
734 | } | ||
735 | |||
650 | static void handle_set_window_type(struct wl_listener *listener, void *data) { | 736 | static void handle_set_window_type(struct wl_listener *listener, void *data) { |
651 | struct sway_xwayland_view *xwayland_view = | 737 | struct sway_xwayland_view *xwayland_view = |
652 | wl_container_of(listener, xwayland_view, set_window_type); | 738 | wl_container_of(listener, xwayland_view, set_window_type); |
653 | struct sway_view *view = &xwayland_view->view; | 739 | struct sway_view *view = &xwayland_view->view; |
654 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; | 740 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; |
655 | if (!xsurface->mapped) { | 741 | if (xsurface->surface == NULL || !xsurface->surface->mapped) { |
656 | return; | 742 | return; |
657 | } | 743 | } |
658 | view_execute_criteria(view); | 744 | view_execute_criteria(view); |
@@ -663,20 +749,39 @@ static void handle_set_hints(struct wl_listener *listener, void *data) { | |||
663 | wl_container_of(listener, xwayland_view, set_hints); | 749 | wl_container_of(listener, xwayland_view, set_hints); |
664 | struct sway_view *view = &xwayland_view->view; | 750 | struct sway_view *view = &xwayland_view->view; |
665 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; | 751 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; |
666 | if (!xsurface->mapped) { | 752 | if (xsurface->surface == NULL || !xsurface->surface->mapped) { |
667 | return; | 753 | return; |
668 | } | 754 | } |
669 | if (!xsurface->hints_urgency && view->urgent_timer) { | 755 | const bool hints_urgency = xcb_icccm_wm_hints_get_urgency(xsurface->hints); |
756 | if (!hints_urgency && view->urgent_timer) { | ||
670 | // The view is in the timeout period. We'll ignore the request to | 757 | // The view is in the timeout period. We'll ignore the request to |
671 | // unset urgency so that the view remains urgent until the timer clears | 758 | // unset urgency so that the view remains urgent until the timer clears |
672 | // it. | 759 | // it. |
673 | return; | 760 | return; |
674 | } | 761 | } |
675 | if (view->allow_request_urgent) { | 762 | if (view->allow_request_urgent) { |
676 | view_set_urgent(view, (bool)xsurface->hints_urgency); | 763 | view_set_urgent(view, hints_urgency); |
677 | } | 764 | } |
678 | } | 765 | } |
679 | 766 | ||
767 | static void handle_associate(struct wl_listener *listener, void *data) { | ||
768 | struct sway_xwayland_view *xwayland_view = | ||
769 | wl_container_of(listener, xwayland_view, associate); | ||
770 | struct wlr_xwayland_surface *xsurface = | ||
771 | xwayland_view->view.wlr_xwayland_surface; | ||
772 | wl_signal_add(&xsurface->surface->events.unmap, &xwayland_view->unmap); | ||
773 | xwayland_view->unmap.notify = handle_unmap; | ||
774 | wl_signal_add(&xsurface->surface->events.map, &xwayland_view->map); | ||
775 | xwayland_view->map.notify = handle_map; | ||
776 | } | ||
777 | |||
778 | static void handle_dissociate(struct wl_listener *listener, void *data) { | ||
779 | struct sway_xwayland_view *xwayland_view = | ||
780 | wl_container_of(listener, xwayland_view, dissociate); | ||
781 | wl_list_remove(&xwayland_view->map.link); | ||
782 | wl_list_remove(&xwayland_view->unmap.link); | ||
783 | } | ||
784 | |||
680 | struct sway_view *view_from_wlr_xwayland_surface( | 785 | struct sway_view *view_from_wlr_xwayland_surface( |
681 | struct wlr_xwayland_surface *xsurface) { | 786 | struct wlr_xwayland_surface *xsurface) { |
682 | return xsurface->data; | 787 | return xsurface->data; |
@@ -692,7 +797,10 @@ struct sway_xwayland_view *create_xwayland_view(struct wlr_xwayland_surface *xsu | |||
692 | return NULL; | 797 | return NULL; |
693 | } | 798 | } |
694 | 799 | ||
695 | view_init(&xwayland_view->view, SWAY_VIEW_XWAYLAND, &view_impl); | 800 | if (!view_init(&xwayland_view->view, SWAY_VIEW_XWAYLAND, &view_impl)) { |
801 | free(xwayland_view); | ||
802 | return NULL; | ||
803 | } | ||
696 | xwayland_view->view.wlr_xwayland_surface = xsurface; | 804 | xwayland_view->view.wlr_xwayland_surface = xsurface; |
697 | 805 | ||
698 | wl_signal_add(&xsurface->events.destroy, &xwayland_view->destroy); | 806 | wl_signal_add(&xsurface->events.destroy, &xwayland_view->destroy); |
@@ -731,6 +839,10 @@ struct sway_xwayland_view *create_xwayland_view(struct wlr_xwayland_surface *xsu | |||
731 | wl_signal_add(&xsurface->events.set_role, &xwayland_view->set_role); | 839 | wl_signal_add(&xsurface->events.set_role, &xwayland_view->set_role); |
732 | xwayland_view->set_role.notify = handle_set_role; | 840 | xwayland_view->set_role.notify = handle_set_role; |
733 | 841 | ||
842 | wl_signal_add(&xsurface->events.set_startup_id, | ||
843 | &xwayland_view->set_startup_id); | ||
844 | xwayland_view->set_startup_id.notify = handle_set_startup_id; | ||
845 | |||
734 | wl_signal_add(&xsurface->events.set_window_type, | 846 | wl_signal_add(&xsurface->events.set_window_type, |
735 | &xwayland_view->set_window_type); | 847 | &xwayland_view->set_window_type); |
736 | xwayland_view->set_window_type.notify = handle_set_window_type; | 848 | xwayland_view->set_window_type.notify = handle_set_window_type; |
@@ -742,11 +854,11 @@ struct sway_xwayland_view *create_xwayland_view(struct wlr_xwayland_surface *xsu | |||
742 | &xwayland_view->set_decorations); | 854 | &xwayland_view->set_decorations); |
743 | xwayland_view->set_decorations.notify = handle_set_decorations; | 855 | xwayland_view->set_decorations.notify = handle_set_decorations; |
744 | 856 | ||
745 | wl_signal_add(&xsurface->events.unmap, &xwayland_view->unmap); | 857 | wl_signal_add(&xsurface->events.associate, &xwayland_view->associate); |
746 | xwayland_view->unmap.notify = handle_unmap; | 858 | xwayland_view->associate.notify = handle_associate; |
747 | 859 | ||
748 | wl_signal_add(&xsurface->events.map, &xwayland_view->map); | 860 | wl_signal_add(&xsurface->events.dissociate, &xwayland_view->dissociate); |
749 | xwayland_view->map.notify = handle_map; | 861 | xwayland_view->dissociate.notify = handle_dissociate; |
750 | 862 | ||
751 | wl_signal_add(&xsurface->events.set_override_redirect, | 863 | wl_signal_add(&xsurface->events.set_override_redirect, |
752 | &xwayland_view->override_redirect); | 864 | &xwayland_view->override_redirect); |