aboutsummaryrefslogtreecommitdiffstats
path: root/sway/old/ipc-server.c
diff options
context:
space:
mode:
authorLibravatar Drew DeVault <sir@cmpwn.com>2017-11-18 11:22:02 -0500
committerLibravatar Drew DeVault <sir@cmpwn.com>2017-11-18 11:22:02 -0500
commit733993a651c71f7e2198d505960d6bbd31e0e107 (patch)
treee51732c5872b624e73355f9e5b3f762101f3cd0d /sway/old/ipc-server.c
parentInitial (awful) pass on xdg shell support (diff)
downloadsway-733993a651c71f7e2198d505960d6bbd31e0e107.tar.gz
sway-733993a651c71f7e2198d505960d6bbd31e0e107.tar.zst
sway-733993a651c71f7e2198d505960d6bbd31e0e107.zip
Move everything to sway/old/
Diffstat (limited to 'sway/old/ipc-server.c')
-rw-r--r--sway/old/ipc-server.c840
1 files changed, 840 insertions, 0 deletions
diff --git a/sway/old/ipc-server.c b/sway/old/ipc-server.c
new file mode 100644
index 00000000..fd64e81b
--- /dev/null
+++ b/sway/old/ipc-server.c
@@ -0,0 +1,840 @@
1// See https://i3wm.org/docs/ipc.html for protocol information
2#ifndef __FreeBSD__
3// Any value will hide SOCK_CLOEXEC on FreeBSD (__BSD_VISIBLE=0)
4#define _XOPEN_SOURCE 700
5#endif
6#include <errno.h>
7#include <string.h>
8#include <sys/socket.h>
9#include <sys/un.h>
10#include <stdbool.h>
11#include <unistd.h>
12#include <stdlib.h>
13#include <sys/ioctl.h>
14#include <fcntl.h>
15#include <json-c/json.h>
16#include <list.h>
17#include <libinput.h>
18#ifdef __linux__
19struct ucred {
20 pid_t pid;
21 uid_t uid;
22 gid_t gid;
23};
24#endif
25#include "sway/ipc-json.h"
26#include "sway/ipc-server.h"
27#include "sway/security.h"
28#include "sway/config.h"
29#include "sway/commands.h"
30#include "sway/input.h"
31#include "sway/server.h"
32#include "stringop.h"
33#include "log.h"
34#include "list.h"
35#include "util.h"
36
37static int ipc_socket = -1;
38static struct wl_event_source *ipc_event_source = NULL;
39static struct sockaddr_un *ipc_sockaddr = NULL;
40static list_t *ipc_client_list = NULL;
41
42static const char ipc_magic[] = {'i', '3', '-', 'i', 'p', 'c'};
43
44struct ipc_client {
45 struct wl_event_source *event_source;
46 struct wl_event_source *writable_event_source;
47 int fd;
48 uint32_t payload_length;
49 uint32_t security_policy;
50 enum ipc_command_type current_command;
51 enum ipc_command_type subscribed_events;
52 size_t write_buffer_len;
53 size_t write_buffer_size;
54 char *write_buffer;
55};
56
57struct sockaddr_un *ipc_user_sockaddr(void);
58int ipc_handle_connection(int fd, uint32_t mask, void *data);
59int ipc_client_handle_readable(int client_fd, uint32_t mask, void *data);
60int ipc_client_handle_writable(int client_fd, uint32_t mask, void *data);
61void ipc_client_disconnect(struct ipc_client *client);
62void ipc_client_handle_command(struct ipc_client *client);
63bool ipc_send_reply(struct ipc_client *client, const char *payload, uint32_t payload_length);
64void ipc_get_workspaces_callback(swayc_t *workspace, void *data);
65void ipc_get_outputs_callback(swayc_t *container, void *data);
66static void ipc_get_marks_callback(swayc_t *container, void *data);
67
68void ipc_init(void) {
69 ipc_socket = socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
70 if (ipc_socket == -1) {
71 sway_abort("Unable to create IPC socket");
72 }
73
74 ipc_sockaddr = ipc_user_sockaddr();
75
76 // We want to use socket name set by user, not existing socket from another sway instance.
77 if (getenv("SWAYSOCK") != NULL && access(getenv("SWAYSOCK"), F_OK) == -1) {
78 strncpy(ipc_sockaddr->sun_path, getenv("SWAYSOCK"), sizeof(ipc_sockaddr->sun_path));
79 ipc_sockaddr->sun_path[sizeof(ipc_sockaddr->sun_path) - 1] = 0;
80 }
81
82 unlink(ipc_sockaddr->sun_path);
83 if (bind(ipc_socket, (struct sockaddr *)ipc_sockaddr, sizeof(*ipc_sockaddr)) == -1) {
84 sway_abort("Unable to bind IPC socket");
85 }
86
87 if (listen(ipc_socket, 3) == -1) {
88 sway_abort("Unable to listen on IPC socket");
89 }
90
91 // Set i3 IPC socket path so that i3-msg works out of the box
92 setenv("I3SOCK", ipc_sockaddr->sun_path, 1);
93 setenv("SWAYSOCK", ipc_sockaddr->sun_path, 1);
94
95 ipc_client_list = create_list();
96
97 ipc_event_source = wl_event_loop_add_fd(server.wl_event_loop, ipc_socket,
98 WL_EVENT_READABLE, ipc_handle_connection, NULL);
99}
100
101void ipc_terminate(void) {
102 if (ipc_event_source) {
103 wl_event_source_remove(ipc_event_source);
104 }
105 close(ipc_socket);
106 unlink(ipc_sockaddr->sun_path);
107
108 list_free(ipc_client_list);
109
110 if (ipc_sockaddr) {
111 free(ipc_sockaddr);
112 }
113}
114
115struct sockaddr_un *ipc_user_sockaddr(void) {
116 struct sockaddr_un *ipc_sockaddr = malloc(sizeof(struct sockaddr_un));
117 if (ipc_sockaddr == NULL) {
118 sway_abort("Can't allocate ipc_sockaddr");
119 }
120
121 ipc_sockaddr->sun_family = AF_UNIX;
122 int path_size = sizeof(ipc_sockaddr->sun_path);
123
124 // Env var typically set by logind, e.g. "/run/user/<user-id>"
125 const char *dir = getenv("XDG_RUNTIME_DIR");
126 if (!dir) {
127 dir = "/tmp";
128 }
129 if (path_size <= snprintf(ipc_sockaddr->sun_path, path_size,
130 "%s/sway-ipc.%i.%i.sock", dir, getuid(), getpid())) {
131 sway_abort("Socket path won't fit into ipc_sockaddr->sun_path");
132 }
133
134 return ipc_sockaddr;
135}
136
137static pid_t get_client_pid(int client_fd) {
138// FreeBSD supports getting uid/gid, but not pid
139#ifdef __linux__
140 struct ucred ucred;
141 socklen_t len = sizeof(struct ucred);
142
143 if (getsockopt(client_fd, SOL_SOCKET, SO_PEERCRED, &ucred, &len) == -1) {
144 return -1;
145 }
146
147 return ucred.pid;
148#else
149 return -1;
150#endif
151}
152
153int ipc_handle_connection(int fd, uint32_t mask, void *data) {
154 (void) fd; (void) data;
155 sway_log(L_DEBUG, "Event on IPC listening socket");
156 assert(mask == WL_EVENT_READABLE);
157
158 int client_fd = accept(ipc_socket, NULL, NULL);
159 if (client_fd == -1) {
160 sway_log_errno(L_ERROR, "Unable to accept IPC client connection");
161 return 0;
162 }
163
164 int flags;
165 if ((flags = fcntl(client_fd, F_GETFD)) == -1
166 || fcntl(client_fd, F_SETFD, flags|FD_CLOEXEC) == -1) {
167 sway_log_errno(L_ERROR, "Unable to set CLOEXEC on IPC client socket");
168 close(client_fd);
169 return 0;
170 }
171 if ((flags = fcntl(client_fd, F_GETFL)) == -1
172 || fcntl(client_fd, F_SETFL, flags|O_NONBLOCK) == -1) {
173 sway_log_errno(L_ERROR, "Unable to set NONBLOCK on IPC client socket");
174 close(client_fd);
175 return 0;
176 }
177
178 struct ipc_client* client = malloc(sizeof(struct ipc_client));
179 if (!client) {
180 sway_log(L_ERROR, "Unable to allocate ipc client");
181 close(client_fd);
182 return 0;
183 }
184 client->payload_length = 0;
185 client->fd = client_fd;
186 client->subscribed_events = 0;
187 client->event_source = wl_event_loop_add_fd(server.wl_event_loop, client_fd,
188 WL_EVENT_READABLE, ipc_client_handle_readable, client);
189 client->writable_event_source = NULL;
190
191 client->write_buffer_size = 128;
192 client->write_buffer_len = 0;
193 client->write_buffer = malloc(client->write_buffer_size);
194 if (!client->write_buffer) {
195 sway_log(L_ERROR, "Unable to allocate ipc client write buffer");
196 close(client_fd);
197 return 0;
198 }
199
200 pid_t pid = get_client_pid(client->fd);
201 client->security_policy = get_ipc_policy_mask(pid);
202
203 sway_log(L_DEBUG, "New client: fd %d, pid %d", client_fd, pid);
204
205 list_add(ipc_client_list, client);
206
207 return 0;
208}
209
210static const int ipc_header_size = sizeof(ipc_magic)+8;
211
212int ipc_client_handle_readable(int client_fd, uint32_t mask, void *data) {
213 struct ipc_client *client = data;
214
215 if (mask & WL_EVENT_ERROR) {
216 sway_log(L_ERROR, "IPC Client socket error, removing client");
217 ipc_client_disconnect(client);
218 return 0;
219 }
220
221 if (mask & WL_EVENT_HANGUP) {
222 sway_log(L_DEBUG, "Client %d hung up", client->fd);
223 ipc_client_disconnect(client);
224 return 0;
225 }
226
227 sway_log(L_DEBUG, "Client %d readable", client->fd);
228
229 int read_available;
230 if (ioctl(client_fd, FIONREAD, &read_available) == -1) {
231 sway_log_errno(L_INFO, "Unable to read IPC socket buffer size");
232 ipc_client_disconnect(client);
233 return 0;
234 }
235
236 // Wait for the rest of the command payload in case the header has already been read
237 if (client->payload_length > 0) {
238 if ((uint32_t)read_available >= client->payload_length) {
239 ipc_client_handle_command(client);
240 }
241 return 0;
242 }
243
244 if (read_available < ipc_header_size) {
245 return 0;
246 }
247
248 uint8_t buf[ipc_header_size];
249 uint32_t *buf32 = (uint32_t*)(buf + sizeof(ipc_magic));
250 // Should be fully available, because read_available >= ipc_header_size
251 ssize_t received = recv(client_fd, buf, ipc_header_size, 0);
252 if (received == -1) {
253 sway_log_errno(L_INFO, "Unable to receive header from IPC client");
254 ipc_client_disconnect(client);
255 return 0;
256 }
257
258 if (memcmp(buf, ipc_magic, sizeof(ipc_magic)) != 0) {
259 sway_log(L_DEBUG, "IPC header check failed");
260 ipc_client_disconnect(client);
261 return 0;
262 }
263
264 client->payload_length = buf32[0];
265 client->current_command = (enum ipc_command_type)buf32[1];
266
267 if (read_available - received >= (long)client->payload_length) {
268 ipc_client_handle_command(client);
269 }
270
271 return 0;
272}
273
274int ipc_client_handle_writable(int client_fd, uint32_t mask, void *data) {
275 struct ipc_client *client = data;
276
277 if (mask & WL_EVENT_ERROR) {
278 sway_log(L_ERROR, "IPC Client socket error, removing client");
279 ipc_client_disconnect(client);
280 return 0;
281 }
282
283 if (mask & WL_EVENT_HANGUP) {
284 sway_log(L_DEBUG, "Client %d hung up", client->fd);
285 ipc_client_disconnect(client);
286 return 0;
287 }
288
289 if (client->write_buffer_len <= 0) {
290 return 0;
291 }
292
293 sway_log(L_DEBUG, "Client %d writable", client->fd);
294
295 ssize_t written = write(client->fd, client->write_buffer, client->write_buffer_len);
296
297 if (written == -1 && errno == EAGAIN) {
298 return 0;
299 } else if (written == -1) {
300 sway_log_errno(L_INFO, "Unable to send data from queue to IPC client");
301 ipc_client_disconnect(client);
302 return 0;
303 }
304
305 memmove(client->write_buffer, client->write_buffer + written, client->write_buffer_len - written);
306 client->write_buffer_len -= written;
307
308 if (client->write_buffer_len == 0 && client->writable_event_source) {
309 wl_event_source_remove(client->writable_event_source);
310 client->writable_event_source = NULL;
311 }
312
313 return 0;
314}
315
316void ipc_client_disconnect(struct ipc_client *client) {
317 if (!sway_assert(client != NULL, "client != NULL")) {
318 return;
319 }
320
321 if (client->fd != -1) {
322 shutdown(client->fd, SHUT_RDWR);
323 }
324
325 sway_log(L_INFO, "IPC Client %d disconnected", client->fd);
326 wl_event_source_remove(client->event_source);
327 if (client->writable_event_source) {
328 wl_event_source_remove(client->writable_event_source);
329 }
330 int i = 0;
331 while (i < ipc_client_list->length && ipc_client_list->items[i] != client) i++;
332 list_del(ipc_client_list, i);
333 free(client->write_buffer);
334 close(client->fd);
335 free(client);
336}
337
338bool output_by_name_test(swayc_t *view, void *data) {
339 char *name = (char *)data;
340 if (view->type != C_OUTPUT) {
341 return false;
342 }
343 return !strcmp(name, view->name);
344}
345
346// greedy wildcard (only "*") matching
347bool mime_type_matches(const char *mime_type, const char *pattern) {
348 const char *wildcard = NULL;
349 while (*mime_type && *pattern) {
350 if (*pattern == '*' && !wildcard) {
351 wildcard = pattern;
352 ++pattern;
353 }
354
355 if (*mime_type != *pattern) {
356 if (!wildcard)
357 return false;
358
359 pattern = wildcard;
360 ++mime_type;
361 continue;
362 }
363
364 ++mime_type;
365 ++pattern;
366 }
367
368 while (*pattern == '*') {
369 ++pattern;
370 }
371
372 return (*mime_type == *pattern);
373}
374
375void ipc_client_handle_command(struct ipc_client *client) {
376 if (!sway_assert(client != NULL, "client != NULL")) {
377 return;
378 }
379
380 char *buf = malloc(client->payload_length + 1);
381 if (!buf) {
382 sway_log_errno(L_INFO, "Unable to allocate IPC payload");
383 ipc_client_disconnect(client);
384 return;
385 }
386 if (client->payload_length > 0) {
387 // Payload should be fully available
388 ssize_t received = recv(client->fd, buf, client->payload_length, 0);
389 if (received == -1)
390 {
391 sway_log_errno(L_INFO, "Unable to receive payload from IPC client");
392 ipc_client_disconnect(client);
393 free(buf);
394 return;
395 }
396 }
397 buf[client->payload_length] = '\0';
398
399 const char *error_denied = "{ \"success\": false, \"error\": \"Permission denied\" }";
400
401 switch (client->current_command) {
402 case IPC_COMMAND:
403 {
404 if (!(client->security_policy & IPC_FEATURE_COMMAND)) {
405 goto exit_denied;
406 }
407 struct cmd_results *results = handle_command(buf, CONTEXT_IPC);
408 const char *json = cmd_results_to_json(results);
409 char reply[256];
410 int length = snprintf(reply, sizeof(reply), "%s", json);
411 ipc_send_reply(client, reply, (uint32_t) length);
412 free_cmd_results(results);
413 goto exit_cleanup;
414 }
415
416 case IPC_SUBSCRIBE:
417 {
418 // TODO: Check if they're permitted to use these events
419 struct json_object *request = json_tokener_parse(buf);
420 if (request == NULL) {
421 ipc_send_reply(client, "{\"success\": false}", 18);
422 sway_log_errno(L_INFO, "Failed to read request");
423 goto exit_cleanup;
424 }
425
426 // parse requested event types
427 for (int i = 0; i < json_object_array_length(request); i++) {
428 const char *event_type = json_object_get_string(json_object_array_get_idx(request, i));
429 if (strcmp(event_type, "workspace") == 0) {
430 client->subscribed_events |= event_mask(IPC_EVENT_WORKSPACE);
431 } else if (strcmp(event_type, "barconfig_update") == 0) {
432 client->subscribed_events |= event_mask(IPC_EVENT_BARCONFIG_UPDATE);
433 } else if (strcmp(event_type, "mode") == 0) {
434 client->subscribed_events |= event_mask(IPC_EVENT_MODE);
435 } else if (strcmp(event_type, "window") == 0) {
436 client->subscribed_events |= event_mask(IPC_EVENT_WINDOW);
437 } else if (strcmp(event_type, "modifier") == 0) {
438 client->subscribed_events |= event_mask(IPC_EVENT_MODIFIER);
439 } else if (strcmp(event_type, "binding") == 0) {
440 client->subscribed_events |= event_mask(IPC_EVENT_BINDING);
441 } else {
442 ipc_send_reply(client, "{\"success\": false}", 18);
443 json_object_put(request);
444 sway_log_errno(L_INFO, "Failed to parse request");
445 goto exit_cleanup;
446 }
447 }
448
449 json_object_put(request);
450
451 ipc_send_reply(client, "{\"success\": true}", 17);
452 goto exit_cleanup;
453 }
454
455 case IPC_GET_WORKSPACES:
456 {
457 if (!(client->security_policy & IPC_FEATURE_GET_WORKSPACES)) {
458 goto exit_denied;
459 }
460 json_object *workspaces = json_object_new_array();
461 container_map(&root_container, ipc_get_workspaces_callback, workspaces);
462 const char *json_string = json_object_to_json_string(workspaces);
463 ipc_send_reply(client, json_string, (uint32_t) strlen(json_string));
464 json_object_put(workspaces); // free
465 goto exit_cleanup;
466 }
467
468 case IPC_GET_INPUTS:
469 {
470 if (!(client->security_policy & IPC_FEATURE_GET_INPUTS)) {
471 goto exit_denied;
472 }
473 json_object *inputs = json_object_new_array();
474 /* TODO WLR
475 if (input_devices) {
476 for(int i = 0; i<input_devices->length; i++) {
477 struct libinput_device *device = input_devices->items[i];
478 json_object_array_add(inputs, ipc_json_describe_input(device));
479 }
480 }
481 */
482 const char *json_string = json_object_to_json_string(inputs);
483 ipc_send_reply(client, json_string, (uint32_t) strlen(json_string));
484 json_object_put(inputs);
485 goto exit_cleanup;
486 }
487
488 case IPC_GET_OUTPUTS:
489 {
490 if (!(client->security_policy & IPC_FEATURE_GET_OUTPUTS)) {
491 goto exit_denied;
492 }
493 json_object *outputs = json_object_new_array();
494 container_map(&root_container, ipc_get_outputs_callback, outputs);
495 const char *json_string = json_object_to_json_string(outputs);
496 ipc_send_reply(client, json_string, (uint32_t) strlen(json_string));
497 json_object_put(outputs); // free
498 goto exit_cleanup;
499 }
500
501 case IPC_GET_TREE:
502 {
503 if (!(client->security_policy & IPC_FEATURE_GET_TREE)) {
504 goto exit_denied;
505 }
506 json_object *tree = ipc_json_describe_container_recursive(&root_container);
507 const char *json_string = json_object_to_json_string(tree);
508 ipc_send_reply(client, json_string, (uint32_t) strlen(json_string));
509 json_object_put(tree);
510 goto exit_cleanup;
511 }
512
513 case IPC_GET_MARKS:
514 {
515 if (!(client->security_policy & IPC_FEATURE_GET_MARKS)) {
516 goto exit_denied;
517 }
518 json_object *marks = json_object_new_array();
519 container_map(&root_container, ipc_get_marks_callback, marks);
520 const char *json_string = json_object_to_json_string(marks);
521 ipc_send_reply(client, json_string, (uint32_t) strlen(json_string));
522 json_object_put(marks);
523 goto exit_cleanup;
524 }
525
526 case IPC_GET_VERSION:
527 {
528 json_object *version = ipc_json_get_version();
529 const char *json_string = json_object_to_json_string(version);
530 ipc_send_reply(client, json_string, (uint32_t)strlen(json_string));
531 json_object_put(version); // free
532 goto exit_cleanup;
533 }
534
535 case IPC_GET_BAR_CONFIG:
536 {
537 if (!(client->security_policy & IPC_FEATURE_GET_BAR_CONFIG)) {
538 goto exit_denied;
539 }
540 if (!buf[0]) {
541 // Send list of configured bar IDs
542 json_object *bars = json_object_new_array();
543 int i;
544 for (i = 0; i < config->bars->length; ++i) {
545 struct bar_config *bar = config->bars->items[i];
546 json_object_array_add(bars, json_object_new_string(bar->id));
547 }
548 const char *json_string = json_object_to_json_string(bars);
549 ipc_send_reply(client, json_string, (uint32_t)strlen(json_string));
550 json_object_put(bars); // free
551 } else {
552 // Send particular bar's details
553 struct bar_config *bar = NULL;
554 int i;
555 for (i = 0; i < config->bars->length; ++i) {
556 bar = config->bars->items[i];
557 if (strcmp(buf, bar->id) == 0) {
558 break;
559 }
560 bar = NULL;
561 }
562 if (!bar) {
563 const char *error = "{ \"success\": false, \"error\": \"No bar with that ID\" }";
564 ipc_send_reply(client, error, (uint32_t)strlen(error));
565 goto exit_cleanup;
566 }
567 json_object *json = ipc_json_describe_bar_config(bar);
568 const char *json_string = json_object_to_json_string(json);
569 ipc_send_reply(client, json_string, (uint32_t)strlen(json_string));
570 json_object_put(json); // free
571 }
572 goto exit_cleanup;
573 }
574
575 case IPC_GET_CLIPBOARD:
576 // TODO WLR
577 break;
578
579 default:
580 sway_log(L_INFO, "Unknown IPC command type %i", client->current_command);
581 goto exit_cleanup;
582 }
583
584exit_denied:
585 ipc_send_reply(client, error_denied, (uint32_t)strlen(error_denied));
586 sway_log(L_DEBUG, "Denied IPC client access to %i", client->current_command);
587
588exit_cleanup:
589 client->payload_length = 0;
590 free(buf);
591 return;
592}
593
594bool ipc_send_reply(struct ipc_client *client, const char *payload, uint32_t payload_length) {
595 assert(payload);
596
597 char data[ipc_header_size];
598 uint32_t *data32 = (uint32_t*)(data + sizeof(ipc_magic));
599
600 memcpy(data, ipc_magic, sizeof(ipc_magic));
601 data32[0] = payload_length;
602 data32[1] = client->current_command;
603
604 while (client->write_buffer_len + ipc_header_size + payload_length >=
605 client->write_buffer_size) {
606 client->write_buffer_size *= 2;
607 }
608
609 // TODO: reduce the limit back to 4 MB when screenshooter is implemented
610 if (client->write_buffer_size > (1 << 28)) { // 256 MB
611 sway_log(L_ERROR, "Client write buffer too big, disconnecting client");
612 ipc_client_disconnect(client);
613 return false;
614 }
615
616 char *new_buffer = realloc(client->write_buffer, client->write_buffer_size);
617 if (!new_buffer) {
618 sway_log(L_ERROR, "Unable to reallocate ipc client write buffer");
619 ipc_client_disconnect(client);
620 return false;
621 }
622 client->write_buffer = new_buffer;
623
624 memcpy(client->write_buffer + client->write_buffer_len, data, ipc_header_size);
625 client->write_buffer_len += ipc_header_size;
626 memcpy(client->write_buffer + client->write_buffer_len, payload, payload_length);
627 client->write_buffer_len += payload_length;
628
629 if (!client->writable_event_source) {
630 client->writable_event_source = wl_event_loop_add_fd(
631 server.wl_event_loop, client->fd, WL_EVENT_WRITABLE,
632 ipc_client_handle_writable, client);
633 }
634
635 sway_log(L_DEBUG, "Added IPC reply to client %d queue: %s", client->fd, payload);
636
637 return true;
638}
639
640void ipc_get_workspaces_callback(swayc_t *workspace, void *data) {
641 if (workspace->type == C_WORKSPACE) {
642 json_object *workspace_json = ipc_json_describe_container(workspace);
643 // override the default focused indicator because
644 // it's set differently for the get_workspaces reply
645 bool focused = root_container.focused == workspace->parent && workspace->parent->focused == workspace;
646 json_object_object_del(workspace_json, "focused");
647 json_object_object_add(workspace_json, "focused", json_object_new_boolean(focused));
648 json_object_array_add((json_object *)data, workspace_json);
649 }
650}
651
652void ipc_get_outputs_callback(swayc_t *container, void *data) {
653 if (container->type == C_OUTPUT) {
654 json_object_array_add((json_object *)data, ipc_json_describe_container(container));
655 }
656}
657
658static void ipc_get_marks_callback(swayc_t *container, void *data) {
659 json_object *object = (json_object *)data;
660 if (container->marks) {
661 for (int i = 0; i < container->marks->length; ++i) {
662 char *mark = (char *)container->marks->items[i];
663 json_object_array_add(object, json_object_new_string(mark));
664 }
665 }
666}
667
668void ipc_send_event(const char *json_string, enum ipc_command_type event) {
669 static struct {
670 enum ipc_command_type event;
671 enum ipc_feature feature;
672 } security_mappings[] = {
673 { IPC_EVENT_WORKSPACE, IPC_FEATURE_EVENT_WORKSPACE },
674 { IPC_EVENT_OUTPUT, IPC_FEATURE_EVENT_OUTPUT },
675 { IPC_EVENT_MODE, IPC_FEATURE_EVENT_MODE },
676 { IPC_EVENT_WINDOW, IPC_FEATURE_EVENT_WINDOW },
677 { IPC_EVENT_BINDING, IPC_FEATURE_EVENT_BINDING },
678 { IPC_EVENT_INPUT, IPC_FEATURE_EVENT_INPUT }
679 };
680
681 uint32_t security_mask = 0;
682 for (size_t i = 0; i < sizeof(security_mappings) / sizeof(security_mappings[0]); ++i) {
683 if (security_mappings[i].event == event) {
684 security_mask = security_mappings[i].feature;
685 break;
686 }
687 }
688
689 int i;
690 struct ipc_client *client;
691 for (i = 0; i < ipc_client_list->length; i++) {
692 client = ipc_client_list->items[i];
693 if (!(client->security_policy & security_mask)) {
694 continue;
695 }
696 if ((client->subscribed_events & event_mask(event)) == 0) {
697 continue;
698 }
699 client->current_command = event;
700 if (!ipc_send_reply(client, json_string, (uint32_t) strlen(json_string))) {
701 sway_log_errno(L_INFO, "Unable to send reply to IPC client");
702 ipc_client_disconnect(client);
703 }
704 }
705}
706
707void ipc_event_workspace(swayc_t *old, swayc_t *new, const char *change) {
708 sway_log(L_DEBUG, "Sending workspace::%s event", change);
709 json_object *obj = json_object_new_object();
710 json_object_object_add(obj, "change", json_object_new_string(change));
711 if (strcmp("focus", change) == 0) {
712 if (old) {
713 json_object_object_add(obj, "old", ipc_json_describe_container_recursive(old));
714 } else {
715 json_object_object_add(obj, "old", NULL);
716 }
717 }
718
719 if (new) {
720 json_object_object_add(obj, "current", ipc_json_describe_container_recursive(new));
721 } else {
722 json_object_object_add(obj, "current", NULL);
723 }
724
725 const char *json_string = json_object_to_json_string(obj);
726 ipc_send_event(json_string, IPC_EVENT_WORKSPACE);
727
728 json_object_put(obj); // free
729}
730
731void ipc_event_window(swayc_t *window, const char *change) {
732 sway_log(L_DEBUG, "Sending window::%s event", change);
733 json_object *obj = json_object_new_object();
734 json_object_object_add(obj, "change", json_object_new_string(change));
735 json_object_object_add(obj, "container", ipc_json_describe_container_recursive(window));
736
737 const char *json_string = json_object_to_json_string(obj);
738 ipc_send_event(json_string, IPC_EVENT_WINDOW);
739
740 json_object_put(obj); // free
741}
742
743void ipc_event_barconfig_update(struct bar_config *bar) {
744 sway_log(L_DEBUG, "Sending barconfig_update event");
745 json_object *json = ipc_json_describe_bar_config(bar);
746 const char *json_string = json_object_to_json_string(json);
747 ipc_send_event(json_string, IPC_EVENT_BARCONFIG_UPDATE);
748
749 json_object_put(json); // free
750}
751
752void ipc_event_mode(const char *mode) {
753 sway_log(L_DEBUG, "Sending mode::%s event", mode);
754 json_object *obj = json_object_new_object();
755 json_object_object_add(obj, "change", json_object_new_string(mode));
756
757 const char *json_string = json_object_to_json_string(obj);
758 ipc_send_event(json_string, IPC_EVENT_MODE);
759
760 json_object_put(obj); // free
761}
762
763void ipc_event_modifier(uint32_t modifier, const char *state) {
764 sway_log(L_DEBUG, "Sending modifier::%s event", state);
765 json_object *obj = json_object_new_object();
766 json_object_object_add(obj, "change", json_object_new_string(state));
767
768 const char *modifier_name = get_modifier_name_by_mask(modifier);
769 json_object_object_add(obj, "modifier", json_object_new_string(modifier_name));
770
771 const char *json_string = json_object_to_json_string(obj);
772 ipc_send_event(json_string, IPC_EVENT_MODIFIER);
773
774 json_object_put(obj); // free
775}
776
777static void ipc_event_binding(json_object *sb_obj) {
778 sway_log(L_DEBUG, "Sending binding::run event");
779 json_object *obj = json_object_new_object();
780 json_object_object_add(obj, "change", json_object_new_string("run"));
781 json_object_object_add(obj, "binding", sb_obj);
782
783 const char *json_string = json_object_to_json_string(obj);
784 ipc_send_event(json_string, IPC_EVENT_BINDING);
785
786 json_object_put(obj); // free
787}
788
789void ipc_event_binding_keyboard(struct sway_binding *sb) {
790 json_object *sb_obj = json_object_new_object();
791 json_object_object_add(sb_obj, "command", json_object_new_string(sb->command));
792
793 const char *names[10];
794
795 int len = get_modifier_names(names, sb->modifiers);
796 int i;
797 json_object *modifiers = json_object_new_array();
798 for (i = 0; i < len; ++i) {
799 json_object_array_add(modifiers, json_object_new_string(names[i]));
800 }
801
802 json_object_object_add(sb_obj, "event_state_mask", modifiers);
803
804 json_object *input_codes = json_object_new_array();
805 int input_code = 0;
806 json_object *symbols = json_object_new_array();
807 json_object *symbol = NULL;
808
809 if (sb->bindcode) { // bindcode: populate input_codes
810 uint32_t keycode;
811 for (i = 0; i < sb->keys->length; ++i) {
812 keycode = *(uint32_t *)sb->keys->items[i];
813 json_object_array_add(input_codes, json_object_new_int(keycode));
814 if (i == 0) {
815 input_code = keycode;
816 }
817 }
818 } else { // bindsym: populate symbols
819 uint32_t keysym;
820 char buffer[64];
821 for (i = 0; i < sb->keys->length; ++i) {
822 keysym = *(uint32_t *)sb->keys->items[i];
823 if (xkb_keysym_get_name(keysym, buffer, 64) > 0) {
824 json_object *str = json_object_new_string(buffer);
825 json_object_array_add(symbols, str);
826 if (i == 0) {
827 symbol = str;
828 }
829 }
830 }
831 }
832
833 json_object_object_add(sb_obj, "input_codes", input_codes);
834 json_object_object_add(sb_obj, "input_code", json_object_new_int(input_code));
835 json_object_object_add(sb_obj, "symbols", symbols);
836 json_object_object_add(sb_obj, "symbol", symbol);
837 json_object_object_add(sb_obj, "input_type", json_object_new_string("keyboard"));
838
839 ipc_event_binding(sb_obj);
840}