diff options
Diffstat (limited to 'sway/desktop/launcher.c')
-rw-r--r-- | sway/desktop/launcher.c | 255 |
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 | */ | ||
19 | static 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 | |||
48 | void 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 | |||
64 | void 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 | |||
76 | struct 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 | |||
101 | struct 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 | |||
139 | static 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 | |||
175 | static 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 | |||
181 | struct 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 | ||
231 | struct 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 | |||
252 | const 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 | } | ||