From 0f0ad5dfdc2f500230984d449721757ba5491e83 Mon Sep 17 00:00:00 2001 From: spy Date: Fri, 25 Feb 2022 16:27:27 +0000 Subject: [PATCH] first commit --- LICENSE | 23 + Makefile | 65 +++ README | 22 + README.md | 2 + TODO | 4 + arg.h | 48 ++ config.def.h | 66 +++ config.h | 58 +++ config.mk | 33 ++ tabbed | Bin 0 -> 32368 bytes tabbed.1 | 171 +++++++ tabbed.c | 1376 ++++++++++++++++++++++++++++++++++++++++++++++++++ tabbed.o | Bin 0 -> 41592 bytes xembed | Bin 0 -> 14464 bytes xembed.1 | 35 ++ xembed.c | 45 ++ xembed.o | Bin 0 -> 2320 bytes 17 files changed, 1948 insertions(+) create mode 100644 LICENSE create mode 100644 Makefile create mode 100644 README create mode 100644 README.md create mode 100644 TODO create mode 100644 arg.h create mode 100644 config.def.h create mode 100644 config.h create mode 100644 config.mk create mode 100755 tabbed create mode 100644 tabbed.1 create mode 100644 tabbed.c create mode 100644 tabbed.o create mode 100755 xembed create mode 100644 xembed.1 create mode 100644 xembed.c create mode 100644 xembed.o diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..d8e9678 --- /dev/null +++ b/LICENSE @@ -0,0 +1,23 @@ +MIT/X Consortium License + +© 2009-2011 Enno Boland +© 2011,2015 Connor Lane Smith +© 2012-2015 Christoph Lohmann <20h@r-36.net> + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..1b95d15 --- /dev/null +++ b/Makefile @@ -0,0 +1,65 @@ +# tabbed - tabbing interface +# See LICENSE file for copyright and license details. + +include config.mk + +SRC = tabbed.c xembed.c +OBJ = ${SRC:.c=.o} +BIN = ${OBJ:.o=} + +all: options ${BIN} + +options: + @echo tabbed build options: + @echo "CFLAGS = ${CFLAGS}" + @echo "LDFLAGS = ${LDFLAGS}" + @echo "CC = ${CC}" + +.c.o: + @echo CC $< + @${CC} -c ${CFLAGS} $< + +${OBJ}: config.h config.mk + +config.h: + @echo creating $@ from config.def.h + @cp config.def.h $@ + +.o: + @echo CC -o $@ + @${CC} -o $@ $< ${LDFLAGS} + +clean: + @echo cleaning + @rm -f ${BIN} ${OBJ} tabbed-${VERSION}.tar.gz + +dist: clean + @echo creating dist tarball + @mkdir -p tabbed-${VERSION} + @cp -R LICENSE Makefile README config.def.h config.mk \ + tabbed.1 arg.h ${SRC} tabbed-${VERSION} + @tar -cf tabbed-${VERSION}.tar tabbed-${VERSION} + @gzip tabbed-${VERSION}.tar + @rm -rf tabbed-${VERSION} + +install: all + @echo installing executable files to ${DESTDIR}${PREFIX}/bin + @mkdir -p "${DESTDIR}${PREFIX}/bin" + @cp -f ${BIN} "${DESTDIR}${PREFIX}/bin" + @chmod 755 "${DESTDIR}${PREFIX}/bin/tabbed" + @echo installing manual pages to ${DESTDIR}${MANPREFIX}/man1 + @mkdir -p "${DESTDIR}${MANPREFIX}/man1" + @sed "s/VERSION/${VERSION}/g" < tabbed.1 > "${DESTDIR}${MANPREFIX}/man1/tabbed.1" + @chmod 644 "${DESTDIR}${MANPREFIX}/man1/tabbed.1" + @sed "s/VERSION/${VERSION}/g" < xembed.1 > "${DESTDIR}${MANPREFIX}/man1/xembed.1" + @chmod 644 "${DESTDIR}${MANPREFIX}/man1/xembed.1" + +uninstall: + @echo removing executable files from ${DESTDIR}${PREFIX}/bin + @rm -f "${DESTDIR}${PREFIX}/bin/tabbed" + @rm -f "${DESTDIR}${PREFIX}/bin/xembed" + @echo removing manual pages from ${DESTDIR}${MANPREFIX}/man1 + @rm -f "${DESTDIR}${MANPREFIX}/man1/tabbed.1" + @rm -f "${DESTDIR}${MANPREFIX}/man1/xembed.1" + +.PHONY: all options clean dist install uninstall diff --git a/README b/README new file mode 100644 index 0000000..4ed6bbe --- /dev/null +++ b/README @@ -0,0 +1,22 @@ +tabbed - generic tabbed interface +================================= +tabbed is a simple tabbed X window container. + +Requirements +------------ +In order to build tabbed you need the Xlib header files. + +Installation +------------ +Edit config.mk to match your local setup (tabbed is installed into +the /usr/local namespace by default). + +Afterwards enter the following command to build and install tabbed +(if necessary as root): + + make clean install + +Running tabbed +-------------- +See the man page for details. + diff --git a/README.md b/README.md new file mode 100644 index 0000000..5adf2bd --- /dev/null +++ b/README.md @@ -0,0 +1,2 @@ +# tabbed +# tabbed diff --git a/TODO b/TODO new file mode 100644 index 0000000..8e1986d --- /dev/null +++ b/TODO @@ -0,0 +1,4 @@ +# TODO +* add some way to detach windows +* add some way to attach windows + diff --git a/arg.h b/arg.h new file mode 100644 index 0000000..ba3fb3f --- /dev/null +++ b/arg.h @@ -0,0 +1,48 @@ +/* + * Copy me if you can. + * by 20h + */ + +#ifndef ARG_H__ +#define ARG_H__ + +extern char *argv0; + +/* use main(int argc, char *argv[]) */ +#define ARGBEGIN for (argv0 = *argv, argv++, argc--;\ + argv[0] && argv[0][0] == '-'\ + && argv[0][1];\ + argc--, argv++) {\ + char argc_;\ + char **argv_;\ + int brk_;\ + if (argv[0][1] == '-' && argv[0][2] == '\0') {\ + argv++;\ + argc--;\ + break;\ + }\ + for (brk_ = 0, argv[0]++, argv_ = argv;\ + argv[0][0] && !brk_;\ + argv[0]++) {\ + if (argv_ != argv)\ + break;\ + argc_ = argv[0][0];\ + switch (argc_) +#define ARGEND }\ + } + +#define ARGC() argc_ + +#define EARGF(x) ((argv[0][1] == '\0' && argv[1] == NULL)?\ + ((x), abort(), (char *)0) :\ + (brk_ = 1, (argv[0][1] != '\0')?\ + (&argv[0][1]) :\ + (argc--, argv++, argv[0]))) + +#define ARGF() ((argv[0][1] == '\0' && argv[1] == NULL)?\ + (char *)0 :\ + (brk_ = 1, (argv[0][1] != '\0')?\ + (&argv[0][1]) :\ + (argc--, argv++, argv[0]))) + +#endif diff --git a/config.def.h b/config.def.h new file mode 100644 index 0000000..defa426 --- /dev/null +++ b/config.def.h @@ -0,0 +1,66 @@ +/* See LICENSE file for copyright and license details. */ + +/* appearance */ +static const char font[] = "monospace:size=9"; +static const char* normbgcolor = "#222222"; +static const char* normfgcolor = "#cccccc"; +static const char* selbgcolor = "#555555"; +static const char* selfgcolor = "#ffffff"; +static const char* urgbgcolor = "#111111"; +static const char* urgfgcolor = "#cc0000"; +static const char before[] = "<"; +static const char after[] = ">"; +static const char titletrim[] = "..."; +static const int tabwidth = 200; +static const Bool foreground = True; +static Bool urgentswitch = False; + +/* + * Where to place a new tab when it is opened. When npisrelative is True, + * then the current position is changed + newposition. If npisrelative + * is False, then newposition is an absolute position. + */ +static int newposition = 0; +static Bool npisrelative = False; + +#define SETPROP(p) { \ + .v = (char *[]){ "/bin/sh", "-c", \ + "prop=\"`xwininfo -children -id $1 | grep '^ 0x' |" \ + "sed -e's@^ *\\(0x[0-9a-f]*\\) \"\\([^\"]*\\)\".*@\\1 \\2@' |" \ + "xargs -0 printf %b | dmenu -l 10 -w $1`\" &&" \ + "xprop -id $1 -f $0 8s -set $0 \"$prop\"", \ + p, winid, NULL \ + } \ +} + +#define MODKEY ControlMask +static Key keys[] = { + /* modifier key function argument */ + { MODKEY|ShiftMask, XK_Return, focusonce, { 0 } }, + { MODKEY|ShiftMask, XK_Return, spawn, { 0 } }, + + { MODKEY|ShiftMask, XK_l, rotate, { .i = +1 } }, + { MODKEY|ShiftMask, XK_h, rotate, { .i = -1 } }, + { MODKEY|ShiftMask, XK_j, movetab, { .i = -1 } }, + { MODKEY|ShiftMask, XK_k, movetab, { .i = +1 } }, + { MODKEY, XK_Tab, rotate, { .i = 0 } }, + + { MODKEY, XK_grave, spawn, SETPROP("_TABBED_SELECT_TAB") }, + { MODKEY, XK_1, move, { .i = 0 } }, + { MODKEY, XK_2, move, { .i = 1 } }, + { MODKEY, XK_3, move, { .i = 2 } }, + { MODKEY, XK_4, move, { .i = 3 } }, + { MODKEY, XK_5, move, { .i = 4 } }, + { MODKEY, XK_6, move, { .i = 5 } }, + { MODKEY, XK_7, move, { .i = 6 } }, + { MODKEY, XK_8, move, { .i = 7 } }, + { MODKEY, XK_9, move, { .i = 8 } }, + { MODKEY, XK_0, move, { .i = 9 } }, + + { MODKEY, XK_q, killclient, { 0 } }, + + { MODKEY, XK_u, focusurgent, { 0 } }, + { MODKEY|ShiftMask, XK_u, toggle, { .v = (void*) &urgentswitch } }, + + { 0, XK_F11, fullscreen, { 0 } }, +}; diff --git a/config.h b/config.h new file mode 100644 index 0000000..44a237d --- /dev/null +++ b/config.h @@ -0,0 +1,58 @@ +/* See LICENSE file for copyright and license details. */ + +/* appearance */ +static const char font[] = "Iosevka:pixelsize=14:antialias=true:autohint=true"; +#include "/home/spy/.config/cols/tabbed.h" +static const char before[] = "<"; +static const char after[] = ">"; +static const char titletrim[] = "..."; +static const int tabwidth = 200; +static const Bool foreground = True; +static Bool urgentswitch = False; + +/* + * Where to place a new tab when it is opened. When npisrelative is True, + * then the current position is changed + newposition. If npisrelative + * is False, then newposition is an absolute position. + */ +static int newposition = 0; +static Bool npisrelative = False; + +#define SETPROP(p) { \ + .v = (char *[]){ "/bin/sh", "-c", \ + "prop=\"`xwininfo -children -id $1 | grep '^ 0x' |" \ + "sed -e's@^ *\\(0x[0-9a-f]*\\) \"\\([^\"]*\\)\".*@\\1 \\2@' |" \ + "xargs -0 printf %b | dmenu -l 10 -w $1`\" &&" \ + "xprop -id $1 -f $0 8s -set $0 \"$prop\"", \ + p, winid, NULL \ + } \ +} + +#define MODKEY ControlMask +static Key keys[] = { + /* modifier key function argument */ + { MODKEY, XK_Return, focusonce, { 0 } }, + { MODKEY, XK_Return, spawn, { 0 } }, + + { MODKEY, XK_h, movetab, { .i = -1 } }, + { MODKEY, XK_l, movetab, { .i = +1 } }, + { MODKEY, XK_j, rotate, { .i = +1 } }, + { MODKEY, XK_k, rotate, { .i = -1 } }, + { MODKEY, XK_Tab, rotate, { .i = 0 } }, + + { MODKEY, XK_grave, spawn, SETPROP("_TABBED_SELECT_TAB") }, + { MODKEY, XK_1, move, { .i = 0 } }, + { MODKEY, XK_2, move, { .i = 1 } }, + { MODKEY, XK_3, move, { .i = 2 } }, + { MODKEY, XK_4, move, { .i = 3 } }, + { MODKEY, XK_5, move, { .i = 4 } }, + { MODKEY, XK_6, move, { .i = 5 } }, + { MODKEY, XK_7, move, { .i = 6 } }, + { MODKEY, XK_8, move, { .i = 7 } }, + { MODKEY, XK_9, move, { .i = 8 } }, + { MODKEY, XK_0, move, { .i = 9 } }, + { MODKEY, XK_q, killclient, { 0 } }, + { MODKEY, XK_u, focusurgent, { 0 } }, + { MODKEY|ShiftMask, XK_u, toggle, { .v = (void*) &urgentswitch } }, + { MODKEY, XK_f, fullscreen, { 0 } }, +}; diff --git a/config.mk b/config.mk new file mode 100644 index 0000000..29caa84 --- /dev/null +++ b/config.mk @@ -0,0 +1,33 @@ +# tabbed version +VERSION = 0.6 + +# Customize below to fit your system + +# paths +PREFIX = /usr/local +MANPREFIX = ${PREFIX}/share/man + +X11INC = /usr/X11R6/include +X11LIB = /usr/X11R6/lib + +# freetype +FREETYPELIBS = -lfontconfig -lXft +FREETYPEINC = /usr/include/freetype2 +# OpenBSD (uncomment) +#FREETYPEINC = ${X11INC}/freetype2 + +# includes and libs +INCS = -I. -I/usr/include -I$(X11INC) -I${FREETYPEINC} +LIBS = -L/usr/lib -lc -L${X11LIB} -lX11 ${FREETYPELIBS} + +# flags +CPPFLAGS = -DVERSION=\"${VERSION}\" -D_DEFAULT_SOURCE +CFLAGS = -std=c99 -pedantic -Wall -Os ${INCS} ${CPPFLAGS} +LDFLAGS = -s ${LIBS} + +# Solaris +#CFLAGS = -fast ${INCS} -DVERSION=\"${VERSION}\" +#LDFLAGS = ${LIBS} + +# compiler and linker +CC = cc diff --git a/tabbed b/tabbed new file mode 100755 index 0000000000000000000000000000000000000000..ce435c0f9d4e2df76c6f419bf44aea2626f52cfe GIT binary patch literal 32368 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/tmp/tabbed.xid); urxvt -embed $( +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "arg.h" + +/* XEMBED messages */ +#define XEMBED_EMBEDDED_NOTIFY 0 +#define XEMBED_WINDOW_ACTIVATE 1 +#define XEMBED_WINDOW_DEACTIVATE 2 +#define XEMBED_REQUEST_FOCUS 3 +#define XEMBED_FOCUS_IN 4 +#define XEMBED_FOCUS_OUT 5 +#define XEMBED_FOCUS_NEXT 6 +#define XEMBED_FOCUS_PREV 7 +/* 8-9 were used for XEMBED_GRAB_KEY/XEMBED_UNGRAB_KEY */ +#define XEMBED_MODALITY_ON 10 +#define XEMBED_MODALITY_OFF 11 +#define XEMBED_REGISTER_ACCELERATOR 12 +#define XEMBED_UNREGISTER_ACCELERATOR 13 +#define XEMBED_ACTIVATE_ACCELERATOR 14 + +/* Details for XEMBED_FOCUS_IN: */ +#define XEMBED_FOCUS_CURRENT 0 +#define XEMBED_FOCUS_FIRST 1 +#define XEMBED_FOCUS_LAST 2 + +/* Macros */ +#define MAX(a, b) ((a) > (b) ? (a) : (b)) +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#define LENGTH(x) (sizeof((x)) / sizeof(*(x))) +#define CLEANMASK(mask) (mask & ~(numlockmask | LockMask)) +#define TEXTW(x) (textnw(x, strlen(x)) + dc.font.height) + +enum { ColFG, ColBG, ColLast }; /* color */ +enum { WMProtocols, WMDelete, WMName, WMState, WMFullscreen, + XEmbed, WMSelectTab, WMLast }; /* default atoms */ + +typedef union { + int i; + const void *v; +} Arg; + +typedef struct { + unsigned int mod; + KeySym keysym; + void (*func)(const Arg *); + const Arg arg; +} Key; + +typedef struct { + int x, y, w, h; + XftColor norm[ColLast]; + XftColor sel[ColLast]; + XftColor urg[ColLast]; + Drawable drawable; + GC gc; + struct { + int ascent; + int descent; + int height; + XftFont *xfont; + } font; +} DC; /* draw context */ + +typedef struct { + char name[256]; + Window win; + int tabx; + Bool urgent; + Bool closed; +} Client; + +/* function declarations */ +static void buttonpress(const XEvent *e); +static void cleanup(void); +static void clientmessage(const XEvent *e); +static void configurenotify(const XEvent *e); +static void configurerequest(const XEvent *e); +static void createnotify(const XEvent *e); +static void destroynotify(const XEvent *e); +static void die(const char *errstr, ...); +static void drawbar(void); +static void drawtext(const char *text, XftColor col[ColLast]); +static void *ecalloc(size_t n, size_t size); +static void *erealloc(void *o, size_t size); +static void expose(const XEvent *e); +static void focus(int c); +static void focusin(const XEvent *e); +static void focusonce(const Arg *arg); +static void focusurgent(const Arg *arg); +static void fullscreen(const Arg *arg); +static char *getatom(int a); +static int getclient(Window w); +static XftColor getcolor(const char *colstr); +static int getfirsttab(void); +static Bool gettextprop(Window w, Atom atom, char *text, unsigned int size); +static void initfont(const char *fontstr); +static Bool isprotodel(int c); +static void keypress(const XEvent *e); +static void killclient(const Arg *arg); +static void manage(Window win); +static void maprequest(const XEvent *e); +static void move(const Arg *arg); +static void movetab(const Arg *arg); +static void propertynotify(const XEvent *e); +static void resize(int c, int w, int h); +static void rotate(const Arg *arg); +static void run(void); +static void sendxembed(int c, long msg, long detail, long d1, long d2); +static void setcmd(int argc, char *argv[], int); +static void setup(void); +static void sigchld(int unused); +static void spawn(const Arg *arg); +static int textnw(const char *text, unsigned int len); +static void toggle(const Arg *arg); +static void unmanage(int c); +static void unmapnotify(const XEvent *e); +static void updatenumlockmask(void); +static void updatetitle(int c); +static int xerror(Display *dpy, XErrorEvent *ee); +static void xsettitle(Window w, const char *str); + +/* variables */ +static int screen; +static void (*handler[LASTEvent]) (const XEvent *) = { + [ButtonPress] = buttonpress, + [ClientMessage] = clientmessage, + [ConfigureNotify] = configurenotify, + [ConfigureRequest] = configurerequest, + [CreateNotify] = createnotify, + [UnmapNotify] = unmapnotify, + [DestroyNotify] = destroynotify, + [Expose] = expose, + [FocusIn] = focusin, + [KeyPress] = keypress, + [MapRequest] = maprequest, + [PropertyNotify] = propertynotify, +}; +static int bh, obh, wx, wy, ww, wh; +static unsigned int numlockmask; +static Bool running = True, nextfocus, doinitspawn = True, + fillagain = False, closelastclient = False, + killclientsfirst = False; +static Display *dpy; +static DC dc; +static Atom wmatom[WMLast]; +static Window root, win; +static Client **clients; +static int nclients, sel = -1, lastsel = -1; +static int (*xerrorxlib)(Display *, XErrorEvent *); +static int cmd_append_pos; +static char winid[64]; +static char **cmd; +static char *wmname = "tabbed"; +static const char *geometry; + +char *argv0; + +/* configuration, allows nested code to access above variables */ +#include "config.h" + +void +buttonpress(const XEvent *e) +{ + const XButtonPressedEvent *ev = &e->xbutton; + int i, fc; + Arg arg; + + if (ev->y < 0 || ev->y > bh) + return; + + if (((fc = getfirsttab()) > 0 && ev->x < TEXTW(before)) || ev->x < 0) + return; + + for (i = fc; i < nclients; i++) { + if (clients[i]->tabx > ev->x) { + switch (ev->button) { + case Button1: + focus(i); + break; + case Button2: + focus(i); + killclient(NULL); + break; + case Button4: /* FALLTHROUGH */ + case Button5: + arg.i = ev->button == Button4 ? -1 : 1; + rotate(&arg); + break; + } + break; + } + } +} + +void +cleanup(void) +{ + int i; + + for (i = 0; i < nclients; i++) { + focus(i); + killclient(NULL); + XReparentWindow(dpy, clients[i]->win, root, 0, 0); + unmanage(i); + } + free(clients); + clients = NULL; + + XFreePixmap(dpy, dc.drawable); + XFreeGC(dpy, dc.gc); + XDestroyWindow(dpy, win); + XSync(dpy, False); + free(cmd); +} + +void +clientmessage(const XEvent *e) +{ + const XClientMessageEvent *ev = &e->xclient; + + if (ev->message_type == wmatom[WMProtocols] && + ev->data.l[0] == wmatom[WMDelete]) { + if (nclients > 1 && killclientsfirst) { + killclient(0); + return; + } + running = False; + } +} + +void +configurenotify(const XEvent *e) +{ + const XConfigureEvent *ev = &e->xconfigure; + + if (ev->window == win && (ev->width != ww || ev->height != wh)) { + ww = ev->width; + wh = ev->height; + XFreePixmap(dpy, dc.drawable); + dc.drawable = XCreatePixmap(dpy, root, ww, wh, + DefaultDepth(dpy, screen)); + + if (!obh && (wh <= bh)) { + obh = bh; + bh = 0; + } else if (!bh && (wh > obh)) { + bh = obh; + obh = 0; + } + + if (sel > -1) + resize(sel, ww, wh - bh); + XSync(dpy, False); + } +} + +void +configurerequest(const XEvent *e) +{ + const XConfigureRequestEvent *ev = &e->xconfigurerequest; + XWindowChanges wc; + int c; + + if ((c = getclient(ev->window)) > -1) { + wc.x = 0; + wc.y = bh; + wc.width = ww; + wc.height = wh - bh; + wc.border_width = 0; + wc.sibling = ev->above; + wc.stack_mode = ev->detail; + XConfigureWindow(dpy, clients[c]->win, ev->value_mask, &wc); + } +} + +void +createnotify(const XEvent *e) +{ + const XCreateWindowEvent *ev = &e->xcreatewindow; + + if (ev->window != win && getclient(ev->window) < 0) + manage(ev->window); +} + +void +destroynotify(const XEvent *e) +{ + const XDestroyWindowEvent *ev = &e->xdestroywindow; + int c; + + if ((c = getclient(ev->window)) > -1) + unmanage(c); +} + +void +die(const char *errstr, ...) +{ + va_list ap; + + va_start(ap, errstr); + vfprintf(stderr, errstr, ap); + va_end(ap); + exit(EXIT_FAILURE); +} + +void +drawbar(void) +{ + XftColor *col; + int c, cc, fc, width; + char *name = NULL; + + if (nclients == 0) { + dc.x = 0; + dc.w = ww; + XFetchName(dpy, win, &name); + drawtext(name ? name : "", dc.norm); + XCopyArea(dpy, dc.drawable, win, dc.gc, 0, 0, ww, bh, 0, 0); + XSync(dpy, False); + + return; + } + + width = ww; + cc = ww / tabwidth; + if (nclients > cc) + cc = (ww - TEXTW(before) - TEXTW(after)) / tabwidth; + + if ((fc = getfirsttab()) + cc < nclients) { + dc.w = TEXTW(after); + dc.x = width - dc.w; + drawtext(after, dc.sel); + width -= dc.w; + } + dc.x = 0; + + if (fc > 0) { + dc.w = TEXTW(before); + drawtext(before, dc.sel); + dc.x += dc.w; + width -= dc.w; + } + + cc = MIN(cc, nclients); + for (c = fc; c < fc + cc; c++) { + dc.w = width / cc; + if (c == sel) { + col = dc.sel; + dc.w += width % cc; + } else { + col = clients[c]->urgent ? dc.urg : dc.norm; + } + drawtext(clients[c]->name, col); + dc.x += dc.w; + clients[c]->tabx = dc.x; + } + XCopyArea(dpy, dc.drawable, win, dc.gc, 0, 0, ww, bh, 0, 0); + XSync(dpy, False); +} + +void +drawtext(const char *text, XftColor col[ColLast]) +{ + int i, j, x, y, h, len, olen; + char buf[256]; + XftDraw *d; + XRectangle r = { dc.x, dc.y, dc.w, dc.h }; + + XSetForeground(dpy, dc.gc, col[ColBG].pixel); + XFillRectangles(dpy, dc.drawable, dc.gc, &r, 1); + if (!text) + return; + + olen = strlen(text); + h = dc.font.ascent + dc.font.descent; + y = dc.y + (dc.h / 2) - (h / 2) + dc.font.ascent; + x = dc.x + (h / 2); + + /* shorten text if necessary */ + for (len = MIN(olen, sizeof(buf)); + len && textnw(text, len) > dc.w - h; len--); + + if (!len) + return; + + memcpy(buf, text, len); + if (len < olen) { + for (i = len, j = strlen(titletrim); j && i; + buf[--i] = titletrim[--j]) + ; + } + + d = XftDrawCreate(dpy, dc.drawable, DefaultVisual(dpy, screen), DefaultColormap(dpy, screen)); + XftDrawStringUtf8(d, &col[ColFG], dc.font.xfont, x, y, (XftChar8 *) buf, len); + XftDrawDestroy(d); +} + +void * +ecalloc(size_t n, size_t size) +{ + void *p; + + if (!(p = calloc(n, size))) + die("%s: cannot calloc\n", argv0); + return p; +} + +void * +erealloc(void *o, size_t size) +{ + void *p; + + if (!(p = realloc(o, size))) + die("%s: cannot realloc\n", argv0); + return p; +} + +void +expose(const XEvent *e) +{ + const XExposeEvent *ev = &e->xexpose; + + if (ev->count == 0 && win == ev->window) + drawbar(); +} + +void +focus(int c) +{ + char buf[BUFSIZ] = "tabbed-"VERSION" ::"; + size_t i, n; + XWMHints* wmh; + + /* If c, sel and clients are -1, raise tabbed-win itself */ + if (nclients == 0) { + cmd[cmd_append_pos] = NULL; + for(i = 0, n = strlen(buf); cmd[i] && n < sizeof(buf); i++) + n += snprintf(&buf[n], sizeof(buf) - n, " %s", cmd[i]); + + xsettitle(win, buf); + XRaiseWindow(dpy, win); + + return; + } + + if (c < 0 || c >= nclients) + return; + + resize(c, ww, wh - bh); + XRaiseWindow(dpy, clients[c]->win); + XSetInputFocus(dpy, clients[c]->win, RevertToParent, CurrentTime); + sendxembed(c, XEMBED_FOCUS_IN, XEMBED_FOCUS_CURRENT, 0, 0); + sendxembed(c, XEMBED_WINDOW_ACTIVATE, 0, 0, 0); + xsettitle(win, clients[c]->name); + + if (sel != c) { + lastsel = sel; + sel = c; + } + + if (clients[c]->urgent && (wmh = XGetWMHints(dpy, clients[c]->win))) { + wmh->flags &= ~XUrgencyHint; + XSetWMHints(dpy, clients[c]->win, wmh); + clients[c]->urgent = False; + XFree(wmh); + } + + drawbar(); + XSync(dpy, False); +} + +void +focusin(const XEvent *e) +{ + const XFocusChangeEvent *ev = &e->xfocus; + int dummy; + Window focused; + + if (ev->mode != NotifyUngrab) { + XGetInputFocus(dpy, &focused, &dummy); + if (focused == win) + focus(sel); + } +} + +void +focusonce(const Arg *arg) +{ + nextfocus = True; +} + +void +focusurgent(const Arg *arg) +{ + int c; + + if (sel < 0) + return; + + for (c = (sel + 1) % nclients; c != sel; c = (c + 1) % nclients) { + if (clients[c]->urgent) { + focus(c); + return; + } + } +} + +void +fullscreen(const Arg *arg) +{ + XEvent e; + + e.type = ClientMessage; + e.xclient.window = win; + e.xclient.message_type = wmatom[WMState]; + e.xclient.format = 32; + e.xclient.data.l[0] = 2; + e.xclient.data.l[1] = wmatom[WMFullscreen]; + e.xclient.data.l[2] = 0; + XSendEvent(dpy, root, False, SubstructureNotifyMask, &e); +} + +char * +getatom(int a) +{ + static char buf[BUFSIZ]; + Atom adummy; + int idummy; + unsigned long ldummy; + unsigned char *p = NULL; + + XGetWindowProperty(dpy, win, wmatom[a], 0L, BUFSIZ, False, XA_STRING, + &adummy, &idummy, &ldummy, &ldummy, &p); + if (p) + strncpy(buf, (char *)p, LENGTH(buf)-1); + else + buf[0] = '\0'; + XFree(p); + + return buf; +} + +int +getclient(Window w) +{ + int i; + + for (i = 0; i < nclients; i++) { + if (clients[i]->win == w) + return i; + } + + return -1; +} + +XftColor +getcolor(const char *colstr) +{ + XftColor color; + + if (!XftColorAllocName(dpy, DefaultVisual(dpy, screen), DefaultColormap(dpy, screen), colstr, &color)) + die("%s: cannot allocate color '%s'\n", argv0, colstr); + + return color; +} + +int +getfirsttab(void) +{ + int cc, ret; + + if (sel < 0) + return 0; + + cc = ww / tabwidth; + if (nclients > cc) + cc = (ww - TEXTW(before) - TEXTW(after)) / tabwidth; + + ret = sel - cc / 2 + (cc + 1) % 2; + return ret < 0 ? 0 : + ret + cc > nclients ? MAX(0, nclients - cc) : + ret; +} + +Bool +gettextprop(Window w, Atom atom, char *text, unsigned int size) +{ + char **list = NULL; + int n; + XTextProperty name; + + if (!text || size == 0) + return False; + + text[0] = '\0'; + XGetTextProperty(dpy, w, &name, atom); + if (!name.nitems) + return False; + + if (name.encoding == XA_STRING) { + strncpy(text, (char *)name.value, size - 1); + } else if (XmbTextPropertyToTextList(dpy, &name, &list, &n) >= Success + && n > 0 && *list) { + strncpy(text, *list, size - 1); + XFreeStringList(list); + } + text[size - 1] = '\0'; + XFree(name.value); + + return True; +} + +void +initfont(const char *fontstr) +{ + if (!(dc.font.xfont = XftFontOpenName(dpy, screen, fontstr)) + && !(dc.font.xfont = XftFontOpenName(dpy, screen, "fixed"))) + die("error, cannot load font: '%s'\n", fontstr); + + dc.font.ascent = dc.font.xfont->ascent; + dc.font.descent = dc.font.xfont->descent; + dc.font.height = dc.font.ascent + dc.font.descent; +} + +Bool +isprotodel(int c) +{ + int i, n; + Atom *protocols; + Bool ret = False; + + if (XGetWMProtocols(dpy, clients[c]->win, &protocols, &n)) { + for (i = 0; !ret && i < n; i++) { + if (protocols[i] == wmatom[WMDelete]) + ret = True; + } + XFree(protocols); + } + + return ret; +} + +void +keypress(const XEvent *e) +{ + const XKeyEvent *ev = &e->xkey; + unsigned int i; + KeySym keysym; + + keysym = XkbKeycodeToKeysym(dpy, (KeyCode)ev->keycode, 0, 0); + for (i = 0; i < LENGTH(keys); i++) { + if (keysym == keys[i].keysym && + CLEANMASK(keys[i].mod) == CLEANMASK(ev->state) && + keys[i].func) + keys[i].func(&(keys[i].arg)); + } +} + +void +killclient(const Arg *arg) +{ + XEvent ev; + + if (sel < 0) + return; + + if (isprotodel(sel) && !clients[sel]->closed) { + ev.type = ClientMessage; + ev.xclient.window = clients[sel]->win; + ev.xclient.message_type = wmatom[WMProtocols]; + ev.xclient.format = 32; + ev.xclient.data.l[0] = wmatom[WMDelete]; + ev.xclient.data.l[1] = CurrentTime; + XSendEvent(dpy, clients[sel]->win, False, NoEventMask, &ev); + clients[sel]->closed = True; + } else { + XKillClient(dpy, clients[sel]->win); + } +} + +void +manage(Window w) +{ + updatenumlockmask(); + { + int i, j, nextpos; + unsigned int modifiers[] = { 0, LockMask, numlockmask, + numlockmask | LockMask }; + KeyCode code; + Client *c; + XEvent e; + + XWithdrawWindow(dpy, w, 0); + XReparentWindow(dpy, w, win, 0, bh); + XSelectInput(dpy, w, PropertyChangeMask | + StructureNotifyMask | EnterWindowMask); + XSync(dpy, False); + + for (i = 0; i < LENGTH(keys); i++) { + if ((code = XKeysymToKeycode(dpy, keys[i].keysym))) { + for (j = 0; j < LENGTH(modifiers); j++) { + XGrabKey(dpy, code, keys[i].mod | + modifiers[j], w, True, + GrabModeAsync, GrabModeAsync); + } + } + } + + c = ecalloc(1, sizeof *c); + c->win = w; + + nclients++; + clients = erealloc(clients, sizeof(Client *) * nclients); + + if(npisrelative) { + nextpos = sel + newposition; + } else { + if (newposition < 0) + nextpos = nclients - newposition; + else + nextpos = newposition; + } + if (nextpos >= nclients) + nextpos = nclients - 1; + if (nextpos < 0) + nextpos = 0; + + if (nclients > 1 && nextpos < nclients - 1) + memmove(&clients[nextpos + 1], &clients[nextpos], + sizeof(Client *) * (nclients - nextpos - 1)); + + clients[nextpos] = c; + updatetitle(nextpos); + + XLowerWindow(dpy, w); + XMapWindow(dpy, w); + + e.xclient.window = w; + e.xclient.type = ClientMessage; + e.xclient.message_type = wmatom[XEmbed]; + e.xclient.format = 32; + e.xclient.data.l[0] = CurrentTime; + e.xclient.data.l[1] = XEMBED_EMBEDDED_NOTIFY; + e.xclient.data.l[2] = 0; + e.xclient.data.l[3] = win; + e.xclient.data.l[4] = 0; + XSendEvent(dpy, root, False, NoEventMask, &e); + + XSync(dpy, False); + + /* Adjust sel before focus does set it to lastsel. */ + if (sel >= nextpos) + sel++; + focus(nextfocus ? nextpos : + sel < 0 ? 0 : + sel); + nextfocus = foreground; + } +} + +void +maprequest(const XEvent *e) +{ + const XMapRequestEvent *ev = &e->xmaprequest; + + if (getclient(ev->window) < 0) + manage(ev->window); +} + +void +move(const Arg *arg) +{ + if (arg->i >= 0 && arg->i < nclients) + focus(arg->i); +} + +void +movetab(const Arg *arg) +{ + int c; + Client *new; + + if (sel < 0) + return; + + c = (sel + arg->i) % nclients; + if (c < 0) + c += nclients; + + if (c == sel) + return; + + new = clients[sel]; + if (sel < c) + memmove(&clients[sel], &clients[sel+1], + sizeof(Client *) * (c - sel)); + else + memmove(&clients[c+1], &clients[c], + sizeof(Client *) * (sel - c)); + clients[c] = new; + sel = c; + + drawbar(); +} + +void +propertynotify(const XEvent *e) +{ + const XPropertyEvent *ev = &e->xproperty; + XWMHints *wmh; + int c; + char* selection = NULL; + Arg arg; + + if (ev->state == PropertyNewValue && ev->atom == wmatom[WMSelectTab]) { + selection = getatom(WMSelectTab); + if (!strncmp(selection, "0x", 2)) { + arg.i = getclient(strtoul(selection, NULL, 0)); + move(&arg); + } else { + cmd[cmd_append_pos] = selection; + arg.v = cmd; + spawn(&arg); + } + } else if (ev->state == PropertyNewValue && ev->atom == XA_WM_HINTS && + (c = getclient(ev->window)) > -1 && + (wmh = XGetWMHints(dpy, clients[c]->win))) { + if (wmh->flags & XUrgencyHint) { + XFree(wmh); + wmh = XGetWMHints(dpy, win); + if (c != sel) { + if (urgentswitch && wmh && + !(wmh->flags & XUrgencyHint)) { + /* only switch, if tabbed was focused + * since last urgency hint if WMHints + * could not be received, + * default to no switch */ + focus(c); + } else { + /* if no switch should be performed, + * mark tab as urgent */ + clients[c]->urgent = True; + drawbar(); + } + } + if (wmh && !(wmh->flags & XUrgencyHint)) { + /* update tabbed urgency hint + * if not set already */ + wmh->flags |= XUrgencyHint; + XSetWMHints(dpy, win, wmh); + } + } + XFree(wmh); + } else if (ev->state != PropertyDelete && ev->atom == XA_WM_NAME && + (c = getclient(ev->window)) > -1) { + updatetitle(c); + } +} + +void +resize(int c, int w, int h) +{ + XConfigureEvent ce; + XWindowChanges wc; + + ce.x = 0; + ce.y = wc.y = bh; + ce.width = wc.width = w; + ce.height = wc.height = h; + ce.type = ConfigureNotify; + ce.display = dpy; + ce.event = clients[c]->win; + ce.window = clients[c]->win; + ce.above = None; + ce.override_redirect = False; + ce.border_width = 0; + + XConfigureWindow(dpy, clients[c]->win, CWY | CWWidth | CWHeight, &wc); + XSendEvent(dpy, clients[c]->win, False, StructureNotifyMask, + (XEvent *)&ce); +} + +void +rotate(const Arg *arg) +{ + int nsel = -1; + + if (sel < 0) + return; + + if (arg->i == 0) { + if (lastsel > -1) + focus(lastsel); + } else if (sel > -1) { + /* Rotating in an arg->i step around the clients. */ + nsel = sel + arg->i; + while (nsel >= nclients) + nsel -= nclients; + while (nsel < 0) + nsel += nclients; + focus(nsel); + } +} + +void +run(void) +{ + XEvent ev; + + /* main event loop */ + XSync(dpy, False); + drawbar(); + if (doinitspawn == True) + spawn(NULL); + + while (running) { + XNextEvent(dpy, &ev); + if (handler[ev.type]) + (handler[ev.type])(&ev); /* call handler */ + } +} + +void +sendxembed(int c, long msg, long detail, long d1, long d2) +{ + XEvent e = { 0 }; + + e.xclient.window = clients[c]->win; + e.xclient.type = ClientMessage; + e.xclient.message_type = wmatom[XEmbed]; + e.xclient.format = 32; + e.xclient.data.l[0] = CurrentTime; + e.xclient.data.l[1] = msg; + e.xclient.data.l[2] = detail; + e.xclient.data.l[3] = d1; + e.xclient.data.l[4] = d2; + XSendEvent(dpy, clients[c]->win, False, NoEventMask, &e); +} + +void +setcmd(int argc, char *argv[], int replace) +{ + int i; + + cmd = ecalloc(argc + 3, sizeof(*cmd)); + if (argc == 0) + return; + for (i = 0; i < argc; i++) + cmd[i] = argv[i]; + cmd[replace > 0 ? replace : argc] = winid; + cmd_append_pos = argc + !replace; + cmd[cmd_append_pos] = cmd[cmd_append_pos + 1] = NULL; +} + +void +setup(void) +{ + int bitm, tx, ty, tw, th, dh, dw, isfixed; + XWMHints *wmh; + XClassHint class_hint; + XSizeHints *size_hint; + + /* clean up any zombies immediately */ + sigchld(0); + + /* init screen */ + screen = DefaultScreen(dpy); + root = RootWindow(dpy, screen); + initfont(font); + bh = dc.h = dc.font.height + 2; + + /* init atoms */ + wmatom[WMDelete] = XInternAtom(dpy, "WM_DELETE_WINDOW", False); + wmatom[WMFullscreen] = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", + False); + wmatom[WMName] = XInternAtom(dpy, "_NET_WM_NAME", False); + wmatom[WMProtocols] = XInternAtom(dpy, "WM_PROTOCOLS", False); + wmatom[WMSelectTab] = XInternAtom(dpy, "_TABBED_SELECT_TAB", False); + wmatom[WMState] = XInternAtom(dpy, "_NET_WM_STATE", False); + wmatom[XEmbed] = XInternAtom(dpy, "_XEMBED", False); + + /* init appearance */ + wx = 0; + wy = 0; + ww = 800; + wh = 600; + isfixed = 0; + + if (geometry) { + tx = ty = tw = th = 0; + bitm = XParseGeometry(geometry, &tx, &ty, (unsigned *)&tw, + (unsigned *)&th); + if (bitm & XValue) + wx = tx; + if (bitm & YValue) + wy = ty; + if (bitm & WidthValue) + ww = tw; + if (bitm & HeightValue) + wh = th; + if (bitm & XNegative && wx == 0) + wx = -1; + if (bitm & YNegative && wy == 0) + wy = -1; + if (bitm & (HeightValue | WidthValue)) + isfixed = 1; + + dw = DisplayWidth(dpy, screen); + dh = DisplayHeight(dpy, screen); + if (wx < 0) + wx = dw + wx - ww - 1; + if (wy < 0) + wy = dh + wy - wh - 1; + } + + dc.norm[ColBG] = getcolor(normbgcolor); + dc.norm[ColFG] = getcolor(normfgcolor); + dc.sel[ColBG] = getcolor(selbgcolor); + dc.sel[ColFG] = getcolor(selfgcolor); + dc.urg[ColBG] = getcolor(urgbgcolor); + dc.urg[ColFG] = getcolor(urgfgcolor); + dc.drawable = XCreatePixmap(dpy, root, ww, wh, + DefaultDepth(dpy, screen)); + dc.gc = XCreateGC(dpy, root, 0, 0); + + win = XCreateSimpleWindow(dpy, root, wx, wy, ww, wh, 0, + dc.norm[ColFG].pixel, dc.norm[ColBG].pixel); + XMapRaised(dpy, win); + XSelectInput(dpy, win, SubstructureNotifyMask | FocusChangeMask | + ButtonPressMask | ExposureMask | KeyPressMask | + PropertyChangeMask | StructureNotifyMask | + SubstructureRedirectMask); + xerrorxlib = XSetErrorHandler(xerror); + + class_hint.res_name = wmname; + class_hint.res_class = "tabbed"; + XSetClassHint(dpy, win, &class_hint); + + size_hint = XAllocSizeHints(); + if (!isfixed) { + size_hint->flags = PSize | PMinSize; + size_hint->height = wh; + size_hint->width = ww; + size_hint->min_height = bh + 1; + } else { + size_hint->flags = PMaxSize | PMinSize; + size_hint->min_width = size_hint->max_width = ww; + size_hint->min_height = size_hint->max_height = wh; + } + wmh = XAllocWMHints(); + XSetWMProperties(dpy, win, NULL, NULL, NULL, 0, size_hint, wmh, NULL); + XFree(size_hint); + XFree(wmh); + + XSetWMProtocols(dpy, win, &wmatom[WMDelete], 1); + + snprintf(winid, sizeof(winid), "%lu", win); + setenv("XEMBED", winid, 1); + + nextfocus = foreground; + focus(-1); +} + +void +sigchld(int unused) +{ + if (signal(SIGCHLD, sigchld) == SIG_ERR) + die("%s: cannot install SIGCHLD handler", argv0); + + while (0 < waitpid(-1, NULL, WNOHANG)); +} + +void +spawn(const Arg *arg) +{ + if (fork() == 0) { + if(dpy) + close(ConnectionNumber(dpy)); + + setsid(); + if (arg && arg->v) { + execvp(((char **)arg->v)[0], (char **)arg->v); + fprintf(stderr, "%s: execvp %s", argv0, + ((char **)arg->v)[0]); + } else { + cmd[cmd_append_pos] = NULL; + execvp(cmd[0], cmd); + fprintf(stderr, "%s: execvp %s", argv0, cmd[0]); + } + perror(" failed"); + exit(0); + } +} + +int +textnw(const char *text, unsigned int len) +{ + XGlyphInfo ext; + XftTextExtentsUtf8(dpy, dc.font.xfont, (XftChar8 *) text, len, &ext); + return ext.xOff; +} + +void +toggle(const Arg *arg) +{ + *(Bool*) arg->v = !*(Bool*) arg->v; +} + +void +unmanage(int c) +{ + if (c < 0 || c >= nclients) { + drawbar(); + XSync(dpy, False); + return; + } + + if (!nclients) + return; + + if (c == 0) { + /* First client. */ + nclients--; + free(clients[0]); + memmove(&clients[0], &clients[1], sizeof(Client *) * nclients); + } else if (c == nclients - 1) { + /* Last client. */ + nclients--; + free(clients[c]); + clients = erealloc(clients, sizeof(Client *) * nclients); + } else { + /* Somewhere inbetween. */ + free(clients[c]); + memmove(&clients[c], &clients[c+1], + sizeof(Client *) * (nclients - (c + 1))); + nclients--; + } + + if (nclients <= 0) { + lastsel = sel = -1; + + if (closelastclient) + running = False; + else if (fillagain && running) + spawn(NULL); + } else { + if (lastsel >= nclients) + lastsel = nclients - 1; + else if (lastsel > c) + lastsel--; + + if (c == sel && lastsel >= 0) { + focus(lastsel); + } else { + if (sel > c) + sel--; + if (sel >= nclients) + sel = nclients - 1; + + focus(sel); + } + } + + drawbar(); + XSync(dpy, False); +} + +void +unmapnotify(const XEvent *e) +{ + const XUnmapEvent *ev = &e->xunmap; + int c; + + if ((c = getclient(ev->window)) > -1) + unmanage(c); +} + +void +updatenumlockmask(void) +{ + unsigned int i, j; + XModifierKeymap *modmap; + + numlockmask = 0; + modmap = XGetModifierMapping(dpy); + for (i = 0; i < 8; i++) { + for (j = 0; j < modmap->max_keypermod; j++) { + if (modmap->modifiermap[i * modmap->max_keypermod + j] + == XKeysymToKeycode(dpy, XK_Num_Lock)) + numlockmask = (1 << i); + } + } + XFreeModifiermap(modmap); +} + +void +updatetitle(int c) +{ + if (!gettextprop(clients[c]->win, wmatom[WMName], clients[c]->name, + sizeof(clients[c]->name))) + gettextprop(clients[c]->win, XA_WM_NAME, clients[c]->name, + sizeof(clients[c]->name)); + if (sel == c) + xsettitle(win, clients[c]->name); + drawbar(); +} + +/* There's no way to check accesses to destroyed windows, thus those cases are + * ignored (especially on UnmapNotify's). Other types of errors call Xlibs + * default error handler, which may call exit. */ +int +xerror(Display *dpy, XErrorEvent *ee) +{ + if (ee->error_code == BadWindow + || (ee->request_code == X_SetInputFocus && + ee->error_code == BadMatch) + || (ee->request_code == X_PolyText8 && + ee->error_code == BadDrawable) + || (ee->request_code == X_PolyFillRectangle && + ee->error_code == BadDrawable) + || (ee->request_code == X_PolySegment && + ee->error_code == BadDrawable) + || (ee->request_code == X_ConfigureWindow && + ee->error_code == BadMatch) + || (ee->request_code == X_GrabButton && + ee->error_code == BadAccess) + || (ee->request_code == X_GrabKey && + ee->error_code == BadAccess) + || (ee->request_code == X_CopyArea && + ee->error_code == BadDrawable)) + return 0; + + fprintf(stderr, "%s: fatal error: request code=%d, error code=%d\n", + argv0, ee->request_code, ee->error_code); + return xerrorxlib(dpy, ee); /* may call exit */ +} + +void +xsettitle(Window w, const char *str) +{ + XTextProperty xtp; + + if (XmbTextListToTextProperty(dpy, (char **)&str, 1, + XCompoundTextStyle, &xtp) == Success) { + XSetTextProperty(dpy, w, &xtp, wmatom[WMName]); + XSetTextProperty(dpy, w, &xtp, XA_WM_NAME); + XFree(xtp.value); + } +} + +void +usage(void) +{ + die("usage: %s [-dfksv] [-g geometry] [-n name] [-p [s+/-]pos]\n" + " [-r narg] [-o color] [-O color] [-t color] [-T color]\n" + " [-u color] [-U color] command...\n", argv0); +} + +int +main(int argc, char *argv[]) +{ + Bool detach = False; + int replace = 0; + char *pstr; + + ARGBEGIN { + case 'c': + closelastclient = True; + fillagain = False; + break; + case 'd': + detach = True; + break; + case 'f': + fillagain = True; + break; + case 'g': + geometry = EARGF(usage()); + break; + case 'k': + killclientsfirst = True; + break; + case 'n': + wmname = EARGF(usage()); + break; + case 'O': + normfgcolor = EARGF(usage()); + break; + case 'o': + normbgcolor = EARGF(usage()); + break; + case 'p': + pstr = EARGF(usage()); + if (pstr[0] == 's') { + npisrelative = True; + newposition = atoi(&pstr[1]); + } else { + newposition = atoi(pstr); + } + break; + case 'r': + replace = atoi(EARGF(usage())); + break; + case 's': + doinitspawn = False; + break; + case 'T': + selfgcolor = EARGF(usage()); + break; + case 't': + selbgcolor = EARGF(usage()); + break; + case 'U': + urgfgcolor = EARGF(usage()); + break; + case 'u': + urgbgcolor = EARGF(usage()); + break; + case 'v': + die("tabbed-"VERSION", © 2009-2016 tabbed engineers, " + "see LICENSE for details.\n"); + break; + default: + usage(); + break; + } ARGEND; + + if (argc < 1) { + doinitspawn = False; + fillagain = False; + } + + setcmd(argc, argv, replace); + + if (!setlocale(LC_CTYPE, "") || !XSupportsLocale()) + fprintf(stderr, "%s: no locale support\n", argv0); + if (!(dpy = XOpenDisplay(NULL))) + die("%s: cannot open display\n", argv0); + + setup(); + printf("0x%lx\n", win); + fflush(NULL); + + if (detach) { + if (fork() == 0) { + fclose(stdout); + } else { + if (dpy) + close(ConnectionNumber(dpy)); + return EXIT_SUCCESS; + } + } + + run(); + cleanup(); + XCloseDisplay(dpy); + + return EXIT_SUCCESS; +} diff --git a/tabbed.o b/tabbed.o new file mode 100644 index 0000000000000000000000000000000000000000..553934486c0c8e91cd8bacf23b07d4b898b7e982 GIT binary patch literal 41592 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 O76QLFsL#Hc{(k~6K6%Ih literal 0 HcmV?d00001 diff --git a/xembed b/xembed new file mode 100755 index 0000000000000000000000000000000000000000..eb63e93e674bae15cba2d4926371dbc11428cf59 GIT binary patch literal 14464 zcmeHOe{37o9e;M3HvFg)2Hir7$O*M9u;vn{X_U5v+DX%1P&)-kt4y@G7yIm3a_r2$ z1h*P$q(CPt1ciSXn}*bB(**3FYMM}o1egUX%0HuLUuER@tTC_1Hvw10d>Jv9Jc-p=jts8IKSVb@i;BI$#tnDiU># zSR~(zX&NUZ*E{`bCI>r{#F>*dJ zC%E-dKD`)$I<$z~3*prjN$16V6wio#pgsJdo%lNajgW4;CYupn+^T57$o)MCI?T(z zYkZm?qWcDpo zi{Pv%BgguH2wdj}$%hbW7<(IRn0C}(SvdNOKkBht7hZYw^&|QBpMEWN>Q#)v z@b7}nYkPrMYn-EXcx+>>c+BTo{JnMf-F5gwz~3Tn7qby4fbsPtVzGG9j3v`qGm}1P zfeRBU9xK>p-i}S0>8waj<iB&tNyH$y}DYiK);v193Dsi+cr>#=>Ez zoXlnIcrKevr;rNE>ZGkE{DFhR1B0<{eMgVrVz<5%jyec`0lDE`{WTEEw8+{sP~_|J zH_to^)3ibSiMZ*_=^L<}W$@AXKFRqUp_5gn`ylgt5|2lu%*j9dmvF*>;Wa_GHt8?B zc=>rCSs^Me9yM$$E`9@(fLC4oO)j421GbIVA6sigm9PzUi7H=n@j*q&eY=ZyU+-Nm z-pK_h`dmCD)Uk+*ztLfZFkJl2E`G$tZ*=i~(a(UN0Y3wN2K)^88TkLnz-P^O{==C2 zxXGArywC}c1y7dkVC`LF?#-qpd62c89{^q3b{Y1~Z4oG;KY?}C(w_t3{jn(DjK z{{#B?C{ry<{~h%45v+Pv`oBXTA8D#*r2h)~__$PEl>V>L$49X0tn^<(A0L~lQ{YFB zL=Q(t4?k>_KAWorPqi8I-!$g?Uxt`)*;v>aZiYv>7a;B&j^Ly38w>rtNKFGZTzV4^ zFB<*=*lzGfL-B`R5EuF6vsq|JQ1f`)}pG~(de=0qf506rHhXmr4Nj`%PS+J zL*a7x0t_Jxn%bsUy(1jblsdH#)ZP37%9jbw*+H$hFZJ!XV$MMbfKMqZJ7N1OLx~|_S z#)b|J4DA!032Ua)woiz_mWDkVj1s@he}1J_!*_2_tkh~h0POi|t#%(EzSBetoP0H_HB@_gtkJl2e#*+y+!0hE&D^Q4>aHOSktW7yLr#O zJGS11bRTR-u>krP5eGsoPX`A=tqTnUA?>Mk(NO#8#%QQ(Zhi2_q4sDvW!0{PAw zFj}a`Yrl@t`n%gHj@RdHA)miqZ6=@B>EU-hG_L>KwOS7SGA+jAwRa;_;5By@s^_(8 zAEOA2*VwfxS8O6=Aej&m#o+5-w6ffER8iJYGHz@U>O?_fh@d$#=$0{rsna*Ohzw?WK9JpYRA_l5mFbG~u&^ zza-@Ljz?7Kz~JC+tsPb|-=o2z({8;>2)!^-u=BP#E_7HBYr#TTJ)5&FJ(Zo-r}D5I zHV^-ESf$3N)36#loggYYFx+9ADIvor%)*4w6DP8T6O#(jc@qYT3NAy#=43h! zz2=}T*y*wZ)!2BUAoO@{a?;A$oTjghcwOKe3T}Oo^o;ob9k;D>J5b&Qn_K@=(lctFc(48d)PDij5|5wnFO1wjU3Sj> zh?{*73YbII^ZkcWu55vN93$qAkDu=Y-vbVZ%zD0`G4lNjLZo@~Zvu<=3fA-ef$>h# z@%TB8F$?v0?_r$htvl5wkYoxNB8Tc~mCM>G}6_?)9u z1wMaFFF_#$8(p@}`hEVrjW?cs#@~as*`?>_hjIsyU>v8sRiF9yp#tX)_s^ea{QD{X zofOW0G}iHXybK&3KkNB-1D856T8MT9&Yty*Rj6_6MTA1T6poS0z&gfHp~9^ni>Syk z((^dIdiJkE0o&s7&qh?uL>;}Rs-69pP{RHx+~HYIkADu2v1o4OzEzFdpQHxYb0AfE GT=6gHhLr^X literal 0 HcmV?d00001 diff --git a/xembed.1 b/xembed.1 new file mode 100644 index 0000000..5b0c28c --- /dev/null +++ b/xembed.1 @@ -0,0 +1,35 @@ +.TH XEMBED 1 tabbed\-VERSION +.SH NAME +xembed \- XEmbed foreground process +.SH SYNOPSIS +.B xembed +.I flag command +.RI [ "argument ..." ] +.SH DESCRIPTION +If the environment variable XEMBED is set, and +.B xembed +is in the foreground of its controlling tty, it will execute +.IP +command flag $XEMBED [argument ...] +.LP +Otherwise it will execute +.IP +command [argument ...] +.LP +.SH EXAMPLE +In a terminal emulator within a +.B tabbed +session, the shell alias +.IP +$ alias surf='xembed -e surf' +.LP +will cause `surf' to open in a new tab, unless it is run in the background, +i.e. `surf &', in which case it will instead open in a new window. +.SH AUTHORS +See the LICENSE file for the authors. +.SH LICENSE +See the LICENSE file for the terms of redistribution. +.SH SEE ALSO +.BR tabbed (1) +.SH BUGS +Please report them. diff --git a/xembed.c b/xembed.c new file mode 100644 index 0000000..cbb0e97 --- /dev/null +++ b/xembed.c @@ -0,0 +1,45 @@ +/* + * See LICENSE file for copyright and license details. + */ + +#include +#include +#include +#include + +int +main(int argc, char *argv[]) +{ + char *xembed; + int tty; + pid_t pgrp, tcpgrp; + + if (argc < 3) { + fprintf(stderr, "usage: %s flag cmd ...\n", argv[0]); + return 2; + } + + if (!(xembed = getenv("XEMBED"))) + goto noembed; + + if ((tty = open("/dev/tty", O_RDONLY)) < 0) + goto noembed; + + pgrp = getpgrp(); + tcpgrp = tcgetpgrp(tty); + + close(tty); + + if (pgrp == tcpgrp) { /* in foreground of tty */ + argv[0] = argv[2]; + argv[2] = xembed; + } else { +noembed: + argv += 2; + } + + execvp(argv[0], argv); + + perror(argv[0]); /* failed to execute */ + return 1; +} diff --git a/xembed.o b/xembed.o new file mode 100644 index 0000000000000000000000000000000000000000..2bca4fa3bbf0394940ae672a49ce76c4795b740f GIT binary patch literal 2320 zcmbu9TWb?h5XUEJv|iVEK@>$-P>iU@rYK^ol1;3xP%o&};+1W3nu^WE&9+2eDip;~ ztG#=1LvHX-^`rb&Yqc};nT@Pg0v*)B>lBC zCAzrmub%VO9IYdRrW#IJ4w!oy=J=#jHj9;#DTG)>qeJKNL#OCaNlqW~ya!}mwMMKf zBbV*g&)LP~%uai5vpshL6J^`>{1GT$(Y;S{w{YK{_kBJ(+xlofZhKbtm!jFW&s(1! zx10L|^}E&j+|fSy+R(9YQ)}(HLVCDWOxvxVg_J$}A#KkM+O02s0IUA^9_PJr%eraZ zvTlE%Gljx{nK^TQWS^PMihj|DZBdGw$qm=QK1x!e4W=cbY<+i|lq2!*5+$1H#I`lP zD_*kmIP>kw_ZDN`NIy^JjV<#%dBc2`vW(1=WtP!5yFB^Y$XJHSE)K~XD^yaG_UUutLoUAn@$)+q$*Qw6&HJ4~JuLnW-_j3Ea7>K@a@SYIg%{l~M5T&8x`vF-H z51-~o0a?ISa?{)fWI;S?Tk{KmEQm*~Xnq}#1$;F(%}anR;A^;PUIkOrrs zN5nyoo_|6m==VjLeyr9p;$1-Pole(%n+j*#HhhbNHrDsQ6( zYE-zhk88{|3y1FR2D`+Tnc@FmQsZoARDN_{djA~a%MAAjjpmQ4{Lvt+(?|0kvZveq zy#Zv=_k{byyP)$m@3uDb-l_E;sS$4hzHu}kZ&S4P7*U^sxZkj-={jgVYu)nOYX7ZH JWS~Uz{{T}26%qgd literal 0 HcmV?d00001