sway-patched-tray-menu/sway/ipc-server.c

972 lines
30 KiB
C
Raw Normal View History

2017-11-23 03:37:07 +01:00
// See https://i3wm.org/docs/ipc.html for protocol information
2018-07-18 12:37:50 +02:00
#include <linux/input-event-codes.h>
2017-11-23 03:37:07 +01:00
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <json.h>
2017-11-23 03:37:07 +01:00
#include <stdbool.h>
#include <stdint.h>
2018-07-18 12:37:50 +02:00
#include <stdio.h>
2017-11-23 03:37:07 +01:00
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <sys/un.h>
#include <unistd.h>
#include <wayland-server-core.h>
2017-11-23 03:37:07 +01:00
#include "sway/commands.h"
2018-07-08 21:34:47 +02:00
#include "sway/config.h"
#include "sway/desktop/transaction.h"
2017-11-23 03:37:07 +01:00
#include "sway/ipc-json.h"
#include "sway/ipc-server.h"
#include "sway/output.h"
2017-11-23 03:37:07 +01:00
#include "sway/server.h"
2017-12-18 20:27:38 +01:00
#include "sway/input/input-manager.h"
#include "sway/input/keyboard.h"
#include "sway/input/seat.h"
#include "sway/tree/root.h"
2018-05-22 23:35:39 +02:00
#include "sway/tree/view.h"
Implement type safe arguments and demote sway_container This commit changes the meaning of sway_container so that it only refers to layout containers and view containers. Workspaces, outputs and the root are no longer known as containers. Instead, root, outputs, workspaces and containers are all a type of node, and containers come in two types: layout containers and view containers. In addition to the above, this implements type safe variables. This means we use specific types such as sway_output and sway_workspace instead of generic containers or nodes. However, it's worth noting that in a few places places (eg. seat focus and transactions) referring to them in a generic way is unavoidable which is why we still use nodes in some places. If you want a TL;DR, look at node.h, as well as the struct definitions for root, output, workspace and container. Note that sway_output now contains a workspaces list, and workspaces now contain a tiling and floating list, and containers now contain a pointer back to the workspace. There are now functions for seat_get_focused_workspace and seat_get_focused_container. The latter will return NULL if a workspace itself is focused. Most other seat functions like seat_get_focus and seat_set_focus now accept and return nodes. In the config->handler_context struct, current_container has been replaced with three pointers: node, container and workspace. node is the same as what current_container was, while workspace is the workspace that the node resides on and container is the actual container, which may be NULL if a workspace itself is focused. The global root_container variable has been replaced with one simply called root, which is a pointer to the sway_root instance. The way outputs are created, enabled, disabled and destroyed has changed. Previously we'd wrap the sway_output in a container when it is enabled, but as we don't have containers any more it needs a different approach. The output_create and output_destroy functions previously created/destroyed the container, but now they create/destroy the sway_output. There is a new function output_disable to disable an output without destroying it. Containers have a new view property. If this is populated then the container is a view container, otherwise it's a layout container. Like before, this property is immutable for the life of the container. Containers have both a `sway_container *parent` and `sway_workspace *workspace`. As we use specific types now, parent cannot point to a workspace so it'll be NULL for containers which are direct children of the workspace. The workspace property is set for all containers, except those which are hidden in the scratchpad as they have no workspace. In some cases we need to refer to workspaces in a container-like way. For example, workspaces have layout and children, but when using specific types this makes it difficult. Likewise, it's difficult for a container to get its parent's layout when the parent could be another container or a workspace. To make it easier, some helper functions have been created: container_parent_layout and container_get_siblings. container_remove_child has been renamed to container_detach and container_replace_child has been renamed to container_replace. `container_handle_fullscreen_reparent(con, old_parent)` has had the old_parent removed. We now unfullscreen the workspace when detaching the container, so this function is simplified and only needs one argument now. container_notify_subtree_changed has been renamed to container_update_representation. This is more descriptive of its purpose. I also wanted to be able to call it with whatever container was changed rather than the container's parent, which makes bubbling up to the workspace easier. There are now state structs per node thing. ie. sway_output_state, sway_workspace_state and sway_container_state. The focus, move and layout commands have been completely refactored to work with the specific types. I considered making these a separate PR, but I'd be backporting my changes only to replace them again, and it's easier just to test everything at once.
2018-08-30 13:00:10 +02:00
#include "sway/tree/workspace.h"
2017-11-23 03:37:07 +01:00
#include "list.h"
#include "log.h"
2018-07-18 12:37:50 +02:00
#include "util.h"
2017-11-23 03:37:07 +01:00
static int ipc_socket = -1;
static struct wl_event_source *ipc_event_source = NULL;
static struct sockaddr_un *ipc_sockaddr = NULL;
static list_t *ipc_client_list = NULL;
static struct wl_listener ipc_display_destroy;
2017-11-23 03:37:07 +01:00
static const char ipc_magic[] = {'i', '3', '-', 'i', 'p', 'c'};
2019-01-16 10:57:51 +01:00
#define IPC_HEADER_SIZE (sizeof(ipc_magic) + 8)
2017-11-23 03:37:07 +01:00
struct ipc_client {
struct wl_event_source *event_source;
struct wl_event_source *writable_event_source;
struct sway_server *server;
int fd;
enum ipc_command_type subscribed_events;
size_t write_buffer_len;
size_t write_buffer_size;
char *write_buffer;
// The following are for storing data between event_loop calls
uint32_t pending_length;
enum ipc_command_type pending_type;
2017-11-23 03:37:07 +01:00
};
int ipc_handle_connection(int fd, uint32_t mask, void *data);
int ipc_client_handle_readable(int client_fd, uint32_t mask, void *data);
int ipc_client_handle_writable(int client_fd, uint32_t mask, void *data);
void ipc_client_disconnect(struct ipc_client *client);
void ipc_client_handle_command(struct ipc_client *client, uint32_t payload_length,
enum ipc_command_type payload_type);
bool ipc_send_reply(struct ipc_client *client, enum ipc_command_type payload_type,
const char *payload, uint32_t payload_length);
2017-11-23 03:37:07 +01:00
static void handle_display_destroy(struct wl_listener *listener, void *data) {
if (ipc_event_source) {
wl_event_source_remove(ipc_event_source);
}
close(ipc_socket);
unlink(ipc_sockaddr->sun_path);
2018-07-04 11:48:57 +02:00
while (ipc_client_list->length) {
2018-12-09 00:55:14 +01:00
ipc_client_disconnect(ipc_client_list->items[ipc_client_list->length-1]);
2018-07-04 11:48:57 +02:00
}
list_free(ipc_client_list);
2018-12-09 00:55:14 +01:00
free(ipc_sockaddr);
wl_list_remove(&ipc_display_destroy.link);
}
2017-11-23 03:37:07 +01:00
void ipc_init(struct sway_server *server) {
ipc_socket = socket(AF_UNIX, SOCK_STREAM, 0);
2017-11-23 03:37:07 +01:00
if (ipc_socket == -1) {
sway_abort("Unable to create IPC socket");
}
if (fcntl(ipc_socket, F_SETFD, FD_CLOEXEC) == -1) {
sway_abort("Unable to set CLOEXEC on IPC socket");
}
if (fcntl(ipc_socket, F_SETFL, O_NONBLOCK) == -1) {
sway_abort("Unable to set NONBLOCK on IPC socket");
}
2017-11-23 03:37:07 +01:00
ipc_sockaddr = ipc_user_sockaddr();
// We want to use socket name set by user, not existing socket from another sway instance.
if (getenv("SWAYSOCK") != NULL && access(getenv("SWAYSOCK"), F_OK) == -1) {
2018-04-13 15:35:23 +02:00
strncpy(ipc_sockaddr->sun_path, getenv("SWAYSOCK"), sizeof(ipc_sockaddr->sun_path) - 1);
2017-11-23 03:37:07 +01:00
ipc_sockaddr->sun_path[sizeof(ipc_sockaddr->sun_path) - 1] = 0;
}
unlink(ipc_sockaddr->sun_path);
if (bind(ipc_socket, (struct sockaddr *)ipc_sockaddr, sizeof(*ipc_sockaddr)) == -1) {
sway_abort("Unable to bind IPC socket");
}
if (listen(ipc_socket, 3) == -1) {
sway_abort("Unable to listen on IPC socket");
}
// Set i3 IPC socket path so that i3-msg works out of the box
setenv("I3SOCK", ipc_sockaddr->sun_path, 1);
setenv("SWAYSOCK", ipc_sockaddr->sun_path, 1);
ipc_client_list = create_list();
ipc_display_destroy.notify = handle_display_destroy;
wl_display_add_destroy_listener(server->wl_display, &ipc_display_destroy);
2017-11-23 03:37:07 +01:00
ipc_event_source = wl_event_loop_add_fd(server->wl_event_loop, ipc_socket,
WL_EVENT_READABLE, ipc_handle_connection, server);
}
struct sockaddr_un *ipc_user_sockaddr(void) {
struct sockaddr_un *ipc_sockaddr = malloc(sizeof(struct sockaddr_un));
if (ipc_sockaddr == NULL) {
sway_abort("Can't allocate ipc_sockaddr");
}
ipc_sockaddr->sun_family = AF_UNIX;
int path_size = sizeof(ipc_sockaddr->sun_path);
// Env var typically set by logind, e.g. "/run/user/<user-id>"
const char *dir = getenv("XDG_RUNTIME_DIR");
if (!dir) {
dir = "/tmp";
}
if (path_size <= snprintf(ipc_sockaddr->sun_path, path_size,
2020-06-04 15:43:42 +02:00
"%s/sway-ipc.%u.%i.sock", dir, getuid(), getpid())) {
2017-11-23 03:37:07 +01:00
sway_abort("Socket path won't fit into ipc_sockaddr->sun_path");
}
return ipc_sockaddr;
}
int ipc_handle_connection(int fd, uint32_t mask, void *data) {
(void) fd;
struct sway_server *server = data;
assert(mask == WL_EVENT_READABLE);
int client_fd = accept(ipc_socket, NULL, NULL);
if (client_fd == -1) {
sway_log_errno(SWAY_ERROR, "Unable to accept IPC client connection");
2017-11-23 03:37:07 +01:00
return 0;
}
int flags;
if ((flags = fcntl(client_fd, F_GETFD)) == -1
|| fcntl(client_fd, F_SETFD, flags|FD_CLOEXEC) == -1) {
sway_log_errno(SWAY_ERROR, "Unable to set CLOEXEC on IPC client socket");
2017-11-23 03:37:07 +01:00
close(client_fd);
return 0;
}
if ((flags = fcntl(client_fd, F_GETFL)) == -1
|| fcntl(client_fd, F_SETFL, flags|O_NONBLOCK) == -1) {
sway_log_errno(SWAY_ERROR, "Unable to set NONBLOCK on IPC client socket");
2017-11-23 03:37:07 +01:00
close(client_fd);
return 0;
}
struct ipc_client *client = malloc(sizeof(struct ipc_client));
if (!client) {
sway_log(SWAY_ERROR, "Unable to allocate ipc client");
2017-11-23 03:37:07 +01:00
close(client_fd);
return 0;
}
client->server = server;
client->pending_length = 0;
2017-11-23 03:37:07 +01:00
client->fd = client_fd;
client->subscribed_events = 0;
client->event_source = wl_event_loop_add_fd(server->wl_event_loop,
client_fd, WL_EVENT_READABLE, ipc_client_handle_readable, client);
client->writable_event_source = NULL;
client->write_buffer_size = 128;
client->write_buffer_len = 0;
client->write_buffer = malloc(client->write_buffer_size);
if (!client->write_buffer) {
sway_log(SWAY_ERROR, "Unable to allocate ipc client write buffer");
2017-11-23 03:37:07 +01:00
close(client_fd);
return 0;
}
sway_log(SWAY_DEBUG, "New client: fd %d", client_fd);
2017-11-23 03:37:07 +01:00
list_add(ipc_client_list, client);
return 0;
}
int ipc_client_handle_readable(int client_fd, uint32_t mask, void *data) {
struct ipc_client *client = data;
if (mask & WL_EVENT_ERROR) {
sway_log(SWAY_ERROR, "IPC Client socket error, removing client");
2017-11-23 03:37:07 +01:00
ipc_client_disconnect(client);
return 0;
}
if (mask & WL_EVENT_HANGUP) {
ipc_client_disconnect(client);
return 0;
}
int read_available;
if (ioctl(client_fd, FIONREAD, &read_available) == -1) {
sway_log_errno(SWAY_INFO, "Unable to read IPC socket buffer size");
2017-11-23 03:37:07 +01:00
ipc_client_disconnect(client);
return 0;
}
// Wait for the rest of the command payload in case the header has already been read
if (client->pending_length > 0) {
if ((uint32_t)read_available >= client->pending_length) {
// Reset pending values.
uint32_t pending_length = client->pending_length;
enum ipc_command_type pending_type = client->pending_type;
client->pending_length = 0;
ipc_client_handle_command(client, pending_length, pending_type);
2017-11-23 03:37:07 +01:00
}
return 0;
}
2019-01-16 10:57:51 +01:00
if (read_available < (int) IPC_HEADER_SIZE) {
2017-11-23 03:37:07 +01:00
return 0;
}
2019-01-16 10:57:51 +01:00
uint8_t buf[IPC_HEADER_SIZE];
// Should be fully available, because read_available >= IPC_HEADER_SIZE
ssize_t received = recv(client_fd, buf, IPC_HEADER_SIZE, 0);
2017-11-23 03:37:07 +01:00
if (received == -1) {
sway_log_errno(SWAY_INFO, "Unable to receive header from IPC client");
2017-11-23 03:37:07 +01:00
ipc_client_disconnect(client);
return 0;
}
if (memcmp(buf, ipc_magic, sizeof(ipc_magic)) != 0) {
sway_log(SWAY_DEBUG, "IPC header check failed");
2017-11-23 03:37:07 +01:00
ipc_client_disconnect(client);
return 0;
}
memcpy(&client->pending_length, buf + sizeof(ipc_magic), sizeof(uint32_t));
memcpy(&client->pending_type, buf + sizeof(ipc_magic) + sizeof(uint32_t), sizeof(uint32_t));
2017-11-23 03:37:07 +01:00
if (read_available - received >= (long)client->pending_length) {
// Reset pending values.
uint32_t pending_length = client->pending_length;
enum ipc_command_type pending_type = client->pending_type;
client->pending_length = 0;
ipc_client_handle_command(client, pending_length, pending_type);
2017-11-23 03:37:07 +01:00
}
return 0;
}
static bool ipc_has_event_listeners(enum ipc_command_type event) {
for (int i = 0; i < ipc_client_list->length; i++) {
2018-04-06 04:50:10 +02:00
struct ipc_client *client = ipc_client_list->items[i];
if ((client->subscribed_events & event_mask(event)) != 0) {
2018-04-06 04:50:10 +02:00
return true;
}
}
2018-04-06 04:50:10 +02:00
return false;
}
2018-02-28 01:53:15 +01:00
static void ipc_send_event(const char *json_string, enum ipc_command_type event) {
struct ipc_client *client;
for (int i = 0; i < ipc_client_list->length; i++) {
2018-02-28 01:53:15 +01:00
client = ipc_client_list->items[i];
if ((client->subscribed_events & event_mask(event)) == 0) {
continue;
}
if (!ipc_send_reply(client, event, json_string,
(uint32_t)strlen(json_string))) {
sway_log_errno(SWAY_INFO, "Unable to send reply to IPC client");
/* ipc_send_reply destroys client on error, which also
* removes it from the list, so we need to process
* current index again */
i--;
2018-02-28 01:53:15 +01:00
}
}
}
Implement type safe arguments and demote sway_container This commit changes the meaning of sway_container so that it only refers to layout containers and view containers. Workspaces, outputs and the root are no longer known as containers. Instead, root, outputs, workspaces and containers are all a type of node, and containers come in two types: layout containers and view containers. In addition to the above, this implements type safe variables. This means we use specific types such as sway_output and sway_workspace instead of generic containers or nodes. However, it's worth noting that in a few places places (eg. seat focus and transactions) referring to them in a generic way is unavoidable which is why we still use nodes in some places. If you want a TL;DR, look at node.h, as well as the struct definitions for root, output, workspace and container. Note that sway_output now contains a workspaces list, and workspaces now contain a tiling and floating list, and containers now contain a pointer back to the workspace. There are now functions for seat_get_focused_workspace and seat_get_focused_container. The latter will return NULL if a workspace itself is focused. Most other seat functions like seat_get_focus and seat_set_focus now accept and return nodes. In the config->handler_context struct, current_container has been replaced with three pointers: node, container and workspace. node is the same as what current_container was, while workspace is the workspace that the node resides on and container is the actual container, which may be NULL if a workspace itself is focused. The global root_container variable has been replaced with one simply called root, which is a pointer to the sway_root instance. The way outputs are created, enabled, disabled and destroyed has changed. Previously we'd wrap the sway_output in a container when it is enabled, but as we don't have containers any more it needs a different approach. The output_create and output_destroy functions previously created/destroyed the container, but now they create/destroy the sway_output. There is a new function output_disable to disable an output without destroying it. Containers have a new view property. If this is populated then the container is a view container, otherwise it's a layout container. Like before, this property is immutable for the life of the container. Containers have both a `sway_container *parent` and `sway_workspace *workspace`. As we use specific types now, parent cannot point to a workspace so it'll be NULL for containers which are direct children of the workspace. The workspace property is set for all containers, except those which are hidden in the scratchpad as they have no workspace. In some cases we need to refer to workspaces in a container-like way. For example, workspaces have layout and children, but when using specific types this makes it difficult. Likewise, it's difficult for a container to get its parent's layout when the parent could be another container or a workspace. To make it easier, some helper functions have been created: container_parent_layout and container_get_siblings. container_remove_child has been renamed to container_detach and container_replace_child has been renamed to container_replace. `container_handle_fullscreen_reparent(con, old_parent)` has had the old_parent removed. We now unfullscreen the workspace when detaching the container, so this function is simplified and only needs one argument now. container_notify_subtree_changed has been renamed to container_update_representation. This is more descriptive of its purpose. I also wanted to be able to call it with whatever container was changed rather than the container's parent, which makes bubbling up to the workspace easier. There are now state structs per node thing. ie. sway_output_state, sway_workspace_state and sway_container_state. The focus, move and layout commands have been completely refactored to work with the specific types. I considered making these a separate PR, but I'd be backporting my changes only to replace them again, and it's easier just to test everything at once.
2018-08-30 13:00:10 +02:00
void ipc_event_workspace(struct sway_workspace *old,
struct sway_workspace *new, const char *change) {
if (!ipc_has_event_listeners(IPC_EVENT_WORKSPACE)) {
return;
}
sway_log(SWAY_DEBUG, "Sending workspace::%s event", change);
json_object *obj = json_object_new_object();
json_object_object_add(obj, "change", json_object_new_string(change));
if (old) {
json_object_object_add(obj, "old",
Implement type safe arguments and demote sway_container This commit changes the meaning of sway_container so that it only refers to layout containers and view containers. Workspaces, outputs and the root are no longer known as containers. Instead, root, outputs, workspaces and containers are all a type of node, and containers come in two types: layout containers and view containers. In addition to the above, this implements type safe variables. This means we use specific types such as sway_output and sway_workspace instead of generic containers or nodes. However, it's worth noting that in a few places places (eg. seat focus and transactions) referring to them in a generic way is unavoidable which is why we still use nodes in some places. If you want a TL;DR, look at node.h, as well as the struct definitions for root, output, workspace and container. Note that sway_output now contains a workspaces list, and workspaces now contain a tiling and floating list, and containers now contain a pointer back to the workspace. There are now functions for seat_get_focused_workspace and seat_get_focused_container. The latter will return NULL if a workspace itself is focused. Most other seat functions like seat_get_focus and seat_set_focus now accept and return nodes. In the config->handler_context struct, current_container has been replaced with three pointers: node, container and workspace. node is the same as what current_container was, while workspace is the workspace that the node resides on and container is the actual container, which may be NULL if a workspace itself is focused. The global root_container variable has been replaced with one simply called root, which is a pointer to the sway_root instance. The way outputs are created, enabled, disabled and destroyed has changed. Previously we'd wrap the sway_output in a container when it is enabled, but as we don't have containers any more it needs a different approach. The output_create and output_destroy functions previously created/destroyed the container, but now they create/destroy the sway_output. There is a new function output_disable to disable an output without destroying it. Containers have a new view property. If this is populated then the container is a view container, otherwise it's a layout container. Like before, this property is immutable for the life of the container. Containers have both a `sway_container *parent` and `sway_workspace *workspace`. As we use specific types now, parent cannot point to a workspace so it'll be NULL for containers which are direct children of the workspace. The workspace property is set for all containers, except those which are hidden in the scratchpad as they have no workspace. In some cases we need to refer to workspaces in a container-like way. For example, workspaces have layout and children, but when using specific types this makes it difficult. Likewise, it's difficult for a container to get its parent's layout when the parent could be another container or a workspace. To make it easier, some helper functions have been created: container_parent_layout and container_get_siblings. container_remove_child has been renamed to container_detach and container_replace_child has been renamed to container_replace. `container_handle_fullscreen_reparent(con, old_parent)` has had the old_parent removed. We now unfullscreen the workspace when detaching the container, so this function is simplified and only needs one argument now. container_notify_subtree_changed has been renamed to container_update_representation. This is more descriptive of its purpose. I also wanted to be able to call it with whatever container was changed rather than the container's parent, which makes bubbling up to the workspace easier. There are now state structs per node thing. ie. sway_output_state, sway_workspace_state and sway_container_state. The focus, move and layout commands have been completely refactored to work with the specific types. I considered making these a separate PR, but I'd be backporting my changes only to replace them again, and it's easier just to test everything at once.
2018-08-30 13:00:10 +02:00
ipc_json_describe_node_recursive(&old->node));
} else {
json_object_object_add(obj, "old", NULL);
}
if (new) {
json_object_object_add(obj, "current",
Implement type safe arguments and demote sway_container This commit changes the meaning of sway_container so that it only refers to layout containers and view containers. Workspaces, outputs and the root are no longer known as containers. Instead, root, outputs, workspaces and containers are all a type of node, and containers come in two types: layout containers and view containers. In addition to the above, this implements type safe variables. This means we use specific types such as sway_output and sway_workspace instead of generic containers or nodes. However, it's worth noting that in a few places places (eg. seat focus and transactions) referring to them in a generic way is unavoidable which is why we still use nodes in some places. If you want a TL;DR, look at node.h, as well as the struct definitions for root, output, workspace and container. Note that sway_output now contains a workspaces list, and workspaces now contain a tiling and floating list, and containers now contain a pointer back to the workspace. There are now functions for seat_get_focused_workspace and seat_get_focused_container. The latter will return NULL if a workspace itself is focused. Most other seat functions like seat_get_focus and seat_set_focus now accept and return nodes. In the config->handler_context struct, current_container has been replaced with three pointers: node, container and workspace. node is the same as what current_container was, while workspace is the workspace that the node resides on and container is the actual container, which may be NULL if a workspace itself is focused. The global root_container variable has been replaced with one simply called root, which is a pointer to the sway_root instance. The way outputs are created, enabled, disabled and destroyed has changed. Previously we'd wrap the sway_output in a container when it is enabled, but as we don't have containers any more it needs a different approach. The output_create and output_destroy functions previously created/destroyed the container, but now they create/destroy the sway_output. There is a new function output_disable to disable an output without destroying it. Containers have a new view property. If this is populated then the container is a view container, otherwise it's a layout container. Like before, this property is immutable for the life of the container. Containers have both a `sway_container *parent` and `sway_workspace *workspace`. As we use specific types now, parent cannot point to a workspace so it'll be NULL for containers which are direct children of the workspace. The workspace property is set for all containers, except those which are hidden in the scratchpad as they have no workspace. In some cases we need to refer to workspaces in a container-like way. For example, workspaces have layout and children, but when using specific types this makes it difficult. Likewise, it's difficult for a container to get its parent's layout when the parent could be another container or a workspace. To make it easier, some helper functions have been created: container_parent_layout and container_get_siblings. container_remove_child has been renamed to container_detach and container_replace_child has been renamed to container_replace. `container_handle_fullscreen_reparent(con, old_parent)` has had the old_parent removed. We now unfullscreen the workspace when detaching the container, so this function is simplified and only needs one argument now. container_notify_subtree_changed has been renamed to container_update_representation. This is more descriptive of its purpose. I also wanted to be able to call it with whatever container was changed rather than the container's parent, which makes bubbling up to the workspace easier. There are now state structs per node thing. ie. sway_output_state, sway_workspace_state and sway_container_state. The focus, move and layout commands have been completely refactored to work with the specific types. I considered making these a separate PR, but I'd be backporting my changes only to replace them again, and it's easier just to test everything at once.
2018-08-30 13:00:10 +02:00
ipc_json_describe_node_recursive(&new->node));
} else {
json_object_object_add(obj, "current", NULL);
}
const char *json_string = json_object_to_json_string(obj);
ipc_send_event(json_string, IPC_EVENT_WORKSPACE);
json_object_put(obj);
}
void ipc_event_window(struct sway_container *window, const char *change) {
if (!ipc_has_event_listeners(IPC_EVENT_WINDOW)) {
return;
}
sway_log(SWAY_DEBUG, "Sending window::%s event", change);
2018-02-28 01:53:15 +01:00
json_object *obj = json_object_new_object();
json_object_object_add(obj, "change", json_object_new_string(change));
Implement type safe arguments and demote sway_container This commit changes the meaning of sway_container so that it only refers to layout containers and view containers. Workspaces, outputs and the root are no longer known as containers. Instead, root, outputs, workspaces and containers are all a type of node, and containers come in two types: layout containers and view containers. In addition to the above, this implements type safe variables. This means we use specific types such as sway_output and sway_workspace instead of generic containers or nodes. However, it's worth noting that in a few places places (eg. seat focus and transactions) referring to them in a generic way is unavoidable which is why we still use nodes in some places. If you want a TL;DR, look at node.h, as well as the struct definitions for root, output, workspace and container. Note that sway_output now contains a workspaces list, and workspaces now contain a tiling and floating list, and containers now contain a pointer back to the workspace. There are now functions for seat_get_focused_workspace and seat_get_focused_container. The latter will return NULL if a workspace itself is focused. Most other seat functions like seat_get_focus and seat_set_focus now accept and return nodes. In the config->handler_context struct, current_container has been replaced with three pointers: node, container and workspace. node is the same as what current_container was, while workspace is the workspace that the node resides on and container is the actual container, which may be NULL if a workspace itself is focused. The global root_container variable has been replaced with one simply called root, which is a pointer to the sway_root instance. The way outputs are created, enabled, disabled and destroyed has changed. Previously we'd wrap the sway_output in a container when it is enabled, but as we don't have containers any more it needs a different approach. The output_create and output_destroy functions previously created/destroyed the container, but now they create/destroy the sway_output. There is a new function output_disable to disable an output without destroying it. Containers have a new view property. If this is populated then the container is a view container, otherwise it's a layout container. Like before, this property is immutable for the life of the container. Containers have both a `sway_container *parent` and `sway_workspace *workspace`. As we use specific types now, parent cannot point to a workspace so it'll be NULL for containers which are direct children of the workspace. The workspace property is set for all containers, except those which are hidden in the scratchpad as they have no workspace. In some cases we need to refer to workspaces in a container-like way. For example, workspaces have layout and children, but when using specific types this makes it difficult. Likewise, it's difficult for a container to get its parent's layout when the parent could be another container or a workspace. To make it easier, some helper functions have been created: container_parent_layout and container_get_siblings. container_remove_child has been renamed to container_detach and container_replace_child has been renamed to container_replace. `container_handle_fullscreen_reparent(con, old_parent)` has had the old_parent removed. We now unfullscreen the workspace when detaching the container, so this function is simplified and only needs one argument now. container_notify_subtree_changed has been renamed to container_update_representation. This is more descriptive of its purpose. I also wanted to be able to call it with whatever container was changed rather than the container's parent, which makes bubbling up to the workspace easier. There are now state structs per node thing. ie. sway_output_state, sway_workspace_state and sway_container_state. The focus, move and layout commands have been completely refactored to work with the specific types. I considered making these a separate PR, but I'd be backporting my changes only to replace them again, and it's easier just to test everything at once.
2018-08-30 13:00:10 +02:00
json_object_object_add(obj, "container",
ipc_json_describe_node_recursive(&window->node));
2018-02-28 01:53:15 +01:00
const char *json_string = json_object_to_json_string(obj);
ipc_send_event(json_string, IPC_EVENT_WINDOW);
2018-03-30 04:10:33 +02:00
json_object_put(obj);
2018-02-28 01:53:15 +01:00
}
2018-03-29 23:20:03 +02:00
void ipc_event_barconfig_update(struct bar_config *bar) {
if (!ipc_has_event_listeners(IPC_EVENT_BARCONFIG_UPDATE)) {
return;
}
sway_log(SWAY_DEBUG, "Sending barconfig_update event");
2018-03-29 23:20:03 +02:00
json_object *json = ipc_json_describe_bar_config(bar);
const char *json_string = json_object_to_json_string(json);
ipc_send_event(json_string, IPC_EVENT_BARCONFIG_UPDATE);
2018-03-30 04:10:33 +02:00
json_object_put(json);
}
void ipc_event_bar_state_update(struct bar_config *bar) {
if (!ipc_has_event_listeners(IPC_EVENT_BAR_STATE_UPDATE)) {
return;
}
sway_log(SWAY_DEBUG, "Sending bar_state_update event");
json_object *json = json_object_new_object();
json_object_object_add(json, "id", json_object_new_string(bar->id));
json_object_object_add(json, "visible_by_modifier",
json_object_new_boolean(bar->visible_by_modifier));
const char *json_string = json_object_to_json_string(json);
ipc_send_event(json_string, IPC_EVENT_BAR_STATE_UPDATE);
json_object_put(json);
}
2018-07-06 00:12:14 +02:00
void ipc_event_mode(const char *mode, bool pango) {
if (!ipc_has_event_listeners(IPC_EVENT_MODE)) {
return;
}
sway_log(SWAY_DEBUG, "Sending mode::%s event", mode);
2018-03-30 04:10:33 +02:00
json_object *obj = json_object_new_object();
json_object_object_add(obj, "change", json_object_new_string(mode));
2018-07-06 00:12:14 +02:00
json_object_object_add(obj, "pango_markup",
json_object_new_boolean(pango));
2018-03-30 04:10:33 +02:00
const char *json_string = json_object_to_json_string(obj);
ipc_send_event(json_string, IPC_EVENT_MODE);
json_object_put(obj);
2018-03-29 23:20:03 +02:00
}
2018-07-13 17:58:45 +02:00
void ipc_event_shutdown(const char *reason) {
if (!ipc_has_event_listeners(IPC_EVENT_SHUTDOWN)) {
return;
}
sway_log(SWAY_DEBUG, "Sending shutdown::%s event", reason);
2018-07-13 17:58:45 +02:00
json_object *json = json_object_new_object();
json_object_object_add(json, "change", json_object_new_string(reason));
const char *json_string = json_object_to_json_string(json);
ipc_send_event(json_string, IPC_EVENT_SHUTDOWN);
json_object_put(json);
}
2018-07-18 12:37:50 +02:00
void ipc_event_binding(struct sway_binding *binding) {
if (!ipc_has_event_listeners(IPC_EVENT_BINDING)) {
return;
}
sway_log(SWAY_DEBUG, "Sending binding event");
2018-07-18 12:37:50 +02:00
json_object *json_binding = json_object_new_object();
json_object_object_add(json_binding, "command", json_object_new_string(binding->command));
const char *names[10];
int len = get_modifier_names(names, binding->modifiers);
json_object *modifiers = json_object_new_array();
for (int i = 0; i < len; ++i) {
json_object_array_add(modifiers, json_object_new_string(names[i]));
}
json_object_object_add(json_binding, "event_state_mask", modifiers);
json_object *input_codes = json_object_new_array();
int input_code = 0;
json_object *symbols = json_object_new_array();
json_object *symbol = NULL;
switch (binding->type) {
case BINDING_KEYCODE:; // bindcode: populate input_codes
2018-07-18 12:37:50 +02:00
uint32_t keycode;
for (int i = 0; i < binding->keys->length; ++i) {
keycode = *(uint32_t *)binding->keys->items[i];
json_object_array_add(input_codes, json_object_new_int(keycode));
if (i == 0) {
input_code = keycode;
}
}
break;
case BINDING_KEYSYM:
case BINDING_MOUSESYM:
case BINDING_MOUSECODE:; // bindsym/mouse: populate symbols
2018-07-18 12:37:50 +02:00
uint32_t keysym;
char buffer[64];
for (int i = 0; i < binding->keys->length; ++i) {
keysym = *(uint32_t *)binding->keys->items[i];
if (keysym >= BTN_LEFT && keysym <= BTN_LEFT + 8) {
snprintf(buffer, 64, "button%u", keysym - BTN_LEFT + 1);
} else if (xkb_keysym_get_name(keysym, buffer, 64) < 0) {
continue;
}
json_object *str = json_object_new_string(buffer);
if (i == 0) {
// str is owned by both symbol and symbols. Make sure
// to bump the ref count.
json_object_array_add(symbols, json_object_get(str));
symbol = str;
} else {
json_object_array_add(symbols, str);
}
}
break;
default:
sway_log(SWAY_DEBUG, "Unsupported ipc binding event");
json_object_put(input_codes);
json_object_put(symbols);
json_object_put(json_binding);
return; // do not send any event
2018-07-18 12:37:50 +02:00
}
json_object_object_add(json_binding, "input_codes", input_codes);
json_object_object_add(json_binding, "input_code", json_object_new_int(input_code));
json_object_object_add(json_binding, "symbols", symbols);
json_object_object_add(json_binding, "symbol", symbol);
bool mouse = binding->type == BINDING_MOUSECODE ||
binding->type == BINDING_MOUSESYM;
json_object_object_add(json_binding, "input_type", mouse
? json_object_new_string("mouse")
: json_object_new_string("keyboard"));
2018-07-18 12:37:50 +02:00
json_object *json = json_object_new_object();
json_object_object_add(json, "change", json_object_new_string("run"));
json_object_object_add(json, "binding", json_binding);
const char *json_string = json_object_to_json_string(json);
ipc_send_event(json_string, IPC_EVENT_BINDING);
json_object_put(json);
}
2018-07-18 13:30:39 +02:00
static void ipc_event_tick(const char *payload) {
if (!ipc_has_event_listeners(IPC_EVENT_TICK)) {
return;
}
sway_log(SWAY_DEBUG, "Sending tick event");
2018-07-18 13:30:39 +02:00
json_object *json = json_object_new_object();
json_object_object_add(json, "first", json_object_new_boolean(false));
json_object_object_add(json, "payload", json_object_new_string(payload));
const char *json_string = json_object_to_json_string(json);
ipc_send_event(json_string, IPC_EVENT_TICK);
json_object_put(json);
}
void ipc_event_input(const char *change, struct sway_input_device *device) {
if (!ipc_has_event_listeners(IPC_EVENT_INPUT)) {
return;
}
sway_log(SWAY_DEBUG, "Sending input event");
json_object *json = json_object_new_object();
json_object_object_add(json, "change", json_object_new_string(change));
json_object_object_add(json, "input", ipc_json_describe_input(device));
const char *json_string = json_object_to_json_string(json);
ipc_send_event(json_string, IPC_EVENT_INPUT);
json_object_put(json);
}
void ipc_event_output(void) {
if (!ipc_has_event_listeners(IPC_EVENT_OUTPUT)) {
return;
}
sway_log(SWAY_DEBUG, "Sending output event");
json_object *json = json_object_new_object();
json_object_object_add(json, "change", json_object_new_string("unspecified"));
const char *json_string = json_object_to_json_string(json);
ipc_send_event(json_string, IPC_EVENT_OUTPUT);
json_object_put(json);
}
2017-11-23 03:37:07 +01:00
int ipc_client_handle_writable(int client_fd, uint32_t mask, void *data) {
struct ipc_client *client = data;
if (mask & WL_EVENT_ERROR) {
sway_log(SWAY_ERROR, "IPC Client socket error, removing client");
2017-11-23 03:37:07 +01:00
ipc_client_disconnect(client);
return 0;
}
if (mask & WL_EVENT_HANGUP) {
ipc_client_disconnect(client);
return 0;
}
if (client->write_buffer_len <= 0) {
return 0;
}
ssize_t written = write(client->fd, client->write_buffer, client->write_buffer_len);
if (written == -1 && errno == EAGAIN) {
return 0;
} else if (written == -1) {
sway_log_errno(SWAY_INFO, "Unable to send data from queue to IPC client");
2017-11-23 03:37:07 +01:00
ipc_client_disconnect(client);
return 0;
}
memmove(client->write_buffer, client->write_buffer + written, client->write_buffer_len - written);
client->write_buffer_len -= written;
if (client->write_buffer_len == 0 && client->writable_event_source) {
wl_event_source_remove(client->writable_event_source);
client->writable_event_source = NULL;
}
return 0;
}
void ipc_client_disconnect(struct ipc_client *client) {
if (!sway_assert(client != NULL, "client != NULL")) {
return;
}
shutdown(client->fd, SHUT_RDWR);
2017-11-23 03:37:07 +01:00
sway_log(SWAY_INFO, "IPC Client %d disconnected", client->fd);
2017-11-23 03:37:07 +01:00
wl_event_source_remove(client->event_source);
if (client->writable_event_source) {
wl_event_source_remove(client->writable_event_source);
}
int i = 0;
while (i < ipc_client_list->length && ipc_client_list->items[i] != client) {
i++;
}
list_del(ipc_client_list, i);
free(client->write_buffer);
close(client->fd);
free(client);
}
Implement type safe arguments and demote sway_container This commit changes the meaning of sway_container so that it only refers to layout containers and view containers. Workspaces, outputs and the root are no longer known as containers. Instead, root, outputs, workspaces and containers are all a type of node, and containers come in two types: layout containers and view containers. In addition to the above, this implements type safe variables. This means we use specific types such as sway_output and sway_workspace instead of generic containers or nodes. However, it's worth noting that in a few places places (eg. seat focus and transactions) referring to them in a generic way is unavoidable which is why we still use nodes in some places. If you want a TL;DR, look at node.h, as well as the struct definitions for root, output, workspace and container. Note that sway_output now contains a workspaces list, and workspaces now contain a tiling and floating list, and containers now contain a pointer back to the workspace. There are now functions for seat_get_focused_workspace and seat_get_focused_container. The latter will return NULL if a workspace itself is focused. Most other seat functions like seat_get_focus and seat_set_focus now accept and return nodes. In the config->handler_context struct, current_container has been replaced with three pointers: node, container and workspace. node is the same as what current_container was, while workspace is the workspace that the node resides on and container is the actual container, which may be NULL if a workspace itself is focused. The global root_container variable has been replaced with one simply called root, which is a pointer to the sway_root instance. The way outputs are created, enabled, disabled and destroyed has changed. Previously we'd wrap the sway_output in a container when it is enabled, but as we don't have containers any more it needs a different approach. The output_create and output_destroy functions previously created/destroyed the container, but now they create/destroy the sway_output. There is a new function output_disable to disable an output without destroying it. Containers have a new view property. If this is populated then the container is a view container, otherwise it's a layout container. Like before, this property is immutable for the life of the container. Containers have both a `sway_container *parent` and `sway_workspace *workspace`. As we use specific types now, parent cannot point to a workspace so it'll be NULL for containers which are direct children of the workspace. The workspace property is set for all containers, except those which are hidden in the scratchpad as they have no workspace. In some cases we need to refer to workspaces in a container-like way. For example, workspaces have layout and children, but when using specific types this makes it difficult. Likewise, it's difficult for a container to get its parent's layout when the parent could be another container or a workspace. To make it easier, some helper functions have been created: container_parent_layout and container_get_siblings. container_remove_child has been renamed to container_detach and container_replace_child has been renamed to container_replace. `container_handle_fullscreen_reparent(con, old_parent)` has had the old_parent removed. We now unfullscreen the workspace when detaching the container, so this function is simplified and only needs one argument now. container_notify_subtree_changed has been renamed to container_update_representation. This is more descriptive of its purpose. I also wanted to be able to call it with whatever container was changed rather than the container's parent, which makes bubbling up to the workspace easier. There are now state structs per node thing. ie. sway_output_state, sway_workspace_state and sway_container_state. The focus, move and layout commands have been completely refactored to work with the specific types. I considered making these a separate PR, but I'd be backporting my changes only to replace them again, and it's easier just to test everything at once.
2018-08-30 13:00:10 +02:00
static void ipc_get_workspaces_callback(struct sway_workspace *workspace,
void *data) {
Implement type safe arguments and demote sway_container This commit changes the meaning of sway_container so that it only refers to layout containers and view containers. Workspaces, outputs and the root are no longer known as containers. Instead, root, outputs, workspaces and containers are all a type of node, and containers come in two types: layout containers and view containers. In addition to the above, this implements type safe variables. This means we use specific types such as sway_output and sway_workspace instead of generic containers or nodes. However, it's worth noting that in a few places places (eg. seat focus and transactions) referring to them in a generic way is unavoidable which is why we still use nodes in some places. If you want a TL;DR, look at node.h, as well as the struct definitions for root, output, workspace and container. Note that sway_output now contains a workspaces list, and workspaces now contain a tiling and floating list, and containers now contain a pointer back to the workspace. There are now functions for seat_get_focused_workspace and seat_get_focused_container. The latter will return NULL if a workspace itself is focused. Most other seat functions like seat_get_focus and seat_set_focus now accept and return nodes. In the config->handler_context struct, current_container has been replaced with three pointers: node, container and workspace. node is the same as what current_container was, while workspace is the workspace that the node resides on and container is the actual container, which may be NULL if a workspace itself is focused. The global root_container variable has been replaced with one simply called root, which is a pointer to the sway_root instance. The way outputs are created, enabled, disabled and destroyed has changed. Previously we'd wrap the sway_output in a container when it is enabled, but as we don't have containers any more it needs a different approach. The output_create and output_destroy functions previously created/destroyed the container, but now they create/destroy the sway_output. There is a new function output_disable to disable an output without destroying it. Containers have a new view property. If this is populated then the container is a view container, otherwise it's a layout container. Like before, this property is immutable for the life of the container. Containers have both a `sway_container *parent` and `sway_workspace *workspace`. As we use specific types now, parent cannot point to a workspace so it'll be NULL for containers which are direct children of the workspace. The workspace property is set for all containers, except those which are hidden in the scratchpad as they have no workspace. In some cases we need to refer to workspaces in a container-like way. For example, workspaces have layout and children, but when using specific types this makes it difficult. Likewise, it's difficult for a container to get its parent's layout when the parent could be another container or a workspace. To make it easier, some helper functions have been created: container_parent_layout and container_get_siblings. container_remove_child has been renamed to container_detach and container_replace_child has been renamed to container_replace. `container_handle_fullscreen_reparent(con, old_parent)` has had the old_parent removed. We now unfullscreen the workspace when detaching the container, so this function is simplified and only needs one argument now. container_notify_subtree_changed has been renamed to container_update_representation. This is more descriptive of its purpose. I also wanted to be able to call it with whatever container was changed rather than the container's parent, which makes bubbling up to the workspace easier. There are now state structs per node thing. ie. sway_output_state, sway_workspace_state and sway_container_state. The focus, move and layout commands have been completely refactored to work with the specific types. I considered making these a separate PR, but I'd be backporting my changes only to replace them again, and it's easier just to test everything at once.
2018-08-30 13:00:10 +02:00
json_object *workspace_json = ipc_json_describe_node(&workspace->node);
2018-03-30 04:16:43 +02:00
// override the default focused indicator because
// it's set differently for the get_workspaces reply
struct sway_seat *seat = input_manager_get_default_seat();
Implement type safe arguments and demote sway_container This commit changes the meaning of sway_container so that it only refers to layout containers and view containers. Workspaces, outputs and the root are no longer known as containers. Instead, root, outputs, workspaces and containers are all a type of node, and containers come in two types: layout containers and view containers. In addition to the above, this implements type safe variables. This means we use specific types such as sway_output and sway_workspace instead of generic containers or nodes. However, it's worth noting that in a few places places (eg. seat focus and transactions) referring to them in a generic way is unavoidable which is why we still use nodes in some places. If you want a TL;DR, look at node.h, as well as the struct definitions for root, output, workspace and container. Note that sway_output now contains a workspaces list, and workspaces now contain a tiling and floating list, and containers now contain a pointer back to the workspace. There are now functions for seat_get_focused_workspace and seat_get_focused_container. The latter will return NULL if a workspace itself is focused. Most other seat functions like seat_get_focus and seat_set_focus now accept and return nodes. In the config->handler_context struct, current_container has been replaced with three pointers: node, container and workspace. node is the same as what current_container was, while workspace is the workspace that the node resides on and container is the actual container, which may be NULL if a workspace itself is focused. The global root_container variable has been replaced with one simply called root, which is a pointer to the sway_root instance. The way outputs are created, enabled, disabled and destroyed has changed. Previously we'd wrap the sway_output in a container when it is enabled, but as we don't have containers any more it needs a different approach. The output_create and output_destroy functions previously created/destroyed the container, but now they create/destroy the sway_output. There is a new function output_disable to disable an output without destroying it. Containers have a new view property. If this is populated then the container is a view container, otherwise it's a layout container. Like before, this property is immutable for the life of the container. Containers have both a `sway_container *parent` and `sway_workspace *workspace`. As we use specific types now, parent cannot point to a workspace so it'll be NULL for containers which are direct children of the workspace. The workspace property is set for all containers, except those which are hidden in the scratchpad as they have no workspace. In some cases we need to refer to workspaces in a container-like way. For example, workspaces have layout and children, but when using specific types this makes it difficult. Likewise, it's difficult for a container to get its parent's layout when the parent could be another container or a workspace. To make it easier, some helper functions have been created: container_parent_layout and container_get_siblings. container_remove_child has been renamed to container_detach and container_replace_child has been renamed to container_replace. `container_handle_fullscreen_reparent(con, old_parent)` has had the old_parent removed. We now unfullscreen the workspace when detaching the container, so this function is simplified and only needs one argument now. container_notify_subtree_changed has been renamed to container_update_representation. This is more descriptive of its purpose. I also wanted to be able to call it with whatever container was changed rather than the container's parent, which makes bubbling up to the workspace easier. There are now state structs per node thing. ie. sway_output_state, sway_workspace_state and sway_container_state. The focus, move and layout commands have been completely refactored to work with the specific types. I considered making these a separate PR, but I'd be backporting my changes only to replace them again, and it's easier just to test everything at once.
2018-08-30 13:00:10 +02:00
struct sway_workspace *focused_ws = seat_get_focused_workspace(seat);
2018-03-30 04:16:43 +02:00
bool focused = workspace == focused_ws;
json_object_object_del(workspace_json, "focused");
json_object_object_add(workspace_json, "focused",
json_object_new_boolean(focused));
json_object_array_add((json_object *)data, workspace_json);
2018-03-30 19:56:31 +02:00
Implement type safe arguments and demote sway_container This commit changes the meaning of sway_container so that it only refers to layout containers and view containers. Workspaces, outputs and the root are no longer known as containers. Instead, root, outputs, workspaces and containers are all a type of node, and containers come in two types: layout containers and view containers. In addition to the above, this implements type safe variables. This means we use specific types such as sway_output and sway_workspace instead of generic containers or nodes. However, it's worth noting that in a few places places (eg. seat focus and transactions) referring to them in a generic way is unavoidable which is why we still use nodes in some places. If you want a TL;DR, look at node.h, as well as the struct definitions for root, output, workspace and container. Note that sway_output now contains a workspaces list, and workspaces now contain a tiling and floating list, and containers now contain a pointer back to the workspace. There are now functions for seat_get_focused_workspace and seat_get_focused_container. The latter will return NULL if a workspace itself is focused. Most other seat functions like seat_get_focus and seat_set_focus now accept and return nodes. In the config->handler_context struct, current_container has been replaced with three pointers: node, container and workspace. node is the same as what current_container was, while workspace is the workspace that the node resides on and container is the actual container, which may be NULL if a workspace itself is focused. The global root_container variable has been replaced with one simply called root, which is a pointer to the sway_root instance. The way outputs are created, enabled, disabled and destroyed has changed. Previously we'd wrap the sway_output in a container when it is enabled, but as we don't have containers any more it needs a different approach. The output_create and output_destroy functions previously created/destroyed the container, but now they create/destroy the sway_output. There is a new function output_disable to disable an output without destroying it. Containers have a new view property. If this is populated then the container is a view container, otherwise it's a layout container. Like before, this property is immutable for the life of the container. Containers have both a `sway_container *parent` and `sway_workspace *workspace`. As we use specific types now, parent cannot point to a workspace so it'll be NULL for containers which are direct children of the workspace. The workspace property is set for all containers, except those which are hidden in the scratchpad as they have no workspace. In some cases we need to refer to workspaces in a container-like way. For example, workspaces have layout and children, but when using specific types this makes it difficult. Likewise, it's difficult for a container to get its parent's layout when the parent could be another container or a workspace. To make it easier, some helper functions have been created: container_parent_layout and container_get_siblings. container_remove_child has been renamed to container_detach and container_replace_child has been renamed to container_replace. `container_handle_fullscreen_reparent(con, old_parent)` has had the old_parent removed. We now unfullscreen the workspace when detaching the container, so this function is simplified and only needs one argument now. container_notify_subtree_changed has been renamed to container_update_representation. This is more descriptive of its purpose. I also wanted to be able to call it with whatever container was changed rather than the container's parent, which makes bubbling up to the workspace easier. There are now state structs per node thing. ie. sway_output_state, sway_workspace_state and sway_container_state. The focus, move and layout commands have been completely refactored to work with the specific types. I considered making these a separate PR, but I'd be backporting my changes only to replace them again, and it's easier just to test everything at once.
2018-08-30 13:00:10 +02:00
focused_ws = output_get_active_workspace(workspace->output);
2018-03-30 19:56:31 +02:00
bool visible = workspace == focused_ws;
json_object_object_add(workspace_json, "visible",
json_object_new_boolean(visible));
}
2018-05-22 23:35:39 +02:00
static void ipc_get_marks_callback(struct sway_container *con, void *data) {
json_object *marks = (json_object *)data;
for (int i = 0; i < con->marks->length; ++i) {
char *mark = (char *)con->marks->items[i];
json_object_array_add(marks, json_object_new_string(mark));
2018-05-22 23:35:39 +02:00
}
}
void ipc_client_handle_command(struct ipc_client *client, uint32_t payload_length,
enum ipc_command_type payload_type) {
2017-11-23 03:37:07 +01:00
if (!sway_assert(client != NULL, "client != NULL")) {
return;
}
char *buf = malloc(payload_length + 1);
2017-11-23 03:37:07 +01:00
if (!buf) {
sway_log_errno(SWAY_INFO, "Unable to allocate IPC payload");
2017-11-23 03:37:07 +01:00
ipc_client_disconnect(client);
return;
}
if (payload_length > 0) {
2017-11-23 03:37:07 +01:00
// Payload should be fully available
ssize_t received = recv(client->fd, buf, payload_length, 0);
2017-11-23 03:37:07 +01:00
if (received == -1)
{
sway_log_errno(SWAY_INFO, "Unable to receive payload from IPC client");
2017-11-23 03:37:07 +01:00
ipc_client_disconnect(client);
free(buf);
return;
}
}
buf[payload_length] = '\0';
2017-11-23 03:37:07 +01:00
switch (payload_type) {
2017-11-23 03:37:07 +01:00
case IPC_COMMAND:
{
char *line = strtok(buf, "\n");
while (line) {
size_t line_length = strlen(line);
if (line + line_length >= buf + payload_length) {
break;
}
line[line_length] = ';';
line = strtok(NULL, "\n");
}
list_t *res_list = execute_command(buf, NULL, NULL);
transaction_commit_dirty();
char *json = cmd_results_to_json(res_list);
int length = strlen(json);
ipc_send_reply(client, payload_type, json, (uint32_t)length);
2018-07-04 11:48:57 +02:00
free(json);
while (res_list->length) {
struct cmd_results *results = res_list->items[0];
free_cmd_results(results);
list_del(res_list, 0);
}
list_free(res_list);
2017-11-23 03:37:07 +01:00
goto exit_cleanup;
2017-12-03 14:33:52 +01:00
}
2018-07-18 13:30:39 +02:00
case IPC_SEND_TICK:
{
ipc_event_tick(buf);
ipc_send_reply(client, payload_type, "{\"success\": true}", 17);
2018-07-18 13:30:39 +02:00
goto exit_cleanup;
}
2017-12-18 14:06:03 +01:00
case IPC_GET_OUTPUTS:
{
json_object *outputs = json_object_new_array();
Implement type safe arguments and demote sway_container This commit changes the meaning of sway_container so that it only refers to layout containers and view containers. Workspaces, outputs and the root are no longer known as containers. Instead, root, outputs, workspaces and containers are all a type of node, and containers come in two types: layout containers and view containers. In addition to the above, this implements type safe variables. This means we use specific types such as sway_output and sway_workspace instead of generic containers or nodes. However, it's worth noting that in a few places places (eg. seat focus and transactions) referring to them in a generic way is unavoidable which is why we still use nodes in some places. If you want a TL;DR, look at node.h, as well as the struct definitions for root, output, workspace and container. Note that sway_output now contains a workspaces list, and workspaces now contain a tiling and floating list, and containers now contain a pointer back to the workspace. There are now functions for seat_get_focused_workspace and seat_get_focused_container. The latter will return NULL if a workspace itself is focused. Most other seat functions like seat_get_focus and seat_set_focus now accept and return nodes. In the config->handler_context struct, current_container has been replaced with three pointers: node, container and workspace. node is the same as what current_container was, while workspace is the workspace that the node resides on and container is the actual container, which may be NULL if a workspace itself is focused. The global root_container variable has been replaced with one simply called root, which is a pointer to the sway_root instance. The way outputs are created, enabled, disabled and destroyed has changed. Previously we'd wrap the sway_output in a container when it is enabled, but as we don't have containers any more it needs a different approach. The output_create and output_destroy functions previously created/destroyed the container, but now they create/destroy the sway_output. There is a new function output_disable to disable an output without destroying it. Containers have a new view property. If this is populated then the container is a view container, otherwise it's a layout container. Like before, this property is immutable for the life of the container. Containers have both a `sway_container *parent` and `sway_workspace *workspace`. As we use specific types now, parent cannot point to a workspace so it'll be NULL for containers which are direct children of the workspace. The workspace property is set for all containers, except those which are hidden in the scratchpad as they have no workspace. In some cases we need to refer to workspaces in a container-like way. For example, workspaces have layout and children, but when using specific types this makes it difficult. Likewise, it's difficult for a container to get its parent's layout when the parent could be another container or a workspace. To make it easier, some helper functions have been created: container_parent_layout and container_get_siblings. container_remove_child has been renamed to container_detach and container_replace_child has been renamed to container_replace. `container_handle_fullscreen_reparent(con, old_parent)` has had the old_parent removed. We now unfullscreen the workspace when detaching the container, so this function is simplified and only needs one argument now. container_notify_subtree_changed has been renamed to container_update_representation. This is more descriptive of its purpose. I also wanted to be able to call it with whatever container was changed rather than the container's parent, which makes bubbling up to the workspace easier. There are now state structs per node thing. ie. sway_output_state, sway_workspace_state and sway_container_state. The focus, move and layout commands have been completely refactored to work with the specific types. I considered making these a separate PR, but I'd be backporting my changes only to replace them again, and it's easier just to test everything at once.
2018-08-30 13:00:10 +02:00
for (int i = 0; i < root->outputs->length; ++i) {
struct sway_output *output = root->outputs->items[i];
json_object *output_json = ipc_json_describe_node(&output->node);
// override the default focused indicator because it's set
// differently for the get_outputs reply
struct sway_seat *seat = input_manager_get_default_seat();
struct sway_workspace *focused_ws =
seat_get_focused_workspace(seat);
bool focused = focused_ws && output == focused_ws->output;
json_object_object_del(output_json, "focused");
json_object_object_add(output_json, "focused",
json_object_new_boolean(focused));
const char *subpixel = sway_wl_output_subpixel_to_string(output->wlr_output->subpixel);
json_object_object_add(output_json, "subpixel_hinting", json_object_new_string(subpixel));
json_object_array_add(outputs, output_json);
2017-12-18 14:06:03 +01:00
}
struct sway_output *output;
Implement type safe arguments and demote sway_container This commit changes the meaning of sway_container so that it only refers to layout containers and view containers. Workspaces, outputs and the root are no longer known as containers. Instead, root, outputs, workspaces and containers are all a type of node, and containers come in two types: layout containers and view containers. In addition to the above, this implements type safe variables. This means we use specific types such as sway_output and sway_workspace instead of generic containers or nodes. However, it's worth noting that in a few places places (eg. seat focus and transactions) referring to them in a generic way is unavoidable which is why we still use nodes in some places. If you want a TL;DR, look at node.h, as well as the struct definitions for root, output, workspace and container. Note that sway_output now contains a workspaces list, and workspaces now contain a tiling and floating list, and containers now contain a pointer back to the workspace. There are now functions for seat_get_focused_workspace and seat_get_focused_container. The latter will return NULL if a workspace itself is focused. Most other seat functions like seat_get_focus and seat_set_focus now accept and return nodes. In the config->handler_context struct, current_container has been replaced with three pointers: node, container and workspace. node is the same as what current_container was, while workspace is the workspace that the node resides on and container is the actual container, which may be NULL if a workspace itself is focused. The global root_container variable has been replaced with one simply called root, which is a pointer to the sway_root instance. The way outputs are created, enabled, disabled and destroyed has changed. Previously we'd wrap the sway_output in a container when it is enabled, but as we don't have containers any more it needs a different approach. The output_create and output_destroy functions previously created/destroyed the container, but now they create/destroy the sway_output. There is a new function output_disable to disable an output without destroying it. Containers have a new view property. If this is populated then the container is a view container, otherwise it's a layout container. Like before, this property is immutable for the life of the container. Containers have both a `sway_container *parent` and `sway_workspace *workspace`. As we use specific types now, parent cannot point to a workspace so it'll be NULL for containers which are direct children of the workspace. The workspace property is set for all containers, except those which are hidden in the scratchpad as they have no workspace. In some cases we need to refer to workspaces in a container-like way. For example, workspaces have layout and children, but when using specific types this makes it difficult. Likewise, it's difficult for a container to get its parent's layout when the parent could be another container or a workspace. To make it easier, some helper functions have been created: container_parent_layout and container_get_siblings. container_remove_child has been renamed to container_detach and container_replace_child has been renamed to container_replace. `container_handle_fullscreen_reparent(con, old_parent)` has had the old_parent removed. We now unfullscreen the workspace when detaching the container, so this function is simplified and only needs one argument now. container_notify_subtree_changed has been renamed to container_update_representation. This is more descriptive of its purpose. I also wanted to be able to call it with whatever container was changed rather than the container's parent, which makes bubbling up to the workspace easier. There are now state structs per node thing. ie. sway_output_state, sway_workspace_state and sway_container_state. The focus, move and layout commands have been completely refactored to work with the specific types. I considered making these a separate PR, but I'd be backporting my changes only to replace them again, and it's easier just to test everything at once.
2018-08-30 13:00:10 +02:00
wl_list_for_each(output, &root->all_outputs, link) {
if (!output->enabled && output != root->fallback_output) {
json_object_array_add(outputs,
ipc_json_describe_disabled_output(output));
}
}
for (int i = 0; i < root->non_desktop_outputs->length; i++) {
struct sway_output_non_desktop *non_desktop_output = root->non_desktop_outputs->items[i];
json_object_array_add(outputs, ipc_json_describe_non_desktop_output(non_desktop_output));
}
2017-12-18 14:06:03 +01:00
const char *json_string = json_object_to_json_string(outputs);
ipc_send_reply(client, payload_type, json_string,
(uint32_t)strlen(json_string));
2017-12-18 14:06:03 +01:00
json_object_put(outputs); // free
goto exit_cleanup;
}
case IPC_GET_WORKSPACES:
{
json_object *workspaces = json_object_new_array();
root_for_each_workspace(ipc_get_workspaces_callback, workspaces);
const char *json_string = json_object_to_json_string(workspaces);
ipc_send_reply(client, payload_type, json_string,
(uint32_t)strlen(json_string));
json_object_put(workspaces); // free
goto exit_cleanup;
}
2018-02-28 01:53:15 +01:00
case IPC_SUBSCRIBE:
{
// TODO: Check if they're permitted to use these events
struct json_object *request = json_tokener_parse(buf);
if (request == NULL || !json_object_is_type(request, json_type_array)) {
2018-11-28 22:51:49 +01:00
const char msg[] = "{\"success\": false}";
ipc_send_reply(client, payload_type, msg, strlen(msg));
sway_log(SWAY_INFO, "Failed to parse subscribe request");
2018-02-28 01:53:15 +01:00
goto exit_cleanup;
}
2018-07-18 13:30:39 +02:00
bool is_tick = false;
2018-02-28 01:53:15 +01:00
// parse requested event types
for (size_t i = 0; i < json_object_array_length(request); i++) {
const char *event_type = json_object_get_string(json_object_array_get_idx(request, i));
if (strcmp(event_type, "workspace") == 0) {
client->subscribed_events |= event_mask(IPC_EVENT_WORKSPACE);
} else if (strcmp(event_type, "output") == 0) {
client->subscribed_events |= event_mask(IPC_EVENT_OUTPUT);
2018-02-28 01:53:15 +01:00
} else if (strcmp(event_type, "barconfig_update") == 0) {
client->subscribed_events |= event_mask(IPC_EVENT_BARCONFIG_UPDATE);
} else if (strcmp(event_type, "bar_state_update") == 0) {
client->subscribed_events |= event_mask(IPC_EVENT_BAR_STATE_UPDATE);
2018-02-28 01:53:15 +01:00
} else if (strcmp(event_type, "mode") == 0) {
client->subscribed_events |= event_mask(IPC_EVENT_MODE);
2018-07-13 17:58:45 +02:00
} else if (strcmp(event_type, "shutdown") == 0) {
client->subscribed_events |= event_mask(IPC_EVENT_SHUTDOWN);
2018-02-28 01:53:15 +01:00
} else if (strcmp(event_type, "window") == 0) {
client->subscribed_events |= event_mask(IPC_EVENT_WINDOW);
} else if (strcmp(event_type, "binding") == 0) {
client->subscribed_events |= event_mask(IPC_EVENT_BINDING);
2018-07-18 13:30:39 +02:00
} else if (strcmp(event_type, "tick") == 0) {
client->subscribed_events |= event_mask(IPC_EVENT_TICK);
is_tick = true;
} else if (strcmp(event_type, "input") == 0) {
client->subscribed_events |= event_mask(IPC_EVENT_INPUT);
2018-02-28 01:53:15 +01:00
} else {
2018-11-28 22:51:49 +01:00
const char msg[] = "{\"success\": false}";
ipc_send_reply(client, payload_type, msg, strlen(msg));
2018-02-28 01:53:15 +01:00
json_object_put(request);
sway_log(SWAY_INFO, "Unsupported event type in subscribe request");
2018-02-28 01:53:15 +01:00
goto exit_cleanup;
}
}
json_object_put(request);
2018-11-28 22:51:49 +01:00
const char msg[] = "{\"success\": true}";
ipc_send_reply(client, payload_type, msg, strlen(msg));
2018-07-18 13:30:39 +02:00
if (is_tick) {
const char tickmsg[] = "{\"first\": true, \"payload\": \"\"}";
ipc_send_reply(client, IPC_EVENT_TICK, tickmsg,
strlen(tickmsg));
2018-07-18 13:30:39 +02:00
}
2018-02-28 01:53:15 +01:00
goto exit_cleanup;
}
2017-12-18 20:27:38 +01:00
case IPC_GET_INPUTS:
{
json_object *inputs = json_object_new_array();
struct sway_input_device *device = NULL;
wl_list_for_each(device, &server.input->devices, link) {
2017-12-18 20:27:38 +01:00
json_object_array_add(inputs, ipc_json_describe_input(device));
}
const char *json_string = json_object_to_json_string(inputs);
ipc_send_reply(client, payload_type, json_string,
(uint32_t)strlen(json_string));
2017-12-18 20:27:38 +01:00
json_object_put(inputs); // free
goto exit_cleanup;
}
2018-05-11 15:51:47 +02:00
case IPC_GET_SEATS:
{
json_object *seats = json_object_new_array();
struct sway_seat *seat = NULL;
wl_list_for_each(seat, &server.input->seats, link) {
2018-05-11 15:51:47 +02:00
json_object_array_add(seats, ipc_json_describe_seat(seat));
}
const char *json_string = json_object_to_json_string(seats);
ipc_send_reply(client, payload_type, json_string,
(uint32_t)strlen(json_string));
2018-05-11 15:51:47 +02:00
json_object_put(seats); // free
goto exit_cleanup;
}
2017-12-03 14:33:52 +01:00
case IPC_GET_TREE:
{
Implement type safe arguments and demote sway_container This commit changes the meaning of sway_container so that it only refers to layout containers and view containers. Workspaces, outputs and the root are no longer known as containers. Instead, root, outputs, workspaces and containers are all a type of node, and containers come in two types: layout containers and view containers. In addition to the above, this implements type safe variables. This means we use specific types such as sway_output and sway_workspace instead of generic containers or nodes. However, it's worth noting that in a few places places (eg. seat focus and transactions) referring to them in a generic way is unavoidable which is why we still use nodes in some places. If you want a TL;DR, look at node.h, as well as the struct definitions for root, output, workspace and container. Note that sway_output now contains a workspaces list, and workspaces now contain a tiling and floating list, and containers now contain a pointer back to the workspace. There are now functions for seat_get_focused_workspace and seat_get_focused_container. The latter will return NULL if a workspace itself is focused. Most other seat functions like seat_get_focus and seat_set_focus now accept and return nodes. In the config->handler_context struct, current_container has been replaced with three pointers: node, container and workspace. node is the same as what current_container was, while workspace is the workspace that the node resides on and container is the actual container, which may be NULL if a workspace itself is focused. The global root_container variable has been replaced with one simply called root, which is a pointer to the sway_root instance. The way outputs are created, enabled, disabled and destroyed has changed. Previously we'd wrap the sway_output in a container when it is enabled, but as we don't have containers any more it needs a different approach. The output_create and output_destroy functions previously created/destroyed the container, but now they create/destroy the sway_output. There is a new function output_disable to disable an output without destroying it. Containers have a new view property. If this is populated then the container is a view container, otherwise it's a layout container. Like before, this property is immutable for the life of the container. Containers have both a `sway_container *parent` and `sway_workspace *workspace`. As we use specific types now, parent cannot point to a workspace so it'll be NULL for containers which are direct children of the workspace. The workspace property is set for all containers, except those which are hidden in the scratchpad as they have no workspace. In some cases we need to refer to workspaces in a container-like way. For example, workspaces have layout and children, but when using specific types this makes it difficult. Likewise, it's difficult for a container to get its parent's layout when the parent could be another container or a workspace. To make it easier, some helper functions have been created: container_parent_layout and container_get_siblings. container_remove_child has been renamed to container_detach and container_replace_child has been renamed to container_replace. `container_handle_fullscreen_reparent(con, old_parent)` has had the old_parent removed. We now unfullscreen the workspace when detaching the container, so this function is simplified and only needs one argument now. container_notify_subtree_changed has been renamed to container_update_representation. This is more descriptive of its purpose. I also wanted to be able to call it with whatever container was changed rather than the container's parent, which makes bubbling up to the workspace easier. There are now state structs per node thing. ie. sway_output_state, sway_workspace_state and sway_container_state. The focus, move and layout commands have been completely refactored to work with the specific types. I considered making these a separate PR, but I'd be backporting my changes only to replace them again, and it's easier just to test everything at once.
2018-08-30 13:00:10 +02:00
json_object *tree = ipc_json_describe_node_recursive(&root->node);
2017-12-03 14:33:52 +01:00
const char *json_string = json_object_to_json_string(tree);
ipc_send_reply(client, payload_type, json_string,
(uint32_t)strlen(json_string));
2017-12-03 14:33:52 +01:00
json_object_put(tree);
goto exit_cleanup;
2017-11-23 03:37:07 +01:00
}
2018-05-22 23:35:39 +02:00
case IPC_GET_MARKS:
{
json_object *marks = json_object_new_array();
root_for_each_container(ipc_get_marks_callback, marks);
2018-05-22 23:35:39 +02:00
const char *json_string = json_object_to_json_string(marks);
ipc_send_reply(client, payload_type, json_string,
(uint32_t)strlen(json_string));
2018-05-22 23:35:39 +02:00
json_object_put(marks);
goto exit_cleanup;
}
2017-11-23 03:37:07 +01:00
case IPC_GET_VERSION:
{
json_object *version = ipc_json_get_version();
const char *json_string = json_object_to_json_string(version);
ipc_send_reply(client, payload_type, json_string,
(uint32_t)strlen(json_string));
2017-11-23 03:37:07 +01:00
json_object_put(version); // free
goto exit_cleanup;
}
case IPC_GET_BAR_CONFIG:
{
if (!buf[0]) {
// Send list of configured bar IDs
json_object *bars = json_object_new_array();
2018-03-30 04:23:41 +02:00
for (int i = 0; i < config->bars->length; ++i) {
struct bar_config *bar = config->bars->items[i];
json_object_array_add(bars, json_object_new_string(bar->id));
}
const char *json_string = json_object_to_json_string(bars);
ipc_send_reply(client, payload_type, json_string,
(uint32_t)strlen(json_string));
json_object_put(bars); // free
} else {
// Send particular bar's details
struct bar_config *bar = NULL;
2018-03-30 04:23:41 +02:00
for (int i = 0; i < config->bars->length; ++i) {
bar = config->bars->items[i];
if (strcmp(buf, bar->id) == 0) {
break;
}
bar = NULL;
}
if (!bar) {
const char *error = "{ \"success\": false, \"error\": \"No bar with that ID\" }";
ipc_send_reply(client, payload_type, error,
(uint32_t)strlen(error));
goto exit_cleanup;
}
json_object *json = ipc_json_describe_bar_config(bar);
const char *json_string = json_object_to_json_string(json);
ipc_send_reply(client, payload_type, json_string,
(uint32_t)strlen(json_string));
json_object_put(json); // free
}
goto exit_cleanup;
}
case IPC_GET_BINDING_MODES:
{
json_object *modes = json_object_new_array();
for (int i = 0; i < config->modes->length; i++) {
struct sway_mode *mode = config->modes->items[i];
json_object_array_add(modes, json_object_new_string(mode->name));
}
const char *json_string = json_object_to_json_string(modes);
ipc_send_reply(client, payload_type, json_string,
(uint32_t)strlen(json_string));
json_object_put(modes); // free
goto exit_cleanup;
}
case IPC_GET_BINDING_STATE:
{
json_object *current_mode = ipc_json_get_binding_mode();
const char *json_string = json_object_to_json_string(current_mode);
ipc_send_reply(client, payload_type, json_string,
(uint32_t)strlen(json_string));
json_object_put(current_mode); // free
goto exit_cleanup;
}
2018-07-08 21:34:47 +02:00
case IPC_GET_CONFIG:
{
json_object *json = json_object_new_object();
json_object_object_add(json, "config", json_object_new_string(config->current_config));
const char *json_string = json_object_to_json_string(json);
ipc_send_reply(client, payload_type, json_string,
(uint32_t)strlen(json_string));
2018-07-08 21:34:47 +02:00
json_object_put(json); // free
goto exit_cleanup;
}
2018-07-08 21:34:47 +02:00
case IPC_SYNC:
{
// It was decided sway will not support this, just return success:false
const char msg[] = "{\"success\": false}";
ipc_send_reply(client, payload_type, msg, strlen(msg));
goto exit_cleanup;
}
2017-11-23 03:37:07 +01:00
default:
sway_log(SWAY_INFO, "Unknown IPC command type %x", payload_type);
2017-11-23 03:37:07 +01:00
goto exit_cleanup;
}
exit_cleanup:
free(buf);
}
bool ipc_send_reply(struct ipc_client *client, enum ipc_command_type payload_type,
const char *payload, uint32_t payload_length) {
2017-11-23 03:37:07 +01:00
assert(payload);
2019-01-16 10:57:51 +01:00
char data[IPC_HEADER_SIZE];
2017-11-23 03:37:07 +01:00
memcpy(data, ipc_magic, sizeof(ipc_magic));
memcpy(data + sizeof(ipc_magic), &payload_length, sizeof(payload_length));
memcpy(data + sizeof(ipc_magic) + sizeof(payload_length), &payload_type, sizeof(payload_type));
2017-11-23 03:37:07 +01:00
2019-01-16 10:57:51 +01:00
while (client->write_buffer_len + IPC_HEADER_SIZE + payload_length >=
2017-11-23 03:37:07 +01:00
client->write_buffer_size) {
client->write_buffer_size *= 2;
}
if (client->write_buffer_size > 4e6) { // 4 MB
2020-02-29 19:49:54 +01:00
sway_log(SWAY_ERROR, "Client write buffer too big (%zu), disconnecting client",
client->write_buffer_size);
2017-11-23 03:37:07 +01:00
ipc_client_disconnect(client);
return false;
}
char *new_buffer = realloc(client->write_buffer, client->write_buffer_size);
if (!new_buffer) {
sway_log(SWAY_ERROR, "Unable to reallocate ipc client write buffer");
2017-11-23 03:37:07 +01:00
ipc_client_disconnect(client);
return false;
}
client->write_buffer = new_buffer;
2019-01-16 10:57:51 +01:00
memcpy(client->write_buffer + client->write_buffer_len, data, IPC_HEADER_SIZE);
client->write_buffer_len += IPC_HEADER_SIZE;
2017-11-23 03:37:07 +01:00
memcpy(client->write_buffer + client->write_buffer_len, payload, payload_length);
client->write_buffer_len += payload_length;
if (!client->writable_event_source) {
client->writable_event_source = wl_event_loop_add_fd(
server.wl_event_loop, client->fd, WL_EVENT_WRITABLE,
ipc_client_handle_writable, client);
}
return true;
}