2fc4faf28a
Noticed by oga@, thanks!
599 lines
12 KiB
C
599 lines
12 KiB
C
/*
|
|
* calmwm - the calm window manager
|
|
*
|
|
* Copyright (c) 2004 Marius Aamodt Eriksen <marius@monkey.org>
|
|
*
|
|
* Permission to use, copy, modify, and distribute this software for any
|
|
* purpose with or without fee is hereby granted, provided that the above
|
|
* copyright notice and this permission notice appear in all copies.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
*
|
|
* $Id: xevents.c,v 1.10 2008/04/08 00:09:50 simon Exp $
|
|
*/
|
|
|
|
/*
|
|
* NOTE:
|
|
* It is the responsibility of the caller to deal with memory
|
|
* management of the xevent's.
|
|
*/
|
|
|
|
#include "headers.h"
|
|
#include "calmwm.h"
|
|
|
|
/*
|
|
* NOTE: in reality, many of these should move to client.c now that
|
|
* we've got this nice event layer.
|
|
*/
|
|
|
|
void
|
|
xev_handle_maprequest(struct xevent *xev, XEvent *ee)
|
|
{
|
|
XMapRequestEvent *e = &ee->xmaprequest;
|
|
struct client_ctx *cc = NULL, *old_cc = client_current();
|
|
XWindowAttributes xattr;
|
|
struct screen_ctx *sc;
|
|
#ifdef notyet
|
|
int state;
|
|
#endif
|
|
|
|
if (old_cc != NULL)
|
|
client_ptrsave(old_cc);
|
|
|
|
if ((cc = client_find(e->window)) == NULL) {
|
|
XGetWindowAttributes(X_Dpy, e->window, &xattr);
|
|
cc = client_new(e->window, screen_fromroot(xattr.root), 1);
|
|
sc = CCTOSC(cc);
|
|
} else {
|
|
cc->beepbeep = 1;
|
|
}
|
|
|
|
#ifdef notyet /* XXX - possibly, we shouldn't map if
|
|
* the window is withdrawn. */
|
|
if (xu_getstate(cc, &state) == 0 && state == WithdrawnState)
|
|
warnx("WITHDRAWNSTATE for %s", cc->name);
|
|
#endif
|
|
|
|
client_ptrwarp(cc);
|
|
xev_register(xev);
|
|
}
|
|
|
|
void
|
|
xev_handle_unmapnotify(struct xevent *xev, XEvent *ee)
|
|
{
|
|
XUnmapEvent *e = &ee->xunmap;
|
|
struct client_ctx *cc;
|
|
struct screen_ctx *sc;
|
|
int wascurrent;
|
|
|
|
if ((cc = client_find(e->window)) != NULL) {
|
|
sc = CCTOSC(cc);
|
|
wascurrent = cc == client_current();
|
|
client_delete(cc, e->send_event, 0);
|
|
|
|
#ifdef notyet
|
|
/* XXX disable the ptrwarp until we handle it
|
|
* better. */
|
|
if (!client_delete(cc, e->send_event, 0) && wascurrent)
|
|
;/* client_ptrwarp(new_cc); */
|
|
#endif
|
|
}
|
|
|
|
xev_register(xev);
|
|
}
|
|
|
|
void
|
|
xev_handle_destroynotify(struct xevent *xev, XEvent *ee)
|
|
{
|
|
XDestroyWindowEvent *e = &ee->xdestroywindow;
|
|
struct client_ctx *cc;
|
|
|
|
if ((cc = client_find(e->window)) != NULL)
|
|
client_delete(cc, 1, 1);
|
|
|
|
xev_register(xev);
|
|
}
|
|
|
|
void
|
|
xev_handle_configurerequest(struct xevent *xev, XEvent *ee)
|
|
{
|
|
XConfigureRequestEvent *e = &ee->xconfigurerequest;
|
|
struct client_ctx *cc;
|
|
struct screen_ctx *sc;
|
|
XWindowChanges wc;
|
|
|
|
if ((cc = client_find(e->window)) != NULL) {
|
|
sc = CCTOSC(cc);
|
|
|
|
client_gravitate(cc, 0);
|
|
if (e->value_mask & CWWidth)
|
|
cc->geom.width = e->width;
|
|
if (e->value_mask & CWHeight)
|
|
cc->geom.height = e->height;
|
|
if (e->value_mask & CWX)
|
|
cc->geom.x = e->x;
|
|
if (e->value_mask & CWY)
|
|
cc->geom.y = e->y;
|
|
|
|
if (cc->geom.x == 0 &&
|
|
cc->geom.width >= DisplayWidth(X_Dpy, sc->which))
|
|
cc->geom.x -= cc->bwidth;
|
|
|
|
if (cc->geom.y == 0 &&
|
|
cc->geom.height >= DisplayHeight(X_Dpy, sc->which))
|
|
cc->geom.y -= cc->bwidth;
|
|
|
|
client_gravitate(cc, 1);
|
|
|
|
wc.x = cc->geom.x - cc->bwidth;
|
|
wc.y = cc->geom.y - cc->bwidth;
|
|
wc.width = cc->geom.width + cc->bwidth*2;
|
|
wc.height = cc->geom.height + cc->bwidth*2;
|
|
wc.border_width = 0;
|
|
|
|
/* We need to move the parent window, too. */
|
|
XConfigureWindow(X_Dpy, cc->pwin, e->value_mask, &wc);
|
|
xev_reconfig(cc);
|
|
}
|
|
|
|
wc.x = cc != NULL ? 0 : e->x;
|
|
wc.y = cc != NULL ? 0 : e->y;
|
|
wc.width = e->width;
|
|
wc.height = e->height;
|
|
wc.stack_mode = Above;
|
|
wc.border_width = 0;
|
|
e->value_mask &= ~CWStackMode;
|
|
e->value_mask |= CWBorderWidth;
|
|
|
|
XConfigureWindow(X_Dpy, e->window, e->value_mask, &wc);
|
|
|
|
xev_register(xev);
|
|
}
|
|
|
|
void
|
|
xev_handle_propertynotify(struct xevent *xev, XEvent *ee)
|
|
{
|
|
XPropertyEvent *e = &ee->xproperty;
|
|
struct client_ctx *cc;
|
|
long tmp;
|
|
|
|
if ((cc = client_find(e->window)) != NULL) {
|
|
switch(e->atom) {
|
|
case XA_WM_NORMAL_HINTS:
|
|
XGetWMNormalHints(X_Dpy, cc->win, cc->size, &tmp);
|
|
break;
|
|
case XA_WM_NAME:
|
|
client_setname(cc);
|
|
break;
|
|
default:
|
|
/* do nothing */
|
|
break;
|
|
}
|
|
}
|
|
|
|
xev_register(xev);
|
|
}
|
|
|
|
void
|
|
xev_reconfig(struct client_ctx *cc)
|
|
{
|
|
XConfigureEvent ce;
|
|
|
|
ce.type = ConfigureNotify;
|
|
ce.event = cc->win;
|
|
ce.window = cc->win;
|
|
ce.x = cc->geom.x;
|
|
ce.y = cc->geom.y;
|
|
ce.width = cc->geom.width;
|
|
ce.height = cc->geom.height;
|
|
ce.border_width = 0;
|
|
ce.above = None;
|
|
ce.override_redirect = 0;
|
|
|
|
XSendEvent(X_Dpy, cc->win, False, StructureNotifyMask, (XEvent *)&ce);
|
|
}
|
|
|
|
void
|
|
xev_handle_enternotify(struct xevent *xev, XEvent *ee)
|
|
{
|
|
XCrossingEvent *e = &ee->xcrossing;
|
|
struct client_ctx *cc;
|
|
|
|
if ((cc = client_find(e->window)) == NULL) {
|
|
/*
|
|
* XXX - later. messes up unclutter. but may be
|
|
* needed when we introduce menu windows and such into
|
|
* the main event loop.
|
|
*/
|
|
#ifdef notyet
|
|
if (e->window != e->root)
|
|
client_nocurrent();
|
|
#endif
|
|
} else
|
|
client_setactive(cc, 1);
|
|
|
|
xev_register(xev);
|
|
}
|
|
|
|
void
|
|
xev_handle_leavenotify(struct xevent *xev, XEvent *ee)
|
|
{
|
|
client_leave(NULL);
|
|
|
|
xev_register(xev);
|
|
}
|
|
|
|
/* We can split this into two event handlers. */
|
|
void
|
|
xev_handle_buttonpress(struct xevent *xev, XEvent *ee)
|
|
{
|
|
XButtonEvent *e = &ee->xbutton;
|
|
struct client_ctx *cc, *old_cc = client_current();
|
|
struct screen_ctx *sc = screen_fromroot(e->root);
|
|
char *wname;
|
|
int altcontrol = e->state == (ControlMask|Mod1Mask);
|
|
|
|
cc = client_find(e->window);
|
|
|
|
if (sc->rootwin == e->window && !altcontrol) {
|
|
struct menu_q menuq;
|
|
struct menu *mi;
|
|
|
|
/* XXXSIGH!!!! */
|
|
if (e->button == Button2) {
|
|
group_menu(e);
|
|
goto out;
|
|
}
|
|
|
|
TAILQ_INIT(&menuq);
|
|
|
|
switch (e->button) {
|
|
case Button1:
|
|
TAILQ_FOREACH(cc, &Clientq, entry) {
|
|
if (cc->flags & CLIENT_HIDDEN) {
|
|
if (cc->label != NULL)
|
|
wname = cc->label;
|
|
else
|
|
wname = cc->name;
|
|
|
|
if (wname == NULL)
|
|
continue;
|
|
|
|
XCALLOC(mi, struct menu);
|
|
strlcpy(mi->text,
|
|
wname, sizeof(mi->text));
|
|
mi->ctx = cc;
|
|
TAILQ_INSERT_TAIL(&menuq, mi, entry);
|
|
}
|
|
}
|
|
break;
|
|
case Button3: {
|
|
struct cmd *cmd;
|
|
conf_reload(&Conf);
|
|
TAILQ_FOREACH(cmd, &Conf.cmdq, entry) {
|
|
XCALLOC(mi, struct menu);
|
|
strlcpy(mi->text, cmd->label, sizeof(mi->text));
|
|
mi->ctx = cmd;
|
|
TAILQ_INSERT_TAIL(&menuq, mi, entry);
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (TAILQ_EMPTY(&menuq))
|
|
goto out;
|
|
|
|
mi = (struct menu *)grab_menu(e, &menuq);
|
|
if (mi == NULL)
|
|
goto cleanup;
|
|
|
|
switch (e->button) {
|
|
case Button1:
|
|
cc = (struct client_ctx *)mi->ctx;
|
|
client_unhide(cc);
|
|
|
|
if (old_cc != NULL)
|
|
client_ptrsave(old_cc);
|
|
client_ptrwarp(cc);
|
|
break;
|
|
case Button3:
|
|
u_spawn(((struct cmd *)mi->ctx)->image);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
cleanup:
|
|
while ((mi = TAILQ_FIRST(&menuq)) != NULL) {
|
|
TAILQ_REMOVE(&menuq, mi, entry);
|
|
xfree(mi);
|
|
}
|
|
|
|
goto out;
|
|
}
|
|
|
|
if (cc == NULL || e->state == 0)
|
|
goto out;
|
|
|
|
sc = CCTOSC(cc);
|
|
|
|
switch (e->button) {
|
|
case Button1:
|
|
if (altcontrol)
|
|
group_sticky_toggle_enter(cc);
|
|
else {
|
|
grab_drag(cc);
|
|
client_move(cc);
|
|
}
|
|
break;
|
|
case Button2:
|
|
grab_sweep(cc);
|
|
client_resize(cc);
|
|
break;
|
|
case Button3:
|
|
client_ptrsave(cc);
|
|
client_lower(cc);
|
|
break;
|
|
}
|
|
out:
|
|
xev_register(xev);
|
|
}
|
|
|
|
void
|
|
xev_handle_buttonrelease(struct xevent *xev, XEvent *ee)
|
|
{
|
|
struct client_ctx *cc = client_current();
|
|
|
|
if (cc != NULL)
|
|
group_sticky_toggle_exit(cc);
|
|
|
|
xev_register(xev);
|
|
|
|
}
|
|
|
|
void
|
|
xev_handle_keypress(struct xevent *xev, XEvent *ee)
|
|
{
|
|
XKeyEvent *e = &ee->xkey;
|
|
struct client_ctx *cc = NULL; /* Make gcc happy. */
|
|
struct keybinding *kb;
|
|
KeySym keysym, skeysym;
|
|
int modshift;
|
|
|
|
keysym = XKeycodeToKeysym(X_Dpy, e->keycode, 0);
|
|
skeysym = XKeycodeToKeysym(X_Dpy, e->keycode, 1);
|
|
|
|
TAILQ_FOREACH(kb, &Conf.keybindingq, entry) {
|
|
if (keysym != kb->keysym && skeysym == kb->keysym)
|
|
modshift = ShiftMask;
|
|
else
|
|
modshift = 0;
|
|
|
|
if ((kb->modmask | modshift) != e->state)
|
|
continue;
|
|
|
|
if ((kb->keycode != 0 && kb->keysym == NoSymbol &&
|
|
kb->keycode == e->keycode) || kb->keysym ==
|
|
(modshift == 0 ? keysym : skeysym))
|
|
break;
|
|
}
|
|
|
|
if (kb == NULL)
|
|
goto out;
|
|
|
|
if ((kb->flags & (KBFLAG_NEEDCLIENT)) &&
|
|
(cc = client_find(e->window)) == NULL &&
|
|
(cc = client_current()) == NULL)
|
|
if (kb->flags & KBFLAG_NEEDCLIENT)
|
|
goto out;
|
|
|
|
(*kb->callback)(cc, kb->argument);
|
|
|
|
out:
|
|
xev_register(xev);
|
|
}
|
|
|
|
/*
|
|
* This is only used for the alt suppression detection.
|
|
*/
|
|
void
|
|
xev_handle_keyrelease(struct xevent *xev, XEvent *ee)
|
|
{
|
|
XKeyEvent *e = &ee->xkey;
|
|
struct screen_ctx *sc = screen_fromroot(e->root);
|
|
int keysym;
|
|
|
|
keysym = XKeycodeToKeysym(X_Dpy, e->keycode, 0);
|
|
if (keysym != XK_Alt_L && keysym != XK_Alt_R)
|
|
goto out;
|
|
|
|
sc->altpersist = 0;
|
|
|
|
/*
|
|
* XXX - better interface... xevents should not know about
|
|
* how/when to mtf.
|
|
*/
|
|
client_mtf(NULL);
|
|
client_altrelease();
|
|
|
|
out:
|
|
xev_register(xev);
|
|
}
|
|
|
|
void
|
|
xev_handle_clientmessage(struct xevent *xev, XEvent *ee)
|
|
{
|
|
XClientMessageEvent *e = &ee->xclient;
|
|
struct client_ctx *cc = client_find(e->window);
|
|
Atom xa_wm_change_state = XInternAtom(X_Dpy, "WM_CHANGE_STATE", False);
|
|
|
|
if (cc == NULL)
|
|
goto out;
|
|
|
|
if (e->message_type == xa_wm_change_state && e->format == 32 &&
|
|
e->data.l[0] == IconicState)
|
|
client_hide(cc);
|
|
out:
|
|
xev_register(xev);
|
|
}
|
|
|
|
/*
|
|
* X Event handling
|
|
*/
|
|
|
|
static struct xevent_q _xevq, _xevq_putaway;
|
|
static short _xev_q_lock = 0;
|
|
volatile sig_atomic_t _xev_quit = 0;
|
|
|
|
void
|
|
xev_init(void)
|
|
{
|
|
TAILQ_INIT(&_xevq);
|
|
TAILQ_INIT(&_xevq_putaway);
|
|
}
|
|
|
|
struct xevent *
|
|
xev_new(Window *win, Window *root,
|
|
int type, void (*cb)(struct xevent *, XEvent *), void *arg)
|
|
{
|
|
struct xevent *xev;
|
|
|
|
XMALLOC(xev, struct xevent);
|
|
xev->xev_win = win;
|
|
xev->xev_root = root;
|
|
xev->xev_type = type;
|
|
xev->xev_cb = cb;
|
|
xev->xev_arg = arg;
|
|
|
|
return (xev);
|
|
}
|
|
|
|
void
|
|
xev_register(struct xevent *xev)
|
|
{
|
|
struct xevent_q *xq;
|
|
|
|
xq = _xev_q_lock ? &_xevq_putaway : &_xevq;
|
|
TAILQ_INSERT_TAIL(xq, xev, entry);
|
|
}
|
|
|
|
void
|
|
_xev_reincorporate(void)
|
|
{
|
|
struct xevent *xev;
|
|
|
|
while ((xev = TAILQ_FIRST(&_xevq_putaway)) != NULL) {
|
|
TAILQ_REMOVE(&_xevq_putaway, xev, entry);
|
|
TAILQ_INSERT_TAIL(&_xevq, xev, entry);
|
|
}
|
|
}
|
|
|
|
void
|
|
xev_handle_expose(struct xevent *xev, XEvent *ee)
|
|
{
|
|
XExposeEvent *e = &ee->xexpose;
|
|
struct client_ctx *cc;
|
|
|
|
if ((cc = client_find(e->window)) != NULL)
|
|
client_draw_border(cc);
|
|
|
|
xev_register(xev);
|
|
}
|
|
|
|
#define ASSIGN(xtype) do { \
|
|
root = e. xtype .root; \
|
|
win = e. xtype .window; \
|
|
} while (0)
|
|
|
|
#define ASSIGN1(xtype) do { \
|
|
win = e. xtype .window; \
|
|
} while (0)
|
|
|
|
void
|
|
xev_loop(void)
|
|
{
|
|
Window win, root;
|
|
int type;
|
|
XEvent e;
|
|
struct xevent *xev, *nextxev;
|
|
|
|
while (_xev_quit == 0) {
|
|
#ifdef DIAGNOSTIC
|
|
if (TAILQ_EMPTY(&_xevq))
|
|
errx(1, "X event queue empty");
|
|
#endif
|
|
|
|
XNextEvent(X_Dpy, &e);
|
|
type = e.type;
|
|
|
|
win = root = 0;
|
|
|
|
switch (type) {
|
|
case MapRequest:
|
|
ASSIGN1(xmaprequest);
|
|
break;
|
|
case UnmapNotify:
|
|
ASSIGN1(xunmap);
|
|
break;
|
|
case ConfigureRequest:
|
|
ASSIGN1(xconfigurerequest);
|
|
break;
|
|
case PropertyNotify:
|
|
ASSIGN1(xproperty);
|
|
break;
|
|
case EnterNotify:
|
|
case LeaveNotify:
|
|
ASSIGN(xcrossing);
|
|
break;
|
|
case ButtonPress:
|
|
ASSIGN(xbutton);
|
|
break;
|
|
case ButtonRelease:
|
|
ASSIGN(xbutton);
|
|
break;
|
|
case KeyPress:
|
|
case KeyRelease:
|
|
ASSIGN(xkey);
|
|
break;
|
|
case DestroyNotify:
|
|
ASSIGN1(xdestroywindow);
|
|
break;
|
|
case ClientMessage:
|
|
ASSIGN1(xclient);
|
|
break;
|
|
default: /* XXX - still need shape event support. */
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* Now, search for matches, and call each of them.
|
|
*/
|
|
_xev_q_lock = 1;
|
|
for (xev = TAILQ_FIRST(&_xevq); xev != NULL; xev = nextxev) {
|
|
nextxev = TAILQ_NEXT(xev, entry);
|
|
|
|
if ((type != xev->xev_type && xev->xev_type != 0) ||
|
|
(xev->xev_win != NULL && win != *xev->xev_win) ||
|
|
(xev->xev_root != NULL && root != *xev->xev_root))
|
|
continue;
|
|
|
|
TAILQ_REMOVE(&_xevq, xev, entry);
|
|
|
|
(*xev->xev_cb)(xev, &e);
|
|
}
|
|
_xev_q_lock = 0;
|
|
_xev_reincorporate();
|
|
}
|
|
}
|
|
|
|
#undef ASSIGN
|
|
#undef ASSIGN1
|