#define BUTTONMASK (ButtonPressMask|ButtonReleaseMask)
#define MOUSEMASK (BUTTONMASK|PointerMotionMask)
#define SWM_PROPLEN (16)
-#define X(r) (r)->g.x
+#define X(r) (r)->g.x
#define Y(r) (r)->g.y
-#define WIDTH(r) (r)->g.w
+#define WIDTH(r) (r)->g.w
#define HEIGHT(r) (r)->g.h
#ifndef SWM_LIB
int other_wm;
int running = 1;
int ss_enabled = 0;
+int xrandr_support;
int xrandr_eventbase;
int ignore_enter = 0;
unsigned int numlockmask = 0;
struct {
int horizontal_msize;
int horizontal_mwin;
+ int horizontal_stacks;
int vertical_msize;
int vertical_mwin;
+ int vertical_stacks;
} l_state;
};
struct swm_screen {
int idx; /* screen index */
struct swm_region_list rl; /* list of regions on this screen */
+ struct swm_region_list orl; /* list of old regions */
Window root;
- int xrandr_support;
struct workspace ws[SWM_WS_MAX];
/* colors */
#define SWM_ARG_ID_CYCLEWS_DOWN (13)
#define SWM_ARG_ID_CYCLESC_UP (14)
#define SWM_ARG_ID_CYCLESC_DOWN (15)
+#define SWM_ARG_ID_COLINC (16)
+#define SWM_ARG_ID_COLDEC (17)
+#define SWM_ARG_ID_ROWINC (16)
+#define SWM_ARG_ID_ROWDEL (17)
#define SWM_ARG_ID_SS_ALL (0)
#define SWM_ARG_ID_SS_WINDOW (1)
#define SWM_ARG_ID_DONTCENTER (0)
i, ScreenCount(display));
}
-void new_region(struct swm_screen *, struct workspace *,
- int, int, int, int);
+void new_region(struct swm_screen *, int, int, int, int);
void
custom_region(char *val)
"(%ux%u)\n", w, h, x, y,
DisplayWidth(display, sidx), DisplayHeight(display, sidx));
- new_region(&screens[sidx], NULL, x, y, w, h);
+ new_region(&screens[sidx], x, y, w, h);
}
int
struct swm_region *
root_to_region(Window root)
{
- struct swm_region *r;
+ struct swm_region *r = NULL;
Window rr, cr;
int i, x, y, wx, wy;
unsigned int mask;
break;
if (XQueryPointer(display, screens[i].root,
- &rr, &cr, &x, &y, &wx, &wy, &mask) == False) {
- /* if we can't query the pointer, grab the first region */
- r = TAILQ_FIRST(&screens[i].rl);
- } else {
- /* otherwise, choose a region based on pointer location */
- TAILQ_FOREACH(r, &screens[i].rl, entry) {
+ &rr, &cr, &x, &y, &wx, &wy, &mask) != False) {
+ /* choose a region based on pointer location */
+ TAILQ_FOREACH(r, &screens[i].rl, entry)
if (x >= X(r) && x <= X(r) + WIDTH(r) &&
y >= Y(r) && y <= Y(r) + HEIGHT(r))
break;
- }
-
- if (r == NULL)
- r = TAILQ_FIRST(&screens[i].rl);
}
+
+ if (r == NULL)
+ r = TAILQ_FIRST(&screens[i].rl);
+
return (r);
}
if (!other_r) {
/* if the other workspace is hidden, switch windows */
/* map new window first to prevent ugly blinking */
+ old_ws->r = NULL;
+ old_ws->restack = 1;
+
TAILQ_FOREACH(win, &new_ws->winlist, entry)
XMapRaised(display, win->id);
TAILQ_FOREACH(win, &old_ws->winlist, entry)
XUnmapWindow(display, win->id);
- old_ws->r = NULL;
- old_ws->restack = 1;
} else {
other_r->ws = old_ws;
old_ws->r = other_r;
XWindowChanges wc;
struct swm_geometry win_g, r_g = *g;
struct ws_win *win, *winfocus;
- int i, j, w_inc, h_inc, w_base, h_base;
+ int i, j, s, w_inc, h_inc, w_base, h_base, stacks;
int hrh, extra, h_slice, last_h = 0;
int split, colno, winno, mwin, msize, mscale;
int remain, missing, v_slice;;
w_base = win->sh.base_width;
mwin = ws->l_state.horizontal_mwin;
mscale = ws->l_state.horizontal_msize;
+ stacks = ws->l_state.horizontal_stacks;
SWAPXY(&r_g);
} else {
w_inc = win->sh.height_inc;
w_base = win->sh.base_height;
mwin = ws->l_state.vertical_mwin;
mscale = ws->l_state.vertical_msize;
+ stacks = ws->l_state.vertical_stacks;
}
win_g = r_g;
+ if (stacks > winno - mwin)
+ stacks = winno - mwin;
+
h_slice = r_g.h / SWM_H_SLICE;
if (mwin && winno > mwin) {
v_slice = r_g.w / SWM_V_SLICE;
split = mwin;
colno = split;
- msize = v_slice * mscale;
+ win_g.w = v_slice * mscale;
if (w_inc > 1 && w_inc < v_slice) {
/* adjust for window's requested size increment */
}
}
- win_g.w = msize;
+ msize = win_g.w;
if (flip)
win_g.x += r_g.w - msize;
} else {
- colno = winno;
- split = 0;
+ if (stacks > 1) {
+ colno = split = (winno - mwin) / stacks;
+ } else {
+ split = 0;
+ colno = winno;
+ }
}
hrh = r_g.h / colno;
extra = r_g.h - (colno * hrh);
win_g.h = hrh - 2;
/* stack all the tiled windows */
- i = j = 0;
+ i = j = 0, s = stacks;
TAILQ_FOREACH(win, &ws->winlist, entry) {
if (win->transient != 0 || win->floating != 0)
continue;
if (split && i == split) {
- colno = winno - split;
+ colno = (winno - split) / s;
+ if (stacks == 1)
+ colno += (winno - split) % s;
+ split = split + colno;
hrh = (r_g.h / colno);
extra = r_g.h - (colno * hrh);
if (flip)
win_g.x = r_g.x;
else
- win_g.x += msize + 2;
- win_g.w = r_g.w - (msize + 2);
+ win_g.x += win_g.w + 2;
+ win_g.w = (r_g.w - (msize + 2) - (stacks * 2)) / stacks;
+ s--;
j = 0;
}
win_g.h = hrh - 2;
h_inc = win->sh.width_inc;
h_base = win->sh.base_width;
} else {
- h_inc = win->sh.height_inc;
+ h_inc = win->sh.height_inc;
h_base = win->sh.base_height;
}
if (j == colno - 1) {
case SWM_ARG_ID_STACKINIT:
ws->l_state.vertical_msize = SWM_V_SLICE / 2;
ws->l_state.vertical_mwin = 1;
+ ws->l_state.vertical_stacks = 1;
break;
case SWM_ARG_ID_MASTERSHRINK:
if (ws->l_state.vertical_msize > 1)
case SWM_ARG_ID_MASTERDEL:
if (ws->l_state.vertical_mwin > 0)
ws->l_state.vertical_mwin--;
+ case SWM_ARG_ID_COLINC:
+ ws->l_state.vertical_stacks++;
+ break;
+ case SWM_ARG_ID_COLDEC:
+ if (ws->l_state.vertical_stacks > 1)
+ ws->l_state.vertical_stacks--;
break;
default:
return;
case SWM_ARG_ID_STACKINIT:
ws->l_state.horizontal_mwin = 1;
ws->l_state.horizontal_msize = SWM_H_SLICE / 2;
+ ws->l_state.horizontal_stacks = 1;
break;
case SWM_ARG_ID_MASTERSHRINK:
if (ws->l_state.horizontal_msize > 1)
if (ws->l_state.horizontal_mwin > 0)
ws->l_state.horizontal_mwin--;
break;
+ case SWM_ARG_ID_COLINC:
+ ws->l_state.horizontal_stacks++;
+ break;
+ case SWM_ARG_ID_COLDEC:
+ if (ws->l_state.horizontal_stacks > 1)
+ ws->l_state.horizontal_stacks--;
default:
return;
}
{ MODKEY, XK_l, stack_config, {.id = SWM_ARG_ID_MASTERGROW} },
{ MODKEY, XK_comma, stack_config, {.id = SWM_ARG_ID_MASTERADD} },
{ MODKEY, XK_period, stack_config, {.id = SWM_ARG_ID_MASTERDEL} },
+ { MODKEY | ShiftMask, XK_comma, stack_config, {.id = SWM_ARG_ID_COLINC} },
+ { MODKEY | ShiftMask, XK_period, stack_config, {.id = SWM_ARG_ID_COLDEC} },
{ MODKEY, XK_Return, swapwin, {.id = SWM_ARG_ID_SWAPMAIN} },
{ MODKEY, XK_j, focus, {.id = SWM_ARG_ID_FOCUSNEXT} },
{ MODKEY, XK_k, focus, {.id = SWM_ARG_ID_FOCUSPREV} },
DNPRINTF(SWM_D_MISC, "manage_window: win %u transient %u\n",
(unsigned)win->id, win->transient);
}
-
+
/*
* Figure out where to put the window. If it was previously assigned to
* a workspace (either by spawn() or manually moving), and isn't
DNPRINTF(SWM_D_MISC, "unmanage_window: %lu\n", win->id);
+ /* don't unmanage if we are switching workspaces */
ws = win->ws;
+ if (ws->restack)
+ return;
+
/* find a window to focus */
if (ws->focus == win)
ws->focus = TAILQ_PREV(win, ws_win_list, entry);
}
void
-remove_region(struct swm_region *r)
-{
- struct swm_screen *s = r->s;
- struct ws_win *win;
-
- DNPRINTF(SWM_D_MISC, "removing region: screen[%d]:%dx%d+%d+%d\n",
- s->idx, WIDTH(r), HEIGHT(r), X(r), Y(r));
-
- TAILQ_FOREACH(win, &r->ws->winlist, entry)
- XUnmapWindow(display, win->id);
- r->ws->r = NULL;
-
- XDestroyWindow(display, r->bar_window);
- TAILQ_REMOVE(&s->rl, r, entry);
- free(r);
-}
-
-void
-new_region(struct swm_screen *s, struct workspace *ws,
- int x, int y, int w, int h)
+new_region(struct swm_screen *s, int x, int y, int w, int h)
{
struct swm_region *r, *n;
+ struct workspace *ws = NULL;
int i;
DNPRINTF(SWM_D_MISC, "new region: screen[%d]:%dx%d+%d+%d\n",
(X(r) + WIDTH(r)) > x &&
Y(r) < (y + h) &&
(Y(r) + HEIGHT(r)) > y) {
- if (ws == NULL)
- ws = r->ws;
- remove_region(r);
+ XDestroyWindow(display, r->bar_window);
+ TAILQ_REMOVE(&s->rl, r, entry);
+ TAILQ_INSERT_TAIL(&s->orl, r, entry);
}
}
- /* pick an appropriate workspace */
+ /* search old regions for one to reuse */
+
+ /* size + location match */
+ TAILQ_FOREACH(r, &s->orl, entry)
+ if (X(r) == x && Y(r) == y &&
+ HEIGHT(r) == h && WIDTH(r) == w)
+ break;
+
+ /* size match */
+ TAILQ_FOREACH(r, &s->orl, entry)
+ if (HEIGHT(r) == h && WIDTH(r) == w)
+ break;
+
+ if (r != NULL) {
+ TAILQ_REMOVE(&s->orl, r, entry);
+ /* try to use old region's workspace */
+ if (r->ws->r == NULL)
+ ws = r->ws;
+ } else
+ if ((r = calloc(1, sizeof(struct swm_region))) == NULL)
+ errx(1, "calloc: failed to allocate memory for screen");
+
+ /* if we don't have a workspace already, find one */
if (ws == NULL) {
for (i = 0; i < SWM_WS_MAX; i++)
if (s->ws[i].r == NULL) {
ws = &s->ws[i];
break;
}
-
- if (ws == NULL)
- errx(1, "no free regions\n");
}
- if ((r = calloc(1, sizeof(struct swm_region))) == NULL)
- errx(1, "calloc: failed to allocate memory for screen");
+ if (ws == NULL)
+ errx(1, "no free workspaces\n");
X(r) = x;
Y(r) = y;
}
void
-setup_screens(void)
+scan_xrandr(int i)
{
#ifdef SWM_XRR_HAS_CRTC
XRRCrtcInfo *ci;
XRRScreenResources *sr;
int c;
+ int ncrtc = 0;
#endif /* SWM_XRR_HAS_CRTC */
+ struct swm_region *r;
+
+
+ if (i >= ScreenCount(display))
+ errx(1, "invalid screen");
+
+ /* remove any old regions */
+ while ((r = TAILQ_FIRST(&screens[i].rl)) != NULL) {
+ r->ws->r = NULL;
+ XDestroyWindow(display, r->bar_window);
+ TAILQ_REMOVE(&screens[i].rl, r, entry);
+ TAILQ_INSERT_TAIL(&screens[i].orl, r, entry);
+ }
+
+ /* map virtual screens onto physical screens */
+#ifdef SWM_XRR_HAS_CRTC
+ if (xrandr_support) {
+ sr = XRRGetScreenResources(display, screens[i].root);
+ if (sr == NULL)
+ new_region(&screens[i], 0, 0,
+ DisplayWidth(display, i),
+ DisplayHeight(display, i));
+ else
+ ncrtc = sr->ncrtc;
+
+ for (c = 0, ci = NULL; c < ncrtc; c++) {
+ ci = XRRGetCrtcInfo(display, sr, sr->crtcs[c]);
+ if (ci->noutput == 0)
+ continue;
+
+ if (ci != NULL && ci->mode == None)
+ new_region(&screens[i], 0, 0,
+ DisplayWidth(display, i),
+ DisplayHeight(display, i));
+ else
+ new_region(&screens[i],
+ ci->x, ci->y, ci->width, ci->height);
+ }
+ if (ci)
+ XRRFreeCrtcInfo(ci);
+ XRRFreeScreenResources(sr);
+ } else
+#endif /* SWM_XRR_HAS_CRTC */
+ {
+ new_region(&screens[i], 0, 0, DisplayWidth(display, i),
+ DisplayHeight(display, i));
+ }
+}
+
+void
+screenchange(XEvent *e) {
+ XRRScreenChangeNotifyEvent *xe = (XRRScreenChangeNotifyEvent *)e;
+ struct swm_region *r;
+ struct ws_win *win;
+ int i;
+
+ DNPRINTF(SWM_D_EVENT, "screenchange: %lu\n", xe->root);
+
+ if (!XRRUpdateConfiguration(e))
+ return;
+
+ /* silly event doesn't include the screen index */
+ for (i = 0; i < ScreenCount(display); i++)
+ if (screens[i].root == xe->root)
+ break;
+ if (i >= ScreenCount(display))
+ errx(1, "screenchange: screen not found\n");
+
+ /* brute force for now, just re-enumerate the regions */
+ scan_xrandr(i);
+
+ /* hide any windows that went away */
+ TAILQ_FOREACH(r, &screens[i].rl, entry)
+ TAILQ_FOREACH(win, &r->ws->winlist, entry)
+ XUnmapWindow(display, win->id);
+ stack();
+}
+
+void
+setup_screens(void)
+{
Window d1, d2, *wins = NULL;
XWindowAttributes wa;
- struct swm_region *r;
unsigned int no;
- int errorbase, major, minor;
- int ncrtc = 0, w = 0;
int i, j, k;
+ int errorbase, major, minor;
struct workspace *ws;
int ws_idx_atom;
errx(1, "calloc: screens");
ws_idx_atom = XInternAtom(display, "_SWM_WS", False);
-
+
+ /* initial Xrandr setup */
+ xrandr_support = XRRQueryExtension(display,
+ &xrandr_eventbase, &errorbase);
+ if (xrandr_support)
+ if (XRRQueryVersion(display, &major, &minor) && major < 1)
+ xrandr_support = 0;
/* map physical screens */
for (i = 0; i < ScreenCount(display); i++) {
DNPRINTF(SWM_D_WS, "setup_screens: init screen %d\n", i);
screens[i].idx = i;
TAILQ_INIT(&screens[i].rl);
+ TAILQ_INIT(&screens[i].orl);
screens[i].root = RootWindow(display, i);
/* set default colors */
setscreencolor("rgb:a0/a0/a0", i + 1, SWM_S_COLOR_BAR_FONT);
/* init all workspaces */
+ /* XXX these should be dynamically allocated too */
for (j = 0; j < SWM_WS_MAX; j++) {
ws = &screens[i].ws[j];
ws->idx = j;
SWM_ARG_ID_STACKINIT);
ws->cur_layout = &layouts[0];
}
-
- /* map virtual screens onto physical screens */
- screens[i].xrandr_support = XRRQueryExtension(display,
- &xrandr_eventbase, &errorbase);
- if (screens[i].xrandr_support)
- if (XRRQueryVersion(display, &major, &minor) &&
- major < 1)
- screens[i].xrandr_support = 0;
-
-#if 0 /* not ready for dynamic screen changes */
- if (screens[i].xrandr_support)
- XRRSelectInput(display,
- screens[r->s].root,
- RRScreenChangeNotifyMask);
-#endif
-
/* grab existing windows (before we build the bars)*/
if (!XQueryTree(display, screens[i].root, &d1, &d2, &wins, &no))
continue;
-#ifdef SWM_XRR_HAS_CRTC
- sr = XRRGetScreenResources(display, screens[i].root);
- if (sr == NULL)
- new_region(&screens[i], &screens[i].ws[w],
- 0, 0, DisplayWidth(display, i),
- DisplayHeight(display, i));
- else
- ncrtc = sr->ncrtc;
-
- for (c = 0, ci = NULL; c < ncrtc; c++) {
- ci = XRRGetCrtcInfo(display, sr, sr->crtcs[c]);
- if (ci->noutput == 0)
- continue;
+ scan_xrandr(i);
- if (ci != NULL && ci->mode == None)
- new_region(&screens[i], &screens[i].ws[w], 0, 0,
- DisplayWidth(display, i),
- DisplayHeight(display, i));
- else
- new_region(&screens[i], &screens[i].ws[w],
- ci->x, ci->y, ci->width, ci->height);
- w++;
- }
- if (ci)
- XRRFreeCrtcInfo(ci);
- XRRFreeScreenResources(sr);
-#else
- new_region(&screens[i], &screens[i].ws[w], 0, 0,
- DisplayWidth(display, i),
- DisplayHeight(display, i));
-#endif /* SWM_XRR_HAS_CRTC */
+ if (xrandr_support)
+ XRRSelectInput(display, screens[i].root,
+ RRScreenChangeNotifyMask);
/* attach windows to a region */
/* normal windows */
- if ((r = TAILQ_FIRST(&screens[i].rl)) == NULL)
- errx(1, "no regions on screen %d", i);
-
- for (i = 0; i < no; i++) {
- XGetWindowAttributes(display, wins[i], &wa);
- if (!XGetWindowAttributes(display, wins[i], &wa) ||
+ for (j = 0; j < no; j++) {
+ XGetWindowAttributes(display, wins[j], &wa);
+ if (!XGetWindowAttributes(display, wins[j], &wa) ||
wa.override_redirect ||
- XGetTransientForHint(display, wins[i], &d1))
+ XGetTransientForHint(display, wins[j], &d1))
continue;
if (wa.map_state == IsViewable ||
- getstate(wins[i]) == NormalState)
- manage_window(wins[i]);
+ getstate(wins[j]) == NormalState)
+ manage_window(wins[j]);
}
/* transient windows */
- for (i = 0; i < no; i++) {
- if (!XGetWindowAttributes(display, wins[i], &wa))
+ for (j = 0; j < no; j++) {
+ if (!XGetWindowAttributes(display, wins[j], &wa))
continue;
- if (XGetTransientForHint(display, wins[i], &d1) &&
- (wa.map_state == IsViewable || getstate(wins[i]) ==
+ if (XGetTransientForHint(display, wins[j], &d1) &&
+ (wa.map_state == IsViewable || getstate(wins[j]) ==
NormalState))
- manage_window(wins[i]);
+ manage_window(wins[j]);
}
if (wins) {
XFree(wins);
bar_alarm = 0;
bar_update();
}
- while(XPending(display)) {
+ while (XPending(display)) {
XNextEvent(display, &e);
- if (handler[e.type])
- handler[e.type](&e);
+ if (e.type < LASTEvent) {
+ if (handler[e.type])
+ handler[e.type](&e);
+ else
+ DNPRINTF(SWM_D_EVENT,
+ "unkown event: %d\n", e.type);
+ } else {
+ switch (e.type - xrandr_eventbase) {
+ case RRScreenChangeNotify:
+ screenchange(&e);
+ break;
+ default:
+ DNPRINTF(SWM_D_EVENT,
+ "unkown event: %d\n", e.type);
+ break;
+ }
+ }
}
}