paulo@0: /* (C)opyright MMVI-MMVII Anselm R. Garbe paulo@0: * See LICENSE file for license details. paulo@0: */ paulo@0: #include "dwm.h" paulo@0: #include paulo@0: #include paulo@0: paulo@0: /* static */ paulo@0: paulo@6: static unsigned int cmdmod[4]; paulo@6: static unsigned int keymode = COMMANDMODE; paulo@6: static KeySym cmdkeysym[4]; paulo@6: paulo@0: typedef struct { paulo@0: unsigned long mod; paulo@0: KeySym keysym; paulo@0: void (*func)(Arg *arg); paulo@0: Arg arg; paulo@0: } Key; paulo@0: paulo@0: KEYS paulo@0: paulo@0: #define CLEANMASK(mask) (mask & ~(numlockmask | LockMask)) paulo@0: #define MOUSEMASK (BUTTONMASK | PointerMotionMask) paulo@0: paulo@0: static Client * paulo@0: getclient(Window w) { paulo@0: Client *c; paulo@0: paulo@0: for(c = clients; c && c->win != w; c = c->next); paulo@0: return c; paulo@0: } paulo@0: paulo@0: static void paulo@0: movemouse(Client *c) { paulo@0: int x1, y1, ocx, ocy, di, nx, ny; paulo@0: unsigned int dui; paulo@0: Window dummy; paulo@0: XEvent ev; paulo@0: paulo@0: ocx = nx = c->x; paulo@0: ocy = ny = c->y; paulo@0: if(XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync, paulo@0: None, cursor[CurMove], CurrentTime) != GrabSuccess) paulo@0: return; paulo@0: c->ismax = False; paulo@0: XQueryPointer(dpy, root, &dummy, &dummy, &x1, &y1, &di, &di, &dui); paulo@0: for(;;) { paulo@0: XMaskEvent(dpy, MOUSEMASK | ExposureMask | SubstructureRedirectMask, &ev); paulo@0: switch (ev.type) { paulo@0: case ButtonRelease: paulo@0: XUngrabPointer(dpy, CurrentTime); paulo@0: return; paulo@0: case ConfigureRequest: paulo@0: case Expose: paulo@0: case MapRequest: paulo@0: handler[ev.type](&ev); paulo@0: break; paulo@0: case MotionNotify: paulo@0: XSync(dpy, False); paulo@0: nx = ocx + (ev.xmotion.x - x1); paulo@0: ny = ocy + (ev.xmotion.y - y1); paulo@0: if(abs(wax + nx) < SNAP) paulo@0: nx = wax; paulo@0: else if(abs((wax + waw) - (nx + c->w + 2 * c->border)) < SNAP) paulo@0: nx = wax + waw - c->w - 2 * c->border; paulo@0: if(abs(way - ny) < SNAP) paulo@0: ny = way; paulo@0: else if(abs((way + wah) - (ny + c->h + 2 * c->border)) < SNAP) paulo@0: ny = way + wah - c->h - 2 * c->border; paulo@0: resize(c, nx, ny, c->w, c->h, False); paulo@0: break; paulo@0: } paulo@0: } paulo@0: } paulo@0: paulo@0: static void paulo@0: resizemouse(Client *c) { paulo@0: int ocx, ocy; paulo@0: int nw, nh; paulo@0: XEvent ev; paulo@0: paulo@0: ocx = c->x; paulo@0: ocy = c->y; paulo@0: if(XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync, paulo@0: None, cursor[CurResize], CurrentTime) != GrabSuccess) paulo@0: return; paulo@0: c->ismax = False; paulo@0: XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w + c->border - 1, c->h + c->border - 1); paulo@0: for(;;) { paulo@0: XMaskEvent(dpy, MOUSEMASK | ExposureMask | SubstructureRedirectMask , &ev); paulo@0: switch(ev.type) { paulo@0: case ButtonRelease: paulo@0: XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, paulo@0: c->w + c->border - 1, c->h + c->border - 1); paulo@0: XUngrabPointer(dpy, CurrentTime); paulo@0: while(XCheckMaskEvent(dpy, EnterWindowMask, &ev)); paulo@0: return; paulo@0: case ConfigureRequest: paulo@0: case Expose: paulo@0: case MapRequest: paulo@0: handler[ev.type](&ev); paulo@0: break; paulo@0: case MotionNotify: paulo@0: XSync(dpy, False); paulo@0: if((nw = ev.xmotion.x - ocx - 2 * c->border + 1) <= 0) paulo@0: nw = 1; paulo@0: if((nh = ev.xmotion.y - ocy - 2 * c->border + 1) <= 0) paulo@0: nh = 1; paulo@0: resize(c, c->x, c->y, nw, nh, True); paulo@0: break; paulo@0: } paulo@0: } paulo@0: } paulo@0: paulo@0: static void paulo@0: buttonpress(XEvent *e) { paulo@0: int x; paulo@0: Arg a; paulo@0: Client *c; paulo@0: XButtonPressedEvent *ev = &e->xbutton; paulo@0: paulo@0: if(barwin == ev->window) { paulo@0: x = 0; paulo@0: for(a.i = 0; a.i < ntags; a.i++) { paulo@0: x += textw(tags[a.i]); paulo@0: if(ev->x < x) { paulo@0: if(ev->button == Button1) { paulo@0: if(ev->state & MODKEY) paulo@0: tag(&a); paulo@0: else paulo@0: view(&a); paulo@0: } paulo@0: else if(ev->button == Button3) { paulo@0: if(ev->state & MODKEY) paulo@0: toggletag(&a); paulo@0: else paulo@0: toggleview(&a); paulo@0: } paulo@0: return; paulo@0: } paulo@0: } paulo@0: if(ev->x < x + blw) paulo@0: switch(ev->button) { paulo@0: case Button1: paulo@0: a.i = -1; paulo@0: setlayout(&a); paulo@0: break; pang@2: case Button3: pang@2: a.i = -2; pang@2: setlayout(&a); pang@2: break; paulo@0: case Button4: paulo@0: a.i = 1; paulo@0: incnmaster(&a); paulo@0: break; paulo@0: case Button5: paulo@0: a.i = -1; paulo@0: incnmaster(&a); paulo@0: break; paulo@0: } paulo@0: if(ev->x > x + blw) paulo@0: switch(ev->button) { paulo@3: case Button1: zoom(NULL); break; paulo@3: case Button2: toggleversatile(NULL); break; paulo@3: case Button3: killclient(NULL); break; paulo@3: case Button4: pushup(NULL); break; paulo@3: case Button5: pushdown(NULL); break; paulo@3: } paulo@3: } paulo@3: else if(tbarwin == ev->window) { paulo@3: int i, w; paulo@4: for(i=0, c = clients; c; c = c->next) { paulo@4: if(isvisible(c)) paulo@4: i++; paulo@4: } paulo@4: if(!i || i >= MAX_TASKS) paulo@4: return; paulo@4: else paulo@4: w = sw/i; paulo@3: switch(ev->button) { paulo@3: case Button4: focusprev(NULL); return; break; paulo@3: case Button5: focusnext(NULL); return; break; paulo@0: } paulo@3: for(i=1, c = clients; c && i*w<=sw; c = c->next, i++) { paulo@3: for(; c && !isvisible(c); c = c->next); paulo@3: if(i*w > ev->x) { paulo@3: switch(ev->button) { paulo@3: case Button1: focus(c); restack(); break; paulo@3: case Button3: focus(c); zoom(NULL); break; paulo@3: } paulo@3: break; paulo@3: } paulo@3: } paulo@3: } paulo@0: else if((c = getclient(ev->window))) { paulo@0: focus(c); paulo@0: if(CLEANMASK(ev->state) != MODKEY) paulo@0: return; paulo@0: if(ev->button == Button1 && (lt->arrange == versatile || c->isversatile)) { paulo@0: restack(); paulo@0: movemouse(c); paulo@0: } paulo@0: else if(ev->button == Button2) paulo@0: zoom(NULL); paulo@0: else if(ev->button == Button3 paulo@0: && (lt->arrange == versatile || c->isversatile) && !c->isfixed) paulo@0: { paulo@0: restack(); paulo@0: resizemouse(c); paulo@0: } paulo@0: } paulo@0: } paulo@0: paulo@0: static void paulo@0: configurerequest(XEvent *e) { paulo@0: Client *c; paulo@0: XConfigureRequestEvent *ev = &e->xconfigurerequest; paulo@0: XWindowChanges wc; paulo@0: paulo@0: if((c = getclient(ev->window))) { paulo@0: c->ismax = False; paulo@0: if(ev->value_mask & CWBorderWidth) paulo@0: c->border = ev->border_width; paulo@0: if(c->isfixed || c->isversatile || (lt->arrange == versatile)) { paulo@0: if(ev->value_mask & CWX) paulo@0: c->x = ev->x; paulo@0: if(ev->value_mask & CWY) paulo@0: c->y = ev->y; paulo@0: if(ev->value_mask & CWWidth) paulo@0: c->w = ev->width; paulo@0: if(ev->value_mask & CWHeight) paulo@0: c->h = ev->height; paulo@0: if((ev->value_mask & (CWX | CWY)) paulo@0: && !(ev->value_mask & (CWWidth | CWHeight))) paulo@0: configure(c); paulo@0: if(isvisible(c)) paulo@0: /* XMoveResizeWindow(dpy, c->win, c->x, c->y, c->w, c->h); */ paulo@0: resize(c, c->x, c->y, c->w, c->h, False); paulo@0: } paulo@0: else paulo@0: configure(c); paulo@0: } paulo@0: else { paulo@0: wc.x = ev->x; paulo@0: wc.y = ev->y; paulo@0: wc.width = ev->width; paulo@0: wc.height = ev->height; paulo@0: wc.border_width = ev->border_width; paulo@0: wc.sibling = ev->above; paulo@0: wc.stack_mode = ev->detail; paulo@0: XConfigureWindow(dpy, ev->window, ev->value_mask, &wc); paulo@0: } paulo@0: XSync(dpy, False); paulo@0: } paulo@0: paulo@0: static void paulo@0: destroynotify(XEvent *e) { paulo@0: Client *c; paulo@0: XDestroyWindowEvent *ev = &e->xdestroywindow; paulo@0: paulo@0: if((c = getclient(ev->window))) paulo@0: unmanage(c); paulo@0: } paulo@0: paulo@0: static void paulo@0: enternotify(XEvent *e) { paulo@0: Client *c; paulo@0: XCrossingEvent *ev = &e->xcrossing; paulo@0: paulo@0: if(ev->mode != NotifyNormal || ev->detail == NotifyInferior) paulo@0: return; paulo@3: if(!CLICK_TO_FOCUS && (c = getclient(ev->window)) && isvisible(c)) paulo@0: focus(c); paulo@0: else if(ev->window == root) { paulo@0: selscreen = True; paulo@0: for(c = stack; c && !isvisible(c); c = c->snext); paulo@0: focus(c); paulo@0: } paulo@0: } paulo@0: paulo@0: static void paulo@0: expose(XEvent *e) { paulo@0: XExposeEvent *ev = &e->xexpose; paulo@0: paulo@0: if(ev->count == 0) { paulo@3: if(barwin == ev->window || tbarwin == ev->window) paulo@0: drawstatus(); paulo@0: } paulo@0: } paulo@0: paulo@0: static void paulo@6: defkeypress(XEvent *e) { paulo@0: static unsigned int len = sizeof key / sizeof key[0]; paulo@0: unsigned int i; paulo@0: KeySym keysym; paulo@0: XKeyEvent *ev = &e->xkey; paulo@0: paulo@0: keysym = XKeycodeToKeysym(dpy, (KeyCode)ev->keycode, 0); paulo@0: for(i = 0; i < len; i++) paulo@0: if(keysym == key[i].keysym paulo@0: && CLEANMASK(key[i].mod) == CLEANMASK(ev->state)) paulo@0: { paulo@0: if(key[i].func) paulo@0: key[i].func(&key[i].arg); paulo@0: } paulo@0: } paulo@0: paulo@0: static void paulo@0: leavenotify(XEvent *e) { paulo@0: XCrossingEvent *ev = &e->xcrossing; paulo@0: paulo@0: if((ev->window == root) && !ev->same_screen) { paulo@0: selscreen = False; paulo@0: focus(NULL); paulo@0: } paulo@0: } paulo@0: paulo@0: static void paulo@0: mappingnotify(XEvent *e) { paulo@0: XMappingEvent *ev = &e->xmapping; paulo@0: paulo@0: XRefreshKeyboardMapping(ev); paulo@0: if(ev->request == MappingKeyboard) paulo@0: grabkeys(); paulo@0: } paulo@0: paulo@0: static void paulo@0: maprequest(XEvent *e) { paulo@0: static XWindowAttributes wa; paulo@0: XMapRequestEvent *ev = &e->xmaprequest; paulo@0: paulo@0: if(!XGetWindowAttributes(dpy, ev->window, &wa)) paulo@0: return; paulo@0: if(wa.override_redirect) paulo@0: return; paulo@0: if(!getclient(ev->window)) paulo@0: manage(ev->window, &wa); paulo@0: } paulo@0: paulo@0: static void paulo@0: propertynotify(XEvent *e) { paulo@0: Client *c; paulo@0: Window trans; paulo@0: XPropertyEvent *ev = &e->xproperty; paulo@0: paulo@0: if(ev->state == PropertyDelete) paulo@0: return; /* ignore */ paulo@0: if((c = getclient(ev->window))) { paulo@0: switch (ev->atom) { paulo@0: default: break; paulo@0: case XA_WM_TRANSIENT_FOR: paulo@0: XGetTransientForHint(dpy, c->win, &trans); paulo@0: if(!c->isversatile && (c->isversatile = (getclient(trans) != NULL))) paulo@0: lt->arrange(); paulo@0: break; paulo@0: case XA_WM_NORMAL_HINTS: paulo@0: updatesizehints(c); paulo@0: break; paulo@0: } paulo@0: if(ev->atom == XA_WM_NAME || ev->atom == netatom[NetWMName]) { paulo@0: updatetitle(c); paulo@0: if(c == sel) paulo@0: drawstatus(); paulo@0: } paulo@0: } paulo@0: } paulo@0: paulo@0: static void paulo@0: unmapnotify(XEvent *e) { paulo@0: Client *c; paulo@0: XUnmapEvent *ev = &e->xunmap; paulo@0: paulo@0: if((c = getclient(ev->window))) paulo@0: unmanage(c); paulo@0: } paulo@0: paulo@6: typedef struct { paulo@6: unsigned int mod[4]; paulo@6: KeySym keysym[4]; paulo@6: void (*func)(Arg *arg); paulo@6: Arg arg; paulo@6: } Command; paulo@6: paulo@6: CMDKEYS paulo@6: COMMANDS paulo@6: paulo@6: void paulo@6: keypress(XEvent *e) { paulo@6: unsigned int i, j; paulo@6: Arg a = {0}; paulo@6: Bool ismatch = False, maybematch = False; paulo@6: KeySym keysym; paulo@6: XKeyEvent *ev; paulo@6: paulo@6: if(keymode == INSERTMODE) paulo@6: defkeypress(e); paulo@6: else if(keymode == COMMANDMODE) { paulo@6: ev = &e->xkey; paulo@6: keysym = XKeycodeToKeysym(dpy, (KeyCode)ev->keycode, 0); paulo@6: if(keysym < XK_Shift_L || keysym > XK_Hyper_R) { paulo@6: for(j = 0; j < LENGTH(cmdkeysym); j++) paulo@6: if(cmdkeysym[j] == 0) { paulo@6: cmdkeysym[j] = keysym; paulo@6: cmdmod[j] = ev->state; paulo@6: break; paulo@6: } paulo@6: for(i = 0; i < LENGTH(commands); i++) { paulo@6: for(j = 0; j < LENGTH(cmdkeysym); j++) { paulo@6: if(cmdkeysym[j] == commands[i].keysym[j] paulo@6: && CLEANMASK(cmdmod[j]) == CLEANMASK(commands[i].mod[j])) paulo@6: ismatch = True; paulo@6: else if(cmdkeysym[j] == 0 paulo@6: && cmdmod[j] == 0) { paulo@6: ismatch = False; paulo@6: maybematch = True; paulo@6: break; paulo@6: } else { paulo@6: ismatch = False; paulo@6: break; paulo@6: } paulo@6: } paulo@6: if(ismatch) { paulo@6: if(commands[i].func) paulo@6: commands[i].func(&(commands[i].arg)); paulo@6: clearcmd(&a); paulo@6: break; paulo@6: } paulo@6: paulo@6: } paulo@6: if(!maybematch) paulo@6: clearcmd(&a); paulo@6: if(!ismatch) { paulo@6: for(i = 0; i < LENGTH(cmdkeys); i++) paulo@6: if(keysym == cmdkeys[i].keysym paulo@6: && CLEANMASK(cmdkeys[i].mod) == CLEANMASK(ev->state) paulo@6: && cmdkeys[i].func) { paulo@6: cmdkeys[i].func(&(cmdkeys[i].arg)); paulo@6: ismatch = True; paulo@6: break; paulo@6: } paulo@6: } paulo@6: } paulo@6: } paulo@6: } paulo@6: paulo@0: /* extern */ paulo@0: paulo@0: void (*handler[LASTEvent]) (XEvent *) = { paulo@0: [ButtonPress] = buttonpress, paulo@0: [ConfigureRequest] = configurerequest, paulo@0: [DestroyNotify] = destroynotify, paulo@0: [EnterNotify] = enternotify, paulo@0: [LeaveNotify] = leavenotify, paulo@0: [Expose] = expose, paulo@0: [KeyPress] = keypress, paulo@0: [MappingNotify] = mappingnotify, paulo@0: [MapRequest] = maprequest, paulo@0: [PropertyNotify] = propertynotify, paulo@0: [UnmapNotify] = unmapnotify paulo@0: }; paulo@0: paulo@0: void paulo@6: grabdefkeys(void) { paulo@0: static unsigned int len = sizeof key / sizeof key[0]; paulo@0: unsigned int i; paulo@0: KeyCode code; paulo@0: paulo@0: XUngrabKey(dpy, AnyKey, AnyModifier, root); paulo@0: for(i = 0; i < len; i++) { paulo@0: code = XKeysymToKeycode(dpy, key[i].keysym); paulo@0: XGrabKey(dpy, code, key[i].mod, root, True, paulo@0: GrabModeAsync, GrabModeAsync); paulo@0: XGrabKey(dpy, code, key[i].mod | LockMask, root, True, paulo@0: GrabModeAsync, GrabModeAsync); paulo@0: XGrabKey(dpy, code, key[i].mod | numlockmask, root, True, paulo@0: GrabModeAsync, GrabModeAsync); paulo@0: XGrabKey(dpy, code, key[i].mod | numlockmask | LockMask, root, True, paulo@0: GrabModeAsync, GrabModeAsync); paulo@0: } paulo@0: } paulo@6: paulo@6: void paulo@6: clearcmd(Arg *arg) { paulo@6: unsigned int i; paulo@6: paulo@6: for(i = 0; i < LENGTH(cmdkeysym); i++) { paulo@6: cmdkeysym[i] = 0; paulo@6: cmdmod[i] = 0; paulo@6: } paulo@6: } paulo@6: paulo@6: void paulo@6: grabkeys(void) { paulo@6: if(keymode == INSERTMODE) { paulo@6: XUngrabKeyboard(dpy, CurrentTime); paulo@6: grabdefkeys(); paulo@6: } else if(keymode == COMMANDMODE) { paulo@6: XUngrabKey(dpy, AnyKey, AnyModifier, root); paulo@6: //XGrabKey(dpy, AnyKey, AnyModifier, root, paulo@6: // True, GrabModeAsync, GrabModeAsync); paulo@6: XGrabKeyboard(dpy, root, True, GrabModeAsync, GrabModeAsync, CurrentTime); paulo@6: } paulo@6: } paulo@6: paulo@6: void paulo@6: setkeymode(Arg *arg) { paulo@6: Arg a = {0}; paulo@6: paulo@6: if(!arg) paulo@6: return; paulo@6: keymode = arg->i; paulo@6: clearcmd(&a); paulo@6: grabkeys(); paulo@6: drawstatus(); paulo@6: } paulo@6: paulo@6: unsigned int paulo@6: getkeymode(void) { paulo@6: return keymode; paulo@6: } paulo@6: paulo@6: void paulo@6: func_insert(void (*argfunc)(Arg *), Arg *arg) { paulo@6: Arg a = { .i = INSERTMODE }; paulo@6: argfunc(arg); paulo@6: setkeymode(&a); paulo@6: }