#include <sys/wait.h>
#include <X11/cursorfont.h>
#include <X11/keysym.h>
+#include <X11/XF86keysym.h>
#include <X11/Xatom.h>
#include <X11/Xlib.h>
#include <X11/Xproto.h>
#define WIDTH(X) ((X)->w + 2 * (X)->bw)
#define HEIGHT(X) ((X)->h + 2 * (X)->bw)
#define TAGMASK ((1 << LENGTH(tags)) - 1)
-#define TEXTW(X) (drw_font_getexts_width(drw->font, X, strlen(X)) + drw->font->h)
+#define TEXTW_NOPAD(X) drw_font_getexts_width(drw->font, X, strlen(X))
+#define TEXTW(X) (TEXTW_NOPAD(X) + drw->font->h)
/* enums */
enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */
NetWMWindowTypeDialog, NetClientList, NetSupportingWMCheck, NetLast }; /* EWMH atoms */
enum { WMProtocols, WMDelete, WMState, WMTakeFocus, WMLast }; /* default atoms */
enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle,
- ClkClientWin, ClkRootWin, ClkLast }; /* clicks */
+ ClkClientWin, ClkRootWin, ClkAnywhere, ClkLast }; /* clicks */
typedef union {
int i;
static Bool gettextprop(Window w, Atom atom, char *text, unsigned int size);
static void grabbuttons(Client *c, Bool focused);
static void grabkeys(void);
-static void grab_typing_keys(void);
static void incnmaster(const Arg *arg);
static void keypress(XEvent *e);
static void killclient(const Arg *arg);
static void run(void);
static void scan(void);
static Bool sendevent(Client *c, Atom proto);
-static void send_keycode(KeyCode key);
static void sendmon(Client *c, Monitor *m);
static void setclientstate(Client *c, long state);
static void setfocus(Client *c);
static int xerrorstart(Display *dpy, XErrorEvent *ee);
static void zoom(const Arg *arg);
-/* variables */
-static KeyCode key_buffer[100];
-static int key_buffer_len = 0;
-static Bool key_buffering = False;
+// variables
+enum { KeyBufferingOff, KeyBufferingAwaitingWindow, KeyBufferingAwaitingFocus };
+static unsigned int key_buffering = 0;
static const char broken[] = "broken";
static char stext[256];
static int screen;
-static int sw, sh; /* X display screen geometry width, height */
-static int bh, blw = 0; /* bar geometry */
+static int sw, sh; // X display screen geometry width, height
+static int bh; // bar height
static int (*xerrorxlib)(Display *, XErrorEvent *);
static unsigned int numlockmask = 0;
static void (*handler[LASTEvent]) (XEvent *) = {
static void window_set_translucent(Client *c);
static void window_set_invisible(Client *c);
static void window_set_opacity(Client *c, int opacity_index);
+static Client* choose_slave (Client *master, Client *focused);
static void update_window_opacities(Monitor *m);
void
window_set_opacity(Client *c, int opacity_index) {
}
void
update_window_opacities(Monitor *m) {
- Client *master, *slave, *c;
- Bool selection_floating = False;
- slave = master = nexttiled(m->clients);
- if (master) slave = nexttiled(master->next);
- if (m->sel && m->sel != master) {
- if (nexttiled(m->sel) == m->sel) // if selection is tiled
- slave = m->sel;
- else
- selection_floating = True;
- }
- for (c = m->clients; c; c = c->next) {
- if (ISVISIBLE(c)) {
- if (c->isfloating || c == m->sel || (selection_floating && (c == master || c == slave))) {
- window_set_opaque(c);
- } else if (c == master || c == slave) {
- window_set_translucent(c);
- } else {
- window_set_opaque(c);
- }
+ Client *master, *slave, *focused, *c;
+ Bool focused_is_floating = False;
+ master = nexttiled(m->clients);
+ focused = (m->sel && ISVISIBLE(m->sel)) ? m->sel : NULL;
+ slave = choose_slave(master, focused);
+ // detect if the focused window is floating (and visible)
+ if (master && focused && focused != slave && focused != master) {
+ if (nexttiled(focused) != focused) {
+ focused_is_floating = True;
+ }
+ }
+ // set opacity of visible floaters, master and slave
+ for (c = nextvisible(m->clients); c; c = nextvisible(c->next)) {
+ if (c->isfloating || c == focused || (focused_is_floating && (c == master || c == slave))) {
+ window_set_opaque(c);
+ } else if (c == master || c == slave) {
+ window_set_translucent(c);
}
}
}
Client *c;
Monitor *m;
XButtonPressedEvent *ev = &e->xbutton;
+ Bool called = False;
+
+ for(i = 0; i < LENGTH(buttons); i++) {
+ if(buttons[i].click == ClkAnywhere && buttons[i].button == ev->button
+ && CLEANMASK(buttons[i].mask) == CLEANMASK(ev->state)) {
+ buttons[i].func(&buttons[i].arg);
+ called = True;
+ }
+ }
+ if (called) {
+ return;
+ }
click = ClkRootWin;
/* focus monitor if necessary */
}
if(ev->window == selmon->barwin) {
return;
- }
- else if((c = wintoclient(ev->window))) {
+ } else if((c = wintoclient(ev->window))) {
focus(c);
click = ClkClientWin;
}
- for(i = 0; i < LENGTH(buttons); i++)
+ for(i = 0; i < LENGTH(buttons); i++) {
if(click == buttons[i].click && buttons[i].func && buttons[i].button == ev->button
- && CLEANMASK(buttons[i].mask) == CLEANMASK(ev->state))
- buttons[i].func(click == ClkTagBar && buttons[i].arg.i == 0 ? &arg : &buttons[i].arg);
+ && CLEANMASK(buttons[i].mask) == CLEANMASK(ev->state)) {
+ if (click == ClkTagBar && buttons[i].arg.i == 0) {
+ buttons[i].func(&arg);
+ } else {
+ buttons[i].func(&buttons[i].arg);
+ }
+ }
+ }
}
void
}
x = 0;
for(i = 0; i < LENGTH(tags); i++) {
- w = TEXTW(tags[i]);
+ w = TEXTW_NOPAD(tags[i]);
drw_setscheme(drw, m->tagset[m->seltags] & 1 << i ? &scheme[SchemeSel] : &scheme[SchemeNorm]);
- drw_text(drw, x, 0, w, bh, tags[i], urg & 1 << i);
- drw_rect(drw, x, 0, w, bh, m == selmon && selmon->sel && selmon->sel->tags & 1 << i,
- occ & 1 << i, urg & 1 << i);
+ drw_text_nopad(drw, x, 0, w, bh, tags[i], urg & 1 << i);
x += w;
}
- w = blw = TEXTW(m->ltsymbol);
- drw_setscheme(drw, &scheme[SchemeNorm]);
- drw_text(drw, x, 0, w, bh, m->ltsymbol, 0);
- x += w;
xx = x;
if(m == selmon) { /* status is only drawn on selected monitor */
w = TEXTW(stext);
grabbuttons(c, True);
XSetWindowBorder(dpy, c->win, scheme[SchemeSel].border->rgb);
setfocus(c);
- }
- else {
+ } else {
XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime);
XDeleteProperty(dpy, root, netatom[NetActiveWindow]);
}
if(selmon->sel && ev->window != selmon->sel->win)
setfocus(selmon->sel);
+
+ if(key_buffering == KeyBufferingAwaitingFocus) {
+ key_buffering = KeyBufferingOff;
+ // there's a few ways to release the grab, with different effects (below)
+ // In this example F2 is mapped to kbspawn
+ // XAllowEvents(dpy, AsyncKeyboard, CurrentTime);
+ // swallows key presses while F2 is still down.
+ // XAllowEvents(dpy, ReplayKeyboard, CurrentTime);
+ // sends the F2 keydown and all the queued events
+ // XUngrabKeyboard(dpy, CurrentTime);
+ // sends everything queued and the F2 key_UP_
+ XUngrabKeyboard(dpy, CurrentTime);
+ }
}
void
XUngrabButton(dpy, AnyButton, AnyModifier, c->win);
if(focused) {
for(i = 0; i < LENGTH(buttons); i++)
- if(buttons[i].click == ClkClientWin)
+ if(buttons[i].click == ClkClientWin || buttons[i].click == ClkAnywhere)
for(j = 0; j < LENGTH(modifiers); j++)
XGrabButton(dpy, buttons[i].button,
buttons[i].mask | modifiers[j],
c->win, False, BUTTONMASK,
GrabModeAsync, GrabModeSync, None, None);
- }
- else
+ } else {
XGrabButton(dpy, AnyButton, AnyModifier, c->win, False,
BUTTONMASK, GrabModeAsync, GrabModeSync, None, None);
+ }
}
}
void
grabkeys(void) {
updatenumlockmask();
+ //XUngrabKey(dpy, AnyKey, AnyModifier, root);
{
unsigned int i, j;
unsigned int modifiers[] = { 0, LockMask, numlockmask, numlockmask|LockMask };
if((code = XKeysymToKeycode(dpy, keys[i].keysym)))
for(j = 0; j < LENGTH(modifiers); j++)
XGrabKey(dpy, code, keys[i].mod | modifiers[j], root,
- True, GrabModeAsync, GrabModeAsync);
- if(key_buffering)
- grab_typing_keys();
- }
-}
-
-void
-grab_typing_keys(void) {
- updatenumlockmask();
- {
- unsigned int i, j;
- unsigned int modifiers[] = { 0, LockMask, numlockmask, numlockmask|LockMask };
- KeySym typing_keys[] = {
- XK_space, XK_Return, XK_period, XK_slash, XK_minus, XK_apostrophe,
- XK_A, XK_B, XK_C, XK_D, XK_E, XK_F, XK_G, XK_H, XK_I, XK_J, XK_K,
- XK_L, XK_M, XK_N, XK_O, XK_P, XK_Q, XK_R, XK_S, XK_T, XK_U, XK_V,
- XK_W, XK_X, XK_Y, XK_Z, XK_0, XK_1, XK_2, XK_3, XK_4, XK_5, XK_6,
- XK_7, XK_8, XK_9,
- };
-
- KeyCode code;
-
- for(i = 0; i < LENGTH(typing_keys); i++)
- if((code = XKeysymToKeycode(dpy, typing_keys[i])))
- for(j = 0; j < LENGTH(modifiers); j++)
- XGrabKey(dpy, code, modifiers[j], root,
- True, GrabModeAsync, GrabModeAsync);
+ False, // report keys to focused window too?
+ GrabModeAsync, // mouse grab mode
+ keys[i].func == kbspawn ? GrabModeSync : GrabModeAsync);
}
}
#endif /* XINERAMA */
void
-send_keycode(KeyCode key) {
- XKeyEvent event;
- if(!selmon->sel) {
- return;
- }
- event.display = dpy;
- event.root = root;
- event.window = selmon->sel->win;
- event.subwindow = None;
- event.same_screen = True;
- event.x = 1;
- event.y = 1;
- event.x_root = 1;
- event.y_root = 1;
- event.time = CurrentTime;
- event.state = 0; // modifiers
- event.keycode = key;
- event.type = KeyPress;
- XSendEvent(event.display, event.window, True, KeyPressMask, (XEvent *)&event);
- event.type = KeyRelease;
- XSendEvent(event.display, event.window, True, KeyPressMask, (XEvent *)&event);
-}
-
-void
keypress(XEvent *e) {
unsigned int i;
KeySym keysym;
XKeyEvent *ev;
- Bool called = False;
ev = &e->xkey;
keysym = XKeycodeToKeysym(dpy, (KeyCode)ev->keycode, 0);
- for(i = 0; i < LENGTH(keys); i++)
+ 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));
- called = True;
- }
- if(!called) {
- if(key_buffering) {
- if(key_buffer_len == LENGTH(key_buffer)) {
- // buffer full, bail
- key_buffer_len = 0;
- key_buffering = False;
- grabkeys(); // stop grabbing typing keys
- } else {
- key_buffer[key_buffer_len] = (KeyCode)ev->keycode;
- key_buffer_len += 1;
- }
- } else {
- send_keycode(ev->keycode);
}
}
}
Client *c, *t = NULL;
Window trans = None;
XWindowChanges wc;
- int i;
if(!(c = calloc(1, sizeof(Client))))
die("fatal: could not malloc() %u bytes\n", sizeof(Client));
arrange(c->mon);
XMapWindow(dpy, c->win);
focus(c);
- if(key_buffering) {
- // FIXME: try turning off buffering below where we send the virt keypresses
- key_buffering = False;
- grabkeys(); // stop grabbing typing keys
- if(key_buffer_len > 0) {
- for(i = 0; i < key_buffer_len; ++i) {
- send_keycode(key_buffer[i]);
- }
- }
- key_buffer_len = 0;
- // FIXME ie here
+ if(key_buffering == KeyBufferingAwaitingWindow) {
+ // almost there! but wait until we are notified that it has focus
+ key_buffering = KeyBufferingAwaitingFocus;
}
}
void
mappingnotify(XEvent *e) {
XMappingEvent *ev = &e->xmapping;
+ // fprintf(stderr, "MapNotify\n");
XRefreshKeyboardMapping(ev);
if(ev->request == MappingKeyboard)
static XWindowAttributes wa;
XMapRequestEvent *ev = &e->xmaprequest;
+ // fprintf(stderr, "MapRequest\n");
if(!XGetWindowAttributes(dpy, ev->window, &wa))
return;
if(wa.override_redirect) {
- key_buffer_len = 0;
- key_buffering = False;
return;
}
if(!wintoclient(ev->window))
XEvent ev;
/* main event loop */
XSync(dpy, False);
- while(running && !XNextEvent(dpy, &ev))
- if(handler[ev.type])
+ while(running && !XNextEvent(dpy, &ev)) {
+ if(handler[ev.type]) {
+ // fprintf(stderr, "handling event type %i\n", ev.type);
handler[ev.type](&ev); /* call handler */
+ } else {
+ // fprintf(stderr, "evt type %i\n", ev.type);
+ }
+ }
}
void
if(!c->neverfocus) {
XSetInputFocus(dpy, c->win, RevertToPointerRoot, CurrentTime);
XChangeProperty(dpy, root, netatom[NetActiveWindow],
- XA_WINDOW, 32, PropModeReplace,
- (unsigned char *) &(c->win), 1);
+ XA_WINDOW, 32, PropModeReplace,
+ (unsigned char *) &(c->win), 1);
}
sendevent(c, wmatom[WMTakeFocus]);
}
void
kbspawn(const Arg *arg) {
key_buffering = True;
- key_buffer_len = 0;
- grab_typing_keys();
spawn(arg);
}
void
spawn(const Arg *arg) {
int tag = 0, i;
- if(arg->v == termcmd) {
+ if(arg->v == termcmd || arg->v == belltermcmd || arg->v == runcmd) {
for(i = 0; i < 32; ++i) {
if(selmon->tagset[selmon->seltags] & (1 << i)) {
tag = i;
}
}
WORKSPACE_NUMBER[17] = workspace_numbers_str[tag][0];
- WORKSPACE_NUMBER[18] = workspace_numbers_str[tag][1];
+ WORKSPACE_NUMBER[18] = workspace_numbers_str[tag][1]; // 2nd digit or null
}
if(arg->v == dmenucmd)
dmenumon[0] = '0' + selmon->num;
}
}
-#define TAB_HEIGHT 19
-#define TAB_PAD 7
+// search forward from start, return the last client before end
+Client*
+prev_tiled (Client *start, Client *end) {
+ Client *w, *n;
+ for (w = start; w && (n = nexttiled(w->next)); w = n) {
+ if (n == end) {
+ return w;
+ }
+ }
+ return NULL;
+}
-void
-jason_layout(Monitor *m) {
- unsigned int i, tiled_count, mw, right_width, tab_counts[2] = {0,0}, cur_tab = 0, *tab_count;
- int tab_top;
- Client *c, *vis_slave = 0, *base = 0;
-
- tab_count = &(tab_counts[0]);
-
- for(tiled_count = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), tiled_count++) {
- if (tiled_count == 0) { // master
- if (c->next) {
- if (m->sel && (m->sel == c || m->sel->isfloating || !ISVISIBLE(m->sel))) {
- vis_slave = nexttiled(c->next);
- } else {
- vis_slave = m->sel;
- }
+// return the window to show on the right side
+Client*
+choose_slave (Client *master, Client *focused) {
+ if (!master) {
+ return NULL;
+ }
+ if (focused) {
+ // FIXME put this same algorithm in update_window_opacities
+ if (focused->isfloating) {
+ // show the window just "under" it
+ Client *prev = prev_tiled(master, focused);
+ // fall back to the first slave
+ if (prev && prev != master) {
+ return prev;
+ } else {
+ return nexttiled(master->next);
}
} else {
- if (c == vis_slave) {
- tab_count = &(tab_counts[1]);
+ // focused window is tiled
+ if (focused == master) {
+ // master is focused, show first slave
+ return nexttiled(master->next);
} else {
- (*tab_count) += 1;
+ // focused window is a slave, so show that one
+ return focused;
}
}
}
- if(tiled_count == 0) {
+ // maybe we get called when there's no focused?
+ return nexttiled(master->next);
+}
+
+#define GUTTER_PX 4
+
+void
+jason_layout(Monitor *m) {
+ Client *c, *master, *slave, *focused, *base = 0;
+ unsigned int master_width, slave_width, slave_left;
+
+ // find master
+ master = nexttiled(m->clients);
+
+ // no master? nothing to do
+ if (!master) {
return;
}
- if(tiled_count > 1 || (tiled_count == 1 && !nexttiled(m->clients)->screen_hog)) {
- mw = m->ww * m->mfact;
+ // find focused and slave
+ focused = (m->sel && ISVISIBLE(m->sel)) ? m->sel : NULL;
+ slave = choose_slave(master, focused);
+
+ // calculate window widths
+ if (!slave && master->screen_hog) {
+ master_width = m->ww;
} else {
- mw = m->ww;
- }
- right_width = m->ww - mw;
- tab_count = &(tab_counts[0]);
- tab_top = m->wy - (m->wh - (2 * (TAB_HEIGHT + TAB_PAD))) + TAB_HEIGHT;
- for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) {
- if (i == 0) {
- resize(c, m->wx, m->wy, mw, m->wh, False, 0);
- } else {
- if (c == vis_slave) {
- resize(c, m->wx + mw, m->wy + TAB_HEIGHT + TAB_PAD, right_width, m->wh - 2 * (TAB_HEIGHT + TAB_PAD), False, base);
- tab_count = &(tab_counts[1]);
- tab_top = m->wy + m->wh - TAB_HEIGHT;
- cur_tab = 0;
- } else {
- // this function does not get called when focus changes
- // resize(c, m->wx + m->ww, m->wy, m->ww - mw, m->wh, False);
- resize(c, m->wx + mw + right_width * cur_tab / (*tab_count), tab_top, right_width, m->wh - 2 * (TAB_HEIGHT + TAB_PAD), False, base);
- cur_tab += 1;
- }
- }
+ master_width = m->ww * m->mfact - GUTTER_PX / 2;
+ }
+ slave_left = m->ww * m->mfact + GUTTER_PX / 2;
+ slave_width = m->ww * (1 - m->mfact) - GUTTER_PX / 2;
+
+ // resize/reposition master
+ resize(master,
+ m->wx,
+ m->wy,
+ master_width,
+ m->wh,
+ False,
+ 0
+ );
+ // resize/reposition slaves
+ base = master; // window to be above in the stacking order
+ for (c = nexttiled(master->next); c; c = nexttiled(c->next)) {
+ resize(c,
+ slave_left,
+ c == slave ? m->wy : m->wy - m->wh,
+ slave_width,
+ m->wh,
+ False,
+ base
+ );
base = c;
}
}