ipc: added backward-compatible I3SOCK ipc

Signed-off-by: Franklin "Snaipe" Mathieu <me@snai.pe>
This commit is contained in:
Franklin "Snaipe" Mathieu 2018-10-28 16:38:07 +00:00
parent 822b5897ad
commit 3a890efe18
4 changed files with 145 additions and 34 deletions

View file

@ -10,7 +10,7 @@ struct sway_server;
void ipc_init(struct sway_server *server); void ipc_init(struct sway_server *server);
struct sockaddr_un *ipc_user_sockaddr(void); struct sockaddr_un *ipc_user_sockaddr(const char *suffix);
void ipc_event_workspace(struct sway_workspace *old, void ipc_event_workspace(struct sway_workspace *old,
struct sway_workspace *new, const char *change); struct sway_workspace *new, const char *change);
@ -50,5 +50,6 @@ struct ipc_client_impl {
}; };
extern const struct ipc_client_impl ipc_client_sway; extern const struct ipc_client_impl ipc_client_sway;
extern const struct ipc_client_impl ipc_client_i3;
#endif #endif

85
sway/ipc-i3.c Normal file
View file

@ -0,0 +1,85 @@
#include <json-c/json.h>
#include "sway/config.h"
#include "sway/ipc-json.h"
#include "sway/ipc-sway.h"
#include "sway/tree/view.h"
static void ipc_i3_json_describe_view(struct sway_container *c, json_object *object) {
const char *app_id = view_get_app_id(c->view);
const char *class = app_id;
const char *instance = app_id;
const char *role = NULL;
uint32_t parent_id = 0;
uint32_t window_id = 0;
#if HAVE_XWAYLAND
if (c->view->type == SWAY_VIEW_XWAYLAND) {
class = view_get_class(c->view);
instance = view_get_instance(c->view);
role = view_get_window_role(c->view);
parent_id = view_get_x11_parent_id(c->view);
window_id = view_get_x11_window_id(c->view);
}
#endif
json_object_object_add(object, "window", json_object_new_int(window_id));
json_object *window_props = json_object_new_object();
if (class) {
json_object_object_add(window_props, "class", json_object_new_string(class));
}
if (instance) {
json_object_object_add(window_props, "instance", json_object_new_string(instance));
}
if (c->title) {
json_object_object_add(window_props, "title", json_object_new_string(c->title));
}
if (role) {
json_object_object_add(window_props, "window_role", json_object_new_string(role));
}
json_object_object_add(window_props, "transient_for",
parent_id ? json_object_new_int(parent_id) : NULL);
json_object_object_add(object, "window_properties", window_props);
}
static void ipc_i3_json_describe_container(struct sway_node *node, json_object *object) {
struct sway_container *c = node->sway_container;
ipc_json_describe_container(node->sway_container, object);
if (c->view) {
ipc_json_describe_view_common(node->sway_container, object);
ipc_i3_json_describe_view(node->sway_container, object);
}
}
const ipc_json_descriptor_map ipc_json_i3_descriptors = {
[N_ROOT] = ipc_json_describe_root,
[N_WORKSPACE] = ipc_json_describe_workspace,
[N_OUTPUT] = ipc_json_describe_output,
[N_CONTAINER] = ipc_i3_json_describe_container,
};
static json_object *ipc_i3_get_tree(struct ipc_client *client,
enum ipc_command_type *type, char *buf) {
return ipc_json_describe_node_recursive(&root->node, &ipc_json_i3_descriptors);
}
static const ipc_handler i3_commands[] = {
[IPC_COMMAND] = ipc_sway_command,
[IPC_GET_BAR_CONFIG] = ipc_sway_get_bar_config,
[IPC_GET_BINDING_MODES] = ipc_sway_get_binding_modes,
[IPC_GET_CONFIG] = ipc_sway_get_config,
[IPC_GET_MARKS] = ipc_sway_get_marks,
[IPC_GET_OUTPUTS] = ipc_sway_get_outputs,
[IPC_GET_TREE] = ipc_i3_get_tree,
[IPC_GET_VERSION] = ipc_sway_get_version,
[IPC_GET_WORKSPACES] = ipc_sway_get_workspaces,
[IPC_SEND_TICK] = ipc_sway_send_tick,
[IPC_SUBSCRIBE] = ipc_sway_subscribe,
};
const struct ipc_client_impl ipc_client_i3 = {
.num_commands = sizeof (i3_commands) / sizeof (*i3_commands),
.commands = i3_commands,
};

View file

