aboutsummaryrefslogtreecommitdiff
path: root/sway/ipc-server.c
diff options
context:
space:
mode:
Diffstat (limited to 'sway/ipc-server.c')
-rw-r--r--sway/ipc-server.c196
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 {
struct get_clipboard_request {
struct ipc_client *client;
json_object *json;
- struct wlc_event_source *event_source;
+ struct wlc_event_source *fd_event_source;
+ struct wlc_event_source *timer_event_source;
char *type;
unsigned int *pending;
+ char *buf;
+ size_t buf_size;
+ size_t buf_position;
};
struct sockaddr_un *ipc_user_sockaddr(void);
@@ -339,54 +343,69 @@ static bool is_text_target(const char *target) {
|| strcmp(target, "COMPOUND_TEXT") == 0);
}
+static void release_clipboard_request(struct get_clipboard_request *req) {
+ if (--(*req->pending) == 0) {
+ const char *str = json_object_to_json_string(req->json);
+ ipc_send_reply(req->client, str, (uint32_t)strlen(str));
+ json_object_put(req->json);
+ }
+
+ free(req->type);
+ free(req->buf);
+ wlc_event_source_remove(req->fd_event_source);
+ wlc_event_source_remove(req->timer_event_source);
+ free(req);
+}
+
static int ipc_selection_data_cb(int fd, uint32_t mask, void *data) {
assert(data);
struct get_clipboard_request *req = (struct get_clipboard_request *)data;
if (mask & WLC_EVENT_ERROR) {
sway_log(L_ERROR, "Selection data fd error");
- goto cleanup;
+ goto release;
}
if (mask & WLC_EVENT_READABLE) {
- static const int max_size = 8192 * 1000;
- int len = 512;
- int i = 0;
- char *buf = malloc(len);
-
- // read data as long as there is data avilable
- // grow the buffer step_size in every iteration
- for(;;) {
- int amt = read(fd, buf + i, len - i - 1);
- if (amt <= 0)
- break;
-
- i += amt;
- if (i >= len - 1) {
- if (len >= max_size) {
- sway_log(L_ERROR, "selection data too large");
- free(buf);
- goto cleanup;
+ static const unsigned int max_size = 8192 * 1024;
+ int amt = 0;
+
+ do {
+ int size = req->buf_size - req->buf_position;
+ int amt = read(fd, req->buf + req->buf_position, size - 1);
+ if (amt < 0) {
+ if (errno == EAGAIN) {
+ return 0;
+ }
+
+ sway_log_errno(L_INFO, "Failed to read from clipboard data fd");
+ goto release;
+ }
+
+ req->buf_position += amt;
+ if (req->buf_position >= req->buf_size - 1) {
+ if (req->buf_size >= max_size) {
+ sway_log(L_ERROR, "get_clipbard: selection data too large");
+ goto release;
}
- char *next = realloc(buf, (len *= 2));
+ char *next = realloc(req->buf, req->buf_size *= 2);
if (!next) {
- sway_log_errno(L_ERROR, "relloc failed");
- free(buf);
- goto cleanup;
+ sway_log_errno(L_ERROR, "get_clipboard: realloc data buffer failed");
+ goto release;
}
- buf = next;
+ req->buf = next;
}
- }
+ } while(amt != 0);
- buf[i] = '\0';
+ req->buf[req->buf_position] = '\0';
if (is_text_target(req->type)) {
json_object_object_add(req->json, req->type,
- json_object_new_string(buf));
+ json_object_new_string(req->buf));
} else {
size_t outlen;
- char *b64 = b64_encode(buf, i, &outlen);
+ char *b64 = b64_encode(req->buf, req->buf_position, &outlen);
char *type = malloc(strlen(req->type) + 8);
strcat(type, ";base64");
json_object_object_add(req->json, type,
@@ -394,22 +413,19 @@ static int ipc_selection_data_cb(int fd, uint32_t mask, void *data) {
free(type);
free(b64);
}
-
- free(buf);
}
-cleanup:
- close(fd);
+release:
+ release_clipboard_request(req);
+ return 0;
+}
- if (--(*req->pending) == 0) {
- const char *str = json_object_to_json_string(req->json);
- ipc_send_reply(req->client, str, (uint32_t)strlen(str));
- json_object_put(req->json);
- }
+static int ipc_selection_timer_cb(void *data) {
+ assert(data);
+ struct get_clipboard_request *req = (struct get_clipboard_request *)data;
- free(req->type);
- wlc_event_source_remove(req->event_source);
- free(req);
+ sway_log(L_INFO, "get_clipbard: timeout for type %s", req->type);
+ release_clipboard_request(req);
return 0;
}
@@ -471,53 +487,69 @@ void ipc_get_clipboard(struct ipc_client *client, char *buf) {
const char *pattern = requested->items[l];
bool found = false;
for (size_t i = 0; i < size; ++i) {
- if (mime_type_matches(types[i], pattern)) {
- found = true;
+ if (!mime_type_matches(types[i], pattern)) {
+ continue;
+ }
- struct get_clipboard_request *req = malloc(sizeof(*req));
- if (!req) {
- sway_log(L_ERROR, "Cannot allocate get_clipboard_request");
- goto data_error;
- }
+ found = true;
- int pipes[2];
- if (pipe(pipes) == -1) {
- sway_log_errno(L_ERROR, "pipe call failed");
- free(req);
- goto data_error;
- }
+ struct get_clipboard_request *req = malloc(sizeof(*req));
+ if (!req) {
+ sway_log(L_ERROR, "get_clipboard: request malloc failed");
+ goto data_error;
+ }
- fcntl(pipes[0], F_SETFD, FD_CLOEXEC | O_NONBLOCK);
- fcntl(pipes[1], F_SETFD, FD_CLOEXEC | O_NONBLOCK);
+ int pipes[2];
+ if (pipe(pipes) == -1) {
+ sway_log_errno(L_ERROR, "get_clipboard: pipe call failed");
+ free(req);
+ goto data_error;
+ }
- if (!wlc_get_selection_data(types[i], pipes[1])) {
- close(pipes[0]);
- close(pipes[1]);
- free(req);
- sway_log(L_ERROR, "wlc_get_selection_data failed");
- goto data_error;
- }
+ fcntl(pipes[0], F_SETFD, FD_CLOEXEC | O_NONBLOCK);
+ fcntl(pipes[1], F_SETFD, FD_CLOEXEC | O_NONBLOCK);
+
+ if (!wlc_get_selection_data(types[i], pipes[1])) {
+ close(pipes[0]);
+ close(pipes[1]);
+ free(req);
+ sway_log(L_ERROR, "get_clipboard: failed to retrieve "
+ "selection data");
+ goto data_error;
+ }
- (*pending)++;
-
- req->client = client;
- req->type = strdup(types[i]);
- req->json = json;
- req->pending = pending;
- req->event_source = wlc_event_loop_add_fd(pipes[0],
- WLC_EVENT_READABLE | WLC_EVENT_ERROR | WLC_EVENT_HANGUP,
- &ipc_selection_data_cb, req);
-
- // NOTE: remove this goto to enable retrieving multiple
- // targets at once. The whole implementation is already
- // made for it. The only reason it was disabled
- // at the time of writing is that neither wlc's xselection
- // implementation nor (apparently) gtk on wayland supports
- // multiple send requests at the same time which makes
- // every request except the last one fail (and therefore
- // return empty data)
- goto cleanup;
+ if (!(req->buf = malloc(512))) {
+ close(pipes[0]);
+ close(pipes[1]);
+ free(req);
+ sway_log_errno(L_ERROR, "get_clipboard: buf malloc failed");
+ goto data_error;
}
+
+ (*pending)++;
+
+ req->client = client;
+ req->type = strdup(types[i]);
+ req->json = json;
+ req->pending = pending;
+ req->buf_position = 0;
+ req->buf_size = 512;
+ req->timer_event_source = wlc_event_loop_add_timer(ipc_selection_timer_cb, req);
+ req->fd_event_source = wlc_event_loop_add_fd(pipes[0],
+ WLC_EVENT_READABLE | WLC_EVENT_ERROR | WLC_EVENT_HANGUP,
+ &ipc_selection_data_cb, req);
+
+ wlc_event_source_timer_update(req->timer_event_source, 1000);
+
+ // NOTE: remove this goto to enable retrieving multiple
+ // targets at once. The whole implementation is already
+ // made for it. The only reason it was disabled
+ // at the time of writing is that neither wlc's xselection
+ // implementation nor (apparently) gtk on wayland supports
+ // multiple send requests at the same time which makes
+ // every request except the last one fail (and therefore
+ // return empty data)
+ goto cleanup;
}
if (!found) {