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.c255
1 files changed, 255 insertions, 0 deletions
diff --git a/sway/desktop/launcher.c b/sway/desktop/launcher.c
new file mode 100644
index 00000000..00a7e38a
--- /dev/null
+++ b/sway/desktop/launcher.c
@@ -0,0 +1,255 @@
1#define _POSIX_C_SOURCE 200809L
2#include <stdlib.h>
3#include <string.h>
4#include <wlr/types/wlr_xdg_activation_v1.h>
5#include "sway/input/seat.h"
6#include "sway/output.h"
7#include "sway/desktop/launcher.h"
8#include "sway/tree/node.h"
9#include "sway/tree/container.h"
10#include "sway/tree/workspace.h"
11#include "sway/tree/root.h"
12#include "log.h"
13
14/**
15 * Get the pid of a parent process given the pid of a child process.
16 *
17 * Returns the parent pid or NULL if the parent pid cannot be determined.
18 */
19static pid_t get_parent_pid(pid_t child) {
20 pid_t parent = -1;
21 char file_name[100];
22 char *buffer = NULL;
23 const char *sep = " ";
24 FILE *stat = NULL;
25 size_t buf_size = 0;
26
27 snprintf(file_name, sizeof(file_name), "/proc/%d/stat", child);
28
29 if ((stat = fopen(file_name, "r"))) {
30 if (getline(&buffer, &buf_size, stat) != -1) {
31 strtok(buffer, sep); // pid
32 strtok(NULL, sep); // executable name
33 strtok(NULL, sep); // state
34 char *token = strtok(NULL, sep); // parent pid
35 parent = strtol(token, NULL, 10);
36 }
37 free(buffer);
38 fclose(stat);
39 }
40
41 if (parent) {
42 return (parent == child) ? -1 : parent;
43 }
44
45 return -1;
46}
47
48void launcher_ctx_consume(struct launcher_ctx *ctx) {
49 // The view is now responsible for destroying this ctx
50 wl_list_remove(&ctx->token_destroy.link);
51 wl_list_init(&ctx->token_destroy.link);
52
53 if (!ctx->activated) {
54 // An unactivated token hasn't been destroyed yet
55 wlr_xdg_activation_token_v1_destroy(ctx->token);
56 }
57 ctx->token = NULL;
58
59 // Prevent additional matches
60 wl_list_remove(&ctx->link);
61 wl_list_init(&ctx->link);
62}
63
64void launcher_ctx_destroy(struct launcher_ctx *ctx) {
65 if (ctx == NULL) {
66 return;
67 }
68 wl_list_remove(&ctx->node_destroy.link);
69 wl_list_remove(&ctx->token_destroy.link);
70 wl_list_remove(&ctx->link);
71 wlr_xdg_activation_token_v1_destroy(ctx->token);
72 free(ctx->fallback_name);
73 free(ctx);
74}
75
76struct launcher_ctx *launcher_ctx_find_pid(pid_t pid) {
77 if (wl_list_empty(&server.pending_launcher_ctxs)) {
78 return NULL;
79 }
80
81 struct launcher_ctx *ctx = NULL;
82 sway_log(SWAY_DEBUG, "Looking up workspace for pid %d", pid);
83
84 do {
85 struct launcher_ctx *_ctx = NULL;
86 wl_list_for_each(_ctx, &server.pending_launcher_ctxs, link) {
87 if (pid == _ctx->pid) {
88 ctx = _ctx;
89 sway_log(SWAY_DEBUG,
90 "found %s match for pid %d: %s",
91 node_type_to_str(ctx->node->type), pid, node_get_name(ctx->node));
92 break;
93 }
94 }
95 pid = get_parent_pid(pid);
96 } while (pid > 1);
97
98 return ctx;
99}
100
101struct sway_workspace *launcher_ctx_get_workspace(
102 struct launcher_ctx *ctx) {
103 struct sway_workspace *ws = NULL;
104 struct sway_output *output = NULL;
105
106 switch (ctx->node->type) {
107 case N_CONTAINER:
108 // Unimplemented
109 // TODO: add container matching?
110 ws = ctx->node->sway_container->pending.workspace;
111 break;
112 case N_WORKSPACE:
113 ws = ctx->node->sway_workspace;
114 break;
115 case N_OUTPUT:
116 output = ctx->node->sway_output;
117 ws = workspace_by_name(ctx->fallback_name);
118 if (!ws) {
119 sway_log(SWAY_DEBUG,
120 "Creating workspace %s for pid %d because it disappeared",
121 ctx->fallback_name, ctx->pid);
122 if (!output->enabled) {
123 sway_log(SWAY_DEBUG,
124 "Workspace output %s is disabled, trying another one",
125 output->wlr_output->name);
126 output = NULL;
127 }
128 ws = workspace_create(output, ctx->fallback_name);
129 }
130 break;
131 case N_ROOT:
132 ws = workspace_create(NULL, ctx->fallback_name);
133 break;
134 }
135
136 return ws;
137}
138
139static void ctx_handle_node_destroy(struct wl_listener *listener, void *data) {
140 struct launcher_ctx *ctx = wl_container_of(listener, ctx, node_destroy);
141 switch (ctx->node->type) {
142 case N_CONTAINER:
143 // Unimplemented
144 break;
145 case N_WORKSPACE:;
146 struct sway_workspace *ws = ctx->node->sway_workspace;
147 wl_list_remove(&ctx->node_destroy.link);
148 wl_list_init(&ctx->node_destroy.link);
149 // We want to save this ws name to recreate later, hopefully on the
150 // same output
151 free(ctx->fallback_name);
152 ctx->fallback_name = strdup(ws->name);
153 if (!ws->output || ws->output->node.destroying) {
154 // If the output is being destroyed it would be pointless to track
155 // If the output is being disabled, we'll find out if it's still
156 // disabled when we try to match it.
157 ctx->node = &root->node;
158 break;
159 }
160 ctx->node = &ws->output->node;
161 wl_signal_add(&ctx->node->events.destroy, &ctx->node_destroy);
162 break;
163 case N_OUTPUT:
164 wl_list_remove(&ctx->node_destroy.link);
165 wl_list_init(&ctx->node_destroy.link);
166 // We'll make the ws ctx->name somewhere else
167 ctx->node = &root->node;
168 break;
169 case N_ROOT:
170 // Unreachable
171 break;
172 }
173}
174
175static void token_handle_destroy(struct wl_listener *listener, void *data) {
176 struct launcher_ctx *ctx = wl_container_of(listener, ctx, token_destroy);
177 ctx->token = NULL;
178 launcher_ctx_destroy(ctx);
179}
180
181struct launcher_ctx *launcher_ctx_create(struct wlr_xdg_activation_token_v1 *token,
182 struct sway_node *node) {
183 struct launcher_ctx *ctx = calloc(1, sizeof(struct launcher_ctx));
184
185 const char *fallback_name = NULL;
186 struct sway_workspace *ws = NULL;
187 switch (node->type) {
188 case N_CONTAINER:
189 // Unimplemented
190 free(ctx);
191 return NULL;
192 case N_WORKSPACE:
193 ws = node->sway_workspace;
194 fallback_name = ws->name;
195 break;
196 case N_OUTPUT:;
197 struct sway_output *output = node->sway_output;
198 ws = output_get_active_workspace(output);
199 fallback_name = ws ? ws->name : NULL;
200 break;
201 case N_ROOT:
202 // Unimplemented
203 free(ctx);
204 return NULL;
205 }
206
207 if (!fallback_name) {
208 // TODO: implement a better fallback.
209 free(ctx);
210 return NULL;
211 }
212
213 ctx->fallback_name = strdup(fallback_name);
214 ctx->token = token;
215 ctx->node = node;
216
217 ctx->node_destroy.notify = ctx_handle_node_destroy;
218 wl_signal_add(&ctx->node->events.destroy, &ctx->node_destroy);
219
220 ctx->token_destroy.notify = token_handle_destroy;
221 wl_signal_add(&token->events.destroy, &ctx->token_destroy);
222
223 wl_list_init(&ctx->link);
224 wl_list_insert(&server.pending_launcher_ctxs, &ctx->link);
225
226 token->data = ctx;
227 return ctx;
228}
229
230// Creates a context with a new token for the internal launcher
231struct launcher_ctx *launcher_ctx_create_internal(void) {
232 struct sway_seat *seat = input_manager_current_seat();
233 struct sway_workspace *ws = seat_get_focused_workspace(seat);
234 if (!ws) {
235 sway_log(SWAY_DEBUG, "Failed to create launch context. No workspace.");
236 return NULL;
237 }
238
239 struct wlr_xdg_activation_token_v1 *token =
240 wlr_xdg_activation_token_v1_create(server.xdg_activation_v1);
241 token->seat = seat->wlr_seat;
242
243 struct launcher_ctx *ctx = launcher_ctx_create(token, &ws->node);
244 if (!ctx) {
245 wlr_xdg_activation_token_v1_destroy(token);
246 return NULL;
247 }
248
249 return ctx;
250}
251
252const char *launcher_ctx_get_token_name(struct launcher_ctx *ctx) {
253 const char *token = wlr_xdg_activation_token_v1_get_name(ctx->token);
254 return token;
255}