From: Michael D. Lowis Date: Fri, 12 Oct 2018 15:04:03 +0000 (-0400) Subject: added placeholders for edit and registrar commands. X-Git-Url: https://git.mdlowis.com/?a=commitdiff_plain;h=e6647bdd10fd6c4a891ffcfaa94e1eded55e018a;p=projs%2Ftide.git added placeholders for edit and registrar commands. --- diff --git a/.gitignore b/.gitignore index 790cbd6..5763852 100644 --- a/.gitignore +++ b/.gitignore @@ -55,3 +55,4 @@ hl-cpp pty tctl flaws.txt +edit diff --git a/Makefile b/Makefile index f2a4d82..9f50973 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,5 @@ INCS = -Iinc/ -MAKEFLAGS = -j4 -BINS = tide +BINS = tide registrar edit MAN1 = docs/tide.1 LIBEDIT_OBJS = \ @@ -51,6 +50,7 @@ libedit.a: $(LIBEDIT_OBJS) $(AR) $(ARFLAGS) $@ $^ tide: src/tide.o libedit.a +registrar: src/registrar.o tests/libedit: tests/libedit.o tests/lib/buf.o tests/lib/utf8.o tests/lib/win.o libedit.a # define implicit rule for building binaries diff --git a/TODO.md b/TODO.md index 2346079..45dfd2f 100644 --- a/TODO.md +++ b/TODO.md @@ -2,11 +2,11 @@ ## STAGING -* implement new version of tfetch (plumb) * implement tide registrar -* implement tctl command -* gap buffer does not handle UTF-8 currently +* implement edit (tctl) command +* implement new version of tfetch (plumb) * Line - Get the current line number(s) containing the selection +* gap buffer does not handle UTF-8 currently ## UNCERTAIN diff --git a/inc/x11.h b/inc/x11.h new file mode 100644 index 0000000..87ed333 --- /dev/null +++ b/inc/x11.h @@ -0,0 +1,89 @@ +#include +#include +#include +#include +#include +#include +#include + +typedef struct XConf { + int fd, screen, width, height; + Window root; + Display* display; + Visual* visual; + Colormap colormap; + unsigned depth; + Window self; + XftDraw* xft; + Pixmap pixmap; + XIC xic; + XIM xim; + GC gc; + void (*eventfns[LASTEvent])(struct XConf*, XEvent*); +} XConf; + +static int x11_init(XConf* x) { + signal(SIGPIPE, SIG_IGN); // Ignore the SIGPIPE signal + setlocale(LC_CTYPE, ""); + XSetLocaleModifiers(""); + /* open the X display and get basic attributes */ + if (!(x->display = XOpenDisplay(0))) + return -1; + x->root = DefaultRootWindow(x->display); + XWindowAttributes wa; + XGetWindowAttributes(x->display, x->root, &wa); + x->visual = wa.visual; + x->colormap = wa.colormap; + x->screen = DefaultScreen(x->display); + x->depth = DefaultDepth(x->display, x->screen); + return 0; +} + +static void x11_mkwin(XConf* x, int width, int height, int evmask) { + /* create the main window */ + x->width = width, x->height = height; + x->self = XCreateSimpleWindow( + x->display, x->root, 0, 0, x->width, x->height, 0, x->depth, -1); + /* register interest in the delete window message */ + Atom wmDeleteMessage = XInternAtom(x->display, "WM_DELETE_WINDOW", False); + XSetWMProtocols(x->display, x->self, &wmDeleteMessage, 1); + /* setup window attributes and events */ + XSetWindowAttributes swa; + swa.backing_store = WhenMapped; + swa.bit_gravity = NorthWestGravity; + XChangeWindowAttributes(x->display, x->self, CWBackingStore|CWBitGravity, &swa); + XSelectInput(x->display, x->self, evmask); +} + +static void x11_init_gc(XConf* x) { + /* set input methods */ + if ((x->xim = XOpenIM(x->display, 0, 0, 0))) + x->xic = XCreateIC(x->xim, XNInputStyle, XIMPreeditNothing|XIMStatusNothing, XNClientWindow, x->self, XNFocusWindow, x->self, NULL); + /* initialize pixmap and drawing context */ + x->pixmap = XCreatePixmap(x->display, x->self, x->width, x->height, x->depth); + x->xft = XftDrawCreate(x->display, x->pixmap, x->visual, x->colormap); + /* initialize the graphics context */ + XGCValues gcv; + gcv.foreground = WhitePixel(x->display, x->screen); + gcv.graphics_exposures = False; + x->gc = XCreateGC(x->display, x->self, GCForeground|GCGraphicsExposures, &gcv); +} + +static void x11_show(XConf* x) { + /* simulate an initial resize and map the window */ + XConfigureEvent ce; + ce.type = ConfigureNotify; + ce.width = x->width; + ce.height = x->height; + XSendEvent(x->display, x->self, False, StructureNotifyMask, (XEvent *)&ce); + XMapWindow(x->display, x->self); +} + +static void x11_event_loop(XConf* x) { + for (XEvent e;;) { + XNextEvent(x->display, &e); + if (x->eventfns[e.type]) + x->eventfns[e.type](x, &e); + for (int status; waitpid(-1, &status, WNOHANG) > 0;); + } +} diff --git a/src/edit.c b/src/edit.c new file mode 100644 index 0000000..3381668 --- /dev/null +++ b/src/edit.c @@ -0,0 +1,148 @@ +#include +#include +#include +#include +#include +#include + +int main(int argc, char** argv) { + return 0; +} + +#if 0 +struct { + Display* display; + Window root; + Window self; + int error; +} X; + +size_t WinCount; +Window* Windows; +char** WinFiles; + +static void get_windows(Window** wins, char*** files, size_t* nwins); +static int error_handler(Display* disp, XErrorEvent* ev); +static void* prop_get(Window win, char* propname, Atom type, unsigned long* nitems); +static void prop_set(Window win, char* propname, Atom type, int format, void* items, unsigned long nitems); +static void edit(char* path); +static Window win_byfile(char* path); +static void focus_window(Window w, char* addr); +static void get_abspath(char* path, char** abspath, char** addr); + +/* Main Routine + ******************************************************************************/ +int main(int argc, char** argv) { + if (!(X.display = XOpenDisplay(0))) + die("could not open display"); + X.root = DefaultRootWindow(X.display); + X.self = XCreateSimpleWindow(X.display, X.root, 0, 0, 1, 1, 0, 0, 0); + XSetErrorHandler(error_handler); + get_windows(&Windows, &WinFiles, &WinCount); + + for (int i = 1; i < argc; i++) { + bool last = (i == argc-1); + char *orig = argv[i], *path = NULL, *addr = NULL; + get_abspath(orig, &path, &addr); + + Window win = win_byfile(path); + if (!win) { + fprintf(stderr, "edit(%s)\n", argv[i]); + edit(argv[i]); + } else if (last) { + fprintf(stderr, "focus(%#x,%s)\n", (int)win, addr); + focus_window(win, addr); + } + free(path); + } + + XFlush(X.display); + return 0; +} + +static void get_windows(Window** wins, char*** files, size_t* nwins) { + XGrabServer(X.display); + unsigned long nwindows = 0, nactive = 0, nstrings = 0; + Window *windows = prop_get(X.root, "TIDE_WINDOWS", XA_WINDOW, &nwindows); + Window *active = calloc(nwindows, sizeof(Window)); + char **wfiles = calloc(nwindows, sizeof(char*)); + Atom xa_comm = XInternAtom(X.display, "TIDE_COMM", False); + for (int i = 0; i < nwindows; i++) { + X.error = 0; + int nprops; + Atom* props = XListProperties(X.display, windows[i], &nprops); + if (!props || X.error) continue; + for (int x = 0; x < nprops; x++) { + if (props[x] == xa_comm) { + active[nactive] = windows[i]; + wfiles[nactive] = prop_get(windows[i], "TIDE_FILE", XA_STRING, &nstrings); + nactive++; + break; + } + } + XFree(props); + } + prop_set(X.root, "TIDE_WINDOWS", XA_WINDOW, 32, active, nactive); + XSync(X.display, False); + XUngrabServer(X.display); + XFree(windows); + *wins = active, *files = wfiles, *nwins = nactive; +} + +static int error_handler(Display* disp, XErrorEvent* ev) { + X.error = ev->error_code; + return 0; +} + +static void* prop_get(Window win, char* propname, Atom type, unsigned long* nitems) { + Atom rtype, prop = XInternAtom(X.display, propname, False); + unsigned long rformat = 0, nleft = 0; + unsigned char* data = NULL; + XGetWindowProperty(X.display, win, prop, 0, -1, False, type, &rtype, + (int*)&rformat, nitems, &nleft, &data); + if (rtype != type) + data = NULL, *nitems = 0; + return data; +} + +static void prop_set(Window win, char* propname, Atom type, int format, void* items, unsigned long nitems) { + Atom prop = XInternAtom(X.display, propname, False); + XChangeProperty(X.display, win, prop, type, format, PropModeReplace, items, (int)nitems); +} + +static void edit(char* path) { + if (fork() == 0) + exit(execvp("tide", (char*[]){ "tide", path, NULL })); +} + +static Window win_byfile(char* path) { + for (int i = 0; i < WinCount; i++) + if (WinFiles[i] && !strcmp(path, WinFiles[i])) + return Windows[i]; + return (Window)0; +} + +static void focus_window(Window w, char* addr) { + XEvent ev = {0}; + ev.xclient.type = ClientMessage; + ev.xclient.send_event = True; + ev.xclient.message_type = XInternAtom(X.display, "_NET_ACTIVE_WINDOW", False); + ev.xclient.window = w; + ev.xclient.format = 32; + long mask = SubstructureRedirectMask | SubstructureNotifyMask; + XSendEvent(X.display, X.root, False, mask, &ev); + XMapRaised(X.display, w); + if (addr && *addr) + prop_set(w, "TIDE_COMM", XA_STRING, 8, addr, strlen(addr)); + XFlush(X.display); +} + +void get_abspath(char* path, char** abspath, char** addr) { + path = stringdup(path); + char* faddr = strrchr(path, ':'); + if (faddr) *(faddr++) = '\0'; + char* rpath = realpath(path, NULL); + if (!rpath) rpath = path; + *abspath = rpath, *addr = faddr; +} +#endif diff --git a/src/registrar.c b/src/registrar.c new file mode 100644 index 0000000..66b50cc --- /dev/null +++ b/src/registrar.c @@ -0,0 +1,68 @@ +#include +#include + +typedef struct TWindow { + struct TWindow* next; + Window win; +} TWindow; + +Atom XA_REGISTRAR, XA_ADD, XA_DEL, XA_OPEN; +TWindow* Windows = NULL; + +static void win_add(Window id) { + TWindow* win = calloc(1, sizeof(TWindow)); + win->win = id; + win->next = Windows; + Windows = win; +} + +static void win_del(Window id) { + if (!Windows) return; + if (Windows->win == id) { + TWindow* deadite = Windows; + Windows = deadite->next; + free(deadite); + } else { + TWindow* w = Windows; + for (;w && w->next && (w->next->win != id); w = w->next); + if (w && w->next) { + TWindow* deadite = w->next; + w->next = deadite->next; + free(deadite); + } + } +} + +void selclear(XConf* x, XEvent* e) { + exit(0); +} + +void clientmsg(XConf* x, XEvent* e) { + if (XA_REGISTRAR != e->xclient.message_type) + return; + if (XA_ADD == e->xclient.data.l[0]) + win_add(e->xclient.window); + else if (XA_DEL == e->xclient.data.l[0]) + win_del(e->xclient.window); + else if (XA_OPEN == e->xclient.data.l[0]) + puts("open"); +} + +int main(int argc, char** argv) { + XConf x; + x11_init(&x); + x11_mkwin(&x, 1, 1, PropertyChangeMask); + XA_REGISTRAR = XInternAtom(x.display, "TIDE_REGISTRAR", 0); + XA_ADD = XInternAtom(x.display, "ADD", 0); + XA_DEL = XInternAtom(x.display, "DEL", 0); + XA_OPEN = XInternAtom(x.display, "OPEN", 0); + x.eventfns[SelectionClear] = selclear; + x.eventfns[ClientMessage] = clientmsg; + if (None == XGetSelectionOwner(x.display, XA_REGISTRAR)) { + XSetSelectionOwner(x.display, XA_REGISTRAR, x.self, CurrentTime); + if (x.self == XGetSelectionOwner(x.display, XA_REGISTRAR)) { + x11_event_loop(&x); + } + } + return 1; +}