i3_yabar_workspaces/test.c
Valentin Boettcher e9b15e2497 init
2018-03-03 11:44:06 +01:00

423 lines
9.7 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#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;
}