+void
+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",
+ s->idx, w, h, x, y);
+
+ /* remove any conflicting regions */
+ n = TAILQ_FIRST(&s->rl);
+ while (n) {
+ r = n;
+ n = TAILQ_NEXT(r, entry);
+ if (X(r) < (x + w) &&
+ (X(r) + WIDTH(r)) > x &&
+ Y(r) < (y + h) &&
+ (Y(r) + HEIGHT(r)) > y) {
+ XDestroyWindow(display, r->bar_window);
+ TAILQ_REMOVE(&s->rl, r, entry);
+ TAILQ_INSERT_TAIL(&s->orl, r, entry);
+ }
+ }
+
+ /* 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 workspaces\n");
+
+ X(r) = x;
+ Y(r) = y;
+ WIDTH(r) = w;
+ HEIGHT(r) = h;
+ r->s = s;
+ r->ws = ws;
+ ws->r = r;
+ TAILQ_INSERT_TAIL(&s->rl, r, entry);
+}
+
+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);
+
+ /* add bars to all regions */
+ for (i = 0; i < ScreenCount(display); i++)
+ TAILQ_FOREACH(r, &screens[i].rl, entry)
+ bar_setup(r);
+ stack();
+}
+
+void
+setup_screens(void)
+{
+ Window d1, d2, *wins = NULL;
+ XWindowAttributes wa;
+ unsigned int no;
+ int i, j, k;
+ int errorbase, major, minor;
+ struct workspace *ws;
+ int ws_idx_atom;
+
+
+ if ((screens = calloc(ScreenCount(display),
+ sizeof(struct swm_screen))) == NULL)
+ 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("red", i + 1, SWM_S_COLOR_FOCUS);
+ setscreencolor("rgb:88/88/88", i + 1, SWM_S_COLOR_UNFOCUS);
+ setscreencolor("rgb:00/80/80", i + 1, SWM_S_COLOR_BAR_BORDER);
+ setscreencolor("black", i + 1, SWM_S_COLOR_BAR);
+ 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;
+ ws->restack = 1;
+ ws->focus = NULL;
+ ws->r = NULL;
+ TAILQ_INIT(&ws->winlist);
+
+ for (k = 0; layouts[k].l_stack != NULL; k++)
+ if (layouts[k].l_config != NULL)
+ layouts[k].l_config(ws,
+ SWM_ARG_ID_STACKINIT);
+ ws->cur_layout = &layouts[0];
+ }
+ /* grab existing windows (before we build the bars)*/
+ if (!XQueryTree(display, screens[i].root, &d1, &d2, &wins, &no))
+ continue;
+
+ scan_xrandr(i);
+
+ if (xrandr_support)
+ XRRSelectInput(display, screens[i].root,
+ RRScreenChangeNotifyMask);
+
+ /* attach windows to a region */
+ /* normal windows */
+ for (j = 0; j < no; j++) {
+ XGetWindowAttributes(display, wins[j], &wa);
+ if (!XGetWindowAttributes(display, wins[j], &wa) ||
+ wa.override_redirect ||
+ XGetTransientForHint(display, wins[j], &d1))
+ continue;
+
+ if (wa.map_state == IsViewable ||
+ getstate(wins[j]) == NormalState)
+ manage_window(wins[j]);
+ }
+ /* transient windows */
+ for (j = 0; j < no; j++) {
+ if (!XGetWindowAttributes(display, wins[j], &wa))
+ continue;
+
+ if (XGetTransientForHint(display, wins[j], &d1) &&
+ (wa.map_state == IsViewable || getstate(wins[j]) ==
+ NormalState))
+ manage_window(wins[j]);
+ }
+ if (wins) {
+ XFree(wins);
+ wins = NULL;
+ }
+ }
+}
+