mirror of
https://github.com/swaywm/sway.git
synced 2026-04-27 06:46:25 -04:00
ipc: refactor ipc handlers into ipc-sway.c
This is preparative work for the backward-compatible I3SOCK implementation. Signed-off-by: Franklin "Snaipe" Mathieu <me@snai.pe>
This commit is contained in:
parent
5fb5984e94
commit
91c1a52595
9 changed files with 371 additions and 315 deletions
|
|
@ -1,6 +1,7 @@
|
||||||
#ifndef _SWAY_COMMANDS_H
|
#ifndef _SWAY_COMMANDS_H
|
||||||
#define _SWAY_COMMANDS_H
|
#define _SWAY_COMMANDS_H
|
||||||
|
|
||||||
|
#include <json-c/json.h>
|
||||||
#include <wlr/util/edges.h>
|
#include <wlr/util/edges.h>
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
|
|
@ -83,9 +84,9 @@ void free_cmd_results(struct cmd_results *results);
|
||||||
/**
|
/**
|
||||||
* Serializes a list of cmd_results to a JSON string.
|
* Serializes a list of cmd_results to a JSON string.
|
||||||
*
|
*
|
||||||
* Free the JSON string later on.
|
* Free the JSON object later on.
|
||||||
*/
|
*/
|
||||||
char *cmd_results_to_json(list_t *res_list);
|
json_object *cmd_results_to_json(list_t *res_list);
|
||||||
|
|
||||||
struct cmd_results *add_color(char *buffer, const char *color);
|
struct cmd_results *add_color(char *buffer, const char *color);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,9 @@
|
||||||
#include "sway/tree/container.h"
|
#include "sway/tree/container.h"
|
||||||
#include "sway/input/input-manager.h"
|
#include "sway/input/input-manager.h"
|
||||||
|
|
||||||
|
json_object *ipc_json_success(void);
|
||||||
|
json_object *ipc_json_failure(const char *error);
|
||||||
|
|
||||||
json_object *ipc_json_get_version(void);
|
json_object *ipc_json_get_version(void);
|
||||||
|
|
||||||
json_object *ipc_json_describe_disabled_output(struct sway_output *o);
|
json_object *ipc_json_describe_disabled_output(struct sway_output *o);
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
#ifndef _SWAY_IPC_SERVER_H
|
#ifndef _SWAY_IPC_SERVER_H
|
||||||
#define _SWAY_IPC_SERVER_H
|
#define _SWAY_IPC_SERVER_H
|
||||||
|
#include <json-c/json.h>
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#include "sway/config.h"
|
#include "sway/config.h"
|
||||||
#include "sway/tree/container.h"
|
#include "sway/tree/container.h"
|
||||||
|
|
@ -19,5 +20,35 @@ void ipc_event_bar_state_update(struct bar_config *bar);
|
||||||
void ipc_event_mode(const char *mode, bool pango);
|
void ipc_event_mode(const char *mode, bool pango);
|
||||||
void ipc_event_shutdown(const char *reason);
|
void ipc_event_shutdown(const char *reason);
|
||||||
void ipc_event_binding(struct sway_binding *binding);
|
void ipc_event_binding(struct sway_binding *binding);
|
||||||
|
void ipc_event_tick(const char *payload);
|
||||||
|
|
||||||
|
struct ipc_client {
|
||||||
|
struct wl_event_source *event_source;
|
||||||
|
struct wl_event_source *writable_event_source;
|
||||||
|
struct sway_server *server;
|
||||||
|
int fd;
|
||||||
|
uint32_t security_policy;
|
||||||
|
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;
|
||||||
|
const struct ipc_client_impl *impl;
|
||||||
|
};
|
||||||
|
|
||||||
|
bool ipc_send_reply(struct ipc_client *client, enum ipc_command_type payload_type,
|
||||||
|
const char *payload, uint32_t payload_length);
|
||||||
|
|
||||||
|
typedef json_object *(*ipc_handler)(struct ipc_client *client,
|
||||||
|
enum ipc_command_type *type, char *data);
|
||||||
|
|
||||||
|
struct ipc_client_impl {
|
||||||
|
size_t num_commands;
|
||||||
|
const ipc_handler *commands;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern const struct ipc_client_impl ipc_client_sway;
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
34
include/sway/ipc-sway.h
Normal file
34
include/sway/ipc-sway.h
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
#ifndef _SWAY_IPC_SWAY_H
|
||||||
|
#define _SWAY_IPC_SWAY_H
|
||||||
|
|
||||||
|
#include <json-c/json.h>
|
||||||
|
#include "sway/ipc-server.h"
|
||||||
|
|
||||||
|
json_object *ipc_sway_command(struct ipc_client *client,
|
||||||
|
enum ipc_command_type *type, char *buf);
|
||||||
|
json_object *ipc_sway_get_bar_config(struct ipc_client *client,
|
||||||
|
enum ipc_command_type *type, char *buf);
|
||||||
|
json_object *ipc_sway_get_binding_modes(struct ipc_client *client,
|
||||||
|
enum ipc_command_type *type, char *buf);
|
||||||
|
json_object *ipc_sway_get_config(struct ipc_client *client,
|
||||||
|
enum ipc_command_type *type, char *buf);
|
||||||
|
json_object *ipc_sway_get_inputs(struct ipc_client *client,
|
||||||
|
enum ipc_command_type *type, char *buf);
|
||||||
|
json_object *ipc_sway_get_marks(struct ipc_client *client,
|
||||||
|
enum ipc_command_type *type, char *buf);
|
||||||
|
json_object *ipc_sway_get_outputs(struct ipc_client *client,
|
||||||
|
enum ipc_command_type *type, char *buf);
|
||||||
|
json_object *ipc_sway_get_seats(struct ipc_client *client,
|
||||||
|
enum ipc_command_type *type, char *buf);
|
||||||
|
json_object *ipc_sway_get_tree(struct ipc_client *client,
|
||||||
|
enum ipc_command_type *type, char *buf);
|
||||||
|
json_object *ipc_sway_get_version(struct ipc_client *client,
|
||||||
|
enum ipc_command_type *type, char *buf);
|
||||||
|
json_object *ipc_sway_get_workspaces(struct ipc_client *client,
|
||||||
|
enum ipc_command_type *type, char *buf);
|
||||||
|
json_object *ipc_sway_send_tick(struct ipc_client *client,
|
||||||
|
enum ipc_command_type *type, char *buf);
|
||||||
|
json_object *ipc_sway_subscribe(struct ipc_client *client,
|
||||||
|
enum ipc_command_type *type, char *buf);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -537,7 +537,7 @@ void free_cmd_results(struct cmd_results *results) {
|
||||||
free(results);
|
free(results);
|
||||||
}
|
}
|
||||||
|
|
||||||
char *cmd_results_to_json(list_t *res_list) {
|
json_object *cmd_results_to_json(list_t *res_list) {
|
||||||
json_object *result_array = json_object_new_array();
|
json_object *result_array = json_object_new_array();
|
||||||
for (int i = 0; i < res_list->length; ++i) {
|
for (int i = 0; i < res_list->length; ++i) {
|
||||||
struct cmd_results *results = res_list->items[i];
|
struct cmd_results *results = res_list->items[i];
|
||||||
|
|
@ -552,10 +552,7 @@ char *cmd_results_to_json(list_t *res_list) {
|
||||||
}
|
}
|
||||||
json_object_array_add(result_array, root);
|
json_object_array_add(result_array, root);
|
||||||
}
|
}
|
||||||
const char *json = json_object_to_json_string(result_array);
|
return result_array;
|
||||||
char *res = strdup(json);
|
|
||||||
json_object_put(result_array);
|
|
||||||
return res;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,21 @@
|
||||||
static const int i3_output_id = INT32_MAX;
|
static const int i3_output_id = INT32_MAX;
|
||||||
static const int i3_scratch_id = INT32_MAX - 1;
|
static const int i3_scratch_id = INT32_MAX - 1;
|
||||||
|
|
||||||
|
json_object *ipc_json_success(void) {
|
||||||
|
json_object *reply = json_object_new_object();
|
||||||
|
json_object_object_add(reply, "success", json_object_new_boolean(true));
|
||||||
|
return reply;
|
||||||
|
}
|
||||||
|
|
||||||
|
json_object *ipc_json_failure(const char *error) {
|
||||||
|
json_object *reply = json_object_new_object();
|
||||||
|
json_object_object_add(reply, "success", json_object_new_boolean(false));
|
||||||
|
if (error) {
|
||||||
|
json_object_object_add(reply, "error", json_object_new_string(error));
|
||||||
|
}
|
||||||
|
return reply;
|
||||||
|
}
|
||||||
|
|
||||||
static const char *ipc_json_layout_description(enum sway_container_layout l) {
|
static const char *ipc_json_layout_description(enum sway_container_layout l) {
|
||||||
switch (l) {
|
switch (l) {
|
||||||
case L_VERT:
|
case L_VERT:
|
||||||
|
|
|
||||||
|
|
@ -15,12 +15,9 @@
|
||||||
#include <sys/un.h>
|
#include <sys/un.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <wayland-server.h>
|
#include <wayland-server.h>
|
||||||
#include "sway/commands.h"
|
|
||||||
#include "sway/config.h"
|
#include "sway/config.h"
|
||||||
#include "sway/desktop/transaction.h"
|
|
||||||
#include "sway/ipc-json.h"
|
#include "sway/ipc-json.h"
|
||||||
#include "sway/ipc-server.h"
|
#include "sway/ipc-server.h"
|
||||||
#include "sway/output.h"
|
|
||||||
#include "sway/server.h"
|
#include "sway/server.h"
|
||||||
#include "sway/input/input-manager.h"
|
#include "sway/input/input-manager.h"
|
||||||
#include "sway/input/keyboard.h"
|
#include "sway/input/keyboard.h"
|
||||||
|
|
@ -42,21 +39,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 ipc_client {
|
|
||||||
struct wl_event_source *event_source;
|
|
||||||
struct wl_event_source *writable_event_source;
|
|
||||||
struct sway_server *server;
|
|
||||||
int fd;
|
|
||||||
uint32_t security_policy;
|
|
||||||
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;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct sockaddr_un *ipc_user_sockaddr(void);
|
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);
|
||||||
|
|
@ -64,8 +46,6 @@ int ipc_client_handle_writable(int client_fd, uint32_t mask, void *data);
|
||||||
void ipc_client_disconnect(struct ipc_client *client);
|
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);
|
||||||
bool ipc_send_reply(struct ipc_client *client, enum ipc_command_type payload_type,
|
|
||||||
const char *payload, uint32_t payload_length);
|
|
||||||
|
|
||||||
static void handle_display_destroy(struct wl_listener *listener, void *data) {
|
static void handle_display_destroy(struct wl_listener *listener, void *data) {
|
||||||
if (ipc_event_source) {
|
if (ipc_event_source) {
|
||||||
|
|
@ -196,6 +176,7 @@ 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;
|
||||||
|
|
||||||
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);
|
||||||
|
|
@ -472,7 +453,7 @@ void ipc_event_binding(struct sway_binding *binding) {
|
||||||
json_object_put(json);
|
json_object_put(json);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ipc_event_tick(const char *payload) {
|
void ipc_event_tick(const char *payload) {
|
||||||
if (!ipc_has_event_listeners(IPC_EVENT_TICK)) {
|
if (!ipc_has_event_listeners(IPC_EVENT_TICK)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -551,33 +532,6 @@ void ipc_client_disconnect(struct ipc_client *client) {
|
||||||
free(client);
|
free(client);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ipc_get_workspaces_callback(struct sway_workspace *workspace,
|
|
||||||
void *data) {
|
|
||||||
json_object *workspace_json = ipc_json_describe_node(&workspace->node);
|
|
||||||
// override the default focused indicator because
|
|
||||||
// it's set differently for the get_workspaces reply
|
|
||||||
struct sway_seat *seat = input_manager_get_default_seat();
|
|
||||||
struct sway_workspace *focused_ws = seat_get_focused_workspace(seat);
|
|
||||||
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);
|
|
||||||
|
|
||||||
focused_ws = output_get_active_workspace(workspace->output);
|
|
||||||
bool visible = workspace == focused_ws;
|
|
||||||
json_object_object_add(workspace_json, "visible",
|
|
||||||
json_object_new_boolean(visible));
|
|
||||||
}
|
|
||||||
|
|
||||||
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));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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) {
|
||||||
if (!sway_assert(client != NULL, "client != NULL")) {
|
if (!sway_assert(client != NULL, "client != NULL")) {
|
||||||
|
|
@ -603,275 +557,23 @@ void ipc_client_handle_command(struct ipc_client *client, uint32_t payload_lengt
|
||||||
}
|
}
|
||||||
buf[payload_length] = '\0';
|
buf[payload_length] = '\0';
|
||||||
|
|
||||||
switch (payload_type) {
|
ipc_handler handler = NULL;
|
||||||
case IPC_COMMAND:
|
if (payload_type >= 0 && (size_t) payload_type < client->impl->num_commands) {
|
||||||
{
|
handler = client->impl->commands[payload_type];
|
||||||
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);
|
|
||||||
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);
|
|
||||||
goto exit_cleanup;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
case IPC_SEND_TICK:
|
if (handler) {
|
||||||
{
|
json_object *json = handler(client, &payload_type, buf);
|
||||||
ipc_event_tick(buf);
|
if (json) {
|
||||||
ipc_send_reply(client, payload_type, "{\"success\": true}", 17);
|
|
||||||
goto exit_cleanup;
|
|
||||||
}
|
|
||||||
|
|
||||||
case IPC_GET_OUTPUTS:
|
|
||||||
{
|
|
||||||
json_object *outputs = json_object_new_array();
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
struct sway_output *output;
|
|
||||||
wl_list_for_each(output, &root->all_outputs, link) {
|
|
||||||
if (!output->enabled && output != root->noop_output) {
|
|
||||||
json_object_array_add(outputs,
|
|
||||||
ipc_json_describe_disabled_output(output));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const char *json_string = json_object_to_json_string(outputs);
|
|
||||||
ipc_send_reply(client, payload_type, json_string,
|
|
||||||
(uint32_t)strlen(json_string));
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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)) {
|
|
||||||
const char msg[] = "{\"success\": false}";
|
|
||||||
ipc_send_reply(client, payload_type, msg, strlen(msg));
|
|
||||||
sway_log(SWAY_INFO, "Failed to parse subscribe request");
|
|
||||||
goto exit_cleanup;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool is_tick = false;
|
|
||||||
// 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, "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);
|
|
||||||
} else if (strcmp(event_type, "mode") == 0) {
|
|
||||||
client->subscribed_events |= event_mask(IPC_EVENT_MODE);
|
|
||||||
} else if (strcmp(event_type, "shutdown") == 0) {
|
|
||||||
client->subscribed_events |= event_mask(IPC_EVENT_SHUTDOWN);
|
|
||||||
} 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);
|
|
||||||
} else if (strcmp(event_type, "tick") == 0) {
|
|
||||||
client->subscribed_events |= event_mask(IPC_EVENT_TICK);
|
|
||||||
is_tick = true;
|
|
||||||
} else {
|
|
||||||
const char msg[] = "{\"success\": false}";
|
|
||||||
ipc_send_reply(client, payload_type, msg, strlen(msg));
|
|
||||||
json_object_put(request);
|
|
||||||
sway_log(SWAY_INFO, "Unsupported event type in subscribe request");
|
|
||||||
goto exit_cleanup;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
json_object_put(request);
|
|
||||||
const char msg[] = "{\"success\": true}";
|
|
||||||
ipc_send_reply(client, payload_type, msg, strlen(msg));
|
|
||||||
if (is_tick) {
|
|
||||||
const char tickmsg[] = "{\"first\": true, \"payload\": \"\"}";
|
|
||||||
ipc_send_reply(client, IPC_EVENT_TICK, tickmsg,
|
|
||||||
strlen(tickmsg));
|
|
||||||
}
|
|
||||||
goto exit_cleanup;
|
|
||||||
}
|
|
||||||
|
|
||||||
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) {
|
|
||||||
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));
|
|
||||||
json_object_put(inputs); // free
|
|
||||||
goto exit_cleanup;
|
|
||||||
}
|
|
||||||
|
|
||||||
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) {
|
|
||||||
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));
|
|
||||||
json_object_put(seats); // free
|
|
||||||
goto exit_cleanup;
|
|
||||||
}
|
|
||||||
|
|
||||||
case IPC_GET_TREE:
|
|
||||||
{
|
|
||||||
json_object *tree = ipc_json_describe_node_recursive(&root->node);
|
|
||||||
const char *json_string = json_object_to_json_string(tree);
|
|
||||||
ipc_send_reply(client, payload_type, json_string,
|
|
||||||
(uint32_t)strlen(json_string));
|
|
||||||
json_object_put(tree);
|
|
||||||
goto exit_cleanup;
|
|
||||||
}
|
|
||||||
|
|
||||||
case IPC_GET_MARKS:
|
|
||||||
{
|
|
||||||
json_object *marks = json_object_new_array();
|
|
||||||
root_for_each_container(ipc_get_marks_callback, marks);
|
|
||||||
const char *json_string = json_object_to_json_string(marks);
|
|
||||||
ipc_send_reply(client, payload_type, json_string,
|
|
||||||
(uint32_t)strlen(json_string));
|
|
||||||
json_object_put(marks);
|
|
||||||
goto exit_cleanup;
|
|
||||||
}
|
|
||||||
|
|
||||||
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));
|
|
||||||
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();
|
|
||||||
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;
|
|
||||||
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);
|
const char *json_string = json_object_to_json_string(json);
|
||||||
ipc_send_reply(client, payload_type, json_string,
|
ipc_send_reply(client, payload_type, json_string,
|
||||||
(uint32_t)strlen(json_string));
|
(uint32_t)strlen(json_string));
|
||||||
json_object_put(json); // free
|
json_object_put(json);
|
||||||
}
|
}
|
||||||
goto exit_cleanup;
|
} else {
|
||||||
}
|
|
||||||
|
|
||||||
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_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));
|
|
||||||
json_object_put(json); // free
|
|
||||||
goto exit_cleanup;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
sway_log(SWAY_INFO, "Unknown IPC command type %x", payload_type);
|
sway_log(SWAY_INFO, "Unknown IPC command type %x", payload_type);
|
||||||
goto exit_cleanup;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
exit_cleanup:
|
|
||||||
free(buf);
|
free(buf);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
272
sway/ipc-sway.c
Normal file
272
sway/ipc-sway.c
Normal file
|
|
@ -0,0 +1,272 @@
|
||||||
|
#include <json-c/json.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include "sway/commands.h"
|
||||||
|
#include "sway/desktop/transaction.h"
|
||||||
|
#include "sway/ipc-json.h"
|
||||||
|
#include "sway/ipc-server.h"
|
||||||
|
#include "sway/output.h"
|
||||||
|
#include "sway/tree/workspace.h"
|
||||||
|
#include "log.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
json_object *ipc_sway_command(struct ipc_client *client,
|
||||||
|
enum ipc_command_type *type, char *buf) {
|
||||||
|
|
||||||
|
size_t payload_length = strlen(buf);
|
||||||
|
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();
|
||||||
|
json_object *reply = cmd_results_to_json(res_list);
|
||||||
|
|
||||||
|
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);
|
||||||
|
return reply;
|
||||||
|
}
|
||||||
|
|
||||||
|
json_object *ipc_sway_send_tick(struct ipc_client *client,
|
||||||
|
enum ipc_command_type *type, char *buf) {
|
||||||
|
ipc_event_tick(buf);
|
||||||
|
return ipc_json_success();
|
||||||
|
}
|
||||||
|
|
||||||
|
json_object *ipc_sway_get_outputs(struct ipc_client *client,
|
||||||
|
enum ipc_command_type *type, char *buf) {
|
||||||
|
json_object *outputs = json_object_new_array();
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
struct sway_output *output;
|
||||||
|
wl_list_for_each(output, &root->all_outputs, link) {
|
||||||
|
if (!output->enabled && output != root->noop_output) {
|
||||||
|
json_object_array_add(outputs,
|
||||||
|
ipc_json_describe_disabled_output(output));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return outputs;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ipc_get_workspaces_callback(struct sway_workspace *workspace,
|
||||||
|
void *data) {
|
||||||
|
json_object *workspace_json = ipc_json_describe_node(&workspace->node);
|
||||||
|
// override the default focused indicator because
|
||||||
|
// it's set differently for the get_workspaces reply
|
||||||
|
struct sway_seat *seat = input_manager_get_default_seat();
|
||||||
|
struct sway_workspace *focused_ws = seat_get_focused_workspace(seat);
|
||||||
|
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);
|
||||||
|
|
||||||
|
focused_ws = output_get_active_workspace(workspace->output);
|
||||||
|
bool visible = workspace == focused_ws;
|
||||||
|
json_object_object_add(workspace_json, "visible",
|
||||||
|
json_object_new_boolean(visible));
|
||||||
|
}
|
||||||
|
|
||||||
|
json_object *ipc_sway_get_workspaces(struct ipc_client *client,
|
||||||
|
enum ipc_command_type *type, char *buf) {
|
||||||
|
json_object *workspaces = json_object_new_array();
|
||||||
|
root_for_each_workspace(ipc_get_workspaces_callback, workspaces);
|
||||||
|
return workspaces;
|
||||||
|
}
|
||||||
|
|
||||||
|
json_object *ipc_sway_subscribe(struct ipc_client *client,
|
||||||
|
enum ipc_command_type *type, char *buf) {
|
||||||
|
// 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)) {
|
||||||
|
sway_log(SWAY_INFO, "Failed to parse subscribe request");
|
||||||
|
return ipc_json_failure(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_tick = false;
|
||||||
|
// 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, "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);
|
||||||
|
} else if (strcmp(event_type, "mode") == 0) {
|
||||||
|
client->subscribed_events |= event_mask(IPC_EVENT_MODE);
|
||||||
|
} else if (strcmp(event_type, "shutdown") == 0) {
|
||||||
|
client->subscribed_events |= event_mask(IPC_EVENT_SHUTDOWN);
|
||||||
|
} 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);
|
||||||
|
} else if (strcmp(event_type, "tick") == 0) {
|
||||||
|
client->subscribed_events |= event_mask(IPC_EVENT_TICK);
|
||||||
|
is_tick = true;
|
||||||
|
} else {
|
||||||
|
json_object_put(request);
|
||||||
|
sway_log(SWAY_INFO, "Unsupported event type in subscribe request");
|
||||||
|
return ipc_json_failure(NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
json_object_put(request);
|
||||||
|
json_object *reply = ipc_json_success();
|
||||||
|
if (is_tick) {
|
||||||
|
const char *json_string = json_object_to_json_string(reply);
|
||||||
|
ipc_send_reply(client, *type, json_string,
|
||||||
|
(uint32_t)strlen(json_string));
|
||||||
|
json_object_put(reply);
|
||||||
|
|
||||||
|
reply = json_object_new_object();
|
||||||
|
json_object_object_add(reply, "first", json_object_new_boolean(true));
|
||||||
|
json_object_object_add(reply, "payload", json_object_new_string(""));
|
||||||
|
*type = IPC_EVENT_TICK;
|
||||||
|
}
|
||||||
|
|
||||||
|
return reply;
|
||||||
|
}
|
||||||
|
|
||||||
|
json_object *ipc_sway_get_inputs(struct ipc_client *client,
|
||||||
|
enum ipc_command_type *type, char *buf) {
|
||||||
|
json_object *inputs = json_object_new_array();
|
||||||
|
struct sway_input_device *device = NULL;
|
||||||
|
wl_list_for_each(device, &server.input->devices, link) {
|
||||||
|
json_object_array_add(inputs, ipc_json_describe_input(device));
|
||||||
|
}
|
||||||
|
return inputs;
|
||||||
|
}
|
||||||
|
|
||||||
|
json_object *ipc_sway_get_seats(struct ipc_client *client,
|
||||||
|
enum ipc_command_type *type, char *buf) {
|
||||||
|
json_object *seats = json_object_new_array();
|
||||||
|
struct sway_seat *seat = NULL;
|
||||||
|
wl_list_for_each(seat, &server.input->seats, link) {
|
||||||
|
json_object_array_add(seats, ipc_json_describe_seat(seat));
|
||||||
|
}
|
||||||
|
return seats;
|
||||||
|
}
|
||||||
|
|
||||||
|
json_object *ipc_sway_get_tree(struct ipc_client *client,
|
||||||
|
enum ipc_command_type *type, char *buf) {
|
||||||
|
return ipc_json_describe_node_recursive(&root->node);
|
||||||
|
}
|
||||||
|
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
json_object *ipc_sway_get_marks(struct ipc_client *client,
|
||||||
|
enum ipc_command_type *type, char *buf) {
|
||||||
|
json_object *marks = json_object_new_array();
|
||||||
|
root_for_each_container(ipc_get_marks_callback, marks);
|
||||||
|
return marks;
|
||||||
|
}
|
||||||
|
|
||||||
|
json_object *ipc_sway_get_version(struct ipc_client *client,
|
||||||
|
enum ipc_command_type *type, char *buf) {
|
||||||
|
return ipc_json_get_version();
|
||||||
|
}
|
||||||
|
|
||||||
|
json_object *ipc_sway_get_bar_config(struct ipc_client *client,
|
||||||
|
enum ipc_command_type *type, char *buf) {
|
||||||
|
if (!buf[0]) {
|
||||||
|
// Send list of configured bar IDs
|
||||||
|
json_object *bars = json_object_new_array();
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
return bars;
|
||||||
|
} else {
|
||||||
|
// Send particular bar's details
|
||||||
|
struct bar_config *bar = NULL;
|
||||||
|
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) {
|
||||||
|
return ipc_json_failure("No bar with that ID");
|
||||||
|
}
|
||||||
|
return ipc_json_describe_bar_config(bar);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
json_object *ipc_sway_get_binding_modes(struct ipc_client *client,
|
||||||
|
enum ipc_command_type *type, char *buf) {
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
return modes;
|
||||||
|
}
|
||||||
|
|
||||||
|
json_object *ipc_sway_get_config(struct ipc_client *client,
|
||||||
|
enum ipc_command_type *type, char *buf) {
|
||||||
|
json_object *json = json_object_new_object();
|
||||||
|
json_object_object_add(json, "config", json_object_new_string(config->current_config));
|
||||||
|
return json;
|
||||||
|
}
|
||||||
|
|
||||||
|
json_object *ipc_sway_sync(struct ipc_client *client,
|
||||||
|
enum ipc_command_type *type, char *buf) {
|
||||||
|
// It was decided sway will not support this, just return success:false
|
||||||
|
return ipc_json_failure(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const ipc_handler sway_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_INPUTS] = ipc_sway_get_inputs,
|
||||||
|
[IPC_GET_MARKS] = ipc_sway_get_marks,
|
||||||
|
[IPC_GET_OUTPUTS] = ipc_sway_get_outputs,
|
||||||
|
[IPC_GET_SEATS] = ipc_sway_get_seats,
|
||||||
|
[IPC_GET_TREE] = ipc_sway_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,
|
||||||
|
[IPC_SYNC] = ipc_sway_sync,
|
||||||
|
};
|
||||||
|
|
||||||
|
const struct ipc_client_impl ipc_client_sway = {
|
||||||
|
.num_commands = sizeof (sway_commands) / sizeof (*sway_commands),
|
||||||
|
.commands = sway_commands,
|
||||||
|
};
|
||||||
|
|
@ -5,6 +5,7 @@ sway_sources = files(
|
||||||
'decoration.c',
|
'decoration.c',
|
||||||
'ipc-json.c',
|
'ipc-json.c',
|
||||||
'ipc-server.c',
|
'ipc-server.c',
|
||||||
|
'ipc-sway.c',
|
||||||
'main.c',
|
'main.c',
|
||||||
'security.c',
|
'security.c',
|
||||||
'server.c',
|
'server.c',
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue