diff options
Diffstat (limited to 'sway/ipc-server.c')
-rw-r--r-- | sway/ipc-server.c | 983 |
1 files changed, 227 insertions, 756 deletions
diff --git a/sway/ipc-server.c b/sway/ipc-server.c index b560b930..045802e1 100644 --- a/sway/ipc-server.c +++ b/sway/ipc-server.c | |||
@@ -1,51 +1,41 @@ | |||
1 | // See https://i3wm.org/docs/ipc.html for protocol information | 1 | // See https://i3wm.org/docs/ipc.html for protocol information |
2 | |||
3 | #ifndef __FreeBSD__ | 2 | #ifndef __FreeBSD__ |
4 | // Any value will hide SOCK_CLOEXEC on FreeBSD (__BSD_VISIBLE=0) | 3 | // Any value will hide SOCK_CLOEXEC on FreeBSD (__BSD_VISIBLE=0) |
5 | #define _XOPEN_SOURCE 700 | 4 | #define _XOPEN_SOURCE 700 |
6 | #endif | 5 | #endif |
7 | 6 | #include <assert.h> | |
8 | #include <errno.h> | 7 | #include <errno.h> |
8 | #include <fcntl.h> | ||
9 | #include <json-c/json.h> | ||
10 | #include <stdbool.h> | ||
11 | #include <stdint.h> | ||
12 | #include <stdlib.h> | ||
9 | #include <string.h> | 13 | #include <string.h> |
10 | #include <sys/socket.h> | 14 | #include <sys/socket.h> |
15 | #include <sys/ioctl.h> | ||
11 | #include <sys/un.h> | 16 | #include <sys/un.h> |
12 | #include <stdbool.h> | ||
13 | #include <wlc/wlc-render.h> | ||
14 | #include <unistd.h> | 17 | #include <unistd.h> |
15 | #include <stdlib.h> | 18 | #include <wayland-server.h> |
16 | #include <sys/ioctl.h> | 19 | #include "sway/commands.h" |
17 | #include <fcntl.h> | ||
18 | #include <json-c/json.h> | ||
19 | #include <list.h> | ||
20 | #include <libinput.h> | ||
21 | #ifdef __linux__ | ||
22 | struct ucred { | ||
23 | pid_t pid; | ||
24 | uid_t uid; | ||
25 | gid_t gid; | ||
26 | }; | ||
27 | #endif | ||
28 | #include "sway/ipc-json.h" | 20 | #include "sway/ipc-json.h" |
29 | #include "sway/ipc-server.h" | 21 | #include "sway/ipc-server.h" |
30 | #include "sway/security.h" | 22 | #include "sway/server.h" |
31 | #include "sway/config.h" | 23 | #include "sway/input/input-manager.h" |
32 | #include "sway/commands.h" | 24 | #include "sway/input/seat.h" |
33 | #include "sway/input.h" | ||
34 | #include "stringop.h" | ||
35 | #include "log.h" | ||
36 | #include "list.h" | 25 | #include "list.h" |
37 | #include "util.h" | 26 | #include "log.h" |
38 | 27 | ||
39 | static int ipc_socket = -1; | 28 | static int ipc_socket = -1; |
40 | static struct wlc_event_source *ipc_event_source = NULL; | 29 | static struct wl_event_source *ipc_event_source = NULL; |
41 | static struct sockaddr_un *ipc_sockaddr = NULL; | 30 | static struct sockaddr_un *ipc_sockaddr = NULL; |
42 | static list_t *ipc_client_list = NULL; | 31 | static list_t *ipc_client_list = NULL; |
43 | 32 | ||
44 | static const char ipc_magic[] = {'i', '3', '-', 'i', 'p', 'c'}; | 33 | static const char ipc_magic[] = {'i', '3', '-', 'i', 'p', 'c'}; |
45 | 34 | ||
46 | struct ipc_client { | 35 | struct ipc_client { |
47 | struct wlc_event_source *event_source; | 36 | struct wl_event_source *event_source; |
48 | struct wlc_event_source *writable_event_source; | 37 | struct wl_event_source *writable_event_source; |
38 | struct sway_server *server; | ||
49 | int fd; | 39 | int fd; |
50 | uint32_t payload_length; | 40 | uint32_t payload_length; |
51 | uint32_t security_policy; | 41 | uint32_t security_policy; |
@@ -56,27 +46,6 @@ struct ipc_client { | |||
56 | char *write_buffer; | 46 | char *write_buffer; |
57 | }; | 47 | }; |
58 | 48 | ||
59 | static list_t *ipc_get_pixel_requests = NULL; | ||
60 | |||
61 | struct get_pixels_request { | ||
62 | struct ipc_client *client; | ||
63 | wlc_handle output; | ||
64 | struct wlc_geometry geo; | ||
65 | }; | ||
66 | |||
67 | struct 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 | |||
80 | struct sockaddr_un *ipc_user_sockaddr(void); | 49 | struct sockaddr_un *ipc_user_sockaddr(void); |
81 | int ipc_handle_connection(int fd, uint32_t mask, void *data); | 50 | int ipc_handle_connection(int fd, uint32_t mask, void *data); |
82 | int ipc_client_handle_readable(int client_fd, uint32_t mask, void *data); | 51 | int ipc_client_handle_readable(int client_fd, uint32_t mask, void *data); |
@@ -84,11 +53,8 @@ int ipc_client_handle_writable(int client_fd, uint32_t mask, void *data); | |||
84 | void ipc_client_disconnect(struct ipc_client *client); | 53 | void ipc_client_disconnect(struct ipc_client *client); |
85 | void ipc_client_handle_command(struct ipc_client *client); | 54 | void ipc_client_handle_command(struct ipc_client *client); |
86 | bool ipc_send_reply(struct ipc_client *client, const char *payload, uint32_t payload_length); | 55 | bool ipc_send_reply(struct ipc_client *client, const char *payload, uint32_t payload_length); |
87 | void ipc_get_workspaces_callback(swayc_t *workspace, void *data); | ||
88 | void ipc_get_outputs_callback(swayc_t *container, void *data); | ||
89 | static void ipc_get_marks_callback(swayc_t *container, void *data); | ||
90 | 56 | ||
91 | void ipc_init(void) { | 57 | void ipc_init(struct sway_server *server) { |
92 | ipc_socket = socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0); | 58 | ipc_socket = socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0); |
93 | if (ipc_socket == -1) { | 59 | if (ipc_socket == -1) { |
94 | sway_abort("Unable to create IPC socket"); | 60 | sway_abort("Unable to create IPC socket"); |
@@ -116,14 +82,14 @@ void ipc_init(void) { | |||
116 | setenv("SWAYSOCK", ipc_sockaddr->sun_path, 1); | 82 | setenv("SWAYSOCK", ipc_sockaddr->sun_path, 1); |
117 | 83 | ||
118 | ipc_client_list = create_list(); | 84 | ipc_client_list = create_list(); |
119 | ipc_get_pixel_requests = create_list(); | ||
120 | 85 | ||
121 | ipc_event_source = wlc_event_loop_add_fd(ipc_socket, WLC_EVENT_READABLE, ipc_handle_connection, NULL); | 86 | ipc_event_source = wl_event_loop_add_fd(server->wl_event_loop, ipc_socket, |
87 | WL_EVENT_READABLE, ipc_handle_connection, server); | ||
122 | } | 88 | } |
123 | 89 | ||
124 | void ipc_terminate(void) { | 90 | void ipc_terminate(void) { |
125 | if (ipc_event_source) { | 91 | if (ipc_event_source) { |
126 | wlc_event_source_remove(ipc_event_source); | 92 | wl_event_source_remove(ipc_event_source); |
127 | } | 93 | } |
128 | close(ipc_socket); | 94 | close(ipc_socket); |
129 | unlink(ipc_sockaddr->sun_path); | 95 | unlink(ipc_sockaddr->sun_path); |
@@ -157,100 +123,82 @@ struct sockaddr_un *ipc_user_sockaddr(void) { | |||
157 | return ipc_sockaddr; | 123 | return ipc_sockaddr; |
158 | } | 124 | } |
159 | 125 | ||
160 | static pid_t get_client_pid(int client_fd) { | ||
161 | // FreeBSD supports getting uid/gid, but not pid | ||
162 | #ifdef __linux__ | ||
163 | struct ucred ucred; | ||
164 | socklen_t len = sizeof(struct ucred); | ||
165 | |||
166 | if (getsockopt(client_fd, SOL_SOCKET, SO_PEERCRED, &ucred, &len) == -1) { | ||
167 | return -1; | ||
168 | } | ||
169 | |||
170 | return ucred.pid; | ||
171 | #else | ||
172 | return -1; | ||
173 | #endif | ||
174 | } | ||
175 | |||
176 | int ipc_handle_connection(int fd, uint32_t mask, void *data) { | 126 | int ipc_handle_connection(int fd, uint32_t mask, void *data) { |
177 | (void) fd; (void) data; | 127 | (void) fd; |
178 | sway_log(L_DEBUG, "Event on IPC listening socket"); | 128 | struct sway_server *server = data; |
179 | assert(mask == WLC_EVENT_READABLE); | 129 | wlr_log(L_DEBUG, "Event on IPC listening socket"); |
130 | assert(mask == WL_EVENT_READABLE); | ||
180 | 131 | ||
181 | int client_fd = accept(ipc_socket, NULL, NULL); | 132 | int client_fd = accept(ipc_socket, NULL, NULL); |
182 | if (client_fd == -1) { | 133 | if (client_fd == -1) { |
183 | sway_log_errno(L_ERROR, "Unable to accept IPC client connection"); | 134 | wlr_log_errno(L_ERROR, "Unable to accept IPC client connection"); |
184 | return 0; | 135 | return 0; |
185 | } | 136 | } |
186 | 137 | ||
187 | int flags; | 138 | int flags; |
188 | if ((flags = fcntl(client_fd, F_GETFD)) == -1 | 139 | if ((flags = fcntl(client_fd, F_GETFD)) == -1 |
189 | || fcntl(client_fd, F_SETFD, flags|FD_CLOEXEC) == -1) { | 140 | || fcntl(client_fd, F_SETFD, flags|FD_CLOEXEC) == -1) { |
190 | sway_log_errno(L_ERROR, "Unable to set CLOEXEC on IPC client socket"); | 141 | wlr_log_errno(L_ERROR, "Unable to set CLOEXEC on IPC client socket"); |
191 | close(client_fd); | 142 | close(client_fd); |
192 | return 0; | 143 | return 0; |
193 | } | 144 | } |
194 | if ((flags = fcntl(client_fd, F_GETFL)) == -1 | 145 | if ((flags = fcntl(client_fd, F_GETFL)) == -1 |
195 | || fcntl(client_fd, F_SETFL, flags|O_NONBLOCK) == -1) { | 146 | || fcntl(client_fd, F_SETFL, flags|O_NONBLOCK) == -1) { |
196 | sway_log_errno(L_ERROR, "Unable to set NONBLOCK on IPC client socket"); | 147 | wlr_log_errno(L_ERROR, "Unable to set NONBLOCK on IPC client socket"); |
197 | close(client_fd); | 148 | close(client_fd); |
198 | return 0; | 149 | return 0; |
199 | } | 150 | } |
200 | 151 | ||
201 | struct ipc_client* client = malloc(sizeof(struct ipc_client)); | 152 | struct ipc_client *client = malloc(sizeof(struct ipc_client)); |
202 | if (!client) { | 153 | if (!client) { |
203 | sway_log(L_ERROR, "Unable to allocate ipc client"); | 154 | wlr_log(L_ERROR, "Unable to allocate ipc client"); |
204 | close(client_fd); | 155 | close(client_fd); |
205 | return 0; | 156 | return 0; |
206 | } | 157 | } |
158 | client->server = server; | ||
207 | client->payload_length = 0; | 159 | client->payload_length = 0; |
208 | client->fd = client_fd; | 160 | client->fd = client_fd; |
209 | client->subscribed_events = 0; | 161 | client->subscribed_events = 0; |
210 | client->event_source = wlc_event_loop_add_fd(client_fd, WLC_EVENT_READABLE, ipc_client_handle_readable, client); | 162 | client->event_source = wl_event_loop_add_fd(server->wl_event_loop, |
163 | client_fd, WL_EVENT_READABLE, ipc_client_handle_readable, client); | ||
211 | client->writable_event_source = NULL; | 164 | client->writable_event_source = NULL; |
212 | 165 | ||
213 | client->write_buffer_size = 128; | 166 | client->write_buffer_size = 128; |
214 | client->write_buffer_len = 0; | 167 | client->write_buffer_len = 0; |
215 | client->write_buffer = malloc(client->write_buffer_size); | 168 | client->write_buffer = malloc(client->write_buffer_size); |
216 | if (!client->write_buffer) { | 169 | if (!client->write_buffer) { |
217 | sway_log(L_ERROR, "Unable to allocate ipc client write buffer"); | 170 | wlr_log(L_ERROR, "Unable to allocate ipc client write buffer"); |
218 | close(client_fd); | 171 | close(client_fd); |
219 | return 0; | 172 | return 0; |
220 | } | 173 | } |
221 | 174 | ||
222 | pid_t pid = get_client_pid(client->fd); | 175 | wlr_log(L_DEBUG, "New client: fd %d", client_fd); |
223 | client->security_policy = get_ipc_policy_mask(pid); | ||
224 | |||
225 | sway_log(L_DEBUG, "New client: fd %d, pid %d", client_fd, pid); | ||
226 | |||
227 | list_add(ipc_client_list, client); | 176 | list_add(ipc_client_list, client); |
228 | |||
229 | return 0; | 177 | return 0; |
230 | } | 178 | } |
231 | 179 | ||
232 | static const int ipc_header_size = sizeof(ipc_magic)+8; | 180 | static const int ipc_header_size = sizeof(ipc_magic) + 8; |
233 | 181 | ||
234 | int ipc_client_handle_readable(int client_fd, uint32_t mask, void *data) { | 182 | int ipc_client_handle_readable(int client_fd, uint32_t mask, void *data) { |
235 | struct ipc_client *client = data; | 183 | struct ipc_client *client = data; |
236 | 184 | ||
237 | if (mask & WLC_EVENT_ERROR) { | 185 | if (mask & WL_EVENT_ERROR) { |
238 | sway_log(L_ERROR, "IPC Client socket error, removing client"); | 186 | wlr_log(L_ERROR, "IPC Client socket error, removing client"); |
239 | ipc_client_disconnect(client); | 187 | ipc_client_disconnect(client); |
240 | return 0; | 188 | return 0; |
241 | } | 189 | } |
242 | 190 | ||
243 | if (mask & WLC_EVENT_HANGUP) { | 191 | if (mask & WL_EVENT_HANGUP) { |
244 | sway_log(L_DEBUG, "Client %d hung up", client->fd); | 192 | wlr_log(L_DEBUG, "Client %d hung up", client->fd); |
245 | ipc_client_disconnect(client); | 193 | ipc_client_disconnect(client); |
246 | return 0; | 194 | return 0; |
247 | } | 195 | } |
248 | 196 | ||
249 | sway_log(L_DEBUG, "Client %d readable", client->fd); | 197 | wlr_log(L_DEBUG, "Client %d readable", client->fd); |
250 | 198 | ||
251 | int read_available; | 199 | int read_available; |
252 | if (ioctl(client_fd, FIONREAD, &read_available) == -1) { | 200 | if (ioctl(client_fd, FIONREAD, &read_available) == -1) { |
253 | sway_log_errno(L_INFO, "Unable to read IPC socket buffer size"); | 201 | wlr_log_errno(L_INFO, "Unable to read IPC socket buffer size"); |
254 | ipc_client_disconnect(client); | 202 | ipc_client_disconnect(client); |
255 | return 0; | 203 | return 0; |
256 | } | 204 | } |
@@ -272,13 +220,13 @@ int ipc_client_handle_readable(int client_fd, uint32_t mask, void *data) { | |||
272 | // Should be fully available, because read_available >= ipc_header_size | 220 | // Should be fully available, because read_available >= ipc_header_size |
273 | ssize_t received = recv(client_fd, buf, ipc_header_size, 0); | 221 | ssize_t received = recv(client_fd, buf, ipc_header_size, 0); |
274 | if (received == -1) { | 222 | if (received == -1) { |
275 | sway_log_errno(L_INFO, "Unable to receive header from IPC client"); | 223 | wlr_log_errno(L_INFO, "Unable to receive header from IPC client"); |
276 | ipc_client_disconnect(client); | 224 | ipc_client_disconnect(client); |
277 | return 0; | 225 | return 0; |
278 | } | 226 | } |
279 | 227 | ||
280 | if (memcmp(buf, ipc_magic, sizeof(ipc_magic)) != 0) { | 228 | if (memcmp(buf, ipc_magic, sizeof(ipc_magic)) != 0) { |
281 | sway_log(L_DEBUG, "IPC header check failed"); | 229 | wlr_log(L_DEBUG, "IPC header check failed"); |
282 | ipc_client_disconnect(client); | 230 | ipc_client_disconnect(client); |
283 | return 0; | 231 | return 0; |
284 | } | 232 | } |
@@ -293,17 +241,110 @@ int ipc_client_handle_readable(int client_fd, uint32_t mask, void *data) { | |||
293 | return 0; | 241 | return 0; |
294 | } | 242 | } |
295 | 243 | ||
244 | static bool ipc_has_event_listeners(enum ipc_command_type event) { | ||
245 | for (int i = 0; i < ipc_client_list->length; i++) { | ||
246 | struct ipc_client *client = ipc_client_list->items[i]; | ||
247 | if ((client->subscribed_events & event_mask(event)) == 0) { | ||
248 | return true; | ||
249 | } | ||
250 | } | ||
251 | return false; | ||
252 | } | ||
253 | |||
254 | static void ipc_send_event(const char *json_string, enum ipc_command_type event) { | ||
255 | struct ipc_client *client; | ||
256 | for (int i = 0; i < ipc_client_list->length; i++) { | ||
257 | client = ipc_client_list->items[i]; | ||
258 | if ((client->subscribed_events & event_mask(event)) == 0) { | ||
259 | continue; | ||
260 | } | ||
261 | client->current_command = event; | ||
262 | if (!ipc_send_reply(client, json_string, (uint32_t) strlen(json_string))) { | ||
263 | wlr_log_errno(L_INFO, "Unable to send reply to IPC client"); | ||
264 | ipc_client_disconnect(client); | ||
265 | } | ||
266 | } | ||
267 | } | ||
268 | |||
269 | void ipc_event_workspace(struct sway_container *old, | ||
270 | struct sway_container *new, const char *change) { | ||
271 | if (!ipc_has_event_listeners(IPC_EVENT_WORKSPACE)) { | ||
272 | return; | ||
273 | } | ||
274 | wlr_log(L_DEBUG, "Sending workspace::%s event", change); | ||
275 | json_object *obj = json_object_new_object(); | ||
276 | json_object_object_add(obj, "change", json_object_new_string(change)); | ||
277 | if (strcmp("focus", change) == 0) { | ||
278 | if (old) { | ||
279 | json_object_object_add(obj, "old", | ||
280 | ipc_json_describe_container_recursive(old)); | ||
281 | } else { | ||
282 | json_object_object_add(obj, "old", NULL); | ||
283 | } | ||
284 | } | ||
285 | |||
286 | if (new) { | ||
287 | json_object_object_add(obj, "current", | ||
288 | ipc_json_describe_container_recursive(new)); | ||
289 | } else { | ||
290 | json_object_object_add(obj, "current", NULL); | ||
291 | } | ||
292 | |||
293 | const char *json_string = json_object_to_json_string(obj); | ||
294 | ipc_send_event(json_string, IPC_EVENT_WORKSPACE); | ||
295 | json_object_put(obj); | ||
296 | } | ||
297 | |||
298 | void ipc_event_window(struct sway_container *window, const char *change) { | ||
299 | if (!ipc_has_event_listeners(IPC_EVENT_WINDOW)) { | ||
300 | return; | ||
301 | } | ||
302 | wlr_log(L_DEBUG, "Sending window::%s event", change); | ||
303 | json_object *obj = json_object_new_object(); | ||
304 | json_object_object_add(obj, "change", json_object_new_string(change)); | ||
305 | json_object_object_add(obj, "container", ipc_json_describe_container_recursive(window)); | ||
306 | |||
307 | const char *json_string = json_object_to_json_string(obj); | ||
308 | ipc_send_event(json_string, IPC_EVENT_WINDOW); | ||
309 | json_object_put(obj); | ||
310 | } | ||
311 | |||
312 | void ipc_event_barconfig_update(struct bar_config *bar) { | ||
313 | if (!ipc_has_event_listeners(IPC_EVENT_BARCONFIG_UPDATE)) { | ||
314 | return; | ||
315 | } | ||
316 | wlr_log(L_DEBUG, "Sending barconfig_update event"); | ||
317 | json_object *json = ipc_json_describe_bar_config(bar); | ||
318 | |||
319 | const char *json_string = json_object_to_json_string(json); | ||
320 | ipc_send_event(json_string, IPC_EVENT_BARCONFIG_UPDATE); | ||
321 | json_object_put(json); | ||
322 | } | ||
323 | |||
324 | void ipc_event_mode(const char *mode) { | ||
325 | if (!ipc_has_event_listeners(IPC_EVENT_MODE)) { | ||
326 | return; | ||
327 | } | ||
328 | wlr_log(L_DEBUG, "Sending mode::%s event", mode); | ||
329 | json_object *obj = json_object_new_object(); | ||
330 | json_object_object_add(obj, "change", json_object_new_string(mode)); | ||
331 | |||
332 | const char *json_string = json_object_to_json_string(obj); | ||
333 | ipc_send_event(json_string, IPC_EVENT_MODE); | ||
334 | json_object_put(obj); | ||
335 | } | ||
336 | |||
296 | int ipc_client_handle_writable(int client_fd, uint32_t mask, void *data) { | 337 | int ipc_client_handle_writable(int client_fd, uint32_t mask, void *data) { |
297 | struct ipc_client *client = data; | 338 | struct ipc_client *client = data; |
298 | 339 | ||
299 | if (mask & WLC_EVENT_ERROR) { | 340 | if (mask & WL_EVENT_ERROR) { |
300 | sway_log(L_ERROR, "IPC Client socket error, removing client"); | 341 | wlr_log(L_ERROR, "IPC Client socket error, removing client"); |
301 | ipc_client_disconnect(client); | 342 | ipc_client_disconnect(client); |
302 | return 0; | 343 | return 0; |
303 | } | 344 | } |
304 | 345 | ||
305 | if (mask & WLC_EVENT_HANGUP) { | 346 | if (mask & WL_EVENT_HANGUP) { |
306 | sway_log(L_DEBUG, "Client %d hung up", client->fd); | 347 | wlr_log(L_DEBUG, "Client %d hung up", client->fd); |
307 | ipc_client_disconnect(client); | 348 | ipc_client_disconnect(client); |
308 | return 0; | 349 | return 0; |
309 | } | 350 | } |
@@ -312,14 +353,14 @@ int ipc_client_handle_writable(int client_fd, uint32_t mask, void *data) { | |||
312 | return 0; | 353 | return 0; |
313 | } | 354 | } |
314 | 355 | ||
315 | sway_log(L_DEBUG, "Client %d writable", client->fd); | 356 | wlr_log(L_DEBUG, "Client %d writable", client->fd); |
316 | 357 | ||
317 | ssize_t written = write(client->fd, client->write_buffer, client->write_buffer_len); | 358 | ssize_t written = write(client->fd, client->write_buffer, client->write_buffer_len); |
318 | 359 | ||
319 | if (written == -1 && errno == EAGAIN) { | 360 | if (written == -1 && errno == EAGAIN) { |
320 | return 0; | 361 | return 0; |
321 | } else if (written == -1) { | 362 | } else if (written == -1) { |
322 | sway_log_errno(L_INFO, "Unable to send data from queue to IPC client"); | 363 | wlr_log_errno(L_INFO, "Unable to send data from queue to IPC client"); |
323 | ipc_client_disconnect(client); | 364 | ipc_client_disconnect(client); |
324 | return 0; | 365 | return 0; |
325 | } | 366 | } |
@@ -328,7 +369,7 @@ int ipc_client_handle_writable(int client_fd, uint32_t mask, void *data) { | |||
328 | client->write_buffer_len -= written; | 369 | client->write_buffer_len -= written; |
329 | 370 | ||
330 | if (client->write_buffer_len == 0 && client->writable_event_source) { | 371 | if (client->write_buffer_len == 0 && client->writable_event_source) { |
331 | wlc_event_source_remove(client->writable_event_source); | 372 | wl_event_source_remove(client->writable_event_source); |
332 | client->writable_event_source = NULL; | 373 | client->writable_event_source = NULL; |
333 | } | 374 | } |
334 | 375 | ||
@@ -344,332 +385,48 @@ void ipc_client_disconnect(struct ipc_client *client) { | |||
344 | shutdown(client->fd, SHUT_RDWR); | 385 | shutdown(client->fd, SHUT_RDWR); |
345 | } | 386 | } |
346 | 387 | ||
347 | sway_log(L_INFO, "IPC Client %d disconnected", client->fd); | 388 | wlr_log(L_INFO, "IPC Client %d disconnected", client->fd); |
348 | wlc_event_source_remove(client->event_source); | 389 | wl_event_source_remove(client->event_source); |
349 | if (client->writable_event_source) { | 390 | if (client->writable_event_source) { |
350 | wlc_event_source_remove(client->writable_event_source); | 391 | wl_event_source_remove(client->writable_event_source); |
351 | } | 392 | } |
352 | int i = 0; | 393 | int i = 0; |
353 | while (i < ipc_client_list->length && ipc_client_list->items[i] != client) i++; | 394 | while (i < ipc_client_list->length && ipc_client_list->items[i] != client) { |
395 | i++; | ||
396 | } | ||
354 | list_del(ipc_client_list, i); | 397 | list_del(ipc_client_list, i); |
355 | free(client->write_buffer); | 398 | free(client->write_buffer); |
356 | close(client->fd); | 399 | close(client->fd); |
357 | free(client); | 400 | free(client); |
358 | } | 401 | } |
359 | 402 | ||
360 | bool output_by_name_test(swayc_t *view, void *data) { | 403 | static void ipc_get_workspaces_callback(struct sway_container *workspace, |
361 | char *name = (char *)data; | 404 | void *data) { |
362 | if (view->type != C_OUTPUT) { | 405 | if (workspace->type != C_WORKSPACE) { |
363 | return false; | ||
364 | } | ||
365 | return !strcmp(name, view->name); | ||
366 | } | ||
367 | |||
368 | void ipc_get_pixels(wlc_handle output) { | ||
369 | if (ipc_get_pixel_requests->length == 0) { | ||
370 | return; | 406 | return; |
371 | } | 407 | } |
372 | 408 | json_object *workspace_json = ipc_json_describe_container(workspace); | |
373 | list_t *unhandled = create_list(); | 409 | // override the default focused indicator because |
374 | 410 | // it's set differently for the get_workspaces reply | |
375 | struct get_pixels_request *req; | 411 | struct sway_seat *seat = |
376 | int i; | 412 | input_manager_get_default_seat(input_manager); |
377 | for (i = 0; i < ipc_get_pixel_requests->length; ++i) { | 413 | struct sway_container *focused_ws = seat_get_focus(seat); |
378 | req = ipc_get_pixel_requests->items[i]; | 414 | if (focused_ws != NULL && focused_ws->type != C_WORKSPACE) { |
379 | if (req->output != output) { | 415 | focused_ws = container_parent(focused_ws, C_WORKSPACE); |
380 | list_add(unhandled, req); | 416 | } |
381 | continue; | 417 | bool focused = workspace == focused_ws; |
382 | } | 418 | json_object_object_del(workspace_json, "focused"); |
383 | 419 | json_object_object_add(workspace_json, "focused", | |
384 | const struct wlc_size *size = &req->geo.size; | 420 | json_object_new_boolean(focused)); |
385 | struct wlc_geometry g_out; | 421 | json_object_array_add((json_object *)data, workspace_json); |
386 | char response_header[9]; | 422 | |
387 | memset(response_header, 0, sizeof(response_header)); | 423 | focused_ws = seat_get_focus_inactive(seat, workspace->parent); |
388 | char *data = malloc(sizeof(response_header) + size->w * size->h * 4); | 424 | if (focused_ws->type != C_WORKSPACE) { |
389 | if (!data) { | 425 | focused_ws = container_parent(focused_ws, C_WORKSPACE); |
390 | sway_log(L_ERROR, "Unable to allocate pixels for get_pixels"); | 426 | } |
391 | ipc_client_disconnect(req->client); | 427 | bool visible = workspace == focused_ws; |
392 | free(req); | 428 | json_object_object_add(workspace_json, "visible", |
393 | continue; | 429 | json_object_new_boolean(visible)); |
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 | |||
415 | static 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 | |||
423 | static 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 | |||
438 | static 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 | |||
501 | error:; | ||
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 | |||
508 | release: | ||
509 | release_clipboard_request(req); | ||
510 | return 0; | ||
511 | } | ||
512 | |||
513 | static 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 | ||
528 | bool mime_type_matches(const char *mime_type, const char *pattern) { | ||
529 | const char *wildcard = NULL; | ||
530 | while (*mime_type && *pattern) { | ||
531 | if (*pattern == '*' && !wildcard) { | ||
532 | wildcard = pattern; | ||
533 | ++pattern; | ||
534 | } | ||
535 | |||
536 | if (*mime_type != *pattern) { | ||
537 | if (!wildcard) | ||
538 | return false; | ||
539 | |||
540 | pattern = wildcard; | ||
541 | ++mime_type; | ||
542 | continue; | ||
543 | } | ||
544 | |||
545 | ++mime_type; | ||
546 | ++pattern; | ||
547 | } | ||
548 | |||
549 | while (*pattern == '*') { | ||
550 | ++pattern; | ||
551 | } | ||
552 | |||
553 | return (*mime_type == *pattern); | ||
554 | } | ||
555 | |||
556 | void 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 | |||
663 | data_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 | |||
670 | cleanup: | ||
671 | list_free(requested); | ||
672 | free(types); | ||
673 | } | 430 | } |
674 | 431 | ||
675 | void ipc_client_handle_command(struct ipc_client *client) { | 432 | void ipc_client_handle_command(struct ipc_client *client) { |
@@ -679,7 +436,7 @@ void ipc_client_handle_command(struct ipc_client *client) { | |||
679 | 436 | ||
680 | char *buf = malloc(client->payload_length + 1); | 437 | char *buf = malloc(client->payload_length + 1); |
681 | if (!buf) { | 438 | if (!buf) { |
682 | sway_log_errno(L_INFO, "Unable to allocate IPC payload"); | 439 | wlr_log_errno(L_INFO, "Unable to allocate IPC payload"); |
683 | ipc_client_disconnect(client); | 440 | ipc_client_disconnect(client); |
684 | return; | 441 | return; |
685 | } | 442 | } |
@@ -688,7 +445,7 @@ void ipc_client_handle_command(struct ipc_client *client) { | |||
688 | ssize_t received = recv(client->fd, buf, client->payload_length, 0); | 445 | ssize_t received = recv(client->fd, buf, client->payload_length, 0); |
689 | if (received == -1) | 446 | if (received == -1) |
690 | { | 447 | { |
691 | sway_log_errno(L_INFO, "Unable to receive payload from IPC client"); | 448 | wlr_log_errno(L_INFO, "Unable to receive payload from IPC client"); |
692 | ipc_client_disconnect(client); | 449 | ipc_client_disconnect(client); |
693 | free(buf); | 450 | free(buf); |
694 | return; | 451 | return; |
@@ -701,10 +458,8 @@ void ipc_client_handle_command(struct ipc_client *client) { | |||
701 | switch (client->current_command) { | 458 | switch (client->current_command) { |
702 | case IPC_COMMAND: | 459 | case IPC_COMMAND: |
703 | { | 460 | { |
704 | if (!(client->security_policy & IPC_FEATURE_COMMAND)) { | 461 | config_clear_handler_context(config); |
705 | goto exit_denied; | 462 | struct cmd_results *results = execute_command(buf, NULL); |
706 | } | ||
707 | struct cmd_results *results = handle_command(buf, CONTEXT_IPC); | ||
708 | const char *json = cmd_results_to_json(results); | 463 | const char *json = cmd_results_to_json(results); |
709 | char reply[256]; | 464 | char reply[256]; |
710 | int length = snprintf(reply, sizeof(reply), "%s", json); | 465 | int length = snprintf(reply, sizeof(reply), "%s", json); |
@@ -713,18 +468,45 @@ void ipc_client_handle_command(struct ipc_client *client) { | |||
713 | goto exit_cleanup; | 468 | goto exit_cleanup; |
714 | } | 469 | } |
715 | 470 | ||
471 | case IPC_GET_OUTPUTS: | ||
472 | { | ||
473 | json_object *outputs = json_object_new_array(); | ||
474 | for (int i = 0; i < root_container.children->length; ++i) { | ||
475 | struct sway_container *container = root_container.children->items[i]; | ||
476 | if (container->type == C_OUTPUT) { | ||
477 | json_object_array_add(outputs, | ||
478 | ipc_json_describe_container(container)); | ||
479 | } | ||
480 | } | ||
481 | const char *json_string = json_object_to_json_string(outputs); | ||
482 | ipc_send_reply(client, json_string, (uint32_t) strlen(json_string)); | ||
483 | json_object_put(outputs); // free | ||
484 | goto exit_cleanup; | ||
485 | } | ||
486 | |||
487 | case IPC_GET_WORKSPACES: | ||
488 | { | ||
489 | json_object *workspaces = json_object_new_array(); | ||
490 | container_for_each_descendant_dfs(&root_container, | ||
491 | ipc_get_workspaces_callback, workspaces); | ||
492 | const char *json_string = json_object_to_json_string(workspaces); | ||
493 | ipc_send_reply(client, json_string, (uint32_t) strlen(json_string)); | ||
494 | json_object_put(workspaces); // free | ||
495 | goto exit_cleanup; | ||
496 | } | ||
497 | |||
716 | case IPC_SUBSCRIBE: | 498 | case IPC_SUBSCRIBE: |
717 | { | 499 | { |
718 | // TODO: Check if they're permitted to use these events | 500 | // TODO: Check if they're permitted to use these events |
719 | struct json_object *request = json_tokener_parse(buf); | 501 | struct json_object *request = json_tokener_parse(buf); |
720 | if (request == NULL) { | 502 | if (request == NULL) { |
721 | ipc_send_reply(client, "{\"success\": false}", 18); | 503 | ipc_send_reply(client, "{\"success\": false}", 18); |
722 | sway_log_errno(L_INFO, "Failed to read request"); | 504 | wlr_log_errno(L_INFO, "Failed to read request"); |
723 | goto exit_cleanup; | 505 | goto exit_cleanup; |
724 | } | 506 | } |
725 | 507 | ||
726 | // parse requested event types | 508 | // parse requested event types |
727 | for (int i = 0; i < json_object_array_length(request); i++) { | 509 | for (size_t i = 0; i < json_object_array_length(request); i++) { |
728 | const char *event_type = json_object_get_string(json_object_array_get_idx(request, i)); | 510 | const char *event_type = json_object_get_string(json_object_array_get_idx(request, i)); |
729 | if (strcmp(event_type, "workspace") == 0) { | 511 | if (strcmp(event_type, "workspace") == 0) { |
730 | client->subscribed_events |= event_mask(IPC_EVENT_WORKSPACE); | 512 | client->subscribed_events |= event_mask(IPC_EVENT_WORKSPACE); |
@@ -741,86 +523,39 @@ void ipc_client_handle_command(struct ipc_client *client) { | |||
741 | } else { | 523 | } else { |
742 | ipc_send_reply(client, "{\"success\": false}", 18); | 524 | ipc_send_reply(client, "{\"success\": false}", 18); |
743 | json_object_put(request); | 525 | json_object_put(request); |
744 | sway_log_errno(L_INFO, "Failed to parse request"); | 526 | wlr_log_errno(L_INFO, "Failed to parse request"); |
745 | goto exit_cleanup; | 527 | goto exit_cleanup; |
746 | } | 528 | } |
747 | } | 529 | } |
748 | 530 | ||
749 | json_object_put(request); | 531 | json_object_put(request); |
750 | |||
751 | ipc_send_reply(client, "{\"success\": true}", 17); | 532 | ipc_send_reply(client, "{\"success\": true}", 17); |
752 | goto exit_cleanup; | 533 | goto exit_cleanup; |
753 | } | 534 | } |
754 | 535 | ||
755 | case IPC_GET_WORKSPACES: | ||
756 | { | ||
757 | if (!(client->security_policy & IPC_FEATURE_GET_WORKSPACES)) { | ||
758 | goto exit_denied; | ||
759 | } | ||
760 | json_object *workspaces = json_object_new_array(); | ||
761 | container_map(&root_container, ipc_get_workspaces_callback, workspaces); | ||
762 | const char *json_string = json_object_to_json_string(workspaces); | ||
763 | ipc_send_reply(client, json_string, (uint32_t) strlen(json_string)); | ||
764 | json_object_put(workspaces); // free | ||
765 | goto exit_cleanup; | ||
766 | } | ||
767 | |||
768 | case IPC_GET_INPUTS: | 536 | case IPC_GET_INPUTS: |
769 | { | 537 | { |
770 | if (!(client->security_policy & IPC_FEATURE_GET_INPUTS)) { | ||
771 | goto exit_denied; | ||
772 | } | ||
773 | json_object *inputs = json_object_new_array(); | 538 | json_object *inputs = json_object_new_array(); |
774 | if (input_devices) { | 539 | struct sway_input_device *device = NULL; |
775 | for(int i = 0; i<input_devices->length; i++) { | 540 | wl_list_for_each(device, &input_manager->devices, link) { |
776 | struct libinput_device *device = input_devices->items[i]; | 541 | json_object_array_add(inputs, ipc_json_describe_input(device)); |
777 | json_object_array_add(inputs, ipc_json_describe_input(device)); | ||
778 | } | ||
779 | } | 542 | } |
780 | const char *json_string = json_object_to_json_string(inputs); | 543 | const char *json_string = json_object_to_json_string(inputs); |
781 | ipc_send_reply(client, json_string, (uint32_t) strlen(json_string)); | 544 | ipc_send_reply(client, json_string, (uint32_t)strlen(json_string)); |
782 | json_object_put(inputs); | 545 | json_object_put(inputs); // free |
783 | goto exit_cleanup; | ||
784 | } | ||
785 | |||
786 | case IPC_GET_OUTPUTS: | ||
787 | { | ||
788 | if (!(client->security_policy & IPC_FEATURE_GET_OUTPUTS)) { | ||
789 | goto exit_denied; | ||
790 | } | ||
791 | json_object *outputs = json_object_new_array(); | ||
792 | container_map(&root_container, ipc_get_outputs_callback, outputs); | ||
793 | const char *json_string = json_object_to_json_string(outputs); | ||
794 | ipc_send_reply(client, json_string, (uint32_t) strlen(json_string)); | ||
795 | json_object_put(outputs); // free | ||
796 | goto exit_cleanup; | 546 | goto exit_cleanup; |
797 | } | 547 | } |
798 | 548 | ||
799 | case IPC_GET_TREE: | 549 | case IPC_GET_TREE: |
800 | { | 550 | { |
801 | if (!(client->security_policy & IPC_FEATURE_GET_TREE)) { | 551 | json_object *tree = |
802 | goto exit_denied; | 552 | ipc_json_describe_container_recursive(&root_container); |
803 | } | ||
804 | json_object *tree = ipc_json_describe_container_recursive(&root_container); | ||
805 | const char *json_string = json_object_to_json_string(tree); | 553 | const char *json_string = json_object_to_json_string(tree); |
806 | ipc_send_reply(client, json_string, (uint32_t) strlen(json_string)); | 554 | ipc_send_reply(client, json_string, (uint32_t) strlen(json_string)); |
807 | json_object_put(tree); | 555 | json_object_put(tree); |
808 | goto exit_cleanup; | 556 | goto exit_cleanup; |
809 | } | 557 | } |
810 | 558 | ||
811 | case IPC_GET_MARKS: | ||
812 | { | ||
813 | if (!(client->security_policy & IPC_FEATURE_GET_MARKS)) { | ||
814 | goto exit_denied; | ||
815 | } | ||
816 | json_object *marks = json_object_new_array(); | ||
817 | container_map(&root_container, ipc_get_marks_callback, marks); | ||
818 | const char *json_string = json_object_to_json_string(marks); | ||
819 | ipc_send_reply(client, json_string, (uint32_t) strlen(json_string)); | ||
820 | json_object_put(marks); | ||
821 | goto exit_cleanup; | ||
822 | } | ||
823 | |||
824 | case IPC_GET_VERSION: | 559 | case IPC_GET_VERSION: |
825 | { | 560 | { |
826 | json_object *version = ipc_json_get_version(); | 561 | json_object *version = ipc_json_get_version(); |
@@ -830,62 +565,12 @@ void ipc_client_handle_command(struct ipc_client *client) { | |||
830 | goto exit_cleanup; | 565 | goto exit_cleanup; |
831 | } | 566 | } |
832 | 567 | ||
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: | 568 | case IPC_GET_BAR_CONFIG: |
880 | { | 569 | { |
881 | if (!(client->security_policy & IPC_FEATURE_GET_BAR_CONFIG)) { | ||
882 | goto exit_denied; | ||
883 | } | ||
884 | if (!buf[0]) { | 570 | if (!buf[0]) { |
885 | // Send list of configured bar IDs | 571 | // Send list of configured bar IDs |
886 | json_object *bars = json_object_new_array(); | 572 | json_object *bars = json_object_new_array(); |
887 | int i; | 573 | for (int i = 0; i < config->bars->length; ++i) { |
888 | for (i = 0; i < config->bars->length; ++i) { | ||
889 | struct bar_config *bar = config->bars->items[i]; | 574 | struct bar_config *bar = config->bars->items[i]; |
890 | json_object_array_add(bars, json_object_new_string(bar->id)); | 575 | json_object_array_add(bars, json_object_new_string(bar->id)); |
891 | } | 576 | } |
@@ -895,8 +580,7 @@ void ipc_client_handle_command(struct ipc_client *client) { | |||
895 | } else { | 580 | } else { |
896 | // Send particular bar's details | 581 | // Send particular bar's details |
897 | struct bar_config *bar = NULL; | 582 | struct bar_config *bar = NULL; |
898 | int i; | 583 | for (int i = 0; i < config->bars->length; ++i) { |
899 | for (i = 0; i < config->bars->length; ++i) { | ||
900 | bar = config->bars->items[i]; | 584 | bar = config->bars->items[i]; |
901 | if (strcmp(buf, bar->id) == 0) { | 585 | if (strcmp(buf, bar->id) == 0) { |
902 | break; | 586 | break; |
@@ -916,24 +600,13 @@ void ipc_client_handle_command(struct ipc_client *client) { | |||
916 | goto exit_cleanup; | 600 | goto exit_cleanup; |
917 | } | 601 | } |
918 | 602 | ||
919 | case IPC_GET_CLIPBOARD: | ||
920 | { | ||
921 | if (!(client->security_policy & IPC_FEATURE_GET_CLIPBOARD)) { | ||
922 | goto exit_denied; | ||
923 | } | ||
924 | |||
925 | ipc_get_clipboard(client, buf); | ||
926 | goto exit_cleanup; | ||
927 | } | ||
928 | |||
929 | default: | 603 | default: |
930 | sway_log(L_INFO, "Unknown IPC command type %i", client->current_command); | 604 | wlr_log(L_INFO, "Unknown IPC command type %i", client->current_command); |
931 | goto exit_cleanup; | 605 | goto exit_cleanup; |
932 | } | 606 | } |
933 | 607 | ||
934 | exit_denied: | ||
935 | ipc_send_reply(client, error_denied, (uint32_t)strlen(error_denied)); | 608 | ipc_send_reply(client, error_denied, (uint32_t)strlen(error_denied)); |
936 | sway_log(L_DEBUG, "Denied IPC client access to %i", client->current_command); | 609 | wlr_log(L_DEBUG, "Denied IPC client access to %i", client->current_command); |
937 | 610 | ||
938 | exit_cleanup: | 611 | exit_cleanup: |
939 | client->payload_length = 0; | 612 | client->payload_length = 0; |
@@ -956,16 +629,15 @@ bool ipc_send_reply(struct ipc_client *client, const char *payload, uint32_t pay | |||
956 | client->write_buffer_size *= 2; | 629 | client->write_buffer_size *= 2; |
957 | } | 630 | } |
958 | 631 | ||
959 | // TODO: reduce the limit back to 4 MB when screenshooter is implemented | 632 | if (client->write_buffer_size > 4e6) { // 4 MB |
960 | if (client->write_buffer_size > (1 << 28)) { // 256 MB | 633 | wlr_log(L_ERROR, "Client write buffer too big, disconnecting client"); |
961 | sway_log(L_ERROR, "Client write buffer too big, disconnecting client"); | ||
962 | ipc_client_disconnect(client); | 634 | ipc_client_disconnect(client); |
963 | return false; | 635 | return false; |
964 | } | 636 | } |
965 | 637 | ||
966 | char *new_buffer = realloc(client->write_buffer, client->write_buffer_size); | 638 | char *new_buffer = realloc(client->write_buffer, client->write_buffer_size); |
967 | if (!new_buffer) { | 639 | if (!new_buffer) { |
968 | sway_log(L_ERROR, "Unable to reallocate ipc client write buffer"); | 640 | wlr_log(L_ERROR, "Unable to reallocate ipc client write buffer"); |
969 | ipc_client_disconnect(client); | 641 | ipc_client_disconnect(client); |
970 | return false; | 642 | return false; |
971 | } | 643 | } |
@@ -977,212 +649,11 @@ bool ipc_send_reply(struct ipc_client *client, const char *payload, uint32_t pay | |||
977 | client->write_buffer_len += payload_length; | 649 | client->write_buffer_len += payload_length; |
978 | 650 | ||
979 | if (!client->writable_event_source) { | 651 | 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); | 652 | client->writable_event_source = wl_event_loop_add_fd( |
653 | server.wl_event_loop, client->fd, WL_EVENT_WRITABLE, | ||
654 | ipc_client_handle_writable, client); | ||
981 | } | 655 | } |
982 | 656 | ||
983 | sway_log(L_DEBUG, "Added IPC reply to client %d queue: %s", client->fd, payload); | 657 | wlr_log(L_DEBUG, "Added IPC reply to client %d queue: %s", client->fd, payload); |
984 | |||
985 | return true; | 658 | return true; |
986 | } | 659 | } |
987 | |||
988 | void ipc_get_workspaces_callback(swayc_t *workspace, void *data) { | ||
989 | if (workspace->type == C_WORKSPACE) { | ||
990 | json_object *workspace_json = ipc_json_describe_container(workspace); | ||
991 | // override the default focused indicator because | ||
992 | // it's set differently for the get_workspaces reply | ||
993 | bool focused = root_container.focused == workspace->parent && workspace->parent->focused == workspace; | ||
994 | json_object_object_del(workspace_json, "focused"); | ||
995 | json_object_object_add(workspace_json, "focused", json_object_new_boolean(focused)); | ||
996 | json_object_array_add((json_object *)data, workspace_json); | ||
997 | } | ||
998 | } | ||
999 | |||
1000 | void ipc_get_outputs_callback(swayc_t *container, void *data) { | ||
1001 | if (container->type == C_OUTPUT) { | ||
1002 | json_object_array_add((json_object *)data, ipc_json_describe_container(container)); | ||
1003 | } | ||
1004 | } | ||
1005 | |||
1006 | static void ipc_get_marks_callback(swayc_t *container, void *data) { | ||
1007 | json_object *object = (json_object *)data; | ||
1008 | if (container->marks) { | ||
1009 | for (int i = 0; i < container->marks->length; ++i) { | ||
1010 | char *mark = (char *)container->marks->items[i]; | ||
1011 | json_object_array_add(object, json_object_new_string(mark)); | ||
1012 | } | ||
1013 | } | ||
1014 | } | ||
1015 | |||
1016 | void ipc_send_event(const char *json_string, enum ipc_command_type event) { | ||
1017 | static struct { | ||
1018 | enum ipc_command_type event; | ||
1019 | enum ipc_feature feature; | ||
1020 | } security_mappings[] = { | ||
1021 | { IPC_EVENT_WORKSPACE, IPC_FEATURE_EVENT_WORKSPACE }, | ||
1022 | { IPC_EVENT_OUTPUT, IPC_FEATURE_EVENT_OUTPUT }, | ||
1023 | { IPC_EVENT_MODE, IPC_FEATURE_EVENT_MODE }, | ||
1024 | { IPC_EVENT_WINDOW, IPC_FEATURE_EVENT_WINDOW }, | ||
1025 | { IPC_EVENT_BINDING, IPC_FEATURE_EVENT_BINDING }, | ||
1026 | { IPC_EVENT_INPUT, IPC_FEATURE_EVENT_INPUT } | ||
1027 | }; | ||
1028 | |||
1029 | uint32_t security_mask = 0; | ||
1030 | for (size_t i = 0; i < sizeof(security_mappings) / sizeof(security_mappings[0]); ++i) { | ||
1031 | if (security_mappings[i].event == event) { | ||
1032 | security_mask = security_mappings[i].feature; | ||
1033 | break; | ||
1034 | } | ||
1035 | } | ||
1036 | |||
1037 | int i; | ||
1038 | struct ipc_client *client; | ||
1039 | for (i = 0; i < ipc_client_list->length; i++) { | ||
1040 | client = ipc_client_list->items[i]; | ||
1041 | if (!(client->security_policy & security_mask)) { | ||
1042 | continue; | ||
1043 | } | ||
1044 | if ((client->subscribed_events & event_mask(event)) == 0) { | ||
1045 | continue; | ||
1046 | } | ||
1047 | client->current_command = event; | ||
1048 | if (!ipc_send_reply(client, json_string, (uint32_t) strlen(json_string))) { | ||
1049 | sway_log_errno(L_INFO, "Unable to send reply to IPC client"); | ||
1050 | ipc_client_disconnect(client); | ||
1051 | } | ||
1052 | } | ||
1053 | } | ||
1054 | |||
1055 | void ipc_event_workspace(swayc_t *old, swayc_t *new, const char *change) { | ||
1056 | sway_log(L_DEBUG, "Sending workspace::%s event", change); | ||
1057 | json_object *obj = json_object_new_object(); | ||
1058 | json_object_object_add(obj, "change", json_object_new_string(change)); | ||
1059 | if (strcmp("focus", change) == 0) { | ||
1060 | if (old) { | ||
1061 | json_object_object_add(obj, "old", ipc_json_describe_container_recursive(old)); | ||
1062 | } else { | ||
1063 | json_object_object_add(obj, "old", NULL); | ||
1064 | } | ||
1065 | } | ||
1066 | |||
1067 | if (new) { | ||
1068 | json_object_object_add(obj, "current", ipc_json_describe_container_recursive(new)); | ||
1069 | } else { | ||
1070 | json_object_object_add(obj, "current", NULL); | ||
1071 | } | ||
1072 | |||
1073 | const char *json_string = json_object_to_json_string(obj); | ||
1074 | ipc_send_event(json_string, IPC_EVENT_WORKSPACE); | ||
1075 | |||
1076 | json_object_put(obj); // free | ||
1077 | } | ||
1078 | |||
1079 | void ipc_event_window(swayc_t *window, const char *change) { | ||
1080 | sway_log(L_DEBUG, "Sending window::%s event", change); | ||
1081 | json_object *obj = json_object_new_object(); | ||
1082 | json_object_object_add(obj, "change", json_object_new_string(change)); | ||
1083 | json_object_object_add(obj, "container", ipc_json_describe_container_recursive(window)); | ||
1084 | |||
1085 | const char *json_string = json_object_to_json_string(obj); | ||
1086 | ipc_send_event(json_string, IPC_EVENT_WINDOW); | ||
1087 | |||
1088 | json_object_put(obj); // free | ||
1089 | } | ||
1090 | |||
1091 | void ipc_event_barconfig_update(struct bar_config *bar) { | ||
1092 | sway_log(L_DEBUG, "Sending barconfig_update event"); | ||
1093 | json_object *json = ipc_json_describe_bar_config(bar); | ||
1094 | const char *json_string = json_object_to_json_string(json); | ||
1095 | ipc_send_event(json_string, IPC_EVENT_BARCONFIG_UPDATE); | ||
1096 | |||
1097 | json_object_put(json); // free | ||
1098 | } | ||
1099 | |||
1100 | void ipc_event_mode(const char *mode) { | ||
1101 | sway_log(L_DEBUG, "Sending mode::%s event", mode); | ||
1102 | json_object *obj = json_object_new_object(); | ||
1103 | json_object_object_add(obj, "change", json_object_new_string(mode)); | ||
1104 | |||
1105 | const char *json_string = json_object_to_json_string(obj); | ||
1106 | ipc_send_event(json_string, IPC_EVENT_MODE); | ||
1107 | |||
1108 | json_object_put(obj); // free | ||
1109 | } | ||
1110 | |||
1111 | void ipc_event_modifier(uint32_t modifier, const char *state) { | ||
1112 | sway_log(L_DEBUG, "Sending modifier::%s event", state); | ||
1113 | json_object *obj = json_object_new_object(); | ||
1114 | json_object_object_add(obj, "change", json_object_new_string(state)); | ||
1115 | |||
1116 | const char *modifier_name = get_modifier_name_by_mask(modifier); | ||
1117 | json_object_object_add(obj, "modifier", json_object_new_string(modifier_name)); | ||
1118 | |||
1119 | const char *json_string = json_object_to_json_string(obj); | ||
1120 | ipc_send_event(json_string, IPC_EVENT_MODIFIER); | ||
1121 | |||
1122 | json_object_put(obj); // free | ||
1123 | } | ||
1124 | |||
1125 | static void ipc_event_binding(json_object *sb_obj) { | ||
1126 | sway_log(L_DEBUG, "Sending binding::run event"); | ||
1127 | json_object *obj = json_object_new_object(); | ||
1128 | json_object_object_add(obj, "change", json_object_new_string("run")); | ||
1129 | json_object_object_add(obj, "binding", sb_obj); | ||
1130 | |||
1131 | const char *json_string = json_object_to_json_string(obj); | ||
1132 | ipc_send_event(json_string, IPC_EVENT_BINDING); | ||
1133 | |||
1134 | json_object_put(obj); // free | ||
1135 | } | ||
1136 | |||
1137 | void ipc_event_binding_keyboard(struct sway_binding *sb) { | ||
1138 | json_object *sb_obj = json_object_new_object(); | ||
1139 | json_object_object_add(sb_obj, "command", json_object_new_string(sb->command)); | ||
1140 | |||
1141 | const char *names[10]; | ||
1142 | |||
1143 | int len = get_modifier_names(names, sb->modifiers); | ||
1144 | int i; | ||
1145 | json_object *modifiers = json_object_new_array(); | ||
1146 | for (i = 0; i < len; ++i) { | ||
1147 | json_object_array_add(modifiers, json_object_new_string(names[i])); | ||
1148 | } | ||
1149 | |||
1150 | json_object_object_add(sb_obj, "event_state_mask", modifiers); | ||
1151 | |||
1152 | json_object *input_codes = json_object_new_array(); | ||
1153 | int input_code = 0; | ||
1154 | json_object *symbols = json_object_new_array(); | ||
1155 | json_object *symbol = NULL; | ||
1156 | |||
1157 | if (sb->bindcode) { // bindcode: populate input_codes | ||
1158 | uint32_t keycode; | ||
1159 | for (i = 0; i < sb->keys->length; ++i) { | ||
1160 | keycode = *(uint32_t *)sb->keys->items[i]; | ||
1161 | json_object_array_add(input_codes, json_object_new_int(keycode)); | ||
1162 | if (i == 0) { | ||
1163 | input_code = keycode; | ||
1164 | } | ||
1165 | } | ||
1166 | } else { // bindsym: populate symbols | ||
1167 | uint32_t keysym; | ||
1168 | char buffer[64]; | ||
1169 | for (i = 0; i < sb->keys->length; ++i) { | ||
1170 | keysym = *(uint32_t *)sb->keys->items[i]; | ||
1171 | if (xkb_keysym_get_name(keysym, buffer, 64) > 0) { | ||
1172 | json_object *str = json_object_new_string(buffer); | ||
1173 | json_object_array_add(symbols, str); | ||
1174 | if (i == 0) { | ||
1175 | symbol = str; | ||
1176 | } | ||
1177 | } | ||
1178 | } | ||
1179 | } | ||
1180 | |||
1181 | json_object_object_add(sb_obj, "input_codes", input_codes); | ||
1182 | json_object_object_add(sb_obj, "input_code", json_object_new_int(input_code)); | ||
1183 | json_object_object_add(sb_obj, "symbols", symbols); | ||
1184 | json_object_object_add(sb_obj, "symbol", symbol); | ||
1185 | json_object_object_add(sb_obj, "input_type", json_object_new_string("keyboard")); | ||
1186 | |||
1187 | ipc_event_binding(sb_obj); | ||
1188 | } | ||