mirror of
https://github.com/vale981/i3_yabar_workspaces
synced 2025-03-04 09:01:41 -05:00
init
This commit is contained in:
parent
ef30be7822
commit
e9b15e2497
5 changed files with 586 additions and 0 deletions
103
include/ipc.h
Normal file
103
include/ipc.h
Normal 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 (don’t 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
423
test.c
Normal 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 don’t use I3_IPC_MAGIC because it’s 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
59
test.h
Normal 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
BIN
test.o
Executable file
Binary file not shown.
1
yajl
Submodule
1
yajl
Submodule
|
@ -0,0 +1 @@
|
|||
Subproject commit 5e3a7856e643b4d6410ddc3f84bc2f38174f2872
|
Loading…
Add table
Reference in a new issue