diff options
Diffstat (limited to 'sway/desktop')
-rw-r--r-- | sway/desktop/desktop.c | 40 | ||||
-rw-r--r-- | sway/desktop/idle_inhibit_v1.c | 46 | ||||
-rw-r--r-- | sway/desktop/launcher.c | 267 | ||||
-rw-r--r-- | sway/desktop/layer_shell.c | 780 | ||||
-rw-r--r-- | sway/desktop/output.c | 980 | ||||
-rw-r--r-- | sway/desktop/render.c | 1204 | ||||
-rw-r--r-- | sway/desktop/surface.c | 46 | ||||
-rw-r--r-- | sway/desktop/transaction.c | 500 | ||||
-rw-r--r-- | sway/desktop/xdg_shell.c | 252 | ||||
-rw-r--r-- | sway/desktop/xwayland.c | 268 |
10 files changed, 1642 insertions, 2741 deletions
diff --git a/sway/desktop/desktop.c b/sway/desktop/desktop.c deleted file mode 100644 index c8d4502c..00000000 --- a/sway/desktop/desktop.c +++ /dev/null | |||
@@ -1,40 +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; | ||
10 | wlr_output_layout_get_box(root->output_layout, | ||
11 | output->wlr_output, &output_box); | ||
12 | output_damage_surface(output, lx - output_box.x, | ||
13 | ly - output_box.y, surface, whole); | ||
14 | } | ||
15 | } | ||
16 | |||
17 | void desktop_damage_whole_container(struct sway_container *con) { | ||
18 | for (int i = 0; i < root->outputs->length; ++i) { | ||
19 | struct sway_output *output = root->outputs->items[i]; | ||
20 | output_damage_whole_container(output, con); | ||
21 | } | ||
22 | } | ||
23 | |||
24 | void desktop_damage_box(struct wlr_box *box) { | ||
25 | for (int i = 0; i < root->outputs->length; ++i) { | ||
26 | struct sway_output *output = root->outputs->items[i]; | ||
27 | output_damage_box(output, box); | ||
28 | } | ||
29 | } | ||
30 | |||
31 | void desktop_damage_view(struct sway_view *view) { | ||
32 | desktop_damage_whole_container(view->container); | ||
33 | struct wlr_box box = { | ||
34 | .x = view->container->current.content_x - view->geometry.x, | ||
35 | .y = view->container->current.content_y - view->geometry.y, | ||
36 | .width = view->surface->current.width, | ||
37 | .height = view->surface->current.height, | ||
38 | }; | ||
39 | desktop_damage_box(&box); | ||
40 | } | ||
diff --git a/sway/desktop/idle_inhibit_v1.c b/sway/desktop/idle_inhibit_v1.c index 82353038..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,7 +34,6 @@ 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->wlr_inhibitor = wlr_inhibitor; | 38 | inhibitor->wlr_inhibitor = wlr_inhibitor; |
40 | wl_list_insert(&manager->inhibitors, &inhibitor->link); | 39 | wl_list_insert(&manager->inhibitors, &inhibitor->link); |
@@ -42,33 +41,34 @@ void handle_idle_inhibitor_v1(struct wl_listener *listener, void *data) { | |||
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->mode != INHIBIT_IDLE_APPLICATION && |
73 | inhibitor->view == view) { | 73 | inhibitor->view == view) { |
74 | return inhibitor; | 74 | return inhibitor; |
@@ -79,9 +79,9 @@ 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->mode == INHIBIT_IDLE_APPLICATION && |
86 | view_from_wlr_surface(inhibitor->wlr_inhibitor->surface) == view) { | 86 | view_from_wlr_surface(inhibitor->wlr_inhibitor->surface) == view) { |
87 | return inhibitor; | 87 | return inhibitor; |
@@ -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 d44d6338..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_fractional_scale_v1.h> | ||
5 | #include <wlr/types/wlr_layer_shell_v1.h> | 6 | #include <wlr/types/wlr_layer_shell_v1.h> |
6 | #include <wlr/types/wlr_output_damage.h> | ||
7 | #include <wlr/types/wlr_output.h> | 7 | #include <wlr/types/wlr_output.h> |
8 | #include <wlr/types/wlr_scene.h> | ||
8 | #include <wlr/types/wlr_subcompositor.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,162 +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 (box.width == 0) { | ||
119 | box.x = bounds.x; | ||
120 | } else if ((state->anchor & both_horiz) == both_horiz) { | ||
121 | box.x = bounds.x + ((bounds.width / 2) - (box.width / 2)); | ||
122 | } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT)) { | ||
123 | box.x = bounds.x; | ||
124 | } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT)) { | ||
125 | box.x = bounds.x + (bounds.width - box.width); | ||
126 | } else { | ||
127 | box.x = bounds.x + ((bounds.width / 2) - (box.width / 2)); | ||
128 | } | ||
129 | // Vertical axis | ||
130 | const uint32_t both_vert = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | ||
131 | | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM; | ||
132 | if (box.height == 0) { | ||
133 | box.y = bounds.y; | ||
134 | } else if ((state->anchor & both_vert) == both_vert) { | ||
135 | box.y = bounds.y + ((bounds.height / 2) - (box.height / 2)); | ||
136 | } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP)) { | ||
137 | box.y = bounds.y; | ||
138 | } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM)) { | ||
139 | box.y = bounds.y + (bounds.height - box.height); | ||
140 | } else { | ||
141 | box.y = bounds.y + ((bounds.height / 2) - (box.height / 2)); | ||
142 | } | ||
143 | // Margin | ||
144 | if (box.width == 0) { | ||
145 | box.x += state->margin.left; | ||
146 | box.width = bounds.width - | ||
147 | (state->margin.left + state->margin.right); | ||
148 | } else if ((state->anchor & both_horiz) == both_horiz) { | ||
149 | // don't apply margins | ||
150 | } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT)) { | ||
151 | box.x += state->margin.left; | ||
152 | } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT)) { | ||
153 | box.x -= state->margin.right; | ||
154 | } | ||
155 | if (box.height == 0) { | ||
156 | box.y += state->margin.top; | ||
157 | box.height = bounds.height - | ||
158 | (state->margin.top + state->margin.bottom); | ||
159 | } else if ((state->anchor & both_vert) == both_vert) { | ||
160 | // don't apply margins | ||
161 | } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP)) { | ||
162 | box.y += state->margin.top; | ||
163 | } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM)) { | ||
164 | box.y -= state->margin.bottom; | ||
165 | } | ||
166 | if (!sway_assert(box.width >= 0 && box.height >= 0, | ||
167 | "Expected layer surface to have positive size")) { | ||
168 | continue; | 68 | continue; |
169 | } | 69 | } |
170 | // Apply | 70 | |
171 | sway_layer->geo = box; | 71 | wlr_scene_layer_surface_v1_configure(surface->scene, full_area, usable_area); |
172 | apply_exclusive(usable_area, state->anchor, state->exclusive_zone, | ||
173 | state->margin.top, state->margin.right, | ||
174 | state->margin.bottom, state->margin.left); | ||
175 | wlr_layer_surface_v1_configure(layer, box.width, box.height); | ||
176 | } | 72 | } |
177 | } | 73 | } |
178 | 74 | ||
@@ -180,81 +76,94 @@ void arrange_layers(struct sway_output *output) { | |||
180 | struct wlr_box usable_area = { 0 }; | 76 | struct wlr_box usable_area = { 0 }; |
181 | wlr_output_effective_resolution(output->wlr_output, | 77 | wlr_output_effective_resolution(output->wlr_output, |
182 | &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); | ||
183 | 85 | ||
184 | // Arrange exclusive surfaces from top->bottom | 86 | if (!wlr_box_equal(&usable_area, &output->usable_area)) { |
185 | arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], | ||
186 | &usable_area, true); | ||
187 | arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP], | ||
188 | &usable_area, true); | ||
189 | arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM], | ||
190 | &usable_area, true); | ||
191 | arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND], | ||
192 | &usable_area, true); | ||
193 | |||
194 | if (memcmp(&usable_area, &output->usable_area, | ||
195 | sizeof(struct wlr_box)) != 0) { | ||
196 | sway_log(SWAY_DEBUG, "Usable area changed, rearranging output"); | 87 | sway_log(SWAY_DEBUG, "Usable area changed, rearranging output"); |
197 | memcpy(&output->usable_area, &usable_area, sizeof(struct wlr_box)); | 88 | output->usable_area = usable_area; |
198 | arrange_output(output); | 89 | arrange_output(output); |
90 | } else { | ||
91 | arrange_popups(root->layers.popup); | ||
199 | } | 92 | } |
93 | } | ||
200 | 94 | ||
201 | // Arrange non-exclusive surfaces from top->bottom | 95 | static struct wlr_scene_tree *sway_layer_get_scene(struct sway_output *output, |
202 | arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], | 96 | enum zwlr_layer_shell_v1_layer type) { |
203 | &usable_area, false); | 97 | switch (type) { |
204 | arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP], | 98 | case ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND: |
205 | &usable_area, false); | 99 | return output->layers.shell_background; |
206 | arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM], | 100 | case ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM: |
207 | &usable_area, false); | 101 | return output->layers.shell_bottom; |
208 | arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND], | 102 | case ZWLR_LAYER_SHELL_V1_LAYER_TOP: |
209 | &usable_area, false); | 103 | return output->layers.shell_top; |
210 | 104 | case ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY: | |
211 | // Find topmost keyboard interactive layer, if such a layer exists | 105 | return output->layers.shell_overlay; |
212 | uint32_t layers_above_shell[] = { | ||
213 | ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY, | ||
214 | ZWLR_LAYER_SHELL_V1_LAYER_TOP, | ||
215 | }; | ||
216 | size_t nlayers = sizeof(layers_above_shell) / sizeof(layers_above_shell[0]); | ||
217 | struct sway_layer_surface *layer, *topmost = NULL; | ||
218 | for (size_t i = 0; i < nlayers; ++i) { | ||
219 | wl_list_for_each_reverse(layer, | ||
220 | &output->layers[layers_above_shell[i]], link) { | ||
221 | if (layer->layer_surface->current.keyboard_interactive && | ||
222 | layer->layer_surface->mapped) { | ||
223 | topmost = layer; | ||
224 | break; | ||
225 | } | ||
226 | } | ||
227 | if (topmost != NULL) { | ||
228 | break; | ||
229 | } | ||
230 | } | 106 | } |
231 | 107 | ||
232 | struct sway_seat *seat; | 108 | sway_assert(false, "unreachable"); |
233 | wl_list_for_each(seat, &server.input->seats, link) { | 109 | return NULL; |
234 | if (topmost != NULL) { | 110 | } |
235 | seat_set_focus_layer(seat, topmost->layer_surface); | 111 | |
236 | } else if (seat->focused_layer && | 112 | static struct sway_layer_surface *sway_layer_surface_create( |
237 | !seat->focused_layer->current.keyboard_interactive) { | 113 | struct wlr_scene_layer_surface_v1 *scene) { |
238 | seat_set_focus_layer(seat, NULL); | 114 | struct sway_layer_surface *surface = calloc(1, sizeof(*surface)); |
239 | } | 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; | ||
240 | } | 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; | ||
241 | } | 144 | } |
242 | 145 | ||
243 | static struct sway_layer_surface *find_mapped_layer_by_client( | 146 | static struct sway_layer_surface *find_mapped_layer_by_client( |
244 | struct wl_client *client, struct wlr_output *ignore_output) { | 147 | struct wl_client *client, struct sway_output *ignore_output) { |
245 | for (int i = 0; i < root->outputs->length; ++i) { | 148 | for (int i = 0; i < root->outputs->length; ++i) { |
246 | struct sway_output *output = root->outputs->items[i]; | 149 | struct sway_output *output = root->outputs->items[i]; |
247 | if (output->wlr_output == ignore_output) { | 150 | if (output == ignore_output) { |
248 | continue; | 151 | continue; |
249 | } | 152 | } |
250 | // For now we'll only check the overlay layer | 153 | // For now we'll only check the overlay layer |
251 | struct sway_layer_surface *lsurface; | 154 | struct wlr_scene_node *node; |
252 | wl_list_for_each(lsurface, | 155 | wl_list_for_each (node, &output->layers.shell_overlay->children, link) { |
253 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], link) { | 156 | struct sway_layer_surface *surface = scene_descriptor_try_get(node, |
254 | 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; | ||
255 | if (wl_resource_get_client(resource) == client | 164 | if (wl_resource_get_client(resource) == client |
256 | && lsurface->layer_surface->mapped) { | 165 | && layer_surface->surface->mapped) { |
257 | return lsurface; | 166 | return surface; |
258 | } | 167 | } |
259 | } | 168 | } |
260 | } | 169 | } |
@@ -262,293 +171,142 @@ static struct sway_layer_surface *find_mapped_layer_by_client( | |||
262 | } | 171 | } |
263 | 172 | ||
264 | static void handle_output_destroy(struct wl_listener *listener, void *data) { | 173 | static void handle_output_destroy(struct wl_listener *listener, void *data) { |
265 | struct sway_layer_surface *sway_layer = | 174 | struct sway_layer_surface *layer = |
266 | wl_container_of(listener, sway_layer, output_destroy); | 175 | wl_container_of(listener, layer, output_destroy); |
267 | // Determine if this layer is being used by an exclusive client. If it is, | ||
268 | // try and find another layer owned by this client to pass focus to. | ||
269 | struct sway_seat *seat = input_manager_get_default_seat(); | ||
270 | struct wl_client *client = | ||
271 | wl_resource_get_client(sway_layer->layer_surface->resource); | ||
272 | bool set_focus = seat->exclusive_client == client; | ||
273 | |||
274 | if (set_focus) { | ||
275 | struct sway_layer_surface *layer = | ||
276 | find_mapped_layer_by_client(client, sway_layer->layer_surface->output); | ||
277 | if (layer) { | ||
278 | seat_set_focus_layer(seat, layer->layer_surface); | ||
279 | } | ||
280 | } | ||
281 | 176 | ||
282 | wlr_layer_surface_v1_destroy(sway_layer->layer_surface); | 177 | layer->output = NULL; |
178 | wlr_scene_node_destroy(&layer->scene->tree->node); | ||
283 | } | 179 | } |
284 | 180 | ||
285 | static void handle_surface_commit(struct wl_listener *listener, void *data) { | 181 | static void handle_node_destroy(struct wl_listener *listener, void *data) { |
286 | struct sway_layer_surface *layer = | 182 | struct sway_layer_surface *layer = |
287 | wl_container_of(listener, layer, surface_commit); | 183 | wl_container_of(listener, layer, node_destroy); |
288 | struct wlr_layer_surface_v1 *layer_surface = layer->layer_surface; | ||
289 | struct wlr_output *wlr_output = layer_surface->output; | ||
290 | sway_assert(wlr_output, "wlr_layer_surface_v1 has null output"); | ||
291 | struct sway_output *output = wlr_output->data; | ||
292 | struct wlr_box old_extent = layer->extent; | ||
293 | |||
294 | bool layer_changed = false; | ||
295 | if (layer_surface->current.committed != 0 | ||
296 | || layer->mapped != layer_surface->mapped) { | ||
297 | layer->mapped = layer_surface->mapped; | ||
298 | 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 | arrange_layers(output); | ||
306 | } | ||
307 | 184 | ||
308 | wlr_surface_get_extends(layer_surface->surface, &layer->extent); | 185 | // destroy the scene descriptor straight away if it exists, otherwise |
309 | layer->extent.x += layer->geo.x; | 186 | // we will try to reflow still considering the destroyed node. |
310 | layer->extent.y += layer->geo.y; | 187 | scene_descriptor_destroy(&layer->tree->node, SWAY_SCENE_DESC_LAYER_SHELL); |
311 | 188 | ||
312 | bool extent_changed = | 189 | // Determine if this layer is being used by an exclusive client. If it is, |
313 | memcmp(&old_extent, &layer->extent, sizeof(struct wlr_box)) != 0; | 190 | // try and find another layer owned by this client to pass focus to. |
314 | if (extent_changed || layer_changed) { | 191 | struct sway_seat *seat = input_manager_get_default_seat(); |
315 | output_damage_box(output, &old_extent); | 192 | struct wl_client *client = |
316 | output_damage_surface(output, layer->geo.x, layer->geo.y, | 193 | wl_resource_get_client(layer->layer_surface->resource); |
317 | layer_surface->surface, true); | 194 | if (!server.session_lock.lock) { |
318 | } else { | 195 | struct sway_layer_surface *consider_layer = |
319 | output_damage_surface(output, layer->geo.x, layer->geo.y, | 196 | find_mapped_layer_by_client(client, layer->output); |
320 | layer_surface->surface, false); | 197 | if (consider_layer) { |
321 | } | 198 | seat_set_focus_layer(seat, consider_layer->layer_surface); |
322 | |||
323 | transaction_commit_dirty(); | ||
324 | } | ||
325 | |||
326 | static void unmap(struct sway_layer_surface *sway_layer) { | ||
327 | struct sway_seat *seat; | ||
328 | wl_list_for_each(seat, &server.input->seats, link) { | ||
329 | if (seat->focused_layer == sway_layer->layer_surface) { | ||
330 | seat_set_focus_layer(seat, NULL); | ||
331 | } | 199 | } |
332 | } | 200 | } |
333 | 201 | ||
334 | cursor_rebase_all(); | 202 | if (layer->output) { |
335 | 203 | arrange_layers(layer->output); | |
336 | struct wlr_output *wlr_output = sway_layer->layer_surface->output; | 204 | transaction_commit_dirty(); |
337 | sway_assert(wlr_output, "wlr_layer_surface_v1 has null output"); | ||
338 | struct sway_output *output = wlr_output->data; | ||
339 | output_damage_surface(output, sway_layer->geo.x, sway_layer->geo.y, | ||
340 | sway_layer->layer_surface->surface, true); | ||
341 | } | ||
342 | |||
343 | static void layer_subsurface_destroy(struct sway_layer_subsurface *subsurface); | ||
344 | |||
345 | static void handle_destroy(struct wl_listener *listener, void *data) { | ||
346 | struct sway_layer_surface *sway_layer = | ||
347 | wl_container_of(listener, sway_layer, destroy); | ||
348 | sway_log(SWAY_DEBUG, "Layer surface destroyed (%s)", | ||
349 | sway_layer->layer_surface->namespace); | ||
350 | if (sway_layer->layer_surface->mapped) { | ||
351 | unmap(sway_layer); | ||
352 | } | 205 | } |
353 | 206 | ||
354 | struct sway_layer_subsurface *subsurface, *subsurface_tmp; | 207 | wlr_scene_node_destroy(&layer->popups->node); |
355 | wl_list_for_each_safe(subsurface, subsurface_tmp, &sway_layer->subsurfaces, link) { | ||
356 | layer_subsurface_destroy(subsurface); | ||
357 | } | ||
358 | |||
359 | wl_list_remove(&sway_layer->link); | ||
360 | wl_list_remove(&sway_layer->destroy.link); | ||
361 | wl_list_remove(&sway_layer->map.link); | ||
362 | wl_list_remove(&sway_layer->unmap.link); | ||
363 | wl_list_remove(&sway_layer->surface_commit.link); | ||
364 | wl_list_remove(&sway_layer->new_popup.link); | ||
365 | wl_list_remove(&sway_layer->new_subsurface.link); | ||
366 | |||
367 | struct wlr_output *wlr_output = sway_layer->layer_surface->output; | ||
368 | sway_assert(wlr_output, "wlr_layer_surface_v1 has null output"); | ||
369 | struct sway_output *output = wlr_output->data; | ||
370 | arrange_layers(output); | ||
371 | transaction_commit_dirty(); | ||
372 | wl_list_remove(&sway_layer->output_destroy.link); | ||
373 | sway_layer->layer_surface->output = NULL; | ||
374 | |||
375 | free(sway_layer); | ||
376 | } | ||
377 | |||
378 | static void handle_map(struct wl_listener *listener, void *data) { | ||
379 | struct sway_layer_surface *sway_layer = wl_container_of(listener, | ||
380 | sway_layer, map); | ||
381 | struct wlr_output *wlr_output = sway_layer->layer_surface->output; | ||
382 | sway_assert(wlr_output, "wlr_layer_surface_v1 has null output"); | ||
383 | struct sway_output *output = wlr_output->data; | ||
384 | output_damage_surface(output, sway_layer->geo.x, sway_layer->geo.y, | ||
385 | sway_layer->layer_surface->surface, true); | ||
386 | wlr_surface_send_enter(sway_layer->layer_surface->surface, | ||
387 | sway_layer->layer_surface->output); | ||
388 | cursor_rebase_all(); | ||
389 | } | ||
390 | 208 | ||
391 | static void handle_unmap(struct wl_listener *listener, void *data) { | 209 | wl_list_remove(&layer->map.link); |
392 | struct sway_layer_surface *sway_layer = wl_container_of( | 210 | wl_list_remove(&layer->unmap.link); |
393 | listener, sway_layer, unmap); | 211 | wl_list_remove(&layer->surface_commit.link); |
394 | unmap(sway_layer); | 212 | wl_list_remove(&layer->node_destroy.link); |
395 | } | 213 | wl_list_remove(&layer->output_destroy.link); |
396 | 214 | ||
397 | static void subsurface_damage(struct sway_layer_subsurface *subsurface, | 215 | layer->layer_surface->data = NULL; |
398 | bool whole) { | ||
399 | struct sway_layer_surface *layer = subsurface->layer_surface; | ||
400 | struct wlr_output *wlr_output = layer->layer_surface->output; | ||
401 | sway_assert(wlr_output, "wlr_layer_surface_v1 has null output"); | ||
402 | struct sway_output *output = wlr_output->data; | ||
403 | int ox = subsurface->wlr_subsurface->current.x + layer->geo.x; | ||
404 | int oy = subsurface->wlr_subsurface->current.y + layer->geo.y; | ||
405 | output_damage_surface( | ||
406 | output, ox, oy, subsurface->wlr_subsurface->surface, whole); | ||
407 | } | ||
408 | 216 | ||
409 | static void subsurface_handle_unmap(struct wl_listener *listener, void *data) { | 217 | free(layer); |
410 | struct sway_layer_subsurface *subsurface = | ||
411 | wl_container_of(listener, subsurface, unmap); | ||
412 | subsurface_damage(subsurface, true); | ||
413 | } | 218 | } |
414 | 219 | ||
415 | static void subsurface_handle_map(struct wl_listener *listener, void *data) { | 220 | static void handle_surface_commit(struct wl_listener *listener, void *data) { |
416 | struct sway_layer_subsurface *subsurface = | 221 | struct sway_layer_surface *surface = |
417 | wl_container_of(listener, subsurface, map); | 222 | wl_container_of(listener, surface, surface_commit); |
418 | subsurface_damage(subsurface, true); | ||
419 | } | ||
420 | |||
421 | static void subsurface_handle_commit(struct wl_listener *listener, void *data) { | ||
422 | struct sway_layer_subsurface *subsurface = | ||
423 | wl_container_of(listener, subsurface, commit); | ||
424 | subsurface_damage(subsurface, false); | ||
425 | } | ||
426 | |||
427 | static void layer_subsurface_destroy(struct sway_layer_subsurface *subsurface) { | ||
428 | wl_list_remove(&subsurface->link); | ||
429 | wl_list_remove(&subsurface->map.link); | ||
430 | wl_list_remove(&subsurface->unmap.link); | ||
431 | wl_list_remove(&subsurface->destroy.link); | ||
432 | wl_list_remove(&subsurface->commit.link); | ||
433 | free(subsurface); | ||
434 | } | ||
435 | |||
436 | static void subsurface_handle_destroy(struct wl_listener *listener, | ||
437 | void *data) { | ||
438 | struct sway_layer_subsurface *subsurface = | ||
439 | wl_container_of(listener, subsurface, destroy); | ||
440 | layer_subsurface_destroy(subsurface); | ||
441 | } | ||
442 | 223 | ||
443 | static struct sway_layer_subsurface *create_subsurface( | 224 | struct wlr_layer_surface_v1 *layer_surface = surface->layer_surface; |
444 | struct wlr_subsurface *wlr_subsurface, | 225 | if (!layer_surface->initialized) { |
445 | struct sway_layer_surface *layer_surface) { | 226 | return; |
446 | struct sway_layer_subsurface *subsurface = | ||
447 | calloc(1, sizeof(struct sway_layer_subsurface)); | ||
448 | if (subsurface == NULL) { | ||
449 | return NULL; | ||
450 | } | 227 | } |
451 | 228 | ||
452 | subsurface->wlr_subsurface = wlr_subsurface; | 229 | uint32_t committed = layer_surface->current.committed; |
453 | subsurface->layer_surface = layer_surface; | 230 | if (committed & WLR_LAYER_SURFACE_V1_STATE_LAYER) { |
454 | wl_list_insert(&layer_surface->subsurfaces, &subsurface->link); | 231 | enum zwlr_layer_shell_v1_layer layer_type = layer_surface->current.layer; |
455 | 232 | struct wlr_scene_tree *output_layer = sway_layer_get_scene( | |
456 | subsurface->map.notify = subsurface_handle_map; | 233 | surface->output, layer_type); |
457 | wl_signal_add(&wlr_subsurface->events.map, &subsurface->map); | 234 | wlr_scene_node_reparent(&surface->scene->tree->node, output_layer); |
458 | subsurface->unmap.notify = subsurface_handle_unmap; | 235 | } |
459 | wl_signal_add(&wlr_subsurface->events.unmap, &subsurface->unmap); | ||
460 | subsurface->destroy.notify = subsurface_handle_destroy; | ||
461 | wl_signal_add(&wlr_subsurface->events.destroy, &subsurface->destroy); | ||
462 | subsurface->commit.notify = subsurface_handle_commit; | ||
463 | wl_signal_add(&wlr_subsurface->surface->events.commit, &subsurface->commit); | ||
464 | |||
465 | return subsurface; | ||
466 | } | ||
467 | |||
468 | static void handle_new_subsurface(struct wl_listener *listener, void *data) { | ||
469 | struct sway_layer_surface *sway_layer_surface = | ||
470 | wl_container_of(listener, sway_layer_surface, new_subsurface); | ||
471 | struct wlr_subsurface *wlr_subsurface = data; | ||
472 | create_subsurface(wlr_subsurface, sway_layer_surface); | ||
473 | } | ||
474 | |||
475 | 236 | ||
476 | static struct sway_layer_surface *popup_get_layer( | 237 | if (layer_surface->initial_commit || committed || layer_surface->surface->mapped != surface->mapped) { |
477 | struct sway_layer_popup *popup) { | 238 | surface->mapped = layer_surface->surface->mapped; |
478 | while (popup->parent_type == LAYER_PARENT_POPUP) { | 239 | arrange_layers(surface->output); |
479 | popup = popup->parent_popup; | 240 | transaction_commit_dirty(); |
480 | } | 241 | } |
481 | return popup->parent_layer; | ||
482 | } | 242 | } |
483 | 243 | ||
484 | static void popup_damage(struct sway_layer_popup *layer_popup, bool whole) { | 244 | static void handle_map(struct wl_listener *listener, void *data) { |
485 | struct wlr_xdg_popup *popup = layer_popup->wlr_popup; | 245 | struct sway_layer_surface *surface = wl_container_of(listener, |
486 | struct wlr_surface *surface = popup->base->surface; | 246 | surface, map); |
487 | int popup_sx = popup->current.geometry.x - popup->base->current.geometry.x; | 247 | |
488 | int popup_sy = popup->current.geometry.y - popup->base->current.geometry.y; | 248 | struct wlr_layer_surface_v1 *layer_surface = |
489 | int ox = popup_sx, oy = popup_sy; | 249 | surface->scene->layer_surface; |
490 | struct sway_layer_surface *layer; | 250 | |
491 | while (true) { | 251 | // focus on new surface |
492 | if (layer_popup->parent_type == LAYER_PARENT_POPUP) { | 252 | if (layer_surface->current.keyboard_interactive && |
493 | layer_popup = layer_popup->parent_popup; | 253 | (layer_surface->current.layer == ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY || |
494 | ox += layer_popup->wlr_popup->current.geometry.x; | 254 | layer_surface->current.layer == ZWLR_LAYER_SHELL_V1_LAYER_TOP)) { |
495 | oy += layer_popup->wlr_popup->current.geometry.y; | 255 | struct sway_seat *seat; |
496 | } else { | 256 | wl_list_for_each(seat, &server.input->seats, link) { |
497 | layer = layer_popup->parent_layer; | 257 | // but only if the currently focused layer has a lower precedence |
498 | ox += layer->geo.x; | 258 | if (!seat->focused_layer || |
499 | oy += layer->geo.y; | 259 | seat->focused_layer->current.layer >= layer_surface->current.layer) { |
500 | break; | 260 | seat_set_focus_layer(seat, layer_surface); |
261 | } | ||
501 | } | 262 | } |
263 | arrange_layers(surface->output); | ||
502 | } | 264 | } |
503 | struct wlr_output *wlr_output = layer->layer_surface->output; | ||
504 | sway_assert(wlr_output, "wlr_layer_surface_v1 has null output"); | ||
505 | struct sway_output *output = wlr_output->data; | ||
506 | output_damage_surface(output, ox, oy, surface, whole); | ||
507 | } | ||
508 | 265 | ||
509 | static void popup_handle_map(struct wl_listener *listener, void *data) { | 266 | cursor_rebase_all(); |
510 | struct sway_layer_popup *popup = wl_container_of(listener, popup, map); | ||
511 | struct sway_layer_surface *layer = popup_get_layer(popup); | ||
512 | struct wlr_output *wlr_output = layer->layer_surface->output; | ||
513 | sway_assert(wlr_output, "wlr_layer_surface_v1 has null output"); | ||
514 | wlr_surface_send_enter(popup->wlr_popup->base->surface, wlr_output); | ||
515 | popup_damage(popup, true); | ||
516 | } | 267 | } |
517 | 268 | ||
518 | static void popup_handle_unmap(struct wl_listener *listener, void *data) { | 269 | static void handle_unmap(struct wl_listener *listener, void *data) { |
519 | struct sway_layer_popup *popup = wl_container_of(listener, popup, unmap); | 270 | struct sway_layer_surface *surface = wl_container_of( |
520 | popup_damage(popup, true); | 271 | listener, surface, unmap); |
521 | } | 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 | } | ||
522 | 278 | ||
523 | static void popup_handle_commit(struct wl_listener *listener, void *data) { | 279 | cursor_rebase_all(); |
524 | struct sway_layer_popup *popup = wl_container_of(listener, popup, commit); | ||
525 | popup_damage(popup, false); | ||
526 | } | 280 | } |
527 | 281 | ||
528 | static void popup_handle_destroy(struct wl_listener *listener, void *data) { | 282 | static void popup_handle_destroy(struct wl_listener *listener, void *data) { |
529 | struct sway_layer_popup *popup = | 283 | struct sway_layer_popup *popup = |
530 | wl_container_of(listener, popup, destroy); | 284 | wl_container_of(listener, popup, destroy); |
531 | 285 | ||
532 | wl_list_remove(&popup->map.link); | ||
533 | wl_list_remove(&popup->unmap.link); | ||
534 | wl_list_remove(&popup->destroy.link); | 286 | wl_list_remove(&popup->destroy.link); |
287 | wl_list_remove(&popup->new_popup.link); | ||
535 | wl_list_remove(&popup->commit.link); | 288 | wl_list_remove(&popup->commit.link); |
536 | free(popup); | 289 | free(popup); |
537 | } | 290 | } |
538 | 291 | ||
539 | static void popup_unconstrain(struct sway_layer_popup *popup) { | 292 | static void popup_unconstrain(struct sway_layer_popup *popup) { |
540 | struct sway_layer_surface *layer = popup_get_layer(popup); | ||
541 | 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; | ||
542 | 295 | ||
543 | struct wlr_output *wlr_output = layer->layer_surface->output; | 296 | // if a client tries to create a popup while we are in the process of destroying |
544 | sway_assert(wlr_output, "wlr_layer_surface_v1 has null output"); | 297 | // its output, don't crash. |
545 | struct sway_output *output = wlr_output->data; | 298 | if (!output) { |
299 | return; | ||
300 | } | ||
301 | |||
302 | int lx, ly; | ||
303 | wlr_scene_node_coords(&popup->toplevel->scene->tree->node, &lx, &ly); | ||
546 | 304 | ||
547 | // 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 |
548 | // of the popup | 306 | // of the popup |
549 | struct wlr_box output_toplevel_sx_box = { | 307 | struct wlr_box output_toplevel_sx_box = { |
550 | .x = -layer->geo.x, | 308 | .x = output->lx - lx, |
551 | .y = -layer->geo.y, | 309 | .y = output->ly - ly, |
552 | .width = output->width, | 310 | .width = output->width, |
553 | .height = output->height, | 311 | .height = output->height, |
554 | }; | 312 | }; |
@@ -556,32 +314,38 @@ static void popup_unconstrain(struct sway_layer_popup *popup) { | |||
556 | 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); |
557 | } | 315 | } |
558 | 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 | |||
559 | 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); |
560 | 325 | ||
561 | 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, |
562 | enum layer_parent parent_type, void *parent) { | 327 | struct sway_layer_surface *toplevel, struct wlr_scene_tree *parent) { |
563 | struct sway_layer_popup *popup = | 328 | struct sway_layer_popup *popup = calloc(1, sizeof(*popup)); |
564 | calloc(1, sizeof(struct sway_layer_popup)); | ||
565 | if (popup == NULL) { | 329 | if (popup == NULL) { |
566 | return NULL; | 330 | return NULL; |
567 | } | 331 | } |
568 | 332 | ||
333 | popup->toplevel = toplevel; | ||
569 | popup->wlr_popup = wlr_popup; | 334 | popup->wlr_popup = wlr_popup; |
570 | popup->parent_type = parent_type; | 335 | popup->scene = wlr_scene_xdg_surface_create(parent, |
571 | popup->parent_layer = parent; | 336 | wlr_popup->base); |
337 | |||
338 | if (!popup->scene) { | ||
339 | free(popup); | ||
340 | return NULL; | ||
341 | } | ||
572 | 342 | ||
573 | popup->map.notify = popup_handle_map; | ||
574 | wl_signal_add(&wlr_popup->base->events.map, &popup->map); | ||
575 | popup->unmap.notify = popup_handle_unmap; | ||
576 | wl_signal_add(&wlr_popup->base->events.unmap, &popup->unmap); | ||
577 | popup->destroy.notify = popup_handle_destroy; | 343 | popup->destroy.notify = popup_handle_destroy; |
578 | wl_signal_add(&wlr_popup->base->events.destroy, &popup->destroy); | 344 | wl_signal_add(&wlr_popup->base->events.destroy, &popup->destroy); |
579 | popup->commit.notify = popup_handle_commit; | ||
580 | wl_signal_add(&wlr_popup->base->surface->events.commit, &popup->commit); | ||
581 | popup->new_popup.notify = popup_handle_new_popup; | 345 | popup->new_popup.notify = popup_handle_new_popup; |
582 | 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); |
583 | 347 | popup->commit.notify = popup_handle_commit; | |
584 | popup_unconstrain(popup); | 348 | wl_signal_add(&wlr_popup->base->surface->events.commit, &popup->commit); |
585 | 349 | ||
586 | return popup; | 350 | return popup; |
587 | } | 351 | } |
@@ -590,19 +354,14 @@ static void popup_handle_new_popup(struct wl_listener *listener, void *data) { | |||
590 | struct sway_layer_popup *sway_layer_popup = | 354 | struct sway_layer_popup *sway_layer_popup = |
591 | wl_container_of(listener, sway_layer_popup, new_popup); | 355 | wl_container_of(listener, sway_layer_popup, new_popup); |
592 | struct wlr_xdg_popup *wlr_popup = data; | 356 | struct wlr_xdg_popup *wlr_popup = data; |
593 | create_popup(wlr_popup, LAYER_PARENT_POPUP, sway_layer_popup); | 357 | create_popup(wlr_popup, sway_layer_popup->toplevel, sway_layer_popup->scene); |
594 | } | 358 | } |
595 | 359 | ||
596 | static void handle_new_popup(struct wl_listener *listener, void *data) { | 360 | static void handle_new_popup(struct wl_listener *listener, void *data) { |
597 | struct sway_layer_surface *sway_layer_surface = | 361 | struct sway_layer_surface *sway_layer_surface = |
598 | wl_container_of(listener, sway_layer_surface, new_popup); | 362 | wl_container_of(listener, sway_layer_surface, new_popup); |
599 | struct wlr_xdg_popup *wlr_popup = data; | 363 | struct wlr_xdg_popup *wlr_popup = data; |
600 | create_popup(wlr_popup, LAYER_PARENT_LAYER, sway_layer_surface); | 364 | create_popup(wlr_popup, sway_layer_surface, sway_layer_surface->popups); |
601 | } | ||
602 | |||
603 | struct sway_layer_surface *layer_from_wlr_layer_surface_v1( | ||
604 | struct wlr_layer_surface_v1 *layer_surface) { | ||
605 | return layer_surface->data; | ||
606 | } | 365 | } |
607 | 366 | ||
608 | void handle_layer_shell_surface(struct wl_listener *listener, void *data) { | 367 | void handle_layer_shell_surface(struct wl_listener *listener, void *data) { |
@@ -634,10 +393,6 @@ void handle_layer_shell_surface(struct wl_listener *listener, void *data) { | |||
634 | sway_log(SWAY_ERROR, | 393 | sway_log(SWAY_ERROR, |
635 | "no output to auto-assign layer surface '%s' to", | 394 | "no output to auto-assign layer surface '%s' to", |
636 | layer_surface->namespace); | 395 | layer_surface->namespace); |
637 | // Note that layer_surface->output can be NULL | ||
638 | // here, but none of our destroy callbacks are | ||
639 | // registered yet so we don't have to make them | ||
640 | // handle that case. | ||
641 | wlr_layer_surface_v1_destroy(layer_surface); | 396 | wlr_layer_surface_v1_destroy(layer_surface); |
642 | return; | 397 | return; |
643 | } | 398 | } |
@@ -646,44 +401,57 @@ void handle_layer_shell_surface(struct wl_listener *listener, void *data) { | |||
646 | layer_surface->output = output->wlr_output; | 401 | layer_surface->output = output->wlr_output; |
647 | } | 402 | } |
648 | 403 | ||
649 | struct sway_layer_surface *sway_layer = | 404 | struct sway_output *output = layer_surface->output->data; |
650 | calloc(1, sizeof(struct sway_layer_surface)); | 405 | |
651 | 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"); | ||
652 | return; | 413 | return; |
653 | } | 414 | } |
654 | 415 | ||
655 | wl_list_init(&sway_layer->subsurfaces); | 416 | struct sway_layer_surface *surface = |
417 | sway_layer_surface_create(scene_surface); | ||
418 | if (!surface) { | ||
419 | wlr_layer_surface_v1_destroy(layer_surface); | ||
656 | 420 | ||
657 | sway_layer->surface_commit.notify = handle_surface_commit; | 421 | sway_log(SWAY_ERROR, "Could not allocate a sway_layer_surface"); |
658 | wl_signal_add(&layer_surface->surface->events.commit, | 422 | return; |
659 | &sway_layer->surface_commit); | 423 | } |
660 | |||
661 | sway_layer->destroy.notify = handle_destroy; | ||
662 | wl_signal_add(&layer_surface->events.destroy, &sway_layer->destroy); | ||
663 | sway_layer->map.notify = handle_map; | ||
664 | wl_signal_add(&layer_surface->events.map, &sway_layer->map); | ||
665 | sway_layer->unmap.notify = handle_unmap; | ||
666 | wl_signal_add(&layer_surface->events.unmap, &sway_layer->unmap); | ||
667 | sway_layer->new_popup.notify = handle_new_popup; | ||
668 | wl_signal_add(&layer_surface->events.new_popup, &sway_layer->new_popup); | ||
669 | sway_layer->new_subsurface.notify = handle_new_subsurface; | ||
670 | wl_signal_add(&layer_surface->surface->events.new_subsurface, | ||
671 | &sway_layer->new_subsurface); | ||
672 | |||
673 | sway_layer->layer_surface = layer_surface; | ||
674 | layer_surface->data = sway_layer; | ||
675 | 424 | ||
676 | struct sway_output *output = layer_surface->output->data; | 425 | if (!scene_descriptor_assign(&scene_surface->tree->node, |
677 | sway_layer->output_destroy.notify = handle_output_destroy; | 426 | SWAY_SCENE_DESC_LAYER_SHELL, surface)) { |
678 | wl_signal_add(&output->events.disable, &sway_layer->output_destroy); | 427 | sway_log(SWAY_ERROR, "Failed to allocate a layer surface descriptor"); |
679 | 428 | // destroying the layer_surface will also destroy its corresponding | |
680 | wl_list_insert(&output->layers[layer_surface->pending.layer], | 429 | // scene node |
681 | &sway_layer->link); | 430 | wlr_layer_surface_v1_destroy(layer_surface); |
682 | 431 | return; | |
683 | // Temporarily set the layer's current state to pending | 432 | } |
684 | // So that we can easily arrange it | 433 | |
685 | struct wlr_layer_surface_v1_state old_state = layer_surface->current; | 434 | surface->output = output; |
686 | layer_surface->current = layer_surface->pending; | 435 | |
687 | arrange_layers(output); | 436 | // now that the surface's output is known, we can advertise its scale |
688 | layer_surface->current = old_state; | 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); | ||
689 | } | 457 | } |
diff --git a/sway/desktop/output.c b/sway/desktop/output.c index 7bb9dab2..2722e556 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c | |||
@@ -1,44 +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> |
7 | #include <wlr/backend/drm.h> | 6 | #include <wlr/config.h> |
8 | #include <wlr/backend/headless.h> | 7 | #include <wlr/backend/headless.h> |
8 | #include <wlr/render/swapchain.h> | ||
9 | #include <wlr/render/wlr_renderer.h> | 9 | #include <wlr/render/wlr_renderer.h> |
10 | #include <wlr/types/wlr_buffer.h> | 10 | #include <wlr/types/wlr_buffer.h> |
11 | #include <wlr/types/wlr_drm_lease_v1.h> | 11 | #include <wlr/types/wlr_gamma_control_v1.h> |
12 | #include <wlr/types/wlr_matrix.h> | 12 | #include <wlr/types/wlr_matrix.h> |
13 | #include <wlr/types/wlr_output_damage.h> | ||
14 | #include <wlr/types/wlr_output_layout.h> | 13 | #include <wlr/types/wlr_output_layout.h> |
14 | #include <wlr/types/wlr_output_management_v1.h> | ||
15 | #include <wlr/types/wlr_output_power_management_v1.h> | ||
15 | #include <wlr/types/wlr_output.h> | 16 | #include <wlr/types/wlr_output.h> |
16 | #include <wlr/types/wlr_presentation_time.h> | 17 | #include <wlr/types/wlr_presentation_time.h> |
17 | #include <wlr/types/wlr_compositor.h> | 18 | #include <wlr/types/wlr_compositor.h> |
18 | #include <wlr/util/region.h> | 19 | #include <wlr/util/region.h> |
20 | #include <wlr/util/transform.h> | ||
19 | #include "config.h" | 21 | #include "config.h" |
20 | #include "log.h" | 22 | #include "log.h" |
21 | #include "sway/config.h" | 23 | #include "sway/config.h" |
22 | #include "sway/desktop/transaction.h" | 24 | #include "sway/desktop/transaction.h" |
23 | #include "sway/input/input-manager.h" | 25 | #include "sway/input/input-manager.h" |
24 | #include "sway/input/seat.h" | 26 | #include "sway/input/seat.h" |
27 | #include "sway/ipc-server.h" | ||
25 | #include "sway/layers.h" | 28 | #include "sway/layers.h" |
26 | #include "sway/output.h" | 29 | #include "sway/output.h" |
30 | #include "sway/scene_descriptor.h" | ||
27 | #include "sway/server.h" | 31 | #include "sway/server.h" |
28 | #include "sway/surface.h" | ||
29 | #include "sway/tree/arrange.h" | 32 | #include "sway/tree/arrange.h" |
30 | #include "sway/tree/container.h" | 33 | #include "sway/tree/container.h" |
31 | #include "sway/tree/root.h" | 34 | #include "sway/tree/root.h" |
32 | #include "sway/tree/view.h" | 35 | #include "sway/tree/view.h" |
33 | #include "sway/tree/workspace.h" | 36 | #include "sway/tree/workspace.h" |
34 | 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 | |||
35 | 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) { |
36 | for (int i = 0; i < root->outputs->length; ++i) { | 56 | for (int i = 0; i < root->outputs->length; ++i) { |
37 | struct sway_output *output = root->outputs->items[i]; | 57 | struct sway_output *output = root->outputs->items[i]; |
38 | char identifier[128]; | 58 | if (output_match_name_or_id(output, name_or_id)) { |
39 | output_get_identifier(identifier, sizeof(identifier), output); | ||
40 | if (strcasecmp(identifier, name_or_id) == 0 | ||
41 | || strcasecmp(output->wlr_output->name, name_or_id) == 0) { | ||
42 | return output; | 59 | return output; |
43 | } | 60 | } |
44 | } | 61 | } |
@@ -48,536 +65,217 @@ struct sway_output *output_by_name_or_id(const char *name_or_id) { | |||
48 | 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) { |
49 | struct sway_output *output; | 66 | struct sway_output *output; |
50 | wl_list_for_each(output, &root->all_outputs, link) { | 67 | wl_list_for_each(output, &root->all_outputs, link) { |
51 | char identifier[128]; | 68 | if (output_match_name_or_id(output, name_or_id)) { |
52 | output_get_identifier(identifier, sizeof(identifier), output); | ||
53 | if (strcasecmp(identifier, name_or_id) == 0 | ||
54 | || strcasecmp(output->wlr_output->name, name_or_id) == 0) { | ||
55 | return output; | 69 | return output; |
56 | } | 70 | } |
57 | } | 71 | } |
58 | return NULL; | 72 | return NULL; |
59 | } | 73 | } |
60 | 74 | ||
61 | struct surface_iterator_data { | ||
62 | sway_surface_iterator_func_t user_iterator; | ||
63 | void *user_data; | ||
64 | |||
65 | struct sway_output *output; | ||
66 | struct sway_view *view; | ||
67 | double ox, oy; | ||
68 | int width, height; | ||
69 | }; | ||
70 | |||
71 | static bool get_surface_box(struct surface_iterator_data *data, | ||
72 | struct wlr_surface *surface, int sx, int sy, | ||
73 | struct wlr_box *surface_box) { | ||
74 | struct sway_output *output = data->output; | ||
75 | |||
76 | if (!wlr_surface_has_buffer(surface)) { | ||
77 | return false; | ||
78 | } | ||
79 | 75 | ||
80 | int sw = surface->current.width; | 76 | struct sway_workspace *output_get_active_workspace(struct sway_output *output) { |
81 | int sh = surface->current.height; | 77 | struct sway_seat *seat = input_manager_current_seat(); |
82 | 78 | struct sway_node *focus = seat_get_active_tiling_child(seat, &output->node); | |
83 | struct wlr_box box = { | 79 | if (!focus) { |
84 | .x = floor(data->ox + sx), | 80 | if (!output->workspaces->length) { |
85 | .y = floor(data->oy + sy), | 81 | return NULL; |
86 | .width = sw, | 82 | } |
87 | .height = sh, | 83 | return output->workspaces->items[0]; |
88 | }; | ||
89 | if (surface_box != NULL) { | ||
90 | memcpy(surface_box, &box, sizeof(struct wlr_box)); | ||
91 | } | 84 | } |
92 | 85 | return focus->sway_workspace; | |
93 | struct wlr_box output_box = { | ||
94 | .width = output->width, | ||
95 | .height = output->height, | ||
96 | }; | ||
97 | |||
98 | struct wlr_box intersection; | ||
99 | return wlr_box_intersection(&intersection, &output_box, &box); | ||
100 | } | 86 | } |
101 | 87 | ||
102 | static void output_for_each_surface_iterator(struct wlr_surface *surface, | 88 | struct send_frame_done_data { |
103 | int sx, int sy, void *_data) { | 89 | struct timespec when; |
104 | struct surface_iterator_data *data = _data; | 90 | int msec_until_refresh; |
105 | 91 | struct sway_output *output; | |
106 | struct wlr_box box; | 92 | }; |
107 | bool intersects = get_surface_box(data, surface, sx, sy, &box); | ||
108 | if (!intersects) { | ||
109 | return; | ||
110 | } | ||
111 | 93 | ||
112 | data->user_iterator(data->output, data->view, surface, &box, | 94 | struct buffer_timer { |
113 | data->user_data); | 95 | struct wl_listener destroy; |
114 | } | 96 | struct wl_event_source *frame_done_timer; |
97 | }; | ||
115 | 98 | ||
116 | void output_surface_for_each_surface(struct sway_output *output, | 99 | static int handle_buffer_timer(void *data) { |
117 | struct wlr_surface *surface, double ox, double oy, | 100 | struct wlr_scene_buffer *buffer = data; |
118 | sway_surface_iterator_func_t iterator, void *user_data) { | ||
119 | struct surface_iterator_data data = { | ||
120 | .user_iterator = iterator, | ||
121 | .user_data = user_data, | ||
122 | .output = output, | ||
123 | .view = NULL, | ||
124 | .ox = ox, | ||
125 | .oy = oy, | ||
126 | .width = surface->current.width, | ||
127 | .height = surface->current.height, | ||
128 | }; | ||
129 | |||
130 | wlr_surface_for_each_surface(surface, | ||
131 | output_for_each_surface_iterator, &data); | ||
132 | } | ||
133 | 101 | ||
134 | void output_view_for_each_surface(struct sway_output *output, | 102 | struct timespec now; |
135 | struct sway_view *view, sway_surface_iterator_func_t iterator, | 103 | clock_gettime(CLOCK_MONOTONIC, &now); |
136 | void *user_data) { | 104 | wlr_scene_buffer_send_frame_done(buffer, &now); |
137 | struct surface_iterator_data data = { | 105 | return 0; |
138 | .user_iterator = iterator, | ||
139 | .user_data = user_data, | ||
140 | .output = output, | ||
141 | .view = view, | ||
142 | .ox = view->container->surface_x - output->lx | ||
143 | - view->geometry.x, | ||
144 | .oy = view->container->surface_y - output->ly | ||
145 | - view->geometry.y, | ||
146 | .width = view->container->current.content_width, | ||
147 | .height = view->container->current.content_height, | ||
148 | }; | ||
149 | |||
150 | view_for_each_surface(view, output_for_each_surface_iterator, &data); | ||
151 | } | 106 | } |
152 | 107 | ||
153 | void output_view_for_each_popup_surface(struct sway_output *output, | 108 | static void handle_buffer_timer_destroy(struct wl_listener *listener, |
154 | struct sway_view *view, sway_surface_iterator_func_t iterator, | 109 | void *data) { |
155 | void *user_data) { | 110 | struct buffer_timer *timer = wl_container_of(listener, timer, destroy); |
156 | struct surface_iterator_data data = { | ||
157 | .user_iterator = iterator, | ||
158 | .user_data = user_data, | ||
159 | .output = output, | ||
160 | .view = view, | ||
161 | .ox = view->container->surface_x - output->lx | ||
162 | - view->geometry.x, | ||
163 | .oy = view->container->surface_y - output->ly | ||
164 | - view->geometry.y, | ||
165 | .width = view->container->current.content_width, | ||
166 | .height = view->container->current.content_height, | ||
167 | }; | ||
168 | |||
169 | view_for_each_popup_surface(view, output_for_each_surface_iterator, &data); | ||
170 | } | ||
171 | 111 | ||
172 | void output_layer_for_each_surface(struct sway_output *output, | 112 | wl_list_remove(&timer->destroy.link); |
173 | struct wl_list *layer_surfaces, sway_surface_iterator_func_t iterator, | 113 | wl_event_source_remove(timer->frame_done_timer); |
174 | void *user_data) { | 114 | free(timer); |
175 | struct sway_layer_surface *layer_surface; | ||
176 | wl_list_for_each(layer_surface, layer_surfaces, link) { | ||
177 | struct wlr_layer_surface_v1 *wlr_layer_surface_v1 = | ||
178 | layer_surface->layer_surface; | ||
179 | struct wlr_surface *surface = wlr_layer_surface_v1->surface; | ||
180 | struct surface_iterator_data data = { | ||
181 | .user_iterator = iterator, | ||
182 | .user_data = user_data, | ||
183 | .output = output, | ||
184 | .view = NULL, | ||
185 | .ox = layer_surface->geo.x, | ||
186 | .oy = layer_surface->geo.y, | ||
187 | .width = surface->current.width, | ||
188 | .height = surface->current.height, | ||
189 | }; | ||
190 | wlr_layer_surface_v1_for_each_surface(wlr_layer_surface_v1, | ||
191 | output_for_each_surface_iterator, &data); | ||
192 | } | ||
193 | } | 115 | } |
194 | 116 | ||
195 | void output_layer_for_each_toplevel_surface(struct sway_output *output, | 117 | static struct buffer_timer *buffer_timer_get_or_create(struct wlr_scene_buffer *buffer) { |
196 | struct wl_list *layer_surfaces, sway_surface_iterator_func_t iterator, | 118 | struct buffer_timer *timer = |
197 | void *user_data) { | 119 | scene_descriptor_try_get(&buffer->node, SWAY_SCENE_DESC_BUFFER_TIMER); |
198 | struct sway_layer_surface *layer_surface; | 120 | if (timer) { |
199 | wl_list_for_each(layer_surface, layer_surfaces, link) { | 121 | return timer; |
200 | struct wlr_layer_surface_v1 *wlr_layer_surface_v1 = | ||
201 | layer_surface->layer_surface; | ||
202 | output_surface_for_each_surface(output, wlr_layer_surface_v1->surface, | ||
203 | layer_surface->geo.x, layer_surface->geo.y, iterator, | ||
204 | user_data); | ||
205 | } | 122 | } |
206 | } | ||
207 | |||
208 | 123 | ||
209 | void output_layer_for_each_popup_surface(struct sway_output *output, | 124 | timer = calloc(1, sizeof(struct buffer_timer)); |
210 | struct wl_list *layer_surfaces, sway_surface_iterator_func_t iterator, | 125 | if (!timer) { |
211 | void *user_data) { | 126 | return NULL; |
212 | struct sway_layer_surface *layer_surface; | ||
213 | wl_list_for_each(layer_surface, layer_surfaces, link) { | ||
214 | struct wlr_layer_surface_v1 *wlr_layer_surface_v1 = | ||
215 | layer_surface->layer_surface; | ||
216 | struct wlr_surface *surface = wlr_layer_surface_v1->surface; | ||
217 | struct surface_iterator_data data = { | ||
218 | .user_iterator = iterator, | ||
219 | .user_data = user_data, | ||
220 | .output = output, | ||
221 | .view = NULL, | ||
222 | .ox = layer_surface->geo.x, | ||
223 | .oy = layer_surface->geo.y, | ||
224 | .width = surface->current.width, | ||
225 | .height = surface->current.height, | ||
226 | }; | ||
227 | wlr_layer_surface_v1_for_each_popup_surface(wlr_layer_surface_v1, | ||
228 | output_for_each_surface_iterator, &data); | ||
229 | } | 127 | } |
230 | } | ||
231 | 128 | ||
232 | #if HAVE_XWAYLAND | 129 | timer->frame_done_timer = wl_event_loop_add_timer(server.wl_event_loop, |
233 | void output_unmanaged_for_each_surface(struct sway_output *output, | 130 | handle_buffer_timer, buffer); |
234 | struct wl_list *unmanaged, sway_surface_iterator_func_t iterator, | 131 | if (!timer->frame_done_timer) { |
235 | void *user_data) { | 132 | free(timer); |
236 | struct sway_xwayland_unmanaged *unmanaged_surface; | 133 | return NULL; |
237 | wl_list_for_each(unmanaged_surface, unmanaged, link) { | ||
238 | struct wlr_xwayland_surface *xsurface = | ||
239 | unmanaged_surface->wlr_xwayland_surface; | ||
240 | double ox = unmanaged_surface->lx - output->lx; | ||
241 | double oy = unmanaged_surface->ly - output->ly; | ||
242 | |||
243 | output_surface_for_each_surface(output, xsurface->surface, ox, oy, | ||
244 | iterator, user_data); | ||
245 | } | 134 | } |
246 | } | ||
247 | #endif | ||
248 | 135 | ||
249 | void output_drag_icons_for_each_surface(struct sway_output *output, | 136 | scene_descriptor_assign(&buffer->node, SWAY_SCENE_DESC_BUFFER_TIMER, timer); |
250 | struct wl_list *drag_icons, sway_surface_iterator_func_t iterator, | ||
251 | void *user_data) { | ||
252 | struct sway_drag_icon *drag_icon; | ||
253 | wl_list_for_each(drag_icon, drag_icons, link) { | ||
254 | double ox = drag_icon->x - output->lx; | ||
255 | double oy = drag_icon->y - output->ly; | ||
256 | |||
257 | if (drag_icon->wlr_drag_icon->mapped) { | ||
258 | output_surface_for_each_surface(output, | ||
259 | drag_icon->wlr_drag_icon->surface, ox, oy, | ||
260 | iterator, user_data); | ||
261 | } | ||
262 | } | ||
263 | } | ||
264 | 137 | ||
265 | static void for_each_surface_container_iterator(struct sway_container *con, | 138 | timer->destroy.notify = handle_buffer_timer_destroy; |
266 | void *_data) { | 139 | wl_signal_add(&buffer->node.events.destroy, &timer->destroy); |
267 | if (!con->view || !view_is_visible(con->view)) { | ||
268 | return; | ||
269 | } | ||
270 | 140 | ||
271 | struct surface_iterator_data *data = _data; | 141 | return timer; |
272 | output_view_for_each_surface(data->output, con->view, | ||
273 | data->user_iterator, data->user_data); | ||
274 | } | 142 | } |
275 | 143 | ||
276 | static void output_for_each_surface(struct sway_output *output, | 144 | static void send_frame_done_iterator(struct wlr_scene_buffer *buffer, |
277 | sway_surface_iterator_func_t iterator, void *user_data) { | 145 | int x, int y, void *user_data) { |
278 | if (server.session_lock.locked) { | 146 | struct send_frame_done_data *data = user_data; |
279 | if (server.session_lock.lock == NULL) { | 147 | struct sway_output *output = data->output; |
280 | return; | 148 | int view_max_render_time = 0; |
281 | } | ||
282 | struct wlr_session_lock_surface_v1 *lock_surface; | ||
283 | wl_list_for_each(lock_surface, &server.session_lock.lock->surfaces, link) { | ||
284 | if (lock_surface->output != output->wlr_output) { | ||
285 | continue; | ||
286 | } | ||
287 | if (!lock_surface->mapped) { | ||
288 | continue; | ||
289 | } | ||
290 | 149 | ||
291 | output_surface_for_each_surface(output, lock_surface->surface, | 150 | if (buffer->primary_output != data->output->scene_output) { |
292 | 0.0, 0.0, iterator, user_data); | ||
293 | } | ||
294 | return; | 151 | return; |
295 | } | 152 | } |
296 | 153 | ||
297 | if (output_has_opaque_overlay_layer_surface(output)) { | 154 | struct wlr_scene_node *current = &buffer->node; |
298 | goto overlay; | 155 | while (true) { |
299 | } | 156 | struct sway_view *view = scene_descriptor_try_get(current, |
300 | 157 | SWAY_SCENE_DESC_VIEW); | |
301 | struct surface_iterator_data data = { | 158 | if (view) { |
302 | .user_iterator = iterator, | 159 | view_max_render_time = view->max_render_time; |
303 | .user_data = user_data, | 160 | break; |
304 | .output = output, | ||
305 | .view = NULL, | ||
306 | }; | ||
307 | |||
308 | struct sway_workspace *workspace = output_get_active_workspace(output); | ||
309 | struct sway_container *fullscreen_con = root->fullscreen_global; | ||
310 | if (!fullscreen_con) { | ||
311 | if (!workspace) { | ||
312 | return; | ||
313 | } | ||
314 | fullscreen_con = workspace->current.fullscreen; | ||
315 | } | ||
316 | if (fullscreen_con) { | ||
317 | for_each_surface_container_iterator(fullscreen_con, &data); | ||
318 | container_for_each_child(fullscreen_con, | ||
319 | for_each_surface_container_iterator, &data); | ||
320 | |||
321 | // TODO: Show transient containers for fullscreen global | ||
322 | if (fullscreen_con == workspace->current.fullscreen) { | ||
323 | for (int i = 0; i < workspace->current.floating->length; ++i) { | ||
324 | struct sway_container *floater = | ||
325 | workspace->current.floating->items[i]; | ||
326 | if (container_is_transient_for(floater, fullscreen_con)) { | ||
327 | for_each_surface_container_iterator(floater, &data); | ||
328 | } | ||
329 | } | ||
330 | } | 161 | } |
331 | #if HAVE_XWAYLAND | ||
332 | output_unmanaged_for_each_surface(output, &root->xwayland_unmanaged, | ||
333 | iterator, user_data); | ||
334 | #endif | ||
335 | } else { | ||
336 | output_layer_for_each_surface(output, | ||
337 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND], | ||
338 | iterator, user_data); | ||
339 | output_layer_for_each_surface(output, | ||
340 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM], | ||
341 | iterator, user_data); | ||
342 | |||
343 | workspace_for_each_container(workspace, | ||
344 | for_each_surface_container_iterator, &data); | ||
345 | |||
346 | #if HAVE_XWAYLAND | ||
347 | output_unmanaged_for_each_surface(output, &root->xwayland_unmanaged, | ||
348 | iterator, user_data); | ||
349 | #endif | ||
350 | output_layer_for_each_surface(output, | ||
351 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP], | ||
352 | iterator, user_data); | ||
353 | } | ||
354 | |||
355 | overlay: | ||
356 | output_layer_for_each_surface(output, | ||
357 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], | ||
358 | iterator, user_data); | ||
359 | output_drag_icons_for_each_surface(output, &root->drag_icons, | ||
360 | iterator, user_data); | ||
361 | } | ||
362 | |||
363 | static int scale_length(int length, int offset, float scale) { | ||
364 | return round((offset + length) * scale) - round(offset * scale); | ||
365 | } | ||
366 | 162 | ||
367 | void scale_box(struct wlr_box *box, float scale) { | 163 | if (!current->parent) { |
368 | box->width = scale_length(box->width, box->x, scale); | 164 | break; |
369 | box->height = scale_length(box->height, box->y, scale); | ||
370 | box->x = round(box->x * scale); | ||
371 | box->y = round(box->y * scale); | ||
372 | } | ||
373 | |||
374 | struct sway_workspace *output_get_active_workspace(struct sway_output *output) { | ||
375 | struct sway_seat *seat = input_manager_current_seat(); | ||
376 | struct sway_node *focus = seat_get_active_tiling_child(seat, &output->node); | ||
377 | if (!focus) { | ||
378 | if (!output->workspaces->length) { | ||
379 | return NULL; | ||
380 | } | 165 | } |
381 | return output->workspaces->items[0]; | ||
382 | } | ||
383 | return focus->sway_workspace; | ||
384 | } | ||
385 | 166 | ||
386 | bool output_has_opaque_overlay_layer_surface(struct sway_output *output) { | 167 | current = ¤t->parent->node; |
387 | struct sway_layer_surface *sway_layer_surface; | ||
388 | wl_list_for_each(sway_layer_surface, | ||
389 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], link) { | ||
390 | struct wlr_surface *wlr_surface = sway_layer_surface->layer_surface->surface; | ||
391 | pixman_box32_t output_box = { | ||
392 | .x2 = output->width, | ||
393 | .y2 = output->height, | ||
394 | }; | ||
395 | pixman_region32_t surface_opaque_box; | ||
396 | pixman_region32_init(&surface_opaque_box); | ||
397 | pixman_region32_copy(&surface_opaque_box, &wlr_surface->opaque_region); | ||
398 | pixman_region32_translate(&surface_opaque_box, | ||
399 | sway_layer_surface->geo.x, sway_layer_surface->geo.y); | ||
400 | pixman_region_overlap_t contains = | ||
401 | pixman_region32_contains_rectangle(&surface_opaque_box, &output_box); | ||
402 | pixman_region32_fini(&surface_opaque_box); | ||
403 | |||
404 | if (contains == PIXMAN_REGION_IN) { | ||
405 | return true; | ||
406 | } | ||
407 | } | 168 | } |
408 | return false; | ||
409 | } | ||
410 | |||
411 | struct send_frame_done_data { | ||
412 | struct timespec when; | ||
413 | int msec_until_refresh; | ||
414 | }; | ||
415 | |||
416 | static void send_frame_done_iterator(struct sway_output *output, | ||
417 | struct sway_view *view, struct wlr_surface *surface, | ||
418 | struct wlr_box *box, void *user_data) { | ||
419 | int view_max_render_time = 0; | ||
420 | if (view != NULL) { | ||
421 | view_max_render_time = view->max_render_time; | ||
422 | } | ||
423 | |||
424 | struct send_frame_done_data *data = user_data; | ||
425 | 169 | ||
426 | int delay = data->msec_until_refresh - output->max_render_time | 170 | int delay = data->msec_until_refresh - output->max_render_time |
427 | - view_max_render_time; | 171 | - view_max_render_time; |
428 | 172 | ||
429 | if (output->max_render_time == 0 || view_max_render_time == 0 || delay < 1) { | 173 | struct buffer_timer *timer = NULL; |
430 | wlr_surface_send_frame_done(surface, &data->when); | ||
431 | } else { | ||
432 | struct sway_surface *sway_surface = surface->data; | ||
433 | wl_event_source_timer_update(sway_surface->frame_done_timer, delay); | ||
434 | } | ||
435 | } | ||
436 | 174 | ||
437 | static void send_frame_done(struct sway_output *output, struct send_frame_done_data *data) { | 175 | if (output->max_render_time != 0 && view_max_render_time != 0 && delay > 0) { |
438 | output_for_each_surface(output, send_frame_done_iterator, data); | 176 | timer = buffer_timer_get_or_create(buffer); |
439 | } | ||
440 | |||
441 | static void count_surface_iterator(struct sway_output *output, | ||
442 | struct sway_view *view, struct wlr_surface *surface, | ||
443 | struct wlr_box *box, void *data) { | ||
444 | size_t *n = data; | ||
445 | (*n)++; | ||
446 | } | ||
447 | |||
448 | static bool scan_out_fullscreen_view(struct sway_output *output, | ||
449 | struct sway_view *view) { | ||
450 | struct wlr_output *wlr_output = output->wlr_output; | ||
451 | struct sway_workspace *workspace = output->current.active_workspace; | ||
452 | if (!sway_assert(workspace, "Expected an active workspace")) { | ||
453 | return false; | ||
454 | } | ||
455 | |||
456 | if (server.session_lock.locked) { | ||
457 | return false; | ||
458 | } | 177 | } |
459 | 178 | ||
460 | if (!wl_list_empty(&view->saved_buffers)) { | 179 | if (timer) { |
461 | 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); | ||
462 | } | 183 | } |
184 | } | ||
463 | 185 | ||
464 | for (int i = 0; i < workspace->current.floating->length; ++i) { | 186 | static enum wlr_scale_filter_mode get_scale_filter(struct sway_output *output, |
465 | struct sway_container *floater = | 187 | struct wlr_scene_buffer *buffer) { |
466 | workspace->current.floating->items[i]; | 188 | // if we are scaling down, we should always choose linear |
467 | if (container_is_transient_for(floater, view->container)) { | 189 | if (buffer->dst_width > 0 && buffer->dst_height > 0 && ( |
468 | return false; | 190 | buffer->dst_width < buffer->buffer_width || |
469 | } | 191 | buffer->dst_height < buffer->buffer_height)) { |
192 | return WLR_SCALE_FILTER_BILINEAR; | ||
470 | } | 193 | } |
471 | 194 | ||
472 | #if HAVE_XWAYLAND | 195 | switch (output->scale_filter) { |
473 | if (!wl_list_empty(&root->xwayland_unmanaged)) { | 196 | case SCALE_FILTER_LINEAR: |
474 | return false; | 197 | return WLR_SCALE_FILTER_BILINEAR; |
198 | case SCALE_FILTER_NEAREST: | ||
199 | return WLR_SCALE_FILTER_NEAREST; | ||
200 | default: | ||
201 | abort(); // unreachable | ||
475 | } | 202 | } |
476 | #endif | 203 | } |
477 | 204 | ||
478 | if (!wl_list_empty(&output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY])) { | 205 | static void output_configure_scene(struct sway_output *output, |
479 | return false; | 206 | struct wlr_scene_node *node, float opacity) { |
480 | } | 207 | if (!node->enabled) { |
481 | if (!wl_list_empty(&root->drag_icons)) { | 208 | return; |
482 | return false; | ||
483 | } | 209 | } |
484 | 210 | ||
485 | struct wlr_surface *surface = view->surface; | 211 | struct sway_container *con = |
486 | if (surface == NULL) { | 212 | scene_descriptor_try_get(node, SWAY_SCENE_DESC_CONTAINER); |
487 | return false; | 213 | if (con) { |
488 | } | 214 | opacity = con->alpha; |
489 | size_t n_surfaces = 0; | ||
490 | output_view_for_each_surface(output, view, | ||
491 | count_surface_iterator, &n_surfaces); | ||
492 | if (n_surfaces != 1) { | ||
493 | return false; | ||
494 | } | 215 | } |
495 | 216 | ||
496 | if (surface->buffer == NULL) { | 217 | if (node->type == WLR_SCENE_NODE_BUFFER) { |
497 | return false; | 218 | struct wlr_scene_buffer *buffer = wlr_scene_buffer_from_node(node); |
498 | } | ||
499 | 219 | ||
500 | if ((float)surface->current.scale != wlr_output->scale || | 220 | // hack: don't call the scene setter because that will damage all outputs |
501 | surface->current.transform != wlr_output->transform) { | 221 | // We don't want to damage outputs that aren't our current output that |
502 | return false; | 222 | // we're configuring |
503 | } | 223 | buffer->filter_mode = get_scale_filter(output, buffer); |
504 | 224 | ||
505 | wlr_output_attach_buffer(wlr_output, &surface->buffer->base); | 225 | wlr_scene_buffer_set_opacity(buffer, opacity); |
506 | if (!wlr_output_test(wlr_output)) { | 226 | } else if (node->type == WLR_SCENE_NODE_TREE) { |
507 | 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 | } | ||
508 | } | 232 | } |
509 | |||
510 | wlr_presentation_surface_sampled_on_output(server.presentation, surface, | ||
511 | wlr_output); | ||
512 | |||
513 | return wlr_output_commit(wlr_output); | ||
514 | } | 233 | } |
515 | 234 | ||
516 | static int output_repaint_timer_handler(void *data) { | 235 | static int output_repaint_timer_handler(void *data) { |
517 | struct sway_output *output = data; | 236 | struct sway_output *output = data; |
518 | if (output->wlr_output == NULL) { | ||
519 | return 0; | ||
520 | } | ||
521 | 237 | ||
522 | output->wlr_output->frame_pending = false; | 238 | if (!output->enabled) { |
523 | |||
524 | struct sway_workspace *workspace = output->current.active_workspace; | ||
525 | if (workspace == NULL) { | ||
526 | return 0; | 239 | return 0; |
527 | } | 240 | } |
528 | 241 | ||
529 | struct sway_container *fullscreen_con = root->fullscreen_global; | 242 | output->wlr_output->frame_pending = false; |
530 | if (!fullscreen_con) { | ||
531 | fullscreen_con = workspace->current.fullscreen; | ||
532 | } | ||
533 | 243 | ||
534 | if (fullscreen_con && fullscreen_con->view && !debug.noscanout) { | 244 | output_configure_scene(output, &root->root_scene->tree.node, 1.0f); |
535 | // Try to scan-out the fullscreen view | ||
536 | static bool last_scanned_out = false; | ||
537 | bool scanned_out = | ||
538 | scan_out_fullscreen_view(output, fullscreen_con->view); | ||
539 | 245 | ||
540 | if (scanned_out && !last_scanned_out) { | 246 | if (output->gamma_lut_changed) { |
541 | sway_log(SWAY_DEBUG, "Scanning out fullscreen view on %s", | 247 | struct wlr_output_state pending; |
542 | 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; | ||
543 | } | 251 | } |
544 | if (last_scanned_out && !scanned_out) { | 252 | |
545 | sway_log(SWAY_DEBUG, "Stopping fullscreen view scan out on %s", | 253 | output->gamma_lut_changed = false; |
546 | output->wlr_output->name); | 254 | struct wlr_gamma_control_v1 *gamma_control = |
547 | output_damage_whole(output); | 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; | ||
548 | } | 260 | } |
549 | last_scanned_out = scanned_out; | ||
550 | 261 | ||
551 | 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); | ||
552 | return 0; | 265 | return 0; |
553 | } | 266 | } |
554 | } | ||
555 | 267 | ||
556 | bool needs_frame; | 268 | wlr_output_state_finish(&pending); |
557 | pixman_region32_t damage; | ||
558 | pixman_region32_init(&damage); | ||
559 | if (!wlr_output_damage_attach_render(output->damage, | ||
560 | &needs_frame, &damage)) { | ||
561 | return 0; | 269 | return 0; |
562 | } | 270 | } |
563 | 271 | ||
564 | if (needs_frame) { | 272 | wlr_scene_output_commit(output->scene_output, NULL); |
565 | struct timespec now; | ||
566 | clock_gettime(CLOCK_MONOTONIC, &now); | ||
567 | |||
568 | output_render(output, &now, &damage); | ||
569 | } else { | ||
570 | wlr_output_rollback(output->wlr_output); | ||
571 | } | ||
572 | |||
573 | pixman_region32_fini(&damage); | ||
574 | |||
575 | return 0; | 273 | return 0; |
576 | } | 274 | } |
577 | 275 | ||
578 | static void damage_handle_frame(struct wl_listener *listener, void *user_data) { | 276 | static void handle_frame(struct wl_listener *listener, void *user_data) { |
579 | struct sway_output *output = | 277 | struct sway_output *output = |
580 | wl_container_of(listener, output, damage_frame); | 278 | wl_container_of(listener, output, frame); |
581 | if (!output->enabled || !output->wlr_output->enabled) { | 279 | if (!output->enabled || !output->wlr_output->enabled) { |
582 | return; | 280 | return; |
583 | } | 281 | } |
@@ -588,9 +286,7 @@ static void damage_handle_frame(struct wl_listener *listener, void *user_data) { | |||
588 | 286 | ||
589 | if (output->max_render_time != 0) { | 287 | if (output->max_render_time != 0) { |
590 | struct timespec now; | 288 | struct timespec now; |
591 | clockid_t presentation_clock | 289 | clock_gettime(CLOCK_MONOTONIC, &now); |
592 | = wlr_backend_get_presentation_clock(server.backend); | ||
593 | clock_gettime(presentation_clock, &now); | ||
594 | 290 | ||
595 | const long NSEC_IN_SECONDS = 1000000000; | 291 | const long NSEC_IN_SECONDS = 1000000000; |
596 | struct timespec predicted_refresh = output->last_presentation; | 292 | struct timespec predicted_refresh = output->last_presentation; |
@@ -637,116 +333,8 @@ static void damage_handle_frame(struct wl_listener *listener, void *user_data) { | |||
637 | struct send_frame_done_data data = {0}; | 333 | struct send_frame_done_data data = {0}; |
638 | clock_gettime(CLOCK_MONOTONIC, &data.when); | 334 | clock_gettime(CLOCK_MONOTONIC, &data.when); |
639 | data.msec_until_refresh = msec_until_refresh; | 335 | data.msec_until_refresh = msec_until_refresh; |
640 | send_frame_done(output, &data); | 336 | data.output = output; |
641 | } | 337 | wlr_scene_output_for_each_buffer(output->scene_output, send_frame_done_iterator, &data); |
642 | |||
643 | void output_damage_whole(struct sway_output *output) { | ||
644 | // The output can exist with no wlr_output if it's just been disconnected | ||
645 | // and the transaction to evacuate it has't completed yet. | ||
646 | if (output && output->wlr_output && output->damage) { | ||
647 | wlr_output_damage_add_whole(output->damage); | ||
648 | } | ||
649 | } | ||
650 | |||
651 | static void damage_surface_iterator(struct sway_output *output, | ||
652 | struct sway_view *view, struct wlr_surface *surface, | ||
653 | struct wlr_box *_box, void *_data) { | ||
654 | bool *data = _data; | ||
655 | bool whole = *data; | ||
656 | |||
657 | struct wlr_box box = *_box; | ||
658 | scale_box(&box, output->wlr_output->scale); | ||
659 | |||
660 | pixman_region32_t damage; | ||
661 | pixman_region32_init(&damage); | ||
662 | wlr_surface_get_effective_damage(surface, &damage); | ||
663 | wlr_region_scale(&damage, &damage, output->wlr_output->scale); | ||
664 | if (ceil(output->wlr_output->scale) > surface->current.scale) { | ||
665 | // When scaling up a surface, it'll become blurry so we need to | ||
666 | // expand the damage region | ||
667 | wlr_region_expand(&damage, &damage, | ||
668 | ceil(output->wlr_output->scale) - surface->current.scale); | ||
669 | } | ||
670 | pixman_region32_translate(&damage, box.x, box.y); | ||
671 | wlr_output_damage_add(output->damage, &damage); | ||
672 | pixman_region32_fini(&damage); | ||
673 | |||
674 | if (whole) { | ||
675 | wlr_output_damage_add_box(output->damage, &box); | ||
676 | } | ||
677 | |||
678 | if (!wl_list_empty(&surface->current.frame_callback_list)) { | ||
679 | wlr_output_schedule_frame(output->wlr_output); | ||
680 | } | ||
681 | } | ||
682 | |||
683 | void output_damage_surface(struct sway_output *output, double ox, double oy, | ||
684 | struct wlr_surface *surface, bool whole) { | ||
685 | output_surface_for_each_surface(output, surface, ox, oy, | ||
686 | damage_surface_iterator, &whole); | ||
687 | } | ||
688 | |||
689 | void output_damage_from_view(struct sway_output *output, | ||
690 | struct sway_view *view) { | ||
691 | if (!view_is_visible(view)) { | ||
692 | return; | ||
693 | } | ||
694 | bool whole = false; | ||
695 | output_view_for_each_surface(output, view, damage_surface_iterator, &whole); | ||
696 | } | ||
697 | |||
698 | // Expecting an unscaled box in layout coordinates | ||
699 | void output_damage_box(struct sway_output *output, struct wlr_box *_box) { | ||
700 | struct wlr_box box; | ||
701 | memcpy(&box, _box, sizeof(struct wlr_box)); | ||
702 | box.x -= output->lx; | ||
703 | box.y -= output->ly; | ||
704 | scale_box(&box, output->wlr_output->scale); | ||
705 | wlr_output_damage_add_box(output->damage, &box); | ||
706 | } | ||
707 | |||
708 | static void damage_child_views_iterator(struct sway_container *con, | ||
709 | void *data) { | ||
710 | if (!con->view || !view_is_visible(con->view)) { | ||
711 | return; | ||
712 | } | ||
713 | struct sway_output *output = data; | ||
714 | bool whole = true; | ||
715 | output_view_for_each_surface(output, con->view, damage_surface_iterator, | ||
716 | &whole); | ||
717 | } | ||
718 | |||
719 | void output_damage_whole_container(struct sway_output *output, | ||
720 | struct sway_container *con) { | ||
721 | // Pad the box by 1px, because the width is a double and might be a fraction | ||
722 | struct wlr_box box = { | ||
723 | .x = con->current.x - output->lx - 1, | ||
724 | .y = con->current.y - output->ly - 1, | ||
725 | .width = con->current.width + 2, | ||
726 | .height = con->current.height + 2, | ||
727 | }; | ||
728 | scale_box(&box, output->wlr_output->scale); | ||
729 | wlr_output_damage_add_box(output->damage, &box); | ||
730 | // Damage subsurfaces as well, which may extend outside the box | ||
731 | if (con->view) { | ||
732 | damage_child_views_iterator(con, output); | ||
733 | } else { | ||
734 | container_for_each_child(con, damage_child_views_iterator, output); | ||
735 | } | ||
736 | } | ||
737 | |||
738 | static void damage_handle_destroy(struct wl_listener *listener, void *data) { | ||
739 | struct sway_output *output = | ||
740 | wl_container_of(listener, output, damage_destroy); | ||
741 | if (!output->enabled) { | ||
742 | return; | ||
743 | } | ||
744 | output_disable(output); | ||
745 | |||
746 | wl_list_remove(&output->damage_destroy.link); | ||
747 | wl_list_remove(&output->damage_frame.link); | ||
748 | |||
749 | transaction_commit_dirty(); | ||
750 | } | 338 | } |
751 | 339 | ||
752 | static void update_output_manager_config(struct sway_server *server) { | 340 | static void update_output_manager_config(struct sway_server *server) { |
@@ -764,33 +352,36 @@ static void update_output_manager_config(struct sway_server *server) { | |||
764 | wlr_output_layout_get_box(root->output_layout, | 352 | wlr_output_layout_get_box(root->output_layout, |
765 | output->wlr_output, &output_box); | 353 | output->wlr_output, &output_box); |
766 | // We mark the output enabled when it's switched off but not disabled | 354 | // We mark the output enabled when it's switched off but not disabled |
767 | config_head->state.enabled = output->current_mode != NULL && output->enabled; | 355 | config_head->state.enabled = !wlr_box_empty(&output_box); |
768 | config_head->state.mode = output->current_mode; | 356 | config_head->state.x = output_box.x; |
769 | if (!wlr_box_empty(&output_box)) { | 357 | config_head->state.y = output_box.y; |
770 | config_head->state.x = output_box.x; | ||
771 | config_head->state.y = output_box.y; | ||
772 | } | ||
773 | } | 358 | } |
774 | 359 | ||
775 | 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(); | ||
776 | } | 363 | } |
777 | 364 | ||
778 | static void handle_destroy(struct wl_listener *listener, void *data) { | 365 | static void begin_destroy(struct sway_output *output) { |
779 | struct sway_output *output = wl_container_of(listener, output, destroy); | ||
780 | struct sway_server *server = output->server; | 366 | struct sway_server *server = output->server; |
781 | output_begin_destroy(output); | ||
782 | 367 | ||
783 | if (output->enabled) { | 368 | if (output->enabled) { |
784 | output_disable(output); | 369 | output_disable(output); |
785 | } | 370 | } |
786 | 371 | ||
372 | output_begin_destroy(output); | ||
373 | |||
787 | wl_list_remove(&output->link); | 374 | wl_list_remove(&output->link); |
788 | 375 | ||
376 | wl_list_remove(&output->layout_destroy.link); | ||
789 | wl_list_remove(&output->destroy.link); | 377 | wl_list_remove(&output->destroy.link); |
790 | wl_list_remove(&output->commit.link); | 378 | wl_list_remove(&output->commit.link); |
791 | wl_list_remove(&output->mode.link); | ||
792 | 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); | ||
793 | 382 | ||
383 | wlr_scene_output_destroy(output->scene_output); | ||
384 | output->scene_output = NULL; | ||
794 | output->wlr_output->data = NULL; | 385 | output->wlr_output->data = NULL; |
795 | output->wlr_output = NULL; | 386 | output->wlr_output = NULL; |
796 | 387 | ||
@@ -799,34 +390,14 @@ static void handle_destroy(struct wl_listener *listener, void *data) { | |||
799 | update_output_manager_config(server); | 390 | update_output_manager_config(server); |
800 | } | 391 | } |
801 | 392 | ||
802 | static void handle_mode(struct wl_listener *listener, void *data) { | 393 | static void handle_destroy(struct wl_listener *listener, void *data) { |
803 | struct sway_output *output = wl_container_of(listener, output, mode); | 394 | struct sway_output *output = wl_container_of(listener, output, destroy); |
804 | if (!output->enabled && !output->enabling) { | 395 | begin_destroy(output); |
805 | struct output_config *oc = find_output_config(output); | ||
806 | if (output->wlr_output->current_mode != NULL && | ||
807 | (!oc || oc->enabled)) { | ||
808 | // We want to enable this output, but it didn't work last time, | ||
809 | // possibly because we hadn't enough CRTCs. Try again now that the | ||
810 | // output has a mode. | ||
811 | sway_log(SWAY_DEBUG, "Output %s has gained a CRTC, " | ||
812 | "trying to enable it", output->wlr_output->name); | ||
813 | apply_output_config(oc, output); | ||
814 | } | ||
815 | return; | ||
816 | } | ||
817 | if (!output->enabled) { | ||
818 | return; | ||
819 | } | ||
820 | arrange_layers(output); | ||
821 | arrange_output(output); | ||
822 | transaction_commit_dirty(); | ||
823 | |||
824 | update_output_manager_config(output->server); | ||
825 | } | 396 | } |
826 | 397 | ||
827 | static void update_textures(struct sway_container *con, void *data) { | 398 | static void handle_layout_destroy(struct wl_listener *listener, void *data) { |
828 | container_update_title_textures(con); | 399 | struct sway_output *output = wl_container_of(listener, output, layout_destroy); |
829 | container_update_marks_textures(con); | 400 | begin_destroy(output); |
830 | } | 401 | } |
831 | 402 | ||
832 | static void handle_commit(struct wl_listener *listener, void *data) { | 403 | static void handle_commit(struct wl_listener *listener, void *data) { |
@@ -837,17 +408,21 @@ static void handle_commit(struct wl_listener *listener, void *data) { | |||
837 | return; | 408 | return; |
838 | } | 409 | } |
839 | 410 | ||
840 | if (event->committed & WLR_OUTPUT_STATE_SCALE) { | 411 | if (event->state->committed & ( |
841 | output_for_each_container(output, update_textures, NULL); | 412 | WLR_OUTPUT_STATE_MODE | |
842 | } | 413 | WLR_OUTPUT_STATE_TRANSFORM | |
843 | 414 | WLR_OUTPUT_STATE_SCALE)) { | |
844 | if (event->committed & (WLR_OUTPUT_STATE_TRANSFORM | WLR_OUTPUT_STATE_SCALE)) { | ||
845 | arrange_layers(output); | 415 | arrange_layers(output); |
846 | arrange_output(output); | 416 | arrange_output(output); |
847 | transaction_commit_dirty(); | 417 | transaction_commit_dirty(); |
848 | 418 | ||
849 | update_output_manager_config(output->server); | 419 | update_output_manager_config(output->server); |
850 | } | 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 | } | ||
851 | } | 426 | } |
852 | 427 | ||
853 | static void handle_present(struct wl_listener *listener, void *data) { | 428 | static void handle_present(struct wl_listener *listener, void *data) { |
@@ -862,6 +437,13 @@ static void handle_present(struct wl_listener *listener, void *data) { | |||
862 | output->refresh_nsec = output_event->refresh; | 437 | output->refresh_nsec = output_event->refresh; |
863 | } | 438 | } |
864 | 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 | |||
865 | static unsigned int last_headless_num = 0; | 447 | static unsigned int last_headless_num = 0; |
866 | 448 | ||
867 | void handle_new_output(struct wl_listener *listener, void *data) { | 449 | void handle_new_output(struct wl_listener *listener, void *data) { |
@@ -883,10 +465,14 @@ void handle_new_output(struct wl_listener *listener, void *data) { | |||
883 | 465 | ||
884 | if (wlr_output->non_desktop) { | 466 | if (wlr_output->non_desktop) { |
885 | sway_log(SWAY_DEBUG, "Not configuring non-desktop output"); | 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 | ||
886 | if (server->drm_lease_manager) { | 470 | if (server->drm_lease_manager) { |
887 | wlr_drm_lease_v1_manager_offer_output(server->drm_lease_manager, | 471 | wlr_drm_lease_v1_manager_offer_output(server->drm_lease_manager, |
888 | wlr_output); | 472 | wlr_output); |
889 | } | 473 | } |
474 | #endif | ||
475 | list_add(root->non_desktop_outputs, non_desktop); | ||
890 | return; | 476 | return; |
891 | } | 477 | } |
892 | 478 | ||
@@ -896,32 +482,46 @@ void handle_new_output(struct wl_listener *listener, void *data) { | |||
896 | return; | 482 | return; |
897 | } | 483 | } |
898 | 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 | } | ||
493 | |||
899 | struct sway_output *output = output_create(wlr_output); | 494 | struct sway_output *output = output_create(wlr_output); |
900 | if (!output) { | 495 | if (!output) { |
496 | sway_log(SWAY_ERROR, "Failed to create a sway output"); | ||
497 | wlr_scene_output_destroy(scene_output); | ||
901 | return; | 498 | return; |
902 | } | 499 | } |
500 | |||
903 | output->server = server; | 501 | output->server = server; |
904 | output->damage = wlr_output_damage_create(wlr_output); | 502 | output->scene_output = scene_output; |
905 | 503 | ||
504 | wl_signal_add(&root->output_layout->events.destroy, &output->layout_destroy); | ||
505 | output->layout_destroy.notify = handle_layout_destroy; | ||
906 | wl_signal_add(&wlr_output->events.destroy, &output->destroy); | 506 | wl_signal_add(&wlr_output->events.destroy, &output->destroy); |
907 | output->destroy.notify = handle_destroy; | 507 | output->destroy.notify = handle_destroy; |
908 | wl_signal_add(&wlr_output->events.commit, &output->commit); | 508 | wl_signal_add(&wlr_output->events.commit, &output->commit); |
909 | output->commit.notify = handle_commit; | 509 | output->commit.notify = handle_commit; |
910 | wl_signal_add(&wlr_output->events.mode, &output->mode); | ||
911 | output->mode.notify = handle_mode; | ||
912 | wl_signal_add(&wlr_output->events.present, &output->present); | 510 | wl_signal_add(&wlr_output->events.present, &output->present); |
913 | output->present.notify = handle_present; | 511 | output->present.notify = handle_present; |
914 | wl_signal_add(&output->damage->events.frame, &output->damage_frame); | 512 | wl_signal_add(&wlr_output->events.frame, &output->frame); |
915 | output->damage_frame.notify = damage_handle_frame; | 513 | output->frame.notify = handle_frame; |
916 | wl_signal_add(&output->damage->events.destroy, &output->damage_destroy); | 514 | wl_signal_add(&wlr_output->events.request_state, &output->request_state); |
917 | output->damage_destroy.notify = damage_handle_destroy; | 515 | output->request_state.notify = handle_request_state; |
918 | 516 | ||
919 | 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, |
920 | output_repaint_timer_handler, output); | 518 | output_repaint_timer_handler, output); |
921 | 519 | ||
922 | struct output_config *oc = find_output_config(output); | 520 | if (server->session_lock.lock) { |
923 | apply_output_config(oc, output); | 521 | sway_session_lock_add_output(server->session_lock.lock, output); |
924 | free_output_config(oc); | 522 | } |
523 | |||
524 | apply_all_output_configs(); | ||
925 | 525 | ||
926 | transaction_commit_dirty(); | 526 | transaction_commit_dirty(); |
927 | 527 | ||
@@ -935,62 +535,104 @@ void handle_output_layout_change(struct wl_listener *listener, | |||
935 | update_output_manager_config(server); | 535 | update_output_manager_config(server); |
936 | } | 536 | } |
937 | 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 | |||
938 | static void output_manager_apply(struct sway_server *server, | 581 | static void output_manager_apply(struct sway_server *server, |
939 | struct wlr_output_configuration_v1 *config, bool test_only) { | 582 | struct wlr_output_configuration_v1 *config, bool test_only) { |
940 | // TODO: perform atomic tests on the whole backend atomically | 583 | size_t configs_len = wl_list_length(&root->all_outputs); |
941 | 584 | struct matched_output_config *configs = calloc(configs_len, sizeof(*configs)); | |
942 | struct wlr_output_configuration_head_v1 *config_head; | 585 | if (!configs) { |
943 | // First disable outputs we need to disable | 586 | return; |
944 | bool ok = true; | 587 | } |
945 | wl_list_for_each(config_head, &config->heads, link) { | 588 | |
946 | struct wlr_output *wlr_output = config_head->state.output; | 589 | int config_idx = 0; |
947 | struct sway_output *output = wlr_output->data; | 590 | struct sway_output *sway_output; |
948 | 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--; | ||
949 | continue; | 594 | continue; |
950 | } | 595 | } |
951 | struct output_config *oc = new_output_config(output->wlr_output->name); | ||
952 | oc->enabled = false; | ||
953 | 596 | ||
954 | if (test_only) { | 597 | struct matched_output_config *cfg = &configs[config_idx++]; |
955 | ok &= test_output_config(oc, output); | 598 | cfg->output = sway_output; |
956 | } else { | ||
957 | oc = store_output_config(oc); | ||
958 | ok &= apply_output_config(oc, output); | ||
959 | } | ||
960 | } | ||
961 | 599 | ||
962 | // Then enable outputs that need to | 600 | struct wlr_output_configuration_head_v1 *config_head; |
963 | wl_list_for_each(config_head, &config->heads, link) { | 601 | wl_list_for_each(config_head, &config->heads, link) { |
964 | struct wlr_output *wlr_output = config_head->state.output; | 602 | if (config_head->state.output == sway_output->wlr_output) { |
965 | struct sway_output *output = wlr_output->data; | 603 | cfg->config = output_config_for_config_head(config_head, sway_output); |
966 | if (!config_head->state.enabled) { | 604 | break; |
967 | continue; | 605 | } |
968 | } | 606 | } |
969 | struct output_config *oc = new_output_config(output->wlr_output->name); | 607 | if (!cfg->config) { |
970 | oc->enabled = true; | 608 | cfg->config = find_output_config(sway_output); |
971 | if (config_head->state.mode != NULL) { | ||
972 | struct wlr_output_mode *mode = config_head->state.mode; | ||
973 | oc->width = mode->width; | ||
974 | oc->height = mode->height; | ||
975 | oc->refresh_rate = mode->refresh / 1000.f; | ||
976 | } else { | ||
977 | oc->width = config_head->state.custom_mode.width; | ||
978 | oc->height = config_head->state.custom_mode.height; | ||
979 | oc->refresh_rate = | ||
980 | config_head->state.custom_mode.refresh / 1000.f; | ||
981 | } | 609 | } |
982 | oc->x = config_head->state.x; | 610 | } |
983 | oc->y = config_head->state.y; | ||
984 | oc->transform = config_head->state.transform; | ||
985 | oc->scale = config_head->state.scale; | ||
986 | 611 | ||
987 | if (test_only) { | 612 | sort_output_configs_by_priority(configs, configs_len); |
988 | 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); | ||
989 | } else { | 631 | } else { |
990 | oc = store_output_config(oc); | 632 | free_output_config(cfg->config); |
991 | ok &= apply_output_config(oc, output); | ||
992 | } | 633 | } |
993 | } | 634 | } |
635 | free(configs); | ||
994 | 636 | ||
995 | if (ok) { | 637 | if (ok) { |
996 | wlr_output_configuration_v1_send_succeeded(config); | 638 | wlr_output_configuration_v1_send_succeeded(config); |
@@ -1034,6 +676,6 @@ void handle_output_power_manager_set_mode(struct wl_listener *listener, | |||
1034 | oc->power = 1; | 676 | oc->power = 1; |
1035 | break; | 677 | break; |
1036 | } | 678 | } |
1037 | oc = store_output_config(oc); | 679 | store_output_config(oc); |
1038 | apply_output_config(oc, output); | 680 | apply_all_output_configs(); |
1039 | } | 681 | } |
diff --git a/sway/desktop/render.c b/sway/desktop/render.c deleted file mode 100644 index ed9ad490..00000000 --- a/sway/desktop/render.c +++ /dev/null | |||
@@ -1,1204 +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_buffer.h> | ||
11 | #include <wlr/types/wlr_matrix.h> | ||
12 | #include <wlr/types/wlr_output_damage.h> | ||
13 | #include <wlr/types/wlr_output_layout.h> | ||
14 | #include <wlr/types/wlr_output.h> | ||
15 | #include <wlr/types/wlr_compositor.h> | ||
16 | #include <wlr/util/region.h> | ||
17 | #include "log.h" | ||
18 | #include "config.h" | ||
19 | #include "sway/config.h" | ||
20 | #include "sway/input/input-manager.h" | ||
21 | #include "sway/input/seat.h" | ||
22 | #include "sway/layers.h" | ||
23 | #include "sway/output.h" | ||
24 | #include "sway/server.h" | ||
25 | #include "sway/tree/arrange.h" | ||
26 | #include "sway/tree/container.h" | ||
27 | #include "sway/tree/root.h" | ||
28 | #include "sway/tree/view.h" | ||
29 | #include "sway/tree/workspace.h" | ||
30 | |||
31 | struct render_data { | ||
32 | pixman_region32_t *damage; | ||
33 | float alpha; | ||
34 | struct wlr_box *clip_box; | ||
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_output->renderer; | ||
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 = wlr_output->renderer; | ||
104 | struct sway_output *output = wlr_output->data; | ||
105 | |||
106 | pixman_region32_t damage; | ||
107 | pixman_region32_init(&damage); | ||
108 | pixman_region32_union_rect(&damage, &damage, dst_box->x, dst_box->y, | ||
109 | dst_box->width, dst_box->height); | ||
110 | pixman_region32_intersect(&damage, &damage, output_damage); | ||
111 | bool damaged = pixman_region32_not_empty(&damage); | ||
112 | if (!damaged) { | ||
113 | goto damage_finish; | ||
114 | } | ||
115 | |||
116 | int nrects; | ||
117 | pixman_box32_t *rects = pixman_region32_rectangles(&damage, &nrects); | ||
118 | for (int i = 0; i < nrects; ++i) { | ||
119 | scissor_output(wlr_output, &rects[i]); | ||
120 | set_scale_filter(wlr_output, texture, output->scale_filter); | ||
121 | if (src_box != NULL) { | ||
122 | wlr_render_subtexture_with_matrix(renderer, texture, src_box, matrix, alpha); | ||
123 | } else { | ||
124 | wlr_render_texture_with_matrix(renderer, texture, matrix, alpha); | ||
125 | } | ||
126 | } | ||
127 | |||
128 | damage_finish: | ||
129 | pixman_region32_fini(&damage); | ||
130 | } | ||
131 | |||
132 | static void render_surface_iterator(struct sway_output *output, | ||
133 | struct sway_view *view, struct wlr_surface *surface, | ||
134 | struct wlr_box *_box, void *_data) { | ||
135 | struct render_data *data = _data; | ||
136 | struct wlr_output *wlr_output = output->wlr_output; | ||
137 | pixman_region32_t *output_damage = data->damage; | ||
138 | float alpha = data->alpha; | ||
139 | |||
140 | struct wlr_texture *texture = wlr_surface_get_texture(surface); | ||
141 | if (!texture) { | ||
142 | return; | ||
143 | } | ||
144 | |||
145 | struct wlr_fbox src_box; | ||
146 | wlr_surface_get_buffer_source_box(surface, &src_box); | ||
147 | |||
148 | struct wlr_box proj_box = *_box; | ||
149 | scale_box(&proj_box, wlr_output->scale); | ||
150 | |||
151 | float matrix[9]; | ||
152 | enum wl_output_transform transform = | ||
153 | wlr_output_transform_invert(surface->current.transform); | ||
154 | wlr_matrix_project_box(matrix, &proj_box, transform, 0.0, | ||
155 | wlr_output->transform_matrix); | ||
156 | |||
157 | struct wlr_box dst_box = *_box; | ||
158 | struct wlr_box *clip_box = data->clip_box; | ||
159 | if (clip_box != NULL) { | ||
160 | dst_box.width = fmin(dst_box.width, clip_box->width); | ||
161 | dst_box.height = fmin(dst_box.height, clip_box->height); | ||
162 | } | ||
163 | scale_box(&dst_box, wlr_output->scale); | ||
164 | |||
165 | render_texture(wlr_output, output_damage, texture, | ||
166 | &src_box, &dst_box, matrix, alpha); | ||
167 | |||
168 | wlr_presentation_surface_sampled_on_output(server.presentation, surface, | ||
169 | wlr_output); | ||
170 | } | ||
171 | |||
172 | static void render_layer_toplevel(struct sway_output *output, | ||
173 | pixman_region32_t *damage, struct wl_list *layer_surfaces) { | ||
174 | struct render_data data = { | ||
175 | .damage = damage, | ||
176 | .alpha = 1.0f, | ||
177 | }; | ||
178 | output_layer_for_each_toplevel_surface(output, layer_surfaces, | ||
179 | render_surface_iterator, &data); | ||
180 | } | ||
181 | |||
182 | static void render_layer_popups(struct sway_output *output, | ||
183 | pixman_region32_t *damage, struct wl_list *layer_surfaces) { | ||
184 | struct render_data data = { | ||
185 | .damage = damage, | ||
186 | .alpha = 1.0f, | ||
187 | }; | ||
188 | output_layer_for_each_popup_surface(output, layer_surfaces, | ||
189 | render_surface_iterator, &data); | ||
190 | } | ||
191 | |||
192 | #if HAVE_XWAYLAND | ||
193 | static void render_unmanaged(struct sway_output *output, | ||
194 | pixman_region32_t *damage, struct wl_list *unmanaged) { | ||
195 | struct render_data data = { | ||
196 | .damage = damage, | ||
197 | .alpha = 1.0f, | ||
198 | }; | ||
199 | output_unmanaged_for_each_surface(output, unmanaged, | ||
200 | render_surface_iterator, &data); | ||
201 | } | ||
202 | #endif | ||
203 | |||
204 | static void render_drag_icons(struct sway_output *output, | ||
205 | pixman_region32_t *damage, struct wl_list *drag_icons) { | ||
206 | struct render_data data = { | ||
207 | .damage = damage, | ||
208 | .alpha = 1.0f, | ||
209 | }; | ||
210 | output_drag_icons_for_each_surface(output, drag_icons, | ||
211 | render_surface_iterator, &data); | ||
212 | } | ||
213 | |||
214 | // _box.x and .y are expected to be layout-local | ||
215 | // _box.width and .height are expected to be output-buffer-local | ||
216 | void render_rect(struct sway_output *output, | ||
217 | pixman_region32_t *output_damage, const struct wlr_box *_box, | ||
218 | float color[static 4]) { | ||
219 | struct wlr_output *wlr_output = output->wlr_output; | ||
220 | struct wlr_renderer *renderer = wlr_output->renderer; | ||
221 | |||
222 | struct wlr_box box; | ||
223 | memcpy(&box, _box, sizeof(struct wlr_box)); | ||
224 | box.x -= output->lx * wlr_output->scale; | ||
225 | box.y -= output->ly * wlr_output->scale; | ||
226 | |||
227 | pixman_region32_t damage; | ||
228 | pixman_region32_init(&damage); | ||
229 | pixman_region32_union_rect(&damage, &damage, box.x, box.y, | ||
230 | box.width, box.height); | ||
231 | pixman_region32_intersect(&damage, &damage, output_damage); | ||
232 | bool damaged = pixman_region32_not_empty(&damage); | ||
233 | if (!damaged) { | ||
234 | goto damage_finish; | ||
235 | } | ||
236 | |||
237 | int nrects; | ||
238 | pixman_box32_t *rects = pixman_region32_rectangles(&damage, &nrects); | ||
239 | for (int i = 0; i < nrects; ++i) { | ||
240 | scissor_output(wlr_output, &rects[i]); | ||
241 | wlr_render_rect(renderer, &box, color, | ||
242 | wlr_output->transform_matrix); | ||
243 | } | ||
244 | |||
245 | damage_finish: | ||
246 | pixman_region32_fini(&damage); | ||
247 | } | ||
248 | |||
249 | void premultiply_alpha(float color[4], float opacity) { | ||
250 | color[3] *= opacity; | ||
251 | color[0] *= color[3]; | ||
252 | color[1] *= color[3]; | ||
253 | color[2] *= color[3]; | ||
254 | } | ||
255 | |||
256 | static void render_view_toplevels(struct sway_view *view, | ||
257 | struct sway_output *output, pixman_region32_t *damage, float alpha) { | ||
258 | struct render_data data = { | ||
259 | .damage = damage, | ||
260 | .alpha = alpha, | ||
261 | }; | ||
262 | struct wlr_box clip_box; | ||
263 | if (!container_is_current_floating(view->container)) { | ||
264 | // As we pass the geometry offsets to the surface iterator, we will | ||
265 | // need to account for the offsets in the clip dimensions. | ||
266 | clip_box.width = view->container->current.content_width + view->geometry.x; | ||
267 | clip_box.height = view->container->current.content_height + view->geometry.y; | ||
268 | data.clip_box = &clip_box; | ||
269 | } | ||
270 | // Render all toplevels without descending into popups | ||
271 | double ox = view->container->surface_x - | ||
272 | output->lx - view->geometry.x; | ||
273 | double oy = view->container->surface_y - | ||
274 | output->ly - view->geometry.y; | ||
275 | output_surface_for_each_surface(output, view->surface, ox, oy, | ||
276 | render_surface_iterator, &data); | ||
277 | } | ||
278 | |||
279 | static void render_view_popups(struct sway_view *view, | ||
280 | struct sway_output *output, pixman_region32_t *damage, float alpha) { | ||
281 | struct render_data data = { | ||
282 | .damage = damage, | ||
283 | .alpha = alpha, | ||
284 | }; | ||
285 | output_view_for_each_popup_surface(output, view, | ||
286 | render_surface_iterator, &data); | ||
287 | } | ||
288 | |||
289 | static void render_saved_view(struct sway_view *view, | ||
290 | struct sway_output *output, pixman_region32_t *damage, float alpha) { | ||
291 | struct wlr_output *wlr_output = output->wlr_output; | ||
292 | |||
293 | if (wl_list_empty(&view->saved_buffers)) { | ||
294 | return; | ||
295 | } | ||
296 | |||
297 | bool floating = container_is_current_floating(view->container); | ||
298 | |||
299 | struct sway_saved_buffer *saved_buf; | ||
300 | wl_list_for_each(saved_buf, &view->saved_buffers, link) { | ||
301 | if (!saved_buf->buffer->texture) { | ||
302 | continue; | ||
303 | } | ||
304 | |||
305 | struct wlr_box proj_box = { | ||
306 | .x = saved_buf->x - view->saved_geometry.x - output->lx, | ||
307 | .y = saved_buf->y - view->saved_geometry.y - output->ly, | ||
308 | .width = saved_buf->width, | ||
309 | .height = saved_buf->height, | ||
310 | }; | ||
311 | |||
312 | struct wlr_box output_box = { | ||
313 | .width = output->width, | ||
314 | .height = output->height, | ||
315 | }; | ||
316 | |||
317 | struct wlr_box intersection; | ||
318 | bool intersects = wlr_box_intersection(&intersection, &output_box, &proj_box); | ||
319 | if (!intersects) { | ||
320 | continue; | ||
321 | } | ||
322 | |||
323 | struct wlr_box dst_box = proj_box; | ||
324 | scale_box(&proj_box, wlr_output->scale); | ||
325 | |||
326 | float matrix[9]; | ||
327 | enum wl_output_transform transform = wlr_output_transform_invert(saved_buf->transform); | ||
328 | wlr_matrix_project_box(matrix, &proj_box, transform, 0, | ||
329 | wlr_output->transform_matrix); | ||
330 | |||
331 | if (!floating) { | ||
332 | dst_box.width = fmin(dst_box.width, | ||
333 | view->container->current.content_width - | ||
334 | (saved_buf->x - view->container->current.content_x) + view->saved_geometry.x); | ||
335 | dst_box.height = fmin(dst_box.height, | ||
336 | view->container->current.content_height - | ||
337 | (saved_buf->y - view->container->current.content_y) + view->saved_geometry.y); | ||
338 | } | ||
339 | scale_box(&dst_box, wlr_output->scale); | ||
340 | |||
341 | render_texture(wlr_output, damage, saved_buf->buffer->texture, | ||
342 | &saved_buf->source_box, &dst_box, matrix, alpha); | ||
343 | } | ||
344 | |||
345 | // FIXME: we should set the surface that this saved buffer originates from | ||
346 | // as sampled here. | ||
347 | // https://github.com/swaywm/sway/pull/4465#discussion_r321082059 | ||
348 | } | ||
349 | |||
350 | /** | ||
351 | * Render a view's surface and left/bottom/right borders. | ||
352 | */ | ||
353 | static void render_view(struct sway_output *output, pixman_region32_t *damage, | ||
354 | struct sway_container *con, struct border_colors *colors) { | ||
355 | struct sway_view *view = con->view; | ||
356 | if (!wl_list_empty(&view->saved_buffers)) { | ||
357 | render_saved_view(view, output, damage, view->container->alpha); | ||
358 | } else if (view->surface) { | ||
359 | render_view_toplevels(view, output, damage, view->container->alpha); | ||
360 | } | ||
361 | |||
362 | if (con->current.border == B_NONE || con->current.border == B_CSD) { | ||
363 | return; | ||
364 | } | ||
365 | |||
366 | struct wlr_box box; | ||
367 | float output_scale = output->wlr_output->scale; | ||
368 | float color[4]; | ||
369 | struct sway_container_state *state = &con->current; | ||
370 | |||
371 | if (state->border_left) { | ||
372 | memcpy(&color, colors->child_border, sizeof(float) * 4); | ||
373 | premultiply_alpha(color, con->alpha); | ||
374 | box.x = floor(state->x); | ||
375 | box.y = floor(state->content_y); | ||
376 | box.width = state->border_thickness; | ||
377 | box.height = state->content_height; | ||
378 | scale_box(&box, output_scale); | ||
379 | render_rect(output, damage, &box, color); | ||
380 | } | ||
381 | |||
382 | list_t *siblings = container_get_current_siblings(con); | ||
383 | enum sway_container_layout layout = | ||
384 | container_current_parent_layout(con); | ||
385 | |||
386 | if (state->border_right) { | ||
387 | if (!container_is_current_floating(con) && siblings->length == 1 && layout == L_HORIZ) { | ||
388 | memcpy(&color, colors->indicator, sizeof(float) * 4); | ||
389 | } else { | ||
390 | memcpy(&color, colors->child_border, sizeof(float) * 4); | ||
391 | } | ||
392 | premultiply_alpha(color, con->alpha); | ||
393 | box.x = floor(state->content_x + state->content_width); | ||
394 | box.y = floor(state->content_y); | ||
395 | box.width = state->border_thickness; | ||
396 | box.height = state->content_height; | ||
397 | scale_box(&box, output_scale); | ||
398 | render_rect(output, damage, &box, color); | ||
399 | } | ||
400 | |||
401 | if (state->border_bottom) { | ||
402 | if (!container_is_current_floating(con) && siblings->length == 1 && layout == L_VERT) { | ||
403 | memcpy(&color, colors->indicator, sizeof(float) * 4); | ||
404 | } else { | ||
405 | memcpy(&color, colors->child_border, sizeof(float) * 4); | ||
406 | } | ||
407 | premultiply_alpha(color, con->alpha); | ||
408 | box.x = floor(state->x); | ||
409 | box.y = floor(state->content_y + state->content_height); | ||
410 | box.width = state->width; | ||
411 | box.height = state->border_thickness; | ||
412 | scale_box(&box, output_scale); | ||
413 | render_rect(output, damage, &box, color); | ||
414 | } | ||
415 | } | ||
416 | |||
417 | /** | ||
418 | * Render a titlebar. | ||
419 | * | ||
420 | * Care must be taken not to render over the same pixel multiple times, | ||
421 | * otherwise the colors will be incorrect when using opacity. | ||
422 | * | ||
423 | * The height is: 1px border, 3px padding, font height, 3px padding, 1px border | ||
424 | * The left side is: 1px border, 2px padding, title | ||
425 | */ | ||
426 | static void render_titlebar(struct sway_output *output, | ||
427 | pixman_region32_t *output_damage, struct sway_container *con, | ||
428 | int x, int y, int width, | ||
429 | struct border_colors *colors, struct wlr_texture *title_texture, | ||
430 | struct wlr_texture *marks_texture) { | ||
431 | struct wlr_box box; | ||
432 | float color[4]; | ||
433 | float output_scale = output->wlr_output->scale; | ||
434 | double output_x = output->lx; | ||
435 | double output_y = output->ly; | ||
436 | int titlebar_border_thickness = config->titlebar_border_thickness; | ||
437 | int titlebar_h_padding = config->titlebar_h_padding; | ||
438 | int titlebar_v_padding = config->titlebar_v_padding; | ||
439 | enum alignment title_align = config->title_align; | ||
440 | |||
441 | // Single pixel bar above title | ||
442 | memcpy(&color, colors->border, sizeof(float) * 4); | ||
443 | premultiply_alpha(color, con->alpha); | ||
444 | box.x = x; | ||
445 | box.y = y; | ||
446 | box.width = width; | ||
447 | box.height = titlebar_border_thickness; | ||
448 | scale_box(&box, output_scale); | ||
449 | render_rect(output, output_damage, &box, color); | ||
450 | |||
451 | // Single pixel bar below title | ||
452 | box.x = x; | ||
453 | box.y = y + container_titlebar_height() - titlebar_border_thickness; | ||
454 | box.width = width; | ||
455 | box.height = titlebar_border_thickness; | ||
456 | scale_box(&box, output_scale); | ||
457 | render_rect(output, output_damage, &box, color); | ||
458 | |||
459 | // Single pixel left edge | ||
460 | box.x = x; | ||
461 | box.y = y + titlebar_border_thickness; | ||
462 | box.width = titlebar_border_thickness; | ||
463 | box.height = container_titlebar_height() - titlebar_border_thickness * 2; | ||
464 | scale_box(&box, output_scale); | ||
465 | render_rect(output, output_damage, &box, color); | ||
466 | |||
467 | // Single pixel right edge | ||
468 | box.x = x + width - titlebar_border_thickness; | ||
469 | box.y = y + titlebar_border_thickness; | ||
470 | box.width = titlebar_border_thickness; | ||
471 | box.height = container_titlebar_height() - titlebar_border_thickness * 2; | ||
472 | scale_box(&box, output_scale); | ||
473 | render_rect(output, output_damage, &box, color); | ||
474 | |||
475 | int inner_x = x - output_x + titlebar_h_padding; | ||
476 | int bg_y = y + titlebar_border_thickness; | ||
477 | size_t inner_width = width - titlebar_h_padding * 2; | ||
478 | |||
479 | // output-buffer local | ||
480 | int ob_inner_x = round(inner_x * output_scale); | ||
481 | int ob_inner_width = scale_length(inner_width, inner_x, output_scale); | ||
482 | int ob_bg_height = scale_length( | ||
483 | (titlebar_v_padding - titlebar_border_thickness) * 2 + | ||
484 | config->font_height, bg_y, output_scale); | ||
485 | |||
486 | // Marks | ||
487 | int ob_marks_x = 0; // output-buffer-local | ||
488 | int ob_marks_width = 0; // output-buffer-local | ||
489 | if (config->show_marks && marks_texture) { | ||
490 | struct wlr_box texture_box = { | ||
491 | .width = marks_texture->width, | ||
492 | .height = marks_texture->height, | ||
493 | }; | ||
494 | ob_marks_width = texture_box.width; | ||
495 | |||
496 | // The marks texture might be shorter than the config->font_height, in | ||
497 | // which case we need to pad it as evenly as possible above and below. | ||
498 | int ob_padding_total = ob_bg_height - texture_box.height; | ||
499 | int ob_padding_above = floor(ob_padding_total / 2.0); | ||
500 | int ob_padding_below = ceil(ob_padding_total / 2.0); | ||
501 | |||
502 | // Render texture. If the title is on the right, the marks will be on | ||
503 | // the left. Otherwise, they will be on the right. | ||
504 | if (title_align == ALIGN_RIGHT || texture_box.width > ob_inner_width) { | ||
505 | texture_box.x = ob_inner_x; | ||
506 | } else { | ||
507 | texture_box.x = ob_inner_x + ob_inner_width - texture_box.width; | ||
508 | } | ||
509 | ob_marks_x = texture_box.x; | ||
510 | |||
511 | texture_box.y = round((bg_y - output_y) * output_scale) + | ||
512 | ob_padding_above; | ||
513 | |||
514 | float matrix[9]; | ||
515 | wlr_matrix_project_box(matrix, &texture_box, | ||
516 | WL_OUTPUT_TRANSFORM_NORMAL, | ||
517 | 0.0, output->wlr_output->transform_matrix); | ||
518 | |||
519 | if (ob_inner_width < texture_box.width) { | ||
520 | texture_box.width = ob_inner_width; | ||
521 | } | ||
522 | render_texture(output->wlr_output, output_damage, marks_texture, | ||
523 | NULL, &texture_box, matrix, con->alpha); | ||
524 | |||
525 | // Padding above | ||
526 | memcpy(&color, colors->background, sizeof(float) * 4); | ||
527 | premultiply_alpha(color, con->alpha); | ||
528 | box.x = texture_box.x + round(output_x * output_scale); | ||
529 | box.y = round((y + titlebar_border_thickness) * output_scale); | ||
530 | box.width = texture_box.width; | ||
531 | box.height = ob_padding_above; | ||
532 | render_rect(output, output_damage, &box, color); | ||
533 | |||
534 | // Padding below | ||
535 | box.y += ob_padding_above + texture_box.height; | ||
536 | box.height = ob_padding_below; | ||
537 | render_rect(output, output_damage, &box, color); | ||
538 | } | ||
539 | |||
540 | // Title text | ||
541 | int ob_title_x = 0; // output-buffer-local | ||
542 | int ob_title_width = 0; // output-buffer-local | ||
543 | if (title_texture) { | ||
544 | struct wlr_box texture_box = { | ||
545 | .width = title_texture->width, | ||
546 | .height = title_texture->height, | ||
547 | }; | ||
548 | |||
549 | // The effective output may be NULL when con is not on any output. | ||
550 | // This can happen because we render all children of containers, | ||
551 | // even those that are out of the bounds of any output. | ||
552 | struct sway_output *effective = container_get_effective_output(con); | ||
553 | float title_scale = effective ? effective->wlr_output->scale : output_scale; | ||
554 | texture_box.width = texture_box.width * output_scale / title_scale; | ||
555 | texture_box.height = texture_box.height * output_scale / title_scale; | ||
556 | ob_title_width = texture_box.width; | ||
557 | |||
558 | // The title texture might be shorter than the config->font_height, | ||
559 | // in which case we need to pad it above and below. | ||
560 | int ob_padding_above = round((titlebar_v_padding - | ||
561 | titlebar_border_thickness) * output_scale); | ||
562 | int ob_padding_below = ob_bg_height - ob_padding_above - | ||
563 | texture_box.height; | ||
564 | |||
565 | // Render texture | ||
566 | if (texture_box.width > ob_inner_width - ob_marks_width) { | ||
567 | texture_box.x = (title_align == ALIGN_RIGHT && ob_marks_width) | ||
568 | ? ob_marks_x + ob_marks_width : ob_inner_x; | ||
569 | } else if (title_align == ALIGN_LEFT) { | ||
570 | texture_box.x = ob_inner_x; | ||
571 | } else if (title_align == ALIGN_CENTER) { | ||
572 | // If there are marks visible, center between the edge and marks. | ||
573 | // Otherwise, center in the inner area. | ||
574 | if (ob_marks_width) { | ||
575 | texture_box.x = (ob_inner_x + ob_marks_x) / 2 | ||
576 | - texture_box.width / 2; | ||
577 | } else { | ||
578 | texture_box.x = ob_inner_x + ob_inner_width / 2 | ||
579 | - texture_box.width / 2; | ||
580 | } | ||
581 | } else { | ||
582 | texture_box.x = ob_inner_x + ob_inner_width - texture_box.width; | ||
583 | } | ||
584 | ob_title_x = texture_box.x; | ||
585 | |||
586 | texture_box.y = | ||
587 | round((bg_y - output_y) * output_scale) + ob_padding_above; | ||
588 | |||
589 | float matrix[9]; | ||
590 | wlr_matrix_project_box(matrix, &texture_box, | ||
591 | WL_OUTPUT_TRANSFORM_NORMAL, | ||
592 | 0.0, output->wlr_output->transform_matrix); | ||
593 | |||
594 | if (ob_inner_width - ob_marks_width < texture_box.width) { | ||
595 | texture_box.width = ob_inner_width - ob_marks_width; | ||
596 | } | ||
597 | |||
598 | render_texture(output->wlr_output, output_damage, title_texture, | ||
599 | NULL, &texture_box, matrix, con->alpha); | ||
600 | |||
601 | // Padding above | ||
602 | memcpy(&color, colors->background, sizeof(float) * 4); | ||
603 | premultiply_alpha(color, con->alpha); | ||
604 | box.x = texture_box.x + round(output_x * output_scale); | ||
605 | box.y = round((y + titlebar_border_thickness) * output_scale); | ||
606 | box.width = texture_box.width; | ||
607 | box.height = ob_padding_above; | ||
608 | render_rect(output, output_damage, &box, color); | ||
609 | |||
610 | // Padding below | ||
611 | box.y += ob_padding_above + texture_box.height; | ||
612 | box.height = ob_padding_below; | ||
613 | render_rect(output, output_damage, &box, color); | ||
614 | } | ||
615 | |||
616 | // Determine the left + right extends of the textures (output-buffer local) | ||
617 | int ob_left_x, ob_left_width, ob_right_x, ob_right_width; | ||
618 | if (ob_title_width == 0 && ob_marks_width == 0) { | ||
619 | ob_left_x = ob_inner_x; | ||
620 | ob_left_width = 0; | ||
621 | ob_right_x = ob_inner_x; | ||
622 | ob_right_width = 0; | ||
623 | } else if (ob_title_x < ob_marks_x) { | ||
624 | ob_left_x = ob_title_x; | ||
625 | ob_left_width = ob_title_width; | ||
626 | ob_right_x = ob_marks_x; | ||
627 | ob_right_width = ob_marks_width; | ||
628 | } else { | ||
629 | ob_left_x = ob_marks_x; | ||
630 | ob_left_width = ob_marks_width; | ||
631 | ob_right_x = ob_title_x; | ||
632 | ob_right_width = ob_title_width; | ||
633 | } | ||
634 | if (ob_left_x < ob_inner_x) { | ||
635 | ob_left_x = ob_inner_x; | ||
636 | } else if (ob_left_x + ob_left_width > ob_right_x + ob_right_width) { | ||
637 | ob_right_x = ob_left_x; | ||
638 | ob_right_width = ob_left_width; | ||
639 | } | ||
640 | |||
641 | // Filler between title and marks | ||
642 | box.width = ob_right_x - ob_left_x - ob_left_width; | ||
643 | if (box.width > 0) { | ||
644 | box.x = ob_left_x + ob_left_width + round(output_x * output_scale); | ||
645 | box.y = round(bg_y * output_scale); | ||
646 | box.height = ob_bg_height; | ||
647 | render_rect(output, output_damage, &box, color); | ||
648 | } | ||
649 | |||
650 | // Padding on left side | ||
651 | box.x = x + titlebar_border_thickness; | ||
652 | box.y = y + titlebar_border_thickness; | ||
653 | box.width = titlebar_h_padding - titlebar_border_thickness; | ||
654 | box.height = (titlebar_v_padding - titlebar_border_thickness) * 2 + | ||
655 | config->font_height; | ||
656 | scale_box(&box, output_scale); | ||
657 | int left_x = ob_left_x + round(output_x * output_scale); | ||
658 | if (box.x + box.width < left_x) { | ||
659 | box.width += left_x - box.x - box.width; | ||
660 | } | ||
661 | render_rect(output, output_damage, &box, color); | ||
662 | |||
663 | // Padding on right side | ||
664 | box.x = x + width - titlebar_h_padding; | ||
665 | box.y = y + titlebar_border_thickness; | ||
666 | box.width = titlebar_h_padding - titlebar_border_thickness; | ||
667 | box.height = (titlebar_v_padding - titlebar_border_thickness) * 2 + | ||
668 | config->font_height; | ||
669 | scale_box(&box, output_scale); | ||
670 | int right_rx = ob_right_x + ob_right_width + round(output_x * output_scale); | ||
671 | if (right_rx < box.x) { | ||
672 | box.width += box.x - right_rx; | ||
673 | box.x = right_rx; | ||
674 | } | ||
675 | render_rect(output, output_damage, &box, color); | ||
676 | } | ||
677 | |||
678 | /** | ||
679 | * Render the top border line for a view using "border pixel". | ||
680 | */ | ||
681 | static void render_top_border(struct sway_output *output, | ||
682 | pixman_region32_t *output_damage, struct sway_container *con, | ||
683 | struct border_colors *colors) { | ||
684 | struct sway_container_state *state = &con->current; | ||
685 | if (!state->border_top) { | ||
686 | return; | ||
687 | } | ||
688 | struct wlr_box box; | ||
689 | float color[4]; | ||
690 | float output_scale = output->wlr_output->scale; | ||
691 | |||
692 | // Child border - top edge | ||
693 | memcpy(&color, colors->child_border, sizeof(float) * 4); | ||
694 | premultiply_alpha(color, con->alpha); | ||
695 | box.x = floor(state->x); | ||
696 | box.y = floor(state->y); | ||
697 | box.width = state->width; | ||
698 | box.height = state->border_thickness; | ||
699 | scale_box(&box, output_scale); | ||
700 | render_rect(output, output_damage, &box, color); | ||
701 | } | ||
702 | |||
703 | struct parent_data { | ||
704 | enum sway_container_layout layout; | ||
705 | struct wlr_box box; | ||
706 | list_t *children; | ||
707 | bool focused; | ||
708 | struct sway_container *active_child; | ||
709 | }; | ||
710 | |||
711 | static void render_container(struct sway_output *output, | ||
712 | pixman_region32_t *damage, struct sway_container *con, bool parent_focused); | ||
713 | |||
714 | /** | ||
715 | * Render a container's children using a L_HORIZ or L_VERT layout. | ||
716 | * | ||
717 | * Wrap child views in borders and leave child containers borderless because | ||
718 | * they'll apply their own borders to their children. | ||
719 | */ | ||
720 | static void render_containers_linear(struct sway_output *output, | ||
721 | pixman_region32_t *damage, struct parent_data *parent) { | ||
722 | for (int i = 0; i < parent->children->length; ++i) { | ||
723 | struct sway_container *child = parent->children->items[i]; | ||
724 | |||
725 | if (child->view) { | ||
726 | struct sway_view *view = child->view; | ||
727 | struct border_colors *colors; | ||
728 | struct wlr_texture *title_texture; | ||
729 | struct wlr_texture *marks_texture; | ||
730 | struct sway_container_state *state = &child->current; | ||
731 | |||
732 | if (view_is_urgent(view)) { | ||
733 | colors = &config->border_colors.urgent; | ||
734 | title_texture = child->title_urgent; | ||
735 | marks_texture = child->marks_urgent; | ||
736 | } else if (state->focused || parent->focused) { | ||
737 | colors = &config->border_colors.focused; | ||
738 | title_texture = child->title_focused; | ||
739 | marks_texture = child->marks_focused; | ||
740 | } else if (child == parent->active_child) { | ||
741 | colors = &config->border_colors.focused_inactive; | ||
742 | title_texture = child->title_focused_inactive; | ||
743 | marks_texture = child->marks_focused_inactive; | ||
744 | } else { | ||
745 | colors = &config->border_colors.unfocused; | ||
746 | title_texture = child->title_unfocused; | ||
747 | marks_texture = child->marks_unfocused; | ||
748 | } | ||
749 | |||
750 | if (state->border == B_NORMAL) { | ||
751 | render_titlebar(output, damage, child, floor(state->x), | ||
752 | floor(state->y), state->width, colors, | ||
753 | title_texture, marks_texture); | ||
754 | } else if (state->border == B_PIXEL) { | ||
755 | render_top_border(output, damage, child, colors); | ||
756 | } | ||
757 | render_view(output, damage, child, colors); | ||
758 | } else { | ||
759 | render_container(output, damage, child, | ||
760 | parent->focused || child->current.focused); | ||
761 | } | ||
762 | } | ||
763 | } | ||
764 | |||
765 | static bool container_is_focused(struct sway_container *con, void *data) { | ||
766 | return con->current.focused; | ||
767 | } | ||
768 | |||
769 | static bool container_has_focused_child(struct sway_container *con) { | ||
770 | return container_find_child(con, container_is_focused, NULL); | ||
771 | } | ||
772 | |||
773 | /** | ||
774 | * Render a container's children using the L_TABBED layout. | ||
775 | */ | ||
776 | static void render_containers_tabbed(struct sway_output *output, | ||
777 | pixman_region32_t *damage, struct parent_data *parent) { | ||
778 | if (!parent->children->length) { | ||
779 | return; | ||
780 | } | ||
781 | struct sway_container *current = parent->active_child; | ||
782 | struct border_colors *current_colors = &config->border_colors.unfocused; | ||
783 | int tab_width = parent->box.width / parent->children->length; | ||
784 | |||
785 | // Render tabs | ||
786 | for (int i = 0; i < parent->children->length; ++i) { | ||
787 | struct sway_container *child = parent->children->items[i]; | ||
788 | struct sway_view *view = child->view; | ||
789 | struct sway_container_state *cstate = &child->current; | ||
790 | struct border_colors *colors; | ||
791 | struct wlr_texture *title_texture; | ||
792 | struct wlr_texture *marks_texture; | ||
793 | bool urgent = view ? | ||
794 | view_is_urgent(view) : container_has_urgent_child(child); | ||
795 | |||
796 | if (urgent) { | ||
797 | colors = &config->border_colors.urgent; | ||
798 | title_texture = child->title_urgent; | ||
799 | marks_texture = child->marks_urgent; | ||
800 | } else if (cstate->focused || parent->focused) { | ||
801 | colors = &config->border_colors.focused; | ||
802 | title_texture = child->title_focused; | ||
803 | marks_texture = child->marks_focused; | ||
804 | } else if (config->has_focused_tab_title && container_has_focused_child(child)) { | ||
805 | colors = &config->border_colors.focused_tab_title; | ||
806 | title_texture = child->title_focused_tab_title; | ||
807 | marks_texture = child->marks_focused_tab_title; | ||
808 | } else if (child == parent->active_child) { | ||
809 | colors = &config->border_colors.focused_inactive; | ||
810 | title_texture = child->title_focused_inactive; | ||
811 | marks_texture = child->marks_focused_inactive; | ||
812 | } else { | ||
813 | colors = &config->border_colors.unfocused; | ||
814 | title_texture = child->title_unfocused; | ||
815 | marks_texture = child->marks_unfocused; | ||
816 | } | ||
817 | |||
818 | int x = floor(cstate->x + tab_width * i); | ||
819 | |||
820 | // Make last tab use the remaining width of the parent | ||
821 | if (i == parent->children->length - 1) { | ||
822 | tab_width = parent->box.width - tab_width * i; | ||
823 | } | ||
824 | |||
825 | render_titlebar(output, damage, child, x, parent->box.y, tab_width, | ||
826 | colors, title_texture, marks_texture); | ||
827 | |||
828 | if (child == current) { | ||
829 | current_colors = colors; | ||
830 | } | ||
831 | } | ||
832 | |||
833 | // Render surface and left/right/bottom borders | ||
834 | if (current->view) { | ||
835 | render_view(output, damage, current, current_colors); | ||
836 | } else { | ||
837 | render_container(output, damage, current, | ||
838 | parent->focused || current->current.focused); | ||
839 | } | ||
840 | } | ||
841 | |||
842 | /** | ||
843 | * Render a container's children using the L_STACKED layout. | ||
844 | */ | ||
845 | static void render_containers_stacked(struct sway_output *output, | ||
846 | pixman_region32_t *damage, struct parent_data *parent) { | ||
847 | if (!parent->children->length) { | ||
848 | return; | ||
849 | } | ||
850 | struct sway_container *current = parent->active_child; | ||
851 | struct border_colors *current_colors = &config->border_colors.unfocused; | ||
852 | size_t titlebar_height = container_titlebar_height(); | ||
853 | |||
854 | // Render titles | ||
855 | for (int i = 0; i < parent->children->length; ++i) { | ||
856 | struct sway_container *child = parent->children->items[i]; | ||
857 | struct sway_view *view = child->view; | ||
858 | struct sway_container_state *cstate = &child->current; | ||
859 | struct border_colors *colors; | ||
860 | struct wlr_texture *title_texture; | ||
861 | struct wlr_texture *marks_texture; | ||
862 | bool urgent = view ? | ||
863 | view_is_urgent(view) : container_has_urgent_child(child); | ||
864 | |||
865 | if (urgent) { | ||
866 | colors = &config->border_colors.urgent; | ||
867 | title_texture = child->title_urgent; | ||
868 | marks_texture = child->marks_urgent; | ||
869 | } else if (cstate->focused || parent->focused) { | ||
870 | colors = &config->border_colors.focused; | ||
871 | title_texture = child->title_focused; | ||
872 | marks_texture = child->marks_focused; | ||
873 | } else if (config->has_focused_tab_title && container_has_focused_child(child)) { | ||
874 | colors = &config->border_colors.focused_tab_title; | ||
875 | title_texture = child->title_focused_tab_title; | ||
876 | marks_texture = child->marks_focused_tab_title; | ||
877 | } else if (child == parent->active_child) { | ||
878 | colors = &config->border_colors.focused_inactive; | ||
879 | title_texture = child->title_focused_inactive; | ||
880 | marks_texture = child->marks_focused_inactive; | ||
881 | } else { | ||
882 | colors = &config->border_colors.unfocused; | ||
883 | title_texture = child->title_unfocused; | ||
884 | marks_texture = child->marks_unfocused; | ||
885 | } | ||
886 | |||
887 | int y = parent->box.y + titlebar_height * i; | ||
888 | render_titlebar(output, damage, child, parent->box.x, y, | ||
889 | parent->box.width, colors, title_texture, marks_texture); | ||
890 | |||
891 | if (child == current) { | ||
892 | current_colors = colors; | ||
893 | } | ||
894 | } | ||
895 | |||
896 | // Render surface and left/right/bottom borders | ||
897 | if (current->view) { | ||
898 | render_view(output, damage, current, current_colors); | ||
899 | } else { | ||
900 | render_container(output, damage, current, | ||
901 | parent->focused || current->current.focused); | ||
902 | } | ||
903 | } | ||
904 | |||
905 | static void render_containers(struct sway_output *output, | ||
906 | pixman_region32_t *damage, struct parent_data *parent) { | ||
907 | if (config->hide_lone_tab && parent->children->length == 1) { | ||
908 | struct sway_container *child = parent->children->items[0]; | ||
909 | if (child->view) { | ||
910 | render_containers_linear(output,damage, parent); | ||
911 | return; | ||
912 | } | ||
913 | } | ||
914 | |||
915 | switch (parent->layout) { | ||
916 | case L_NONE: | ||
917 | case L_HORIZ: | ||
918 | case L_VERT: | ||
919 | render_containers_linear(output, damage, parent); | ||
920 | break; | ||
921 | case L_STACKED: | ||
922 | render_containers_stacked(output, damage, parent); | ||
923 | break; | ||
924 | case L_TABBED: | ||
925 | render_containers_tabbed(output, damage, parent); | ||
926 | break; | ||
927 | } | ||
928 | } | ||
929 | |||
930 | static void render_container(struct sway_output *output, | ||
931 | pixman_region32_t *damage, struct sway_container *con, bool focused) { | ||
932 | struct parent_data data = { | ||
933 | .layout = con->current.layout, | ||
934 | .box = { | ||
935 | .x = floor(con->current.x), | ||
936 | .y = floor(con->current.y), | ||
937 | .width = con->current.width, | ||
938 | .height = con->current.height, | ||
939 | }, | ||
940 | .children = con->current.children, | ||
941 | .focused = focused, | ||
942 | .active_child = con->current.focused_inactive_child, | ||
943 | }; | ||
944 | render_containers(output, damage, &data); | ||
945 | } | ||
946 | |||
947 | static void render_workspace(struct sway_output *output, | ||
948 | pixman_region32_t *damage, struct sway_workspace *ws, bool focused) { | ||
949 | struct parent_data data = { | ||
950 | .layout = ws->current.layout, | ||
951 | .box = { | ||
952 | .x = floor(ws->current.x), | ||
953 | .y = floor(ws->current.y), | ||
954 | .width = ws->current.width, | ||
955 | .height = ws->current.height, | ||
956 | }, | ||
957 | .children = ws->current.tiling, | ||
958 | .focused = focused, | ||
959 | .active_child = ws->current.focused_inactive_child, | ||
960 | }; | ||
961 | render_containers(output, damage, &data); | ||
962 | } | ||
963 | |||
964 | static void render_floating_container(struct sway_output *soutput, | ||
965 | pixman_region32_t *damage, struct sway_container *con) { | ||
966 | if (con->view) { | ||
967 | struct sway_view *view = con->view; | ||
968 | struct border_colors *colors; | ||
969 | struct wlr_texture *title_texture; | ||
970 | struct wlr_texture *marks_texture; | ||
971 | |||
972 | if (view_is_urgent(view)) { | ||
973 | colors = &config->border_colors.urgent; | ||
974 | title_texture = con->title_urgent; | ||
975 | marks_texture = con->marks_urgent; | ||
976 | } else if (con->current.focused) { | ||
977 | colors = &config->border_colors.focused; | ||
978 | title_texture = con->title_focused; | ||
979 | marks_texture = con->marks_focused; | ||
980 | } else { | ||
981 | colors = &config->border_colors.unfocused; | ||
982 | title_texture = con->title_unfocused; | ||
983 | marks_texture = con->marks_unfocused; | ||
984 | } | ||
985 | |||
986 | if (con->current.border == B_NORMAL) { | ||
987 | render_titlebar(soutput, damage, con, floor(con->current.x), | ||
988 | floor(con->current.y), con->current.width, colors, | ||
989 | title_texture, marks_texture); | ||
990 | } else if (con->current.border == B_PIXEL) { | ||
991 | render_top_border(soutput, damage, con, colors); | ||
992 | } | ||
993 | render_view(soutput, damage, con, colors); | ||
994 | } else { | ||
995 | render_container(soutput, damage, con, con->current.focused); | ||
996 | } | ||
997 | } | ||
998 | |||
999 | static void render_floating(struct sway_output *soutput, | ||
1000 | pixman_region32_t *damage) { | ||
1001 | for (int i = 0; i < root->outputs->length; ++i) { | ||
1002 | struct sway_output *output = root->outputs->items[i]; | ||
1003 | for (int j = 0; j < output->current.workspaces->length; ++j) { | ||
1004 | struct sway_workspace *ws = output->current.workspaces->items[j]; | ||
1005 | if (!workspace_is_visible(ws)) { | ||
1006 | continue; | ||
1007 | } | ||
1008 | for (int k = 0; k < ws->current.floating->length; ++k) { | ||
1009 | struct sway_container *floater = ws->current.floating->items[k]; | ||
1010 | if (floater->current.fullscreen_mode != FULLSCREEN_NONE) { | ||
1011 | continue; | ||
1012 | } | ||
1013 | render_floating_container(soutput, damage, floater); | ||
1014 | } | ||
1015 | } | ||
1016 | } | ||
1017 | } | ||
1018 | |||
1019 | static void render_seatops(struct sway_output *output, | ||
1020 | pixman_region32_t *damage) { | ||
1021 | struct sway_seat *seat; | ||
1022 | wl_list_for_each(seat, &server.input->seats, link) { | ||
1023 | seatop_render(seat, output, damage); | ||
1024 | } | ||
1025 | } | ||
1026 | |||
1027 | void output_render(struct sway_output *output, struct timespec *when, | ||
1028 | pixman_region32_t *damage) { | ||
1029 | struct wlr_output *wlr_output = output->wlr_output; | ||
1030 | struct wlr_renderer *renderer = output->server->renderer; | ||
1031 | |||
1032 | struct sway_workspace *workspace = output->current.active_workspace; | ||
1033 | if (workspace == NULL) { | ||
1034 | return; | ||
1035 | } | ||
1036 | |||
1037 | struct sway_container *fullscreen_con = root->fullscreen_global; | ||
1038 | if (!fullscreen_con) { | ||
1039 | fullscreen_con = workspace->current.fullscreen; | ||
1040 | } | ||
1041 | |||
1042 | wlr_renderer_begin(renderer, wlr_output->width, wlr_output->height); | ||
1043 | |||
1044 | if (debug.damage == DAMAGE_RERENDER) { | ||
1045 | int width, height; | ||
1046 | wlr_output_transformed_resolution(wlr_output, &width, &height); | ||
1047 | pixman_region32_union_rect(damage, damage, 0, 0, width, height); | ||
1048 | } | ||
1049 | |||
1050 | if (!pixman_region32_not_empty(damage)) { | ||
1051 | // Output isn't damaged but needs buffer swap | ||
1052 | goto renderer_end; | ||
1053 | } | ||
1054 | |||
1055 | if (debug.damage == DAMAGE_HIGHLIGHT) { | ||
1056 | wlr_renderer_clear(renderer, (float[]){1, 1, 0, 1}); | ||
1057 | } | ||
1058 | |||
1059 | if (server.session_lock.locked) { | ||
1060 | float clear_color[] = {0.0f, 0.0f, 0.0f, 1.0f}; | ||
1061 | if (server.session_lock.lock == NULL) { | ||
1062 | // abandoned lock -> red BG | ||
1063 | clear_color[0] = 1.f; | ||
1064 | } | ||
1065 | int nrects; | ||
1066 | pixman_box32_t *rects = pixman_region32_rectangles(damage, &nrects); | ||
1067 | for (int i = 0; i < nrects; ++i) { | ||
1068 | scissor_output(wlr_output, &rects[i]); | ||
1069 | wlr_renderer_clear(renderer, clear_color); | ||
1070 | } | ||
1071 | |||
1072 | if (server.session_lock.lock != NULL) { | ||
1073 | struct render_data data = { | ||
1074 | .damage = damage, | ||
1075 | .alpha = 1.0f, | ||
1076 | }; | ||
1077 | |||
1078 | struct wlr_session_lock_surface_v1 *lock_surface; | ||
1079 | wl_list_for_each(lock_surface, &server.session_lock.lock->surfaces, link) { | ||
1080 | if (lock_surface->output != wlr_output) { | ||
1081 | continue; | ||
1082 | } | ||
1083 | if (!lock_surface->mapped) { | ||
1084 | continue; | ||
1085 | } | ||
1086 | |||
1087 | output_surface_for_each_surface(output, lock_surface->surface, | ||
1088 | 0.0, 0.0, render_surface_iterator, &data); | ||
1089 | } | ||
1090 | } | ||
1091 | goto renderer_end; | ||
1092 | } | ||
1093 | |||
1094 | if (output_has_opaque_overlay_layer_surface(output)) { | ||
1095 | goto render_overlay; | ||
1096 | } | ||
1097 | |||
1098 | if (fullscreen_con) { | ||
1099 | float clear_color[] = {0.0f, 0.0f, 0.0f, 1.0f}; | ||
1100 | |||
1101 | int nrects; | ||
1102 | pixman_box32_t *rects = pixman_region32_rectangles(damage, &nrects); | ||
1103 | for (int i = 0; i < nrects; ++i) { | ||
1104 | scissor_output(wlr_output, &rects[i]); | ||
1105 | wlr_renderer_clear(renderer, clear_color); | ||
1106 | } | ||
1107 | |||
1108 | if (fullscreen_con->view) { | ||
1109 | if (!wl_list_empty(&fullscreen_con->view->saved_buffers)) { | ||
1110 | render_saved_view(fullscreen_con->view, output, damage, 1.0f); | ||
1111 | } else if (fullscreen_con->view->surface) { | ||
1112 | render_view_toplevels(fullscreen_con->view, | ||
1113 | output, damage, 1.0f); | ||
1114 | } | ||
1115 | } else { | ||
1116 | render_container(output, damage, fullscreen_con, | ||
1117 | fullscreen_con->current.focused); | ||
1118 | } | ||
1119 | |||
1120 | for (int i = 0; i < workspace->current.floating->length; ++i) { | ||
1121 | struct sway_container *floater = | ||
1122 | workspace->current.floating->items[i]; | ||
1123 | if (container_is_transient_for(floater, fullscreen_con)) { | ||
1124 | render_floating_container(output, damage, floater); | ||
1125 | } | ||
1126 | } | ||
1127 | #if HAVE_XWAYLAND | ||
1128 | render_unmanaged(output, damage, &root->xwayland_unmanaged); | ||
1129 | #endif | ||
1130 | } else { | ||
1131 | float clear_color[] = {0.25f, 0.25f, 0.25f, 1.0f}; | ||
1132 | |||
1133 | int nrects; | ||
1134 | pixman_box32_t *rects = pixman_region32_rectangles(damage, &nrects); | ||
1135 | for (int i = 0; i < nrects; ++i) { | ||
1136 | scissor_output(wlr_output, &rects[i]); | ||
1137 | wlr_renderer_clear(renderer, clear_color); | ||
1138 | } | ||
1139 | |||
1140 | render_layer_toplevel(output, damage, | ||
1141 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND]); | ||
1142 | render_layer_toplevel(output, damage, | ||
1143 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM]); | ||
1144 | |||
1145 | render_workspace(output, damage, workspace, workspace->current.focused); | ||
1146 | render_floating(output, damage); | ||
1147 | #if HAVE_XWAYLAND | ||
1148 | render_unmanaged(output, damage, &root->xwayland_unmanaged); | ||
1149 | #endif | ||
1150 | render_layer_toplevel(output, damage, | ||
1151 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]); | ||
1152 | |||
1153 | render_layer_popups(output, damage, | ||
1154 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND]); | ||
1155 | render_layer_popups(output, damage, | ||
1156 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM]); | ||
1157 | render_layer_popups(output, damage, | ||
1158 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]); | ||
1159 | } | ||
1160 | |||
1161 | render_seatops(output, damage); | ||
1162 | |||
1163 | struct sway_seat *seat = input_manager_current_seat(); | ||
1164 | struct sway_container *focus = seat_get_focused_container(seat); | ||
1165 | if (focus && focus->view) { | ||
1166 | render_view_popups(focus->view, output, damage, focus->alpha); | ||
1167 | } | ||
1168 | |||
1169 | render_overlay: | ||
1170 | render_layer_toplevel(output, damage, | ||
1171 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]); | ||
1172 | render_layer_popups(output, damage, | ||
1173 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]); | ||
1174 | render_drag_icons(output, damage, &root->drag_icons); | ||
1175 | |||
1176 | renderer_end: | ||
1177 | wlr_renderer_scissor(renderer, NULL); | ||
1178 | wlr_output_render_software_cursors(wlr_output, damage); | ||
1179 | wlr_renderer_end(renderer); | ||
1180 | |||
1181 | int width, height; | ||
1182 | wlr_output_transformed_resolution(wlr_output, &width, &height); | ||
1183 | |||
1184 | pixman_region32_t frame_damage; | ||
1185 | pixman_region32_init(&frame_damage); | ||
1186 | |||
1187 | enum wl_output_transform transform = | ||
1188 | wlr_output_transform_invert(wlr_output->transform); | ||
1189 | wlr_region_transform(&frame_damage, &output->damage->current, | ||
1190 | transform, width, height); | ||
1191 | |||
1192 | if (debug.damage != DAMAGE_DEFAULT) { | ||
1193 | pixman_region32_union_rect(&frame_damage, &frame_damage, | ||
1194 | 0, 0, wlr_output->width, wlr_output->height); | ||
1195 | } | ||
1196 | |||
1197 | wlr_output_set_damage(wlr_output, &frame_damage); | ||
1198 | pixman_region32_fini(&frame_damage); | ||
1199 | |||
1200 | if (!wlr_output_commit(wlr_output)) { | ||
1201 | return; | ||
1202 | } | ||
1203 | output->last_frame = *when; | ||
1204 | } | ||
diff --git a/sway/desktop/surface.c b/sway/desktop/surface.c deleted file mode 100644 index 1d7b536d..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_compositor.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 f5a3a053..042141ab 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" |
@@ -214,39 +213,20 @@ static void transaction_add_node(struct sway_transaction *transaction, | |||
214 | 213 | ||
215 | static void apply_output_state(struct sway_output *output, | 214 | static void apply_output_state(struct sway_output *output, |
216 | struct sway_output_state *state) { | 215 | struct sway_output_state *state) { |
217 | output_damage_whole(output); | ||
218 | list_free(output->current.workspaces); | 216 | list_free(output->current.workspaces); |
219 | memcpy(&output->current, state, sizeof(struct sway_output_state)); | 217 | memcpy(&output->current, state, sizeof(struct sway_output_state)); |
220 | output_damage_whole(output); | ||
221 | } | 218 | } |
222 | 219 | ||
223 | static void apply_workspace_state(struct sway_workspace *ws, | 220 | static void apply_workspace_state(struct sway_workspace *ws, |
224 | struct sway_workspace_state *state) { | 221 | struct sway_workspace_state *state) { |
225 | output_damage_whole(ws->current.output); | ||
226 | list_free(ws->current.floating); | 222 | list_free(ws->current.floating); |
227 | list_free(ws->current.tiling); | 223 | list_free(ws->current.tiling); |
228 | memcpy(&ws->current, state, sizeof(struct sway_workspace_state)); | 224 | memcpy(&ws->current, state, sizeof(struct sway_workspace_state)); |
229 | output_damage_whole(ws->current.output); | ||
230 | } | 225 | } |
231 | 226 | ||
232 | static void apply_container_state(struct sway_container *container, | 227 | static void apply_container_state(struct sway_container *container, |
233 | struct sway_container_state *state) { | 228 | struct sway_container_state *state) { |
234 | struct sway_view *view = container->view; | 229 | struct sway_view *view = container->view; |
235 | // Damage the old location | ||
236 | desktop_damage_whole_container(container); | ||
237 | if (view && !wl_list_empty(&view->saved_buffers)) { | ||
238 | struct sway_saved_buffer *saved_buf; | ||
239 | wl_list_for_each(saved_buf, &view->saved_buffers, link) { | ||
240 | struct wlr_box box = { | ||
241 | .x = saved_buf->x - view->saved_geometry.x, | ||
242 | .y = saved_buf->y - view->saved_geometry.y, | ||
243 | .width = saved_buf->width, | ||
244 | .height = saved_buf->height, | ||
245 | }; | ||
246 | desktop_damage_box(&box); | ||
247 | } | ||
248 | } | ||
249 | |||
250 | // There are separate children lists for each instruction state, the | 230 | // There are separate children lists for each instruction state, the |
251 | // container's current state and the container's pending state | 231 | // container's current state and the container's pending state |
252 | // (ie. con->children). The list itself needs to be freed here. | 232 | // (ie. con->children). The list itself needs to be freed here. |
@@ -256,35 +236,443 @@ static void apply_container_state(struct sway_container *container, | |||
256 | 236 | ||
257 | memcpy(&container->current, state, sizeof(struct sway_container_state)); | 237 | memcpy(&container->current, state, sizeof(struct sway_container_state)); |
258 | 238 | ||
259 | if (view && !wl_list_empty(&view->saved_buffers)) { | 239 | if (view) { |
260 | if (!container->node.destroying || container->node.ntxnrefs == 1) { | 240 | if (view->saved_surface_tree) { |
261 | 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; | ||
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); | ||
389 | } | ||
390 | |||
391 | if (con->view) { | ||
392 | int border_top = container_titlebar_height(); | ||
393 | int border_width = con->current.border_thickness; | ||
394 | |||
395 | if (title_bar && con->current.border != B_NORMAL) { | ||
396 | wlr_scene_node_set_enabled(&con->title_bar.tree->node, false); | ||
397 | wlr_scene_node_set_enabled(&con->border.top->node, true); | ||
398 | } else { | ||
399 | wlr_scene_node_set_enabled(&con->border.top->node, false); | ||
400 | } | ||
401 | |||
402 | if (con->current.border == B_NORMAL) { | ||
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; | ||
466 | } else { | ||
467 | layout = ws->current.layout; | ||
468 | } | ||
469 | if (layout == L_TABBED || layout == L_STACKED) { | ||
470 | return 0; | ||
471 | } | ||
472 | temp = temp->pending.parent; | ||
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; | ||
500 | |||
501 | if (floater->current.fullscreen_mode != FULLSCREEN_NONE) { | ||
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); | ||
528 | } | ||
529 | } | ||
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); | ||
262 | } | 604 | } |
263 | } | 605 | } |
606 | } | ||
264 | 607 | ||
265 | // If the view hasn't responded to the configure, center it within | 608 | void arrange_popups(struct wlr_scene_tree *popups) { |
266 | // the container. This is important for fullscreen views which | 609 | struct wlr_scene_node *node; |
267 | // refuse to resize to the size of the output. | 610 | wl_list_for_each(node, &popups->children, link) { |
268 | if (view && view->surface) { | 611 | struct sway_popup_desc *popup = scene_descriptor_try_get(node, |
269 | view_center_surface(view); | 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); | ||
270 | } | 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); | ||
271 | 629 | ||
272 | // Damage the new location | 630 | // hide all contents in the scratchpad |
273 | desktop_damage_whole_container(container); | 631 | for (int i = 0; i < root->scratchpad->length; i++) { |
274 | if (view && view->surface) { | 632 | struct sway_container *con = root->scratchpad->items[i]; |
275 | struct wlr_surface *surface = view->surface; | 633 | |
276 | struct wlr_box box = { | 634 | wlr_scene_node_set_enabled(&con->scene_tree->node, false); |
277 | .x = container->current.content_x - view->geometry.x, | ||
278 | .y = container->current.content_y - view->geometry.y, | ||
279 | .width = surface->current.width, | ||
280 | .height = surface->current.height, | ||
281 | }; | ||
282 | desktop_damage_box(&box); | ||
283 | } | 635 | } |
284 | 636 | ||
285 | if (!container->node.destroying) { | 637 | if (fs) { |
286 | container_discover_outputs(container); | 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 | } | ||
287 | } | 673 | } |
674 | |||
675 | arrange_popups(root->layers.popup); | ||
288 | } | 676 | } |
289 | 677 | ||
290 | /** | 678 | /** |
@@ -326,8 +714,6 @@ static void transaction_apply(struct sway_transaction *transaction) { | |||
326 | 714 | ||
327 | node->instruction = NULL; | 715 | node->instruction = NULL; |
328 | } | 716 | } |
329 | |||
330 | cursor_rebase_all(); | ||
331 | } | 717 | } |
332 | 718 | ||
333 | static void transaction_commit_pending(void); | 719 | static void transaction_commit_pending(void); |
@@ -340,11 +726,13 @@ static void transaction_progress(void) { | |||
340 | return; | 726 | return; |
341 | } | 727 | } |
342 | transaction_apply(server.queued_transaction); | 728 | transaction_apply(server.queued_transaction); |
729 | arrange_root(root); | ||
730 | cursor_rebase_all(); | ||
343 | transaction_destroy(server.queued_transaction); | 731 | transaction_destroy(server.queued_transaction); |
344 | server.queued_transaction = NULL; | 732 | server.queued_transaction = NULL; |
345 | 733 | ||
346 | if (!server.pending_transaction) { | 734 | if (!server.pending_transaction) { |
347 | sway_idle_inhibit_v1_check_active(server.idle_inhibit_manager_v1); | 735 | sway_idle_inhibit_v1_check_active(); |
348 | return; | 736 | return; |
349 | } | 737 | } |
350 | 738 | ||
@@ -415,21 +803,11 @@ static void transaction_commit(struct sway_transaction *transaction) { | |||
415 | ++transaction->num_waiting; | 803 | ++transaction->num_waiting; |
416 | } | 804 | } |
417 | 805 | ||
418 | // From here on we are rendering a saved buffer of the view, which | 806 | view_send_frame_done(node->sway_container->view); |
419 | // means we can send a frame done event to make the client redraw it | ||
420 | // as soon as possible. Additionally, this is required if a view is | ||
421 | // mapping and its default geometry doesn't intersect an output. | ||
422 | struct timespec now; | ||
423 | clock_gettime(CLOCK_MONOTONIC, &now); | ||
424 | wlr_surface_send_frame_done( | ||
425 | node->sway_container->view->surface, &now); | ||
426 | } | 807 | } |
427 | if (!hidden && node_is_view(node) && | 808 | if (!hidden && node_is_view(node) && |
428 | wl_list_empty(&node->sway_container->view->saved_buffers)) { | 809 | !node->sway_container->view->saved_surface_tree) { |
429 | view_save_buffer(node->sway_container->view); | 810 | view_save_buffer(node->sway_container->view); |
430 | memcpy(&node->sway_container->view->saved_geometry, | ||
431 | &node->sway_container->view->geometry, | ||
432 | sizeof(struct wlr_box)); | ||
433 | } | 811 | } |
434 | node->instruction = instruction; | 812 | node->instruction = instruction; |
435 | } | 813 | } |
@@ -499,16 +877,18 @@ static void set_instruction_ready( | |||
499 | transaction_progress(); | 877 | transaction_progress(); |
500 | } | 878 | } |
501 | 879 | ||
502 | void transaction_notify_view_ready_by_serial(struct sway_view *view, | 880 | bool transaction_notify_view_ready_by_serial(struct sway_view *view, |
503 | uint32_t serial) { | 881 | uint32_t serial) { |
504 | struct sway_transaction_instruction *instruction = | 882 | struct sway_transaction_instruction *instruction = |
505 | view->container->node.instruction; | 883 | view->container->node.instruction; |
506 | if (instruction != NULL && instruction->serial == serial) { | 884 | if (instruction != NULL && instruction->serial == serial) { |
507 | set_instruction_ready(instruction); | 885 | set_instruction_ready(instruction); |
886 | return true; | ||
508 | } | 887 | } |
888 | return false; | ||
509 | } | 889 | } |
510 | 890 | ||
511 | void transaction_notify_view_ready_by_geometry(struct sway_view *view, | 891 | bool transaction_notify_view_ready_by_geometry(struct sway_view *view, |
512 | double x, double y, int width, int height) { | 892 | double x, double y, int width, int height) { |
513 | struct sway_transaction_instruction *instruction = | 893 | struct sway_transaction_instruction *instruction = |
514 | view->container->node.instruction; | 894 | view->container->node.instruction; |
@@ -518,7 +898,9 @@ void transaction_notify_view_ready_by_geometry(struct sway_view *view, | |||
518 | instruction->container_state.content_width == width && | 898 | instruction->container_state.content_width == width && |
519 | instruction->container_state.content_height == height) { | 899 | instruction->container_state.content_height == height) { |
520 | set_instruction_ready(instruction); | 900 | set_instruction_ready(instruction); |
901 | return true; | ||
521 | } | 902 | } |
903 | return false; | ||
522 | } | 904 | } |
523 | 905 | ||
524 | static void _transaction_commit_dirty(bool server_request) { | 906 | static void _transaction_commit_dirty(bool server_request) { |
diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c index 8da922d5..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,55 +18,39 @@ | |||
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_view_coords(struct sway_view_child *child, | ||
25 | int *sx, int *sy) { | ||
26 | struct sway_xdg_popup *popup = (struct sway_xdg_popup *)child; | ||
27 | struct wlr_xdg_popup *wlr_popup = popup->wlr_xdg_popup; | ||
28 | |||
29 | wlr_xdg_popup_get_toplevel_coords(wlr_popup, | ||
30 | wlr_popup->current.geometry.x - wlr_popup->base->current.geometry.x, | ||
31 | wlr_popup->current.geometry.y - wlr_popup->base->current.geometry.y, | ||
32 | sx, sy); | ||
33 | } | ||
34 | |||
35 | static void popup_destroy(struct sway_view_child *child) { | ||
36 | if (!sway_assert(child->impl == &popup_impl, | ||
37 | "Expected an xdg_shell popup")) { | ||
38 | return; | ||
39 | } | ||
40 | struct sway_xdg_popup *popup = (struct sway_xdg_popup *)child; | ||
41 | wl_list_remove(&popup->new_popup.link); | ||
42 | wl_list_remove(&popup->destroy.link); | ||
43 | free(popup); | ||
44 | } | ||
45 | |||
46 | static const struct sway_view_child_impl popup_impl = { | ||
47 | .get_view_coords = popup_get_view_coords, | ||
48 | .destroy = popup_destroy, | ||
49 | }; | ||
50 | |||
51 | static struct sway_xdg_popup *popup_create( | 21 | static struct sway_xdg_popup *popup_create( |
52 | 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); | ||
53 | 24 | ||
54 | 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) { |
55 | struct sway_xdg_popup *popup = | 26 | struct sway_xdg_popup *popup = |
56 | wl_container_of(listener, popup, new_popup); | 27 | wl_container_of(listener, popup, new_popup); |
57 | struct wlr_xdg_popup *wlr_popup = data; | 28 | struct wlr_xdg_popup *wlr_popup = data; |
58 | popup_create(wlr_popup, popup->child.view); | 29 | popup_create(wlr_popup, popup->view, popup->xdg_surface_tree); |
59 | } | 30 | } |
60 | 31 | ||
61 | static void popup_handle_destroy(struct wl_listener *listener, void *data) { | 32 | static void popup_handle_destroy(struct wl_listener *listener, void *data) { |
62 | struct sway_xdg_popup *popup = wl_container_of(listener, popup, destroy); | 33 | struct sway_xdg_popup *popup = wl_container_of(listener, popup, destroy); |
63 | 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); | ||
64 | } | 41 | } |
65 | 42 | ||
66 | static void popup_unconstrain(struct sway_xdg_popup *popup) { | 43 | static void popup_unconstrain(struct sway_xdg_popup *popup) { |
67 | struct sway_view *view = popup->child.view; | 44 | struct sway_view *view = popup->view; |
68 | struct wlr_xdg_popup *wlr_popup = popup->wlr_xdg_popup; | 45 | struct wlr_xdg_popup *wlr_popup = popup->wlr_xdg_popup; |
69 | 46 | ||
70 | struct sway_output *output = view->container->pending.workspace->output; | 47 | struct sway_workspace *workspace = view->container->pending.workspace; |
48 | if (!workspace) { | ||
49 | // is null if in the scratchpad | ||
50 | return; | ||
51 | } | ||
52 | |||
53 | struct sway_output *output = workspace->output; | ||
71 | 54 | ||
72 | // 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 |
73 | // of the popup | 56 | // of the popup |
@@ -81,32 +64,72 @@ static void popup_unconstrain(struct sway_xdg_popup *popup) { | |||
81 | 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); |
82 | } | 65 | } |
83 | 66 | ||
84 | static struct sway_xdg_popup *popup_create( | 67 | static void popup_handle_surface_commit(struct wl_listener *listener, void *data) { |
85 | 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) { | ||
86 | struct wlr_xdg_surface *xdg_surface = wlr_popup->base; | 81 | struct wlr_xdg_surface *xdg_surface = wlr_popup->base; |
87 | 82 | ||
88 | struct sway_xdg_popup *popup = | 83 | struct sway_xdg_popup *popup = calloc(1, sizeof(struct sway_xdg_popup)); |
89 | calloc(1, sizeof(struct sway_xdg_popup)); | 84 | if (!popup) { |
90 | 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); | ||
91 | return NULL; | 94 | return NULL; |
92 | } | 95 | } |
93 | view_child_init(&popup->child, &popup_impl, view, xdg_surface->surface); | 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); | ||
102 | return NULL; | ||
103 | } | ||
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 | |||
94 | popup->wlr_xdg_popup = xdg_surface->popup; | 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; | ||
95 | 120 | ||
121 | wl_signal_add(&xdg_surface->surface->events.commit, &popup->surface_commit); | ||
122 | popup->surface_commit.notify = popup_handle_surface_commit; | ||
96 | wl_signal_add(&xdg_surface->events.new_popup, &popup->new_popup); | 123 | wl_signal_add(&xdg_surface->events.new_popup, &popup->new_popup); |
97 | popup->new_popup.notify = popup_handle_new_popup; | 124 | popup->new_popup.notify = popup_handle_new_popup; |
98 | 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); | ||
99 | popup->destroy.notify = popup_handle_destroy; | 128 | popup->destroy.notify = popup_handle_destroy; |
100 | 129 | ||
101 | wl_signal_add(&xdg_surface->events.map, &popup->child.surface_map); | ||
102 | wl_signal_add(&xdg_surface->events.unmap, &popup->child.surface_unmap); | ||
103 | |||
104 | popup_unconstrain(popup); | ||
105 | |||
106 | return popup; | 130 | return popup; |
107 | } | 131 | } |
108 | 132 | ||
109 | |||
110 | static struct sway_xdg_shell_view *xdg_shell_view_from_view( | 133 | static struct sway_xdg_shell_view *xdg_shell_view_from_view( |
111 | struct sway_view *view) { | 134 | struct sway_view *view) { |
112 | if (!sway_assert(view->type == SWAY_VIEW_XDG_SHELL, | 135 | if (!sway_assert(view->type == SWAY_VIEW_XDG_SHELL, |
@@ -163,12 +186,19 @@ static void set_tiled(struct sway_view *view, bool tiled) { | |||
163 | if (xdg_shell_view_from_view(view) == NULL) { | 186 | if (xdg_shell_view_from_view(view) == NULL) { |
164 | return; | 187 | return; |
165 | } | 188 | } |
166 | enum wlr_edges edges = WLR_EDGE_NONE; | 189 | if (wl_resource_get_version(view->wlr_xdg_toplevel->resource) >= |
167 | if (tiled) { | 190 | XDG_TOPLEVEL_STATE_TILED_LEFT_SINCE_VERSION) { |
168 | edges = WLR_EDGE_LEFT | WLR_EDGE_RIGHT | WLR_EDGE_TOP | | 191 | enum wlr_edges edges = WLR_EDGE_NONE; |
169 | WLR_EDGE_BOTTOM; | 192 | if (tiled) { |
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); | ||
170 | } | 201 | } |
171 | wlr_xdg_toplevel_set_tiled(view->wlr_xdg_toplevel, edges); | ||
172 | } | 202 | } |
173 | 203 | ||
174 | static void set_fullscreen(struct sway_view *view, bool fullscreen) { | 204 | static void set_fullscreen(struct sway_view *view, bool fullscreen) { |
@@ -194,24 +224,6 @@ static bool wants_floating(struct sway_view *view) { | |||
194 | || toplevel->parent; | 224 | || toplevel->parent; |
195 | } | 225 | } |
196 | 226 | ||
197 | static void for_each_surface(struct sway_view *view, | ||
198 | wlr_surface_iterator_func_t iterator, void *user_data) { | ||
199 | if (xdg_shell_view_from_view(view) == NULL) { | ||
200 | return; | ||
201 | } | ||
202 | wlr_xdg_surface_for_each_surface(view->wlr_xdg_toplevel->base, iterator, | ||
203 | user_data); | ||
204 | } | ||
205 | |||
206 | static void for_each_popup_surface(struct sway_view *view, | ||
207 | wlr_surface_iterator_func_t iterator, void *user_data) { | ||
208 | if (xdg_shell_view_from_view(view) == NULL) { | ||
209 | return; | ||
210 | } | ||
211 | wlr_xdg_surface_for_each_popup_surface(view->wlr_xdg_toplevel->base, | ||
212 | iterator, user_data); | ||
213 | } | ||
214 | |||
215 | static bool is_transient_for(struct sway_view *child, | 227 | static bool is_transient_for(struct sway_view *child, |
216 | struct sway_view *ancestor) { | 228 | struct sway_view *ancestor) { |
217 | if (xdg_shell_view_from_view(child) == NULL) { | 229 | if (xdg_shell_view_from_view(child) == NULL) { |
@@ -259,8 +271,6 @@ static const struct sway_view_impl view_impl = { | |||
259 | .set_fullscreen = set_fullscreen, | 271 | .set_fullscreen = set_fullscreen, |
260 | .set_resizing = set_resizing, | 272 | .set_resizing = set_resizing, |
261 | .wants_floating = wants_floating, | 273 | .wants_floating = wants_floating, |
262 | .for_each_surface = for_each_surface, | ||
263 | .for_each_popup_surface = for_each_popup_surface, | ||
264 | .is_transient_for = is_transient_for, | 274 | .is_transient_for = is_transient_for, |
265 | .close = _close, | 275 | .close = _close, |
266 | .close_popups = close_popups, | 276 | .close_popups = close_popups, |
@@ -273,6 +283,20 @@ static void handle_commit(struct wl_listener *listener, void *data) { | |||
273 | struct sway_view *view = &xdg_shell_view->view; | 283 | struct sway_view *view = &xdg_shell_view->view; |
274 | struct wlr_xdg_surface *xdg_surface = view->wlr_xdg_toplevel->base; | 284 | struct wlr_xdg_surface *xdg_surface = view->wlr_xdg_toplevel->base; |
275 | 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 | } | ||
299 | |||
276 | struct wlr_box new_geo; | 300 | struct wlr_box new_geo; |
277 | wlr_xdg_surface_get_geometry(xdg_surface, &new_geo); | 301 | wlr_xdg_surface_get_geometry(xdg_surface, &new_geo); |
278 | bool new_size = new_geo.width != view->geometry.width || | 302 | bool new_size = new_geo.width != view->geometry.width || |
@@ -284,23 +308,32 @@ static void handle_commit(struct wl_listener *listener, void *data) { | |||
284 | // The client changed its surface size in this commit. For floating | 308 | // The client changed its surface size in this commit. For floating |
285 | // containers, we resize the container to match. For tiling containers, | 309 | // containers, we resize the container to match. For tiling containers, |
286 | // we only recenter the surface. | 310 | // we only recenter the surface. |
287 | desktop_damage_view(view); | ||
288 | memcpy(&view->geometry, &new_geo, sizeof(struct wlr_box)); | 311 | memcpy(&view->geometry, &new_geo, sizeof(struct wlr_box)); |
289 | if (container_is_floating(view->container)) { | 312 | if (container_is_floating(view->container)) { |
290 | view_update_size(view); | 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 | } | ||
291 | transaction_commit_dirty_client(); | 319 | transaction_commit_dirty_client(); |
292 | } else { | ||
293 | view_center_surface(view); | ||
294 | } | 320 | } |
295 | desktop_damage_view(view); | 321 | |
322 | view_center_and_clip_surface(view); | ||
296 | } | 323 | } |
297 | 324 | ||
298 | if (view->container->node.instruction) { | 325 | if (view->container->node.instruction) { |
299 | transaction_notify_view_ready_by_serial(view, | 326 | bool successful = transaction_notify_view_ready_by_serial(view, |
300 | xdg_surface->current.configure_serial); | 327 | xdg_surface->current.configure_serial); |
301 | } | ||
302 | 328 | ||
303 | view_damage_from(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 | } | ||
336 | } | ||
304 | } | 337 | } |
305 | 338 | ||
306 | static void handle_set_title(struct wl_listener *listener, void *data) { | 339 | static void handle_set_title(struct wl_listener *listener, void *data) { |
@@ -315,6 +348,7 @@ static void handle_set_app_id(struct wl_listener *listener, void *data) { | |||
315 | struct sway_xdg_shell_view *xdg_shell_view = | 348 | struct sway_xdg_shell_view *xdg_shell_view = |
316 | wl_container_of(listener, xdg_shell_view, set_app_id); | 349 | wl_container_of(listener, xdg_shell_view, set_app_id); |
317 | struct sway_view *view = &xdg_shell_view->view; | 350 | struct sway_view *view = &xdg_shell_view->view; |
351 | view_update_app_id(view); | ||
318 | view_execute_criteria(view); | 352 | view_execute_criteria(view); |
319 | } | 353 | } |
320 | 354 | ||
@@ -322,7 +356,16 @@ static void handle_new_popup(struct wl_listener *listener, void *data) { | |||
322 | struct sway_xdg_shell_view *xdg_shell_view = | 356 | struct sway_xdg_shell_view *xdg_shell_view = |
323 | wl_container_of(listener, xdg_shell_view, new_popup); | 357 | wl_container_of(listener, xdg_shell_view, new_popup); |
324 | struct wlr_xdg_popup *wlr_popup = data; | 358 | struct wlr_xdg_popup *wlr_popup = data; |
325 | 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); | ||
326 | } | 369 | } |
327 | 370 | ||
328 | static void handle_request_maximize(struct wl_listener *listener, void *data) { | 371 | static void handle_request_maximize(struct wl_listener *listener, void *data) { |
@@ -338,7 +381,7 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data) | |||
338 | struct wlr_xdg_toplevel *toplevel = xdg_shell_view->view.wlr_xdg_toplevel; | 381 | struct wlr_xdg_toplevel *toplevel = xdg_shell_view->view.wlr_xdg_toplevel; |
339 | struct sway_view *view = &xdg_shell_view->view; | 382 | struct sway_view *view = &xdg_shell_view->view; |
340 | 383 | ||
341 | if (!toplevel->base->mapped) { | 384 | if (!toplevel->base->surface->mapped) { |
342 | return; | 385 | return; |
343 | } | 386 | } |
344 | 387 | ||
@@ -403,7 +446,6 @@ static void handle_unmap(struct wl_listener *listener, void *data) { | |||
403 | 446 | ||
404 | view_unmap(view); | 447 | view_unmap(view); |
405 | 448 | ||
406 | wl_list_remove(&xdg_shell_view->commit.link); | ||
407 | wl_list_remove(&xdg_shell_view->new_popup.link); | 449 | wl_list_remove(&xdg_shell_view->new_popup.link); |
408 | wl_list_remove(&xdg_shell_view->request_maximize.link); | 450 | wl_list_remove(&xdg_shell_view->request_maximize.link); |
409 | wl_list_remove(&xdg_shell_view->request_fullscreen.link); | 451 | wl_list_remove(&xdg_shell_view->request_fullscreen.link); |
@@ -446,10 +488,6 @@ static void handle_map(struct wl_listener *listener, void *data) { | |||
446 | 488 | ||
447 | transaction_commit_dirty(); | 489 | transaction_commit_dirty(); |
448 | 490 | ||
449 | xdg_shell_view->commit.notify = handle_commit; | ||
450 | wl_signal_add(&toplevel->base->surface->events.commit, | ||
451 | &xdg_shell_view->commit); | ||
452 | |||
453 | xdg_shell_view->new_popup.notify = handle_new_popup; | 491 | xdg_shell_view->new_popup.notify = handle_new_popup; |
454 | wl_signal_add(&toplevel->base->events.new_popup, | 492 | wl_signal_add(&toplevel->base->events.new_popup, |
455 | &xdg_shell_view->new_popup); | 493 | &xdg_shell_view->new_popup); |
@@ -489,6 +527,7 @@ static void handle_destroy(struct wl_listener *listener, void *data) { | |||
489 | wl_list_remove(&xdg_shell_view->destroy.link); | 527 | wl_list_remove(&xdg_shell_view->destroy.link); |
490 | wl_list_remove(&xdg_shell_view->map.link); | 528 | wl_list_remove(&xdg_shell_view->map.link); |
491 | wl_list_remove(&xdg_shell_view->unmap.link); | 529 | wl_list_remove(&xdg_shell_view->unmap.link); |
530 | wl_list_remove(&xdg_shell_view->commit.link); | ||
492 | view->wlr_xdg_toplevel = NULL; | 531 | view->wlr_xdg_toplevel = NULL; |
493 | if (view->xdg_decoration) { | 532 | if (view->xdg_decoration) { |
494 | view->xdg_decoration->view = NULL; | 533 | view->xdg_decoration->view = NULL; |
@@ -501,17 +540,12 @@ struct sway_view *view_from_wlr_xdg_surface( | |||
501 | return xdg_surface->data; | 540 | return xdg_surface->data; |
502 | } | 541 | } |
503 | 542 | ||
504 | void handle_xdg_shell_surface(struct wl_listener *listener, void *data) { | 543 | void handle_xdg_shell_toplevel(struct wl_listener *listener, void *data) { |
505 | struct wlr_xdg_surface *xdg_surface = data; | 544 | struct wlr_xdg_toplevel *xdg_toplevel = data; |
506 | |||
507 | if (xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP) { | ||
508 | sway_log(SWAY_DEBUG, "New xdg_shell popup"); | ||
509 | return; | ||
510 | } | ||
511 | 545 | ||
512 | 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'", |
513 | xdg_surface->toplevel->title, xdg_surface->toplevel->app_id); | 547 | xdg_toplevel->title, xdg_toplevel->app_id); |
514 | wlr_xdg_surface_ping(xdg_surface); | 548 | wlr_xdg_surface_ping(xdg_toplevel->base); |
515 | 549 | ||
516 | struct sway_xdg_shell_view *xdg_shell_view = | 550 | struct sway_xdg_shell_view *xdg_shell_view = |
517 | calloc(1, sizeof(struct sway_xdg_shell_view)); | 551 | calloc(1, sizeof(struct sway_xdg_shell_view)); |
@@ -519,17 +553,29 @@ void handle_xdg_shell_surface(struct wl_listener *listener, void *data) { | |||
519 | return; | 553 | return; |
520 | } | 554 | } |
521 | 555 | ||
522 | 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)) { |
523 | xdg_shell_view->view.wlr_xdg_toplevel = xdg_surface->toplevel; | 557 | free(xdg_shell_view); |
558 | return; | ||
559 | } | ||
560 | xdg_shell_view->view.wlr_xdg_toplevel = xdg_toplevel; | ||
524 | 561 | ||
525 | xdg_shell_view->map.notify = handle_map; | 562 | xdg_shell_view->map.notify = handle_map; |
526 | 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); |
527 | 564 | ||
528 | xdg_shell_view->unmap.notify = handle_unmap; | 565 | xdg_shell_view->unmap.notify = handle_unmap; |
529 | 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); | ||
530 | 571 | ||
531 | xdg_shell_view->destroy.notify = handle_destroy; | 572 | xdg_shell_view->destroy.notify = handle_destroy; |
532 | 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; | ||
533 | 578 | ||
534 | xdg_surface->data = xdg_shell_view; | 579 | wlr_xdg_toplevel_set_wm_capabilities(xdg_toplevel, |
580 | XDG_TOPLEVEL_WM_CAPABILITIES_FULLSCREEN); | ||
535 | } | 581 | } |
diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c index 7c5dde53..270cf08f 100644 --- a/sway/desktop/xwayland.c +++ b/sway/desktop/xwayland.c | |||
@@ -1,21 +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> |
9 | #include <xcb/xcb_icccm.h> | 10 | #include <xcb/xcb_icccm.h> |
10 | #include "log.h" | 11 | #include "log.h" |
11 | #include "sway/desktop.h" | ||
12 | #include "sway/desktop/transaction.h" | 12 | #include "sway/desktop/transaction.h" |
13 | #include "sway/input/cursor.h" | 13 | #include "sway/input/cursor.h" |
14 | #include "sway/input/input-manager.h" | 14 | #include "sway/input/input-manager.h" |
15 | #include "sway/input/seat.h" | 15 | #include "sway/input/seat.h" |
16 | #include "sway/output.h" | 16 | #include "sway/output.h" |
17 | #include "sway/scene_descriptor.h" | ||
17 | #include "sway/tree/arrange.h" | 18 | #include "sway/tree/arrange.h" |
18 | #include "sway/tree/container.h" | 19 | #include "sway/tree/container.h" |
20 | #include "sway/server.h" | ||
19 | #include "sway/tree/view.h" | 21 | #include "sway/tree/view.h" |
20 | #include "sway/tree/workspace.h" | 22 | #include "sway/tree/workspace.h" |
21 | 23 | ||
@@ -43,29 +45,12 @@ static void unmanaged_handle_request_configure(struct wl_listener *listener, | |||
43 | ev->width, ev->height); | 45 | ev->width, ev->height); |
44 | } | 46 | } |
45 | 47 | ||
46 | static void unmanaged_handle_commit(struct wl_listener *listener, void *data) { | ||
47 | struct sway_xwayland_unmanaged *surface = | ||
48 | wl_container_of(listener, surface, commit); | ||
49 | struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface; | ||
50 | |||
51 | desktop_damage_surface(xsurface->surface, surface->lx, surface->ly, | ||
52 | false); | ||
53 | } | ||
54 | |||
55 | 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) { |
56 | struct sway_xwayland_unmanaged *surface = | 49 | struct sway_xwayland_unmanaged *surface = |
57 | wl_container_of(listener, surface, set_geometry); | 50 | wl_container_of(listener, surface, set_geometry); |
58 | struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface; | 51 | struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface; |
59 | 52 | ||
60 | if (xsurface->x != surface->lx || xsurface->y != surface->ly) { | 53 | wlr_scene_node_set_position(&surface->surface_scene->buffer->node, xsurface->x, xsurface->y); |
61 | // Surface has moved | ||
62 | desktop_damage_surface(xsurface->surface, surface->lx, surface->ly, | ||
63 | true); | ||
64 | surface->lx = xsurface->x; | ||
65 | surface->ly = xsurface->y; | ||
66 | desktop_damage_surface(xsurface->surface, surface->lx, surface->ly, | ||
67 | true); | ||
68 | } | ||
69 | } | 54 | } |
70 | 55 | ||
71 | static void unmanaged_handle_map(struct wl_listener *listener, void *data) { | 56 | static void unmanaged_handle_map(struct wl_listener *listener, void *data) { |
@@ -73,17 +58,18 @@ static void unmanaged_handle_map(struct wl_listener *listener, void *data) { | |||
73 | wl_container_of(listener, surface, map); | 58 | wl_container_of(listener, surface, map); |
74 | struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface; | 59 | struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface; |
75 | 60 | ||
76 | wl_list_insert(root->xwayland_unmanaged.prev, &surface->link); | 61 | surface->surface_scene = wlr_scene_surface_create(root->layers.unmanaged, |
62 | xsurface->surface); | ||
77 | 63 | ||
78 | wl_signal_add(&xsurface->events.set_geometry, &surface->set_geometry); | 64 | if (surface->surface_scene) { |
79 | surface->set_geometry.notify = unmanaged_handle_set_geometry; | 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); | ||
80 | 69 | ||
81 | wl_signal_add(&xsurface->surface->events.commit, &surface->commit); | 70 | wl_signal_add(&xsurface->events.set_geometry, &surface->set_geometry); |
82 | surface->commit.notify = unmanaged_handle_commit; | 71 | surface->set_geometry.notify = unmanaged_handle_set_geometry; |
83 | 72 | } | |
84 | surface->lx = xsurface->x; | ||
85 | surface->ly = xsurface->y; | ||
86 | desktop_damage_surface(xsurface->surface, surface->lx, surface->ly, true); | ||
87 | 73 | ||
88 | if (wlr_xwayland_or_surface_wants_focus(xsurface)) { | 74 | if (wlr_xwayland_or_surface_wants_focus(xsurface)) { |
89 | struct sway_seat *seat = input_manager_current_seat(); | 75 | struct sway_seat *seat = input_manager_current_seat(); |
@@ -97,10 +83,13 @@ static void unmanaged_handle_unmap(struct wl_listener *listener, void *data) { | |||
97 | struct sway_xwayland_unmanaged *surface = | 83 | struct sway_xwayland_unmanaged *surface = |
98 | wl_container_of(listener, surface, unmap); | 84 | wl_container_of(listener, surface, unmap); |
99 | struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface; | 85 | struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface; |
100 | desktop_damage_surface(xsurface->surface, xsurface->x, xsurface->y, true); | 86 | |
101 | wl_list_remove(&surface->link); | 87 | if (surface->surface_scene) { |
102 | wl_list_remove(&surface->set_geometry.link); | 88 | wl_list_remove(&surface->set_geometry.link); |
103 | wl_list_remove(&surface->commit.link); | 89 | |
90 | wlr_scene_node_destroy(&surface->surface_scene->buffer->node); | ||
91 | surface->surface_scene = NULL; | ||
92 | } | ||
104 | 93 | ||
105 | struct sway_seat *seat = input_manager_current_seat(); | 94 | struct sway_seat *seat = input_manager_current_seat(); |
106 | if (seat->wlr_seat->keyboard_state.focused_surface == xsurface->surface) { | 95 | if (seat->wlr_seat->keyboard_state.focused_surface == xsurface->surface) { |
@@ -123,8 +112,10 @@ static void unmanaged_handle_unmap(struct wl_listener *listener, void *data) { | |||
123 | } | 112 | } |
124 | 113 | ||
125 | static void unmanaged_handle_request_activate(struct wl_listener *listener, void *data) { | 114 | static void unmanaged_handle_request_activate(struct wl_listener *listener, void *data) { |
126 | struct wlr_xwayland_surface *xsurface = data; | 115 | struct sway_xwayland_unmanaged *surface = |
127 | if (!xsurface->mapped) { | 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) { | ||
128 | return; | 119 | return; |
129 | } | 120 | } |
130 | struct sway_seat *seat = input_manager_current_seat(); | 121 | struct sway_seat *seat = input_manager_current_seat(); |
@@ -136,12 +127,29 @@ static void unmanaged_handle_request_activate(struct wl_listener *listener, void | |||
136 | seat_set_focus_surface(seat, xsurface->surface, false); | 127 | seat_set_focus_surface(seat, xsurface->surface, false); |
137 | } | 128 | } |
138 | 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 | |||
139 | static void unmanaged_handle_destroy(struct wl_listener *listener, void *data) { | 147 | static void unmanaged_handle_destroy(struct wl_listener *listener, void *data) { |
140 | struct sway_xwayland_unmanaged *surface = | 148 | struct sway_xwayland_unmanaged *surface = |
141 | wl_container_of(listener, surface, destroy); | 149 | wl_container_of(listener, surface, destroy); |
142 | wl_list_remove(&surface->request_configure.link); | 150 | wl_list_remove(&surface->request_configure.link); |
143 | wl_list_remove(&surface->map.link); | 151 | wl_list_remove(&surface->associate.link); |
144 | wl_list_remove(&surface->unmap.link); | 152 | wl_list_remove(&surface->dissociate.link); |
145 | wl_list_remove(&surface->destroy.link); | 153 | wl_list_remove(&surface->destroy.link); |
146 | wl_list_remove(&surface->override_redirect.link); | 154 | wl_list_remove(&surface->override_redirect.link); |
147 | wl_list_remove(&surface->request_activate.link); | 155 | wl_list_remove(&surface->request_activate.link); |
@@ -149,6 +157,7 @@ static void unmanaged_handle_destroy(struct wl_listener *listener, void *data) { | |||
149 | } | 157 | } |
150 | 158 | ||
151 | 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); | ||
152 | 161 | ||
153 | 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); |
154 | 163 | ||
@@ -157,14 +166,22 @@ static void unmanaged_handle_override_redirect(struct wl_listener *listener, voi | |||
157 | wl_container_of(listener, surface, override_redirect); | 166 | wl_container_of(listener, surface, override_redirect); |
158 | struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface; | 167 | struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface; |
159 | 168 | ||
160 | bool mapped = xsurface->mapped; | 169 | bool associated = xsurface->surface != NULL; |
170 | bool mapped = associated && xsurface->surface->mapped; | ||
161 | if (mapped) { | 171 | if (mapped) { |
162 | unmanaged_handle_unmap(&surface->unmap, NULL); | 172 | unmanaged_handle_unmap(&surface->unmap, NULL); |
163 | } | 173 | } |
174 | if (associated) { | ||
175 | unmanaged_handle_dissociate(&surface->dissociate, NULL); | ||
176 | } | ||
164 | 177 | ||
165 | unmanaged_handle_destroy(&surface->destroy, NULL); | 178 | unmanaged_handle_destroy(&surface->destroy, NULL); |
166 | xsurface->data = NULL; | 179 | xsurface->data = NULL; |
180 | |||
167 | 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 | } | ||
168 | if (mapped) { | 185 | if (mapped) { |
169 | handle_map(&xwayland_view->map, xsurface); | 186 | handle_map(&xwayland_view->map, xsurface); |
170 | } | 187 | } |
@@ -184,10 +201,10 @@ static struct sway_xwayland_unmanaged *create_unmanaged( | |||
184 | wl_signal_add(&xsurface->events.request_configure, | 201 | wl_signal_add(&xsurface->events.request_configure, |
185 | &surface->request_configure); | 202 | &surface->request_configure); |
186 | surface->request_configure.notify = unmanaged_handle_request_configure; | 203 | surface->request_configure.notify = unmanaged_handle_request_configure; |
187 | wl_signal_add(&xsurface->events.map, &surface->map); | 204 | wl_signal_add(&xsurface->events.associate, &surface->associate); |
188 | surface->map.notify = unmanaged_handle_map; | 205 | surface->associate.notify = unmanaged_handle_associate; |
189 | wl_signal_add(&xsurface->events.unmap, &surface->unmap); | 206 | wl_signal_add(&xsurface->events.dissociate, &surface->dissociate); |
190 | surface->unmap.notify = unmanaged_handle_unmap; | 207 | surface->dissociate.notify = unmanaged_handle_dissociate; |
191 | wl_signal_add(&xsurface->events.destroy, &surface->destroy); | 208 | wl_signal_add(&xsurface->events.destroy, &surface->destroy); |
192 | surface->destroy.notify = unmanaged_handle_destroy; | 209 | surface->destroy.notify = unmanaged_handle_destroy; |
193 | wl_signal_add(&xsurface->events.set_override_redirect, &surface->override_redirect); | 210 | wl_signal_add(&xsurface->events.set_override_redirect, &surface->override_redirect); |
@@ -396,17 +413,6 @@ static const struct sway_view_impl view_impl = { | |||
396 | .destroy = destroy, | 413 | .destroy = destroy, |
397 | }; | 414 | }; |
398 | 415 | ||
399 | static void get_geometry(struct sway_view *view, struct wlr_box *box) { | ||
400 | box->x = box->y = 0; | ||
401 | if (view->surface) { | ||
402 | box->width = view->surface->current.width; | ||
403 | box->height = view->surface->current.height; | ||
404 | } else { | ||
405 | box->width = 0; | ||
406 | box->height = 0; | ||
407 | } | ||
408 | } | ||
409 | |||
410 | static void handle_commit(struct wl_listener *listener, void *data) { | 416 | static void handle_commit(struct wl_listener *listener, void *data) { |
411 | struct sway_xwayland_view *xwayland_view = | 417 | struct sway_xwayland_view *xwayland_view = |
412 | wl_container_of(listener, xwayland_view, commit); | 418 | wl_container_of(listener, xwayland_view, commit); |
@@ -414,34 +420,38 @@ static void handle_commit(struct wl_listener *listener, void *data) { | |||
414 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; | 420 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; |
415 | struct wlr_surface_state *state = &xsurface->surface->current; | 421 | struct wlr_surface_state *state = &xsurface->surface->current; |
416 | 422 | ||
417 | struct wlr_box new_geo; | 423 | struct wlr_box new_geo = {0}; |
418 | get_geometry(view, &new_geo); | 424 | new_geo.width = state->width; |
425 | new_geo.height = state->height; | ||
426 | |||
419 | bool new_size = new_geo.width != view->geometry.width || | 427 | bool new_size = new_geo.width != view->geometry.width || |
420 | new_geo.height != view->geometry.height || | 428 | new_geo.height != view->geometry.height; |
421 | new_geo.x != view->geometry.x || | ||
422 | new_geo.y != view->geometry.y; | ||
423 | 429 | ||
424 | if (new_size) { | 430 | if (new_size) { |
425 | // The client changed its surface size in this commit. For floating | 431 | // The client changed its surface size in this commit. For floating |
426 | // containers, we resize the container to match. For tiling containers, | 432 | // containers, we resize the container to match. For tiling containers, |
427 | // we only recenter the surface. | 433 | // we only recenter the surface. |
428 | desktop_damage_view(view); | ||
429 | memcpy(&view->geometry, &new_geo, sizeof(struct wlr_box)); | 434 | memcpy(&view->geometry, &new_geo, sizeof(struct wlr_box)); |
430 | if (container_is_floating(view->container)) { | 435 | if (container_is_floating(view->container)) { |
431 | view_update_size(view); | 436 | view_update_size(view); |
432 | transaction_commit_dirty_client(); | 437 | transaction_commit_dirty_client(); |
433 | } else { | ||
434 | view_center_surface(view); | ||
435 | } | 438 | } |
436 | desktop_damage_view(view); | 439 | |
440 | view_center_and_clip_surface(view); | ||
437 | } | 441 | } |
438 | 442 | ||
439 | if (view->container->node.instruction) { | 443 | if (view->container->node.instruction) { |
440 | transaction_notify_view_ready_by_geometry(view, | 444 | bool successful = transaction_notify_view_ready_by_geometry(view, |
441 | xsurface->x, xsurface->y, state->width, state->height); | 445 | xsurface->x, xsurface->y, state->width, state->height); |
442 | } | ||
443 | 446 | ||
444 | view_damage_from(view); | 447 | // If we saved the view and this commit isn't what we're looking for |
448 | // that means the user will never actually see the buffers submitted to | ||
449 | // us here. Just send frame done events to these surfaces so they can | ||
450 | // commit another time for us. | ||
451 | if (view->saved_surface_tree && !successful) { | ||
452 | view_send_frame_done(view); | ||
453 | } | ||
454 | } | ||
445 | } | 455 | } |
446 | 456 | ||
447 | static void handle_destroy(struct wl_listener *listener, void *data) { | 457 | static void handle_destroy(struct wl_listener *listener, void *data) { |
@@ -466,11 +476,12 @@ static void handle_destroy(struct wl_listener *listener, void *data) { | |||
466 | wl_list_remove(&xwayland_view->set_title.link); | 476 | wl_list_remove(&xwayland_view->set_title.link); |
467 | wl_list_remove(&xwayland_view->set_class.link); | 477 | wl_list_remove(&xwayland_view->set_class.link); |
468 | 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); | ||
469 | wl_list_remove(&xwayland_view->set_window_type.link); | 480 | wl_list_remove(&xwayland_view->set_window_type.link); |
470 | wl_list_remove(&xwayland_view->set_hints.link); | 481 | wl_list_remove(&xwayland_view->set_hints.link); |
471 | wl_list_remove(&xwayland_view->set_decorations.link); | 482 | wl_list_remove(&xwayland_view->set_decorations.link); |
472 | wl_list_remove(&xwayland_view->map.link); | 483 | wl_list_remove(&xwayland_view->associate.link); |
473 | wl_list_remove(&xwayland_view->unmap.link); | 484 | wl_list_remove(&xwayland_view->dissociate.link); |
474 | wl_list_remove(&xwayland_view->override_redirect.link); | 485 | wl_list_remove(&xwayland_view->override_redirect.link); |
475 | view_begin_destroy(&xwayland_view->view); | 486 | view_begin_destroy(&xwayland_view->view); |
476 | } | 487 | } |
@@ -484,16 +495,28 @@ static void handle_unmap(struct wl_listener *listener, void *data) { | |||
484 | return; | 495 | return; |
485 | } | 496 | } |
486 | 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 | |||
487 | view_unmap(view); | 506 | view_unmap(view); |
507 | } | ||
488 | 508 | ||
489 | 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; | ||
490 | } | 513 | } |
491 | 514 | ||
492 | static void handle_map(struct wl_listener *listener, void *data) { | 515 | static void handle_map(struct wl_listener *listener, void *data) { |
493 | struct sway_xwayland_view *xwayland_view = | 516 | struct sway_xwayland_view *xwayland_view = |
494 | wl_container_of(listener, xwayland_view, map); | 517 | wl_container_of(listener, xwayland_view, map); |
495 | struct wlr_xwayland_surface *xsurface = data; | ||
496 | 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; | ||
497 | 520 | ||
498 | view->natural_width = xsurface->width; | 521 | view->natural_width = xsurface->width; |
499 | view->natural_height = xsurface->height; | 522 | view->natural_height = xsurface->height; |
@@ -506,23 +529,42 @@ static void handle_map(struct wl_listener *listener, void *data) { | |||
506 | // Put it back into the tree | 529 | // Put it back into the tree |
507 | view_map(view, xsurface->surface, xsurface->fullscreen, NULL, false); | 530 | view_map(view, xsurface->surface, xsurface->fullscreen, NULL, false); |
508 | 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 | |||
509 | transaction_commit_dirty(); | 541 | transaction_commit_dirty(); |
510 | } | 542 | } |
511 | 543 | ||
544 | static void handle_dissociate(struct wl_listener *listener, void *data); | ||
545 | |||
512 | static void handle_override_redirect(struct wl_listener *listener, void *data) { | 546 | static void handle_override_redirect(struct wl_listener *listener, void *data) { |
513 | struct sway_xwayland_view *xwayland_view = | 547 | struct sway_xwayland_view *xwayland_view = |
514 | wl_container_of(listener, xwayland_view, override_redirect); | 548 | wl_container_of(listener, xwayland_view, override_redirect); |
515 | struct wlr_xwayland_surface *xsurface = data; | ||
516 | 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; | ||
517 | 551 | ||
518 | bool mapped = xsurface->mapped; | 552 | bool associated = xsurface->surface != NULL; |
553 | bool mapped = associated && xsurface->surface->mapped; | ||
519 | if (mapped) { | 554 | if (mapped) { |
520 | handle_unmap(&xwayland_view->unmap, NULL); | 555 | handle_unmap(&xwayland_view->unmap, NULL); |
521 | } | 556 | } |
557 | if (associated) { | ||
558 | handle_dissociate(&xwayland_view->dissociate, NULL); | ||
559 | } | ||
522 | 560 | ||
523 | handle_destroy(&xwayland_view->destroy, view); | 561 | handle_destroy(&xwayland_view->destroy, view); |
524 | xsurface->data = NULL; | 562 | xsurface->data = NULL; |
563 | |||
525 | 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 | } | ||
526 | if (mapped) { | 568 | if (mapped) { |
527 | unmanaged_handle_map(&unmanaged->map, xsurface); | 569 | unmanaged_handle_map(&unmanaged->map, xsurface); |
528 | } | 570 | } |
@@ -534,7 +576,7 @@ static void handle_request_configure(struct wl_listener *listener, void *data) { | |||
534 | struct wlr_xwayland_surface_configure_event *ev = data; | 576 | struct wlr_xwayland_surface_configure_event *ev = data; |
535 | struct sway_view *view = &xwayland_view->view; | 577 | struct sway_view *view = &xwayland_view->view; |
536 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; | 578 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; |
537 | if (!xsurface->mapped) { | 579 | if (xsurface->surface == NULL || !xsurface->surface->mapped) { |
538 | wlr_xwayland_surface_configure(xsurface, ev->x, ev->y, | 580 | wlr_xwayland_surface_configure(xsurface, ev->x, ev->y, |
539 | ev->width, ev->height); | 581 | ev->width, ev->height); |
540 | return; | 582 | return; |
@@ -563,7 +605,7 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data) | |||
563 | wl_container_of(listener, xwayland_view, request_fullscreen); | 605 | wl_container_of(listener, xwayland_view, request_fullscreen); |
564 | struct sway_view *view = &xwayland_view->view; | 606 | struct sway_view *view = &xwayland_view->view; |
565 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; | 607 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; |
566 | if (!xsurface->mapped) { | 608 | if (xsurface->surface == NULL || !xsurface->surface->mapped) { |
567 | return; | 609 | return; |
568 | } | 610 | } |
569 | container_set_fullscreen(view->container, xsurface->fullscreen); | 611 | container_set_fullscreen(view->container, xsurface->fullscreen); |
@@ -577,7 +619,7 @@ static void handle_request_minimize(struct wl_listener *listener, void *data) { | |||
577 | wl_container_of(listener, xwayland_view, request_minimize); | 619 | wl_container_of(listener, xwayland_view, request_minimize); |
578 | struct sway_view *view = &xwayland_view->view; | 620 | struct sway_view *view = &xwayland_view->view; |
579 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; | 621 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; |
580 | if (!xsurface->mapped) { | 622 | if (xsurface->surface == NULL || !xsurface->surface->mapped) { |
581 | return; | 623 | return; |
582 | } | 624 | } |
583 | 625 | ||
@@ -592,7 +634,7 @@ static void handle_request_move(struct wl_listener *listener, void *data) { | |||
592 | wl_container_of(listener, xwayland_view, request_move); | 634 | wl_container_of(listener, xwayland_view, request_move); |
593 | struct sway_view *view = &xwayland_view->view; | 635 | struct sway_view *view = &xwayland_view->view; |
594 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; | 636 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; |
595 | if (!xsurface->mapped) { | 637 | if (xsurface->surface == NULL || !xsurface->surface->mapped) { |
596 | return; | 638 | return; |
597 | } | 639 | } |
598 | if (!container_is_floating(view->container) || | 640 | if (!container_is_floating(view->container) || |
@@ -608,7 +650,7 @@ static void handle_request_resize(struct wl_listener *listener, void *data) { | |||
608 | wl_container_of(listener, xwayland_view, request_resize); | 650 | wl_container_of(listener, xwayland_view, request_resize); |
609 | struct sway_view *view = &xwayland_view->view; | 651 | struct sway_view *view = &xwayland_view->view; |
610 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; | 652 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; |
611 | if (!xsurface->mapped) { | 653 | if (xsurface->surface == NULL || !xsurface->surface->mapped) { |
612 | return; | 654 | return; |
613 | } | 655 | } |
614 | if (!container_is_floating(view->container)) { | 656 | if (!container_is_floating(view->container)) { |
@@ -624,10 +666,10 @@ static void handle_request_activate(struct wl_listener *listener, void *data) { | |||
624 | wl_container_of(listener, xwayland_view, request_activate); | 666 | wl_container_of(listener, xwayland_view, request_activate); |
625 | struct sway_view *view = &xwayland_view->view; | 667 | struct sway_view *view = &xwayland_view->view; |
626 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; | 668 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; |
627 | if (!xsurface->mapped) { | 669 | if (xsurface->surface == NULL || !xsurface->surface->mapped) { |
628 | return; | 670 | return; |
629 | } | 671 | } |
630 | view_request_activate(view); | 672 | view_request_activate(view, NULL); |
631 | 673 | ||
632 | transaction_commit_dirty(); | 674 | transaction_commit_dirty(); |
633 | } | 675 | } |
@@ -637,7 +679,7 @@ static void handle_set_title(struct wl_listener *listener, void *data) { | |||
637 | wl_container_of(listener, xwayland_view, set_title); | 679 | wl_container_of(listener, xwayland_view, set_title); |
638 | struct sway_view *view = &xwayland_view->view; | 680 | struct sway_view *view = &xwayland_view->view; |
639 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; | 681 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; |
640 | if (!xsurface->mapped) { | 682 | if (xsurface->surface == NULL || !xsurface->surface->mapped) { |
641 | return; | 683 | return; |
642 | } | 684 | } |
643 | view_update_title(view, false); | 685 | view_update_title(view, false); |
@@ -649,7 +691,7 @@ static void handle_set_class(struct wl_listener *listener, void *data) { | |||
649 | wl_container_of(listener, xwayland_view, set_class); | 691 | wl_container_of(listener, xwayland_view, set_class); |
650 | struct sway_view *view = &xwayland_view->view; | 692 | struct sway_view *view = &xwayland_view->view; |
651 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; | 693 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; |
652 | if (!xsurface->mapped) { | 694 | if (xsurface->surface == NULL || !xsurface->surface->mapped) { |
653 | return; | 695 | return; |
654 | } | 696 | } |
655 | view_execute_criteria(view); | 697 | view_execute_criteria(view); |
@@ -660,18 +702,43 @@ static void handle_set_role(struct wl_listener *listener, void *data) { | |||
660 | wl_container_of(listener, xwayland_view, set_role); | 702 | wl_container_of(listener, xwayland_view, set_role); |
661 | struct sway_view *view = &xwayland_view->view; | 703 | struct sway_view *view = &xwayland_view->view; |
662 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; | 704 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; |
663 | if (!xsurface->mapped) { | 705 | if (xsurface->surface == NULL || !xsurface->surface->mapped) { |
664 | return; | 706 | return; |
665 | } | 707 | } |
666 | view_execute_criteria(view); | 708 | view_execute_criteria(view); |
667 | } | 709 | } |
668 | 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 | |||
669 | 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) { |
670 | struct sway_xwayland_view *xwayland_view = | 737 | struct sway_xwayland_view *xwayland_view = |
671 | wl_container_of(listener, xwayland_view, set_window_type); | 738 | wl_container_of(listener, xwayland_view, set_window_type); |
672 | struct sway_view *view = &xwayland_view->view; | 739 | struct sway_view *view = &xwayland_view->view; |
673 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; | 740 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; |
674 | if (!xsurface->mapped) { | 741 | if (xsurface->surface == NULL || !xsurface->surface->mapped) { |
675 | return; | 742 | return; |
676 | } | 743 | } |
677 | view_execute_criteria(view); | 744 | view_execute_criteria(view); |
@@ -682,7 +749,7 @@ static void handle_set_hints(struct wl_listener *listener, void *data) { | |||
682 | wl_container_of(listener, xwayland_view, set_hints); | 749 | wl_container_of(listener, xwayland_view, set_hints); |
683 | struct sway_view *view = &xwayland_view->view; | 750 | struct sway_view *view = &xwayland_view->view; |
684 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; | 751 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; |
685 | if (!xsurface->mapped) { | 752 | if (xsurface->surface == NULL || !xsurface->surface->mapped) { |
686 | return; | 753 | return; |
687 | } | 754 | } |
688 | const bool hints_urgency = xcb_icccm_wm_hints_get_urgency(xsurface->hints); | 755 | const bool hints_urgency = xcb_icccm_wm_hints_get_urgency(xsurface->hints); |
@@ -697,6 +764,24 @@ static void handle_set_hints(struct wl_listener *listener, void *data) { | |||
697 | } | 764 | } |
698 | } | 765 | } |
699 | 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 | |||
700 | struct sway_view *view_from_wlr_xwayland_surface( | 785 | struct sway_view *view_from_wlr_xwayland_surface( |
701 | struct wlr_xwayland_surface *xsurface) { | 786 | struct wlr_xwayland_surface *xsurface) { |
702 | return xsurface->data; | 787 | return xsurface->data; |
@@ -712,7 +797,10 @@ struct sway_xwayland_view *create_xwayland_view(struct wlr_xwayland_surface *xsu | |||
712 | return NULL; | 797 | return NULL; |
713 | } | 798 | } |
714 | 799 | ||
715 | 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 | } | ||
716 | xwayland_view->view.wlr_xwayland_surface = xsurface; | 804 | xwayland_view->view.wlr_xwayland_surface = xsurface; |
717 | 805 | ||
718 | wl_signal_add(&xsurface->events.destroy, &xwayland_view->destroy); | 806 | wl_signal_add(&xsurface->events.destroy, &xwayland_view->destroy); |
@@ -751,6 +839,10 @@ struct sway_xwayland_view *create_xwayland_view(struct wlr_xwayland_surface *xsu | |||
751 | wl_signal_add(&xsurface->events.set_role, &xwayland_view->set_role); | 839 | wl_signal_add(&xsurface->events.set_role, &xwayland_view->set_role); |
752 | xwayland_view->set_role.notify = handle_set_role; | 840 | xwayland_view->set_role.notify = handle_set_role; |
753 | 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 | |||
754 | wl_signal_add(&xsurface->events.set_window_type, | 846 | wl_signal_add(&xsurface->events.set_window_type, |
755 | &xwayland_view->set_window_type); | 847 | &xwayland_view->set_window_type); |
756 | xwayland_view->set_window_type.notify = handle_set_window_type; | 848 | xwayland_view->set_window_type.notify = handle_set_window_type; |
@@ -762,11 +854,11 @@ struct sway_xwayland_view *create_xwayland_view(struct wlr_xwayland_surface *xsu | |||
762 | &xwayland_view->set_decorations); | 854 | &xwayland_view->set_decorations); |
763 | xwayland_view->set_decorations.notify = handle_set_decorations; | 855 | xwayland_view->set_decorations.notify = handle_set_decorations; |
764 | 856 | ||
765 | wl_signal_add(&xsurface->events.unmap, &xwayland_view->unmap); | 857 | wl_signal_add(&xsurface->events.associate, &xwayland_view->associate); |
766 | xwayland_view->unmap.notify = handle_unmap; | 858 | xwayland_view->associate.notify = handle_associate; |
767 | 859 | ||
768 | wl_signal_add(&xsurface->events.map, &xwayland_view->map); | 860 | wl_signal_add(&xsurface->events.dissociate, &xwayland_view->dissociate); |
769 | xwayland_view->map.notify = handle_map; | 861 | xwayland_view->dissociate.notify = handle_dissociate; |
770 | 862 | ||
771 | wl_signal_add(&xsurface->events.set_override_redirect, | 863 | wl_signal_add(&xsurface->events.set_override_redirect, |
772 | &xwayland_view->override_redirect); | 864 | &xwayland_view->override_redirect); |