diff options
-rw-r--r-- | Makefile | 5 | ||||
-rw-r--r-- | clipfs.c | 121 |
2 files changed, 112 insertions, 14 deletions
@@ -1,6 +1,7 @@ CFLAGS += -Wall -Wextra -Wpedantic -CFLAGS += `pkg-config --cflags xcb xcb-util xcb-icccm` -LDLIBS += `pkg-config --libs xcb xcb-util xcb-icccm` +CFLAGS += -DFUSE_USE_VERSION=31 +CFLAGS += `pkg-config --cflags fuse3 xcb xcb-util xcb-icccm` +LDLIBS += `pkg-config --libs fuse3 xcb xcb-util xcb-icccm` all: clipfs @@ -1,41 +1,68 @@ +#include <errno.h> #include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <fuse3/fuse.h> #include <xcb/xcb.h> #include <xcb/xcb_icccm.h> #include <xcb/xcb_util.h> -xcb_connection_t * c; -xcb_screen_t * s; -xcb_window_t w; +struct selection +{ + uint32_t len; + char * data; +}; + +void update_selection(void); +xcb_atom_t get_atom(xcb_connection_t *, const char *); +int clip_getattr(const char *, struct stat *, struct fuse_file_info *); +int clip_readdir(const char *, void *, fuse_fill_dir_t, off_t, struct fuse_file_info *, enum fuse_readdir_flags); +int clip_open(const char *, struct fuse_file_info *); +int clip_read(const char *, char *, size_t, off_t, struct fuse_file_info *); const uint32_t mask = XCB_CW_OVERRIDE_REDIRECT | XCB_CW_EVENT_MASK; const uint32_t values[] = {1, XCB_EVENT_MASK_PROPERTY_CHANGE}; +const struct fuse_operations clip_ops = { + .getattr = clip_getattr, + .readdir = clip_readdir, + .open = clip_open, + .read = clip_read, +}; + +static xcb_connection_t * c; +static xcb_screen_t * s; +static xcb_window_t w; +static struct selection x = {.len = 0, .data = NULL}; -xcb_atom_t get_atom(xcb_connection_t *, const char *); int main(int argc, char ** argv) { - (void) argc; - (void) argv; c = xcb_connect(NULL, NULL); s = xcb_setup_roots_iterator(xcb_get_setup(c)).data; w = xcb_generate_id(c); - xcb_atom_t TARGET_PROPERTY = get_atom(c, "_TARGET_SELECTION"); - xcb_atom_t UTF8_STRING = get_atom(c, "UTF8_STRING"); // TODO: Actually, focus on "rawest" format at first. xcb_create_window( c, XCB_COPY_FROM_PARENT, w, s->root, 0, 0, 1, 1, 0, XCB_COPY_FROM_PARENT, s->root_visual, mask, values); + xcb_flush(c); + fuse_main(argc, argv, &clip_ops, NULL); + xcb_disconnect(c); +} + + +void update_selection(void) +{ + xcb_atom_t TARGET_PROPERTY = get_atom(c, "_TARGET_SELECTION"); + xcb_atom_t UTF8_STRING = get_atom(c, "UTF8_STRING"); // TODO: Actually, focus on "rawest" format at first. // TODO-maybe: Look for the selection owner first? xcb_convert_selection(c, w, XCB_ATOM_PRIMARY, UTF8_STRING, TARGET_PROPERTY, XCB_CURRENT_TIME); xcb_flush(c); xcb_generic_event_t * e; - int done = 0; // TODO: Look into continuous notifications? + int done = 0; // TODO: Look into continuous notifications and keeping the data somewhere? while (!done) { e = xcb_wait_for_event(c); - if (NULL == e) break; + if (NULL == e) return; if (XCB_SELECTION_NOTIFY != XCB_EVENT_RESPONSE_TYPE(e)) { free(e); @@ -48,7 +75,11 @@ int main(int argc, char ** argv) xcb_get_property_cookie_t cookie = xcb_icccm_get_text_property(c, n->requestor, n->property); if (xcb_icccm_get_text_property_reply(c, cookie, &prop, NULL)) { - printf("%.*s\n", prop.name_len, prop.name); + x.data = realloc(x.data, prop.name_len); + if (NULL == x.data) + x.len = 0; + x.len = prop.name_len; + memcpy(x.data, prop.name, prop.name_len); xcb_icccm_get_text_property_reply_wipe(&prop); xcb_delete_property(c, n->requestor, n->property); done = 1; @@ -56,9 +87,9 @@ int main(int argc, char ** argv) } free(e); } - xcb_disconnect(c); } + xcb_atom_t get_atom(xcb_connection_t * c, const char * name) { xcb_intern_atom_cookie_t cookie = xcb_intern_atom(c, 0, strlen(name), name); @@ -67,3 +98,69 @@ xcb_atom_t get_atom(xcb_connection_t * c, const char * name) free(reply); return atom; } + + +int clip_getattr(const char * path, struct stat * st, struct fuse_file_info * fi) +{ + (void) fi; + update_selection(); // TODO: The content is updated way too often. + memset(st, 0, sizeof(struct stat)); + if (0 == strcmp(path, "/")) + { + st->st_mode = S_IFDIR | 0755; + st->st_nlink = 2; + } + else if (0 == strcmp(path + 1, "primary")) + { + st->st_mode = S_IFREG | 0444; + st->st_nlink = 1; + st->st_size = x.len; + } + else + return -ENOENT; + return 0; +} + + +int clip_readdir( + const char * path, void * buf, fuse_fill_dir_t filler, off_t offset, struct fuse_file_info * fi, + enum fuse_readdir_flags flags) +{ + (void) offset; + (void) fi; + (void) flags; + if (0 != strcmp(path, "/")) + return -ENOENT; + filler(buf, ".", NULL, 0, 0); + filler(buf, "..", NULL, 0, 0); + filler(buf, "primary", NULL, 0, 0); + return 0; +} + + +int clip_open(const char * path, struct fuse_file_info * fi) +{ + if (0 != strcmp(path + 1, "primary")) + return -ENOENT; + if (O_RDONLY != (fi->flags & O_ACCMODE)) + return -EACCES; + return 0; // TODO: Maybe queue selection content retrieval here? +} + + +int clip_read(const char * path, char * buf, size_t size, off_t offset, struct fuse_file_info * fi) +{ + (void) fi; + if (0 != strcmp(path + 1, "primary")) + return -ENOENT; + update_selection(); // TODO: The content is updated way too often. + if (offset < x.len) + { + if (offset + size > x.len) + size = x.len - offset; + memcpy(buf, x.data + offset, size); + } + else + size = 0; + return size; +} |