diff options
-rw-r--r-- | sway/ipc-server.c | 196 |
1 files changed, 114 insertions, 82 deletions
diff --git a/sway/ipc-server.c b/sway/ipc-server.c index ae758883..937382af 100644 --- a/sway/ipc-server.c +++ b/sway/ipc-server.c | |||
@@ -59,9 +59,13 @@ struct get_pixels_request { | |||
59 | struct get_clipboard_request { | 59 | struct get_clipboard_request { |
60 | struct ipc_client *client; | 60 | struct ipc_client *client; |
61 | json_object *json; | 61 | json_object *json; |
62 | struct wlc_event_source *event_source; | 62 | struct wlc_event_source *fd_event_source; |
63 | struct wlc_event_source *timer_event_source; | ||
63 | char *type; | 64 | char *type; |
64 | unsigned int *pending; | 65 | unsigned int *pending; |
66 | char *buf; | ||
67 | size_t buf_size; | ||
68 | size_t buf_position; | ||
65 | }; | 69 | }; |
66 | 70 | ||
67 | struct sockaddr_un *ipc_user_sockaddr(void); | 71 | struct sockaddr_un *ipc_user_sockaddr(void); |
@@ -339,54 +343,69 @@ static bool is_text_target(const char *target) { | |||
339 | || strcmp(target, "COMPOUND_TEXT") == 0); | 343 | || strcmp(target, "COMPOUND_TEXT") == 0); |
340 | } | 344 | } |
341 | 345 | ||
346 | static void release_clipboard_request(struct get_clipboard_request *req) { | ||
347 | if (--(*req->pending) == 0) { | ||
348 | const char *str = json_object_to_json_string(req->json); | ||
349 | ipc_send_reply(req->client, str, (uint32_t)strlen(str)); | ||
350 | json_object_put(req->json); | ||
351 | } | ||
352 | |||
353 | free(req->type); | ||
354 | free(req->buf); | ||
355 | wlc_event_source_remove(req->fd_event_source); | ||
356 | wlc_event_source_remove(req->timer_event_source); | ||
357 | free(req); | ||
358 | } | ||
359 | |||
342 | static int ipc_selection_data_cb(int fd, uint32_t mask, void *data) { | 360 | static int ipc_selection_data_cb(int fd, uint32_t mask, void *data) { |
343 | assert(data); | 361 | assert(data); |
344 | struct get_clipboard_request *req = (struct get_clipboard_request *)data; | 362 | struct get_clipboard_request *req = (struct get_clipboard_request *)data; |
345 | 363 | ||
346 | if (mask & WLC_EVENT_ERROR) { | 364 | if (mask & WLC_EVENT_ERROR) { |
347 | sway_log(L_ERROR, "Selection data fd error"); | 365 | sway_log(L_ERROR, "Selection data fd error"); |
348 | goto cleanup; | 366 | goto release; |
349 | } | 367 | } |
350 | 368 | ||
351 | if (mask & WLC_EVENT_READABLE) { | 369 | if (mask & WLC_EVENT_READABLE) { |
352 | static const int max_size = 8192 * 1000; | 370 | static const unsigned int max_size = 8192 * 1024; |
353 | int len = 512; | 371 | int amt = 0; |
354 | int i = 0; | 372 | |
355 | char *buf = malloc(len); | 373 | do { |
356 | 374 | int size = req->buf_size - req->buf_position; | |
357 | // read data as long as there is data avilable | 375 | int amt = read(fd, req->buf + req->buf_position, size - 1); |
358 | // grow the buffer step_size in every iteration | 376 | if (amt < 0) { |
359 | for(;;) { | 377 | if (errno == EAGAIN) { |
360 | int amt = read(fd, buf + i, len - i - 1); | 378 | return 0; |
361 | if (amt <= 0) | 379 | } |
362 | break; | 380 | |
363 | 381 | sway_log_errno(L_INFO, "Failed to read from clipboard data fd"); | |
364 | i += amt; | 382 | goto release; |
365 | if (i >= len - 1) { | 383 | } |
366 | if (len >= max_size) { | 384 | |
367 | sway_log(L_ERROR, "selection data too large"); | 385 | req->buf_position += amt; |
368 | free(buf); | 386 | if (req->buf_position >= req->buf_size - 1) { |
369 | goto cleanup; | 387 | if (req->buf_size >= max_size) { |
388 | sway_log(L_ERROR, "get_clipbard: selection data too large"); | ||
389 | goto release; | ||
370 | } | 390 | } |
371 | char *next = realloc(buf, (len *= 2)); | 391 | char *next = realloc(req->buf, req->buf_size *= 2); |
372 | if (!next) { | 392 | if (!next) { |
373 | sway_log_errno(L_ERROR, "relloc failed"); | 393 | sway_log_errno(L_ERROR, "get_clipboard: realloc data buffer failed"); |
374 | free(buf); | 394 | goto release; |
375 | goto cleanup; | ||
376 | } | 395 | } |
377 | 396 | ||
378 | buf = next; | 397 | req->buf = next; |
379 | } | 398 | } |
380 | } | 399 | } while(amt != 0); |
381 | 400 | ||
382 | buf[i] = '\0'; | 401 | req->buf[req->buf_position] = '\0'; |
383 | 402 | ||
384 | if (is_text_target(req->type)) { | 403 | if (is_text_target(req->type)) { |
385 | json_object_object_add(req->json, req->type, | 404 | json_object_object_add(req->json, req->type, |
386 | json_object_new_string(buf)); | 405 | json_object_new_string(req->buf)); |
387 | } else { | 406 | } else { |
388 | size_t outlen; | 407 | size_t outlen; |
389 | char *b64 = b64_encode(buf, i, &outlen); | 408 | char *b64 = b64_encode(req->buf, req->buf_position, &outlen); |
390 | char *type = malloc(strlen(req->type) + 8); | 409 | char *type = malloc(strlen(req->type) + 8); |
391 | strcat(type, ";base64"); | 410 | strcat(type, ";base64"); |
392 | json_object_object_add(req->json, type, | 411 | json_object_object_add(req->json, type, |
@@ -394,22 +413,19 @@ static int ipc_selection_data_cb(int fd, uint32_t mask, void *data) { | |||
394 | free(type); | 413 | free(type); |
395 | free(b64); | 414 | free(b64); |
396 | } | 415 | } |
397 | |||
398 | free(buf); | ||
399 | } | 416 | } |
400 | 417 | ||
401 | cleanup: | 418 | release: |
402 | close(fd); | 419 | release_clipboard_request(req); |
420 | return 0; | ||
421 | } | ||
403 | 422 | ||
404 | if (--(*req->pending) == 0) { | 423 | static int ipc_selection_timer_cb(void *data) { |
405 | const char *str = json_object_to_json_string(req->json); | 424 | assert(data); |
406 | ipc_send_reply(req->client, str, (uint32_t)strlen(str)); | 425 | struct get_clipboard_request *req = (struct get_clipboard_request *)data; |
407 | json_object_put(req->json); | ||
408 | } | ||
409 | 426 | ||
410 | free(req->type); | 427 | sway_log(L_INFO, "get_clipbard: timeout for type %s", req->type); |
411 | wlc_event_source_remove(req->event_source); | 428 | release_clipboard_request(req); |
412 | free(req); | ||
413 | return 0; | 429 | return 0; |
414 | } | 430 | } |
415 | 431 | ||
@@ -471,53 +487,69 @@ void ipc_get_clipboard(struct ipc_client *client, char *buf) { | |||
471 | const char *pattern = requested->items[l]; | 487 | const char *pattern = requested->items[l]; |
472 | bool found = false; | 488 | bool found = false; |
473 | for (size_t i = 0; i < size; ++i) { | 489 | for (size_t i = 0; i < size; ++i) { |
474 | if (mime_type_matches(types[i], pattern)) { | 490 | if (!mime_type_matches(types[i], pattern)) { |
475 | found = true; | 491 | continue; |
492 | } | ||
476 | 493 | ||
477 | struct get_clipboard_request *req = malloc(sizeof(*req)); | 494 | found = true; |
478 | if (!req) { | ||
479 | sway_log(L_ERROR, "Cannot allocate get_clipboard_request"); | ||
480 | goto data_error; | ||
481 | } | ||
482 | 495 | ||
483 | int pipes[2]; | 496 | struct get_clipboard_request *req = malloc(sizeof(*req)); |
484 | if (pipe(pipes) == -1) { | 497 | if (!req) { |
485 | sway_log_errno(L_ERROR, "pipe call failed"); | 498 | sway_log(L_ERROR, "get_clipboard: request malloc failed"); |
486 | free(req); | 499 | goto data_error; |
487 | goto data_error; | 500 | } |
488 | } | ||
489 | 501 | ||
490 | fcntl(pipes[0], F_SETFD, FD_CLOEXEC | O_NONBLOCK); | 502 | int pipes[2]; |
491 | fcntl(pipes[1], F_SETFD, FD_CLOEXEC | O_NONBLOCK); | 503 | if (pipe(pipes) == -1) { |
504 | sway_log_errno(L_ERROR, "get_clipboard: pipe call failed"); | ||
505 | free(req); | ||
506 | goto data_error; | ||
507 | } | ||
492 | 508 | ||
493 | if (!wlc_get_selection_data(types[i], pipes[1])) { | 509 | fcntl(pipes[0], F_SETFD, FD_CLOEXEC | O_NONBLOCK); |
494 | close(pipes[0]); | 510 | fcntl(pipes[1], F_SETFD, FD_CLOEXEC | O_NONBLOCK); |
495 | close(pipes[1]); | 511 | |
496 | free(req); | 512 | if (!wlc_get_selection_data(types[i], pipes[1])) { |
497 | sway_log(L_ERROR, "wlc_get_selection_data failed"); | 513 | close(pipes[0]); |
498 | goto data_error; | 514 | close(pipes[1]); |
499 | } | 515 | free(req); |
516 | sway_log(L_ERROR, "get_clipboard: failed to retrieve " | ||
517 | "selection data"); | ||
518 | goto data_error; | ||
519 | } | ||
500 | 520 | ||
501 | (*pending)++; | 521 | if (!(req->buf = malloc(512))) { |
502 | 522 | close(pipes[0]); | |
503 | req->client = client; | 523 | close(pipes[1]); |
504 | req->type = strdup(types[i]); | 524 | free(req); |
505 | req->json = json; | 525 | sway_log_errno(L_ERROR, "get_clipboard: buf malloc failed"); |
506 | req->pending = pending; | 526 | goto data_error; |
507 | req->event_source = wlc_event_loop_add_fd(pipes[0], | ||
508 | WLC_EVENT_READABLE | WLC_EVENT_ERROR | WLC_EVENT_HANGUP, | ||
509 | &ipc_selection_data_cb, req); | ||
510 | |||
511 | // NOTE: remove this goto to enable retrieving multiple | ||
512 | // targets at once. The whole implementation is already | ||
513 | // made for it. The only reason it was disabled | ||
514 | // at the time of writing is that neither wlc's xselection | ||
515 | // implementation nor (apparently) gtk on wayland supports | ||
516 | // multiple send requests at the same time which makes | ||
517 | // every request except the last one fail (and therefore | ||
518 | // return empty data) | ||
519 | goto cleanup; | ||
520 | } | 527 | } |
528 | |||
529 | (*pending)++; | ||
530 | |||
531 | req->client = client; | ||
532 | req->type = strdup(types[i]); | ||
533 | req->json = json; | ||
534 | req->pending = pending; | ||
535 | req->buf_position = 0; | ||
536 | req->buf_size = 512; | ||
537 | req->timer_event_source = wlc_event_loop_add_timer(ipc_selection_timer_cb, req); | ||
538 | req->fd_event_source = wlc_event_loop_add_fd(pipes[0], | ||
539 | WLC_EVENT_READABLE | WLC_EVENT_ERROR | WLC_EVENT_HANGUP, | ||
540 | &ipc_selection_data_cb, req); | ||
541 | |||
542 | wlc_event_source_timer_update(req->timer_event_source, 1000); | ||
543 | |||
544 | // NOTE: remove this goto to enable retrieving multiple | ||
545 | // targets at once. The whole implementation is already | ||
546 | // made for it. The only reason it was disabled | ||
547 | // at the time of writing is that neither wlc's xselection | ||
548 | // implementation nor (apparently) gtk on wayland supports | ||
549 | // multiple send requests at the same time which makes | ||
550 | // every request except the last one fail (and therefore | ||
551 | // return empty data) | ||
552 | goto cleanup; | ||
521 | } | 553 | } |
522 | 554 | ||
523 | if (!found) { | 555 | if (!found) { |