This commit is contained in:
Valentin Boettcher 2018-03-03 11:44:06 +01:00
parent ef30be7822
commit e9b15e2497
5 changed files with 586 additions and 0 deletions

103
include/ipc.h Normal file
View file

@ -0,0 +1,103 @@
/*
* vim:ts=4:sw=4:expandtab
*
* i3 - an improved dynamic tiling window manager
* © 2009 Michael Stapelberg and contributors (see also: LICENSE)
*
* This public header defines the different constants and message types to use
* for the IPC interface to i3 (see docs/ipc for more information).
*
*/
#pragma once
#include <stdint.h>
typedef struct i3_ipc_header {
/* 6 = strlen(I3_IPC_MAGIC) */
char magic[6];
uint32_t size;
uint32_t type;
} __attribute__((packed)) i3_ipc_header_t;
/*
* Messages from clients to i3
*
*/
/** Never change this, only on major IPC breakage (dont do that) */
#define I3_IPC_MAGIC "i3-ipc"
/** Deprecated: use I3_IPC_MESSAGE_TYPE_RUN_COMMAND */
#define I3_IPC_MESSAGE_TYPE_COMMAND 0
/** The payload of the message will be interpreted as a command */
#define I3_IPC_MESSAGE_TYPE_RUN_COMMAND 0
/** Requests the current workspaces from i3 */
#define I3_IPC_MESSAGE_TYPE_GET_WORKSPACES 1
/** Subscribe to the specified events */
#define I3_IPC_MESSAGE_TYPE_SUBSCRIBE 2
/** Requests the current outputs from i3 */
#define I3_IPC_MESSAGE_TYPE_GET_OUTPUTS 3
/** Requests the tree layout from i3 */
#define I3_IPC_MESSAGE_TYPE_GET_TREE 4
/** Request the current defined marks from i3 */
#define I3_IPC_MESSAGE_TYPE_GET_MARKS 5
/** Request the configuration for a specific 'bar' */
#define I3_IPC_MESSAGE_TYPE_GET_BAR_CONFIG 6
/** Request the i3 version */
#define I3_IPC_MESSAGE_TYPE_GET_VERSION 7
/** Request a list of configured binding modes. */
#define I3_IPC_MESSAGE_TYPE_GET_BINDING_MODES 8
/** Request the raw last loaded i3 config. */
#define I3_IPC_MESSAGE_TYPE_GET_CONFIG 9
/*
* Messages from i3 to clients
*
*/
#define I3_IPC_REPLY_TYPE_COMMAND 0
#define I3_IPC_REPLY_TYPE_WORKSPACES 1
#define I3_IPC_REPLY_TYPE_SUBSCRIBE 2
#define I3_IPC_REPLY_TYPE_OUTPUTS 3
#define I3_IPC_REPLY_TYPE_TREE 4
#define I3_IPC_REPLY_TYPE_MARKS 5
#define I3_IPC_REPLY_TYPE_BAR_CONFIG 6
#define I3_IPC_REPLY_TYPE_VERSION 7
#define I3_IPC_REPLY_TYPE_BINDING_MODES 8
#define I3_IPC_REPLY_TYPE_CONFIG 9
/*
* Events from i3 to clients. Events have the first bit set high.
*
*/
#define I3_IPC_EVENT_MASK (1 << 31)
/* The workspace event will be triggered upon changes in the workspace list */
#define I3_IPC_EVENT_WORKSPACE (I3_IPC_EVENT_MASK | 0)
/* The output event will be triggered upon changes in the output list */
#define I3_IPC_EVENT_OUTPUT (I3_IPC_EVENT_MASK | 1)
/* The output event will be triggered upon mode changes */
#define I3_IPC_EVENT_MODE (I3_IPC_EVENT_MASK | 2)
/* The window event will be triggered upon window changes */
#define I3_IPC_EVENT_WINDOW (I3_IPC_EVENT_MASK | 3)
/** Bar config update will be triggered to update the bar config */
#define I3_IPC_EVENT_BARCONFIG_UPDATE (I3_IPC_EVENT_MASK | 4)
/** The binding event will be triggered when bindings run */
#define I3_IPC_EVENT_BINDING (I3_IPC_EVENT_MASK | 5)
/** The shutdown event will be triggered when the ipc shuts down */
#define I3_IPC_EVENT_SHUTDOWN (I3_IPC_EVENT_MASK | 6)

