[ipc] remove complex managing of message types. guile will do better easily
This commit is contained in:
parent
176a56df0a
commit
0270a9c565
214
i3-msg/main.c
214
i3-msg/main.c
|
@ -50,101 +50,6 @@ void errorlog(char *fmt, ...) {
|
||||||
|
|
||||||
static char *last_key = NULL;
|
static char *last_key = NULL;
|
||||||
|
|
||||||
typedef struct reply_t {
|
|
||||||
bool success;
|
|
||||||
char *error;
|
|
||||||
char *input;
|
|
||||||
char *errorposition;
|
|
||||||
} reply_t;
|
|
||||||
|
|
||||||
static int exit_code = 0;
|
|
||||||
static reply_t last_reply;
|
|
||||||
|
|
||||||
static int reply_boolean_cb(void *params, int val) {
|
|
||||||
if (strcmp(last_key, "success") == 0)
|
|
||||||
last_reply.success = val;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int reply_string_cb(void *params, const unsigned char *val, size_t len) {
|
|
||||||
char *str = sstrndup((const char *)val, len);
|
|
||||||
|
|
||||||
if (strcmp(last_key, "error") == 0)
|
|
||||||
last_reply.error = str;
|
|
||||||
else if (strcmp(last_key, "input") == 0)
|
|
||||||
last_reply.input = str;
|
|
||||||
else if (strcmp(last_key, "errorposition") == 0)
|
|
||||||
last_reply.errorposition = str;
|
|
||||||
else
|
|
||||||
free(str);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int reply_start_map_cb(void *params) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int reply_end_map_cb(void *params) {
|
|
||||||
if (!last_reply.success) {
|
|
||||||
if (last_reply.input) {
|
|
||||||
fprintf(stderr, "ERROR: Your command: %s\n", last_reply.input);
|
|
||||||
fprintf(stderr, "ERROR: %s\n", last_reply.errorposition);
|
|
||||||
}
|
|
||||||
fprintf(stderr, "ERROR: %s\n", last_reply.error);
|
|
||||||
exit_code = 2;
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int reply_map_key_cb(void *params, const unsigned char *keyVal, size_t keyLen) {
|
|
||||||
free(last_key);
|
|
||||||
last_key = sstrndup((const char *)keyVal, keyLen);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static yajl_callbacks reply_callbacks = {
|
|
||||||
.yajl_boolean = reply_boolean_cb,
|
|
||||||
.yajl_string = reply_string_cb,
|
|
||||||
.yajl_start_map = reply_start_map_cb,
|
|
||||||
.yajl_map_key = reply_map_key_cb,
|
|
||||||
.yajl_end_map = reply_end_map_cb,
|
|
||||||
};
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
* Config reply callbacks
|
|
||||||
*******************************************************************************/
|
|
||||||
|
|
||||||
static char *config_last_key = NULL;
|
|
||||||
|
|
||||||
static int config_string_cb(void *params, const unsigned char *val, size_t len) {
|
|
||||||
char *str = sstrndup((const char *)val, len);
|
|
||||||
if (strcmp(config_last_key, "config") == 0) {
|
|
||||||
fprintf(stdout, "%s", str);
|
|
||||||
}
|
|
||||||
free(str);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int config_start_map_cb(void *params) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int config_end_map_cb(void *params) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int config_map_key_cb(void *params, const unsigned char *keyVal, size_t keyLen) {
|
|
||||||
config_last_key = sstrndup((const char *)keyVal, keyLen);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static yajl_callbacks config_callbacks = {
|
|
||||||
.yajl_string = config_string_cb,
|
|
||||||
.yajl_start_map = config_start_map_cb,
|
|
||||||
.yajl_map_key = config_map_key_cb,
|
|
||||||
.yajl_end_map = config_end_map_cb,
|
|
||||||
};
|
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
int main(int argc, char *argv[]) {
|
||||||
#if defined(__OpenBSD__)
|
#if defined(__OpenBSD__)
|
||||||
if (pledge("stdio rpath unix", NULL) == -1)
|
if (pledge("stdio rpath unix", NULL) == -1)
|
||||||
|
@ -152,17 +57,11 @@ int main(int argc, char *argv[]) {
|
||||||
#endif
|
#endif
|
||||||
char *socket_path = NULL;
|
char *socket_path = NULL;
|
||||||
int o, option_index = 0;
|
int o, option_index = 0;
|
||||||
uint32_t message_type = I3_IPC_MESSAGE_TYPE_RUN_COMMAND;
|
|
||||||
char *payload = NULL;
|
char *payload = NULL;
|
||||||
bool quiet = false;
|
|
||||||
bool monitor = false;
|
|
||||||
|
|
||||||
static struct option long_options[] = {
|
static struct option long_options[] = {
|
||||||
{"socket", required_argument, 0, 's'},
|
{"socket", required_argument, 0, 's'},
|
||||||
{"type", required_argument, 0, 't'},
|
|
||||||
{"version", no_argument, 0, 'v'},
|
{"version", no_argument, 0, 'v'},
|
||||||
{"quiet", no_argument, 0, 'q'},
|
|
||||||
{"monitor", no_argument, 0, 'm'},
|
|
||||||
{"help", no_argument, 0, 'h'},
|
{"help", no_argument, 0, 'h'},
|
||||||
{0, 0, 0, 0}};
|
{0, 0, 0, 0}};
|
||||||
|
|
||||||
|
@ -172,57 +71,18 @@ int main(int argc, char *argv[]) {
|
||||||
if (o == 's') {
|
if (o == 's') {
|
||||||
free(socket_path);
|
free(socket_path);
|
||||||
socket_path = sstrdup(optarg);
|
socket_path = sstrdup(optarg);
|
||||||
} else if (o == 't') {
|
|
||||||
if (strcasecmp(optarg, "command") == 0) {
|
|
||||||
message_type = I3_IPC_MESSAGE_TYPE_RUN_COMMAND;
|
|
||||||
} else if (strcasecmp(optarg, "run_command") == 0) {
|
|
||||||
message_type = I3_IPC_MESSAGE_TYPE_RUN_COMMAND;
|
|
||||||
} else if (strcasecmp(optarg, "get_workspaces") == 0) {
|
|
||||||
message_type = I3_IPC_MESSAGE_TYPE_GET_WORKSPACES;
|
|
||||||
} else if (strcasecmp(optarg, "get_outputs") == 0) {
|
|
||||||
message_type = I3_IPC_MESSAGE_TYPE_GET_OUTPUTS;
|
|
||||||
} else if (strcasecmp(optarg, "get_tree") == 0) {
|
|
||||||
message_type = I3_IPC_MESSAGE_TYPE_GET_TREE;
|
|
||||||
} else if (strcasecmp(optarg, "get_marks") == 0) {
|
|
||||||
message_type = I3_IPC_MESSAGE_TYPE_GET_MARKS;
|
|
||||||
} else if (strcasecmp(optarg, "get_bar_config") == 0) {
|
|
||||||
message_type = I3_IPC_MESSAGE_TYPE_GET_BAR_CONFIG;
|
|
||||||
} else if (strcasecmp(optarg, "get_binding_modes") == 0) {
|
|
||||||
message_type = I3_IPC_MESSAGE_TYPE_GET_BINDING_MODES;
|
|
||||||
} else if (strcasecmp(optarg, "get_version") == 0) {
|
|
||||||
message_type = I3_IPC_MESSAGE_TYPE_GET_VERSION;
|
|
||||||
} else if (strcasecmp(optarg, "get_config") == 0) {
|
|
||||||
message_type = I3_IPC_MESSAGE_TYPE_GET_CONFIG;
|
|
||||||
} else if (strcasecmp(optarg, "send_tick") == 0) {
|
|
||||||
message_type = I3_IPC_MESSAGE_TYPE_SEND_TICK;
|
|
||||||
} else if (strcasecmp(optarg, "subscribe") == 0) {
|
|
||||||
message_type = I3_IPC_MESSAGE_TYPE_SUBSCRIBE;
|
|
||||||
} else {
|
|
||||||
printf("Unknown message type\n");
|
|
||||||
printf("Known types: run_command, get_workspaces, get_outputs, get_tree, get_marks, get_bar_config, get_binding_modes, get_version, get_config, send_tick, subscribe\n");
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
} else if (o == 'q') {
|
|
||||||
quiet = true;
|
|
||||||
} else if (o == 'm') {
|
|
||||||
monitor = true;
|
|
||||||
} else if (o == 'v') {
|
} else if (o == 'v') {
|
||||||
printf("i3-msg " I3_VERSION "\n");
|
puts("i3-msg " I3_VERSION "\n");
|
||||||
return 0;
|
return 0;
|
||||||
} else if (o == 'h') {
|
} else if (o == 'h') {
|
||||||
printf("i3-msg " I3_VERSION "\n");
|
puts("i3-msg " I3_VERSION "\n");
|
||||||
printf("i3-msg [-s <socket>] [-t <type>] [-m] <message>\n");
|
puts("i3-msg [-s <socket>] [-m] <message>\n");
|
||||||
return 0;
|
return 0;
|
||||||
} else if (o == '?') {
|
} else if (o == '?') {
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (monitor && message_type != I3_IPC_MESSAGE_TYPE_SUBSCRIBE) {
|
|
||||||
fprintf(stderr, "The monitor option -m is used with -t SUBSCRIBE exclusively.\n");
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Use all arguments, separated by whitespace, as payload.
|
/* Use all arguments, separated by whitespace, as payload.
|
||||||
* This way, you don’t have to do i3-msg 'mark foo', you can use
|
* This way, you don’t have to do i3-msg 'mark foo', you can use
|
||||||
* i3-msg mark foo */
|
* i3-msg mark foo */
|
||||||
|
@ -242,78 +102,18 @@ int main(int argc, char *argv[]) {
|
||||||
payload = sstrdup("");
|
payload = sstrdup("");
|
||||||
|
|
||||||
int sockfd = ipc_connect(socket_path);
|
int sockfd = ipc_connect(socket_path);
|
||||||
if (ipc_send_message(sockfd, strlen(payload), message_type, (uint8_t *)payload) == -1)
|
if (ipc_send_message(sockfd, strlen(payload), (uint8_t *)payload) == -1)
|
||||||
err(EXIT_FAILURE, "IPC: write()");
|
err(EXIT_FAILURE, "IPC: write()");
|
||||||
free(payload);
|
free(payload);
|
||||||
|
|
||||||
uint32_t reply_length;
|
uint32_t reply_length;
|
||||||
uint32_t reply_type;
|
|
||||||
uint8_t *reply;
|
uint8_t *reply;
|
||||||
int ret;
|
int ret;
|
||||||
if ((ret = ipc_recv_message(sockfd, &reply_type, &reply_length, &reply)) != 0) {
|
if ((ret = ipc_recv_message(sockfd, &reply_length, &reply)) != 0) {
|
||||||
if (ret == -1)
|
if (ret == -1)
|
||||||
err(EXIT_FAILURE, "IPC: read()");
|
err(EXIT_FAILURE, "IPC: read()");
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
if (reply_type != message_type)
|
puts(reply);
|
||||||
errx(EXIT_FAILURE, "IPC: Received reply of type %d but expected %d", reply_type, message_type);
|
return 0;
|
||||||
/* For the reply of commands, have a look if that command was successful.
|
|
||||||
* If not, nicely format the error message. */
|
|
||||||
if (reply_type == I3_IPC_REPLY_TYPE_COMMAND) {
|
|
||||||
yajl_handle handle = yajl_alloc(&reply_callbacks, NULL, NULL);
|
|
||||||
yajl_status state = yajl_parse(handle, (const unsigned char *)reply, reply_length);
|
|
||||||
yajl_free(handle);
|
|
||||||
|
|
||||||
switch (state) {
|
|
||||||
case yajl_status_ok:
|
|
||||||
break;
|
|
||||||
case yajl_status_client_canceled:
|
|
||||||
case yajl_status_error:
|
|
||||||
errx(EXIT_FAILURE, "IPC: Could not parse JSON reply.");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!quiet) {
|
|
||||||
printf("%.*s\n", reply_length, reply);
|
|
||||||
}
|
|
||||||
} else if (reply_type == I3_IPC_REPLY_TYPE_CONFIG) {
|
|
||||||
yajl_handle handle = yajl_alloc(&config_callbacks, NULL, NULL);
|
|
||||||
yajl_status state = yajl_parse(handle, (const unsigned char *)reply, reply_length);
|
|
||||||
yajl_free(handle);
|
|
||||||
|
|
||||||
switch (state) {
|
|
||||||
case yajl_status_ok:
|
|
||||||
break;
|
|
||||||
case yajl_status_client_canceled:
|
|
||||||
case yajl_status_error:
|
|
||||||
errx(EXIT_FAILURE, "IPC: Could not parse JSON reply.");
|
|
||||||
}
|
|
||||||
} else if (reply_type == I3_IPC_REPLY_TYPE_SUBSCRIBE) {
|
|
||||||
do {
|
|
||||||
free(reply);
|
|
||||||
if ((ret = ipc_recv_message(sockfd, &reply_type, &reply_length, &reply)) != 0) {
|
|
||||||
if (ret == -1)
|
|
||||||
err(EXIT_FAILURE, "IPC: read()");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!(reply_type & I3_IPC_EVENT_MASK)) {
|
|
||||||
errx(EXIT_FAILURE, "IPC: Received reply of type %d but expected an event", reply_type);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!quiet) {
|
|
||||||
fprintf(stdout, "%.*s\n", reply_length, reply);
|
|
||||||
fflush(stdout);
|
|
||||||
}
|
|
||||||
} while (monitor);
|
|
||||||
} else {
|
|
||||||
if (!quiet) {
|
|
||||||
printf("%.*s\n", reply_length, reply);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
free(reply);
|
|
||||||
|
|
||||||
close(sockfd);
|
|
||||||
|
|
||||||
return exit_code;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,8 +59,7 @@ typedef void (*handler_t)(ipc_client *, uint8_t *, int, uint32_t, uint32_t);
|
||||||
/* Macro to declare a callback */
|
/* Macro to declare a callback */
|
||||||
#define IPC_HANDLER(name) \
|
#define IPC_HANDLER(name) \
|
||||||
static void handle_##name(ipc_client *client, uint8_t *message, \
|
static void handle_##name(ipc_client *client, uint8_t *message, \
|
||||||
int size, uint32_t message_size, \
|
int size, uint32_t message_size)
|
||||||
uint32_t message_type)
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handler for activity on the listening socket, meaning that a new client
|
* Handler for activity on the listening socket, meaning that a new client
|
||||||
|
|
|
@ -301,8 +301,7 @@ int ipc_connect(const char *socket_path);
|
||||||
* Returns 0 on success.
|
* Returns 0 on success.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
int ipc_send_message(int sockfd, const uint32_t message_size,
|
int ipc_send_message(int sockfd, const uint32_t message_size, const uint8_t *payload);
|
||||||
const uint32_t message_type, const uint8_t *payload);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reads a message from the given socket file descriptor and stores its length
|
* Reads a message from the given socket file descriptor and stores its length
|
||||||
|
@ -315,8 +314,7 @@ int ipc_send_message(int sockfd, const uint32_t message_size,
|
||||||
* Returns 0 on success.
|
* Returns 0 on success.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
int ipc_recv_message(int sockfd, uint32_t *message_type,
|
int ipc_recv_message(int sockfd, uint32_t *reply_length, uint8_t **reply);
|
||||||
uint32_t *reply_length, uint8_t **reply);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates a configure_notify event and sends it to the given window
|
* Generates a configure_notify event and sends it to the given window
|
||||||
|
|
|
@ -26,8 +26,7 @@
|
||||||
* Returns 0 on success.
|
* Returns 0 on success.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
int ipc_recv_message(int sockfd, uint32_t *message_type,
|
int ipc_recv_message(int sockfd, uint32_t *reply_length, uint8_t **reply) {
|
||||||
uint32_t *reply_length, uint8_t **reply) {
|
|
||||||
/* Read the message header first */
|
/* Read the message header first */
|
||||||
const uint32_t to_read = strlen(I3_IPC_MAGIC) + sizeof(uint32_t) + sizeof(uint32_t);
|
const uint32_t to_read = strlen(I3_IPC_MAGIC) + sizeof(uint32_t) + sizeof(uint32_t);
|
||||||
char msg[to_read];
|
char msg[to_read];
|
||||||
|
@ -60,8 +59,6 @@ int ipc_recv_message(int sockfd, uint32_t *message_type,
|
||||||
walk += strlen(I3_IPC_MAGIC);
|
walk += strlen(I3_IPC_MAGIC);
|
||||||
memcpy(reply_length, walk, sizeof(uint32_t));
|
memcpy(reply_length, walk, sizeof(uint32_t));
|
||||||
walk += sizeof(uint32_t);
|
walk += sizeof(uint32_t);
|
||||||
if (message_type != NULL)
|
|
||||||
memcpy(message_type, walk, sizeof(uint32_t));
|
|
||||||
|
|
||||||
*reply = smalloc(*reply_length);
|
*reply = smalloc(*reply_length);
|
||||||
|
|
||||||
|
|
|
@ -18,13 +18,11 @@
|
||||||
* Returns 0 on success.
|
* Returns 0 on success.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
int ipc_send_message(int sockfd, const uint32_t message_size,
|
int ipc_send_message(int sockfd, const uint32_t message_size, const uint8_t *payload) {
|
||||||
const uint32_t message_type, const uint8_t *payload) {
|
|
||||||
const i3_ipc_header_t header = {
|
const i3_ipc_header_t header = {
|
||||||
/* We don’t use I3_IPC_MAGIC because it’s a 0-terminated C string. */
|
/* We don’t use I3_IPC_MAGIC because it’s a 0-terminated C string. */
|
||||||
.magic = {'i', '3', '-', 'i', 'p', 'c'},
|
.magic = {'i', '3', '-', 'i', 'p', 'c'},
|
||||||
.size = message_size,
|
.size = message_size};
|
||||||
.type = message_type};
|
|
||||||
|
|
||||||
if (writeall(sockfd, ((void *)&header), sizeof(i3_ipc_header_t)) == -1)
|
if (writeall(sockfd, ((void *)&header), sizeof(i3_ipc_header_t)) == -1)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
|
@ -71,15 +71,16 @@ void display_running_version(void) {
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
|
|
||||||
int sockfd = ipc_connect(NULL);
|
int sockfd = ipc_connect(NULL);
|
||||||
if (ipc_send_message(sockfd, 0, I3_IPC_MESSAGE_TYPE_GET_VERSION,
|
/* if (ipc_send_message(sockfd, 0, I3_IPC_MESSAGE_TYPE_GET_VERSION, */
|
||||||
(uint8_t *)"") == -1)
|
/* (uint8_t *)"") == -1) */
|
||||||
err(EXIT_FAILURE, "IPC: write()");
|
/* err(EXIT_FAILURE, "IPC: write()"); */
|
||||||
|
err(EXIT_FAILURE, "GUILE: NOT IMPLEMENTED");
|
||||||
|
|
||||||
uint32_t reply_length;
|
uint32_t reply_length;
|
||||||
uint32_t reply_type;
|
uint32_t reply_type;
|
||||||
uint8_t *reply;
|
uint8_t *reply;
|
||||||
int ret;
|
int ret;
|
||||||
if ((ret = ipc_recv_message(sockfd, &reply_type, &reply_length, &reply)) != 0) {
|
if ((ret = ipc_recv_message(sockfd, &reply_length, &reply)) != 0) {
|
||||||
if (ret == -1)
|
if (ret == -1)
|
||||||
err(EXIT_FAILURE, "IPC: read()");
|
err(EXIT_FAILURE, "IPC: read()");
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
|
|
442
src/ipc.c
442
src/ipc.c
|
@ -870,285 +870,6 @@ static void dump_bar_config(yajl_gen gen, Barconfig *config) {
|
||||||
#undef YSTR_IF_SET
|
#undef YSTR_IF_SET
|
||||||
}
|
}
|
||||||
|
|
||||||
IPC_HANDLER(tree) {
|
|
||||||
setlocale(LC_NUMERIC, "C");
|
|
||||||
yajl_gen gen = ygenalloc();
|
|
||||||
dump_node(gen, croot, false);
|
|
||||||
setlocale(LC_NUMERIC, "");
|
|
||||||
|
|
||||||
const unsigned char *payload;
|
|
||||||
ylength length;
|
|
||||||
y(get_buf, &payload, &length);
|
|
||||||
|
|
||||||
ipc_send_client_message(client, length, I3_IPC_REPLY_TYPE_TREE, payload);
|
|
||||||
y(free);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Formats the reply message for a GET_WORKSPACES request and sends it to the
|
|
||||||
* client
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
IPC_HANDLER(get_workspaces) {
|
|
||||||
yajl_gen gen = ygenalloc();
|
|
||||||
y(array_open);
|
|
||||||
|
|
||||||
Con *focused_ws = con_get_workspace(focused);
|
|
||||||
|
|
||||||
Con *output;
|
|
||||||
TAILQ_FOREACH (output, &(croot->nodes_head), nodes) {
|
|
||||||
if (con_is_internal(output))
|
|
||||||
continue;
|
|
||||||
Con *ws;
|
|
||||||
TAILQ_FOREACH (ws, &(output_get_content(output)->nodes_head), nodes) {
|
|
||||||
assert(ws->type == CT_WORKSPACE);
|
|
||||||
y(map_open);
|
|
||||||
|
|
||||||
ystr("id");
|
|
||||||
y(integer, (uintptr_t)ws);
|
|
||||||
|
|
||||||
ystr("num");
|
|
||||||
y(integer, ws->num);
|
|
||||||
|
|
||||||
ystr("name");
|
|
||||||
ystr(ws->name);
|
|
||||||
|
|
||||||
ystr("visible");
|
|
||||||
y(bool, workspace_is_visible(ws));
|
|
||||||
|
|
||||||
ystr("focused");
|
|
||||||
y(bool, ws == focused_ws);
|
|
||||||
|
|
||||||
ystr("rect");
|
|
||||||
y(map_open);
|
|
||||||
ystr("x");
|
|
||||||
y(integer, ws->rect.x);
|
|
||||||
ystr("y");
|
|
||||||
y(integer, ws->rect.y);
|
|
||||||
ystr("width");
|
|
||||||
y(integer, ws->rect.width);
|
|
||||||
ystr("height");
|
|
||||||
y(integer, ws->rect.height);
|
|
||||||
y(map_close);
|
|
||||||
|
|
||||||
ystr("output");
|
|
||||||
ystr(output->name);
|
|
||||||
|
|
||||||
ystr("urgent");
|
|
||||||
y(bool, ws->urgent);
|
|
||||||
|
|
||||||
y(map_close);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
y(array_close);
|
|
||||||
|
|
||||||
const unsigned char *payload;
|
|
||||||
ylength length;
|
|
||||||
y(get_buf, &payload, &length);
|
|
||||||
|
|
||||||
ipc_send_client_message(client, length, I3_IPC_REPLY_TYPE_WORKSPACES, payload);
|
|
||||||
y(free);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Formats the reply message for a GET_OUTPUTS request and sends it to the
|
|
||||||
* client
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
IPC_HANDLER(get_outputs) {
|
|
||||||
yajl_gen gen = ygenalloc();
|
|
||||||
y(array_open);
|
|
||||||
|
|
||||||
Output *output;
|
|
||||||
TAILQ_FOREACH (output, &outputs, outputs) {
|
|
||||||
y(map_open);
|
|
||||||
|
|
||||||
ystr("name");
|
|
||||||
ystr(output_primary_name(output));
|
|
||||||
|
|
||||||
ystr("active");
|
|
||||||
y(bool, output->active);
|
|
||||||
|
|
||||||
ystr("primary");
|
|
||||||
y(bool, output->primary);
|
|
||||||
|
|
||||||
ystr("rect");
|
|
||||||
y(map_open);
|
|
||||||
ystr("x");
|
|
||||||
y(integer, output->rect.x);
|
|
||||||
ystr("y");
|
|
||||||
y(integer, output->rect.y);
|
|
||||||
ystr("width");
|
|
||||||
y(integer, output->rect.width);
|
|
||||||
ystr("height");
|
|
||||||
y(integer, output->rect.height);
|
|
||||||
y(map_close);
|
|
||||||
|
|
||||||
ystr("current_workspace");
|
|
||||||
Con *ws = NULL;
|
|
||||||
if (output->con && (ws = con_get_fullscreen_con(output->con, CF_OUTPUT)))
|
|
||||||
ystr(ws->name);
|
|
||||||
else
|
|
||||||
y(null);
|
|
||||||
|
|
||||||
y(map_close);
|
|
||||||
}
|
|
||||||
|
|
||||||
y(array_close);
|
|
||||||
|
|
||||||
const unsigned char *payload;
|
|
||||||
ylength length;
|
|
||||||
y(get_buf, &payload, &length);
|
|
||||||
|
|
||||||
ipc_send_client_message(client, length, I3_IPC_REPLY_TYPE_OUTPUTS, payload);
|
|
||||||
y(free);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Formats the reply message for a GET_MARKS request and sends it to the
|
|
||||||
* client
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
IPC_HANDLER(get_marks) {
|
|
||||||
yajl_gen gen = ygenalloc();
|
|
||||||
y(array_open);
|
|
||||||
|
|
||||||
Con *con;
|
|
||||||
TAILQ_FOREACH (con, &all_cons, all_cons) {
|
|
||||||
mark_t *mark;
|
|
||||||
TAILQ_FOREACH (mark, &(con->marks_head), marks) {
|
|
||||||
ystr(mark->name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
y(array_close);
|
|
||||||
|
|
||||||
const unsigned char *payload;
|
|
||||||
ylength length;
|
|
||||||
y(get_buf, &payload, &length);
|
|
||||||
|
|
||||||
ipc_send_client_message(client, length, I3_IPC_REPLY_TYPE_MARKS, payload);
|
|
||||||
y(free);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Returns the version of i3
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
IPC_HANDLER(get_version) {
|
|
||||||
yajl_gen gen = ygenalloc();
|
|
||||||
y(map_open);
|
|
||||||
|
|
||||||
ystr("major");
|
|
||||||
y(integer, MAJOR_VERSION);
|
|
||||||
|
|
||||||
ystr("minor");
|
|
||||||
y(integer, MINOR_VERSION);
|
|
||||||
|
|
||||||
ystr("patch");
|
|
||||||
y(integer, PATCH_VERSION);
|
|
||||||
|
|
||||||
ystr("human_readable");
|
|
||||||
ystr(i3_version);
|
|
||||||
|
|
||||||
ystr("loaded_config_file_name");
|
|
||||||
ystr(current_configpath);
|
|
||||||
|
|
||||||
y(map_close);
|
|
||||||
|
|
||||||
const unsigned char *payload;
|
|
||||||
ylength length;
|
|
||||||
y(get_buf, &payload, &length);
|
|
||||||
|
|
||||||
ipc_send_client_message(client, length, I3_IPC_REPLY_TYPE_VERSION, payload);
|
|
||||||
y(free);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Formats the reply message for a GET_BAR_CONFIG request and sends it to the
|
|
||||||
* client.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
IPC_HANDLER(get_bar_config) {
|
|
||||||
yajl_gen gen = ygenalloc();
|
|
||||||
|
|
||||||
/* If no ID was passed, we return a JSON array with all IDs */
|
|
||||||
if (message_size == 0) {
|
|
||||||
y(array_open);
|
|
||||||
Barconfig *current;
|
|
||||||
TAILQ_FOREACH (current, &barconfigs, configs) {
|
|
||||||
ystr(current->id);
|
|
||||||
}
|
|
||||||
y(array_close);
|
|
||||||
|
|
||||||
const unsigned char *payload;
|
|
||||||
ylength length;
|
|
||||||
y(get_buf, &payload, &length);
|
|
||||||
|
|
||||||
ipc_send_client_message(client, length, I3_IPC_REPLY_TYPE_BAR_CONFIG, payload);
|
|
||||||
y(free);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* To get a properly terminated buffer, we copy
|
|
||||||
* message_size bytes out of the buffer */
|
|
||||||
char *bar_id = NULL;
|
|
||||||
sasprintf(&bar_id, "%.*s", message_size, message);
|
|
||||||
LOG("IPC: looking for config for bar ID \"%s\"\n", bar_id);
|
|
||||||
Barconfig *current, *config = NULL;
|
|
||||||
TAILQ_FOREACH (current, &barconfigs, configs) {
|
|
||||||
if (strcmp(current->id, bar_id) != 0)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
config = current;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
free(bar_id);
|
|
||||||
|
|
||||||
if (!config) {
|
|
||||||
/* If we did not find a config for the given ID, the reply will contain
|
|
||||||
* a null 'id' field. */
|
|
||||||
y(map_open);
|
|
||||||
|
|
||||||
ystr("id");
|
|
||||||
y(null);
|
|
||||||
|
|
||||||
y(map_close);
|
|
||||||
} else {
|
|
||||||
dump_bar_config(gen, config);
|
|
||||||
}
|
|
||||||
|
|
||||||
const unsigned char *payload;
|
|
||||||
ylength length;
|
|
||||||
y(get_buf, &payload, &length);
|
|
||||||
|
|
||||||
ipc_send_client_message(client, length, I3_IPC_REPLY_TYPE_BAR_CONFIG, payload);
|
|
||||||
y(free);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Returns a list of configured binding modes
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
IPC_HANDLER(get_binding_modes) {
|
|
||||||
yajl_gen gen = ygenalloc();
|
|
||||||
|
|
||||||
y(array_open);
|
|
||||||
struct Mode *mode;
|
|
||||||
SLIST_FOREACH (mode, &modes, modes) {
|
|
||||||
ystr(mode->name);
|
|
||||||
}
|
|
||||||
y(array_close);
|
|
||||||
|
|
||||||
const unsigned char *payload;
|
|
||||||
ylength length;
|
|
||||||
y(get_buf, &payload, &length);
|
|
||||||
|
|
||||||
ipc_send_client_message(client, length, I3_IPC_REPLY_TYPE_BINDING_MODES, payload);
|
|
||||||
y(free);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Callback for the YAJL parser (will be called when a string is parsed).
|
* Callback for the YAJL parser (will be called when a string is parsed).
|
||||||
*
|
*
|
||||||
|
@ -1176,107 +897,6 @@ static int add_subscription(void *extra, const unsigned char *s,
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Subscribes this connection to the event types which were given as a JSON
|
|
||||||
* serialized array in the payload field of the message.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
IPC_HANDLER(subscribe) {
|
|
||||||
yajl_handle p;
|
|
||||||
yajl_status stat;
|
|
||||||
|
|
||||||
/* Setup the JSON parser */
|
|
||||||
static yajl_callbacks callbacks = {
|
|
||||||
.yajl_string = add_subscription,
|
|
||||||
};
|
|
||||||
|
|
||||||
p = yalloc(&callbacks, (void *)client);
|
|
||||||
stat = yajl_parse(p, (const unsigned char *)message, message_size);
|
|
||||||
if (stat != yajl_status_ok) {
|
|
||||||
unsigned char *err;
|
|
||||||
err = yajl_get_error(p, true, (const unsigned char *)message,
|
|
||||||
message_size);
|
|
||||||
ELOG("YAJL parse error: %s\n", err);
|
|
||||||
yajl_free_error(p, err);
|
|
||||||
|
|
||||||
const char *reply = "{\"success\":false}";
|
|
||||||
ipc_send_client_message(client, strlen(reply), I3_IPC_REPLY_TYPE_SUBSCRIBE, (const uint8_t *)reply);
|
|
||||||
yajl_free(p);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
yajl_free(p);
|
|
||||||
const char *reply = "{\"success\":true}";
|
|
||||||
ipc_send_client_message(client, strlen(reply), I3_IPC_REPLY_TYPE_SUBSCRIBE, (const uint8_t *)reply);
|
|
||||||
|
|
||||||
if (client->first_tick_sent) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool is_tick = false;
|
|
||||||
for (int i = 0; i < client->num_events; i++) {
|
|
||||||
if (strcmp(client->events[i], "tick") == 0) {
|
|
||||||
is_tick = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!is_tick) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
client->first_tick_sent = true;
|
|
||||||
const char *payload = "{\"first\":true,\"payload\":\"\"}";
|
|
||||||
ipc_send_client_message(client, strlen(payload), I3_IPC_EVENT_TICK, (const uint8_t *)payload);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Returns the raw last loaded i3 configuration file contents.
|
|
||||||
*/
|
|
||||||
IPC_HANDLER(get_config) {
|
|
||||||
yajl_gen gen = ygenalloc();
|
|
||||||
|
|
||||||
y(map_open);
|
|
||||||
|
|
||||||
ystr("config");
|
|
||||||
ystr(current_config);
|
|
||||||
|
|
||||||
y(map_close);
|
|
||||||
|
|
||||||
const unsigned char *payload;
|
|
||||||
ylength length;
|
|
||||||
y(get_buf, &payload, &length);
|
|
||||||
|
|
||||||
ipc_send_client_message(client, length, I3_IPC_REPLY_TYPE_CONFIG, payload);
|
|
||||||
y(free);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Sends the tick event from the message payload to subscribers. Establishes a
|
|
||||||
* synchronization point in event-related tests.
|
|
||||||
*/
|
|
||||||
IPC_HANDLER(send_tick) {
|
|
||||||
yajl_gen gen = ygenalloc();
|
|
||||||
|
|
||||||
y(map_open);
|
|
||||||
|
|
||||||
ystr("first");
|
|
||||||
y(bool, false);
|
|
||||||
|
|
||||||
ystr("payload");
|
|
||||||
yajl_gen_string(gen, (unsigned char *)message, message_size);
|
|
||||||
|
|
||||||
y(map_close);
|
|
||||||
|
|
||||||
const unsigned char *payload;
|
|
||||||
ylength length;
|
|
||||||
y(get_buf, &payload, &length);
|
|
||||||
|
|
||||||
ipc_send_event("tick", I3_IPC_EVENT_TICK, (const char *)payload);
|
|
||||||
y(free);
|
|
||||||
|
|
||||||
const char *reply = "{\"success\":true}";
|
|
||||||
ipc_send_client_message(client, strlen(reply), I3_IPC_REPLY_TYPE_TICK, (const uint8_t *)reply);
|
|
||||||
DLOG("Sent tick event\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
struct sync_state {
|
struct sync_state {
|
||||||
char *last_key;
|
char *last_key;
|
||||||
|
@ -1302,58 +922,6 @@ static int _sync_json_int(void *extra, long long val) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
IPC_HANDLER(sync) {
|
|
||||||
yajl_handle p;
|
|
||||||
yajl_status stat;
|
|
||||||
|
|
||||||
/* Setup the JSON parser */
|
|
||||||
static yajl_callbacks callbacks = {
|
|
||||||
.yajl_map_key = _sync_json_key,
|
|
||||||
.yajl_integer = _sync_json_int,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct sync_state state;
|
|
||||||
memset(&state, '\0', sizeof(struct sync_state));
|
|
||||||
p = yalloc(&callbacks, (void *)&state);
|
|
||||||
stat = yajl_parse(p, (const unsigned char *)message, message_size);
|
|
||||||
FREE(state.last_key);
|
|
||||||
if (stat != yajl_status_ok) {
|
|
||||||
unsigned char *err;
|
|
||||||
err = yajl_get_error(p, true, (const unsigned char *)message,
|
|
||||||
message_size);
|
|
||||||
ELOG("YAJL parse error: %s\n", err);
|
|
||||||
yajl_free_error(p, err);
|
|
||||||
|
|
||||||
const char *reply = "{\"success\":false}";
|
|
||||||
ipc_send_client_message(client, strlen(reply), I3_IPC_REPLY_TYPE_SYNC, (const uint8_t *)reply);
|
|
||||||
yajl_free(p);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
yajl_free(p);
|
|
||||||
|
|
||||||
DLOG("received IPC sync request (rnd = %d, window = 0x%08x)\n", state.rnd, state.window);
|
|
||||||
sync_respond(state.window, state.rnd);
|
|
||||||
const char *reply = "{\"success\":true}";
|
|
||||||
ipc_send_client_message(client, strlen(reply), I3_IPC_REPLY_TYPE_SYNC, (const uint8_t *)reply);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* The index of each callback function corresponds to the numeric
|
|
||||||
* value of the message type (see include/i3/ipc.h) */
|
|
||||||
handler_t handlers[12] = {
|
|
||||||
handle_run_command,
|
|
||||||
handle_get_workspaces,
|
|
||||||
handle_subscribe,
|
|
||||||
handle_get_outputs,
|
|
||||||
handle_tree,
|
|
||||||
handle_get_marks,
|
|
||||||
handle_get_bar_config,
|
|
||||||
handle_get_version,
|
|
||||||
handle_get_binding_modes,
|
|
||||||
handle_get_config,
|
|
||||||
handle_send_tick,
|
|
||||||
handle_sync,
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Handler for activity on a client connection, receives a message from a
|
* Handler for activity on a client connection, receives a message from a
|
||||||
* client.
|
* client.
|
||||||
|
@ -1365,13 +933,12 @@ handler_t handlers[12] = {
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
static void ipc_receive_message(EV_P_ struct ev_io *w, int revents) {
|
static void ipc_receive_message(EV_P_ struct ev_io *w, int revents) {
|
||||||
uint32_t message_type;
|
|
||||||
uint32_t message_length;
|
uint32_t message_length;
|
||||||
uint8_t *message = NULL;
|
uint8_t *message = NULL;
|
||||||
ipc_client *client = (ipc_client *)w->data;
|
ipc_client *client = (ipc_client *)w->data;
|
||||||
assert(client->fd == w->fd);
|
assert(client->fd == w->fd);
|
||||||
|
|
||||||
int ret = ipc_recv_message(w->fd, &message_type, &message_length, &message);
|
int ret = ipc_recv_message(w->fd, &message_length, &message);
|
||||||
/* EOF or other error */
|
/* EOF or other error */
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
/* Was this a spurious read? See ev(3) */
|
/* Was this a spurious read? See ev(3) */
|
||||||
|
@ -1387,12 +954,7 @@ static void ipc_receive_message(EV_P_ struct ev_io *w, int revents) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (message_type >= (sizeof(handlers) / sizeof(handler_t)))
|
handle_run_command(client, message, 0, message_length);
|
||||||
DLOG("Unhandled message type: %d\n", message_type);
|
|
||||||
else {
|
|
||||||
handler_t h = handlers[message_type];
|
|
||||||
h(client, message, 0, message_length, message_type);
|
|
||||||
}
|
|
||||||
|
|
||||||
FREE(message);
|
FREE(message);
|
||||||
}
|
}
|
||||||
|
|
|
@ -520,8 +520,7 @@ int main(int argc, char *argv[]) {
|
||||||
if (connect(sockfd, (const struct sockaddr *)&addr, sizeof(struct sockaddr_un)) < 0)
|
if (connect(sockfd, (const struct sockaddr *)&addr, sizeof(struct sockaddr_un)) < 0)
|
||||||
err(EXIT_FAILURE, "Could not connect to i3");
|
err(EXIT_FAILURE, "Could not connect to i3");
|
||||||
|
|
||||||
if (ipc_send_message(sockfd, strlen(payload), I3_IPC_MESSAGE_TYPE_RUN_COMMAND,
|
if (ipc_send_message(sockfd, strlen(payload), (uint8_t *)payload) == -1)
|
||||||
(uint8_t *)payload) == -1)
|
|
||||||
err(EXIT_FAILURE, "IPC: write()");
|
err(EXIT_FAILURE, "IPC: write()");
|
||||||
FREE(payload);
|
FREE(payload);
|
||||||
|
|
||||||
|
@ -529,7 +528,7 @@ int main(int argc, char *argv[]) {
|
||||||
uint32_t reply_type;
|
uint32_t reply_type;
|
||||||
uint8_t *reply;
|
uint8_t *reply;
|
||||||
int ret;
|
int ret;
|
||||||
if ((ret = ipc_recv_message(sockfd, &reply_type, &reply_length, &reply)) != 0) {
|
if ((ret = ipc_recv_message(sockfd, &reply_length, &reply)) != 0) {
|
||||||
if (ret == -1)
|
if (ret == -1)
|
||||||
err(EXIT_FAILURE, "IPC: read()");
|
err(EXIT_FAILURE, "IPC: read()");
|
||||||
return 1;
|
return 1;
|
||||||
|
|
Loading…
Reference in New Issue