diff --git a/Makefile b/Makefile index 2f2ee1c..8bbf13e 100644 --- a/Makefile +++ b/Makefile @@ -11,7 +11,7 @@ SRCS= calmwm.c screen.c xmalloc.c client.c grab.c menu.c \ CPPFLAGS+= -I${X11BASE}/include -I${X11BASE}/include/freetype2 -I${.CURDIR} LDADD+= -L${X11BASE}/lib -lXft -lXrender -lX11 -lXau -lXdmcp -lXext \ - -lfontconfig -lexpat -lfreetype -lz + -lfontconfig -lexpat -lfreetype -lz -lXinerama -lXrandr MANDIR= ${X11BASE}/man/cat MAN= cwm.1 cwmrc.5 diff --git a/calmwm.c b/calmwm.c index c64035d..446ed17 100644 --- a/calmwm.c +++ b/calmwm.c @@ -36,6 +36,7 @@ u_int Nscreens; struct client_ctx_q Clientq; int Doshape, Shape_ev; +int HasXinerama, HasRandr, Randr_ev; int Starting; struct conf Conf; @@ -121,6 +122,8 @@ dpy_init(const char *dpyname) Doshape = XShapeQueryExtension(X_Dpy, &Shape_ev, &i); + HasRandr = XRRQueryExtension(X_Dpy, &Randr_ev, &i); + TAILQ_INIT(&Screenq); } @@ -133,7 +136,7 @@ x_setup(void) Nscreens = ScreenCount(X_Dpy); for (i = 0; i < (int)Nscreens; i++) { - XMALLOC(sc, struct screen_ctx); + XCALLOC(sc, struct screen_ctx); x_setupscreen(sc, i); TAILQ_INSERT_TAIL(&Screenq, sc, entry); } @@ -161,6 +164,7 @@ x_setupscreen(struct screen_ctx *sc, u_int which) Window *wins, w0, w1; XWindowAttributes winattr; XSetWindowAttributes rootattr; + int fake; u_int nwins, i; Curscreen = sc; @@ -246,6 +250,18 @@ x_setupscreen(struct screen_ctx *sc, u_int which) XChangeWindowAttributes(X_Dpy, sc->rootwin, CWEventMask, &rootattr); + if (XineramaQueryExtension(X_Dpy, &fake, &fake) == 1 && + ((HasXinerama = XineramaIsActive(X_Dpy)) == 1)) + HasXinerama = 1; + if (HasRandr) + XRRSelectInput(X_Dpy, sc->rootwin, RRScreenChangeNotifyMask); + /* + * initial setup of xinerama screens, if we're using RandR then we'll + * redo this whenever the screen changes since a CTRC may have been + * added or removed + */ + screen_init_xinerama(sc); + XSync(X_Dpy, False); return; diff --git a/calmwm.h b/calmwm.h index c20e66d..be26b57 100644 --- a/calmwm.h +++ b/calmwm.h @@ -62,6 +62,9 @@ struct screen_ctx { XftDraw *xftdraw; XftColor xftcolor; + + int xinerama_no; + XineramaScreenInfo *xinerama; }; TAILQ_HEAD(screen_ctx_q, screen_ctx); @@ -369,6 +372,7 @@ void xev_handle_keyrelease(struct xevent *, XEvent *); void xev_handle_expose(struct xevent *, XEvent *); void xev_handle_clientmessage(struct xevent *, XEvent *); void xev_handle_shape(struct xevent *, XEvent *); +void xev_handle_randr(struct xevent *, XEvent *); void xev_handle_mapping(struct xevent *, XEvent *); #define XEV_QUICK(a, b, c, d, e) do { \ @@ -417,6 +421,7 @@ char *xstrdup(const char *); struct screen_ctx *screen_fromroot(Window); struct screen_ctx *screen_current(void); void screen_updatestackingorder(void); +void screen_init_xinerama(struct screen_ctx *); void conf_setup(struct conf *, const char *); void conf_client(struct client_ctx *); @@ -512,6 +517,8 @@ extern u_int Nscreens; extern struct client_ctx_q Clientq; extern int Doshape, Shape_ev; +extern int Doshape, Shape_ev; +extern int HasXinerama, HasRandr, Randr_ev; extern struct conf Conf; #endif /* _CALMWM_H_ */ diff --git a/client.c b/client.c index 72ccb1e..6e2ef24 100644 --- a/client.c +++ b/client.c @@ -22,6 +22,8 @@ #include "calmwm.h" int _inwindowbounds(struct client_ctx *, int, int); +XineramaScreenInfo *client_find_xinerama_screen(int , int , + struct screen_ctx *); static char emptystring[] = ""; @@ -322,16 +324,36 @@ void client_maximize(struct client_ctx *cc) { struct screen_ctx *sc = CCTOSC(cc); + int xmax = sc->xmax, ymax = sc->ymax; + int x_org = 0, y_org = 0; if (cc->flags & CLIENT_MAXIMIZED) { cc->geom = cc->savegeom; } else { if (!(cc->flags & CLIENT_VMAXIMIZED)) cc->savegeom = cc->geom; - cc->geom.x = Conf.gap_left; - cc->geom.y = Conf.gap_top; - cc->geom.height = sc->ymax - (Conf.gap_top + Conf.gap_bottom); - cc->geom.width = sc->xmax - (Conf.gap_left + Conf.gap_right); + if (HasXinerama) { + XineramaScreenInfo *xine; + /* + * pick screen that the middle of the window is on. + * that's probably more fair than if just the origin of + * a window is poking over a boundary + */ + xine = client_find_xinerama_screen(cc->geom.x + + cc->geom.width / 2, cc->geom.y + + cc->geom.height / 2, CCTOSC(cc)); + if (xine == NULL) + goto calc; + x_org = xine->x_org; + y_org = xine->y_org; + xmax = xine->width; + ymax = xine->height; + } +calc: + cc->geom.x = x_org + Conf.gap_left; + cc->geom.y = y_org + Conf.gap_top; + cc->geom.height = ymax - (Conf.gap_top + Conf.gap_bottom); + cc->geom.width = xmax - (Conf.gap_left + Conf.gap_right); cc->flags |= CLIENT_DOMAXIMIZE; } @@ -342,15 +364,27 @@ void client_vertmaximize(struct client_ctx *cc) { struct screen_ctx *sc = CCTOSC(cc); + int y_org = 0, ymax = sc->ymax; if (cc->flags & CLIENT_VMAXIMIZED) { cc->geom = cc->savegeom; } else { if (!(cc->flags & CLIENT_MAXIMIZED)) cc->savegeom = cc->geom; - cc->geom.y = cc->bwidth + Conf.gap_top; - cc->geom.height = (sc->ymax - cc->bwidth * 2) - - (Conf.gap_top + Conf.gap_bottom); + if (HasXinerama) { + XineramaScreenInfo *xine; + xine = client_find_xinerama_screen(cc->geom.x + + cc->geom.width / 2, cc->geom.y + + cc->geom.height / 2, CCTOSC(cc)); + if (xine == NULL) + goto calc; + y_org = xine->y_org; + ymax = xine->height; + } +calc: + cc->geom.y = y_org + cc->bwidth + Conf.gap_top; + cc->geom.height = ymax - (cc->bwidth * 2) - (Conf.gap_top + + Conf.gap_bottom); cc->flags |= CLIENT_DOVMAXIMIZE; } @@ -667,20 +701,18 @@ void client_placecalc(struct client_ctx *cc) { struct screen_ctx *sc = CCTOSC(cc); - int yslack, xslack, xmouse, ymouse; - - yslack = sc->ymax - cc->geom.height - cc->bwidth; - xslack = sc->xmax - cc->geom.width - cc->bwidth; - - xu_ptr_getpos(sc->rootwin, &xmouse, &ymouse); - - xmouse = MAX(xmouse, cc->bwidth) - cc->geom.width / 2; - ymouse = MAX(ymouse, cc->bwidth) - cc->geom.height / 2; - - xmouse = MAX(xmouse, (int)cc->bwidth); - ymouse = MAX(ymouse, (int)cc->bwidth); + int yslack, xslack; if (cc->size->flags & USPosition) { + /* + * Ignore XINERAMA screens, just make sure it's somewhere + * in the virtual desktop. else it stops people putting xterms + * at startup in the screen the mouse doesn't start in *sigh*. + * XRandR bits mean that {x,y}max shouldn't be outside what's + * currently there. + */ + yslack = sc->ymax - cc->geom.height - cc->bwidth; + xslack = sc->xmax - cc->geom.width - cc->bwidth; if (cc->size->x >= 0) cc->geom.x = MAX(MIN(cc->size->x, xslack), cc->bwidth); else @@ -690,23 +722,50 @@ client_placecalc(struct client_ctx *cc) else cc->geom.y = cc->bwidth; } else { - if (xslack >= 0) { + XineramaScreenInfo *info; + int xmouse, ymouse, xorig, yorig; + int xmax, ymax; + + xu_ptr_getpos(sc->rootwin, &xmouse, &ymouse); + if (HasXinerama) { + info = client_find_xinerama_screen(xmouse, ymouse, sc); + if (info == NULL) + goto noxine; + xorig = info->x_org; + yorig = info->y_org; + xmax = xorig + info->width; + ymax = yorig + info->height; + } else { +noxine: + xorig = yorig = 0; + xmax = sc->xmax; + ymax = sc->ymax; + } + xmouse = MAX(xmouse, xorig + cc->bwidth) - cc->geom.width / 2; + ymouse = MAX(ymouse, yorig + cc->bwidth) - cc->geom.height / 2; + + xmouse = MAX(xmouse, xorig + (int)cc->bwidth); + ymouse = MAX(ymouse, yorig + (int)cc->bwidth); + + xslack = xmax - cc->geom.width - cc->bwidth; + yslack = ymax - cc->geom.height - cc->bwidth; + if (xslack >= xorig) { cc->geom.x = MAX(MIN(xmouse, xslack), - Conf.gap_left + cc->bwidth); + xorig + Conf.gap_left + cc->bwidth); if (cc->geom.x > (xslack - Conf.gap_right)) cc->geom.x -= Conf.gap_right; } else { - cc->geom.x = cc->bwidth + Conf.gap_left; - cc->geom.width = sc->xmax - Conf.gap_left; + cc->geom.x = xorig + cc->bwidth + Conf.gap_left; + cc->geom.width = xmax - Conf.gap_left; } - if (yslack >= 0) { + if (yslack >= yorig) { cc->geom.y = MAX(MIN(ymouse, yslack), - Conf.gap_top + cc->bwidth); + yorig + Conf.gap_top + cc->bwidth); if (cc->geom.y > (yslack - Conf.gap_bottom)) cc->geom.y -= Conf.gap_bottom; } else { - cc->geom.y = cc->bwidth + Conf.gap_top; - cc->geom.height = sc->ymax - Conf.gap_top; + cc->geom.y = yorig + cc->bwidth + Conf.gap_top; + cc->geom.height = ymax - Conf.gap_top; } } } @@ -793,3 +852,21 @@ _inwindowbounds(struct client_ctx *cc, int x, int y) return (x < cc->geom.width && x >= 0 && y < cc->geom.height && y >= 0); } + +/* + * Find which xinerama screen the coordinates (x,y) is on. + */ +XineramaScreenInfo * +client_find_xinerama_screen(int x, int y, struct screen_ctx *sc) +{ + XineramaScreenInfo *info; + int i; + + for (i = 0; i < sc->xinerama_no; i++) { + info = &sc->xinerama[i]; + if (x > info->x_org && x < info->x_org + info->width && + y > info->y_org && y < info->y_org + info->height) + return (info); + } + return (NULL); +} diff --git a/headers.h b/headers.h index 2d0f1cc..b973475 100644 --- a/headers.h +++ b/headers.h @@ -41,6 +41,8 @@ #include #include +#include +#include #include #include #include diff --git a/screen.c b/screen.c index c03003d..70ee0db 100644 --- a/screen.c +++ b/screen.c @@ -67,3 +67,28 @@ screen_updatestackingorder(void) XFree(wins); } + +void +screen_init_xinerama(struct screen_ctx *sc) +{ + XineramaScreenInfo *info; + int no, fake; + + if (HasXinerama == 0 || XineramaIsActive(X_Dpy) == 0) { + HasXinerama = 0; + sc->xinerama_no = 0; + } + + info = XineramaQueryScreens(X_Dpy, &no); + if (info == NULL) { + /*is xinerama is actually off, instead of a malloc failure? */ + if (sc->xinerama == NULL) + HasXinerama = NULL; + return; + } + + if (sc->xinerama != NULL) + XFree(sc->xinerama); + sc->xinerama = info; + sc->xinerama_no = no; +} diff --git a/xevents.c b/xevents.c index 274af43..dd8de77 100644 --- a/xevents.c +++ b/xevents.c @@ -375,6 +375,22 @@ xev_handle_shape(struct xevent *xev, XEvent *ee) 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 @@ -525,6 +541,8 @@ xev_loop(void) default: if (e.type == Shape_ev) xev_handle_shape(xev, &e); + else if (e.type == Randr_ev) + xev_handle_randr(xev, &e); break; }