423
test.c Normal file
View file

@ -0,0 +1,423 @@
#include "test.h"
/*
* Reads a message from the given socket file descriptor and stores its length
* (reply_length) as well as a pointer to its contents (reply).
*
* Returns -1 when read() fails, errno will remain.
* Returns -2 on EOF.
* Returns -3 when the IPC protocol is violated (invalid magic, unexpected
* message type, EOF instead of a message). Additionally, the error will be
* printed to stderr.
* Returns 0 on success.
*
*/
int ipc_recv_message(uint32_t *message_type,
uint32_t *reply_length, uint8_t **reply) {
/* Read the message header first */
const uint32_t to_read = strlen(I3_IPC_MAGIC) + sizeof(uint32_t) + sizeof(uint32_t);
char msg[to_read];
char *walk = msg;
uint32_t read_bytes = 0;
while (read_bytes < to_read) {
int n = read(i3_sockfd, msg + read_bytes, to_read - read_bytes);
if (n == -1)
return -1;
if (n == 0) {
if (read_bytes == 0) {
return -2;
} else {
return -3;
}
}
read_bytes += n;
}
if (memcmp(walk, I3_IPC_MAGIC, strlen(I3_IPC_MAGIC)) != 0) {
return -3;
}
walk += strlen(I3_IPC_MAGIC);
memcpy(reply_length, walk, sizeof(uint32_t));
walk += sizeof(uint32_t);
if (message_type != NULL)
memcpy(message_type, walk, sizeof(uint32_t));
*reply = malloc(*reply_length);
read_bytes = 0;
while (read_bytes < *reply_length) {
const int n = read(i3_sockfd, *reply + read_bytes, *reply_length - read_bytes);
if (n == -1) {
if (errno == EINTR || errno == EAGAIN)
continue;
return -1;
}
if (n == 0) {
return -3;
}
read_bytes += n;
}
return 0;
}
/*
* Formats a message (payload) of the given size and type and sends it to i3 via
* the given socket file descriptor.
*
* Returns -1 when write() fails, errno will remain.
* Returns 0 on success.
*
*/
int ipc_send_message_s(const uint32_t message_type, char* msg){
ipc_send_message(strlen(msg), message_type, (uint8_t *)msg);
}
int ipc_send_message(const uint32_t message_size,
const uint32_t message_type, const uint8_t *payload) {
const i3_ipc_header_t header = {
/* We dont use I3_IPC_MAGIC because its a 0-terminated C string. */
.magic = {'i', '3', '-', 'i', 'p', 'c'},
.size = message_size,
.type = message_type};
if (write(i3_sockfd, ((void *)&header), sizeof(i3_ipc_header_t)) == -1)
return -1;
if (write(i3_sockfd, payload, message_size) == -1)
return -1;
return 0;
}
int i3_connect() {
if((i3_sockfd=socket (PF_LOCAL, SOCK_STREAM, 0)) < 0)
return -1;
i3_addr.sun_family = AF_LOCAL;
strcpy(i3_addr.sun_path, i3_socket_path);
if (connect ( i3_sockfd,
(struct sockaddr *) &i3_addr,
sizeof (i3_addr)) < 0)
return -1;
return 0;
}
workspace get_workspace_by_num(unsigned int num, int * index){
workspace cur = workspaces.ws;
int i = 0;
fflush(stdout);
for(; i<workspaces.len && cur->num != num; i++){
//printf("HEY %i, %i\n", cur->num, workspaces.len);
cur = cur->next;
}
if(index != NULL){
if(i == workspaces.len)
i--;
*index = i;
}
return cur;
}
workspace get_workspace(int index){
if(index < 0){
return NULL;
}
workspace ret = workspaces.ws;
for(int i = 0; i<workspaces.len && i<index; i++)
ret = ret->next;
return ret;
}
int insert_workspace(workspace ws, int position){
if(position < 0) {
position = (workspaces.len == 0) ? 0 : workspaces.len;
}
//printf("Insert %s at %i \n", ws->name, position);
fflush(stdout);
if(position > workspaces.len)
return -1;
workspace before = get_workspace(position - 1);
workspace after = (before == NULL) ? NULL : before->next;
if(workspaces.len == 0 || position == 0){
after = workspaces.ws;
before = NULL;
workspaces.ws = ws;
}
if(before != NULL){
before->next = ws;
ws->prev = before;
} else {
ws->prev = NULL;
}
if(after != NULL){
after->prev = ws;
ws->next = after;
} else {
ws->next = NULL;
}
workspaces.len++;
return 0;
};
int remove_workspace(int position){
if(position < 0 || position > workspaces.len - 1)
return -1;
workspace to_delete = get_workspace(position);
workspace before = to_delete->prev;
workspace after = to_delete->next;
free(to_delete);
if(before != NULL && after != NULL){
after->prev = before;
before->next = after;
} if(after == NULL && before != NULL){
before->next = NULL;
} if (after != NULL && before != NULL){
after->prev = 0;
}
if(position == 0){
workspaces.ws = after;
}
workspaces.len--;
return 1;
}
int clean_workspaces(){
workspace ws = workspaces.ws;
while(ws != NULL) {
workspace next = ws->next;
free(ws);
ws = next;
};
workspaces.ws = NULL;
workspaces.len = 0;
}
int sanitize_reply(uint8_t ** reply, uint32_t reply_length){
char * reply_san = malloc(reply_length + 1);
memcpy(reply_san, *reply, reply_length);
reply_san[reply_length] = '\0';
free(*reply);
*reply = reply_san;
return 0;
};
int get_workspaces(){
if (ipc_send_message(0, I3_IPC_MESSAGE_TYPE_GET_WORKSPACES, NULL) == -1)
return -1;
uint32_t reply_length;
uint32_t reply_type;
uint8_t *reply;
int ret;
if ((ret = ipc_recv_message(&reply_type, &reply_length, &reply)) != 0) {
if (ret == -1)
return -1;
}
if(reply_type != I3_IPC_MESSAGE_TYPE_GET_WORKSPACES)
return -1;
sanitize_reply(&reply, reply_length);
yajl_val node = yajl_tree_parse(reply, NULL, 0);
const char * path_num[] = { "num", (const char *) 0 };
const char * path_name[] = { "name", (const char *) 0 };
const char * path_focused[] = { "focused", (const char *) 0 };
if(node && YAJL_IS_ARRAY(node)){
size_t len = node->u.array.len;
for(int i = 0; i < len; i++){
yajl_val obj = node->u.array.values[ i ];
yajl_val num = yajl_tree_get(obj, path_num, yajl_t_number);
yajl_val name = yajl_tree_get(obj, path_name, yajl_t_string);
yajl_val focused = yajl_tree_get(obj, path_focused, yajl_t_true);
workspace new_ws = malloc(sizeof(struct workspace_t));
new_ws->num = (YAJL_GET_INTEGER(num));
new_ws->active = YAJL_IS_TRUE(focused) ? 1 : 0;
new_ws->name = strdup(YAJL_GET_STRING(name));
insert_workspace(new_ws, -1);
}
} else {
return -1;
}
yajl_tree_free(node);
free(reply);
return 0;
};
int format_ws_list(){
// Let's rock!
char* template_inactive = "<span>%s</span> ";
char* template_active = "<span background=\"green\" underline=\"double\">%s</span> ";
char* buff = malloc(strlen(template_inactive) * (workspaces.len - 1)
+ strlen(template_active));
char* walk = buff;
workspace ws = workspaces.ws;
while(ws != NULL) {
char * template = ws->active == 1 ? template_active : template_inactive;
sprintf(walk, template, ws->name);
walk += strlen(template) - 1;
ws = ws->next;
};
free(workspaces.workspaces_format);
workspaces.workspaces_format = buff;
}
int listen_to_events(){
char *listen_command = "[\"workspace\"]";
ipc_send_message_s(I3_IPC_MESSAGE_TYPE_SUBSCRIBE, listen_command);
while(1) {
uint32_t reply_length;
uint32_t reply_type;
uint8_t *reply;
int ret;
fflush(stdout);
if ((ret = ipc_recv_message(&reply_type, &reply_length, &reply)) != 0){
free(reply);
continue;
}
if(reply_type == I3_IPC_EVENT_WORKSPACE){
sanitize_reply(&reply, reply_length);
yajl_val node = yajl_tree_parse((const char *) reply, NULL, 0);
const char * type_path[] = { "change", (const char *) 0 };
const char * curr_num_path[] = { "current", "num", (const char *) 0 };
const char * curr_name_path[] = { "current", "name", (const char *) 0 };
const char * old_num_path[] = { "old", "num", (const char *) 0 };
yajl_val type = yajl_tree_get(node, type_path, yajl_t_string);
yajl_val curr_num = yajl_tree_get(node, curr_num_path, yajl_t_number);
yajl_val old_num = yajl_tree_get(node, old_num_path, yajl_t_number);
char* type_str = YAJL_GET_STRING(type);
//printf("EVENT: %s, %i\n", type_str, YAJL_GET_INTEGER(curr_num));
fflush(stdout);
switch(type_str[0]){
case 'i': {
int index = -1;
workspace ws = get_workspace_by_num(YAJL_GET_INTEGER(curr_num), &index);
//printf("%i", index);
fflush(stdout);
yajl_val curr_name = yajl_tree_get(node, curr_name_path, yajl_t_string);
workspace new = malloc(sizeof(struct workspace_t));
new->name = strdup(YAJL_GET_STRING(curr_name));
new->num = YAJL_GET_INTEGER(curr_num);
insert_workspace(new, index + 1);
break;
}
case 'f': {
workspace ws = get_workspace_by_num(YAJL_GET_INTEGER(curr_num), NULL);
if(ws != NULL) {
ws->active = 1;
ws = get_workspace_by_num(YAJL_GET_INTEGER(old_num), NULL);
ws->active = 0;
}
break;
}
case 'e': {
int index = -1;
get_workspace_by_num(YAJL_GET_INTEGER(curr_num), &index);
remove_workspace(index);
break;
}
default:
break;
}
format_ws_list();
printf("%s\n", workspaces.workspaces_format);
yajl_tree_free(node);
}
free(reply);
}
}
int main(int argc, char **argv){
workspaces.len = 0;
i3_socket_path = get_i3_socket();
i3_connect();
get_workspaces();
format_ws_list();
listen_to_events();
clean_workspaces();
free(i3_socket_path);
free(workspaces.workspaces_format);
}
char* get_i3_socket(){
FILE *pf;
char *command = "i3 --get-socketpath";
char *tmp = malloc(200);
char* buffer;
// ask i3
pf = popen(command,"r");
if(!pf){
fprintf(stderr, "Could not open pipe for output.\n");
return NULL;
}
// get the socket path
fscanf(pf, "%s", tmp);
buffer = malloc(strlen(tmp)+1);
strcpy(buffer, tmp);
free(tmp);
if (pclose(pf) != 0){ // failed to close stream
return NULL;
}
return buffer;
}

