diff options
-rw-r--r-- | include/ipc.h | 4 | ||||
-rw-r--r-- | sway/ipc.c | 132 | ||||
-rw-r--r-- | sway/workspace.c | 4 |
3 files changed, 110 insertions, 30 deletions
diff --git a/include/ipc.h b/include/ipc.h index 1932ad2d..36957ac1 100644 --- a/include/ipc.h +++ b/include/ipc.h | |||
@@ -1,6 +1,8 @@ | |||
1 | #ifndef _SWAY_IPC_H | 1 | #ifndef _SWAY_IPC_H |
2 | #define _SWAY_IPC_H | 2 | #define _SWAY_IPC_H |
3 | 3 | ||
4 | #include "container.h" | ||
5 | |||
4 | enum ipc_command_type { | 6 | enum ipc_command_type { |
5 | IPC_COMMAND = 0, | 7 | IPC_COMMAND = 0, |
6 | IPC_GET_WORKSPACES = 1, | 8 | IPC_GET_WORKSPACES = 1, |
@@ -16,4 +18,6 @@ void ipc_init(void); | |||
16 | void ipc_terminate(void); | 18 | void ipc_terminate(void); |
17 | struct sockaddr_un *ipc_user_sockaddr(void); | 19 | struct sockaddr_un *ipc_user_sockaddr(void); |
18 | 20 | ||
21 | void ipc_event_workspace(swayc_t *old, swayc_t *new); | ||
22 | |||
19 | #endif | 23 | #endif |
@@ -12,6 +12,7 @@ | |||
12 | #include <fcntl.h> | 12 | #include <fcntl.h> |
13 | #include <ctype.h> | 13 | #include <ctype.h> |
14 | #include <json-c/json.h> | 14 | #include <json-c/json.h> |
15 | #include <list.h> | ||
15 | #include "ipc.h" | 16 | #include "ipc.h" |
16 | #include "log.h" | 17 | #include "log.h" |
17 | #include "config.h" | 18 | #include "config.h" |
@@ -22,6 +23,7 @@ | |||
22 | static int ipc_socket = -1; | 23 | static int ipc_socket = -1; |
23 | static struct wlc_event_source *ipc_event_source = NULL; | 24 | static struct wlc_event_source *ipc_event_source = NULL; |
24 | static struct sockaddr_un *ipc_sockaddr = NULL; | 25 | static struct sockaddr_un *ipc_sockaddr = NULL; |
26 | static list_t *ipc_client_list = NULL; | ||
25 | 27 | ||
26 | static const char ipc_magic[] = {'i', '3', '-', 'i', 'p', 'c'}; | 28 | static const char ipc_magic[] = {'i', '3', '-', 'i', 'p', 'c'}; |
27 | 29 | ||
@@ -30,6 +32,7 @@ struct ipc_client { | |||
30 | int fd; | 32 | int fd; |
31 | uint32_t payload_length; | 33 | uint32_t payload_length; |
32 | enum ipc_command_type current_command; | 34 | enum ipc_command_type current_command; |
35 | enum ipc_command_type subscribed_events; | ||
33 | }; | 36 | }; |
34 | 37 | ||
35 | struct sockaddr_un *ipc_user_sockaddr(void); | 38 | struct sockaddr_un *ipc_user_sockaddr(void); |
@@ -65,6 +68,8 @@ void ipc_init(void) { | |||
65 | // Set i3 IPC socket path so that i3-msg works out of the box | 68 | // Set i3 IPC socket path so that i3-msg works out of the box |
66 | setenv("I3SOCK", ipc_sockaddr->sun_path, 1); | 69 | setenv("I3SOCK", ipc_sockaddr->sun_path, 1); |
67 | 70 | ||
71 | ipc_client_list = create_list(); | ||
72 | |||
68 | ipc_event_source = wlc_event_loop_add_fd(ipc_socket, WLC_EVENT_READABLE, ipc_handle_connection, NULL); | 73 | ipc_event_source = wlc_event_loop_add_fd(ipc_socket, WLC_EVENT_READABLE, ipc_handle_connection, NULL); |
69 | } | 74 | } |
70 | 75 | ||
@@ -75,6 +80,8 @@ void ipc_terminate(void) { | |||
75 | close(ipc_socket); | 80 | close(ipc_socket); |
76 | unlink(ipc_sockaddr->sun_path); | 81 | unlink(ipc_sockaddr->sun_path); |
77 | 82 | ||
83 | list_free(ipc_client_list); | ||
84 | |||
78 | if (ipc_sockaddr) { | 85 | if (ipc_sockaddr) { |
79 | free(ipc_sockaddr); | 86 | free(ipc_sockaddr); |
80 | } | 87 | } |
@@ -122,6 +129,8 @@ int ipc_handle_connection(int fd, uint32_t mask, void *data) { | |||
122 | client->fd = client_fd; | 129 | client->fd = client_fd; |
123 | client->event_source = wlc_event_loop_add_fd(client_fd, WLC_EVENT_READABLE, ipc_client_handle_readable, client); | 130 | client->event_source = wlc_event_loop_add_fd(client_fd, WLC_EVENT_READABLE, ipc_client_handle_readable, client); |
124 | 131 | ||
132 | list_add(ipc_client_list, client); | ||
133 | |||
125 | return 0; | 134 | return 0; |
126 | } | 135 | } |
127 | 136 | ||
@@ -133,11 +142,13 @@ int ipc_client_handle_readable(int client_fd, uint32_t mask, void *data) { | |||
133 | 142 | ||
134 | if (mask & WLC_EVENT_ERROR) { | 143 | if (mask & WLC_EVENT_ERROR) { |
135 | sway_log(L_INFO, "IPC Client socket error, removing client"); | 144 | sway_log(L_INFO, "IPC Client socket error, removing client"); |
145 | client->fd = -1; | ||
136 | ipc_client_disconnect(client); | 146 | ipc_client_disconnect(client); |
137 | return 0; | 147 | return 0; |
138 | } | 148 | } |
139 | 149 | ||
140 | if (mask & WLC_EVENT_HANGUP) { | 150 | if (mask & WLC_EVENT_HANGUP) { |
151 | client->fd = -1; | ||
141 | ipc_client_disconnect(client); | 152 | ipc_client_disconnect(client); |
142 | return 0; | 153 | return 0; |
143 | } | 154 | } |
@@ -195,8 +206,15 @@ void ipc_client_disconnect(struct ipc_client *client) | |||
195 | return; | 206 | return; |
196 | } | 207 | } |
197 | 208 | ||
209 | if (client->fd != -1) { | ||
210 | shutdown(client->fd, SHUT_RDWR); | ||
211 | } | ||
212 | |||
198 | sway_log(L_INFO, "IPC Client %d disconnected", client->fd); | 213 | sway_log(L_INFO, "IPC Client %d disconnected", client->fd); |
199 | wlc_event_source_remove(client->event_source); | 214 | wlc_event_source_remove(client->event_source); |
215 | int i = 0; | ||
216 | while (i < ipc_client_list->length && ipc_client_list->items[i] != client) i++; | ||
217 | list_del(ipc_client_list, i); | ||
200 | close(client->fd); | 218 | close(client->fd); |
201 | free(client); | 219 | free(client); |
202 | } | 220 | } |
@@ -230,6 +248,35 @@ void ipc_client_handle_command(struct ipc_client *client) { | |||
230 | free_cmd_results(results); | 248 | free_cmd_results(results); |
231 | break; | 249 | break; |
232 | } | 250 | } |
251 | case IPC_SUBSCRIBE: | ||
252 | { | ||
253 | buf[client->payload_length] = '\0'; | ||
254 | struct json_object *request = json_tokener_parse(buf); | ||
255 | if (request == NULL) { | ||
256 | ipc_send_reply(client, "{\"success\": false}", 18); | ||
257 | ipc_client_disconnect(client); | ||
258 | return; | ||
259 | } | ||
260 | |||
261 | // parse requested event types | ||
262 | for (int i = 0; i < json_object_array_length(request); i++) { | ||
263 | const char *event_type = json_object_get_string(json_object_array_get_idx(request, i)); | ||
264 | if (strcmp(event_type, "workspace") == 0) { | ||
265 | client->subscribed_events |= IPC_GET_WORKSPACES; | ||
266 | } | ||
267 | else { | ||
268 | ipc_send_reply(client, "{\"success\": false}", 18); | ||
269 | ipc_client_disconnect(client); | ||
270 | json_object_put(request); | ||
271 | return; | ||
272 | } | ||
273 | } | ||
274 | |||
275 | json_object_put(request); | ||
276 | |||
277 | ipc_send_reply(client, "{\"success\": true}", 17); | ||
278 | break; | ||
279 | } | ||
233 | case IPC_GET_WORKSPACES: | 280 | case IPC_GET_WORKSPACES: |
234 | { | 281 | { |
235 | json_object *workspaces = json_object_new_array(); | 282 | json_object *workspaces = json_object_new_array(); |
@@ -309,44 +356,69 @@ bool ipc_send_reply(struct ipc_client *client, const char *payload, uint32_t pay | |||
309 | return true; | 356 | return true; |
310 | } | 357 | } |
311 | 358 | ||
359 | json_object *ipc_json_describe_workspace(swayc_t *workspace) { | ||
360 | int num = isdigit(workspace->name[0]) ? atoi(workspace->name) : -1; | ||
361 | json_object *object = json_object_new_object(); | ||
362 | json_object *rect = json_object_new_object(); | ||
363 | json_object_object_add(rect, "x", json_object_new_int((int32_t) workspace->x)); | ||
364 | json_object_object_add(rect, "y", json_object_new_int((int32_t) workspace->y)); | ||
365 | json_object_object_add(rect, "width", json_object_new_int((int32_t) workspace->width)); | ||
366 | json_object_object_add(rect, "height", json_object_new_int((int32_t) workspace->height)); | ||
367 | |||
368 | json_object_object_add(object, "num", json_object_new_int(num)); | ||
369 | json_object_object_add(object, "name", json_object_new_string(workspace->name)); | ||
370 | json_object_object_add(object, "visible", json_object_new_boolean(workspace->visible)); | ||
371 | bool focused = root_container.focused == workspace->parent && workspace->parent->focused == workspace; | ||
372 | json_object_object_add(object, "focused", json_object_new_boolean(focused)); | ||
373 | json_object_object_add(object, "rect", rect); | ||
374 | json_object_object_add(object, "output", json_object_new_string(workspace->parent ? workspace->parent->name : "null")); | ||
375 | json_object_object_add(object, "urgent", json_object_new_boolean(false)); | ||
376 | |||
377 | return object; | ||
378 | } | ||
379 | |||
312 | void ipc_get_workspaces_callback(swayc_t *workspace, void *data) { | 380 | void ipc_get_workspaces_callback(swayc_t *workspace, void *data) { |
313 | if (workspace->type == C_WORKSPACE) { | 381 | if (workspace->type == C_WORKSPACE) { |
314 | int num = isdigit(workspace->name[0]) ? atoi(workspace->name) : -1; | 382 | json_object_array_add((json_object *)data, ipc_json_describe_workspace(workspace)); |
315 | json_object *object = json_object_new_object(); | ||
316 | json_object *rect = json_object_new_object(); | ||
317 | json_object_object_add(rect, "x", json_object_new_int((int32_t) workspace->x)); | ||
318 | json_object_object_add(rect, "y", json_object_new_int((int32_t) workspace->y)); | ||
319 | json_object_object_add(rect, "width", json_object_new_int((int32_t) workspace->width)); | ||
320 | json_object_object_add(rect, "height", json_object_new_int((int32_t) workspace->height)); | ||
321 | |||
322 | json_object_object_add(object, "num", json_object_new_int(num)); | ||
323 | json_object_object_add(object, "name", json_object_new_string(workspace->name)); | ||
324 | json_object_object_add(object, "visible", json_object_new_boolean(workspace->visible)); | ||
325 | bool focused = root_container.focused == workspace->parent && workspace->parent->focused == workspace; | ||
326 | json_object_object_add(object, "focused", json_object_new_boolean(focused)); | ||
327 | json_object_object_add(object, "rect", rect); | ||
328 | json_object_object_add(object, "output", json_object_new_string(workspace->parent->name)); | ||
329 | json_object_object_add(object, "urgent", json_object_new_boolean(false)); | ||
330 | |||
331 | json_object_array_add((json_object *)data, object); | ||
332 | } | 383 | } |
333 | } | 384 | } |
334 | 385 | ||
386 | json_object *ipc_json_describe_output(swayc_t *output) { | ||
387 | json_object *object = json_object_new_object(); | ||
388 | json_object *rect = json_object_new_object(); | ||
389 | json_object_object_add(rect, "x", json_object_new_int((int32_t) output->x)); | ||
390 | json_object_object_add(rect, "y", json_object_new_int((int32_t) output->y)); | ||
391 | json_object_object_add(rect, "width", json_object_new_int((int32_t) output->width)); | ||
392 | json_object_object_add(rect, "height", json_object_new_int((int32_t) output->height)); | ||
393 | |||
394 | json_object_object_add(object, "name", json_object_new_string(output->name)); | ||
395 | json_object_object_add(object, "active", json_object_new_boolean(true)); | ||
396 | json_object_object_add(object, "primary", json_object_new_boolean(false)); | ||
397 | json_object_object_add(object, "rect", rect); | ||
398 | json_object_object_add(object, "current_workspace", | ||
399 | output->focused ? json_object_new_string(output->focused->name) : NULL); | ||
400 | |||
401 | return object; | ||
402 | } | ||
403 | |||
335 | void ipc_get_outputs_callback(swayc_t *container, void *data) { | 404 | void ipc_get_outputs_callback(swayc_t *container, void *data) { |
336 | if (container->type == C_OUTPUT) { | 405 | if (container->type == C_OUTPUT) { |
337 | json_object *object = json_object_new_object(); | 406 | json_object_array_add((json_object *)data, ipc_json_describe_output(container)); |
338 | json_object *rect = json_object_new_object(); | 407 | } |
339 | json_object_object_add(rect, "x", json_object_new_int((int32_t) container->x)); | 408 | } |
340 | json_object_object_add(rect, "y", json_object_new_int((int32_t) container->y)); | ||
341 | json_object_object_add(rect, "width", json_object_new_int((int32_t) container->width)); | ||
342 | json_object_object_add(rect, "height", json_object_new_int((int32_t) container->height)); | ||
343 | 409 | ||
344 | json_object_object_add(object, "name", json_object_new_string(container->name)); | 410 | void ipc_event_workspace(swayc_t *old, swayc_t *new) { |
345 | json_object_object_add(object, "active", json_object_new_boolean(true)); | 411 | json_object *obj = json_object_new_object(); |
346 | json_object_object_add(object, "primary", json_object_new_boolean(false)); | 412 | json_object_object_add(obj, "change", json_object_new_string("focus")); |
347 | json_object_object_add(object, "rect", rect); | 413 | json_object_object_add(obj, "old", ipc_json_describe_workspace(old)); |
348 | json_object_object_add(object, "current_workspace", container->focused ? json_object_new_string(container->focused->name) : NULL); | 414 | json_object_object_add(obj, "current", ipc_json_describe_workspace(new)); |
415 | const char *json_string = json_object_to_json_string(obj); | ||
349 | 416 | ||
350 | json_object_array_add((json_object *)data, object); | 417 | for (int i = 0; i < ipc_client_list->length; i++) { |
418 | struct ipc_client *client = ipc_client_list->items[i]; | ||
419 | if ((client->subscribed_events & IPC_GET_WORKSPACES) == 0) break; | ||
420 | ipc_send_reply(client, json_string, (uint32_t) strlen(json_string)); | ||
351 | } | 421 | } |
422 | |||
423 | json_object_put(obj); // free | ||
352 | } | 424 | } |
diff --git a/sway/workspace.c b/sway/workspace.c index b7e9760b..604fc8e4 100644 --- a/sway/workspace.c +++ b/sway/workspace.c | |||
@@ -13,6 +13,7 @@ | |||
13 | #include "stringop.h" | 13 | #include "stringop.h" |
14 | #include "focus.h" | 14 | #include "focus.h" |
15 | #include "util.h" | 15 | #include "util.h" |
16 | #include "ipc.h" | ||
16 | 17 | ||
17 | char *prev_workspace_name = NULL; | 18 | char *prev_workspace_name = NULL; |
18 | 19 | ||
@@ -221,5 +222,8 @@ bool workspace_switch(swayc_t *workspace) { | |||
221 | return false; | 222 | return false; |
222 | } | 223 | } |
223 | arrange_windows(workspace, -1, -1); | 224 | arrange_windows(workspace, -1, -1); |
225 | |||
226 | ipc_event_workspace(active_ws, workspace); | ||
227 | |||
224 | return true; | 228 | return true; |
225 | } | 229 | } |