aboutsummaryrefslogtreecommitdiffstats
path: root/sway/desktop/launcher.c
diff options
context:
space:
mode:
Diffstat (limited to 'sway/desktop/launcher.c')
-rw-r--r--sway/desktop/launcher.c267
1 files changed, 267 insertions, 0 deletions
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 */
18static 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
47void 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
63void 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
78struct 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
103struct 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
141static 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
177static 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
183struct 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
234static void launch_ctx_handle_seat_destroy(struct wl_listener *listener, void *data) {
235 struct launcher_ctx *ctx = wl_container_of(listener, ctx, seat_destroy);
236 ctx->seat = NULL;
237 wl_list_remove(&ctx->seat_destroy.link);
238}
239
240// Creates a context with a new token for the internal launcher
241struct 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
264const 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}