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