├── src ├── x11fs.h ├── win_oper.h ├── win_xcb.h ├── win_oper.c ├── win_xcb.c └── x11fs.c ├── Makefile ├── README.md └── LICENSE /src/x11fs.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #define WID_STRING_LENGTH 11 3 | typedef enum {X11FS_SUCCESS, X11FS_FAILURE} X11FS_STATUS; 4 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CC=cc 2 | LD=$(CC) 3 | 4 | LIBS=`pkg-config fuse --libs` `pkg-config xcb --libs` `pkg-config xcb-icccm --libs` 5 | CFLAGS=-Wall -std=gnu11 -c `pkg-config fuse --cflags` `pkg-config xcb --cflags` 6 | 7 | PREFIX?=/usr 8 | 9 | SRCDIR=src 10 | OBJDIR=obj 11 | BINDIR=bin 12 | 13 | SRC := $(wildcard $(SRCDIR)/*.c) 14 | OBJ := $(SRC:$(SRCDIR)/%.c=$(OBJDIR)/%.o) 15 | 16 | all: x11fs 17 | 18 | $(OBJDIR): 19 | mkdir $(OBJDIR) 20 | 21 | $(BINDIR): 22 | mkdir $(BINDIR) 23 | 24 | $(OBJDIR)/%.o: $(SRCDIR)/%.c $(wildcard $(SRCDIR)/*.h) $(OBJDIR) 25 | $(CC) $(CFLAGS) -o $@ $< 26 | 27 | x11fs: $(BINDIR) $(OBJ) 28 | $(LD) $(OBJ) $(LIBS) -o $(BINDIR)/$@ 29 | 30 | install: x11fs 31 | mkdir -p $(DESTDIR)$(PREFIX)/bin/ 32 | cp $(BINDIR)/x11fs $(DESTDIR)$(PREFIX)/bin/ 33 | 34 | uninstall: 35 | rm $(DESTDIR)$(PREFIX)/bin/x11fs 36 | 37 | clean: 38 | rm $(OBJDIR)/*.o 39 | rm $(BINDIR)/x11fs 40 | -------------------------------------------------------------------------------- /src/win_oper.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | char *root_width_read(int wid); 5 | char *root_height_read(int wid); 6 | 7 | void border_color_write(int wid, const char *buf); 8 | 9 | char *border_width_read(int wid); 10 | void border_width_write(int wid, const char *buf); 11 | 12 | 13 | char *geometry_width_read(int wid); 14 | void geometry_width_write(int wid, const char *buf); 15 | 16 | char *geometry_height_read(int wid); 17 | void geometry_height_write(int wid, const char *buf); 18 | 19 | char *geometry_x_read(int wid); 20 | void geometry_x_write(int wid, const char *buf); 21 | 22 | char *geometry_y_read(int wid); 23 | void geometry_y_write(int wid, const char *buf); 24 | 25 | 26 | char *mapped_read(int wid); 27 | void mapped_write(int wid, const char *buf); 28 | 29 | 30 | char *ignored_read(int wid); 31 | void ignored_write(int wid, const char *buf); 32 | 33 | 34 | void stack_write(int wid, const char *buf); 35 | 36 | 37 | char *title_read(int wid); 38 | 39 | 40 | char *class_read(int wid); 41 | 42 | 43 | char *event_read(int wid); 44 | 45 | 46 | char *focused_read(int wid); 47 | void focused_write(int wid, const char *buf); 48 | -------------------------------------------------------------------------------- /src/win_xcb.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "x11fs.h" 3 | #include 4 | #include 5 | 6 | X11FS_STATUS xcb_init(); 7 | void xcb_cleanup(); 8 | 9 | bool exists(int wid); 10 | int *list_windows(); 11 | void close_window(int wid); 12 | 13 | int focused(); 14 | void focus(int wid); 15 | 16 | int get_width(int wid); 17 | void set_width(int wid, int width); 18 | 19 | int get_height(int wid); 20 | void set_height(int wid, int height); 21 | 22 | int get_x(int wid); 23 | void set_x(int wid, int x); 24 | 25 | int get_y(int wid); 26 | void set_y(int wid, int y); 27 | 28 | int get_border_width(int wid); 29 | void set_border_width(int wid, int width); 30 | 31 | void set_border_color(int wid, int color); 32 | 33 | bool get_mapped(int wid); 34 | void set_mapped(int wid, bool mapped); 35 | 36 | bool get_ignored(int wid); 37 | void set_ignored(int wid, bool ignored); 38 | 39 | char *get_title(int wid); 40 | 41 | char **get_class(int wid); 42 | 43 | void set_stack_mode(int wid, int position); 44 | #define raise(wid) set_stack_mode(wid, XCB_STACK_MODE_ABOVE) 45 | #define lower(wid) set_stack_mode(wid, XCB_STACK_MODE_BELOW) 46 | 47 | void set_subscription(int wid, int eventmask); 48 | 49 | char *get_events(); 50 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | x11fs X window virtual filesystem 2 | ================================= 3 | 4 | *This is heavily based off wmutils. [Check them out](http://wmutils.io).* 5 | 6 | x11fs is a tool for manipulating X windows. 7 | It creates a vitual filesystem to represent open windows, similar to what /proc does for processes. 8 | This allows windows to be controlled using any language or tool with simple file IO, in a true unix fashion. 9 | 10 | 11 | Build 12 | ----- 13 | 14 | After installing the relevant developement packages for fuse and xcb for your distro (on Ubuntu these are libxcb1-dev, libxcb-icccm4-dev and libfuse-dev), x11fs can be built using the make command. 15 | Installation can be done by invoking make install. 16 | 17 | 18 | Usage 19 | ----- 20 | 21 | See the [wiki](https://github.com/sdhand/x11fs/wiki) for more information. 22 | Or go [here](https://github.com/sdhand/x11fs/wiki/Example-Setup) for a simple example setup. 23 | 24 | Contact 25 | ------- 26 | 27 | Please join #x11fs on freenode if you have any questions or just want to talk about the project. Please be patient however, there are not many members of the channel so it may be inactive at times. 28 | 29 | Thanks to 30 | --------- 31 | 32 | [Luiz de Milon](https://github.com/kori) for helping to come up with the initial idea. 33 | The creators of [wmutils](https://wmutils.io) for providing some inspiration, and some basic xcb code to study. 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016, Samuel Hand 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 5 | 6 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 7 | 8 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 9 | 10 | 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 11 | 12 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 13 | -------------------------------------------------------------------------------- /src/win_oper.c: -------------------------------------------------------------------------------- 1 | #include "win_oper.h" 2 | #include "win_xcb.h" 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "x11fs.h" 8 | 9 | //Specific read and write functions for each file 10 | 11 | void border_color_write(int wid, const char *buf) 12 | { 13 | set_border_color(wid, strtol(buf, NULL, 16)); 14 | } 15 | 16 | char *border_width_read(int wid) 17 | { 18 | int border_width=get_border_width(wid); 19 | if(border_width==-1){ 20 | errno = -EIO; 21 | return NULL; 22 | } 23 | //Work out the size needed to malloc by calling snprintf with a size of 0 24 | char *border_width_string=malloc(snprintf(NULL, 0, "%d\n", border_width)+1); 25 | sprintf(border_width_string, "%d\n", border_width); 26 | return border_width_string; 27 | } 28 | 29 | void border_width_write(int wid, const char *buf) 30 | { 31 | set_border_width(wid, atoi(buf)); 32 | } 33 | 34 | char *root_width_read(int wid) 35 | { 36 | (void) wid; 37 | return geometry_width_read(-1); 38 | } 39 | 40 | char *root_height_read(int wid) 41 | { 42 | (void) wid; 43 | return geometry_height_read(-1); 44 | } 45 | 46 | char *geometry_width_read(int wid) 47 | { 48 | int width=get_width(wid); 49 | if(width==-1){ 50 | errno = -EIO; 51 | return NULL; 52 | } 53 | 54 | char *width_string=malloc(snprintf(NULL, 0, "%d\n", width)+1); 55 | sprintf(width_string, "%d\n", width); 56 | return width_string; 57 | } 58 | 59 | void geometry_width_write(int wid, const char *buf) 60 | { 61 | set_width(wid, atoi(buf)); 62 | } 63 | 64 | char *geometry_height_read(int wid) 65 | { 66 | int height=get_height(wid); 67 | if(height==-1){ 68 | errno = -EIO; 69 | return NULL; 70 | } 71 | 72 | char *height_string=malloc(snprintf(NULL, 0, "%d\n", height)+1); 73 | sprintf(height_string, "%d\n", height); 74 | return height_string; 75 | } 76 | 77 | void geometry_height_write(int wid, const char *buf) 78 | { 79 | set_height(wid, atoi(buf)); 80 | } 81 | 82 | char *geometry_x_read(int wid) 83 | { 84 | int x=get_x(wid); 85 | if(x==-1){ 86 | errno = -EIO; 87 | return NULL; 88 | } 89 | 90 | char *x_string=malloc(snprintf(NULL, 0, "%d\n", x)+1); 91 | sprintf(x_string, "%d\n", x); 92 | return x_string; 93 | } 94 | 95 | void geometry_x_write(int wid, const char *buf) 96 | { 97 | set_x(wid, atoi(buf)); 98 | } 99 | 100 | char *geometry_y_read(int wid) 101 | { 102 | int y=get_y(wid); 103 | if(y==-1){ 104 | errno = -EIO; 105 | return NULL; 106 | } 107 | 108 | char *y_string=malloc(snprintf(NULL, 0, "%d\n", y)+1); 109 | sprintf(y_string, "%d\n", y); 110 | return y_string; 111 | } 112 | 113 | void geometry_y_write(int wid, const char *buf) 114 | { 115 | set_y(wid, atoi(buf)); 116 | } 117 | 118 | char *mapped_read(int wid) 119 | { 120 | return strdup(get_mapped(wid) ? "true\n" : "false\n"); 121 | } 122 | 123 | void mapped_write(int wid, const char *buf) 124 | { 125 | if(!strcmp(buf, "true\n")) 126 | set_mapped(wid, true); 127 | if(!strcmp(buf, "false\n")) 128 | set_mapped(wid, false); 129 | } 130 | 131 | char *ignored_read(int wid) 132 | { 133 | return strdup(get_ignored(wid) ? "true\n" : "false\n"); 134 | } 135 | 136 | void ignored_write(int wid, const char *buf) 137 | { 138 | if(!strcmp(buf, "true\n")) 139 | set_ignored(wid, true); 140 | if(!strcmp(buf, "false\n")) 141 | set_ignored(wid, false); 142 | } 143 | 144 | void stack_write(int wid, const char *buf) 145 | { 146 | if(!strcmp(buf, "raise\n")) 147 | raise(wid); 148 | if(!strcmp(buf, "lower\n")) 149 | lower(wid); 150 | } 151 | 152 | char *title_read(int wid) 153 | { 154 | char *title=get_title(wid); 155 | size_t title_len = strlen(title); 156 | char *title_string=malloc(title_len+2); 157 | memset(title_string, 0, title_len+2); 158 | if ( title_len ) { sprintf(title_string, "%s\n", title); } 159 | free(title); 160 | return title_string; 161 | } 162 | 163 | char *class_read(int wid) 164 | { 165 | char **classes=get_class(wid); 166 | size_t class0_len = strlen(classes[0]), class1_len = strlen(classes[1]); 167 | char *class_string=malloc(class0_len + class1_len + 3); 168 | if ( class0_len ) { 169 | sprintf(class_string, "%s\n", classes[0]); 170 | } 171 | if ( class1_len ) { 172 | sprintf(class_string + class0_len + 1, "%s\n", classes[1]); 173 | } 174 | free(classes[0]); 175 | free(classes[1]); 176 | free(classes); 177 | return class_string; 178 | } 179 | 180 | char *event_read(int wid) 181 | { 182 | return get_events(); 183 | } 184 | 185 | char *focused_read(int wid) 186 | { 187 | (void) wid; 188 | char *focusedwin; 189 | int focusedid=focused(); 190 | if(focusedid){ 191 | focusedwin = malloc(WID_STRING_LENGTH+1); 192 | sprintf(focusedwin, "0x%08x\n", focusedid); 193 | }else{ 194 | focusedwin = malloc(6); 195 | sprintf(focusedwin, "root\n"); 196 | } 197 | return focusedwin; 198 | } 199 | 200 | void focused_write(int wid, const char *buf) 201 | { 202 | (void) wid; 203 | focus(strtol(buf, NULL, 16)); 204 | } 205 | -------------------------------------------------------------------------------- /src/win_xcb.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "x11fs.h" 9 | 10 | //Our connection to xcb and our screen 11 | static xcb_connection_t *conn; 12 | static xcb_screen_t *scrn; 13 | 14 | //Setup our connection to the X server and get the first screen 15 | //TODO: Check how this works with multimonitor setups 16 | X11FS_STATUS xcb_init() 17 | { 18 | conn = xcb_connect(NULL, NULL); 19 | if(xcb_connection_has_error(conn)){ 20 | warnx("Cannot open display: %s", getenv("DISPLAY")); 21 | return X11FS_FAILURE; 22 | } 23 | 24 | scrn = xcb_setup_roots_iterator(xcb_get_setup(conn)).data; 25 | if(!scrn){ 26 | warnx("Cannot retrieve screen information"); 27 | return X11FS_FAILURE; 28 | } 29 | return X11FS_SUCCESS; 30 | } 31 | 32 | //End our connection 33 | void xcb_cleanup(){ 34 | if(conn) 35 | xcb_disconnect(conn); 36 | } 37 | 38 | //check if a window exists 39 | bool exists(int wid) 40 | { 41 | xcb_get_window_attributes_cookie_t attr_c = xcb_get_window_attributes(conn, wid); 42 | xcb_get_window_attributes_reply_t *attr_r = xcb_get_window_attributes_reply(conn, attr_c, NULL); 43 | 44 | if(!attr_r) 45 | return false; 46 | 47 | free(attr_r); 48 | return true; 49 | } 50 | 51 | //List every open window 52 | int *list_windows() 53 | { 54 | //Get the window tree for the root window 55 | xcb_query_tree_cookie_t tree_c = xcb_query_tree(conn, scrn->root); 56 | xcb_query_tree_reply_t *tree_r = xcb_query_tree_reply(conn, tree_c, NULL); 57 | 58 | if(!tree_r) 59 | warnx("Couldn't find the root window's"); 60 | 61 | //Get the array of windows 62 | xcb_window_t *xcb_win_list = xcb_query_tree_children(tree_r); 63 | if(!xcb_win_list) 64 | warnx("Couldn't find the root window's children"); 65 | 66 | int *win_list = malloc(sizeof(int)*(tree_r->children_len+1)); 67 | int i; 68 | for (i=0; ichildren_len; i++) { 69 | win_list[i] = xcb_win_list[i]; 70 | } 71 | 72 | free(tree_r); 73 | 74 | //Null terminate our list 75 | win_list[i]=0; 76 | return win_list; 77 | } 78 | 79 | static xcb_atom_t xcb_atom_get(xcb_connection_t *conn, char *name) 80 | { 81 | xcb_intern_atom_cookie_t cookie = xcb_intern_atom(conn ,0, strlen(name), name); 82 | xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply(conn, cookie, NULL); 83 | return !reply ? XCB_ATOM_STRING : reply->atom; 84 | } 85 | 86 | void close_window(int wid) 87 | { 88 | xcb_icccm_get_wm_protocols_reply_t reply; 89 | bool supports_delete = false; 90 | if (xcb_icccm_get_wm_protocols_reply(conn, xcb_icccm_get_wm_protocols(conn, wid, xcb_atom_get(conn, "WM_PROTOCOLS")), &reply, NULL)) { 91 | for (int i = 0; i != reply.atoms_len; ++i){ 92 | if(reply.atoms[i] == xcb_atom_get(conn, "WM_DELETE_WINDOW")){ 93 | supports_delete=true; 94 | break; 95 | } 96 | } 97 | } 98 | if(supports_delete){ 99 | xcb_client_message_event_t ev; 100 | ev.response_type = XCB_CLIENT_MESSAGE; 101 | ev.sequence = 0; 102 | ev.format = 32; 103 | ev.window = wid; 104 | ev.type = xcb_atom_get(conn, "WM_PROTOCOLS"); 105 | ev.data.data32[0] = xcb_atom_get(conn, "WM_DELETE_WINDOW"); 106 | ev.data.data32[1] = XCB_CURRENT_TIME; 107 | 108 | xcb_send_event(conn, 0, wid, XCB_EVENT_MASK_NO_EVENT, (char *)&ev); 109 | }else 110 | xcb_kill_client(conn, wid); 111 | xcb_flush(conn); 112 | } 113 | 114 | //Get the focused window 115 | int focused() 116 | { 117 | //Ask xcb for the focused window 118 | xcb_get_input_focus_cookie_t focus_c; 119 | xcb_get_input_focus_reply_t *focus_r; 120 | 121 | focus_c = xcb_get_input_focus(conn); 122 | focus_r = xcb_get_input_focus_reply(conn, focus_c, NULL); 123 | 124 | //Couldn't find the focused window 125 | if(!focus_r) 126 | return -1; 127 | 128 | int focused = focus_r->focus; 129 | if(focused==scrn->root) 130 | focused=0; 131 | free(focus_r); 132 | return focused; 133 | } 134 | 135 | //Change the focus 136 | void focus(int wid) 137 | { 138 | xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT, wid, XCB_CURRENT_TIME); 139 | xcb_flush(conn); 140 | } 141 | 142 | //Get the properties of a window (title, class etc) 143 | static xcb_get_property_reply_t *get_prop(int wid, xcb_atom_t property, xcb_atom_t type) 144 | { 145 | xcb_get_property_cookie_t prop_c = xcb_get_property(conn, 0, wid, property, type, 0L, 32L); 146 | return xcb_get_property_reply(conn, prop_c, NULL); 147 | } 148 | 149 | //Get the geometry of a window 150 | static xcb_get_geometry_reply_t *get_geom(int wid) 151 | { 152 | xcb_get_geometry_cookie_t geom_c = xcb_get_geometry(conn, wid); 153 | return xcb_get_geometry_reply(conn, geom_c, NULL); 154 | } 155 | 156 | //Get the attributes of a window (mapped, ignored etc) 157 | static xcb_get_window_attributes_reply_t *get_attr(int wid) 158 | { 159 | xcb_get_window_attributes_cookie_t attr_c = xcb_get_window_attributes(conn, wid); 160 | return xcb_get_window_attributes_reply(conn, attr_c, NULL); 161 | } 162 | 163 | //Bunch of functions to get and set window properties etc. 164 | //All should be fairly self explanatory 165 | 166 | #define DEFINE_NORM_SETTER(name, fn, prop) \ 167 | void set_##name(int wid, int arg) {\ 168 | uint32_t values[] = {arg};\ 169 | fn(conn, wid, prop, values);\ 170 | xcb_flush(conn);\ 171 | } 172 | 173 | DEFINE_NORM_SETTER(border_width, xcb_configure_window, XCB_CONFIG_WINDOW_BORDER_WIDTH); 174 | DEFINE_NORM_SETTER(border_color, xcb_change_window_attributes, XCB_CW_BORDER_PIXEL); 175 | DEFINE_NORM_SETTER(ignored, xcb_change_window_attributes, XCB_CW_OVERRIDE_REDIRECT); 176 | DEFINE_NORM_SETTER(width, xcb_configure_window, XCB_CONFIG_WINDOW_WIDTH); 177 | DEFINE_NORM_SETTER(height, xcb_configure_window, XCB_CONFIG_WINDOW_HEIGHT); 178 | DEFINE_NORM_SETTER(x, xcb_configure_window, XCB_CONFIG_WINDOW_X); 179 | DEFINE_NORM_SETTER(y, xcb_configure_window, XCB_CONFIG_WINDOW_Y); 180 | DEFINE_NORM_SETTER(stack_mode, xcb_configure_window, XCB_CONFIG_WINDOW_STACK_MODE); 181 | DEFINE_NORM_SETTER(subscription, xcb_change_window_attributes, XCB_CW_EVENT_MASK); 182 | 183 | #define DEFINE_GEOM_GETTER(name) \ 184 | int get_##name(int wid)\ 185 | {\ 186 | if(wid==-1)\ 187 | wid=scrn->root;\ 188 | xcb_get_geometry_reply_t *geom_r = get_geom(wid);\ 189 | if(!geom_r)\ 190 | return -1;\ 191 | \ 192 | int name = geom_r->name;\ 193 | free(geom_r);\ 194 | return name;\ 195 | } 196 | 197 | DEFINE_GEOM_GETTER(width); 198 | DEFINE_GEOM_GETTER(height); 199 | DEFINE_GEOM_GETTER(x); 200 | DEFINE_GEOM_GETTER(y); 201 | DEFINE_GEOM_GETTER(border_width); 202 | 203 | int get_mapped(int wid) 204 | { 205 | xcb_get_window_attributes_reply_t *attr_r = get_attr(wid); 206 | if(!attr_r) 207 | return -1; 208 | 209 | int map_state = attr_r->map_state; 210 | free(attr_r); 211 | return map_state == XCB_MAP_STATE_VIEWABLE; 212 | } 213 | 214 | void set_mapped(int wid, int mapstate) 215 | { 216 | if(mapstate) 217 | xcb_map_window(conn, wid); 218 | else 219 | xcb_unmap_window(conn, wid); 220 | xcb_flush(conn); 221 | } 222 | 223 | int get_ignored(int wid) 224 | { 225 | xcb_get_window_attributes_reply_t *attr_r = get_attr(wid); 226 | if(!attr_r) 227 | return -1; 228 | 229 | int or = attr_r->override_redirect; 230 | free(attr_r); 231 | return or; 232 | } 233 | 234 | char *get_title(int wid) 235 | { 236 | xcb_get_property_reply_t *prop_r = get_prop(wid, XCB_ATOM_WM_NAME, XCB_ATOM_STRING); 237 | if(!prop_r) 238 | return NULL; 239 | 240 | char *title = (char *) xcb_get_property_value(prop_r); 241 | int len = xcb_get_property_value_length(prop_r); 242 | char *title_string=malloc(len+1); 243 | sprintf(title_string, "%.*s", len, title); 244 | free(prop_r); 245 | return title_string; 246 | } 247 | 248 | //Get an array of the classes of the window 249 | char **get_class(int wid) 250 | { 251 | char **classes = malloc(sizeof(char*)*2); 252 | xcb_get_property_reply_t *prop_r = get_prop(wid, XCB_ATOM_WM_CLASS, XCB_ATOM_STRING); 253 | if(!prop_r) { 254 | free(classes); 255 | return NULL; 256 | } 257 | 258 | char *class; 259 | class=(char *) xcb_get_property_value(prop_r); 260 | classes[0]=strdup(class); 261 | classes[1]=strdup(class+strlen(class)+1); 262 | 263 | free(prop_r); 264 | return classes; 265 | } 266 | 267 | #define subscribe(wid) set_subscription(wid, XCB_EVENT_MASK_ENTER_WINDOW|XCB_EVENT_MASK_LEAVE_WINDOW) 268 | #define unsubscribe(wid) set_subscription(wid, XCB_EVENT_MASK_NO_EVENT) 269 | 270 | //Get events for a window 271 | char *get_events(){ 272 | //Subscribe to events from all windows 273 | uint32_t values[] = {XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY}; 274 | xcb_change_window_attributes(conn, scrn->root, XCB_CW_EVENT_MASK, values); 275 | int *windows=list_windows(); 276 | int wid; 277 | while((wid=*(windows++))){ 278 | if(!get_ignored(wid)) 279 | subscribe(wid); 280 | } 281 | 282 | char *event_string; 283 | bool done = false; 284 | while(!done){ 285 | xcb_generic_event_t *event = xcb_wait_for_event(conn); 286 | int wid; 287 | const char * ev_id = NULL; 288 | xcb_enter_notify_event_t *e; 289 | switch (event->response_type & ~0x80){ 290 | case XCB_CREATE_NOTIFY: 291 | ev_id = "CREATE"; 292 | wid = ((xcb_create_notify_event_t * )event)->window; 293 | break; 294 | 295 | case XCB_DESTROY_NOTIFY: 296 | ev_id = "DESTROY"; 297 | wid = ((xcb_destroy_notify_event_t * )event)->window; 298 | break; 299 | 300 | case XCB_ENTER_NOTIFY: 301 | e=(xcb_enter_notify_event_t*) event; 302 | printf("%d\n", e->detail); 303 | if((e->detail==0) || (e->detail==1) || (e->detail==4) || (e->detail==3)){ 304 | ev_id = "ENTER"; 305 | wid = ((xcb_enter_notify_event_t * )event)->event; 306 | } 307 | break; 308 | 309 | case XCB_MAP_NOTIFY: 310 | ev_id = "MAP"; 311 | wid = ((xcb_map_notify_event_t * )event)->window; 312 | break; 313 | 314 | case XCB_UNMAP_NOTIFY: 315 | ev_id = "UNMAP"; 316 | wid = ((xcb_unmap_notify_event_t * )event)->window; 317 | break; 318 | } 319 | 320 | if ( ev_id ) { 321 | event_string = malloc(snprintf(NULL, 0, "%s: 0x%08x\n", ev_id, wid) + 1); 322 | sprintf(event_string, "%s: 0x%08x\n", ev_id, wid); 323 | done = true; 324 | } 325 | } 326 | //Unsubscribe from events 327 | unsubscribe(scrn->root); 328 | while((wid=*(windows++))){ 329 | unsubscribe(wid); 330 | } 331 | 332 | return event_string; 333 | } 334 | -------------------------------------------------------------------------------- /src/x11fs.c: -------------------------------------------------------------------------------- 1 | #define FUSE_USE_VERSION 26 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include "x11fs.h" 11 | #include "win_xcb.h" 12 | #include "win_oper.h" 13 | 14 | 15 | //Represents a single file, contains pointers to the functions to call to read and write for that file 16 | struct x11fs_file{ 17 | const char *path; 18 | int mode; 19 | bool direct_io; 20 | bool dir; 21 | char *(*read)(int wid); 22 | void (*write)(int wid, const char *buf); 23 | }; 24 | 25 | 26 | //Our file layout 27 | static const struct x11fs_file x11fs_files[] = { 28 | {"/", S_IFDIR | 0700, false, true, NULL, NULL}, 29 | {"/root", S_IFDIR | 0700, false, true, NULL, NULL}, 30 | {"/root/geometry", S_IFDIR | 0700, false, true, NULL, NULL}, 31 | {"/root/geometry/width", S_IFREG | 0400, false, false, root_width_read, NULL}, 32 | {"/root/geometry/height", S_IFREG | 0400, false, false, root_height_read, NULL}, 33 | {"/0x*", S_IFDIR | 0700, false, true, NULL, NULL}, 34 | {"/0x*/border", S_IFDIR | 0700, false, true, NULL, NULL}, 35 | {"/0x*/border/color", S_IFREG | 0200, false, false, NULL, border_color_write}, 36 | {"/0x*/border/width", S_IFREG | 0600, false, false, border_width_read, border_width_write}, 37 | {"/0x*/geometry", S_IFDIR | 0700, false, true, NULL, NULL}, 38 | {"/0x*/geometry/width", S_IFREG | 0600, false, false, geometry_width_read, geometry_width_write}, 39 | {"/0x*/geometry/height", S_IFREG | 0600, false, false, geometry_height_read, geometry_height_write}, 40 | {"/0x*/geometry/x", S_IFREG | 0600, false, false, geometry_x_read, geometry_x_write}, 41 | {"/0x*/geometry/y", S_IFREG | 0600, false, false, geometry_y_read, geometry_y_write}, 42 | {"/0x*/mapped", S_IFREG | 0600, false, false, mapped_read, mapped_write}, 43 | {"/0x*/ignored", S_IFREG | 0600, false, false, ignored_read, ignored_write}, 44 | {"/0x*/stack", S_IFREG | 0200, false, false, NULL, stack_write}, 45 | {"/0x*/title", S_IFREG | 0400, false, false, title_read, NULL}, 46 | {"/0x*/class", S_IFREG | 0400, false, false, class_read, NULL}, 47 | {"/focused", S_IFREG | 0600, false, false, focused_read, focused_write}, 48 | {"/event", S_IFREG | 0400, true, false, event_read, NULL}, 49 | }; 50 | 51 | //Pull out the id of a window from a path 52 | static int get_winid(const char *path) 53 | { 54 | int wid = -1; 55 | //Check if the path is to a window directory or it's contents 56 | if(!strncmp(path, "/0x", 3) && sscanf(path, "/0x%08x", &wid) != 1){ 57 | wid = 0; 58 | } 59 | 60 | return wid; 61 | } 62 | 63 | //Runs when our filesystem is unmounted 64 | static void x11fs_destroy() 65 | { 66 | xcb_cleanup(); 67 | } 68 | 69 | //Doesn't actually do anything but it's required we implement this as it will get called when writing a value to a file that's shorter than the current contents 70 | static int x11fs_truncate(const char *path, off_t size) 71 | { 72 | return 0; 73 | } 74 | 75 | 76 | //Gives information about a file 77 | static int x11fs_getattr(const char *path, struct stat *stbuf) 78 | { 79 | //zero the information about the file 80 | memset(stbuf, 0, sizeof(struct stat)); 81 | 82 | //loop through our filesystem layout and check if the path matches one in our layout 83 | size_t files_length = sizeof(x11fs_files)/sizeof(struct x11fs_file); 84 | for(size_t i=0; ist_nlink = x11fs_files[i].dir ? 2 : 1; 94 | stbuf->st_mode = x11fs_files[i].mode; 95 | 96 | //Set the size of a file by getting its contents 97 | //If the file uses direct IO (it acts like a stream, just set size to 0) 98 | stbuf->st_size = 0; 99 | if((x11fs_files[i].read != NULL) && !(x11fs_files[i].direct_io)) 100 | { 101 | char *read_string=x11fs_files[i].read(wid); 102 | if(!read_string) 103 | return errno; 104 | 105 | stbuf->st_size=strlen(read_string); 106 | free(read_string); 107 | } 108 | 109 | return 0; 110 | } 111 | } 112 | 113 | //No such file or directory 114 | return -ENOENT; 115 | } 116 | 117 | //Gives the contents of a directory 118 | static int x11fs_readdir(const char *path, void *buf, fuse_fill_dir_t filler, off_t offset, struct fuse_file_info *fi) 119 | { 120 | (void) offset; 121 | (void) fi; 122 | 123 | //If the path is to a non existant window says so 124 | int wid; 125 | if((wid = get_winid(path)) != -1 && !exists(wid)){ 126 | return -ENOENT; 127 | } 128 | 129 | bool exists = false; 130 | bool dir = false; 131 | 132 | 133 | //Iterate through our filesystem layout 134 | size_t files_length = sizeof(x11fs_files)/sizeof(struct x11fs_file); 135 | for(size_t i=0; i strlen(path)) 159 | && ((matchpath+len)[0] == '/') 160 | && !strchr(matchpath+len+1, '/')){ 161 | dir = true; 162 | 163 | //If it's a wildcarded window in our layout with the list of actual windows 164 | if(!strcmp(matchpath, "/0x*")){ 165 | //Get the list of windows 166 | int *wins = list_windows(); 167 | 168 | //Add each window to our directory listing 169 | for(int j=0; wins[j]; j++){ 170 | int win = wins[j]; 171 | char *win_string; 172 | 173 | win_string = malloc(sizeof(char)*(WID_STRING_LENGTH)); 174 | sprintf(win_string, "0x%08x", win); 175 | 176 | filler(buf, win_string, NULL, 0); 177 | 178 | free(win_string); 179 | } 180 | 181 | free(wins); 182 | } 183 | //Otherwise just add the file to our directory listing 184 | else 185 | filler(buf, matchpath+len+1, NULL, 0); 186 | } 187 | } 188 | free(matchpath); 189 | } 190 | 191 | if(!exists) 192 | return -ENOENT; 193 | 194 | //Add any extra needed elements to the directory list 195 | if(dir){ 196 | filler(buf, ".", NULL, 0); 197 | filler(buf, "..", NULL, 0); 198 | }else 199 | return -ENOTDIR; 200 | 201 | return 0; 202 | } 203 | 204 | 205 | //Open a file, just check if it exists and set non seekable 206 | static int x11fs_open(const char *path, struct fuse_file_info *fi) 207 | { 208 | //Iterate through our layout 209 | size_t files_length = sizeof(x11fs_files)/sizeof(struct x11fs_file); 210 | for(size_t i=0; inonseekable=1; 224 | fi->direct_io=x11fs_files[i].direct_io; 225 | return 0; 226 | } 227 | } 228 | return -ENOENT; 229 | } 230 | 231 | //Read a file 232 | static int x11fs_read(const char *path, char *buf, size_t size, off_t offset, struct fuse_file_info *fi) 233 | { 234 | //Iterate through our layout 235 | size_t files_length = sizeof(x11fs_files)/sizeof(struct x11fs_file); 236 | for(size_t i=0; i len) 259 | size = len; 260 | memcpy(buf, result, size); 261 | free(result); 262 | return size; 263 | } 264 | } 265 | return -ENOENT; 266 | } 267 | 268 | //Write to a file 269 | static int x11fs_write(const char *path, const char *buf, size_t size, off_t offset, struct fuse_file_info *fi) 270 | { 271 | //Iterate through our layout 272 | size_t files_length = sizeof(x11fs_files)/sizeof(struct x11fs_file); 273 | for(size_t i=0; i11) 306 | return -ENOSYS; 307 | 308 | //Close the window 309 | close_window(wid); 310 | return 0; 311 | } 312 | 313 | //Functions for fuse to run on IO events 314 | static struct fuse_operations x11fs_operations = { 315 | .destroy = x11fs_destroy, 316 | .truncate = x11fs_truncate, 317 | .getattr = x11fs_getattr, 318 | .readdir = x11fs_readdir, 319 | .open = x11fs_open, 320 | .read = x11fs_read, 321 | .write = x11fs_write, 322 | .rmdir = x11fs_rmdir, 323 | }; 324 | 325 | //Just setup our connection to X then let fuse handle the rest 326 | int main(int argc, char **argv) 327 | { 328 | if(xcb_init()!=X11FS_SUCCESS){ 329 | fputs("Failed to setup xcb. Quiting...\n", stderr); 330 | return 1; 331 | } 332 | return fuse_main(argc, argv, &x11fs_operations, NULL); 333 | } 334 | --------------------------------------------------------------------------------