59
test.h Normal file
View file

@ -0,0 +1,59 @@
#include <yajl/yajl_tree.h>
#include <yajl/yajl_parse.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
#include "include/ipc.h"
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <unistd.h>
#include <errno.h>
#include <inttypes.h>
#define WS_FOCUS "focus"
#define WS_EMPTY "empty"
#define WS_INIT "init"
//TODO: dynamic
char *i3_socket_path;
int i3_sockfd;
struct sockaddr_un i3_addr;
int sanitize_reply(uint8_t ** reply, uint32_t reply_length);
char* get_i3_socket();
int ipc_recv_message(uint32_t *message_type,
uint32_t *reply_length, uint8_t **reply);
int ipc_send_message_s(uint32_t message_type, char* msg);
int ipc_send_message(const uint32_t message_size,
const uint32_t message_type, const uint8_t *payload);
typedef struct workspace_t* workspace;
struct workspace_t {
unsigned int num;
int active;
char* name;
workspace next;
workspace prev;
};
struct workspaces_t {
unsigned int len;
workspace ws;
char* workspaces_format;
} workspaces;
int insert_workspace(workspace ws, int position);
int remove_workspace(int position);
workspace get_workspace(int index);
workspace get_workspace_by_num(unsigned int num, int * index);
int get_workspaces();
int clean_workspaces();

BIN
test.o Executable file

Binary file not shown.

1
yajl Submodule

@ -0,0 +1 @@
Subproject commit 5e3a7856e643b4d6410ddc3f84bc2f38174f2872