tabbed/tabbed.c

900 lines
21 KiB
C
Raw Normal View History

2009-09-07 06:32:58 -05:00
/* See LICENSE file for copyright and license details.
*
* To understand tabbed, start reading main().
*/
#include <sys/wait.h>
2009-09-07 06:32:58 -05:00
#include <locale.h>
#include <stdarg.h>
#include <unistd.h>
#include <signal.h>
2009-09-07 06:32:58 -05:00
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <X11/Xatom.h>
#include <X11/Xlib.h>
2009-09-08 04:30:08 -05:00
#include <X11/Xproto.h>
#include <X11/Xutil.h>
2009-09-07 06:32:58 -05:00
2009-09-23 02:53:30 -05:00
/* 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
2009-09-23 02:53:30 -05:00
/* 8-9 were used for XEMBED_GRAB_KEY/XEMBED_UNGRAB_KEY */
#define XEMBED_MODALITY_ON 10
#define XEMBED_MODALITY_OFF 11
2009-09-23 02:53:30 -05:00
#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
2009-09-07 06:32:58 -05:00
2009-09-23 03:00:06 -05:00
/* Macros */
#define MAX(a, b) ((a) > (b) ? (a) : (b))
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#define LENGTH(x) (sizeof (x) / sizeof *(x))
2009-09-23 03:00:06 -05:00
#define CLEANMASK(mask) (mask & ~(numlockmask|LockMask))
#define TEXTW(x) (textnw(x, strlen(x)) + dc.font.height)
enum { ColFG, ColBG, ColLast }; /* color */
enum { WMProtocols, WMDelete, WMLast }; /* default atoms */
2009-09-23 03:00:06 -05:00
2009-09-07 06:32:58 -05:00
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;
unsigned long norm[ColLast];
unsigned long sel[ColLast];
Drawable drawable;
GC gc;
struct {
int ascent;
int descent;
int height;
XFontSet set;
XFontStruct *xfont;
} font;
} DC; /* draw context */
typedef struct Client {
2009-09-07 06:32:58 -05:00
char name[256];
struct Client *next;
Window win;
int tabx;
Bool mapped;
Bool closed;
} Client;
2009-09-07 06:32:58 -05:00
/* function declarations */
static void buttonpress(const XEvent *e);
2009-09-07 06:32:58 -05:00
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);
2009-09-07 06:32:58 -05:00
static void die(const char *errstr, ...);
2009-09-08 04:30:08 -05:00
static void drawbar();
static void drawtext(const char *text, unsigned long col[ColLast]);
2009-09-23 03:33:10 -05:00
static void *emallocz(size_t size);
static void expose(const XEvent *e);
2009-09-08 04:53:32 -05:00
static void focus(Client *c);
static void focusin(const XEvent *e);
static void focusonce(const Arg *arg);
2009-09-08 04:30:08 -05:00
static Client *getclient(Window w);
2009-09-23 03:33:10 -05:00
static unsigned long getcolor(const char *colstr);
2009-09-08 04:30:08 -05:00
static Client *getfirsttab();
static Bool gettextprop(Window w, Atom atom, char *text, unsigned int size);
2009-09-07 06:32:58 -05:00
static void initfont(const char *fontstr);
2009-09-23 03:33:10 -05:00
static Bool isprotodel(Client *c);
static void keypress(const XEvent *e);
2009-09-07 15:22:08 -05:00
static void killclient(const Arg *arg);
2009-09-08 04:30:08 -05:00
static void manage(Window win);
static void maprequest(const XEvent *e);
2009-09-23 03:33:10 -05:00
static void move(const Arg *arg);
static void propertynotify(const XEvent *e);
2009-09-08 04:30:08 -05:00
static void resize(Client *c, int w, int h);
2009-09-07 06:32:58 -05:00
static void rotate(const Arg *arg);
static void run(void);
static void sendxembed(Client *c, long msg, long detail, long d1, long d2);
2009-10-28 14:31:34 -05:00
static void setup(void);
static void setcmd(int argc, char *argv[]);
2009-10-15 08:40:18 -05:00
static void sigchld(int unused);
2009-09-23 03:33:10 -05:00
static void spawn(const Arg *arg);
2009-09-07 06:32:58 -05:00
static int textnw(const char *text, unsigned int len);
2009-09-08 05:50:08 -05:00
static void unmanage(Client *c);
2009-09-07 06:32:58 -05:00
static void updatenumlockmask(void);
2009-09-08 04:30:08 -05:00
static void updatetitle(Client *c);
2009-09-08 00:34:15 -05:00
static int xerror(Display *dpy, XErrorEvent *ee);
2009-09-07 06:32:58 -05:00
/* variables */
static int screen;
static void (*handler[LASTEvent]) (const XEvent *) = {
2009-09-08 04:53:32 -05:00
[ButtonPress] = buttonpress,
2009-09-13 05:13:10 -05:00
[ClientMessage] = clientmessage,
2009-09-23 03:33:10 -05:00
[ConfigureNotify] = configurenotify,
2009-10-13 01:13:30 -05:00
[ConfigureRequest] = configurerequest,
2009-09-22 13:17:20 -05:00
[CreateNotify] = createnotify,
2009-09-23 03:33:10 -05:00
[DestroyNotify] = destroynotify,
[Expose] = expose,
2009-09-23 02:53:30 -05:00
[FocusIn] = focusin,
2009-09-23 03:33:10 -05:00
[KeyPress] = keypress,
[MapRequest] = maprequest,
2009-09-23 03:33:10 -05:00
[PropertyNotify] = propertynotify,
2009-09-07 06:32:58 -05:00
};
2009-09-23 03:33:10 -05:00
static int bh, wx, wy, ww, wh;
static unsigned int numlockmask = 0;
static Bool running = True, nextfocus;
2009-09-07 06:32:58 -05:00
static Display *dpy;
static DC dc;
static Atom wmatom[WMLast], xembedatom;
2009-09-07 06:32:58 -05:00
static Window root, win;
static Client *clients = NULL, *sel = NULL, *lastsel = NULL;
static int (*xerrorxlib)(Display *, XErrorEvent *);
2009-10-13 01:13:30 -05:00
static char winid[64];
static char **cmd = NULL;
2009-09-07 06:32:58 -05:00
/* configuration, allows nested code to access above variables */
#include "config.h"
2009-09-08 04:53:32 -05:00
void
buttonpress(const XEvent *e) {
const XButtonPressedEvent *ev = &e->xbutton;
2009-09-08 04:53:32 -05:00
int i;
Client *c;
Arg arg;
2009-09-08 04:53:32 -05:00
c = getfirsttab();
if(c != clients && ev->x < TEXTW(before))
return;
for(i = 0; c; c = c->next, i++) {
if(c->tabx > ev->x) {
switch(ev->button) {
case Button1:
focus(c);
break;
case Button2:
focus(c);
killclient(NULL);
break;
case Button4:
case Button5:
arg.i = ev->button == Button4 ? -1 : 1;
rotate(&arg);
break;
}
2009-09-08 04:53:32 -05:00
break;
}
}
}
2009-09-07 06:32:58 -05:00
void
cleanup(void) {
Client *c, *n;
for(c = clients; c; c = n) {
focus(c);
killclient(NULL);
XReparentWindow(dpy, c->win, root, 0, 0);
n = c->next;
unmanage(c);
}
2009-09-07 06:32:58 -05:00
if(dc.font.set)
XFreeFontSet(dpy, dc.font.set);
else
XFreeFont(dpy, dc.font.xfont);
XFreePixmap(dpy, dc.drawable);
XFreeGC(dpy, dc.gc);
XDestroyWindow(dpy, win);
XSync(dpy, False);
free(cmd);
2009-09-07 06:32:58 -05:00
}
2009-09-13 05:13:10 -05:00
void
clientmessage(const XEvent *e) {
const XClientMessageEvent *ev = &e->xclient;
2009-09-13 05:13:10 -05:00
if(ev->message_type == wmatom[WMProtocols]
&& ev->data.l[0] == wmatom[WMDelete])
running = False;
2009-09-13 05:13:10 -05:00
}
2009-10-13 01:13:30 -05:00
2009-09-07 06:32:58 -05:00
void
configurenotify(const XEvent *e) {
const XConfigureEvent *ev = &e->xconfigure;
2009-09-07 06:32:58 -05:00
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));
2009-10-28 14:31:34 -05:00
if(sel)
resize(sel, ww, wh - bh);
2009-09-08 04:30:08 -05:00
XSync(dpy, False);
2009-09-07 06:32:58 -05:00
}
}
2009-10-13 01:13:30 -05:00
void
configurerequest(const XEvent *e) {
const XConfigureRequestEvent *ev = &e->xconfigurerequest;
2009-10-13 01:13:30 -05:00
XWindowChanges wc;
Client *c;
if((c = getclient(ev->window))) {
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, c->win, ev->value_mask, &wc);
}
}
2009-09-22 13:17:20 -05:00
void
createnotify(const XEvent *e) {
const XCreateWindowEvent *ev = &e->xcreatewindow;
2009-09-22 13:17:20 -05:00
2009-09-23 02:53:30 -05:00
if(ev->window != win && !getclient(ev->window))
2009-09-22 13:17:20 -05:00
manage(ev->window);
}
2009-09-08 05:50:08 -05:00
void
destroynotify(const XEvent *e) {
const XDestroyWindowEvent *ev = &e->xdestroywindow;
2009-09-08 05:50:08 -05:00
Client *c;
if((c = getclient(ev->window)))
unmanage(c);
}
2009-09-07 06:32:58 -05:00
void
die(const char *errstr, ...) {
va_list ap;
va_start(ap, errstr);
vfprintf(stderr, errstr, ap);
va_end(ap);
exit(EXIT_FAILURE);
}
2009-09-08 04:30:08 -05:00
void
drawbar() {
unsigned long *col;
2009-09-08 05:17:59 -05:00
int n, width;
2009-09-08 04:30:08 -05:00
Client *c, *fc;
2009-10-13 01:13:30 -05:00
char *name = NULL;
2009-09-08 04:30:08 -05:00
if(!clients) {
dc.x = 0;
dc.w = ww;
2009-10-13 01:13:30 -05:00
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;
}
2009-09-08 05:17:59 -05:00
width = ww;
for(c = clients; c; c = c->next)
c->tabx = -1;
2009-09-08 05:17:59 -05:00
for(n = 0, fc = c = getfirsttab(); c; c = c->next, n++);
2009-09-23 03:35:06 -05:00
if(n * tabwidth > width) {
2009-09-08 05:17:59 -05:00
dc.w = TEXTW(after);
dc.x = width - dc.w;
2009-09-08 05:50:08 -05:00
drawtext(after, dc.sel);
2009-09-08 05:17:59 -05:00
width -= dc.w;
}
2009-09-08 04:30:08 -05:00
dc.x = 0;
2009-09-08 05:17:59 -05:00
if(fc != clients) {
dc.w = TEXTW(before);
2009-09-08 05:50:08 -05:00
drawtext(before, dc.sel);
2009-09-08 05:17:59 -05:00
dc.x += dc.w;
width -= dc.w;
}
for(c = fc; c && dc.x < width; c = c->next) {
2009-09-08 04:30:08 -05:00
dc.w = tabwidth;
if(c == sel) {
col = dc.sel;
2009-09-08 05:17:59 -05:00
if(n * tabwidth > width)
dc.w += width % tabwidth;
else
2009-09-23 03:35:06 -05:00
dc.w = width - (n - 1) * tabwidth;
2009-09-08 04:30:08 -05:00
}
else {
col = dc.norm;
}
drawtext(c->name, col);
dc.x += dc.w;
c->tabx = dc.x;
2009-09-08 04:30:08 -05:00
}
XCopyArea(dpy, dc.drawable, win, dc.gc, 0, 0, ww, bh, 0, 0);
XSync(dpy, False);
}
void
drawtext(const char *text, unsigned long col[ColLast]) {
int i, x, y, h, len, olen;
char buf[256];
2009-09-08 04:30:08 -05:00
XRectangle r = { dc.x, dc.y, dc.w, dc.h };
XSetForeground(dpy, dc.gc, col[ColBG]);
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; i && i > len - 3; buf[--i] = '.');
XSetForeground(dpy, dc.gc, col[ColFG]);
if(dc.font.set)
XmbDrawString(dpy, dc.drawable, dc.font.set, dc.gc, x, y, buf, len);
else
XDrawString(dpy, dc.drawable, dc.gc, x, y, buf, len);
}
void *
emallocz(size_t size) {
void *p;
if(!(p = calloc(1, size)))
die("tabbed: cannot malloc\n");
return p;
2009-09-07 06:32:58 -05:00
}
void
expose(const XEvent *e) {
const XExposeEvent *ev = &e->xexpose;
2009-09-08 04:30:08 -05:00
if(ev->count == 0 && win == ev->window)
drawbar();
}
void
focus(Client *c) {
/* If c, sel and clients are NULL, raise tabbed-win itself */
if(!c && !(c = sel ? sel : clients)) {
2011-11-20 10:38:48 -06:00
char buf[BUFSIZ] = "tabbed-"VERSION" ::";
size_t i, n;
for(i = 0, n = strlen(buf); cmd[i] && n < sizeof buf; i++)
n += snprintf(&buf[n], sizeof buf - n, " %s", cmd[i]);
XStoreName(dpy, win, buf);
XRaiseWindow(dpy, win);
2009-09-08 04:30:08 -05:00
return;
}
2009-10-28 14:31:34 -05:00
resize(c, ww, wh - bh);
2009-09-08 04:30:08 -05:00
XRaiseWindow(dpy, c->win);
2011-09-26 16:39:59 -05:00
XSetInputFocus(dpy, c->win, RevertToParent, CurrentTime);
sendxembed(c, XEMBED_FOCUS_IN, XEMBED_FOCUS_CURRENT, 0, 0);
sendxembed(c, XEMBED_WINDOW_ACTIVATE, 0, 0, 0);
2009-10-27 06:54:22 -05:00
XStoreName(dpy, win, c->name);
if(sel != c) {
lastsel = sel;
}
2009-09-23 02:59:10 -05:00
sel = c;
2009-09-08 04:30:08 -05:00
drawbar();
2009-09-07 06:32:58 -05:00
}
2009-09-23 02:53:30 -05:00
void
focusin(const XEvent *e) {
2011-09-26 16:39:59 -05:00
const XFocusChangeEvent *ev = &e->xfocus;
int dummy;
Window focused;
if(ev->mode != NotifyUngrab) {
XGetInputFocus(dpy, &focused, &dummy);
if(focused == win)
focus(sel);
}
2009-09-23 02:53:30 -05:00
}
void
focusonce(const Arg *arg) {
nextfocus = True;
}
2009-09-08 04:30:08 -05:00
Client *
getclient(Window w) {
Client *c;
for(c = clients; c; c = c->next)
if(c->win == w)
return c;
return NULL;
}
2009-09-23 03:33:10 -05:00
unsigned long
getcolor(const char *colstr) {
Colormap cmap = DefaultColormap(dpy, screen);
XColor color;
if(!XAllocNamedColor(dpy, cmap, colstr, &color, &color))
die("tabbed: cannot allocate color '%s'\n", colstr);
2009-09-23 03:33:10 -05:00
return color.pixel;
}
2009-09-08 04:30:08 -05:00
Client *
getfirsttab() {
unsigned int n, seli;
Client *c, *fc;
if(!sel)
return NULL;
2009-09-08 04:30:08 -05:00
c = fc = clients;
for(n = 0; c; c = c->next, n++);
if(n * tabwidth > ww) {
for(seli = 0, c = clients; c && c != sel; c = c->next, seli++);
for(; seli * tabwidth > ww / 2 && n * tabwidth > ww;
fc = fc->next, seli--, n--);
}
return fc;
}
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;
}
2009-09-07 06:32:58 -05:00
void
initfont(const char *fontstr) {
char *def, **missing;
int i, n;
missing = NULL;
if(dc.font.set)
XFreeFontSet(dpy, dc.font.set);
dc.font.set = XCreateFontSet(dpy, fontstr, &missing, &n, &def);
if(missing) {
while(n--)
fprintf(stderr, "tabbed: missing fontset: %s\n", missing[n]);
XFreeStringList(missing);
}
if(dc.font.set) {
XFontStruct **xfonts;
char **font_names;
dc.font.ascent = dc.font.descent = 0;
n = XFontsOfFontSet(dc.font.set, &xfonts, &font_names);
for(i = 0, dc.font.ascent = 0, dc.font.descent = 0; i < n; i++) {
dc.font.ascent = MAX(dc.font.ascent, (*xfonts)->ascent);
dc.font.descent = MAX(dc.font.descent,(*xfonts)->descent);
xfonts++;
}
}
else {
if(dc.font.xfont)
XFreeFont(dpy, dc.font.xfont);
dc.font.xfont = NULL;
if(!(dc.font.xfont = XLoadQueryFont(dpy, fontstr))
&& !(dc.font.xfont = XLoadQueryFont(dpy, "fixed")))
die("tabbed: cannot load font: '%s'\n", fontstr);
2009-09-07 06:32:58 -05:00
dc.font.ascent = dc.font.xfont->ascent;
dc.font.descent = dc.font.xfont->descent;
}
dc.font.height = dc.font.ascent + dc.font.descent;
}
2009-09-08 05:50:08 -05:00
Bool
isprotodel(Client *c) {
int i, n;
Atom *protocols;
Bool ret = False;
if(XGetWMProtocols(dpy, c->win, &protocols, &n)) {
for(i = 0; !ret && i < n; i++)
if(protocols[i] == wmatom[WMDelete])
ret = True;
XFree(protocols);
}
return ret;
}
2009-09-07 06:32:58 -05:00
void
keypress(const XEvent *e) {
const XKeyEvent *ev = &e->xkey;
2009-09-07 06:32:58 -05:00
unsigned int i;
KeySym keysym;
keysym = XKeycodeToKeysym(dpy, (KeyCode)ev->keycode, 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));
}
2009-09-07 15:21:27 -05:00
void
2009-09-07 15:22:39 -05:00
killclient(const Arg *arg) {
2009-09-08 05:50:08 -05:00
XEvent ev;
if(!sel)
return;
if(isprotodel(sel) && !sel->closed) {
2009-09-08 05:50:08 -05:00
ev.type = ClientMessage;
ev.xclient.window = 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, sel->win, False, NoEventMask, &ev);
sel->closed = True;
2009-09-08 05:50:08 -05:00
}
else
XKillClient(dpy, sel->win);
2009-09-07 15:21:27 -05:00
}
2009-09-07 23:57:19 -05:00
void
2009-09-08 04:30:08 -05:00
manage(Window w) {
updatenumlockmask();
{
int i, j;
unsigned int modifiers[] = { 0, LockMask, numlockmask, numlockmask|LockMask };
KeyCode code;
Client *c;
2009-09-13 05:13:10 -05:00
XEvent e;
2009-09-08 04:30:08 -05:00
2009-09-13 05:13:10 -05:00
XWithdrawWindow(dpy, w, 0);
2009-09-08 04:30:08 -05:00
XReparentWindow(dpy, w, win, 0, bh);
2009-09-23 02:53:30 -05:00
XSelectInput(dpy, w, PropertyChangeMask|StructureNotifyMask|EnterWindowMask);
2009-09-13 05:13:10 -05:00
XSync(dpy, False);
2009-09-08 04:30:08 -05:00
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 = emallocz(sizeof *c);
2009-09-08 04:30:08 -05:00
c->next = clients;
c->win = w;
clients = c;
updatetitle(c);
drawbar();
2009-09-13 05:13:10 -05:00
XMapRaised(dpy, w);
e.xclient.window = w;
e.xclient.type = ClientMessage;
e.xclient.message_type = xembedatom;
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);
2009-09-23 02:53:30 -05:00
XSync(dpy, False);
focus(nextfocus ? c : sel);
nextfocus = foreground;
2010-02-16 12:53:03 -06:00
if(!lastsel)
lastsel = c;
2009-09-08 04:30:08 -05:00
}
}
void
maprequest(const XEvent *e) {
const XMapRequestEvent *ev = &e->xmaprequest;
if(!getclient(ev->window))
manage(ev->window);
}
2009-09-23 03:33:10 -05:00
void
move(const Arg *arg) {
int i;
Client *c;
for(i = 0, c = clients; c; c = c->next, i++) {
if(arg->i == i)
focus(c);
}
}
2009-09-08 04:30:08 -05:00
void
propertynotify(const XEvent *e) {
const XPropertyEvent *ev = &e->xproperty;
2009-09-08 04:30:08 -05:00
Client *c;
if(ev->state != PropertyDelete && ev->atom == XA_WM_NAME
&& (c = getclient(ev->window))) {
updatetitle(c);
}
}
void
resize(Client *c, int w, int h) {
XConfigureEvent ce;
XWindowChanges wc;
ce.x = 0;
ce.y = bh;
ce.width = wc.width = w;
ce.height = wc.height = h;
ce.type = ConfigureNotify;
ce.display = dpy;
ce.event = c->win;
ce.window = c->win;
ce.above = None;
ce.override_redirect = False;
ce.border_width = 0;
XConfigureWindow(dpy, c->win, CWWidth|CWHeight, &wc);
XSendEvent(dpy, c->win, False, StructureNotifyMask, (XEvent *)&ce);
2009-09-07 23:57:19 -05:00
}
2009-09-07 06:32:58 -05:00
void
rotate(const Arg *arg) {
2009-09-08 04:30:08 -05:00
Client *c;
if(arg->i == 0)
focus(lastsel);
else if(arg->i > 0) {
2009-09-08 04:30:08 -05:00
if(sel && sel->next)
focus(sel->next);
else
focus(clients);
}
else {
for(c = clients; c && c->next && c->next != sel; c = c->next);
if(c)
focus(c);
}
2009-09-07 06:32:58 -05:00
}
void
run(void) {
XEvent ev;
2009-10-13 12:37:20 -05:00
/* main event loop */
2009-09-07 06:32:58 -05:00
XSync(dpy, False);
drawbar();
2011-09-26 16:36:33 -05:00
spawn(NULL);
2009-09-07 06:32:58 -05:00
while(running) {
XNextEvent(dpy, &ev);
if(handler[ev.type])
(handler[ev.type])(&ev); /* call handler */
2009-09-07 06:32:58 -05:00
}
}
2009-10-28 14:31:34 -05:00
void
sendxembed(Client *c, long msg, long detail, long d1, long d2) {
XEvent e = { 0 };
e.xclient.window = c->win;
e.xclient.type = ClientMessage;
e.xclient.message_type = xembedatom;
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, c->win, False, NoEventMask, &e);
}
void
setcmd(int argc, char *argv[]) {
int i;
cmd = emallocz((argc+2) * sizeof *cmd);
for(i = 0; i < argc; i++)
cmd[i] = argv[i];
cmd[argc] = winid;
cmd[argc+1] = NULL;
}
2009-09-07 06:32:58 -05:00
void
setup(void) {
2009-09-23 03:33:10 -05:00
/* clean up any zombies immediately */
sigchld(0);
2009-09-07 06:32:58 -05:00
/* init screen */
screen = DefaultScreen(dpy);
root = RootWindow(dpy, screen);
initfont(font);
2009-09-08 04:30:08 -05:00
bh = dc.h = dc.font.height + 2;
2009-09-08 05:50:08 -05:00
/* init atoms */
wmatom[WMProtocols] = XInternAtom(dpy, "WM_PROTOCOLS", False);
wmatom[WMDelete] = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
2009-09-13 05:13:10 -05:00
xembedatom = XInternAtom(dpy, "_XEMBED", False);
2009-09-07 06:32:58 -05:00
/* init appearance */
wx = 0;
wy = 0;
ww = 800;
wh = 600;
dc.norm[ColBG] = getcolor(normbgcolor);
dc.norm[ColFG] = getcolor(normfgcolor);
dc.sel[ColBG] = getcolor(selbgcolor);
dc.sel[ColFG] = getcolor(selfgcolor);
dc.drawable = XCreatePixmap(dpy, root, ww, wh, DefaultDepth(dpy, screen));
dc.gc = XCreateGC(dpy, root, 0, 0);
if(!dc.font.set)
XSetFont(dpy, dc.gc, dc.font.xfont->fid);
win = XCreateSimpleWindow(dpy, root, wx, wy, ww, wh, 0, dc.norm[ColFG], dc.norm[ColBG]);
XMapRaised(dpy, win);
2009-09-23 02:53:30 -05:00
XSelectInput(dpy, win, SubstructureNotifyMask|FocusChangeMask|
ButtonPressMask|ExposureMask|KeyPressMask|
StructureNotifyMask|SubstructureRedirectMask);
xerrorxlib = XSetErrorHandler(xerror);
2009-09-08 06:06:13 -05:00
XClassHint class_hint;
class_hint.res_name = "tabbed";
class_hint.res_class = "Tabbed";
2009-09-08 06:06:13 -05:00
XSetClassHint(dpy, win, &class_hint);
XSetWMProtocols(dpy, win, &wmatom[WMDelete], 1);
snprintf(winid, sizeof winid, "%lu", win);
nextfocus = foreground;
2009-10-13 01:13:30 -05:00
focus(clients);
2009-09-07 06:32:58 -05:00
}
2009-09-23 03:33:10 -05:00
void
sigchld(int unused) {
if(signal(SIGCHLD, sigchld) == SIG_ERR)
die("tabbed: cannot install SIGCHLD handler");
2009-09-23 03:33:10 -05:00
while(0 < waitpid(-1, NULL, WNOHANG));
}
void
spawn(const Arg *arg) {
if(fork() == 0) {
if(dpy)
close(ConnectionNumber(dpy));
setsid();
execvp(cmd[0], cmd);
fprintf(stderr, "tabbed: execvp %s", cmd[0]);
2009-09-23 03:33:10 -05:00
perror(" failed");
exit(0);
}
}
2009-09-07 06:32:58 -05:00
int
textnw(const char *text, unsigned int len) {
XRectangle r;
if(dc.font.set) {
XmbTextExtents(dc.font.set, text, len, NULL, &r);
return r.width;
}
return XTextWidth(dc.font.xfont, text, len);
}
2009-09-08 05:50:08 -05:00
void
unmanage(Client *c) {
Client *pc;
if(!clients)
return;
else if(c == clients)
pc = clients = c->next;
else {
for(pc = clients; pc && pc->next && pc->next != c; pc = pc->next);
2009-09-08 05:50:08 -05:00
pc->next = c->next;
}
if(c == lastsel)
2010-02-16 12:53:03 -06:00
lastsel = clients;
if(c == sel) {
sel = pc;
focus(lastsel);
}
2009-09-08 05:50:08 -05:00
free(c);
2010-02-16 12:53:03 -06:00
drawbar();
2009-09-08 05:50:08 -05:00
XSync(dpy, False);
}
2009-09-07 06:32:58 -05:00
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);
}
2009-09-08 04:30:08 -05:00
void
updatetitle(Client *c) {
gettextprop(c->win, XA_WM_NAME, c->name, sizeof c->name);
2009-09-08 06:06:13 -05:00
if(sel == c)
XStoreName(dpy, win, c->name);
2009-09-08 04:30:08 -05:00
drawbar();
}
2009-09-08 00:34:15 -05:00
/* 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, "tabbed: fatal error: request code=%d, error code=%d\n",
2009-09-08 00:34:15 -05:00
ee->request_code, ee->error_code);
return xerrorxlib(dpy, ee); /* may call exit */
2009-09-08 00:34:15 -05:00
}
2009-09-07 06:32:58 -05:00
int
main(int argc, char *argv[]) {
int i, detach = 0;
for(i = 1; i < argc && !cmd; i++) {
if(!strcmp("-v", argv[i]))
die("tabbed-"VERSION", © 2009-2011 tabbed engineers, see LICENSE for details\n");
else if(!strcmp("-d", argv[i]))
detach = 1;
else
setcmd(argc-i, argv+i);
}
if(!cmd)
2011-06-18 08:37:43 -05:00
die("usage: tabbed [-d] [-v] command...\n");
2009-09-07 06:32:58 -05:00
if(!setlocale(LC_CTYPE, "") || !XSupportsLocale())
fprintf(stderr, "tabbed: no locale support\n");
2010-08-09 05:59:13 -05:00
if(!(dpy = XOpenDisplay(NULL)))
2009-09-07 06:32:58 -05:00
die("tabbed: cannot open display\n");
setup();
2011-06-18 08:37:43 -05:00
printf("0x%lx\n", win);
fflush(NULL);
if(detach) {
if(fork() == 0)
fclose(stdout);
else {
if(dpy)
close(ConnectionNumber(dpy));
return EXIT_SUCCESS;
}
2009-09-13 05:13:10 -05:00
}
2009-09-07 06:32:58 -05:00
run();
cleanup();
XCloseDisplay(dpy);
2009-09-13 05:13:10 -05:00
return EXIT_SUCCESS;
2009-09-07 06:32:58 -05:00
}