aboutsummaryrefslogtreecommitdiffstats
path: root/sway/ipc-server.c
diff options
context:
space:
mode:
authorLibravatar Drew DeVault <sir@cmpwn.com>2017-10-22 10:37:30 -0400
committerLibravatar Drew DeVault <sir@cmpwn.com>2017-11-11 09:08:50 -0500
commit7c448b408126aef0561be0761871f968921d7db0 (patch)
tree055716519b975eca17b2e27254217acd1b801c20 /sway/ipc-server.c
parentFind and link to wlroots (diff)
downloadsway-7c448b408126aef0561be0761871f968921d7db0.tar.gz
sway-7c448b408126aef0561be0761871f968921d7db0.tar.zst
sway-7c448b408126aef0561be0761871f968921d7db0.zip
Fire up the wlroots backend and run the event loop
Diffstat (limited to 'sway/ipc-server.c')
-rw-r--r--sway/ipc-server.c389
1 files changed, 22 insertions, 367 deletions
diff --git a/sway/ipc-server.c b/sway/ipc-server.c
index b560b930..9ba736d8 100644
--- a/sway/ipc-server.c
+++ b/sway/ipc-server.c
@@ -10,7 +10,6 @@
10#include <sys/socket.h> 10#include <sys/socket.h>
11#include <sys/un.h> 11#include <sys/un.h>
12#include <stdbool.h> 12#include <stdbool.h>
13#include <wlc/wlc-render.h>
14#include <unistd.h> 13#include <unistd.h>
15#include <stdlib.h> 14#include <stdlib.h>
16#include <sys/ioctl.h> 15#include <sys/ioctl.h>
@@ -31,21 +30,22 @@ struct ucred {
31#include "sway/config.h" 30#include "sway/config.h"
32#include "sway/commands.h" 31#include "sway/commands.h"
33#include "sway/input.h" 32#include "sway/input.h"
33#include "sway/server.h"
34#include "stringop.h" 34#include "stringop.h"
35#include "log.h" 35#include "log.h"
36#include "list.h" 36#include "list.h"
37#include "util.h" 37#include "util.h"
38 38
39static int ipc_socket = -1; 39static int ipc_socket = -1;
40static struct wlc_event_source *ipc_event_source = NULL; 40static struct wl_event_source *ipc_event_source = NULL;
41static struct sockaddr_un *ipc_sockaddr = NULL; 41static struct sockaddr_un *ipc_sockaddr = NULL;
42static list_t *ipc_client_list = NULL; 42static list_t *ipc_client_list = NULL;
43 43
44static const char ipc_magic[] = {'i', '3', '-', 'i', 'p', 'c'}; 44static const char ipc_magic[] = {'i', '3', '-', 'i', 'p', 'c'};
45 45
46struct ipc_client { 46struct ipc_client {
47 struct wlc_event_source *event_source; 47 struct wl_event_source *event_source;
48 struct wlc_event_source *writable_event_source; 48 struct wl_event_source *writable_event_source;
49 int fd; 49 int fd;
50 uint32_t payload_length; 50 uint32_t payload_length;
51 uint32_t security_policy; 51 uint32_t security_policy;
@@ -58,25 +58,6 @@ struct ipc_client {
58 58
59static list_t *ipc_get_pixel_requests = NULL; 59static list_t *ipc_get_pixel_requests = NULL;
60 60
61struct get_pixels_request {
62 struct ipc_client *client;
63 wlc_handle output;
64 struct wlc_geometry geo;
65};
66
67struct get_clipboard_request {
68 struct ipc_client *client;
69 json_object *json;
70 int fd;
71 struct wlc_event_source *fd_event_source;
72 struct wlc_event_source *timer_event_source;
73 char *type;
74 unsigned int *pending;
75 char *buf;
76 size_t buf_size;
77 size_t buf_position;
78};
79
80struct sockaddr_un *ipc_user_sockaddr(void); 61struct sockaddr_un *ipc_user_sockaddr(void);
81int ipc_handle_connection(int fd, uint32_t mask, void *data); 62int ipc_handle_connection(int fd, uint32_t mask, void *data);
82int ipc_client_handle_readable(int client_fd, uint32_t mask, void *data); 63int ipc_client_handle_readable(int client_fd, uint32_t mask, void *data);
@@ -118,12 +99,13 @@ void ipc_init(void) {
118 ipc_client_list = create_list(); 99 ipc_client_list = create_list();
119 ipc_get_pixel_requests = create_list(); 100 ipc_get_pixel_requests = create_list();
120 101
121 ipc_event_source = wlc_event_loop_add_fd(ipc_socket, WLC_EVENT_READABLE, ipc_handle_connection, NULL); 102 ipc_event_source = wl_event_loop_add_fd(server.wl_event_loop, ipc_socket,
103 WL_EVENT_READABLE, ipc_handle_connection, NULL);
122} 104}
123 105
124void ipc_terminate(void) { 106void ipc_terminate(void) {
125 if (ipc_event_source) { 107 if (ipc_event_source) {
126 wlc_event_source_remove(ipc_event_source); 108 wl_event_source_remove(ipc_event_source);
127 } 109 }
128 close(ipc_socket); 110 close(ipc_socket);
129 unlink(ipc_sockaddr->sun_path); 111 unlink(ipc_sockaddr->sun_path);
@@ -176,7 +158,7 @@ static pid_t get_client_pid(int client_fd) {
176int ipc_handle_connection(int fd, uint32_t mask, void *data) { 158int ipc_handle_connection(int fd, uint32_t mask, void *data) {
177 (void) fd; (void) data; 159 (void) fd; (void) data;
178 sway_log(L_DEBUG, "Event on IPC listening socket"); 160 sway_log(L_DEBUG, "Event on IPC listening socket");
179 assert(mask == WLC_EVENT_READABLE); 161 assert(mask == WL_EVENT_READABLE);
180 162
181 int client_fd = accept(ipc_socket, NULL, NULL); 163 int client_fd = accept(ipc_socket, NULL, NULL);
182 if (client_fd == -1) { 164 if (client_fd == -1) {
@@ -207,7 +189,8 @@ int ipc_handle_connection(int fd, uint32_t mask, void *data) {
207 client->payload_length = 0; 189 client->payload_length = 0;
208 client->fd = client_fd; 190 client->fd = client_fd;
209 client->subscribed_events = 0; 191 client->subscribed_events = 0;
210 client->event_source = wlc_event_loop_add_fd(client_fd, WLC_EVENT_READABLE, ipc_client_handle_readable, client); 192 client->event_source = wl_event_loop_add_fd(server.wl_event_loop, client_fd,
193 WL_EVENT_READABLE, ipc_client_handle_readable, client);
211 client->writable_event_source = NULL; 194 client->writable_event_source = NULL;
212 195
213 client->write_buffer_size = 128; 196 client->write_buffer_size = 128;
@@ -234,13 +217,13 @@ static const int ipc_header_size = sizeof(ipc_magic)+8;
234int ipc_client_handle_readable(int client_fd, uint32_t mask, void *data) { 217int ipc_client_handle_readable(int client_fd, uint32_t mask, void *data) {
235 struct ipc_client *client = data; 218 struct ipc_client *client = data;
236 219
237 if (mask & WLC_EVENT_ERROR) { 220 if (mask & WL_EVENT_ERROR) {
238 sway_log(L_ERROR, "IPC Client socket error, removing client"); 221 sway_log(L_ERROR, "IPC Client socket error, removing client");
239 ipc_client_disconnect(client); 222 ipc_client_disconnect(client);
240 return 0; 223 return 0;
241 } 224 }
242 225
243 if (mask & WLC_EVENT_HANGUP) { 226 if (mask & WL_EVENT_HANGUP) {
244 sway_log(L_DEBUG, "Client %d hung up", client->fd); 227 sway_log(L_DEBUG, "Client %d hung up", client->fd);
245 ipc_client_disconnect(client); 228 ipc_client_disconnect(client);
246 return 0; 229 return 0;
@@ -296,13 +279,13 @@ int ipc_client_handle_readable(int client_fd, uint32_t mask, void *data) {
296int ipc_client_handle_writable(int client_fd, uint32_t mask, void *data) { 279int ipc_client_handle_writable(int client_fd, uint32_t mask, void *data) {
297 struct ipc_client *client = data; 280 struct ipc_client *client = data;
298 281
299 if (mask & WLC_EVENT_ERROR) { 282 if (mask & WL_EVENT_ERROR) {
300 sway_log(L_ERROR, "IPC Client socket error, removing client"); 283 sway_log(L_ERROR, "IPC Client socket error, removing client");
301 ipc_client_disconnect(client); 284 ipc_client_disconnect(client);
302 return 0; 285 return 0;
303 } 286 }
304 287
305 if (mask & WLC_EVENT_HANGUP) { 288 if (mask & WL_EVENT_HANGUP) {
306 sway_log(L_DEBUG, "Client %d hung up", client->fd); 289 sway_log(L_DEBUG, "Client %d hung up", client->fd);
307 ipc_client_disconnect(client); 290 ipc_client_disconnect(client);
308 return 0; 291 return 0;
@@ -328,7 +311,7 @@ int ipc_client_handle_writable(int client_fd, uint32_t mask, void *data) {
328 client->write_buffer_len -= written; 311 client->write_buffer_len -= written;
329 312
330 if (client->write_buffer_len == 0 && client->writable_event_source) { 313 if (client->write_buffer_len == 0 && client->writable_event_source) {
331 wlc_event_source_remove(client->writable_event_source); 314 wl_event_source_remove(client->writable_event_source);
332 client->writable_event_source = NULL; 315 client->writable_event_source = NULL;
333 } 316 }
334 317
@@ -345,9 +328,9 @@ void ipc_client_disconnect(struct ipc_client *client) {
345 } 328 }
346 329
347 sway_log(L_INFO, "IPC Client %d disconnected", client->fd); 330 sway_log(L_INFO, "IPC Client %d disconnected", client->fd);
348 wlc_event_source_remove(client->event_source); 331 wl_event_source_remove(client->event_source);
349 if (client->writable_event_source) { 332 if (client->writable_event_source) {
350 wlc_event_source_remove(client->writable_event_source); 333 wl_event_source_remove(client->writable_event_source);
351 } 334 }
352 int i = 0; 335 int i = 0;
353 while (i < ipc_client_list->length && ipc_client_list->items[i] != client) i++; 336 while (i < ipc_client_list->length && ipc_client_list->items[i] != client) i++;
@@ -365,165 +348,6 @@ bool output_by_name_test(swayc_t *view, void *data) {
365 return !strcmp(name, view->name); 348 return !strcmp(name, view->name);
366} 349}
367 350
368void ipc_get_pixels(wlc_handle output) {
369 if (ipc_get_pixel_requests->length == 0) {
370 return;
371 }
372
373 list_t *unhandled = create_list();
374
375 struct get_pixels_request *req;
376 int i;
377 for (i = 0; i < ipc_get_pixel_requests->length; ++i) {
378 req = ipc_get_pixel_requests->items[i];
379 if (req->output != output) {
380 list_add(unhandled, req);
381 continue;
382 }
383
384 const struct wlc_size *size = &req->geo.size;
385 struct wlc_geometry g_out;
386 char response_header[9];
387 memset(response_header, 0, sizeof(response_header));
388 char *data = malloc(sizeof(response_header) + size->w * size->h * 4);
389 if (!data) {
390 sway_log(L_ERROR, "Unable to allocate pixels for get_pixels");
391 ipc_client_disconnect(req->client);
392 free(req);
393 continue;
394 }
395 wlc_pixels_read(WLC_RGBA8888, &req->geo, &g_out, data + sizeof(response_header));
396
397 response_header[0] = 1;
398 uint32_t *_size = (uint32_t *)(response_header + 1);
399 _size[0] = g_out.size.w;
400 _size[1] = g_out.size.h;
401 size_t len = sizeof(response_header) + (g_out.size.w * g_out.size.h * 4);
402 memcpy(data, response_header, sizeof(response_header));
403 ipc_send_reply(req->client, data, len);
404 free(data);
405 // free the request since it has been handled
406 free(req);
407 }
408
409 // free old list of pixel requests and set new list to all unhandled
410 // requests (request for another output).
411 list_free(ipc_get_pixel_requests);
412 ipc_get_pixel_requests = unhandled;
413}
414
415static bool is_text_target(const char *target) {
416 return (strncmp(target, "text/", 5) == 0
417 || strcmp(target, "UTF8_STRING") == 0
418 || strcmp(target, "STRING") == 0
419 || strcmp(target, "TEXT") == 0
420 || strcmp(target, "COMPOUND_TEXT") == 0);
421}
422
423static void release_clipboard_request(struct get_clipboard_request *req) {
424 if (--(*req->pending) == 0) {
425 const char *str = json_object_to_json_string(req->json);
426 ipc_send_reply(req->client, str, (uint32_t)strlen(str));
427 json_object_put(req->json);
428 }
429
430 free(req->type);
431 free(req->buf);
432 wlc_event_source_remove(req->fd_event_source);
433 wlc_event_source_remove(req->timer_event_source);
434 close(req->fd);
435 free(req);
436}
437
438static int ipc_selection_data_cb(int fd, uint32_t mask, void *data) {
439 assert(data);
440 struct get_clipboard_request *req = (struct get_clipboard_request *)data;
441
442 if (mask & WLC_EVENT_ERROR) {
443 sway_log(L_ERROR, "Selection data fd error");
444 goto error;
445 }
446
447 if (mask & WLC_EVENT_READABLE) {
448 static const unsigned int max_size = 8192 * 1024;
449 int amt = 0;
450
451 do {
452 int size = req->buf_size - req->buf_position;
453 int amt = read(fd, req->buf + req->buf_position, size - 1);
454 if (amt < 0) {
455 if (errno == EAGAIN) {
456 return 0;
457 }
458
459 sway_log_errno(L_INFO, "Failed to read from clipboard data fd");
460 goto release;
461 }
462
463 req->buf_position += amt;
464 if (req->buf_position >= req->buf_size - 1) {
465 if (req->buf_size >= max_size) {
466 sway_log(L_ERROR, "get_clipbard: selection data too large");
467 goto error;
468 }
469 char *next = realloc(req->buf, req->buf_size *= 2);
470 if (!next) {
471 sway_log_errno(L_ERROR, "get_clipboard: realloc data buffer failed");
472 goto error;
473 }
474
475 req->buf = next;
476 }
477 } while(amt != 0);
478
479 req->buf[req->buf_position] = '\0';
480
481 json_object *obj = json_object_new_object();
482 json_object_object_add(obj, "success", json_object_new_boolean(true));
483 if (is_text_target(req->type)) {
484 json_object_object_add(obj, "content", json_object_new_string(req->buf));
485 json_object_object_add(req->json, req->type, obj);
486 } else {
487 size_t outlen;
488 char *b64 = b64_encode(req->buf, req->buf_position, &outlen);
489 json_object_object_add(obj, "content", json_object_new_string(b64));
490 free(b64);
491
492 char *type = malloc(strlen(req->type) + 8);
493 strcat(type, ";base64");
494 json_object_object_add(req->json, type, obj);
495 free(type);
496 }
497 }
498
499 goto release;
500
501error:;
502 json_object *obj = json_object_new_object();
503 json_object_object_add(obj, "success", json_object_new_boolean(false));
504 json_object_object_add(obj, "error",
505 json_object_new_string("Failed to retrieve data"));
506 json_object_object_add(req->json, req->type, obj);
507
508release:
509 release_clipboard_request(req);
510 return 0;
511}
512
513static int ipc_selection_timer_cb(void *data) {
514 assert(data);
515 struct get_clipboard_request *req = (struct get_clipboard_request *)data;
516
517 sway_log(L_INFO, "get_clipbard: timeout for type %s", req->type);
518 json_object *obj = json_object_new_object();
519 json_object_object_add(obj, "success", json_object_new_boolean(false));
520 json_object_object_add(obj, "error", json_object_new_string("Timeout"));
521 json_object_object_add(req->json, req->type, obj);
522
523 release_clipboard_request(req);
524 return 0;
525}
526
527// greedy wildcard (only "*") matching 351// greedy wildcard (only "*") matching
528bool mime_type_matches(const char *mime_type, const char *pattern) { 352bool mime_type_matches(const char *mime_type, const char *pattern) {
529 const char *wildcard = NULL; 353 const char *wildcard = NULL;
@@ -553,125 +377,6 @@ bool mime_type_matches(const char *mime_type, const char *pattern) {
553 return (*mime_type == *pattern); 377 return (*mime_type == *pattern);
554} 378}
555 379
556void ipc_get_clipboard(struct ipc_client *client, char *buf) {
557 size_t size;
558 const char **types = wlc_get_selection_types(&size);
559 if (client->payload_length == 0) {
560 json_object *obj = json_object_new_array();
561 for (size_t i = 0; i < size; ++i) {
562 json_object_array_add(obj, json_object_new_string(types[i]));
563 }
564
565 const char *str = json_object_to_json_string(obj);
566 ipc_send_reply(client, str, strlen(str));
567 json_object_put(obj);
568 return;
569 }
570
571 unescape_string(buf);
572 strip_quotes(buf);
573 list_t *requested = split_string(buf, " ");
574 json_object *json = json_object_new_object();
575 unsigned int *pending = malloc(sizeof(unsigned int));
576 *pending = 0;
577
578 for (size_t l = 0; l < (size_t) requested->length; ++l) {
579 const char *pattern = requested->items[l];
580 bool found = false;
581 for (size_t i = 0; i < size; ++i) {
582 if (!mime_type_matches(types[i], pattern)) {
583 continue;
584 }
585
586 found = true;
587
588 struct get_clipboard_request *req = malloc(sizeof(*req));
589 if (!req) {
590 sway_log(L_ERROR, "get_clipboard: request malloc failed");
591 goto data_error;
592 }
593
594 int pipes[2];
595 if (pipe(pipes) == -1) {
596 sway_log_errno(L_ERROR, "get_clipboard: pipe call failed");
597 free(req);
598 goto data_error;
599 }
600
601 fcntl(pipes[0], F_SETFD, FD_CLOEXEC | O_NONBLOCK);
602 fcntl(pipes[1], F_SETFD, FD_CLOEXEC | O_NONBLOCK);
603
604 if (!wlc_get_selection_data(types[i], pipes[1])) {
605 close(pipes[0]);
606 close(pipes[1]);
607 free(req);
608 sway_log(L_ERROR, "get_clipboard: failed to retrieve "
609 "selection data");
610 goto data_error;
611 }
612
613 if (!(req->buf = malloc(512))) {
614 close(pipes[0]);
615 close(pipes[1]);
616 free(req);
617 sway_log_errno(L_ERROR, "get_clipboard: buf malloc failed");
618 goto data_error;
619 }
620
621 (*pending)++;
622
623 req->client = client;
624 req->type = strdup(types[i]);
625 req->json = json;
626 req->pending = pending;
627 req->buf_position = 0;
628 req->buf_size = 512;
629 req->fd = pipes[0];
630 req->timer_event_source = wlc_event_loop_add_timer(ipc_selection_timer_cb, req);
631 req->fd_event_source = wlc_event_loop_add_fd(pipes[0],
632 WLC_EVENT_READABLE | WLC_EVENT_ERROR | WLC_EVENT_HANGUP,
633 &ipc_selection_data_cb, req);
634
635 wlc_event_source_timer_update(req->timer_event_source, 30000);
636
637 // NOTE: remove this goto to enable retrieving multiple
638 // targets at once. The whole implementation is already
639 // made for it. The only reason it was disabled
640 // at the time of writing is that neither wlc's xselection
641 // implementation nor (apparently) gtk on wayland supports
642 // multiple send requests at the same time which makes
643 // every request except the last one fail (and therefore
644 // return empty data)
645 goto cleanup;
646 }
647
648 if (!found) {
649 sway_log(L_INFO, "Invalid clipboard type %s requested", pattern);
650 }
651 }
652
653 if (*pending == 0) {
654 static const char *error_empty = "{ \"success\": false, \"error\": "
655 "\"No matching types found\" }";
656 ipc_send_reply(client, error_empty, (uint32_t)strlen(error_empty));
657 free(json);
658 free(pending);
659 }
660
661 goto cleanup;
662
663data_error:;
664 static const char *error_json = "{ \"success\": false, \"error\": "
665 "\"Failed to create clipboard data request\" }";
666 ipc_send_reply(client, error_json, (uint32_t)strlen(error_json));
667 free(json);
668 free(pending);
669
670cleanup:
671 list_free(requested);
672 free(types);
673}
674
675void ipc_client_handle_command(struct ipc_client *client) { 380void ipc_client_handle_command(struct ipc_client *client) {
676 if (!sway_assert(client != NULL, "client != NULL")) { 381 if (!sway_assert(client != NULL, "client != NULL")) {
677 return; 382 return;
@@ -830,52 +535,6 @@ void ipc_client_handle_command(struct ipc_client *client) {
830 goto exit_cleanup; 535 goto exit_cleanup;
831 } 536 }
832 537
833 case IPC_SWAY_GET_PIXELS:
834 {
835 char response_header[9];
836 memset(response_header, 0, sizeof(response_header));
837
838 json_object *obj = json_tokener_parse(buf);
839 json_object *o, *x, *y, *w, *h;
840
841 json_object_object_get_ex(obj, "output", &o);
842 json_object_object_get_ex(obj, "x", &x);
843 json_object_object_get_ex(obj, "y", &y);
844 json_object_object_get_ex(obj, "w", &w);
845 json_object_object_get_ex(obj, "h", &h);
846
847 struct wlc_geometry g = {
848 .origin = {
849 .x = json_object_get_int(x),
850 .y = json_object_get_int(y)
851 },
852 .size = {
853 .w = json_object_get_int(w),
854 .h = json_object_get_int(h)
855 }
856 };
857
858 swayc_t *output = swayc_by_test(&root_container, output_by_name_test, (void *)json_object_get_string(o));
859 json_object_put(obj);
860
861 if (!output) {
862 sway_log(L_ERROR, "IPC GET_PIXELS request with unknown output name");
863 ipc_send_reply(client, response_header, sizeof(response_header));
864 goto exit_cleanup;
865 }
866 struct get_pixels_request *req = malloc(sizeof(struct get_pixels_request));
867 if (!req) {
868 sway_log(L_ERROR, "Unable to allocate get_pixels request");
869 goto exit_cleanup;
870 }
871 req->client = client;
872 req->output = output->handle;
873 req->geo = g;
874 list_add(ipc_get_pixel_requests, req);
875 wlc_output_schedule_render(output->handle);
876 goto exit_cleanup;
877 }
878
879 case IPC_GET_BAR_CONFIG: 538 case IPC_GET_BAR_CONFIG:
880 { 539 {
881 if (!(client->security_policy & IPC_FEATURE_GET_BAR_CONFIG)) { 540 if (!(client->security_policy & IPC_FEATURE_GET_BAR_CONFIG)) {
@@ -917,14 +576,8 @@ void ipc_client_handle_command(struct ipc_client *client) {
917 } 576 }
918 577
919 case IPC_GET_CLIPBOARD: 578 case IPC_GET_CLIPBOARD:
920 { 579 // TODO WLR
921 if (!(client->security_policy & IPC_FEATURE_GET_CLIPBOARD)) { 580 break;
922 goto exit_denied;
923 }
924
925 ipc_get_clipboard(client, buf);
926 goto exit_cleanup;
927 }
928 581
929 default: 582 default:
930 sway_log(L_INFO, "Unknown IPC command type %i", client->current_command); 583 sway_log(L_INFO, "Unknown IPC command type %i", client->current_command);
@@ -977,7 +630,9 @@ bool ipc_send_reply(struct ipc_client *client, const char *payload, uint32_t pay
977 client->write_buffer_len += payload_length; 630 client->write_buffer_len += payload_length;
978 631
979 if (!client->writable_event_source) { 632 if (!client->writable_event_source) {
980 client->writable_event_source = wlc_event_loop_add_fd(client->fd, WLC_EVENT_WRITABLE, ipc_client_handle_writable, client); 633 client->writable_event_source = wl_event_loop_add_fd(
634 server.wl_event_loop, client->fd, WL_EVENT_WRITABLE,
635 ipc_client_handle_writable, client);
981 } 636 }
982 637
983 sway_log(L_DEBUG, "Added IPC reply to client %d queue: %s", client->fd, payload); 638 sway_log(L_DEBUG, "Added IPC reply to client %d queue: %s", client->fd, payload);