@ -30,9 +30,15 @@
#include "log.h" #include "log.h"
#include "util.h" #include "util.h"
static int ipc_socket = -1; struct ipc {
static struct wl_event_source *ipc_event_source = NULL; int socket;
static struct sockaddr_un *ipc_sockaddr = NULL; struct wl_event_source *event_source;
struct sockaddr_un *sockaddr;
};
static struct ipc sway_ipc = { .socket = -1 };
static struct ipc i3_ipc = { .socket = -1 };
static list_t *ipc_client_list = NULL; static list_t *ipc_client_list = NULL;
static struct wl_listener ipc_display_destroy; static struct wl_listener ipc_display_destroy;
@ -40,7 +46,6 @@ static const char ipc_magic[] = {'i', '3', '-', 'i', 'p', 'c'};
#define IPC_HEADER_SIZE (sizeof(ipc_magic) + 8) #define IPC_HEADER_SIZE (sizeof(ipc_magic) + 8)
struct sockaddr_un *ipc_user_sockaddr(void);
int ipc_handle_connection(int fd, uint32_t mask, void *data); 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_readable(int client_fd, uint32_t mask, void *data);
int ipc_client_handle_writable(int client_fd, uint32_t mask, void *data); int ipc_client_handle_writable(int client_fd, uint32_t mask, void *data);
@ -48,66 +53,77 @@ void ipc_client_disconnect(struct ipc_client *client);
void ipc_client_handle_command(struct ipc_client *client, uint32_t payload_length, void ipc_client_handle_command(struct ipc_client *client, uint32_t payload_length,
enum ipc_command_type payload_type); enum ipc_command_type payload_type);
static void handle_display_destroy(struct wl_listener *listener, void *data) { static void ipc_destroy(struct ipc *ipc) {
if (ipc_event_source) { if (ipc->event_source) {
wl_event_source_remove(ipc_event_source); wl_event_source_remove(ipc->event_source);
} }
close(ipc_socket); close(ipc->socket);
unlink(ipc_sockaddr->sun_path); unlink(ipc->sockaddr->sun_path);
}
static void handle_display_destroy(struct wl_listener *listener, void *data) {
ipc_destroy(&sway_ipc);
ipc_destroy(&i3_ipc);
while (ipc_client_list->length) { while (ipc_client_list->length) {
ipc_client_disconnect(ipc_client_list->items[ipc_client_list->length-1]); ipc_client_disconnect(ipc_client_list->items[ipc_client_list->length-1]);
} }
list_free(ipc_client_list); list_free(ipc_client_list);
free(ipc_sockaddr); free(sway_ipc.sockaddr);
free(i3_ipc.sockaddr);
wl_list_remove(&ipc_display_destroy.link); wl_list_remove(&ipc_display_destroy.link);
} }
void ipc_init(struct sway_server *server) { static void ipc_init_socket(struct sway_server *server, struct ipc *ipc,
ipc_socket = socket(AF_UNIX, SOCK_STREAM, 0); const char *envvar, const char *sockpath_suffix) {
if (ipc_socket == -1) {
ipc->sockaddr = ipc_user_sockaddr(sockpath_suffix);
ipc->socket = socket(AF_UNIX, SOCK_STREAM, 0);
if (ipc->socket == -1) {
sway_abort("Unable to create IPC socket"); sway_abort("Unable to create IPC socket");
} }
if (fcntl(ipc_socket, F_SETFD, FD_CLOEXEC) == -1) { if (fcntl(ipc->socket, F_SETFD, FD_CLOEXEC) == -1) {
sway_abort("Unable to set CLOEXEC on IPC socket"); sway_abort("Unable to set CLOEXEC on IPC socket");
} }
if (fcntl(ipc_socket, F_SETFL, O_NONBLOCK) == -1) { if (fcntl(ipc->socket, F_SETFL, O_NONBLOCK) == -1) {
sway_abort("Unable to set NONBLOCK on IPC socket"); sway_abort("Unable to set NONBLOCK on IPC socket");
} }
ipc_sockaddr = ipc_user_sockaddr();
// We want to use socket name set by user, not existing socket from another sway instance. // 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) { if (getenv(envvar) != NULL && access(getenv(envvar), F_OK) == -1) {
strncpy(ipc_sockaddr->sun_path, getenv("SWAYSOCK"), sizeof(ipc_sockaddr->sun_path) - 1); strncpy(ipc->sockaddr->sun_path, getenv(envvar), sizeof(ipc->sockaddr->sun_path) - 1);
ipc_sockaddr->sun_path[sizeof(ipc_sockaddr->sun_path) - 1] = 0; ipc->sockaddr->sun_path[sizeof(ipc->sockaddr->sun_path) - 1] = 0;
} }
unlink(ipc_sockaddr->sun_path); unlink(ipc->sockaddr->sun_path);
if (bind(ipc_socket, (struct sockaddr *)ipc_sockaddr, sizeof(*ipc_sockaddr)) == -1) { if (bind(ipc->socket, (struct sockaddr *)ipc->sockaddr, sizeof(*ipc->sockaddr)) == -1) {
sway_abort("Unable to bind IPC socket"); sway_abort("Unable to bind IPC socket");
} }
if (listen(ipc_socket, 3) == -1) { if (listen(ipc->socket, 3) == -1) {
sway_abort("Unable to listen on IPC socket"); sway_abort("Unable to listen on IPC socket");
} }
// Set i3 IPC socket path so that i3-msg works out of the box setenv(envvar, ipc->sockaddr->sun_path, 1);
setenv("I3SOCK", ipc_sockaddr->sun_path, 1);
setenv("SWAYSOCK", ipc_sockaddr->sun_path, 1);
ipc->event_source = wl_event_loop_add_fd(server->wl_event_loop, ipc->socket,
WL_EVENT_READABLE, ipc_handle_connection, server);
}
void ipc_init(struct sway_server *server) {
ipc_client_list = create_list(); ipc_client_list = create_list();
ipc_display_destroy.notify = handle_display_destroy; ipc_display_destroy.notify = handle_display_destroy;
wl_display_add_destroy_listener(server->wl_display, &ipc_display_destroy); wl_display_add_destroy_listener(server->wl_display, &ipc_display_destroy);
ipc_event_source = wl_event_loop_add_fd(server->wl_event_loop, ipc_socket, ipc_init_socket(server, &sway_ipc, "SWAYSOCK", "");
WL_EVENT_READABLE, ipc_handle_connection, server); // Set i3 IPC socket path so that i3-msg works out of the box
ipc_init_socket(server, &i3_ipc, "I3SOCK", ".i3");
} }
struct sockaddr_un *ipc_user_sockaddr(void) { struct sockaddr_un *ipc_user_sockaddr(const char *suffix) {
struct sockaddr_un *ipc_sockaddr = malloc(sizeof(struct sockaddr_un)); struct sockaddr_un *ipc_sockaddr = malloc(sizeof(struct sockaddr_un));
if (ipc_sockaddr == NULL) { if (ipc_sockaddr == NULL) {
sway_abort("Can't allocate ipc_sockaddr"); sway_abort("Can't allocate ipc_sockaddr");
@ -122,7 +138,7 @@ struct sockaddr_un *ipc_user_sockaddr(void) {
dir = "/tmp"; dir = "/tmp";
} }
if (path_size <= snprintf(ipc_sockaddr->sun_path, path_size, if (path_size <= snprintf(ipc_sockaddr->sun_path, path_size,
"%s/sway-ipc.%i.%i.sock", dir, getuid(), getpid())) { "%s/sway-ipc%s.%i.%i.sock", dir, suffix, getuid(), getpid())) {
sway_abort("Socket path won't fit into ipc_sockaddr->sun_path"); sway_abort("Socket path won't fit into ipc_sockaddr->sun_path");
} }
@ -130,12 +146,11 @@ struct sockaddr_un *ipc_user_sockaddr(void) {
} }
int ipc_handle_connection(int fd, uint32_t mask, void *data) { int ipc_handle_connection(int fd, uint32_t mask, void *data) {
(void) fd;
struct sway_server *server = data; struct sway_server *server = data;
sway_log(SWAY_DEBUG, "Event on IPC listening socket"); sway_log(SWAY_DEBUG, "Event on IPC listening socket");
assert(mask == WL_EVENT_READABLE); assert(mask == WL_EVENT_READABLE);
int client_fd = accept(ipc_socket, NULL, NULL); int client_fd = accept(fd, NULL, NULL);
if (client_fd == -1) { if (client_fd == -1) {
sway_log_errno(SWAY_ERROR, "Unable to accept IPC client connection"); sway_log_errno(SWAY_ERROR, "Unable to accept IPC client connection");
return 0; return 0;
@ -177,7 +192,16 @@ int ipc_handle_connection(int fd, uint32_t mask, void *data) {
close(client_fd); close(client_fd);
return 0; return 0;
} }
client->impl = &ipc_client_sway;
if (fd == sway_ipc.socket) {
client->impl = &ipc_client_sway;
} else if (fd == i3_ipc.socket) {
client->impl = &ipc_client_i3;
} else {
sway_log(SWAY_ERROR, "Got connection event from unknown source");
close(client_fd);
return 0;
}
sway_log(SWAY_DEBUG, "New client: fd %d", client_fd); sway_log(SWAY_DEBUG, "New client: fd %d", client_fd);
list_add(ipc_client_list, client); list_add(ipc_client_list, client);

View file

@ -3,6 +3,7 @@ sway_sources = files(
'config.c', 'config.c',
'criteria.c', 'criteria.c',
'decoration.c', 'decoration.c',
'ipc-i3.c',
'ipc-json.c', 'ipc-json.c',
'ipc-server.c', 'ipc-server.c',
'ipc-sway.c', 'ipc-sway.c',