eb02c0fb7a
reconfiguration events and change our sizes depending on that. We also detect the xinerama screens for maximize, vertmaximize and initial window placement. This could be improved by automatically resizing maximized windows when the screen resolution changes and by moving windows that would be hidden into visible space. Said changes will come shortly. Tested by many. matthieu@ didn't oppose.
572 lines
12 KiB
C
572 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.29 2008/09/29 23:16:46 oga 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;
|
|
XWindowAttributes xattr;
|
|
struct client_ctx *cc = NULL, *old_cc;
|
|
struct screen_ctx *sc;
|
|
|
|
#ifdef notyet
|
|
int state;
|
|
#endif
|
|
|
|
if ((old_cc = client_current()) != 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;
|
|
|
|
if ((cc = client_find(e->window)) != NULL)
|
|
client_delete(cc, e->send_event, 0);
|
|
|
|
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 ? cc->bwidth : e->x;
|
|
wc.y = cc != NULL ? cc->bwidth : 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;
|
|
struct screen_ctx *sc;
|
|
struct mousebinding *mb;
|
|
char *wname;
|
|
|
|
sc = screen_fromroot(e->root);
|
|
cc = client_find(e->window);
|
|
|
|
/* Ignore caps lock and numlock */
|
|
e->state &= ~(Mod2Mask | LockMask);
|
|
|
|
TAILQ_FOREACH(mb, &Conf.mousebindingq, entry) {
|
|
if (e->button == mb->button && e->state == mb->modmask)
|
|
break;
|
|
}
|
|
|
|
if (mb == NULL)
|
|
goto out;
|
|
|
|
if (mb->context == MOUSEBIND_CTX_ROOT) {
|
|
if (e->window != sc->rootwin)
|
|
goto out;
|
|
} else if (mb->context == MOUSEBIND_CTX_WIN) {
|
|
cc = client_find(e->window);
|
|
if (cc == NULL)
|
|
goto out;
|
|
}
|
|
|
|
(*mb->callback)(cc, e);
|
|
out:
|
|
xev_register(xev);
|
|
}
|
|
|
|
void
|
|
xev_handle_buttonrelease(struct xevent *xev, XEvent *ee)
|
|
{
|
|
struct client_ctx *cc;
|
|
|
|
if ((cc = client_current()) != 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;
|
|
struct keybinding *kb;
|
|
KeySym keysym, skeysym;
|
|
int modshift;
|
|
|
|
keysym = XKeycodeToKeysym(X_Dpy, e->keycode, 0);
|
|
skeysym = XKeycodeToKeysym(X_Dpy, e->keycode, 1);
|
|
|
|
/* we don't care about caps lock and numlock here */
|
|
e->state &= ~(LockMask | Mod2Mask);
|
|
|
|
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;
|
|
struct client_ctx *cc;
|
|
int keysym;
|
|
|
|
sc = screen_fromroot(e->root);
|
|
cc = client_current();
|
|
|
|
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);
|
|
|
|
if (cc != NULL) {
|
|
group_sticky_toggle_exit(cc);
|
|
XUngrabKeyboard(X_Dpy, CurrentTime);
|
|
}
|
|
|
|
out:
|
|
xev_register(xev);
|
|
}
|
|
|
|
void
|
|
xev_handle_clientmessage(struct xevent *xev, XEvent *ee)
|
|
{
|
|
XClientMessageEvent *e = &ee->xclient;
|
|
Atom xa_wm_change_state;
|
|
struct client_ctx *cc;
|
|
|
|
xa_wm_change_state = XInternAtom(X_Dpy, "WM_CHANGE_STATE", False);
|
|
|
|
if ((cc = client_find(e->window)) == 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);
|
|
}
|
|
|
|
void
|
|
xev_handle_shape(struct xevent *xev, XEvent *ee)
|
|
{
|
|
XShapeEvent *sev = (XShapeEvent *) ee;
|
|
struct client_ctx *cc;
|
|
|
|
if ((cc = client_find(sev->window)) != NULL)
|
|
client_do_shape(cc);
|
|
}
|
|
|
|
void
|
|
xev_handle_randr(struct xevent *xev, XEvent *ee)
|
|
{
|
|
XRRScreenChangeNotifyEvent *rev = (XRRScreenChangeNotifyEvent *)ee;
|
|
struct client_ctx *cc;
|
|
struct screen_ctx *sc;
|
|
|
|
if ((cc = client_find(rev->window)) != NULL) {
|
|
XRRUpdateConfiguration(ee);
|
|
sc = CCTOSC(cc);
|
|
sc->xmax = rev->width;
|
|
sc->ymax = rev->height;
|
|
screen_init_xinerama(sc);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Called when the keymap has changed.
|
|
* Ungrab all keys, reload keymap and then regrab
|
|
*/
|
|
void
|
|
xev_handle_mapping(struct xevent *xev, XEvent *ee)
|
|
{
|
|
XMappingEvent *e = &ee->xmapping;
|
|
struct keybinding *kb;
|
|
|
|
TAILQ_FOREACH(kb, &Conf.keybindingq, entry)
|
|
conf_ungrab(&Conf, kb);
|
|
|
|
XRefreshKeyboardMapping(e);
|
|
|
|
TAILQ_FOREACH(kb, &Conf.keybindingq, entry)
|
|
conf_grab(&Conf, kb);
|
|
|
|
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 && e->count == 0) {
|
|
client_draw_border(cc);
|
|
client_do_shape(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;
|
|
XEvent e;
|
|
struct xevent *xev = NULL, *nextxev;
|
|
int type;
|
|
|
|
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:
|
|
if (e.type == Shape_ev)
|
|
xev_handle_shape(xev, &e);
|
|
else if (e.type == Randr_ev)
|
|
xev_handle_randr(xev, &e);
|
|
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
|