summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVito Caputo <vcaputo@gnugeneration.com>2016-08-28 00:36:53 -0700
committerVito Caputo <vcaputo@gnugeneration.com>2016-09-09 14:14:31 -0700
commit4642216f70dd98134a79f9299b7ca4bc876649c7 (patch)
treebdf9fd892bc54a2f2a678a9828c6af9d9fc8bed2
parente99f5ac1293a0ae1f498bc4c73c4c04e4edb8665 (diff)
*: refactor all the things
Long overdue house cleaning. The addition of compositing/monitoring overlays in vwm3 pushed vwm well past what is a reasonable size for a simple thousand line file. This is a first step towards restoring sanity in the code, but no behavioral differences are intended, this is mostly just shuffling around and organizing code. I expect some performance regressions initially, follow-on commits will make more improvements to that end as the dust settles.
-rw-r--r--.gitignore1
-rw-r--r--src/clickety.c244
-rw-r--r--src/clickety.h12
-rw-r--r--src/colors.def (renamed from colors.def)0
-rw-r--r--src/composite.c331
-rw-r--r--src/composite.h24
-rw-r--r--src/context.c97
-rw-r--r--src/context.h14
-rw-r--r--src/desktop.c158
-rw-r--r--src/desktop.h25
-rw-r--r--src/key.c371
-rw-r--r--src/key.h11
-rw-r--r--src/launch.c45
-rw-r--r--src/launch.h13
-rw-r--r--src/launchers.def (renamed from launchers.def)0
-rw-r--r--src/libvmon/LICENSE (renamed from libvmon/LICENSE)0
-rw-r--r--src/libvmon/bitmap.h (renamed from libvmon/bitmap.h)0
-rw-r--r--src/libvmon/defs/_begin.def (renamed from libvmon/defs/_begin.def)0
-rw-r--r--src/libvmon/defs/_end.def (renamed from libvmon/defs/_end.def)0
-rw-r--r--src/libvmon/defs/proc_files.def (renamed from libvmon/defs/proc_files.def)0
-rw-r--r--src/libvmon/defs/proc_io.def (renamed from libvmon/defs/proc_io.def)0
-rw-r--r--src/libvmon/defs/proc_stat.def (renamed from libvmon/defs/proc_stat.def)0
-rw-r--r--src/libvmon/defs/proc_vm.def (renamed from libvmon/defs/proc_vm.def)0
-rw-r--r--src/libvmon/defs/proc_wants.def (renamed from libvmon/defs/proc_wants.def)0
-rw-r--r--src/libvmon/defs/sys_stat.def (renamed from libvmon/defs/sys_stat.def)0
-rw-r--r--src/libvmon/defs/sys_vm.def (renamed from libvmon/defs/sys_vm.def)0
-rw-r--r--src/libvmon/defs/sys_wants.def (renamed from libvmon/defs/sys_wants.def)0
-rw-r--r--src/libvmon/vmon.c (renamed from libvmon/vmon.c)0
-rw-r--r--src/libvmon/vmon.h (renamed from libvmon/vmon.h)0
-rw-r--r--src/list.h (renamed from list.h)0
-rw-r--r--src/logo.c66
-rw-r--r--src/logo.h8
-rw-r--r--src/overlay.c960
-rw-r--r--src/overlay.h36
-rw-r--r--src/screen.c152
-rw-r--r--src/screen.h19
-rw-r--r--src/vwm.c291
-rw-r--r--src/vwm.h79
-rw-r--r--src/window.c468
-rw-r--r--src/window.h81
-rw-r--r--src/xevent.c269
-rw-r--r--src/xevent.h18
-rw-r--r--src/xwindow.c247
-rw-r--r--src/xwindow.h52
-rw-r--r--vwm.c3285
-rw-r--r--vwm.h84
46 files changed, 4092 insertions, 3369 deletions
diff --git a/.gitignore b/.gitignore
index 551120e..e537ae5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,3 @@
*.o
vwm
+*.swp
diff --git a/src/clickety.c b/src/clickety.c
new file mode 100644
index 0000000..a64e6d7
--- /dev/null
+++ b/src/clickety.c
@@ -0,0 +1,244 @@
+/*
+ * \/\/\
+ *
+ * Copyright (C) 2012-2016 Vito Caputo - <vcaputo@gnugeneration.com>
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+ /* input event handling stuff */
+
+#include <X11/Xlib.h>
+
+#include "clickety.h"
+#include "desktop.h"
+#include "vwm.h"
+#include "window.h"
+#include "xwindow.h"
+
+/* simple little state machine for managing mouse click/drag/release sequences so we don't need another event loop */
+typedef enum _vwm_adjust_mode_t {
+ VWM_ADJUST_RESIZE,
+ VWM_ADJUST_MOVE
+} vwm_adjust_mode_t;
+
+typedef struct _vwm_clickety_t {
+ vwm_window_t *vwin;
+ vwm_adjust_mode_t mode;
+ XWindowAttributes orig, lastrect;
+ int impetus_x, impetus_y, impetus_x_root, impetus_y_root;
+} vwm_clickety_t;
+
+static vwm_clickety_t clickety;
+
+/* helper function for resizing the window, how the motion is applied depends on where in the window the impetus event occurred */
+static void compute_resize(XEvent *terminus, XWindowAttributes *new)
+{
+ vwm_window_t *vwin;
+ int dw = (clickety.orig.width / 2);
+ int dh = (clickety.orig.height / 2);
+ int xdelta = (terminus->xbutton.x_root - clickety.impetus_x_root);
+ int ydelta = (terminus->xbutton.y_root - clickety.impetus_y_root);
+ int min_width = 0, min_height = 0;
+ int width_inc = 1, height_inc = 1;
+
+ /* TODO: there's a problem here WRT border width, I should be considering it, I just haven't bothered to fix it since it doesn't seem to matter */
+ if ((vwin = clickety.vwin) && vwin->hints) {
+ if ((vwin->hints_supplied & PMinSize)) {
+ min_width = vwin->hints->min_width;
+ min_height = vwin->hints->min_height;
+ VWM_TRACE("window size hints exist and minimum sizes are w=%i h=%i", min_width, min_height);
+ }
+
+ if ((vwin->hints_supplied & PResizeInc)) {
+ width_inc = vwin->hints->width_inc;
+ height_inc = vwin->hints->height_inc;
+ VWM_TRACE("window size hints exist and resize increments are w=%i h=%i", width_inc, height_inc);
+ if (!width_inc) width_inc = 1;
+ if (!height_inc) height_inc = 1;
+ }
+ }
+
+ xdelta = xdelta / width_inc * width_inc;
+ ydelta = ydelta / height_inc * height_inc;
+
+ if (clickety.impetus_x < dw && clickety.impetus_y < dh) {
+ /* grabbed top left */
+ new->x = clickety.orig.x + xdelta;
+ new->y = clickety.orig.y + ydelta;
+ new->width = clickety.orig.width - xdelta;
+ new->height = clickety.orig.height - ydelta;
+ } else if (clickety.impetus_x > dw && clickety.impetus_y < dh) {
+ /* grabbed top right */
+ new->x = clickety.orig.x;
+ new->y = clickety.orig.y + ydelta;
+ new->width = clickety.orig.width + xdelta;
+ new->height = clickety.orig.height - ydelta;
+ } else if (clickety.impetus_x < dw && clickety.impetus_y > dh) {
+ /* grabbed bottom left */
+ new->x = clickety.orig.x + xdelta;
+ new->y = clickety.orig.y;
+ new->width = clickety.orig.width - xdelta;
+ new->height = clickety.orig.height + ydelta;
+ } else if (clickety.impetus_x > dw && clickety.impetus_y > dh) {
+ /* grabbed bottom right */
+ new->x = clickety.orig.x;
+ new->y = clickety.orig.y;
+ new->width = clickety.orig.width + xdelta;
+ new->height = clickety.orig.height + ydelta;
+ }
+
+ /* constrain the width and height of the window according to the minimums */
+ if (new->width < min_width) {
+ if (clickety.orig.x != new->x) new->x -= (min_width - new->width);
+ new->width = min_width;
+ }
+
+ if (new->height < min_height) {
+ if (clickety.orig.y != new->y) new->y -= (min_height - new->height);
+ new->height = min_height;
+ }
+}
+
+
+void vwm_clickety_motion(vwm_t *vwm, Window win, XMotionEvent *motion)
+{
+ XWindowChanges changes = { .border_width = WINDOW_BORDER_WIDTH };
+
+ if (!clickety.vwin) return;
+
+ /* TODO: verify win matches clickety.vwin? */
+ switch (clickety.mode) {
+ case VWM_ADJUST_MOVE:
+ changes.x = clickety.orig.x + (motion->x_root - clickety.impetus_x_root);
+ changes.y = clickety.orig.y + (motion->y_root - clickety.impetus_y_root);
+ XConfigureWindow(vwm->display, win, CWX | CWY | CWBorderWidth, &changes);
+ break;
+
+ case VWM_ADJUST_RESIZE: {
+ XWindowAttributes resized;
+
+ /* XXX: it just so happens the XMotionEvent structure is identical to XButtonEvent in the fields
+ * needed by compute_resize... */
+ compute_resize((XEvent *)motion, &resized);
+ /* TODO: this is probably broken with compositing active, but it doesn't seem to be too messed up in practice */
+ /* erase the last rectangle */
+ XDrawRectangle(vwm->display, VWM_XROOT(vwm), vwm->gc, clickety.lastrect.x, clickety.lastrect.y, clickety.lastrect.width, clickety.lastrect.height);
+ /* draw a frame @ resized coordinates */
+ XDrawRectangle(vwm->display, VWM_XROOT(vwm), vwm->gc, resized.x, resized.y, resized.width, resized.height);
+ /* remember the last rectangle */
+ clickety.lastrect = resized;
+ break;
+ }
+
+ default:
+ break;
+ }
+}
+
+
+void vwm_clickety_released(vwm_t *vwm, Window win, XButtonPressedEvent *terminus)
+{
+ XWindowChanges changes = { .border_width = WINDOW_BORDER_WIDTH };
+
+ if (!clickety.vwin) return;
+
+ switch (clickety.mode) {
+ case VWM_ADJUST_MOVE:
+ changes.x = clickety.orig.x + (terminus->x_root - clickety.impetus_x_root);
+ changes.y = clickety.orig.y + (terminus->y_root - clickety.impetus_y_root);
+ XConfigureWindow(vwm->display, win, CWX | CWY | CWBorderWidth, &changes);
+ break;
+
+ case VWM_ADJUST_RESIZE: {
+ XWindowAttributes resized;
+ compute_resize((XEvent *)terminus, &resized);
+ /* move and resize the window @ resized */
+ XDrawRectangle(vwm->display, VWM_XROOT(vwm), vwm->gc, clickety.lastrect.x, clickety.lastrect.y, clickety.lastrect.width, clickety.lastrect.height);
+ changes.x = resized.x;
+ changes.y = resized.y;
+ changes.width = resized.width;
+ changes.height = resized.height;
+ XConfigureWindow(vwm->display, win, CWX | CWY | CWWidth | CWHeight | CWBorderWidth, &changes);
+ XUngrabServer(vwm->display);
+ break;
+ }
+
+ default:
+ break;
+ }
+ /* once you manipulate the window it's no longer fullscreened, simply hitting Mod1+Return once will restore fullscreened mode */
+ clickety.vwin->autoconfigured = VWM_WIN_AUTOCONF_NONE;
+
+ clickety.vwin = NULL; /* reset clickety */
+
+ XFlush(vwm->display);
+ XUngrabPointer(vwm->display, CurrentTime);
+}
+
+
+/* on pointer buttonpress we initiate a clickety sequence; setup clickety with the window and impetus coordinates.. */
+int vwm_clickety_pressed(vwm_t *vwm, Window win, XButtonPressedEvent *impetus)
+{
+ vwm_window_t *vwin;
+
+ /* verify the window still exists */
+ if (!XGetWindowAttributes(vwm->display, win, &clickety.orig)) goto _fail;
+
+ if (!(vwin = vwm_win_lookup(vwm, win))) goto _fail;
+
+ if (impetus->state & WM_GRAB_MODIFIER) {
+
+ /* always set the input focus to the clicked window, note if we allow this to happen on the root window, it enters sloppy focus mode
+ * until a non-root window is clicked, which is an interesting hybrid but not how I prefer it. */
+ if (vwin != vwm->focused_desktop->focused_window && vwin->xwindow->id != VWM_XROOT(vwm)) {
+ vwm_win_focus(vwm, vwin);
+ vwm_win_mru(vwm, vwin);
+ }
+
+ switch (impetus->button) {
+ case Button1:
+ /* immediately raise the window if we're relocating,
+ * resizes are supported without raising (which also enables NULL resizes to focus without raising) */
+ clickety.mode = VWM_ADJUST_MOVE;
+ XRaiseWindow(vwm->display, win);
+ break;
+
+ case Button3:
+ /* grab the server on resize for the xor rubber-banding's sake */
+ XGrabServer(vwm->display);
+ XSync(vwm->display, False);
+
+ /* FIXME: none of the resize DrawRectangle() calls consider the window border. */
+ XDrawRectangle(vwm->display, VWM_XROOT(vwm), vwm->gc, clickety.orig.x, clickety.orig.y, clickety.orig.width, clickety.orig.height);
+ clickety.lastrect = clickety.orig;
+
+ clickety.mode = VWM_ADJUST_RESIZE;
+ break;
+
+ default:
+ goto _fail;
+ }
+ clickety.vwin = vwin;
+ clickety.impetus_x_root = impetus->x_root;
+ clickety.impetus_y_root = impetus->y_root;
+ clickety.impetus_x = impetus->x;
+ clickety.impetus_y = impetus->y;
+ }
+
+ return 1;
+
+_fail:
+ XUngrabPointer(vwm->display, CurrentTime);
+
+ return 0;
+}
diff --git a/src/clickety.h b/src/clickety.h
new file mode 100644
index 0000000..b2b51f1
--- /dev/null
+++ b/src/clickety.h
@@ -0,0 +1,12 @@
+#ifndef _CLICKETY_H
+#define _CLICKETY_H
+
+#include <X11/Xlib.h>
+
+#include "vwm.h"
+
+void vwm_clickety_motion(vwm_t *vwm, Window win, XMotionEvent *motion);
+void vwm_clickety_released(vwm_t *vwm, Window win, XButtonPressedEvent *terminus);
+int vwm_clickety_pressed(vwm_t *vwm, Window win, XButtonPressedEvent *impetus);
+
+#endif
diff --git a/colors.def b/src/colors.def
index 97c0dc9..97c0dc9 100644
--- a/colors.def
+++ b/src/colors.def
diff --git a/src/composite.c b/src/composite.c
new file mode 100644
index 0000000..afa4add
--- /dev/null
+++ b/src/composite.c
@@ -0,0 +1,331 @@
+/*
+ * \/\/\
+ *
+ * Copyright (C) 2012-2016 Vito Caputo - <vcaputo@gnugeneration.com>
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/* The compositing code is heavily influenced by Keith Packard's xcompmgr.
+ */
+
+#include <X11/Xlib.h>
+#include <X11/extensions/Xcomposite.h>
+#include <X11/extensions/Xdamage.h>
+#include <X11/extensions/Xfixes.h>
+#include <X11/extensions/Xrender.h>
+
+#include "xwindow.h"
+#include "vwm.h"
+
+ /* compositing manager stuff */
+typedef enum _vwm_compositing_mode_t {
+ VWM_COMPOSITING_OFF = 0, /* non-composited, no redirected windows, most efficient */
+ VWM_COMPOSITING_MONITORS = 1 /* composited process monitoring overlays, slower but really useful. */
+} vwm_compositing_mode_t;
+
+static vwm_compositing_mode_t compositing_mode = VWM_COMPOSITING_OFF; /* current compositing mode */
+static XserverRegion combined_damage = None;
+static Picture root_picture = None, root_buffer = None; /* compositing gets double buffered */
+static XWindowAttributes root_attrs;
+static XRenderPictureAttributes pa_inferiors = { .subwindow_mode = IncludeInferiors };
+static int repaint_needed;
+
+/* bind the window to a "namewindowpixmap" and create a picture from it (compositing) */
+static void bind_namewindow(vwm_t *vwm, vwm_xwindow_t *xwin)
+{
+ xwin->pixmap = XCompositeNameWindowPixmap(vwm->display, xwin->id);
+ xwin->picture = XRenderCreatePicture(vwm->display, xwin->pixmap,
+ XRenderFindVisualFormat(vwm->display, xwin->attrs.visual),
+ CPSubwindowMode, &pa_inferiors);
+ XFreePixmap(vwm->display, xwin->pixmap);
+}
+
+/* free the window's picture for accessing its redirected contents (compositing) */
+static void unbind_namewindow(vwm_t *vwm, vwm_xwindow_t *xwin)
+{
+ XRenderFreePicture(vwm->display, xwin->picture);
+}
+
+void vwm_composite_xwin_create(vwm_t *vwm, vwm_xwindow_t *xwin)
+{
+ if (!compositing_mode) return;
+
+ bind_namewindow(vwm, xwin);
+ xwin->damage = XDamageCreate(vwm->display, xwin->id, XDamageReportNonEmpty);
+}
+
+void vwm_composite_xwin_destroy(vwm_t *vwm, vwm_xwindow_t *xwin)
+{
+ if (!compositing_mode) return;
+
+ unbind_namewindow(vwm, xwin);
+ XDamageDestroy(vwm->display, xwin->damage);
+}
+
+/* add damage to the global combined damage queue where we accumulate damage between calls to paint_all() */
+void vwm_composite_damage_add(vwm_t *vwm, XserverRegion damage)
+{
+ if (combined_damage != None) {
+ XFixesUnionRegion(vwm->display, combined_damage, combined_damage, damage);
+ XFixesDestroyRegion(vwm->display, damage);
+ } else {
+ combined_damage = damage;
+ }
+}
+
+/* helper to damage an entire window including the border */
+void vwm_composite_damage_win(vwm_t *vwm, vwm_xwindow_t *xwin)
+{
+ XserverRegion region;
+ XRectangle rect = { xwin->attrs.x,
+ xwin->attrs.y,
+ xwin->attrs.width + xwin->attrs.border_width * 2,
+ xwin->attrs.height + xwin->attrs.border_width * 2 };
+
+ if (!compositing_mode) return;
+
+ region = XFixesCreateRegion(vwm->display, &rect, 1);
+ vwm_composite_damage_add(vwm, region);
+}
+
+
+void vwm_composite_handle_configure(vwm_t *vwm, vwm_xwindow_t *xwin, XWindowAttributes *new_attrs) {
+ if (!compositing_mode) return;
+
+ /* damage the old and new window areas */
+ XserverRegion region;
+ XRectangle rects[2] = { { xwin->attrs.x,
+ xwin->attrs.y,
+ xwin->attrs.width + xwin->attrs.border_width * 2,
+ xwin->attrs.height + xwin->attrs.border_width * 2 },
+ { new_attrs->x,
+ new_attrs->y,
+ new_attrs->width + new_attrs->border_width * 2,
+ new_attrs->height + new_attrs->border_width * 2 } };
+
+ region = XFixesCreateRegion(vwm->display, rects, 2);
+ vwm_composite_damage_add(vwm, region);
+ unbind_namewindow(vwm, xwin);
+ bind_namewindow(vwm, xwin);
+}
+
+
+void vwm_composite_handle_map(vwm_t *vwm, vwm_xwindow_t *xwin) {
+ if (!compositing_mode) return;
+
+ vwm_composite_damage_win(vwm, xwin);
+ unbind_namewindow(vwm, xwin);
+ bind_namewindow(vwm, xwin);
+}
+
+/* take what regions of the damaged window have been damaged, subtract them from the per-window damage object, and add them to the combined damage */
+void vwm_composite_damage_event(vwm_t *vwm, XDamageNotifyEvent *ev)
+{
+ XserverRegion region;
+ vwm_xwindow_t *xwin;
+
+ xwin = vwm_xwin_lookup(vwm, ev->drawable);
+ if (!xwin) {
+ VWM_ERROR("damaged unknown drawable %x", (unsigned int)ev->drawable);
+ return;
+ }
+
+ region = XFixesCreateRegion(vwm->display, NULL, 0);
+ XDamageSubtract(vwm->display, xwin->damage, None, region);
+ XFixesTranslateRegion(vwm->display, region, xwin->attrs.x + xwin->attrs.border_width, xwin->attrs.y + xwin->attrs.border_width);
+ vwm_composite_damage_add(vwm, region);
+}
+
+
+/* throw away our double buffered root pictures so they get recreated on the next paint_all() */
+/* used in response to screen configuration changes... */
+void vwm_composite_invalidate_root(vwm_t *vwm)
+{
+ if (!compositing_mode) return;
+
+ if (root_picture) XRenderFreePicture(vwm->display, root_picture);
+ root_picture = None;
+ if (root_buffer) XRenderFreePicture(vwm->display, root_picture);
+ root_buffer = None;
+}
+
+void vwm_composite_repaint_needed(vwm_t *vwm)
+{
+ if (!compositing_mode) return;
+
+ repaint_needed = 1;
+}
+
+/* consume combined_damage by iterating the xwindows list from the top down, drawing visible windows as encountered, subtracting their area from combined_damage */
+/* when compositing is active this is the sole function responsible for making things show up on the screen */
+void vwm_composite_paint_all(vwm_t *vwm)
+{
+ vwm_xwindow_t *xwin;
+ XRenderColor bgcolor = {0x0000, 0x00, 0x00, 0xffff};
+ Region occluded = XCreateRegion();
+ static XserverRegion undamage_region = None;
+
+ /* if there's no damage to repaint, short-circuit, this happens when compositing for overlays is disabled. */
+ if (!compositing_mode || (combined_damage == None && !repaint_needed)) return;
+
+ repaint_needed = 0;
+
+ if (!undamage_region) undamage_region = XFixesCreateRegion(vwm->display, NULL, 0);
+
+ /* (re)create the root picture from the root window and allocate a double buffer for the off-screen composition of the damaged contents */
+ if (root_picture == None) {
+ Pixmap root_pixmap;
+
+ XGetWindowAttributes(vwm->display, VWM_XROOT(vwm), &root_attrs);
+ root_picture = XRenderCreatePicture(vwm->display, VWM_XROOT(vwm),
+ XRenderFindVisualFormat(vwm->display, VWM_XVISUAL(vwm)),
+ CPSubwindowMode, &pa_inferiors);
+ root_pixmap = XCreatePixmap(vwm->display, VWM_XROOT(vwm), root_attrs.width, root_attrs.height, VWM_XDEPTH(vwm));
+ root_buffer = XRenderCreatePicture(vwm->display, root_pixmap, XRenderFindVisualFormat(vwm->display, VWM_XVISUAL(vwm)), 0, 0);
+ XFreePixmap(vwm->display, root_pixmap);
+ }
+
+ /* compose overlays for all visible windows up front in a separate pass (kind of lame, but it's simpler since compose_overlay() adds to combined_damage) */
+ list_for_each_entry_prev(xwin, &vwm->xwindows, xwindows) {
+ XRectangle r;
+
+ if (!vwm_xwin_is_mapped(vwm, xwin)) continue; /* if !mapped skip */
+
+ /* Everything mapped next goes through an occlusion check.
+ * Since the composite extension stops delivery of VisibilityNotify events for redirected windows,
+ * (it assumes redirected windows should be treated as part of a potentially transparent composite, and provides no api to alter this assumption)
+ * we can't simply select the VisibilityNotify events on all windows and cache their visibility state in vwm_xwindow_t then skip
+ * xwin->state==VisibilityFullyObscured windows here to avoid the cost of pointlessly composing overlays and rendering fully obscured windows :(.
+ *
+ * Instead we accumulate an occluded region (starting empty) of painted windows from the top-down on every paint_all().
+ * Before we compose_overlay() a window, we check if the window's rectangle fits entirely within the occluded region.
+ * If it does, no compose_overlay() is performed.
+ * If it doesn't, compose_overlay() is called, and the window's rect is added to the occluded region.
+ * The occluded knowledge is also cached for the XRenderComposite() loop immediately following, where we skip the rendering of
+ * occluded windows as well.
+ * This does technically break SHAPE windows (xeyes, xmms), but only when monitoring is enabled which covers them with rectangular overlays anyways.
+ */
+ r.x = xwin->attrs.x;
+ r.y = xwin->attrs.y;
+ r.width = xwin->attrs.width + xwin->attrs.border_width * 2;
+ r.height = xwin->attrs.height + xwin->attrs.border_width * 2;
+ if (XRectInRegion(occluded, r.x, r.y, r.width, r.height) != RectangleIn) {
+ /* the window isn't fully occluded, compose it and add it to occluded */
+ if (xwin->monitor && !xwin->attrs.override_redirect) vwm_overlay_xwin_compose(vwm, xwin);
+ XUnionRectWithRegion(&r, occluded, occluded);
+ xwin->occluded = 0;
+ } else {
+ xwin->occluded = 1;
+ VWM_TRACE("window %#x occluded, skipping compose_overlay()", (int)xwin->id);
+ }
+ }
+ XDestroyRegion(occluded);
+
+ /* start with the clip regions set to the damaged area accumulated since the previous paint_all() */
+ XFixesSetPictureClipRegion(vwm->display, root_buffer, 0, 0, combined_damage); /* this is the double buffer where the in-flight screen contents are staged */
+ XFixesSetPictureClipRegion(vwm->display, root_picture, 0, 0, combined_damage); /* this is the visible root window */
+
+ /* since translucent windows aren't supported in vwm, I can do this more efficiently */
+ list_for_each_entry_prev(xwin, &vwm->xwindows, xwindows) {
+ XRectangle r;
+
+ if (!vwm_xwin_is_mapped(vwm, xwin) || xwin->occluded) continue; /* if !mapped or occluded skip */
+
+ /* these coordinates + dimensions incorporate the border (since XCompositeNameWindowPixmap is being used) */
+ r.x = xwin->attrs.x;
+ r.y = xwin->attrs.y;
+ r.width = xwin->attrs.width + xwin->attrs.border_width * 2;
+ r.height = xwin->attrs.height + xwin->attrs.border_width * 2;
+
+ /* render the redirected window contents into root_buffer, note root_buffer has the remaining combined_damage set as the clip region */
+ XRenderComposite(vwm->display, PictOpSrc, xwin->picture, None, root_buffer,
+ 0, 0, 0, 0, /* src x, y, mask x, y */
+ r.x, r.y, /* dst x, y */
+ r.width, r.height);
+
+ if (xwin->monitor && !xwin->attrs.override_redirect && xwin->overlay.width) {
+ /* draw the monitoring overlay atop the window, note we stay within the window borders here. */
+ XRenderComposite(vwm->display, PictOpOver, xwin->overlay.picture, None, root_buffer,
+ 0, 0, 0, 0, /* src x,y, maxk x, y */
+ xwin->attrs.x + xwin->attrs.border_width, /* dst x */
+ xwin->attrs.y + xwin->attrs.border_width, /* dst y */
+ xwin->attrs.width, vwm_overlay_xwin_composed_height(vwm, xwin)); /* w, h */
+ }
+
+ /* subtract the region of the window from the combined damage and update the root_buffer clip region to reflect the remaining damage */
+ XFixesSetRegion(vwm->display, undamage_region, &r, 1);
+ XFixesSubtractRegion(vwm->display, combined_damage, combined_damage, undamage_region);
+ XFixesSetPictureClipRegion(vwm->display, root_buffer, 0, 0, combined_damage);
+ }
+
+ /* at this point all of the visible windows have been subtracted from the clip region, so paint any root window damage (draw background) */
+ XRenderFillRectangle(vwm->display, PictOpSrc, root_buffer, &bgcolor, 0, 0, root_attrs.width, root_attrs.height);
+
+ /* discard the root_buffer clip region and copy root_buffer to root_picture, root_picture still has the combined damage as its clip region */
+ XFixesSetPictureClipRegion(vwm->display, root_buffer, 0, 0, None);
+ XRenderComposite(vwm->display, PictOpSrc, root_buffer, None, root_picture, 0, 0, 0, 0, 0, 0, root_attrs.width, root_attrs.height);
+
+ /* fin */
+ XFixesDestroyRegion(vwm->display, combined_damage);
+ combined_damage = None;
+ XSync(vwm->display, False);
+}
+
+
+/* toggle compositing/monitoring overlays on/off */
+void vwm_composite_toggle(vwm_t *vwm)
+{
+ vwm_xwindow_t *xwin;
+
+ XGrabServer(vwm->display);
+ XSync(vwm->display, False);
+
+ switch (compositing_mode) {
+ case VWM_COMPOSITING_OFF:
+ VWM_TRACE("enabling compositing");
+ compositing_mode = VWM_COMPOSITING_MONITORS;
+ XCompositeRedirectSubwindows(vwm->display, VWM_XROOT(vwm), CompositeRedirectManual);
+ list_for_each_entry_prev(xwin, &vwm->xwindows, xwindows) {
+ bind_namewindow(vwm, xwin);
+ xwin->damage = XDamageCreate(vwm->display, xwin->id, XDamageReportNonEmpty);
+ }
+ /* damage everything */
+ /* TODO: keep a list of rects reflecting all the current screens and create a region from that... */
+ vwm_composite_damage_add(vwm, XFixesCreateRegionFromWindow(vwm->display, VWM_XROOT(vwm), WindowRegionBounding));
+ break;
+
+ case VWM_COMPOSITING_MONITORS: {
+ XEvent ev;
+
+ VWM_TRACE("disabling compositing");
+ compositing_mode = VWM_COMPOSITING_OFF;
+ list_for_each_entry_prev(xwin, &vwm->xwindows, xwindows) {
+ unbind_namewindow(vwm, xwin);
+ XDamageDestroy(vwm->display, xwin->damage);
+ }
+ XCompositeUnredirectSubwindows(vwm->display, VWM_XROOT(vwm), CompositeRedirectManual);
+ vwm_composite_invalidate_root(vwm);
+
+ /* if there's any damage queued up discard it so we don't enter paint_all() until compositing is reenabled again. */
+ if (combined_damage) {
+ XFixesDestroyRegion(vwm->display, combined_damage);
+ combined_damage = None;
+ }
+ while (XCheckTypedEvent(vwm->display, vwm->damage_event + XDamageNotify, &ev) != False);
+ break;
+ }
+ }
+
+ XUngrabServer(vwm->display);
+}
diff --git a/src/composite.h b/src/composite.h
new file mode 100644
index 0000000..fb4f623
--- /dev/null
+++ b/src/composite.h
@@ -0,0 +1,24 @@
+#ifndef _COMPOSITE_H
+#define _COMPOSITE_H
+
+#include <X11/Xlib.h>
+#include <X11/extensions/Xdamage.h>
+#include <X11/extensions/Xfixes.h>
+
+typedef struct _vwm_t vwm_t;
+typedef struct _vwm_xwindow_t vwm_xwindow_t;
+
+void vwm_composite_xwin_create(vwm_t *vwm, vwm_xwindow_t *xwin);
+void vwm_composite_xwin_destroy(vwm_t *vwm, vwm_xwindow_t *xwin);
+void vwm_composite_damage_add(vwm_t *vwm, XserverRegion damage);
+void vwm_composite_damage_win(vwm_t *vwm, vwm_xwindow_t *xwin);
+void vwm_composite_handle_configure(vwm_t *vwm, vwm_xwindow_t *xwin, XWindowAttributes *new_attrs);
+void vwm_composite_handle_map(vwm_t *vwm, vwm_xwindow_t *xwin);
+void vwm_composite_damage_event(vwm_t *vwm, XDamageNotifyEvent *ev);
+void vwm_composite_damage_win(vwm_t *vwm, vwm_xwindow_t *xwin);
+void vwm_composite_paint_all(vwm_t *vwm);
+void vwm_composite_invalidate_root(vwm_t *vwm);
+void vwm_composite_repaint_needed(vwm_t *vwm);
+void vwm_composite_toggle(vwm_t *vwm);
+
+#endif
diff --git a/src/context.c b/src/context.c
new file mode 100644
index 0000000..cede8a9
--- /dev/null
+++ b/src/context.c
@@ -0,0 +1,97 @@
+/*
+ * \/\/\
+ *
+ * Copyright (C) 2012-2016 Vito Caputo - <vcaputo@gnugeneration.com>
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+ /* desktop/shelf context handling */
+
+#include <X11/Xlib.h>
+
+#include "context.h"
+#include "desktop.h"
+#include "vwm.h"
+#include "xwindow.h"
+#include "window.h"
+
+/* switch to the desired context if it isn't already the focused one, inform caller if anything happened */
+int vwm_context_focus(vwm_t *vwm, vwm_context_t desired_context)
+{
+ vwm_context_t entry_context = vwm->focused_context;
+
+ switch (vwm->focused_context) {
+ vwm_xwindow_t *xwin;
+ vwm_window_t *vwin;
+
+ case VWM_CONTEXT_SHELF:
+ if (desired_context == VWM_CONTEXT_SHELF) break;
+
+ /* desired == DESKTOP && focused == SHELF */
+
+ VWM_TRACE("unmapping shelf window \"%s\"", vwm->focused_shelf->xwindow->name);
+ vwm_win_unmap(vwm, vwm->focused_shelf);
+ XFlush(vwm->display); /* for a more responsive feel */
+
+ /* map the focused desktop, from the top of the stack down */
+ list_for_each_entry_prev(xwin, &vwm->xwindows, xwindows) {
+ if (!(vwin = xwin->managed)) continue;
+ if (vwin->desktop == vwm->focused_desktop && !vwin->shelved) {
+ VWM_TRACE("Mapping desktop window \"%s\"", xwin->name);
+ vwm_win_map(vwm, vwin);
+ }
+ }
+
+ if (vwm->focused_desktop->focused_window) {
+ VWM_TRACE("Focusing \"%s\"", vwm->focused_desktop->focused_window->xwindow->name);
+ XSetInputFocus(vwm->display, vwm->focused_desktop->focused_window->xwindow->id, RevertToPointerRoot, CurrentTime);
+ }
+
+ vwm->focused_context = VWM_CONTEXT_DESKTOP;
+ break;
+
+ case VWM_CONTEXT_DESKTOP:
+ /* unmap everything, map the shelf */
+ if (desired_context == VWM_CONTEXT_DESKTOP) break;
+
+ /* desired == SHELF && focused == DESKTOP */
+
+ /* there should be a focused shelf if the shelf contains any windows, we NOOP the switch if the shelf is empty. */
+ if (vwm->focused_shelf) {
+ /* unmap everything on the current desktop */
+ list_for_each_entry(xwin, &vwm->xwindows, xwindows) {
+ if (!(vwin = xwin->managed)) continue;
+ if (vwin->desktop == vwm->focused_desktop) {
+ VWM_TRACE("Unmapping desktop window \"%s\"", xwin->name);
+ vwm_win_unmap(vwm, vwin);
+ }
+ }
+
+ XFlush(vwm->display); /* for a more responsive feel */
+
+ VWM_TRACE("Mapping shelf window \"%s\"", vwm->focused_shelf->xwindow->name);
+ vwm_win_map(vwm, vwm->focused_shelf);
+ vwm_win_focus(vwm, vwm->focused_shelf);
+
+ vwm->focused_context = VWM_CONTEXT_SHELF;
+ }
+ break;
+
+ default:
+ VWM_BUG("unexpected focused context %x", vwm->focused_context);
+ break;
+ }
+
+ /* return if the context has been changed, the caller may need to branch differently if nothing happened */
+ return (vwm->focused_context != entry_context);
+}
diff --git a/src/context.h b/src/context.h
new file mode 100644
index 0000000..1604bd3
--- /dev/null
+++ b/src/context.h
@@ -0,0 +1,14 @@
+#ifndef _CONTEXT_H
+#define _CONTEXT_H
+
+typedef struct _vwm_t vwm_t;
+
+typedef enum _vwm_context_t {
+ VWM_CONTEXT_DESKTOP = 0, /* focus the desktop context */
+ VWM_CONTEXT_SHELF, /* focus the shelf context */
+ VWM_CONTEXT_OTHER /* focus the other context relative to the current one */
+} vwm_context_t;
+
+int vwm_context_focus(vwm_t *vwm, vwm_context_t desired_context);
+
+#endif
diff --git a/src/desktop.c b/src/desktop.c
new file mode 100644
index 0000000..d3fdd13
--- /dev/null
+++ b/src/desktop.c
@@ -0,0 +1,158 @@
+/*
+ * \/\/\
+ *
+ * Copyright (C) 2012-2016 Vito Caputo - <vcaputo@gnugeneration.com>
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+ /* virtual desktops */
+
+#include <X11/Xlib.h>
+#include <stdlib.h>
+
+#include "list.h"
+#include "context.h"
+#include "desktop.h"
+#include "vwm.h"
+#include "xwindow.h"
+
+/* make the specified desktop the most recently used one */
+void vwm_desktop_mru(vwm_t *vwm, vwm_desktop_t *desktop)
+{
+ VWM_TRACE("MRU desktop: %p", desktop);
+ list_move(&desktop->desktops_mru, &vwm->desktops_mru);
+}
+
+
+/* focus a virtual desktop */
+/* this switches to the desktop context if necessary, maps and unmaps windows accordingly if necessary */
+int vwm_desktop_focus(vwm_t *vwm, vwm_desktop_t *desktop)
+{
+ XGrabServer(vwm->display);
+ XSync(vwm->display, False);
+
+ /* if the context switched and the focused desktop is the desired desktop there's nothing else to do */
+ if ((vwm_context_focus(vwm, VWM_CONTEXT_DESKTOP) && vwm->focused_desktop != desktop) || vwm->focused_desktop != desktop) {
+ vwm_xwindow_t *xwin;
+ vwm_window_t *vwin;
+
+ /* unmap the windows on the currently focused desktop, map those on the newly focused one */
+ list_for_each_entry(xwin, &vwm->xwindows, xwindows) {
+ if (!(vwin = xwin->managed) || vwin->shelved) continue;
+ if (vwin->desktop == vwm->focused_desktop) vwm_win_unmap(vwm, vwin);
+ }
+
+ XFlush(vwm->display);
+
+ list_for_each_entry_prev(xwin, &vwm->xwindows, xwindows) {
+ if (!(vwin = xwin->managed) || vwin->shelved) continue;
+ if (vwin->desktop == desktop) vwm_win_map(vwm, vwin);
+ }
+
+ vwm->focused_desktop = desktop;
+ }
+
+ /* directly focus the desktop's focused window if there is one, we don't use vwm_win_focus() intentionally XXX */
+ if (vwm->focused_desktop->focused_window) {
+ VWM_TRACE("Focusing \"%s\"", vwm->focused_desktop->focused_window->xwindow->name);
+ XSetInputFocus(vwm->display, vwm->focused_desktop->focused_window->xwindow->id, RevertToPointerRoot, CurrentTime);
+ }
+
+ XUngrabServer(vwm->display);
+
+ return 1;
+}
+
+/* return next MRU desktop relative to the supplied desktop */
+vwm_desktop_t * vwm_desktop_next_mru(vwm_t *vwm, vwm_desktop_t *desktop) {
+ list_head_t *next;
+
+ /* this dance is necessary because the list head @ vwm->desktops_mru has no vwm_desktop_t container,
+ * and we're exploiting the circular nature of the doubly linked lists, so we need to take care to skip
+ * past the container-less head.
+ */
+ if (desktop->desktops_mru.next == &vwm->desktops_mru) {
+ next = desktop->desktops_mru.next->next;
+ } else {
+ next = desktop->desktops_mru.next;
+ }
+
+ return list_entry(next, vwm_desktop_t, desktops_mru);
+}
+
+/* return next desktop spatially relative to the supplied desktop, no wrap-around */
+vwm_desktop_t * vwm_desktop_next(vwm_t *vwm, vwm_desktop_t *desktop) {
+ if (desktop->desktops.next != &vwm->desktops) {
+ desktop = list_entry(desktop->desktops.next, vwm_desktop_t, desktops);
+ }
+
+ return desktop;
+}
+
+
+/* return previous desktop spatially relative to the supplied desktop, no wrap-around */
+vwm_desktop_t * vwm_desktop_prev(vwm_t *vwm, vwm_desktop_t *desktop) {
+ if (desktop->desktops.prev != &vwm->desktops) {
+ desktop = list_entry(desktop->desktops.prev, vwm_desktop_t, desktops);
+ }
+
+ return desktop;
+}
+
+/* create a virtual desktop */
+vwm_desktop_t * vwm_desktop_create(vwm_t *vwm, char *name)
+{
+ vwm_desktop_t *desktop;
+
+ desktop = malloc(sizeof(vwm_desktop_t));
+ if (desktop == NULL) {
+ VWM_PERROR("Failed to allocate desktop");
+ goto _fail;
+ }
+
+ desktop->name = name == NULL ? name : strdup(name);
+ desktop->focused_window = NULL;
+
+ list_add_tail(&desktop->desktops, &vwm->desktops);
+ list_add_tail(&desktop->desktops_mru, &vwm->desktops_mru);
+
+ return desktop;
+
+_fail:
+ return NULL;
+}
+
+
+/* destroy a virtual desktop */
+void vwm_desktop_destroy(vwm_t *vwm, vwm_desktop_t *desktop)
+{
+ /* silently refuse to destroy a desktop having windows (for now) */
+ /* there's _always_ a focused window on a desktop having mapped windows */
+ /* also silently refuse to destroy the last desktop (for now) */
+ if (desktop->focused_window || (desktop->desktops.next == desktop->desktops.prev)) return;
+
+ /* focus the MRU desktop that isn't this one if we're the focused desktop */
+ if (desktop == vwm->focused_desktop) {
+ vwm_desktop_t *next_desktop;
+
+ list_for_each_entry(next_desktop, &vwm->desktops_mru, desktops_mru) {
+ if (next_desktop != desktop) {
+ vwm_desktop_focus(vwm, next_desktop);
+ break;
+ }
+ }
+ }
+
+ list_del(&desktop->desktops);
+ list_del(&desktop->desktops_mru);
+}
diff --git a/src/desktop.h b/src/desktop.h
new file mode 100644
index 0000000..cc6df38
--- /dev/null
+++ b/src/desktop.h
@@ -0,0 +1,25 @@
+#ifndef _DESKTOP_H
+#define _DESKTOP_H
+
+#include "list.h"
+#include "window.h"
+
+typedef struct _vwm_t vwm_t;
+typedef struct _vwm_window_t vwm_window_t;
+
+typedef struct _vwm_desktop_t {
+ list_head_t desktops; /* global list of (virtual) desktops */
+ list_head_t desktops_mru; /* global list of (virtual) desktops in MRU order */
+ char *name; /* name of the desktop (TODO) */
+ vwm_window_t *focused_window; /* the focused window on this virtual desktop */
+} vwm_desktop_t;
+
+void vwm_desktop_mru(vwm_t *vwm, vwm_desktop_t *desktop);
+int vwm_desktop_focus(vwm_t *vwm, vwm_desktop_t *desktop);
+vwm_desktop_t * vwm_desktop_create(vwm_t *vwm, char *name);
+void vwm_desktop_destroy(vwm_t *vwm, vwm_desktop_t *desktop);
+vwm_desktop_t * vwm_desktop_next_mru(vwm_t *vwm, vwm_desktop_t *desktop);
+vwm_desktop_t * vwm_desktop_next(vwm_t *vwm, vwm_desktop_t *desktop);
+vwm_desktop_t * vwm_desktop_prev(vwm_t *vwm, vwm_desktop_t *desktop);
+
+#endif
diff --git a/src/key.c b/src/key.c
new file mode 100644
index 0000000..7a15ed0
--- /dev/null
+++ b/src/key.c
@@ -0,0 +1,371 @@
+/*
+ * \/\/\
+ *
+ * Copyright (C) 2012-2016 Vito Caputo - <vcaputo@gnugeneration.com>
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <X11/Xlib.h>
+#include <X11/keysym.h>
+#include <stdlib.h>
+
+#include "composite.h"
+#include "desktop.h"
+#include "launch.h"
+#include "list.h"
+#include "vwm.h"
+#include "window.h"
+#include "xwindow.h"
+
+static int key_is_grabbed; /* flag for tracking keyboard grab state */
+
+/* Poll the keyboard state to see if _any_ keys are pressed */
+static int keys_pressed(vwm_t *vwm) {
+ int i;
+ char state[32];
+
+ XQueryKeymap(vwm->display, state);
+
+ for (i = 0; i < sizeof(state); i++) {
+ if (state[i]) return 1;
+ }
+
+ return 0;
+}
+
+/* Called in response to KeyRelease events, for now only interesting for detecting when Mod1 is released termintaing
+ * window cycling for application of MRU on the focused window */
+void vwm_key_released(vwm_t *vwm, Window win, XKeyReleasedEvent *keyrelease)
+{
+ vwm_window_t *vwin;
+ KeySym sym;
+
+ switch ((sym = XLookupKeysym(keyrelease, 0))) {
+ case XK_Alt_R:
+ case XK_Alt_L: /* TODO: actually use the modifier mapping, for me XK_Alt_[LR] is Mod1. XGetModifierMapping()... */
+ VWM_TRACE("XK_Alt_[LR] released");
+
+ /* aborted? try restore focused_origin */
+ if (key_is_grabbed > 1 && vwm->focused_origin) {
+ VWM_TRACE("restoring %p on %p", vwm->focused_origin, vwm->focused_origin->desktop);
+ vwm_desktop_focus(vwm, vwm->focused_origin->desktop);
+ vwm_win_focus(vwm, vwm->focused_origin);
+ }
+
+ /* make the focused window the most recently used */
+ if ((vwin = vwm_win_focused(vwm))) vwm_win_mru(vwm, vwin);
+
+ /* make the focused desktop the most recently used */
+ if (vwm->focused_context == VWM_CONTEXT_DESKTOP && vwm->focused_desktop) vwm_desktop_mru(vwm, vwm->focused_desktop);
+
+ break;
+
+ default:
+ VWM_TRACE("Unhandled keycode: %x", (unsigned int)sym);
+ break;
+ }
+
+ if (key_is_grabbed && !keys_pressed(vwm)) {
+ XUngrabKeyboard(vwm->display, CurrentTime);
+ XFlush(vwm->display);
+ key_is_grabbed = 0;
+ vwm->fence_mask = 0; /* reset the fence mask on release for VWM_FENCE_MASKED_VIOLATE */
+ }
+}
+
+
+/* Called in response to KeyPress events, I currenly only grab Mod1 keypress events */
+void vwm_key_pressed(vwm_t *vwm, Window win, XKeyPressedEvent *keypress)
+{
+ vwm_window_t *vwin;
+ KeySym sym;
+ static KeySym last_sym;
+ static typeof(keypress->state) last_state;
+ static int repeat_cnt = 0;
+ int do_grab = 0;
+ char *quit_console_args[] = {"/bin/sh", "-c", "screen -dr " CONSOLE_SESSION_STRING " -X quit", NULL};
+
+ sym = XLookupKeysym(keypress, 0);
+
+ /* detect repeaters, note repeaters do not span interrupted Mod1 sequences! */
+ if (key_is_grabbed && sym == last_sym && keypress->state == last_state) {
+ repeat_cnt++;
+ } else {
+ repeat_cnt = 0;
+ }
+
+ vwin = vwm_win_focused(vwm);
+
+ switch (sym) {
+
+#define launcher(_sym, _label, _argv)\
+ case _sym: \
+ { \
+ char *args[] = {"/bin/sh", "-c", "screen -dr " CONSOLE_SESSION_STRING " -X screen /bin/sh -i -x -c \"" _argv " || sleep 86400\"", NULL};\
+ vwm_launch(vwm, args, VWM_LAUNCH_MODE_BG);\
+ break; \
+ }
+#include "launchers.def"
+#undef launcher
+ case XK_Alt_L: /* transaction abort */
+ case XK_Alt_R:
+ if (key_is_grabbed) key_is_grabbed++;
+ VWM_TRACE("aborting with origin %p", vwm->focused_origin);
+ break;
+
+ case XK_grave: /* toggle shelf visibility */
+ vwm_context_focus(vwm, VWM_CONTEXT_OTHER);
+ break;
+
+ case XK_Tab: /* cycle focused window */
+ do_grab = 1; /* update MRU window on commit (Mod1 release) */
+
+ /* focus the next window, note this doesn't affect MRU yet, that happens on Mod1 release */
+ if (vwin) {
+ if (keypress->state & ShiftMask) {
+ vwm_win_focus_next(vwm, vwin, VWM_FENCE_MASKED_VIOLATE);
+ } else {
+ vwm_win_focus_next(vwm, vwin, VWM_FENCE_RESPECT);
+ }
+ }
+ break;
+
+ case XK_space: { /* cycle focused desktop utilizing MRU */
+ vwm_desktop_t *next_desktop = vwm_desktop_next_mru(vwm, vwm->focused_desktop);
+
+ do_grab = 1; /* update MRU desktop on commit (Mod1 release) */
+
+ if (keypress->state & ShiftMask) {
+ /* migrate the focused window with the desktop focus to the most recently used desktop */
+ if (vwin) vwm_win_migrate(vwm, vwin, next_desktop);
+ } else {
+ vwm_desktop_focus(vwm, next_desktop);
+ }
+ break;
+ }
+
+ case XK_d: /* destroy focused */
+ if (vwin) {
+ if (keypress->state & ShiftMask) { /* brutally destroy the focused window */
+ XKillClient(vwm->display, vwin->xwindow->id);
+ } else { /* kindly destroy the focused window */
+ vwm_xwin_message(vwm, vwin->xwindow, vwm->wm_protocols_atom, vwm->wm_delete_atom);
+ }
+ } else if (vwm->focused_context == VWM_CONTEXT_DESKTOP) {
+ /* destroy the focused desktop when destroy occurs without any windows */
+ vwm_desktop_destroy(vwm, vwm->focused_desktop);
+ }
+ break;
+
+ case XK_Escape: /* leave VWM rudely, after triple press */
+ do_grab = 1;
+
+ if (repeat_cnt == 2) {
+ vwm_launch(vwm, quit_console_args, VWM_LAUNCH_MODE_FG);
+ exit(42);
+ }
+ break;
+
+ case XK_v: /* instantiate (and focus) a new (potentially empty, unless migrating) virtual desktop */
+ do_grab = 1; /* update MRU desktop on commit (Mod1 release) */
+
+ if (keypress->state & ShiftMask) {
+ if (vwin) {
+ /* migrate the focused window to a newly created virtual desktop, focusing the new desktop simultaneously */
+ vwm_win_migrate(vwm, vwin, vwm_desktop_create(vwm, NULL));
+ }
+ } else {
+ vwm_desktop_focus(vwm, vwm_desktop_create(vwm, NULL));
+ vwm_desktop_mru(vwm, vwm->focused_desktop);
+ }
+ break;
+
+ case XK_h: /* previous virtual desktop, if we're in the shelf context this will simply switch to desktop context */
+ do_grab = 1; /* update MRU desktop on commit (Mod1 release) */
+
+ if (keypress->state & ShiftMask) {
+ if (vwin) {
+ /* migrate the focused window with the desktop focus to the previous desktop */
+ vwm_win_migrate(vwm, vwin, vwm_desktop_prev(vwm, vwin->desktop));
+ }
+ } else {
+ if (vwm->focused_context == VWM_CONTEXT_SHELF) {
+ /* focus the focused desktop instead of the shelf */
+ vwm_context_focus(vwm, VWM_CONTEXT_DESKTOP);
+ } else {
+ /* focus the previous desktop */
+ vwm_desktop_focus(vwm, vwm_desktop_prev(vwm, vwm->focused_desktop));
+ }
+ }
+ break;
+
+ case XK_l: /* next virtual desktop, if we're in the shelf context this will simply switch to desktop context */
+ do_grab = 1; /* update MRU desktop on commit (Mod1 release) */
+
+ if (keypress->state & ShiftMask) {
+ if (vwin) {
+ /* migrate the focused window with the desktop focus to the next desktop */
+ vwm_win_migrate(vwm, vwin, vwm_desktop_next(vwm, vwin->desktop));
+ }
+ } else {
+ if (vwm->focused_context == VWM_CONTEXT_SHELF) {
+ /* focus the focused desktop instead of the shelf */
+ vwm_context_focus(vwm, VWM_CONTEXT_DESKTOP);
+ } else {
+ /* focus the next desktop */
+ vwm_desktop_focus(vwm, vwm_desktop_next(vwm, vwm->focused_desktop));
+ }
+ }
+ break;
+
+ case XK_k: /* raise or shelve the focused window */
+ if (vwin) {
+ if (keypress->state & ShiftMask) { /* shelf the window and focus the shelf */
+ if (vwm->focused_context != VWM_CONTEXT_SHELF) {
+ /* shelve the focused window while focusing the shelf */
+ vwm_win_shelve(vwm, vwin);
+ vwm_context_focus(vwm, VWM_CONTEXT_SHELF);
+ }
+ } else {
+ do_grab = 1;
+
+ XRaiseWindow(vwm->display, vwin->xwindow->id);
+
+ if (repeat_cnt == 1) {
+ /* double: reraise & fullscreen */
+ vwm_win_autoconf(vwm, vwin, VWM_SCREEN_REL_XWIN, VWM_WIN_AUTOCONF_FULL);
+ } else if (repeat_cnt == 2) {
+ /* triple: reraise & fullscreen w/borders obscured by screen perimiter */
+ vwm_win_autoconf(vwm, vwin, VWM_SCREEN_REL_XWIN, VWM_WIN_AUTOCONF_ALL);
+ } else if (vwm->xinerama_screens_cnt > 1) {
+ if (repeat_cnt == 3) {
+ /* triple: reraise & fullscreen across all screens */
+ vwm_win_autoconf(vwm, vwin, VWM_SCREEN_REL_TOTAL, VWM_WIN_AUTOCONF_FULL);
+ } else if (repeat_cnt == 4) {
+ /* quadruple: reraise & fullscreen w/borders obscured by screen perimiter */
+ vwm_win_autoconf(vwm, vwin, VWM_SCREEN_REL_TOTAL, VWM_WIN_AUTOCONF_ALL);
+ }
+ }
+ XFlush(vwm->display);
+ }
+ }
+ break;
+
+ case XK_j: /* lower or unshelve the focused window */
+ if (vwin) {
+ if (keypress->state & ShiftMask) { /* unshelf the window to the focused desktop, and focus the desktop */
+ if (vwm->focused_context == VWM_CONTEXT_SHELF) {
+ /* unshelve the focused window, focus the desktop it went to */
+ vwm_win_migrate(vwm, vwin, vwm->focused_desktop);
+ }
+ } else {
+ if (vwin->autoconfigured == VWM_WIN_AUTOCONF_ALL) {
+ vwm_win_autoconf(vwm, vwin, VWM_SCREEN_REL_XWIN, VWM_WIN_AUTOCONF_FULL);
+ } else {
+ XLowerWindow(vwm->display, vwin->xwindow->id);
+ }
+ XFlush(vwm->display);
+ }
+ }
+ break;
+
+ case XK_Return: /* (full-screen / restore) focused window */
+ if (vwin) {
+ if (vwin->autoconfigured) {
+ vwm_win_autoconf(vwm, vwin, VWM_SCREEN_REL_XWIN, VWM_WIN_AUTOCONF_NONE);
+ } else {
+ vwm_win_autoconf(vwm, vwin, VWM_SCREEN_REL_XWIN, VWM_WIN_AUTOCONF_FULL);
+ }
+ }
+ break;
+
+ case XK_s: /* shelve focused window */
+ if (vwin && !vwin->shelved) vwm_win_shelve(vwm, vwin);
+ break;
+
+ case XK_bracketleft: /* reconfigure the focused window to occupy the left or top half of the screen or left quarters on repeat */
+ if (vwin) {
+ do_grab = 1;
+
+ if (keypress->state & ShiftMask) {
+ if (!repeat_cnt) {
+ vwm_win_autoconf(vwm, vwin, VWM_SCREEN_REL_XWIN, VWM_WIN_AUTOCONF_HALF, VWM_SIDE_TOP);
+ } else {
+ vwm_win_autoconf(vwm, vwin, VWM_SCREEN_REL_XWIN, VWM_WIN_AUTOCONF_QUARTER, VWM_CORNER_TOP_LEFT);
+ }
+ } else {
+ if (!repeat_cnt) {
+ vwm_win_autoconf(vwm, vwin, VWM_SCREEN_REL_XWIN, VWM_WIN_AUTOCONF_HALF, VWM_SIDE_LEFT);
+ } else {
+ vwm_win_autoconf(vwm, vwin, VWM_SCREEN_REL_XWIN, VWM_WIN_AUTOCONF_QUARTER, VWM_CORNER_BOTTOM_LEFT);
+ }
+ }
+ }
+ break;
+
+ case XK_bracketright: /* reconfigure the focused window to occupy the right or bottom half of the screen or right quarters on repeat */
+ if (vwin) {
+ do_grab = 1;
+
+ if (keypress->state & ShiftMask) {
+ if (!repeat_cnt) {
+ vwm_win_autoconf(vwm, vwin, VWM_SCREEN_REL_XWIN, VWM_WIN_AUTOCONF_HALF, VWM_SIDE_BOTTOM);
+ } else {
+ vwm_win_autoconf(vwm, vwin, VWM_SCREEN_REL_XWIN, VWM_WIN_AUTOCONF_QUARTER, VWM_CORNER_BOTTOM_RIGHT);
+ }
+ } else {
+ if (!repeat_cnt) {
+ vwm_win_autoconf(vwm, vwin, VWM_SCREEN_REL_XWIN, VWM_WIN_AUTOCONF_HALF, VWM_SIDE_RIGHT);
+ } else {
+ vwm_win_autoconf(vwm, vwin, VWM_SCREEN_REL_XWIN, VWM_WIN_AUTOCONF_QUARTER, VWM_CORNER_TOP_RIGHT);
+ }
+ }
+ }
+ break;
+
+ case XK_semicolon: /* toggle composited overlays */
+ vwm_composite_toggle(vwm);
+ break;
+
+ case XK_apostrophe: /* reset snowflakes of the focused window, suppressed when not compositing */
+ if (vwin) {
+ vwm_overlay_xwin_reset_snowflakes(vwm, vwin->xwindow);
+ }
+ break;
+
+ case XK_Right: /* increase sampling frequency */
+ vwm_overlay_rate_increase(vwm);
+ break;
+
+ case XK_Left: /* decrease sampling frequency */
+ vwm_overlay_rate_decrease(vwm);
+ break;
+
+ default:
+ VWM_TRACE("Unhandled keycode: %x", (unsigned int)sym);
+ break;
+ }
+
+ /* if what we're doing requests a grab, if not already grabbed, grab keyboard */
+ if (!key_is_grabbed && do_grab) {
+ VWM_TRACE("saving focused_origin of %p", vwin);
+ vwm->focused_origin = vwin; /* for returning to on abort */
+ XGrabKeyboard(vwm->display, VWM_XROOT(vwm), False, GrabModeAsync, GrabModeAsync, CurrentTime);
+ key_is_grabbed = 1;
+ }
+
+ /* remember the symbol for repeater detection */
+ last_sym = sym;
+ last_state = keypress->state;
+}
diff --git a/src/key.h b/src/key.h
new file mode 100644
index 0000000..d0bbb15
--- /dev/null
+++ b/src/key.h
@@ -0,0 +1,11 @@
+#ifndef _KEY_H
+#define _KEY_H
+
+#include <X11/Xlib.h>
+
+typedef struct _vwm_t vwm_t;
+
+void vwm_key_released(vwm_t *vwm, Window win, XKeyReleasedEvent *keyrelease);
+void vwm_key_pressed(vwm_t *vwm, Window win, XKeyPressedEvent *keypress);
+
+#endif
diff --git a/src/launch.c b/src/launch.c
new file mode 100644
index 0000000..123e850
--- /dev/null
+++ b/src/launch.c
@@ -0,0 +1,45 @@
+/*
+ * \/\/\
+ *
+ * Copyright (C) 2012-2016 Vito Caputo - <vcaputo@gnugeneration.com>
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+ /* launching of external processes / X clients */
+
+#include <stdlib.h>
+#include <sys/resource.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "launch.h"
+#include "vwm.h"
+
+#define LAUNCHED_RELATIVE_PRIORITY 10 /* the wm priority plus this is used as the priority of launched processes */
+
+/* launch a child command specified in argv, mode decides if we wait for the child to exit before returning. */
+void vwm_launch(vwm_t *vwm, char **argv, vwm_launch_mode_t mode)
+{
+ /* XXX: in BG mode I double fork and let init inherit the orphan so I don't have to collect the return status */
+ if (mode == VWM_LAUNCH_MODE_FG || !fork()) {
+ if (!fork()) {
+ /* child */
+ setpriority(PRIO_PROCESS, getpid(), vwm->priority + LAUNCHED_RELATIVE_PRIORITY);
+ execvp(argv[0], argv);
+ }
+ if (mode == VWM_LAUNCH_MODE_BG) exit(0);
+ }
+ wait(NULL); /* TODO: could wait for the specific pid, particularly in FG mode ... */
+}
diff --git a/src/launch.h b/src/launch.h
new file mode 100644
index 0000000..0e279b0
--- /dev/null
+++ b/src/launch.h
@@ -0,0 +1,13 @@
+#ifndef _LAUNCH_H
+#define _LAUNCH_H
+
+typedef struct _vwm_t vwm_t;
+
+typedef enum _vwm_launch_mode_t {
+ VWM_LAUNCH_MODE_FG,
+ VWM_LAUNCH_MODE_BG,
+} vwm_launch_mode_t;
+
+void vwm_launch(vwm_t *vwm, char **argv, vwm_launch_mode_t mode);
+
+#endif
diff --git a/launchers.def b/src/launchers.def
index 2c63f96..2c63f96 100644
--- a/launchers.def
+++ b/src/launchers.def
diff --git a/libvmon/LICENSE b/src/libvmon/LICENSE
index 94a9ed0..94a9ed0 100644
--- a/libvmon/LICENSE
+++ b/src/libvmon/LICENSE
diff --git a/libvmon/bitmap.h b/src/libvmon/bitmap.h
index eacb97d..eacb97d 100644
--- a/libvmon/bitmap.h
+++ b/src/libvmon/bitmap.h
diff --git a/libvmon/defs/_begin.def b/src/libvmon/defs/_begin.def
index 047fd4d..047fd4d 100644
--- a/libvmon/defs/_begin.def
+++ b/src/libvmon/defs/_begin.def
diff --git a/libvmon/defs/_end.def b/src/libvmon/defs/_end.def
index 9243349..9243349 100644
--- a/libvmon/defs/_end.def
+++ b/src/libvmon/defs/_end.def
diff --git a/libvmon/defs/proc_files.def b/src/libvmon/defs/proc_files.def
index 6e3db98..6e3db98 100644
--- a/libvmon/defs/proc_files.def
+++ b/src/libvmon/defs/proc_files.def
diff --git a/libvmon/defs/proc_io.def b/src/libvmon/defs/proc_io.def
index 0e1776d..0e1776d 100644
--- a/libvmon/defs/proc_io.def
+++ b/src/libvmon/defs/proc_io.def
diff --git a/libvmon/defs/proc_stat.def b/src/libvmon/defs/proc_stat.def
index db704e9..db704e9 100644
--- a/libvmon/defs/proc_stat.def
+++ b/src/libvmon/defs/proc_stat.def
diff --git a/libvmon/defs/proc_vm.def b/src/libvmon/defs/proc_vm.def
index 9028272..9028272 100644
--- a/libvmon/defs/proc_vm.def
+++ b/src/libvmon/defs/proc_vm.def
diff --git a/libvmon/defs/proc_wants.def b/src/libvmon/defs/proc_wants.def
index 7f02602..7f02602 100644
--- a/libvmon/defs/proc_wants.def
+++ b/src/libvmon/defs/proc_wants.def
diff --git a/libvmon/defs/sys_stat.def b/src/libvmon/defs/sys_stat.def
index ef8b9a7..ef8b9a7 100644
--- a/libvmon/defs/sys_stat.def
+++ b/src/libvmon/defs/sys_stat.def
diff --git a/libvmon/defs/sys_vm.def b/src/libvmon/defs/sys_vm.def
index 33b4d3f..33b4d3f 100644
--- a/libvmon/defs/sys_vm.def
+++ b/src/libvmon/defs/sys_vm.def
diff --git a/libvmon/defs/sys_wants.def b/src/libvmon/defs/sys_wants.def
index 750d7bb..750d7bb 100644
--- a/libvmon/defs/sys_wants.def
+++ b/src/libvmon/defs/sys_wants.def
diff --git a/libvmon/vmon.c b/src/libvmon/vmon.c
index 7d2b2a0..7d2b2a0 100644
--- a/libvmon/vmon.c
+++ b/src/libvmon/vmon.c
diff --git a/libvmon/vmon.h b/src/libvmon/vmon.h
index 06a062b..06a062b 100644
--- a/libvmon/vmon.h
+++ b/src/libvmon/vmon.h
diff --git a/list.h b/src/list.h
index 48bca36..48bca36 100644
--- a/list.h
+++ b/src/list.h
diff --git a/src/logo.c b/src/logo.c
new file mode 100644
index 0000000..8fe7291
--- /dev/null
+++ b/src/logo.c
@@ -0,0 +1,66 @@
+/*
+ * \/\/\
+ *
+ * Copyright (C) 2012-2016 Vito Caputo - <vcaputo@gnugeneration.com>
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <unistd.h>
+#include <X11/Xlib.h>
+
+#include "screen.h"
+#include "vwm.h"
+
+ /* startup logo */
+
+/* animated \/\/\ logo done with simple XOR'd lines, a display of the WM being started and ready */
+#define VWM_LOGO_POINTS 6
+void vwm_draw_logo(vwm_t *vwm)
+{
+ int i;
+ unsigned int width, height, yoff, xoff;
+ XPoint points[VWM_LOGO_POINTS];
+ const vwm_screen_t *scr = vwm_screen_find(vwm, VWM_SCREEN_REL_POINTER);
+
+ XGrabServer(vwm->display);
+
+ /* use the dimensions of the pointer-containing screen */
+ width = scr->width;
+ height = scr->height;
+ xoff = scr->x_org;
+ yoff = scr->y_org + ((float)height * .333);
+ height /= 3;
+
+ /* the logo gets shrunken vertically until it's essentially a flat line */
+ while (height -= 2) {
+ /* scale and center the points to the screen size */
+ for (i = 0; i < VWM_LOGO_POINTS; i++) {
+ points[i].x = xoff + (i * .2 * (float)width);
+ points[i].y = (i % 2 * (float)height) + yoff;
+ }
+
+ XDrawLines(vwm->display, VWM_XROOT(vwm), vwm->gc, points, sizeof(points) / sizeof(XPoint), CoordModeOrigin);
+ XFlush(vwm->display);
+ usleep(3333);
+ XDrawLines(vwm->display, VWM_XROOT(vwm), vwm->gc, points, sizeof(points) / sizeof(XPoint), CoordModeOrigin);
+ XFlush(vwm->display);
+
+ /* the width is shrunken as well, but only by as much as it is tall */
+ yoff++;
+ width -= 4;
+ xoff += 2;
+ }
+
+ XUngrabServer(vwm->display);
+}
diff --git a/src/logo.h b/src/logo.h
new file mode 100644
index 0000000..51995c9
--- /dev/null
+++ b/src/logo.h
@@ -0,0 +1,8 @@
+#ifndef _LOGO_H
+#define _LOGO_H
+
+typedef struct _vwm_t vwm_t;
+
+void vwm_draw_logo(vwm_t *vwm);
+
+#endif
diff --git a/src/overlay.c b/src/overlay.c
new file mode 100644
index 0000000..aeffd8e
--- /dev/null
+++ b/src/overlay.c
@@ -0,0 +1,960 @@
+/*
+ * \/\/\
+ *
+ * Copyright (C) 2012-2016 Vito Caputo - <vcaputo@gnugeneration.com>
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/* libvmon integration, warning: this gets a little crazy especially in the rendering. */
+
+#include <X11/Xatom.h>
+#include <X11/Xlib.h>
+#include <X11/extensions/Xfixes.h>
+#include <X11/extensions/Xrender.h>
+#include <stdlib.h>
+#include <sys/time.h>
+
+#include "composite.h"
+#include "libvmon/vmon.h"
+#include "list.h"
+#include "overlay.h"
+#include "vwm.h"
+#include "xwindow.h"
+
+/* TODO: move to overlay.h */
+#define OVERLAY_MASK_DEPTH 8 /* XXX: 1 would save memory, but Xorg isn't good at it */
+#define OVERLAY_MASK_FORMAT PictStandardA8
+#define OVERLAY_FIXED_FONT "-misc-fixed-medium-r-semicondensed--13-120-75-75-c-60-iso10646-1"
+#define OVERLAY_ROW_HEIGHT 15 /* this should always be larger than the font height */
+#define OVERLAY_GRAPH_MIN_WIDTH 200 /* always create graphs at least this large */
+#define OVERLAY_GRAPH_MIN_HEIGHT (4 * OVERLAY_ROW_HEIGHT)
+#define OVERLAY_ISTHREAD_ARGV "~" /* use this string to mark threads in the argv field */
+#define OVERLAY_NOCOMM_ARGV "#missed it!" /* use this string to substitute the command when missing in argv field */
+
+
+/* libvmon */
+static struct timeval maybe_sample, last_sample, this_sample = {0,0};
+static typeof(((vmon_sys_stat_t *)0)->user) last_user_cpu;
+static typeof(((vmon_sys_stat_t *)0)->system) last_system_cpu;
+static unsigned long long last_total, this_total, total_delta;
+static unsigned long long last_idle, last_iowait, idle_delta, iowait_delta;
+static vmon_t vmon;
+
+static float sampling_intervals[] = {
+ 1, /* ~1Hz */
+ .1, /* ~10Hz */
+ .05, /* ~20Hz */
+ .025, /* ~40Hz */
+ .01666}; /* ~60Hz */
+static int prev_sampling_interval = 1, sampling_interval = 1;
+
+/* space we need for every process being monitored */
+typedef struct _vwm_perproc_ctxt_t {
+ typeof(vmon.generation) generation;
+ typeof(((vmon_proc_stat_t *)0)->utime) last_utime;
+ typeof(((vmon_proc_stat_t *)0)->stime) last_stime;
+ typeof(((vmon_proc_stat_t *)0)->utime) utime_delta;
+ typeof(((vmon_proc_stat_t *)0)->stime) stime_delta;
+} vwm_perproc_ctxt_t;
+
+/* Compositing / Overlays */
+static XFontStruct *overlay_font;
+static GC text_gc;
+static XRenderPictureAttributes pa_repeat = { .repeat = 1 };
+static XRenderPictureAttributes pa_no_repeat = { .repeat = 0 };
+static Picture overlay_shadow_fill, /* TODO: the repetition here smells like an XMacro waiting to happen */
+ overlay_text_fill,
+ overlay_bg_fill,
+ overlay_snowflakes_text_fill,
+ overlay_grapha_fill,
+ overlay_graphb_fill,
+ overlay_finish_fill;
+static XRenderColor overlay_visible_color = { 0xffff, 0xffff, 0xffff, 0xffff },
+ overlay_shadow_color = { 0x0000, 0x0000, 0x0000, 0x8800},
+ overlay_bg_color = { 0x0, 0x1000, 0x0, 0x9000},
+ overlay_div_color = { 0x2000, 0x3000, 0x2000, 0x9000},
+ overlay_snowflakes_visible_color = { 0xd000, 0xd000, 0xd000, 0x8000 },
+ overlay_trans_color = {0x00, 0x00, 0x00, 0x00},
+ overlay_grapha_color = { 0xff00, 0x0000, 0x0000, 0x3000 }, /* ~red */
+ overlay_graphb_color = { 0x0000, 0xffff, 0xffff, 0x3000 }; /* ~cyan */
+
+
+/* we need a copy of this pointer for the vmon callback :( */
+static vwm_t *vwm_ptr;
+
+/* moves what's below a given row up above it if specified, the row becoming discarded */
+static void snowflake_row(vwm_t *vwm, vwm_xwindow_t *xwin, Picture pic, int copy, int row)
+{
+ VWM_TRACE("pid=%i xwin=%p row=%i copy=%i heirarhcy_end=%i", xwin->monitor->pid, xwin, row, copy, xwin->overlay.heirarchy_end);
+
+ if (copy) {
+ /* copy row to tmp */
+ XRenderComposite(vwm->display, PictOpSrc, pic, None, xwin->overlay.tmp_picture,
+ 0, row * OVERLAY_ROW_HEIGHT, /* src */
+ 0, 0, /* mask */
+ 0, 0, /* dest */
+ xwin->overlay.width, OVERLAY_ROW_HEIGHT); /* dimensions */
+ }
+
+ /* shift up */
+ XRenderChangePicture(vwm->display, pic, CPRepeat, &pa_no_repeat);
+ XRenderComposite(vwm->display, PictOpSrc, pic, None, pic,
+ 0, (1 + row) * OVERLAY_ROW_HEIGHT, /* src */
+ 0, 0, /* mask */
+ 0, row * OVERLAY_ROW_HEIGHT, /* dest */
+ xwin->overlay.width, (1 + xwin->overlay.heirarchy_end) * OVERLAY_ROW_HEIGHT - (1 + row) * OVERLAY_ROW_HEIGHT); /* dimensions */
+ XRenderChangePicture(vwm->display, pic, CPRepeat, &pa_repeat);
+
+ if (copy) {
+ /* copy tmp to top of snowflakes */
+ XRenderComposite(vwm->display, PictOpSrc, xwin->overlay.tmp_picture, None, pic,
+ 0, 0, /* src */
+ 0, 0, /* mask */
+ 0, (xwin->overlay.heirarchy_end) * OVERLAY_ROW_HEIGHT, /* dest */
+ xwin->overlay.width, OVERLAY_ROW_HEIGHT); /* dimensions */
+ } else {
+ /* clear the snowflake row */
+ XRenderFillRectangle(vwm->display, PictOpSrc, pic, &overlay_trans_color,
+ 0, (xwin->overlay.heirarchy_end) * OVERLAY_ROW_HEIGHT, /* dest */
+ xwin->overlay.width, OVERLAY_ROW_HEIGHT); /* dimensions */
+ }
+}
+
+/* XXX TODO libvmon automagic children following races with explicit X client pid monitoring with different outcomes, it should be irrelevant which wins,
+ * currently the only visible difference is the snowflakes gap (heirarchy_end) varies, which is why I haven't bothered to fix it, I barely even notice.
+ */
+
+/* shifts what's below a given row down a row, and clears the row, preparing it for populating */
+static void allocate_row(vwm_t *vwm, vwm_xwindow_t *xwin, Picture pic, int row)
+{
+ VWM_TRACE("pid=%i xwin=%p row=%i", xwin->monitor->pid, xwin, row);
+
+ /* shift everything below the row down */
+ XRenderComposite(vwm->display, PictOpSrc, pic, None, pic,
+ 0, row * OVERLAY_ROW_HEIGHT, /* src */
+ 0, 0, /* mask */
+ 0, (1 + row) * OVERLAY_ROW_HEIGHT, /* dest */
+ xwin->overlay.width, xwin->overlay.height - (1 + row) * OVERLAY_ROW_HEIGHT); /* dimensions */
+ /* fill the space created with transparent pixels */
+ XRenderFillRectangle(vwm->display, PictOpSrc, pic, &overlay_trans_color,
+ 0, row * OVERLAY_ROW_HEIGHT, /* dest */
+ xwin->overlay.width, OVERLAY_ROW_HEIGHT); /* dimensions */
+}
+
+
+/* shadow a row from the text layer in the shadow layer */
+static void shadow_row(vwm_t *vwm, vwm_xwindow_t *xwin, int row)
+{
+ /* the current technique for creating the shadow is to simply render the text at +1/-1 pixel offsets on both axis in translucent black */
+ XRenderComposite(vwm->display, PictOpSrc, overlay_shadow_fill, xwin->overlay.text_picture, xwin->overlay.shadow_picture,
+ 0, 0,
+ -1, row * OVERLAY_ROW_HEIGHT,
+ 0, row * OVERLAY_ROW_HEIGHT,
+ xwin->attrs.width, OVERLAY_ROW_HEIGHT);
+
+ XRenderComposite(vwm->display, PictOpOver, overlay_shadow_fill, xwin->overlay.text_picture, xwin->overlay.shadow_picture,
+ 0, 0,
+ 0, -1 + row * OVERLAY_ROW_HEIGHT,
+ 0, row * OVERLAY_ROW_HEIGHT,
+ xwin->attrs.width, OVERLAY_ROW_HEIGHT);
+
+ XRenderComposite(vwm->display, PictOpOver, overlay_shadow_fill, xwin->overlay.text_picture, xwin->overlay.shadow_picture,
+ 0, 0,
+ 1, row * OVERLAY_ROW_HEIGHT,
+ 0, row * OVERLAY_ROW_HEIGHT,
+ xwin->attrs.width, OVERLAY_ROW_HEIGHT);
+
+ XRenderComposite(vwm->display, PictOpOver, overlay_shadow_fill, xwin->overlay.text_picture, xwin->overlay.shadow_picture,
+ 0, 0,
+ 0, 1 + row * OVERLAY_ROW_HEIGHT,
+ 0, row * OVERLAY_ROW_HEIGHT,
+ xwin->attrs.width, OVERLAY_ROW_HEIGHT);
+}
+
+
+/* simple helper to map the vmon per-proc argv array into an XTextItem array, deals with threads vs. processes and the possibility of the comm field not getting read in before the process exited... */
+static void argv2xtext(vmon_proc_t *proc, XTextItem *items, int *nr_items) /* XXX TODO: reallocate items when too small... */
+{
+ int i;
+ int nr = 0;
+
+ if (proc->is_thread) { /* stick the thread marker at the start of threads */
+ items[0].nchars = sizeof(OVERLAY_ISTHREAD_ARGV) - 1;
+ items[0].chars = OVERLAY_ISTHREAD_ARGV;
+ items[0].delta = 4;
+ items[0].font = None;
+ nr++;
+ }
+
+ if (((vmon_proc_stat_t *)proc->stores[VMON_STORE_PROC_STAT])->comm.len) {
+ items[nr].nchars = ((vmon_proc_stat_t *)proc->stores[VMON_STORE_PROC_STAT])->comm.len - 1;
+ items[nr].chars = ((vmon_proc_stat_t *)proc->stores[VMON_STORE_PROC_STAT])->comm.array;
+ } else {
+ /* sometimes a process is so ephemeral we don't manage to sample its comm, XXX TODO: we always have a pid, stringify it? */
+ items[nr].nchars = sizeof(OVERLAY_NOCOMM_ARGV) - 1;
+ items[nr].chars = OVERLAY_NOCOMM_ARGV;
+ }
+ items[nr].delta = 4;
+ items[nr].font = None;
+ nr++;
+
+ for (i = 1; i < ((vmon_proc_stat_t *)proc->stores[VMON_STORE_PROC_STAT])->argc; nr++, i++) {
+ items[nr].chars = ((vmon_proc_stat_t *)proc->stores[VMON_STORE_PROC_STAT])->argv[i];
+ items[nr].nchars = strlen(((vmon_proc_stat_t *)proc->stores[VMON_STORE_PROC_STAT])->argv[i]); /* TODO: libvmon should inform us of the length */
+ items[nr].delta = 4;
+ items[nr].font = None;
+ }
+
+ (*nr_items) = nr;
+}
+
+
+/* helper for counting number of existing descendants subtrees */
+static int count_rows(vmon_proc_t *proc) {
+ int count = 1; /* XXX maybe suppress proc->is_new? */
+ vmon_proc_t *child;
+
+ if (!proc->is_thread) {
+ list_for_each_entry(child, &proc->threads, threads) {
+ count += count_rows(child);
+ }
+ }
+
+ list_for_each_entry(child, &proc->children, siblings) {
+ count += count_rows(child);
+ }
+
+ return count;
+}
+
+
+/* recursive draw function for the consolidated version of the overlay rendering which also implements snowflakes */
+static void draw_overlay(vwm_t *vwm, vwm_xwindow_t *xwin, vmon_proc_t *proc, int *depth, int *row)
+{
+ vmon_proc_t *child;
+ vwm_perproc_ctxt_t *proc_ctxt = proc->foo;
+ vmon_proc_stat_t *proc_stat = proc->stores[VMON_STORE_PROC_STAT];
+
+ /* graph variables */
+ int a_height, b_height;
+ double utime_delta, stime_delta;
+
+ /* text variables */
+ char str[256];
+ int str_len;
+ XTextItem items[1024]; /* XXX TODO: dynamically allocate this and just keep it at the high water mark.. create a struct to encapsulate this, nr_items, and alloc_items... */
+ int nr_items;
+ int direction, ascent, descent;
+ XCharStruct charstruct;
+
+ if ((*row)) { /* except row 0 (Idle/IOWait graph), handle any stale and new processes/threads */
+ if (proc->is_stale) {
+ /* what to do when a process (subtree) has gone away */
+ static int in_stale = 0;
+ int in_stale_entrypoint = 0;
+
+ /* I snowflake the stale processes from the leaves up for a more intuitive snowflake order...
+ * (I expect the command at the root of the subtree to appear at the top of the snowflakes...) */
+ /* This does require that I do a separate forward recursion to determine the number of rows
+ * so I can correctly snowflake in reverse */
+ if (!in_stale) {
+ VWM_TRACE("entered stale at xwin=%p depth=%i row=%i", xwin, *depth, *row);
+ in_stale_entrypoint = in_stale = 1;
+ (*row) += count_rows(proc) - 1;
+ }
+
+ (*depth)++;
+ list_for_each_entry_prev(child, &proc->children, siblings) {
+ draw_overlay(vwm, xwin, child, depth, row);
+ (*row)--;
+ }
+
+ if (!proc->is_thread) {
+ list_for_each_entry_prev(child, &proc->threads, threads) {
+ draw_overlay(vwm, xwin, child, depth, row);
+ (*row)--;
+ }
+ }
+ (*depth)--;
+
+ VWM_TRACE("%i (%.*s) is stale @ depth %i row %i is_thread=%i", proc->pid,
+ ((vmon_proc_stat_t *)proc->stores[VMON_STORE_PROC_STAT])->comm.len - 1,
+ ((vmon_proc_stat_t *)proc->stores[VMON_STORE_PROC_STAT])->comm.array,
+ (*depth), (*row), proc->is_thread);
+
+ /* stamp the graphs with the finish line */
+ XRenderComposite(vwm->display, PictOpSrc, overlay_finish_fill, None, xwin->overlay.grapha_picture,
+ 0, 0, /* src x, y */
+ 0, 0, /* mask x, y */
+ xwin->overlay.phase, (*row) * OVERLAY_ROW_HEIGHT, /* dst x, y */
+ 1, OVERLAY_ROW_HEIGHT - 1);
+ XRenderComposite(vwm->display, PictOpSrc, overlay_finish_fill, None, xwin->overlay.graphb_picture,
+ 0, 0, /* src x, y */
+ 0, 0, /* mask x, y */
+ xwin->overlay.phase, (*row) * OVERLAY_ROW_HEIGHT, /* dst x, y */
+ 1, OVERLAY_ROW_HEIGHT - 1);
+
+ /* extract the row from the various layers */
+ snowflake_row(vwm, xwin, xwin->overlay.grapha_picture, 1, (*row));
+ snowflake_row(vwm, xwin, xwin->overlay.graphb_picture, 1, (*row));
+ snowflake_row(vwm, xwin, xwin->overlay.text_picture, 0, (*row));
+ snowflake_row(vwm, xwin, xwin->overlay.shadow_picture, 0, (*row));
+ xwin->overlay.snowflakes_cnt++;
+
+ /* stamp the name (and whatever else we include) into overlay.text_picture */
+ argv2xtext(proc, items, &nr_items);
+ XDrawText(vwm->display, xwin->overlay.text_pixmap, text_gc,
+ 5, (xwin->overlay.heirarchy_end + 1) * OVERLAY_ROW_HEIGHT - 3,/* dst x, y */
+ items, nr_items);
+ shadow_row(vwm, xwin, xwin->overlay.heirarchy_end);
+
+ xwin->overlay.heirarchy_end--;
+
+ if (in_stale_entrypoint) {
+ VWM_TRACE("exited stale at xwin=%p depth=%i row=%i", xwin, *depth, *row);
+ in_stale = 0;
+ }
+
+ return;
+ } else if (proc->is_new) {
+ /* what to do when a process has been introduced */
+ VWM_TRACE("%i is new", proc->pid);
+
+ allocate_row(vwm, xwin, xwin->overlay.grapha_picture, (*row));
+ allocate_row(vwm, xwin, xwin->overlay.graphb_picture, (*row));
+ allocate_row(vwm, xwin, xwin->overlay.text_picture, (*row));
+ allocate_row(vwm, xwin, xwin->overlay.shadow_picture, (*row));
+
+ xwin->overlay.heirarchy_end++;
+ }
+ }
+
+/* CPU utilization graphs */
+ if (!(*row)) {
+ /* XXX: sortof kludged in IOWait and Idle % @ row 0 */
+ stime_delta = iowait_delta;
+ utime_delta = idle_delta;
+ } else {
+ /* use the generation number to avoid recomputing this stuff for callbacks recurring on the same process in the same sample */
+ if (proc_ctxt->generation != vmon.generation) {
+ proc_ctxt->stime_delta = proc_stat->stime - proc_ctxt->last_stime;
+ proc_ctxt->utime_delta = proc_stat->utime - proc_ctxt->last_utime;
+ proc_ctxt->last_utime = proc_stat->utime;
+ proc_ctxt->last_stime = proc_stat->stime;
+
+ proc_ctxt->generation = vmon.generation;
+ }
+
+ if (proc->is_new) {
+ /* we need a minimum of two samples before we can compute a delta to plot,
+ * so we suppress that and instead mark the start of monitoring with an impossible 100% of both graph contexts, a starting line. */
+ stime_delta = utime_delta = total_delta;
+ } else {
+ stime_delta = proc_ctxt->stime_delta;
+ utime_delta = proc_ctxt->utime_delta;
+ }
+ }
+
+ /* compute the bar heights for this sample */
+ a_height = (stime_delta / total_delta * (double)(OVERLAY_ROW_HEIGHT - 1)); /* give up 1 pixel for the div */
+ b_height = (utime_delta / total_delta * (double)(OVERLAY_ROW_HEIGHT - 1));
+
+ /* round up to 1 pixel when the scaled result is a fraction less than 1,
+ * I want to at least see 1 pixel blips for the slightest cpu utilization */
+ if (stime_delta && !a_height) a_height = 1;
+ if (utime_delta && !b_height) b_height = 1;
+
+ /* draw the two bars for this sample at the current phase in the graphs, note the first is ceiling-based, second floor-based */
+ XRenderFillRectangle(vwm->display, PictOpSrc, xwin->overlay.grapha_picture, &overlay_visible_color,
+ xwin->overlay.phase, (*row) * OVERLAY_ROW_HEIGHT, /* dst x, y */
+ 1, a_height); /* dst w, h */
+ XRenderFillRectangle(vwm->display, PictOpSrc, xwin->overlay.graphb_picture, &overlay_visible_color,
+ xwin->overlay.phase, (*row) * OVERLAY_ROW_HEIGHT + (OVERLAY_ROW_HEIGHT - b_height) - 1, /* dst x, y */
+ 1, b_height); /* dst w, h */
+
+ if (!(*row)) {
+ /* here's where the Idle/IOWait row drawing concludes */
+ if (1 /* FIXME TODO compositing_mode*/) {
+ snprintf(str, sizeof(str), "\\/\\/\\ %2iHz %n", (int)(sampling_interval < 0 ? 0 : 1 / sampling_intervals[sampling_interval]), &str_len);
+ /* TODO: I clear and redraw this row every time, which is unnecessary, small optimization would be to only do so when:
+ * - overlay resized, and then constrain the clear to the affected width
+ * - Hz changed
+ */
+ XRenderFillRectangle(vwm->display, PictOpSrc, xwin->overlay.text_picture, &overlay_trans_color,
+ 0, 0, /* dst x, y */
+ xwin->attrs.width, OVERLAY_ROW_HEIGHT); /* dst w, h */
+ XTextExtents(overlay_font, str, str_len, &direction, &ascent, &descent, &charstruct);
+ XDrawString(vwm->display, xwin->overlay.text_pixmap, text_gc,
+ xwin->attrs.width - charstruct.width, OVERLAY_ROW_HEIGHT - 3, /* dst x, y */
+ str, str_len);
+ shadow_row(vwm, xwin, 0);
+ }
+ (*row)++;
+ draw_overlay(vwm, xwin, proc, depth, row);
+ return;
+ }
+
+/* process heirarchy text and accompanying per-process details like wchan/pid/state... */
+ if (1 /* FIXME TODO compositing_mode */) { /* this stuff can be skipped when monitors aren't visible */
+ /* TODO: make the columns interactively configurable @ runtime */
+ if (!proc->is_new) {
+ /* XXX for now always clear the row, this should be capable of being optimized in the future (if the datums driving the text haven't changed...) */
+ XRenderFillRectangle(vwm->display, PictOpSrc, xwin->overlay.text_picture, &overlay_trans_color,
+ 0, (*row) * OVERLAY_ROW_HEIGHT, /* dst x, y */
+ xwin->overlay.width, OVERLAY_ROW_HEIGHT); /* dst w, h */
+ }
+
+ /* put the process' wchan, state, and PID columns @ the far right */
+ if (proc->is_thread || list_empty(&proc->threads)) { /* only threads or non-threaded processes include the wchan and state */
+ snprintf(str, sizeof(str), " %.*s %5i %c %n",
+ proc_stat->wchan.len,
+ proc_stat->wchan.len == 1 && proc_stat->wchan.array[0] == '0' ? "-" : proc_stat->wchan.array,
+ proc->pid,
+ proc_stat->state,
+ &str_len);
+ } else { /* we're a process having threads, suppress the wchan and state, as they will be displayed for the thread of same pid */
+ snprintf(str, sizeof(str), " %5i %n", proc->pid, &str_len);
+ }
+
+ XTextExtents(overlay_font, str, str_len, &direction, &ascent, &descent, &charstruct);
+
+ /* the process' comm label indented according to depth, followed with their respective argv's */
+ argv2xtext(proc, items, &nr_items);
+ XDrawText(vwm->display, xwin->overlay.text_pixmap, text_gc,
+ (*depth) * (OVERLAY_ROW_HEIGHT / 2), ((*row) + 1) * OVERLAY_ROW_HEIGHT - 3, /* dst x, y */
+ items, nr_items);
+
+ /* ensure the area for the rest of the stuff is cleared, we don't put much text into thread rows so skip it for those. */
+ if (!proc->is_thread) {
+ XRenderFillRectangle(vwm->display, PictOpSrc, xwin->overlay.text_picture, &overlay_trans_color,
+ xwin->attrs.width - charstruct.width, (*row) * OVERLAY_ROW_HEIGHT, /* dst x,y */
+ xwin->overlay.width - (xwin->attrs.width - charstruct.width), OVERLAY_ROW_HEIGHT); /* dst w,h */
+ }
+
+ XDrawString(vwm->display, xwin->overlay.text_pixmap, text_gc,
+ xwin->attrs.width - charstruct.width, ((*row) + 1) * OVERLAY_ROW_HEIGHT - 3, /* dst x, y */
+ str, str_len);
+
+ /* only if this process isn't the root process @ the window shall we consider all relational drawing conditions */
+ if (proc != xwin->monitor) {
+ vmon_proc_t *ancestor, *sibling, *last_sibling = NULL;
+ struct list_head *rem;
+ int needs_tee = 0;
+ int bar_x = 0, bar_y = 0;
+ int sub;
+
+ /* XXX: everything done in this code block only dirties _this_ process' row in the rendered overlay output */
+
+ /* walk up the ancestors until reaching xwin->monitor, any ancestors we encounter which have more siblings we draw a vertical bar for */
+ /* this draws the |'s in something like: | | | | comm */
+ for (sub = 1, ancestor = proc->parent; ancestor && ancestor != xwin->monitor; ancestor = ancestor->parent) {
+ sub++;
+ bar_x = ((*depth) - sub) * (OVERLAY_ROW_HEIGHT / 2) + 4;
+ bar_y = ((*row) + 1) * OVERLAY_ROW_HEIGHT;
+
+ /* determine if the ancestor has remaining siblings which are not stale, if so, draw a connecting bar at its depth */
+ for (rem = ancestor->siblings.next; rem != &ancestor->parent->children; rem = rem->next) {
+ if (!(list_entry(rem, vmon_proc_t, siblings)->is_stale)) {
+ XDrawLine(vwm->display, xwin->overlay.text_pixmap, text_gc,
+ bar_x, bar_y - OVERLAY_ROW_HEIGHT, /* dst x1, y1 */
+ bar_x, bar_y); /* dst x2, y2 (vertical line) */
+ break; /* stop looking for more siblings at this ancestor when we find one that isn't stale */
+ }
+ }
+ }
+
+ /* determine if _any_ of our siblings have children requiring us to draw a tee immediately before our comm string.
+ * The only sibling which doesn't cause this to happen is the last one in the children list, if it has children it has no impact on its remaining
+ * siblings, as there are none.
+ *
+ * This draws the + in something like: | | | | +comm
+ */
+
+ /* find the last sibling (this has to be done due to the potential for stale siblings at the tail, and we'd rather not repeatedly check for it) */
+ list_for_each_entry(sibling, &proc->parent->children, siblings) {
+ if (!sibling->is_stale) last_sibling = sibling;
+ }
+
+ /* now look for siblings with non-stale children to determine if a tee is needed, ignoring the last sibling */
+ list_for_each_entry(sibling, &proc->parent->children, siblings) {
+ /* skip stale siblings, they aren't interesting as they're invisible, and the last sibling has no bearing on wether we tee or not. */
+ if (sibling->is_stale || sibling == last_sibling) continue;
+
+ /* if any of the other siblings have children which are not stale, put a tee in front of our name, but ignore stale children */
+ list_for_each_entry(child, &sibling->children, siblings) {
+ if (!child->is_stale) {
+ needs_tee = 1;
+ break;
+ }
+ }
+
+ /* if we still don't think we need a tee, check if there are threads */
+ if (!needs_tee) {
+ list_for_each_entry(child, &sibling->threads, threads) {
+ if (!child->is_stale) {
+ needs_tee = 1;
+ break;
+ }
+ }
+ }
+
+ /* found a tee is necessary, all that's left is to determine if the tee is a corner and draw it accordingly, stopping the search. */
+ if (needs_tee) {
+ bar_x = ((*depth) - 1) * (OVERLAY_ROW_HEIGHT / 2) + 4;
+
+ /* if we're the last sibling, corner the tee by shortening the vbar */
+ if (proc == last_sibling) {
+ XDrawLine(vwm->display, xwin->overlay.text_pixmap, text_gc,
+ bar_x, bar_y - OVERLAY_ROW_HEIGHT, /* dst x1, y1 */
+ bar_x, bar_y - 4); /* dst x2, y2 (vertical bar) */
+ } else {
+ XDrawLine(vwm->display, xwin->overlay.text_pixmap, text_gc,
+ bar_x, bar_y - OVERLAY_ROW_HEIGHT, /* dst x1, y1 */
+ bar_x, bar_y); /* dst x2, y2 (vertical bar) */
+ }
+
+ XDrawLine(vwm->display, xwin->overlay.text_pixmap, text_gc,
+ bar_x, bar_y - 4, /* dst x1, y1 */
+ bar_x + 2, bar_y - 4); /* dst x2, y2 (horizontal bar) */
+
+ /* terminate the outer sibling loop upon drawing the tee... */
+ break;
+ }
+ }
+ }
+ shadow_row(vwm, xwin, (*row));
+ }
+
+ (*row)++;
+
+ /* recur any threads first, then any children processes */
+ (*depth)++;
+ if (!proc->is_thread) { /* XXX: the threads member serves as the list head only when not a thread */
+ list_for_each_entry(child, &proc->threads, threads) {
+ draw_overlay(vwm, xwin, child, depth, row);
+ }
+ }
+
+ list_for_each_entry(child, &proc->children, siblings) {
+ draw_overlay(vwm, xwin, child, depth, row);
+ }
+ (*depth)--;
+}
+
+
+/* consolidated version of overlay text and graph rendering, makes snowflakes integration cleaner, this always gets called regadless of the overlays mode */
+static void maintain_overlay(vwm_t *vwm, vwm_xwindow_t *xwin)
+{
+ int row = 0, depth = 0;
+
+ if (!xwin->monitor || !xwin->monitor->stores[VMON_STORE_PROC_STAT]) return;
+
+ /* TODO:
+ * I side effect of responding to window resizes in this function is there's a latency proportional to the current sample_interval.
+ * Something to fix is to resize the overlays when the window resizes.
+ * However, simply resizing the overlays is insufficient. Their contents need to be redrawn in the new dimensions, this is where it
+ * gets annoying. The current maintain/draw_overlay makes assumptions about being run from the periodic vmon per-process callback.
+ * There needs to be a redraw mode added where draw_overlay is just reconstructing the current state, which requires that we suppress
+ * the phase advance and in maintain_overlay() and just enter draw_overlay() to redraw everything for the same generation.
+ * So this probably requires some tweaking of draw_overlay() as well as maintain_overlay(). I want to be able tocall mainta_overlays()
+ * from anywhere, and have it detect if it's being called on the same generation or if the generation has advanced.
+ * For now, the monitors will just be a little latent in window resizes which is pretty harmless artifact.
+ */
+
+ /* if the window is larger than the overlays currently are, enlarge them */
+ if (xwin->attrs.width > xwin->overlay.width || xwin->attrs.height > xwin->overlay.height) {
+ vwm_overlay_t existing;
+ Pixmap pixmap;
+
+ existing = xwin->overlay;
+
+ xwin->overlay.width = MAX(xwin->overlay.width, MAX(xwin->attrs.width, OVERLAY_GRAPH_MIN_WIDTH));
+ xwin->overlay.height = MAX(xwin->overlay.height, MAX(xwin->attrs.height, OVERLAY_GRAPH_MIN_HEIGHT));
+
+ pixmap = XCreatePixmap(vwm->display, VWM_XROOT(vwm), xwin->overlay.width, xwin->overlay.height, OVERLAY_MASK_DEPTH);
+ xwin->overlay.grapha_picture = XRenderCreatePicture(vwm->display, pixmap, XRenderFindStandardFormat(vwm->display, OVERLAY_MASK_FORMAT), CPRepeat, &pa_repeat);
+ XFreePixmap(vwm->display, pixmap);
+ XRenderFillRectangle(vwm->display, PictOpSrc, xwin->overlay.grapha_picture, &overlay_trans_color, 0, 0, xwin->overlay.width, xwin->overlay.height);
+
+ pixmap = XCreatePixmap(vwm->display, VWM_XROOT(vwm), xwin->overlay.width, xwin->overlay.height, OVERLAY_MASK_DEPTH);
+ xwin->overlay.graphb_picture = XRenderCreatePicture(vwm->display, pixmap, XRenderFindStandardFormat(vwm->display, OVERLAY_MASK_FORMAT), CPRepeat, &pa_repeat);
+ XFreePixmap(vwm->display, pixmap);
+ XRenderFillRectangle(vwm->display, PictOpSrc, xwin->overlay.graphb_picture, &overlay_trans_color, 0, 0, xwin->overlay.width, xwin->overlay.height);
+
+ pixmap = XCreatePixmap(vwm->display, VWM_XROOT(vwm), xwin->overlay.width, OVERLAY_ROW_HEIGHT, OVERLAY_MASK_DEPTH);
+ xwin->overlay.tmp_picture = XRenderCreatePicture(vwm->display, pixmap, XRenderFindStandardFormat(vwm->display, OVERLAY_MASK_FORMAT), 0, NULL);
+ XFreePixmap(vwm->display, pixmap);
+
+ /* keep the text_pixmap reference around for XDrawText usage */
+ xwin->overlay.text_pixmap = XCreatePixmap(vwm->display, VWM_XROOT(vwm), xwin->overlay.width, xwin->overlay.height, OVERLAY_MASK_DEPTH);
+ xwin->overlay.text_picture = XRenderCreatePicture(vwm->display, xwin->overlay.text_pixmap, XRenderFindStandardFormat(vwm->display, OVERLAY_MASK_FORMAT), 0, NULL);
+ XRenderFillRectangle(vwm->display, PictOpSrc, xwin->overlay.text_picture, &overlay_trans_color, 0, 0, xwin->overlay.width, xwin->overlay.height);
+
+ pixmap = XCreatePixmap(vwm->display, VWM_XROOT(vwm), xwin->overlay.width, xwin->overlay.height, OVERLAY_MASK_DEPTH);
+ xwin->overlay.shadow_picture = XRenderCreatePicture(vwm->display, pixmap, XRenderFindStandardFormat(vwm->display, OVERLAY_MASK_FORMAT), 0, NULL);
+ XFreePixmap(vwm->display, pixmap);
+ XRenderFillRectangle(vwm->display, PictOpSrc, xwin->overlay.shadow_picture, &overlay_trans_color, 0, 0, xwin->overlay.width, xwin->overlay.height);
+
+ pixmap = XCreatePixmap(vwm->display, VWM_XROOT(vwm), xwin->overlay.width, xwin->overlay.height, 32);
+ xwin->overlay.picture = XRenderCreatePicture(vwm->display, pixmap, XRenderFindStandardFormat(vwm->display, PictStandardARGB32), 0, NULL);
+ XFreePixmap(vwm->display, pixmap);
+
+ if (existing.width) {
+ /* XXX: note the graph pictures are copied from their current phase in the x dimension */
+ XRenderComposite(vwm->display, PictOpSrc, existing.grapha_picture, None, xwin->overlay.grapha_picture,
+ existing.phase, 0, /* src x, y */
+ 0, 0, /* mask x, y */
+ 0, 0, /* dest x, y */
+ existing.width, existing.height);
+ XRenderComposite(vwm->display, PictOpSrc, existing.graphb_picture, None, xwin->overlay.graphb_picture,
+ existing.phase, 0, /* src x, y */
+ 0, 0, /* mask x, y */
+ 0, 0, /* dest x, y */
+ existing.width, existing.height);
+ XRenderComposite(vwm->display, PictOpSrc, existing.text_picture, None, xwin->overlay.text_picture,
+ 0, 0, /* src x, y */
+ 0, 0, /* mask x, y */
+ 0, 0, /* dest x, y */
+ existing.width, existing.height);
+ XRenderComposite(vwm->display, PictOpSrc, existing.shadow_picture, None, xwin->overlay.shadow_picture,
+ 0, 0, /* src x, y */
+ 0, 0, /* mask x, y */
+ 0, 0, /* dest x, y */
+ existing.width, existing.height);
+ XRenderComposite(vwm->display, PictOpSrc, existing.picture, None, xwin->overlay.picture,
+ 0, 0, /* src x, y */
+ 0, 0, /* mask x, y */
+ 0, 0, /* dest x, y */
+ existing.width, existing.height);
+ xwin->overlay.phase = 0; /* having unrolled the existing graph[ab] pictures into the larger ones, phase is reset to 0 */
+ XRenderFreePicture(vwm->display, existing.grapha_picture);
+ XRenderFreePicture(vwm->display, existing.graphb_picture);
+ XRenderFreePicture(vwm->display, existing.tmp_picture);
+ XRenderFreePicture(vwm->display, existing.text_picture);
+ XFreePixmap(vwm->display, existing.text_pixmap);
+ XRenderFreePicture(vwm->display, existing.shadow_picture);
+ XRenderFreePicture(vwm->display, existing.picture);
+ }
+ }
+
+ xwin->overlay.phase += (xwin->overlay.width - 1); /* simply change this to .phase++ to scroll the other direction */
+ xwin->overlay.phase %= xwin->overlay.width;
+ XRenderFillRectangle(vwm->display, PictOpSrc, xwin->overlay.grapha_picture, &overlay_trans_color, xwin->overlay.phase, 0, 1, xwin->overlay.height);
+ XRenderFillRectangle(vwm->display, PictOpSrc, xwin->overlay.graphb_picture, &overlay_trans_color, xwin->overlay.phase, 0, 1, xwin->overlay.height);
+
+ /* recursively draw the monitored processes to the overlay */
+ draw_overlay(vwm, xwin, xwin->monitor, &depth, &row);
+}
+
+
+/* this callback gets invoked at sample time for every process we've explicitly monitored (not autofollowed children/threads)
+ * It's where we update the cumulative data for all windows, including the graph masks, regardless of their visibility
+ * It's also where we compose the graphs and text for visible windows into a picture ready for compositing with the window contents */
+static void proc_sample_callback(vmon_t *vmon, vmon_proc_t *proc, vwm_xwindow_t *xwin)
+{
+ //VWM_TRACE("proc=%p xwin=%p", proc, xwin);
+ /* render the various always-updated overlays, this is the component we do regardless of the overlays mode and window visibility,
+ * essentially the incrementally rendered/historic components */
+ maintain_overlay(vwm_ptr, xwin);
+
+ /* if we've updated overlays for a mapped window, kick the compositor to do the costly parts of overlay drawing and compositing. */
+ if (vwm_xwin_is_mapped(vwm_ptr, xwin)) vwm_composite_repaint_needed(vwm_ptr);
+}
+
+
+/* this callback gets invoked at sample time once "per sys" */
+static void sample_callback(vmon_t *_vmon)
+{
+ vmon_sys_stat_t *sys_stat = vmon.stores[VMON_STORE_SYS_STAT];
+ this_total = sys_stat->user + sys_stat->nice + sys_stat->system +
+ sys_stat->idle + sys_stat->iowait + sys_stat->irq +
+ sys_stat->softirq + sys_stat->steal + sys_stat->guest;
+
+ total_delta = this_total - last_total;
+ idle_delta = sys_stat->idle - last_idle;
+ iowait_delta = sys_stat->iowait - last_iowait;
+}
+
+
+/* these callbacks are invoked by the vmon library when process instances become monitored/unmonitored */
+static void vmon_ctor_cb(vmon_t *vmon, vmon_proc_t *proc)
+{
+ VWM_TRACE("proc->pid=%i", proc->pid);
+ proc->foo = calloc(1, sizeof(vwm_perproc_ctxt_t));
+}
+
+
+static void vmon_dtor_cb(vmon_t *vmon, vmon_proc_t *proc)
+{
+ VWM_TRACE("proc->pid=%i", proc->pid);
+ if (proc->foo) {
+ free(proc->foo);
+ proc->foo = NULL;
+ }
+}
+
+
+/* return the composed height of the overlay */
+int vwm_overlay_xwin_composed_height(vwm_t *vwm, vwm_xwindow_t *xwin)
+{
+ int snowflakes = xwin->overlay.snowflakes_cnt ? 1 + xwin->overlay.snowflakes_cnt : 0; /* don't include the separator row if there are no snowflakes */
+
+ return MIN((xwin->overlay.heirarchy_end + snowflakes) * OVERLAY_ROW_HEIGHT, xwin->attrs.height);
+}
+
+/* reset snowflakes on the specified window */
+void vwm_overlay_xwin_reset_snowflakes(vwm_t *vwm, vwm_xwindow_t *xwin) {
+ if (xwin->overlay.snowflakes_cnt) {
+ xwin->overlay.snowflakes_cnt = 0;
+ vwm_composite_damage_win(vwm, xwin);
+ }
+}
+
+static void init_overlay(vwm_t *vwm) {
+ static int initialized;
+ Window bitmask;
+
+ if (initialized) return;
+ initialized = 1;
+
+ /* we stow the vwm pointer so the vmon callback can access it, rather than allocating something
+ * to encapsulate the xwin and vwm pointers just for the callback... */
+ vwm_ptr = vwm;
+
+ /* initialize libvmon */
+ vmon_init(&vmon, VMON_FLAG_2PASS, VMON_WANT_SYS_STAT, (VMON_WANT_PROC_STAT | VMON_WANT_PROC_FOLLOW_CHILDREN | VMON_WANT_PROC_FOLLOW_THREADS));
+ vmon.proc_ctor_cb = vmon_ctor_cb;
+ vmon.proc_dtor_cb = vmon_dtor_cb;
+ vmon.sample_cb = sample_callback;
+ gettimeofday(&this_sample, NULL);
+
+ /* get all the text and graphics stuff setup for overlays */
+ overlay_font = XLoadQueryFont(vwm->display, OVERLAY_FIXED_FONT);
+
+ /* create a GC for rendering the text using Xlib into the text overlay stencils */
+ bitmask = XCreatePixmap(vwm->display, VWM_XROOT(vwm), 1, 1, OVERLAY_MASK_DEPTH);
+ text_gc = XCreateGC(vwm->display, bitmask, 0, NULL);
+ XSetForeground(vwm->display, text_gc, WhitePixel(vwm->display, vwm->screen_num));
+ XFreePixmap(vwm->display, bitmask);
+
+ /* create some repeating source fill pictures for drawing through the text and graph stencils */
+ bitmask = XCreatePixmap(vwm->display, VWM_XROOT(vwm), 1, 1, 32);
+ overlay_text_fill = XRenderCreatePicture(vwm->display, bitmask, XRenderFindStandardFormat(vwm->display, PictStandardARGB32), CPRepeat, &pa_repeat);
+ XRenderFillRectangle(vwm->display, PictOpSrc, overlay_text_fill, &overlay_visible_color, 0, 0, 1, 1);
+
+ bitmask = XCreatePixmap(vwm->display, VWM_XROOT(vwm), 1, 1, 32);
+ overlay_shadow_fill = XRenderCreatePicture(vwm->display, bitmask, XRenderFindStandardFormat(vwm->display, PictStandardARGB32), CPRepeat, &pa_repeat);
+ XRenderFillRectangle(vwm->display, PictOpSrc, overlay_shadow_fill, &overlay_shadow_color, 0, 0, 1, 1);
+
+ bitmask = XCreatePixmap(vwm->display, VWM_XROOT(vwm), 1, OVERLAY_ROW_HEIGHT, 32);
+ overlay_bg_fill = XRenderCreatePicture(vwm->display, bitmask, XRenderFindStandardFormat(vwm->display, PictStandardARGB32), CPRepeat, &pa_repeat);
+ XRenderFillRectangle(vwm->display, PictOpSrc, overlay_bg_fill, &overlay_bg_color, 0, 0, 1, OVERLAY_ROW_HEIGHT);
+ XRenderFillRectangle(vwm->display, PictOpSrc, overlay_bg_fill, &overlay_div_color, 0, OVERLAY_ROW_HEIGHT - 1, 1, 1);
+
+ bitmask = XCreatePixmap(vwm->display, VWM_XROOT(vwm), 1, 1, 32);
+ overlay_snowflakes_text_fill = XRenderCreatePicture(vwm->display, bitmask, XRenderFindStandardFormat(vwm->display, PictStandardARGB32), CPRepeat, &pa_repeat);
+ XRenderFillRectangle(vwm->display, PictOpSrc, overlay_snowflakes_text_fill, &overlay_snowflakes_visible_color, 0, 0, 1, 1);
+
+ bitmask = XCreatePixmap(vwm->display, VWM_XROOT(vwm), 1, 1, 32);
+ overlay_grapha_fill = XRenderCreatePicture(vwm->display, bitmask, XRenderFindStandardFormat(vwm->display, PictStandardARGB32), CPRepeat, &pa_repeat);
+ XRenderFillRectangle(vwm->display, PictOpSrc, overlay_grapha_fill, &overlay_grapha_color, 0, 0, 1, 1);
+
+ bitmask = XCreatePixmap(vwm->display, VWM_XROOT(vwm), 1, 1, 32);
+ overlay_graphb_fill = XRenderCreatePicture(vwm->display, bitmask, XRenderFindStandardFormat(vwm->display, PictStandardARGB32), CPRepeat, &pa_repeat);
+ XRenderFillRectangle(vwm->display, PictOpSrc, overlay_graphb_fill, &overlay_graphb_color, 0, 0, 1, 1);
+
+ bitmask = XCreatePixmap(vwm->display, VWM_XROOT(vwm), 1, 2, 32);
+ overlay_finish_fill = XRenderCreatePicture(vwm->display, bitmask, XRenderFindStandardFormat(vwm->display, PictStandardARGB32), CPRepeat, &pa_repeat);
+ XRenderFillRectangle(vwm->display, PictOpSrc, overlay_finish_fill, &overlay_visible_color, 0, 0, 1, 1);
+ XRenderFillRectangle(vwm->display, PictOpSrc, overlay_finish_fill, &overlay_trans_color, 0, 1, 1, 1);
+}
+
+
+/* install a monitor on the window if it doesn't already have one and has _NET_WM_PID set */
+void vwm_overlay_xwin_create(vwm_t *vwm, vwm_xwindow_t *xwin)
+{
+ Atom type;
+ int fmt;
+ unsigned long nitems;
+ unsigned long nbytes;
+ long *foo = NULL;
+ int pid = -1;
+
+ init_overlay(vwm);
+
+ if (xwin->monitor) return;
+
+ if (XGetWindowProperty(vwm->display, xwin->id, vwm->wm_pid_atom, 0, 1, False, XA_CARDINAL,
+ &type, &fmt, &nitems, &nbytes, (unsigned char **)&foo) != Success || !foo) return;
+
+ pid = *foo;
+ XFree(foo);
+
+ /* add the client process to the monitoring heirarchy */
+ /* XXX note libvmon here maintains a unique callback for each unique callback+xwin pair, so multi-window processes work */
+ xwin->monitor = vmon_proc_monitor(&vmon, NULL, pid, VMON_WANT_PROC_INHERIT, (void (*)(vmon_t *, vmon_proc_t *, void *))proc_sample_callback, xwin);
+ /* FIXME: count_rows() isn't returning the right count sometimes (off by ~1), it seems to be related to racing with the automatic child monitoring */
+ /* the result is an extra row sometimes appearing below the process heirarchy */
+ xwin->overlay.heirarchy_end = 1 + count_rows(xwin->monitor);
+ xwin->overlay.snowflakes_cnt = 0;
+}
+
+
+/* remove monitoring on the window if installed */
+void vwm_overlay_xwin_destroy(vwm_t *vwm, vwm_xwindow_t *xwin)
+{
+ if (xwin->monitor) vmon_proc_unmonitor(&vmon, xwin->monitor, (void (*)(vmon_t *, vmon_proc_t *, void *))proc_sample_callback, xwin);
+}
+
+
+/* this composes the maintained overlay into the window's overlay picture, this gets called from paint_all() on every repaint of xwin */
+/* we noop the call if the gen_last_composed and monitor->proc.generation numbers match, indicating there's nothing new to compose. */
+void vwm_overlay_xwin_compose(vwm_t *vwm, vwm_xwindow_t *xwin)
+{
+ XserverRegion region;
+ XRectangle damage;
+ int height;
+
+ if (!xwin->overlay.width) return; /* prevent winning race with maintain_overlay() and using an unready overlay... */
+
+ if (xwin->overlay.gen_last_composed == xwin->monitor->generation) return; /* noop if no sampling occurred since last compose */
+ xwin->overlay.gen_last_composed = xwin->monitor->generation; /* remember this generation */
+
+ //VWM_TRACE("composing %p", xwin);
+
+ height = vwm_overlay_xwin_composed_height(vwm, xwin);
+
+ /* fill the overlay picture with the background */
+ XRenderComposite(vwm->display, PictOpSrc, overlay_bg_fill, None, xwin->overlay.picture,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ xwin->attrs.width, height);
+
+ /* draw the graphs into the overlay through the stencils being maintained by the sample callbacks */
+ XRenderComposite(vwm->display, PictOpOver, overlay_grapha_fill, xwin->overlay.grapha_picture, xwin->overlay.picture,
+ 0, 0,
+ xwin->overlay.phase, 0,
+ 0, 0,
+ xwin->attrs.width, height);
+ XRenderComposite(vwm->display, PictOpOver, overlay_graphb_fill, xwin->overlay.graphb_picture, xwin->overlay.picture,
+ 0, 0,
+ xwin->overlay.phase, 0,
+ 0, 0,
+ xwin->attrs.width, height);
+
+ /* draw the shadow into the overlay picture using a translucent black source drawn through the shadow mask */
+ XRenderComposite(vwm->display, PictOpOver, overlay_shadow_fill, xwin->overlay.shadow_picture, xwin->overlay.picture,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ xwin->attrs.width, height);
+
+ /* render overlay text into the overlay picture using a white source drawn through the overlay text as a mask, on top of everything */
+ XRenderComposite(vwm->display, PictOpOver, overlay_text_fill, xwin->overlay.text_picture, xwin->overlay.picture,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ xwin->attrs.width, (xwin->overlay.heirarchy_end * OVERLAY_ROW_HEIGHT));
+
+ XRenderComposite(vwm->display, PictOpOver, overlay_snowflakes_text_fill, xwin->overlay.text_picture, xwin->overlay.picture,
+ 0, 0,
+ 0, xwin->overlay.heirarchy_end * OVERLAY_ROW_HEIGHT,
+ 0, xwin->overlay.heirarchy_end * OVERLAY_ROW_HEIGHT,
+ xwin->attrs.width, height - (xwin->overlay.heirarchy_end * OVERLAY_ROW_HEIGHT));
+
+ /* damage the window to ensure the updated overlay is drawn (TODO: this can be done more selectively/efficiently) */
+ damage.x = xwin->attrs.x + xwin->attrs.border_width;
+ damage.y = xwin->attrs.y + xwin->attrs.border_width;
+ damage.width = xwin->attrs.width;
+ damage.height = height;
+ region = XFixesCreateRegion(vwm->display, &damage, 1);
+ vwm_composite_damage_add(vwm, region);
+}
+
+void vwm_overlay_rate_increase(vwm_t *vwm) {
+ if (sampling_interval + 1 < sizeof(sampling_intervals) / sizeof(sampling_intervals[0])) sampling_interval++;
+}
+
+void vwm_overlay_rate_decrease(vwm_t *vwm) {
+ if (sampling_interval >= 0) sampling_interval--;
+}
+
+
+/* comvenience function for returning the time delta as a seconds.fraction float */
+static float delta(struct timeval *cur, struct timeval *prev)
+{
+ struct timeval res;
+ float delta;
+
+ /* determine the # of whole.fractional seconds between prev and cur */
+ timersub(cur, prev, &res);
+
+ delta = res.tv_sec;
+ delta += (float)((float)res.tv_usec) / 1000000.0;
+
+ return delta;
+}
+
+
+void vwm_overlay_update(vwm_t *vwm, int *desired_delay) {
+ static int sampling_paused = 0;
+ static int contiguous_drops = 0;
+ float this_delta;
+
+ init_overlay(vwm);
+
+ gettimeofday(&maybe_sample, NULL);
+ if ((sampling_interval == -1 && !sampling_paused) || /* XXX this is kind of a kludge to get the 0 Hz indicator drawn before pausing */
+ (sampling_interval != -1 && ((this_delta = delta(&maybe_sample, &this_sample)) >= sampling_intervals[sampling_interval]))) {
+ vmon_sys_stat_t *sys_stat;
+
+ /* automatically lower the sample rate if we can't keep up with the current sample rate */
+ if (sampling_interval != -1 && sampling_interval <= prev_sampling_interval &&
+ this_delta >= (sampling_intervals[sampling_interval] * 1.5)) {
+ contiguous_drops++;
+ /* require > 1 contiguous drops before lowering the rate, tolerates spurious one-off stalls */
+ if (contiguous_drops > 2) sampling_interval--;
+ } else contiguous_drops = 0;
+
+ /* age the sys-wide sample data into "last" variables, before the new sample overwrites them. */
+ last_sample = this_sample;
+ this_sample = maybe_sample;
+ if ((sys_stat = vmon.stores[VMON_STORE_SYS_STAT])) {
+ last_user_cpu = sys_stat->user;
+ last_system_cpu = sys_stat->system;
+ last_total = sys_stat->user +
+ sys_stat->nice +
+ sys_stat->system +
+ sys_stat->idle +
+ sys_stat->iowait +
+ sys_stat->irq +
+ sys_stat->softirq +
+ sys_stat->steal +
+ sys_stat->guest;
+
+ last_idle = sys_stat->idle;
+ last_iowait = sys_stat->iowait;
+ }
+
+ vmon_sample(&vmon); /* XXX: calls proc_sample_callback() for explicitly monitored processes after sampling their descendants */
+ /* XXX: also calls sample_callback() per invocation after sampling the sys wants */
+ sampling_paused = (sampling_interval == -1);
+ prev_sampling_interval = sampling_interval;
+ }
+
+
+ /* TODO: make some effort to compute how long to sleep, but this is perfectly fine for now. */
+ *desired_delay = sampling_interval != -1 ? sampling_intervals[sampling_interval] * 300.0 : -1;
+}
diff --git a/src/overlay.h b/src/overlay.h
new file mode 100644
index 0000000..94e2dca
--- /dev/null
+++ b/src/overlay.h
@@ -0,0 +1,36 @@
+#ifndef _OVERLAY_H
+#define _OVERLAY_H
+
+#include <X11/Xlib.h>
+#include <X11/extensions/Xrender.h>
+
+typedef struct _vwm_t vwm_t;
+typedef struct _vwm_xwindow_t vwm_xwindow_t;
+
+/* everything needed by the per-window overlay's context */
+typedef struct _vwm_overlay_t {
+ Pixmap text_pixmap; /* pixmap for overlayed text (kept around for XDrawText usage) */
+ Picture text_picture; /* picture representation of text_pixmap */
+ Picture shadow_picture; /* text shadow layer */
+ Picture grapha_picture; /* graph A layer */
+ Picture graphb_picture; /* graph B layer */
+ Picture tmp_picture; /* 1 row worth of temporary picture space */
+ Picture picture; /* overlay picture derived from the pixmap, for render compositing */
+ int width; /* current width of the overlay */
+ int height; /* current height of the overlay */
+ int phase; /* current position within the (horizontally scrolling) graphs */
+ int heirarchy_end; /* row where the process heirarchy currently ends */
+ int snowflakes_cnt; /* count of snowflaked rows (reset to zero to truncate snowflakes display) */
+ int gen_last_composed; /* the last composed vmon generation */
+} vwm_overlay_t;
+
+int vwm_overlay_xwin_composed_height(vwm_t *vwm, vwm_xwindow_t *xwin);
+void vwm_overlay_xwin_reset_snowflakes(vwm_t *vwm, vwm_xwindow_t *xwin);
+void vwm_overlay_xwin_create(vwm_t *vwm, vwm_xwindow_t *xwin);
+void vwm_overlay_xwin_destroy(vwm_t *vwm, vwm_xwindow_t *xwin);
+void vwm_overlay_xwin_compose(vwm_t *vwm, vwm_xwindow_t *xwin);
+void vwm_overlay_rate_increase(vwm_t *vwm);
+void vwm_overlay_rate_decrease(vwm_t *vwm);
+void vwm_overlay_update(vwm_t *vwm, int *desired_delay);
+
+#endif
diff --git a/src/screen.c b/src/screen.c
new file mode 100644
index 0000000..8fb63a5
--- /dev/null
+++ b/src/screen.c
@@ -0,0 +1,152 @@
+/*
+ * \/\/\
+ *
+ * Copyright (C) 2012-2016 Vito Caputo - <vcaputo@gnugeneration.com>
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+/* return the appropriate screen, don't use the return value across event loops because randr events reallocate the array. */
+
+#include <X11/Xlib.h>
+#include <stdarg.h>
+#include <values.h>
+
+#include "list.h"
+#include "screen.h"
+#include "vwm.h"
+#include "window.h"
+#include "xwindow.h"
+
+ /* Xinerama/multihead screen functions */
+
+/* return what fraction (0.0-1.0) of vwin overlaps with scr */
+static float vwm_screen_overlaps_xwin(vwm_t *vwm, const vwm_screen_t *scr, vwm_xwindow_t *xwin)
+{
+ float pct = 0, xover = 0, yover = 0;
+
+ if (scr->x_org + scr->width < xwin->attrs.x || scr->x_org > xwin->attrs.x + xwin->attrs.width ||
+ scr->y_org + scr->height < xwin->attrs.y || scr->y_org > xwin->attrs.y + xwin->attrs.height)
+ goto _out;
+
+ /* they overlap, by how much? */
+ xover = MIN(scr->x_org + scr->width, xwin->attrs.x + xwin->attrs.width) - MAX(scr->x_org, xwin->attrs.x);
+ yover = MIN(scr->y_org + scr->height, xwin->attrs.y + xwin->attrs.height) - MAX(scr->y_org, xwin->attrs.y);
+
+ pct = (xover * yover) / (xwin->attrs.width * xwin->attrs.height);
+_out:
+ VWM_TRACE("xover=%f yover=%f width=%i height=%i pct=%.4f", xover, yover, xwin->attrs.width, xwin->attrs.height, pct);
+ return pct;
+}
+
+
+const vwm_screen_t * vwm_screen_find(vwm_t *vwm, vwm_screen_rel_t rel, ...)
+{
+ static vwm_screen_t faux;
+ vwm_screen_t *scr, *best = &faux; /* default to faux as best */
+ int i;
+
+ faux.screen_number = 0;
+ faux.x_org = 0;
+ faux.y_org = 0;
+ faux.width = WidthOfScreen(DefaultScreenOfDisplay(vwm->display));
+ faux.height = HeightOfScreen(DefaultScreenOfDisplay(vwm->display));
+
+ if (!vwm->xinerama_screens) goto _out;
+
+#define for_each_screen(_tmp) \
+ for (i = 0, _tmp = vwm->xinerama_screens; i < vwm->xinerama_screens_cnt; _tmp = &vwm->xinerama_screens[++i])
+
+ switch (rel) {
+ case VWM_SCREEN_REL_XWIN: {
+ va_list ap;
+ vwm_xwindow_t *xwin;
+ float best_pct = 0, this_pct;
+
+ va_start(ap, rel);
+ xwin = va_arg(ap, vwm_xwindow_t *);
+ va_end(ap);
+
+ for_each_screen(scr) {
+ this_pct = vwm_screen_overlaps_xwin(vwm, scr, xwin);
+ if (this_pct > best_pct) {
+ best = scr;
+ best_pct = this_pct;
+ }
+ }
+ break;
+ }
+
+ case VWM_SCREEN_REL_POINTER: {
+ int root_x, root_y, win_x, win_y;
+ unsigned int mask;
+ Window root, child;
+
+ /* get the pointer coordinates and find which screen it's in */
+ XQueryPointer(vwm->display, VWM_XROOT(vwm), &root, &child, &root_x, &root_y, &win_x, &win_y, &mask);
+
+ for_each_screen(scr) {
+ if (root_x >= scr->x_org && root_x < scr->x_org + scr->width &&
+ root_y >= scr->y_org && root_y < scr->y_org + scr->height) {
+ best = scr;
+ break;
+ }
+ }
+ break;
+ }
+
+ case VWM_SCREEN_REL_TOTAL: {
+ short x1 = MAXSHORT, y1 = MAXSHORT, x2 = MINSHORT, y2 = MINSHORT;
+ /* find the smallest x_org and y_org, the highest x_org + width and y_org + height, those are the two corners of the total rect */
+ for_each_screen(scr) {
+ if (scr->x_org < x1) x1 = scr->x_org;
+ if (scr->y_org < y1) y1 = scr->y_org;
+ if (scr->x_org + scr->width > x2) x2 = scr->x_org + scr->width;
+ if (scr->y_org + scr->height > y2) y2 = scr->y_org + scr->height;
+ }
+ faux.x_org = x1;
+ faux.y_org = y1;
+ faux.width = x2 - x1;
+ faux.height = y2 - y1;
+ best = &faux;
+ break;
+ }
+ }
+_out:
+ VWM_TRACE("Found Screen #%i: %hix%hi @ %hi,%hi", best->screen_number, best->width, best->height, best->x_org, best->y_org);
+
+ return best;
+}
+
+
+/* check if a screen contains any windows (assuming the current desktop) */
+int vwm_screen_is_empty(vwm_t *vwm, const vwm_screen_t *scr)
+{
+ vwm_xwindow_t *xwin;
+ int is_empty = 1;
+
+ list_for_each_entry(xwin, &vwm->xwindows, xwindows) {
+ if (!xwin->mapped) continue;
+ if (!xwin->managed || (xwin->managed->desktop == vwm->focused_desktop && !xwin->managed->shelved && !xwin->managed->configuring)) {
+ /* XXX: it may make more sense to see what %age of the screen is overlapped by windows, and consider it empty if < some % */
+ /* This is just seeing if any window is predominantly within the specified screen, the rationale being if you had a focusable
+ * window on the screen you would have used the keyboard to make windows go there; this function is only used in determining
+ * wether a new window should go where the pointer is or not. */
+ if (vwm_screen_overlaps_xwin(vwm, scr, xwin) >= 0.05) {
+ is_empty = 0;
+ break;
+ }
+ }
+ }
+
+ return is_empty;
+}
diff --git a/src/screen.h b/src/screen.h
new file mode 100644
index 0000000..c8b7531
--- /dev/null
+++ b/src/screen.h
@@ -0,0 +1,19 @@
+#ifndef _SCREEN_H
+#define _SCREEN_H
+
+#include <X11/extensions/Xinerama.h> /* XINERAMA extension, facilitates easy multihead awareness */
+
+typedef struct _vwm_t vwm_t;
+
+typedef XineramaScreenInfo vwm_screen_t; /* conveniently reuse the xinerama type for describing screens */
+
+typedef enum _vwm_screen_rel_t {
+ VWM_SCREEN_REL_XWIN, /* return the screen the supplied window most resides in */
+ VWM_SCREEN_REL_POINTER, /* return the screen the pointer resides in */
+ VWM_SCREEN_REL_TOTAL, /* return the bounding rectangle of all screens as one */
+} vwm_screen_rel_t;
+
+const vwm_screen_t * vwm_screen_find(vwm_t *vwm, vwm_screen_rel_t rel, ...);
+int vwm_screen_is_empty(vwm_t *vwm, const vwm_screen_t *scr);
+
+#endif
diff --git a/src/vwm.c b/src/vwm.c
new file mode 100644
index 0000000..c2e5126
--- /dev/null
+++ b/src/vwm.c
@@ -0,0 +1,291 @@
+/*
+ * \/\/\
+ *
+ * Copyright (C) 2012-2016 Vito Caputo - <vcaputo@gnugeneration.com>
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <X11/Xlib.h>
+#include <X11/keysym.h>
+#include <X11/cursorfont.h>
+#include <X11/Xatom.h>
+#include <X11/extensions/sync.h> /* SYNC extension, enables us to give vwm the highest X client priority, helps keep vwm responsive at all times */
+#include <X11/extensions/Xinerama.h> /* XINERAMA extension, facilitates easy multihead awareness */
+#include <X11/extensions/Xrandr.h> /* RANDR extension, facilitates display configuration change awareness */
+#include <X11/extensions/Xdamage.h> /* Damage extension, enables receipt of damage events, reports visible regions needing updating (compositing) */
+#include <X11/extensions/Xrender.h> /* Render extension, enables use of alpha channels and accelerated rendering of surfaces having alpha (compositing) */
+#include <X11/extensions/Xcomposite.h> /* Composite extension, enables off-screen redirection of window rendering (compositing) */
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdarg.h>
+#include <string.h>
+#include <inttypes.h>
+#include <values.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/resource.h>
+#include <poll.h>
+
+#include "composite.h"
+#include "desktop.h"
+#include "launch.h"
+#include "logo.h"
+#include "vwm.h"
+#include "xevent.h"
+#include "xwindow.h"
+
+static vwm_t vwm;
+
+ /* Sync */
+static int sync_event, sync_error;
+
+ /* Xinerama */
+static int xinerama_event, xinerama_error;
+static int randr_event, randr_error;
+
+ /* Compositing */
+static int composite_event, composite_error, composite_opcode;
+
+static int errhandler(Display *display, XErrorEvent *err)
+{
+ /* TODO */
+ return 1;
+}
+
+int main(int argc, char *argv[])
+{
+ int err = 0;
+ int done = 0;
+ XEvent event;
+ Cursor pointer;
+ struct pollfd pfd;
+ char *console_args[] = {"xterm", "-class", CONSOLE_WM_CLASS, "-e", "/bin/sh", "-c", "screen -D -RR " CONSOLE_SESSION_STRING, NULL};
+
+#define reterr_if(_cond, _fmt, _args...) \
+ err++;\
+ if (_cond) {\
+ VWM_ERROR(_fmt, ##_args);\
+ return err;\
+ }
+
+ INIT_LIST_HEAD(&vwm.desktops);
+ INIT_LIST_HEAD(&vwm.desktops_mru);
+ INIT_LIST_HEAD(&vwm.windows_mru);
+ INIT_LIST_HEAD(&vwm.xwindows);
+
+ /* open connection with the server */
+ reterr_if((vwm.display = XOpenDisplay(NULL)) == NULL, "Cannot open display");
+
+ /* prevent children from inheriting the X connection */
+ reterr_if(fcntl(ConnectionNumber(vwm.display), F_SETFD, FD_CLOEXEC) < 0, "Cannot set FD_CLOEXEC on X connection");
+
+ /* get our scheduling priority, clients are launched with a priority LAUNCHED_RELATIVE_PRIORITY nicer than this */
+ reterr_if((vwm.priority = getpriority(PRIO_PROCESS, getpid())) == -1, "Cannot get scheduling priority");
+
+ XSetErrorHandler(errhandler);
+
+ vwm.screen_num = DefaultScreen(vwm.display);
+
+ /* query the needed X extensions */
+ reterr_if(!XQueryExtension (vwm.display, COMPOSITE_NAME, &composite_opcode, &composite_event, &composite_error), "No composite extension available");
+ reterr_if(!XDamageQueryExtension(vwm.display, &vwm.damage_event, &vwm.damage_error), "No damage extension available");
+ if (XSyncQueryExtension(vwm.display, &sync_event, &sync_error)) {
+ /* set the window manager to the maximum X client priority */
+ XSyncSetPriority(vwm.display, VWM_XROOT(&vwm), 0x7fffffff);
+ }
+
+ if (XineramaQueryExtension(vwm.display, &xinerama_event, &xinerama_error)) {
+ vwm.xinerama_screens = XineramaQueryScreens(vwm.display, &vwm.xinerama_screens_cnt);
+ }
+
+ if (XRRQueryExtension(vwm.display, &randr_event, &randr_error)) {
+ XRRSelectInput(vwm.display, VWM_XROOT(&vwm), RRScreenChangeNotifyMask);
+ }
+
+ /* allocate colors, I make assumptions about the X server's color capabilities since I'll only use this on modern-ish computers... */
+ vwm.cmap = DefaultColormap(vwm.display, vwm.screen_num);
+
+#define color(_sym, _str) \
+ XAllocNamedColor(vwm.display, vwm.cmap, _str, &vwm.colors._sym ## _color, &vwm.colors._sym ## _color);
+#include "colors.def"
+#undef color
+
+ vwm.wm_delete_atom = XInternAtom(vwm.display, "WM_DELETE_WINDOW", False);
+ vwm.wm_protocols_atom = XInternAtom(vwm.display, "WM_PROTOCOLS", False);
+ vwm.wm_pid_atom = XInternAtom(vwm.display, "_NET_WM_PID", False);
+
+ XSelectInput(vwm.display, VWM_XROOT(&vwm),
+ PropertyChangeMask | SubstructureNotifyMask | SubstructureRedirectMask | PointerMotionMask | ButtonPressMask | ButtonReleaseMask);
+ XGrabKey(vwm.display, AnyKey, WM_GRAB_MODIFIER, VWM_XROOT(&vwm), False, GrabModeAsync, GrabModeAsync);
+
+ XFlush(vwm.display);
+
+ XSetInputFocus(vwm.display, VWM_XROOT(&vwm), RevertToPointerRoot, CurrentTime);
+
+ /* create initial virtual desktop */
+ vwm_desktop_focus(&vwm, vwm_desktop_create(&vwm, NULL));
+ vwm_desktop_mru(&vwm, vwm.focused_desktop);
+
+ /* manage all preexisting windows */
+ vwm_xwin_create_existing(&vwm);
+
+ /* create GC for logo drawing and window rubber-banding */
+ vwm.gc = XCreateGC(vwm.display, VWM_XROOT(&vwm), 0, NULL);
+ XSetSubwindowMode(vwm.display, vwm.gc, IncludeInferiors);
+ XSetFunction(vwm.display, vwm.gc, GXxor);
+
+ /* launch the console here so it's likely ready by the time the logo animation finishes (there's no need to synchronize with it currently) */
+ vwm_launch(&vwm, console_args, VWM_LAUNCH_MODE_BG);
+
+ /* first the logo color is the foreground */
+ XSetForeground(vwm.display, vwm.gc, vwm.colors.logo_color.pixel);
+ vwm_draw_logo(&vwm);
+
+ /* change to the rubber-banding foreground color */
+ XSetForeground(vwm.display, vwm.gc, vwm.colors.rubberband_color.pixel);
+
+ XClearWindow(vwm.display, VWM_XROOT(&vwm));
+
+ /* set the pointer */
+ pointer = XCreateFontCursor(vwm.display, XC_X_cursor);
+ XDefineCursor(vwm.display, VWM_XROOT(&vwm), pointer);
+
+ pfd.events = POLLIN;
+ pfd.revents = 0;
+ pfd.fd = ConnectionNumber(vwm.display);
+
+ while (!done) {
+ do {
+ int delay;
+
+ vwm_overlay_update(&vwm, &delay);
+ XFlush(vwm.display);
+
+ if (!XPending(vwm.display)) {
+ if (poll(&pfd, 1, delay) == 0) break;
+ }
+
+ XNextEvent(vwm.display, &event);
+ switch (event.type) {
+ case KeyPress:
+ VWM_TRACE("keypress");
+ vwm_xevent_handle_key_press(&vwm, &event.xkey);
+ break;
+
+ case KeyRelease:
+ VWM_TRACE("keyrelease");
+ vwm_xevent_handle_key_release(&vwm, &event.xkey);
+ break;
+
+ case ButtonPress:
+ VWM_TRACE("buttonpresss");
+ vwm_xevent_handle_button_press(&vwm, &event.xbutton);
+ break;
+
+ case MotionNotify:
+ //VWM_TRACE("motionnotify");
+ vwm_xevent_handle_motion_notify(&vwm, &event.xmotion);
+ break;
+
+ case ButtonRelease:
+ VWM_TRACE("buttonrelease");
+ vwm_xevent_handle_button_release(&vwm, &event.xbutton);
+ break;
+
+ case CreateNotify:
+ VWM_TRACE("createnotify");
+ vwm_xevent_handle_create_notify(&vwm, &event.xcreatewindow);
+ break;
+
+ case DestroyNotify:
+ VWM_TRACE("destroynotify");
+ vwm_xevent_handle_destroy_notify(&vwm, &event.xdestroywindow);
+ break;
+
+ case ConfigureRequest:
+ VWM_TRACE("configurerequest");
+ vwm_xevent_handle_configure_request(&vwm, &event.xconfigurerequest);
+ break;
+
+ case ConfigureNotify:
+ VWM_TRACE("configurenotify");
+ vwm_xevent_handle_configure_notify(&vwm, &event.xconfigure);
+ break;
+
+ case UnmapNotify:
+ VWM_TRACE("unmapnotify");
+ vwm_xevent_handle_unmap_notify(&vwm, &event.xunmap);
+ break;
+
+ case MapNotify:
+ VWM_TRACE("mapnotify");
+ vwm_xevent_handle_map_notify(&vwm, &event.xmap);
+ break;
+
+ case MapRequest:
+ VWM_TRACE("maprequest");
+ vwm_xevent_handle_map_request(&vwm, &event.xmaprequest);
+ break;
+
+ case PropertyNotify:
+ VWM_TRACE("property notify");
+ vwm_xevent_handle_property_notify(&vwm, &event.xproperty);
+ break;
+
+ case MappingNotify:
+ VWM_TRACE("mapping notify");
+ vwm_xevent_handle_mapping_notify(&vwm, &event.xmapping);
+ break;
+
+ case Expose:
+ VWM_TRACE("expose");
+ break;
+
+ case GravityNotify:
+ VWM_TRACE("gravitynotify");
+ break;
+
+ case ReparentNotify:
+ VWM_TRACE("reparentnotify");
+ break;
+
+ default:
+ if (event.type == randr_event + RRScreenChangeNotify) {
+ VWM_TRACE("rrscreenchangenotify");
+ if (vwm.xinerama_screens) XFree(vwm.xinerama_screens);
+ vwm.xinerama_screens = XineramaQueryScreens(vwm.display, &vwm.xinerama_screens_cnt);
+
+ vwm_composite_invalidate_root(&vwm);
+ } else if (event.type == vwm.damage_event + XDamageNotify) {
+ //VWM_TRACE("damagenotify");
+ vwm_composite_damage_event(&vwm, (XDamageNotifyEvent *)&event);
+ } else {
+ VWM_ERROR("Unhandled X op %i", event.type);
+ }
+ break;
+ }
+ } while (QLength(vwm.display));
+
+ vwm_composite_paint_all(&vwm);
+ }
+
+ /* close connection to server */
+ XFlush(vwm.display);
+ XCloseDisplay(vwm.display);
+
+ return 0;
+}
diff --git a/src/vwm.h b/src/vwm.h
new file mode 100644
index 0000000..8bc0f7a
--- /dev/null
+++ b/src/vwm.h
@@ -0,0 +1,79 @@
+#ifndef _UTIL_H
+#define _UTIL_H
+
+#include <stdio.h>
+// #include <X11/Xlib.h>
+// #include <X11/Xutil.h>
+#include <errno.h>
+#include <X11/Xlib.h>
+#include <X11/extensions/Xinerama.h>
+
+#include "context.h"
+#include "list.h"
+
+#define WINDOW_BORDER_WIDTH 1
+#define WM_GRAB_MODIFIER Mod1Mask /* the modifier for invoking vwm's controls */
+ /* Mod4Mask would be the windows key instead of Alt, but there's an assumption
+ * in the code that grabs are being activated by Alt which complicates changing it,
+ * search for XGetModifierMapping to see where, feel free to fix it. Or you can
+ * just hack the code to expect the appropriate key instead of Alt, I didn't see the
+ * value of making it modifier mapping aware if it's always Alt for me. */
+
+#define CONSOLE_WM_CLASS "VWMConsoleXTerm" /* the class we specify to the "console" xterm */
+#define CONSOLE_SESSION_STRING "_vwm_console.$DISPLAY" /* the unique console screen session identifier */
+
+
+#define VWM_ERROR(_fmt, _args...) fprintf(stderr, "%s:%i\t%s() "_fmt"\n", __FILE__, __LINE__, __FUNCTION__, ##_args)
+#define VWM_PERROR(_fmt, _args...) fprintf(stderr, "%s:%i\t%s() "_fmt"; %s\n", __FILE__, __LINE__, __FUNCTION__, ##_args, strerror(errno))
+#define VWM_BUG(_fmt, _args...) fprintf(stderr, "BUG %s:%i\t%s() "_fmt"; %s\n", __FILE__, __LINE__, __FUNCTION__, ##_args, strerror(errno))
+
+#ifdef TRACE
+#define VWM_TRACE(_fmt, _args...) fprintf(stderr, "%s:%i\t%s() "_fmt"\n", __FILE__, __LINE__, __FUNCTION__, ##_args)
+#else
+#define VWM_TRACE(_fmt, _args...) do { } while(0)
+#endif
+
+#define VWM_XROOT(_vwm) RootWindow((_vwm)->display, (_vwm)->screen_num)
+#define VWM_XVISUAL(_vwm) DefaultVisual((_vwm)->display, (_vwm)->screen_num)
+#define VWM_XDEPTH(_vwm) DefaultDepth((_vwm)->display, (_vwm)->screen_num)
+
+#define MIN(_a, _b) ((_a) < (_b) ? (_a) : (_b))
+#define MAX(_a, _b) ((_a) > (_b) ? (_a) : (_b))
+
+typedef struct _vwm_window_t vwm_window_t;
+typedef struct _vwm_desktop_t vwm_desktop_t;
+
+typedef struct _vwm_t {
+ Display *display;
+ Colormap cmap;
+ int screen_num;
+ GC gc;
+ Atom wm_delete_atom;
+ Atom wm_protocols_atom;
+ Atom wm_pid_atom;
+ int damage_event, damage_error;
+
+ list_head_t desktops; /* global list of all (virtual) desktops in spatial created-in order */
+ list_head_t desktops_mru; /* global list of all (virtual) desktops in MRU order */
+ list_head_t windows_mru; /* global list of all managed windows kept in MRU order */
+ list_head_t xwindows; /* global list of all xwindows kept in the X server stacking order */
+ vwm_window_t *console; /* the console window */
+ vwm_window_t *focused_origin; /* the originating window in a grabbed operation/transaction */
+ vwm_desktop_t *focused_desktop; /* currently focused (virtual) desktop */
+ vwm_window_t *focused_shelf; /* currently focused shelved window */
+ vwm_context_t focused_context; /* currently focused context */
+ int priority; /* scheduling priority of the vwm process, launcher nices relative to this */
+ unsigned long fence_mask; /* global mask state for vwm_win_focus_next(... VWM_FENCE_MASKED_VIOLATE),
+ * if you use vwm on enough screens to overflow this, pics or it didn't happen. */
+ struct colors {
+#define color(_sym, _str) \
+ XColor _sym ## _color;
+ #include "colors.def"
+#undef color
+ } colors;
+
+ XineramaScreenInfo *xinerama_screens;
+ int xinerama_screens_cnt;
+} vwm_t;
+
+#endif
diff --git a/src/window.c b/src/window.c
new file mode 100644
index 0000000..e28330a
--- /dev/null
+++ b/src/window.c
@@ -0,0 +1,468 @@
+/*
+ * \/\/\
+ *
+ * Copyright (C) 2012-2016 Vito Caputo - <vcaputo@gnugeneration.com>
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+ /* vwm "managed" windows (vwm_window_t) (which are built upon the "core" X windows (vwm_xwindow_t)) */
+
+#include <X11/Xlib.h>
+#include <stdlib.h>
+#include <stdarg.h>
+
+#include "desktop.h"
+#include "list.h"
+#include "vwm.h"
+#include "window.h"
+#include "xwindow.h"
+
+/* unmap the specified window and set the unmapping-in-progress flag so we can discard vwm-generated UnmapNotify events */
+void vwm_win_unmap(vwm_t *vwm, vwm_window_t *vwin)
+{
+ if (!vwin->xwindow->mapped) {
+ VWM_TRACE("inhibited unmap of \"%s\", not mapped by client", vwin->xwindow->name);
+ return;
+ }
+ VWM_TRACE("Unmapping \"%s\"", vwin->xwindow->name);
+ vwin->unmapping = 1;
+ XUnmapWindow(vwm->display, vwin->xwindow->id);
+}
+
+
+/* map the specified window and set the mapping-in-progress flag so we can discard vwm-generated MapNotify events */
+void vwm_win_map(vwm_t *vwm, vwm_window_t *vwin)
+{
+ if (!vwin->xwindow->mapped) {
+ VWM_TRACE("inhibited map of \"%s\", not mapped by client", vwin->xwindow->name);
+ return;
+ }
+ VWM_TRACE("Mapping \"%s\"", vwin->xwindow->name);
+ vwin->mapping = 1;
+ XMapWindow(vwm->display, vwin->xwindow->id);
+}
+
+
+/* make the specified window the most recently used one */
+void vwm_win_mru(vwm_t *vwm, vwm_window_t *vwin)
+{
+ list_move(&vwin->windows_mru, &vwm->windows_mru);
+}
+
+
+/* look up the X window in the global managed windows list */
+vwm_window_t * vwm_win_lookup(vwm_t *vwm, Window win)
+{
+ vwm_window_t *tmp, *vwin = NULL;
+
+ list_for_each_entry(tmp, &vwm->windows_mru, windows_mru) {
+ if (tmp->xwindow->id == win) {
+ vwin = tmp;
+ break;
+ }
+ }
+
+ return vwin;
+}
+
+
+/* return the currently focused window (considers current context...), may return NULL */
+vwm_window_t * vwm_win_focused(vwm_t *vwm)
+{
+ vwm_window_t *vwin = NULL;
+
+ switch (vwm->focused_context) {
+ case VWM_CONTEXT_SHELF:
+ vwin = vwm->focused_shelf;
+ break;
+
+ case VWM_CONTEXT_DESKTOP:
+ if (vwm->focused_desktop) vwin = vwm->focused_desktop->focused_window;
+ break;
+
+ default:
+ VWM_BUG("Unsupported context");
+ break;
+ }
+
+ return vwin;
+}
+
+
+/* "autoconfigure" windows (configuration shortcuts like fullscreen/halfscreen/quarterscreen) and restoring the window */
+void vwm_win_autoconf(vwm_t *vwm, vwm_window_t *vwin, vwm_screen_rel_t rel, vwm_win_autoconf_t conf, ...)
+{
+ const vwm_screen_t *scr;
+ va_list ap;
+ XWindowChanges changes = { .border_width = WINDOW_BORDER_WIDTH };
+
+ /* remember the current configuration as the "client" configuration if it's not an autoconfigured one. */
+ if (vwin->autoconfigured == VWM_WIN_AUTOCONF_NONE) vwin->client = vwin->xwindow->attrs;
+
+ scr = vwm_screen_find(vwm, rel, vwin->xwindow); /* XXX FIXME: this becomes a bug when vwm_screen_find() uses non-xwin va_args */
+ va_start(ap, conf);
+ switch (conf) {
+ case VWM_WIN_AUTOCONF_QUARTER: {
+ vwm_corner_t corner = va_arg(ap, vwm_corner_t);
+ changes.width = scr->width / 2 - (WINDOW_BORDER_WIDTH * 2);
+ changes.height = scr->height / 2 - (WINDOW_BORDER_WIDTH * 2);
+ switch (corner) {
+ case VWM_CORNER_TOP_LEFT:
+ changes.x = scr->x_org;
+ changes.y = scr->y_org;
+ break;
+
+ case VWM_CORNER_TOP_RIGHT:
+ changes.x = scr->x_org + scr->width / 2;
+ changes.y = scr->y_org;
+ break;
+
+ case VWM_CORNER_BOTTOM_RIGHT:
+ changes.x = scr->x_org + scr->width / 2;
+ changes.y = scr->y_org + scr->height / 2;
+ break;
+
+ case VWM_CORNER_BOTTOM_LEFT:
+ changes.x = scr->x_org;
+ changes.y = scr->y_org + scr->height / 2;
+ break;
+ }
+ break;
+ }
+
+ case VWM_WIN_AUTOCONF_HALF: {
+ vwm_side_t side = va_arg(ap, vwm_side_t);
+ switch (side) {
+ case VWM_SIDE_TOP:
+ changes.width = scr->width - (WINDOW_BORDER_WIDTH * 2);
+ changes.height = scr->height / 2 - (WINDOW_BORDER_WIDTH * 2);
+ changes.x = scr->x_org;
+ changes.y = scr->y_org;
+ break;
+
+ case VWM_SIDE_BOTTOM:
+ changes.width = scr->width - (WINDOW_BORDER_WIDTH * 2);
+ changes.height = scr->height / 2 - (WINDOW_BORDER_WIDTH * 2);
+ changes.x = scr->x_org;
+ changes.y = scr->y_org + scr->height / 2;
+ break;
+
+ case VWM_SIDE_LEFT:
+ changes.width = scr->width / 2 - (WINDOW_BORDER_WIDTH * 2);
+ changes.height = scr->height - (WINDOW_BORDER_WIDTH * 2);
+ changes.x = scr->x_org;
+ changes.y = scr->y_org;
+ break;
+
+ case VWM_SIDE_RIGHT:
+ changes.width = scr->width / 2 - (WINDOW_BORDER_WIDTH * 2);
+ changes.height = scr->height - (WINDOW_BORDER_WIDTH * 2);
+ changes.x = scr->x_org + scr->width / 2;
+ changes.y = scr->y_org;
+ break;
+ }
+ break;
+ }
+
+ case VWM_WIN_AUTOCONF_FULL:
+ changes.width = scr->width - WINDOW_BORDER_WIDTH * 2;
+ changes.height = scr->height - WINDOW_BORDER_WIDTH * 2;
+ changes.x = scr->x_org;
+ changes.y = scr->y_org;
+ break;
+
+ case VWM_WIN_AUTOCONF_ALL:
+ changes.width = scr->width;
+ changes.height = scr->height;
+ changes.x = scr->x_org;
+ changes.y = scr->y_org;
+ changes.border_width = 0;
+ break;
+
+ case VWM_WIN_AUTOCONF_NONE: /* restore window if autoconfigured */
+ changes.width = vwin->client.width;
+ changes.height = vwin->client.height;
+ changes.x = vwin->client.x;
+ changes.y = vwin->client.y;
+ break;
+ }
+ va_end(ap);
+
+ XConfigureWindow(vwm->display, vwin->xwindow->id, CWX | CWY | CWWidth | CWHeight | CWBorderWidth, &changes);
+ vwin->autoconfigured = conf;
+}
+
+
+/* focus a window */
+/* this updates window border color as needed and the X input focus if mapped */
+void vwm_win_focus(vwm_t *vwm, vwm_window_t *vwin)
+{
+ VWM_TRACE("focusing: %#x", (unsigned int)vwin->xwindow->id);
+
+ if (vwm_xwin_is_mapped(vwm, vwin->xwindow)) {
+ /* if vwin is mapped give it the input focus */
+ XSetInputFocus(vwm->display, vwin->xwindow->id, RevertToPointerRoot, CurrentTime);
+ }
+
+ /* update the border color accordingly */
+ if (vwin->shelved) {
+ /* set the border of the newly focused window to the shelved color */
+ XSetWindowBorder(vwm->display, vwin->xwindow->id, vwin == vwm->console ? vwm->colors.shelved_console_border_color.pixel : vwm->colors.shelved_window_border_color.pixel);
+ /* fullscreen windows in the shelf when focused, since we don't intend to overlap there */
+ vwm_win_autoconf(vwm, vwin, VWM_SCREEN_REL_POINTER, VWM_WIN_AUTOCONF_FULL); /* XXX TODO: for now the shelf follows the pointer, it's simple. */
+ } else {
+ if (vwin->desktop->focused_window) {
+ /* set the border of the previously focused window on the same desktop to the unfocused color */
+ XSetWindowBorder(vwm->display, vwin->desktop->focused_window->xwindow->id, vwm->colors.unfocused_window_border_color.pixel);
+ }
+
+ /* set the border of the newly focused window to the focused color */
+ XSetWindowBorder(vwm->display, vwin->xwindow->id, vwm->colors.focused_window_border_color.pixel);
+
+ /* persist this on a per-desktop basis so it can be restored on desktop switches */
+ vwin->desktop->focused_window = vwin;
+ }
+}
+
+
+/* focus the next window on a virtual desktop relative to the supplied window, in the specified context, respecting screen boundaries according to fence. */
+vwm_window_t * vwm_win_focus_next(vwm_t *vwm, vwm_window_t *vwin, vwm_fence_t fence)
+{
+ const vwm_screen_t *scr = vwm_screen_find(vwm, VWM_SCREEN_REL_XWIN, vwin->xwindow), *next_scr = NULL;
+ vwm_window_t *next;
+ unsigned long visited_mask;
+
+_retry:
+ visited_mask = 0;
+ list_for_each_entry(next, &vwin->windows_mru, windows_mru) {
+ /* searching for the next mapped window in this context, using vwin->windows as the head */
+ if (&next->windows_mru == &vwm->windows_mru) continue; /* XXX: skip the containerless head, we're leveraging the circular list implementation */
+
+ if ((vwin->shelved && next->shelved) ||
+ ((!vwin->shelved && !next->shelved && next->desktop == vwin->desktop) &&
+ (fence == VWM_FENCE_IGNORE ||
+ ((fence == VWM_FENCE_RESPECT || fence == VWM_FENCE_TRY_RESPECT) && vwm_screen_find(vwm, VWM_SCREEN_REL_XWIN, next->xwindow) == scr) ||
+ (fence == VWM_FENCE_VIOLATE && vwm_screen_find(vwm, VWM_SCREEN_REL_XWIN, next->xwindow) != scr) ||
+ (fence == VWM_FENCE_MASKED_VIOLATE && (next_scr = vwm_screen_find(vwm, VWM_SCREEN_REL_XWIN, next->xwindow)) != scr &&
+ !((1UL << next_scr->screen_number) & vwm->fence_mask))
+ ))) break;
+
+ if (fence == VWM_FENCE_MASKED_VIOLATE && next_scr && next_scr != scr) visited_mask |= (1UL << next_scr->screen_number);
+ }
+
+ if (fence == VWM_FENCE_TRY_RESPECT && next == vwin) {
+ /* if we tried to respect the fence and failed to find a next, fallback to ignoring the fence and try again */
+ fence = VWM_FENCE_IGNORE;
+ goto _retry;
+ } else if (fence == VWM_FENCE_MASKED_VIOLATE && next_scr) {
+ /* if we did a masked violate update the mask with the potentially new screen number */
+ if (next == vwin && visited_mask) {
+ /* if we failed to find a next window on a different screen but had candidates we've exhausted screens and need to reset the mask then retry */
+ VWM_TRACE("all candidate screens masked @ 0x%lx, resetting mask", vwm->fence_mask);
+ vwm->fence_mask = 0;
+ goto _retry;
+ }
+ vwm->fence_mask |= (1UL << next_scr->screen_number);
+ VWM_TRACE("VWM_FENCE_MASKED_VIOLATE fence_mask now: 0x%lx\n", vwm->fence_mask);
+ }
+
+ if (vwin->shelved) {
+ if (next != vwm->focused_shelf) {
+ /* shelf switch, unmap the focused shelf and take it over */
+ /* TODO FIXME: this makes assumptions about the shelf being focused calling unmap/map directly.. */
+ vwm_win_unmap(vwm, vwm->focused_shelf);
+
+ XFlush(vwm->display);
+
+ vwm_win_map(vwm, next);
+ vwm->focused_shelf = next;
+ vwm_win_focus(vwm, next);
+ }
+ } else {
+ if (next != next->desktop->focused_window) {
+ /* focus the changed window */
+ vwm_win_focus(vwm, next);
+ XRaiseWindow(vwm->display, next->xwindow->id);
+ }
+ }
+
+ VWM_TRACE("vwin=%p xwin=%p name=\"%s\"", next, next->xwindow, next->xwindow->name);
+
+ return next;
+}
+
+
+/* shelves a window, if the window is focused we focus the next one (if possible) */
+void vwm_win_shelve(vwm_t *vwm, vwm_window_t *vwin)
+{
+ /* already shelved, NOOP */
+ if (vwin->shelved) return;
+
+ /* shelving focused window, focus the next window */
+ if (vwin == vwin->desktop->focused_window) {
+ vwm_win_mru(vwm, vwm_win_focus_next(vwm, vwin, VWM_FENCE_RESPECT));
+ }
+
+ if (vwin == vwin->desktop->focused_window) {
+ /* TODO: we can probably put this into vwm_win_focus_next() and have it always handled there... */
+ vwin->desktop->focused_window = NULL;
+ }
+
+ vwin->shelved = 1;
+ vwm_win_mru(vwm, vwin);
+
+ /* newly shelved windows always become the focused shelf */
+ vwm->focused_shelf = vwin;
+
+ vwm_win_unmap(vwm, vwin);
+}
+
+
+/* helper for (idempotently) unfocusing a window, deals with context switching etc... */
+void vwm_win_unfocus(vwm_t *vwm, vwm_window_t *vwin)
+{
+ /* if we're the focused shelved window, cycle the focus to the next shelved window if possible, if there's no more shelf, switch to the desktop */
+ /* TODO: there's probably some icky behaviors for focused windows unmapping/destroying in unfocused contexts, we probably jump contexts suddenly. */
+ if (vwin == vwm->focused_shelf) {
+ VWM_TRACE("unfocusing focused shelf");
+ vwm_win_focus_next(vwm, vwin, VWM_FENCE_IGNORE);
+
+ if (vwin == vwm->focused_shelf) {
+ VWM_TRACE("shelf empty, leaving");
+ /* no other shelved windows, exit the shelf context */
+ vwm_context_focus(vwm, VWM_CONTEXT_DESKTOP);
+ vwm->focused_shelf = NULL;
+ }
+ }
+
+ /* if we're the focused window cycle the focus to the next window on the desktop if possible */
+ if (vwin->desktop->focused_window == vwin) {
+ VWM_TRACE("unfocusing focused window");
+ vwm_win_focus_next(vwm, vwin, VWM_FENCE_TRY_RESPECT);
+ }
+
+ if (vwin->desktop->focused_window == vwin) {
+ VWM_TRACE("desktop empty");
+ vwin->desktop->focused_window = NULL;
+ }
+}
+
+
+/* demote an managed window to an unmanaged one */
+vwm_xwindow_t * vwm_win_unmanage(vwm_t *vwm, vwm_window_t *vwin)
+{
+ vwm_win_mru(vwm, vwin); /* bump vwin to the mru head before unfocusing so we always move focus to the current head on unmanage of the focused window */
+ vwm_win_unfocus(vwm, vwin);
+ list_del(&vwin->windows_mru);
+
+ if (vwin == vwm->console) vwm->console = NULL;
+ if (vwin == vwm->focused_origin) vwm->focused_origin = NULL;
+
+ vwin->xwindow->managed = NULL;
+
+ return vwin->xwindow;
+}
+
+
+/* promote an unmanaged window to a managed one */
+vwm_window_t * vwm_win_manage_xwin(vwm_t *vwm, vwm_xwindow_t *xwin)
+{
+ vwm_window_t *focused, *vwin = NULL;
+
+ if (xwin->managed) {
+ VWM_TRACE("suppressed re-management of xwin=%p", xwin);
+ goto _fail;
+ }
+
+ if (!(vwin = (vwm_window_t *)malloc(sizeof(vwm_window_t)))) {
+ VWM_PERROR("Failed to allocate vwin");
+ goto _fail;
+ }
+
+ XUngrabButton(vwm->display, AnyButton, AnyModifier, xwin->id);
+ XGrabButton(vwm->display, AnyButton, WM_GRAB_MODIFIER, xwin->id, False, (PointerMotionMask | ButtonPressMask | ButtonReleaseMask), GrabModeAsync, GrabModeAsync, None, None);
+ XGrabKey(vwm->display, AnyKey, WM_GRAB_MODIFIER, xwin->id, False, GrabModeAsync, GrabModeAsync);
+ XSetWindowBorder(vwm->display, xwin->id, vwm->colors.unfocused_window_border_color.pixel);
+
+ vwin->hints = XAllocSizeHints();
+ if (!vwin->hints) {
+ VWM_PERROR("Failed to allocate WM hints");
+ goto _fail;
+ }
+ XGetWMNormalHints(vwm->display, xwin->id, vwin->hints, &vwin->hints_supplied);
+
+ xwin->managed = vwin;
+ vwin->xwindow = xwin;
+
+ vwin->desktop = vwm->focused_desktop;
+ vwin->autoconfigured = VWM_WIN_AUTOCONF_NONE;
+ vwin->mapping = vwin->unmapping = vwin->configuring = 0;
+ vwin->shelved = (vwm->focused_context == VWM_CONTEXT_SHELF); /* if we're in the shelf when the window is created, the window is shelved */
+ vwin->client = xwin->attrs; /* remember whatever the current attributes are */
+
+ VWM_TRACE("hints: flags=%lx x=%i y=%i w=%i h=%i minw=%i minh=%i maxw=%i maxh=%i winc=%i hinc=%i basew=%i baseh=%i grav=%x",
+ vwin->hints->flags,
+ vwin->hints->x, vwin->hints->y,
+ vwin->hints->width, vwin->hints->height,
+ vwin->hints->min_width, vwin->hints->min_height,
+ vwin->hints->max_width, vwin->hints->max_height,
+ vwin->hints->width_inc, vwin->hints->height_inc,
+ vwin->hints->base_width, vwin->hints->base_height,
+ vwin->hints->win_gravity);
+
+ if ((vwin->hints_supplied & (USSize | PSize))) {
+ vwin->client.width = vwin->hints->base_width;
+ vwin->client.height = vwin->hints->base_height;
+ }
+
+ /* put it on the global windows_mru list, if there's a focused window insert the new one after it */
+ if (!list_empty(&vwm->windows_mru) && (focused = vwm_win_focused(vwm))) {
+ /* insert the vwin immediately after the focused window, so Mod1+Tab goes to the new window */
+ list_add(&vwin->windows_mru, &focused->windows_mru);
+ } else {
+ list_add(&vwin->windows_mru, &vwm->windows_mru);
+ }
+
+ /* always raise newly managed windows so we know about them. */
+ XRaiseWindow(vwm->display, xwin->id);
+
+ /* if the desktop has no focused window yet, automatically focus the newly managed one, provided we're on the desktop context */
+ if (!vwm->focused_desktop->focused_window && vwm->focused_context == VWM_CONTEXT_DESKTOP) {
+ VWM_TRACE("Mapped new window \"%s\" is alone on desktop \"%s\", focusing", xwin->name, vwm->focused_desktop->name);
+ vwm_win_focus(vwm, vwin);
+ }
+
+ return vwin;
+
+_fail:
+ if (vwin) {
+ if (vwin->hints) XFree(vwin->hints);
+ free(vwin);
+ }
+ return NULL;
+}
+
+
+/* migrate a window to a new desktop, focuses the destination desktop as well */
+void vwm_win_migrate(vwm_t *vwm, vwm_window_t *vwin, vwm_desktop_t *desktop)
+{
+ vwm_win_unfocus(vwm, vwin); /* go through the motions of unfocusing the window if it is focused */
+ vwin->shelved = 0; /* ensure not shelved */
+ vwin->desktop = desktop; /* assign the new desktop */
+ vwm_desktop_focus(vwm, desktop); /* currently we always focus the new desktop in a migrate */
+
+ vwm_win_focus(vwm, vwin); /* focus the window so borders get updated */
+ vwm_win_mru(vwm, vwin); /* TODO: is this right? shouldn't the Mod1 release be what's responsible for this? I migrate so infrequently it probably doesn't matter */
+
+ XRaiseWindow(vwm->display, vwin->xwindow->id); /* ensure the window is raised */
+}
diff --git a/src/window.h b/src/window.h
new file mode 100644
index 0000000..3b3c745
--- /dev/null
+++ b/src/window.h
@@ -0,0 +1,81 @@
+#ifndef _WINDOW_H
+#define _WINDOW_H
+
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+
+#include "list.h"
+#include "screen.h"
+
+typedef struct _vwm_t vwm_t;
+typedef struct _vwm_xwindow_t vwm_xwindow_t;
+typedef struct _vwm_desktop_t vwm_desktop_t;
+
+typedef enum _vwm_win_autoconf_t {
+ VWM_WIN_AUTOCONF_NONE, /* un-autoconfigured window (used to restore the configuration) */
+ VWM_WIN_AUTOCONF_QUARTER, /* quarter-screened */
+ VWM_WIN_AUTOCONF_HALF, /* half-screened */
+ VWM_WIN_AUTOCONF_FULL, /* full-screened */
+ VWM_WIN_AUTOCONF_ALL /* all-screened (borderless) */
+} vwm_win_autoconf_t;
+
+/* the managed window we create for every mapped window we actually manage */
+typedef struct _vwm_window_t {
+ list_head_t windows_mru; /* global list of managed windows kept in MRU order */
+
+ vwm_xwindow_t *xwindow; /* window being managed */
+ vwm_desktop_t *desktop; /* desktop this window belongs to currently */
+
+ XWindowAttributes client; /* attrs of the client-configured window */
+
+ XSizeHints *hints; /* hints the client supplied */
+ long hints_supplied; /* bitfield reflecting the hints the client supplied */
+
+ unsigned int autoconfigured:3; /* autoconfigured window states (none/quarter/half/full/all) */
+ unsigned int mapping:1; /* is the window being mapped? (by vwm) */
+ unsigned int unmapping:1; /* is the window being unmapped? (by vwm) */
+ unsigned int configuring:1; /* is the window being configured/placed? (by vwm) */
+ unsigned int shelved:1; /* is the window shelved? */
+} vwm_window_t;
+
+
+void vwm_win_unmap(vwm_t *vwm, vwm_window_t *vwin);
+void vwm_win_map(vwm_t *vwm, vwm_window_t *vwin);
+void vwm_win_mru(vwm_t *vwm, vwm_window_t *vwin);
+vwm_window_t * vwm_win_lookup(vwm_t *vwm, Window win);
+vwm_window_t * vwm_win_focused(vwm_t *vwm);
+
+typedef enum _vwm_side_t {
+ VWM_SIDE_TOP,
+ VWM_SIDE_BOTTOM,
+ VWM_SIDE_LEFT,
+ VWM_SIDE_RIGHT
+} vwm_side_t;
+
+typedef enum _vwm_corner_t {
+ VWM_CORNER_TOP_LEFT,
+ VWM_CORNER_TOP_RIGHT,
+ VWM_CORNER_BOTTOM_RIGHT,
+ VWM_CORNER_BOTTOM_LEFT
+} vwm_corner_t;
+
+void vwm_win_autoconf(vwm_t *vwm, vwm_window_t *vwin, vwm_screen_rel_t rel, vwm_win_autoconf_t conf, ...);
+void vwm_win_focus(vwm_t *vwm, vwm_window_t *vwin);
+
+typedef enum _vwm_fence_t {
+ VWM_FENCE_IGNORE = 0, /* behave as if screen boundaries don't exist (like the pre-Xinerama code) */
+ VWM_FENCE_RESPECT, /* confine operation to within the screen */
+ VWM_FENCE_TRY_RESPECT, /* confine operation to within the screen, unless no options exist. */
+ VWM_FENCE_VIOLATE, /* leave the screen for any other */
+ VWM_FENCE_MASKED_VIOLATE /* leave the screen for any other not masked */
+} vwm_fence_t;
+
+vwm_window_t * vwm_win_focus_next(vwm_t *vwm, vwm_window_t *vwin, vwm_fence_t fence);
+void vwm_win_shelve(vwm_t *vwm, vwm_window_t *vwin);
+void vwm_win_unfocus(vwm_t *vwm, vwm_window_t *vwin);
+vwm_xwindow_t * vwm_win_unmanage(vwm_t *vwm, vwm_window_t *vwin);
+vwm_window_t * vwm_win_manage_xwin(vwm_t *vwm, vwm_xwindow_t *xwin);
+void vwm_win_migrate(vwm_t *vwm, vwm_window_t *vwin, vwm_desktop_t *desktop);
+
+
+#endif
diff --git a/src/xevent.c b/src/xevent.c
new file mode 100644
index 0000000..1955ee3
--- /dev/null
+++ b/src/xevent.c
@@ -0,0 +1,269 @@
+/*
+ * \/\/\
+ *
+ * Copyright (C) 2012-2016 Vito Caputo - <vcaputo@gnugeneration.com>
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "X11/Xlib.h"
+
+#include "key.h"
+#include "clickety.h"
+#include "composite.h"
+#include "desktop.h"
+#include "screen.h"
+#include "vwm.h"
+#include "window.h"
+#include "xwindow.h"
+
+/* this forms the glue between the X event loop in vwm.c and the rest of the
+ * code making up vwm */
+
+void vwm_xevent_handle_key_press(vwm_t *vwm, XKeyPressedEvent *ev)
+{
+ vwm_key_pressed(vwm, ev->window, ev);
+}
+
+
+void vwm_xevent_handle_key_release(vwm_t *vwm, XKeyReleasedEvent *ev)
+{
+ vwm_key_released(vwm, ev->window, ev);
+}
+
+
+void vwm_xevent_handle_button_press(vwm_t *vwm, XButtonPressedEvent *ev)
+{
+ vwm_clickety_pressed(vwm, ev->window, ev);
+}
+
+
+void vwm_xevent_handle_motion_notify(vwm_t *vwm, XMotionEvent *ev)
+{
+ vwm_clickety_motion(vwm, ev->window, ev);
+}
+
+
+void vwm_xevent_handle_button_release(vwm_t *vwm, XButtonReleasedEvent *ev)
+{
+ vwm_clickety_released(vwm, ev->window, ev);
+}
+
+
+void vwm_xevent_handle_create_notify(vwm_t *vwm, XCreateWindowEvent *ev)
+{
+ vwm_xwin_create(vwm, ev->window, VWM_NOT_GRABBED);
+}
+
+
+void vwm_xevent_handle_destroy_notify(vwm_t *vwm, XDestroyWindowEvent *ev)
+{
+ vwm_xwindow_t *xwin;
+
+ if ((xwin = vwm_xwin_lookup(vwm, ev->window))) {
+ vwm_xwin_destroy(vwm, xwin);
+ }
+}
+
+
+void vwm_xevent_handle_configure_request(vwm_t *vwm, XConfigureRequestEvent *ev)
+{
+ XWindowChanges changes = {
+ .x = ev->x, /* TODO: for now I don't manipulate anything */
+ .y = ev->y,
+ .width = ev->width,
+ .height = ev->height,
+ .border_width = WINDOW_BORDER_WIDTH /* except I do override whatever the border width may be */
+ };
+ unsigned long change_mask = (ev->value_mask & (CWX | CWY | CWWidth | CWHeight)) | CWBorderWidth;
+ vwm_xwindow_t *xwin;
+
+ /* XXX: windows raising themselves is annoying, so discard CWSibling and CWStackMode. */
+
+ if ((xwin = vwm_xwin_lookup(vwm, ev->window)) &&
+ xwin->managed &&
+ xwin->managed->autoconfigured == VWM_WIN_AUTOCONF_ALL) {
+ /* this is to allow auto-allscreen to succeed in getting a borderless window configured */
+ change_mask &= ~CWBorderWidth;
+ }
+
+ XConfigureWindow(vwm->display, ev->window, change_mask, &changes);
+}
+
+
+void vwm_xevent_handle_configure_notify(vwm_t *vwm, XConfigureEvent *ev)
+{
+ vwm_xwindow_t *xwin;
+
+ if ((xwin = vwm_xwin_lookup(vwm, ev->window))) {
+ XWindowAttributes attrs;
+ vwm_xwin_restack(vwm, xwin, ev->above);
+ XGetWindowAttributes(vwm->display, ev->window, &attrs);
+ vwm_composite_handle_configure(vwm, xwin, &attrs);
+ VWM_TRACE("pre x=%i y=%i w=%i h=%i\n", xwin->attrs.x, xwin->attrs.y, xwin->attrs.width, xwin->attrs.height);
+ xwin->attrs = attrs;
+ VWM_TRACE("post x=%i y=%i w=%i h=%i\n", xwin->attrs.x, xwin->attrs.y, xwin->attrs.width, xwin->attrs.height);
+ }
+}
+
+
+void vwm_xevent_handle_unmap_notify(vwm_t *vwm, XUnmapEvent *ev)
+{
+ vwm_xwindow_t *xwin;
+
+ /* unlike MapRequest, we simply are notified when a window is unmapped. */
+ if ((xwin = vwm_xwin_lookup(vwm, ev->window))) {
+ if (xwin->managed) {
+ if (xwin->managed->unmapping) {
+ VWM_TRACE("swallowed vwm-induced UnmapNotify");
+ xwin->managed->unmapping = 0;
+ } else {
+ /* client requested unmap, demote the window and note the unmapped state */
+ vwm_win_unmanage(vwm, xwin->managed);
+ xwin->mapped = 0;
+ }
+ } else {
+ /* if it's not managed, we can't have caused the map */
+ xwin->mapped = 0;
+ }
+
+ vwm_composite_damage_win(vwm, xwin);
+ }
+}
+
+
+void vwm_xevent_handle_map_notify(vwm_t *vwm, XMapEvent *ev)
+{
+ vwm_xwindow_t *xwin;
+
+ if ((xwin = vwm_xwin_lookup(vwm, ev->window))) {
+ if (xwin->managed && xwin->managed->mapping) {
+ VWM_TRACE("swallowed vwm-induced MapNotify");
+ xwin->managed->mapping = 0;
+ } else {
+ /* some windows like popup dialog boxes bypass MapRequest */
+ xwin->mapped = 1;
+ }
+
+ vwm_composite_handle_map(vwm, xwin);
+ }
+}
+
+
+void vwm_xevent_handle_map_request(vwm_t *vwm, XMapRequestEvent *ev)
+{
+ vwm_xwindow_t *xwin;
+ vwm_window_t *vwin = NULL;
+ int domap = 1;
+
+ /* FIXME TODO: this is a fairly spuriously open-coded mess, this stuff
+ * needs to be factored and moved elsewhere */
+
+ if ((xwin = vwm_xwin_lookup(vwm, ev->window)) &&
+ ((vwin = xwin->managed) || (vwin = vwm_win_manage_xwin(vwm, xwin)))) {
+ XWindowAttributes attrs;
+ XWindowChanges changes = {.x = 0, .y = 0};
+ unsigned changes_mask = (CWX | CWY);
+ XClassHint *classhint;
+ const vwm_screen_t *scr = NULL;
+
+ xwin->mapped = 1; /* note that the client mapped the window */
+
+ /* figure out if the window is the console */
+ if ((classhint = XAllocClassHint())) {
+ if (XGetClassHint(vwm->display, ev->window, classhint) && !strcmp(classhint->res_class, CONSOLE_WM_CLASS)) {
+ vwm->console = vwin;
+ vwm_win_shelve(vwm, vwin);
+ vwm_win_autoconf(vwm, vwin, VWM_SCREEN_REL_XWIN, VWM_WIN_AUTOCONF_FULL);
+ domap = 0;
+ }
+
+ if (classhint->res_class) XFree(classhint->res_class);
+ if (classhint->res_name) XFree(classhint->res_name);
+ XFree(classhint);
+ }
+
+ /* TODO: this is a good place to hook in a window placement algo */
+
+ /* on client-requested mapping we place the window */
+ if (!vwin->shelved) {
+ /* we place the window on the screen containing the the pointer only if that screen is empty,
+ * otherwise we place windows on the screen containing the currently focused window */
+ /* since we query the geometry of windows in determining where to place them, a configuring
+ * flag is used to exclude the window being configured from those queries */
+ scr = vwm_screen_find(vwm, VWM_SCREEN_REL_POINTER);
+ vwin->configuring = 1;
+ if (vwm_screen_is_empty(vwm, scr)) {
+ /* focus the new window if it isn't already focused when it's going to an empty screen */
+ VWM_TRACE("window \"%s\" is alone on screen \"%i\", focusing", vwin->xwindow->name, scr->screen_number);
+ vwm_win_focus(vwm, vwin);
+ } else {
+ scr = vwm_screen_find(vwm, VWM_SCREEN_REL_XWIN, vwm->focused_desktop->focused_window->xwindow);
+ }
+ vwin->configuring = 0;
+
+ changes.x = scr->x_org;
+ changes.y = scr->y_org;
+ } else if (vwm->focused_context == VWM_CONTEXT_SHELF) {
+ scr = vwm_screen_find(vwm, VWM_SCREEN_REL_XWIN, vwm->focused_shelf->xwindow);
+ changes.x = scr->x_org;
+ changes.y = scr->y_org;
+ }
+
+ /* XXX TODO: does this belong here? */
+ XGetWMNormalHints(vwm->display, ev->window, vwin->hints, &vwin->hints_supplied);
+ XGetWindowAttributes(vwm->display, ev->window, &attrs);
+
+
+ /* if the window size is precisely the screen size then directly "allscreen" the window right here */
+ if (!vwin->shelved && scr &&
+ attrs.width == scr->width &&
+ attrs.height == scr->height) {
+ VWM_TRACE("auto-allscreened window \"%s\"", vwin->xwindow->name);
+ changes.border_width = 0;
+ changes_mask |= CWBorderWidth;
+ vwin->autoconfigured = VWM_WIN_AUTOCONF_ALL;
+ }
+
+ vwin->client.x = changes.x;
+ vwin->client.y = changes.y;
+ vwin->client.height = attrs.height;
+ vwin->client.width = attrs.width;
+
+ XConfigureWindow(vwm->display, ev->window, changes_mask, &changes);
+ }
+
+ if (domap) {
+ XMapWindow(vwm->display, ev->window);
+ if (vwin && vwin->desktop->focused_window == vwin) {
+ XSync(vwm->display, False);
+ XSetInputFocus(vwm->display, vwin->xwindow->id, RevertToPointerRoot, CurrentTime);
+ }
+ }
+}
+
+
+void vwm_xevent_handle_property_notify(vwm_t *vwm, XPropertyEvent *ev)
+{
+ vwm_xwindow_t *xwin;
+
+ if ((xwin = vwm_xwin_lookup(vwm, ev->window)) &&
+ ev->atom == vwm->wm_pid_atom &&
+ ev->state == PropertyNewValue) vwm_overlay_xwin_create(vwm, xwin);
+}
+
+
+void vwm_xevent_handle_mapping_notify(vwm_t *vwm, XMappingEvent *ev)
+{
+ XRefreshKeyboardMapping(ev);
+}
diff --git a/src/xevent.h b/src/xevent.h
new file mode 100644
index 0000000..dc710fd
--- /dev/null
+++ b/src/xevent.h
@@ -0,0 +1,18 @@
+#include "X11/Xlib.h"
+
+typedef struct _vwm_t vwm_t;
+
+void vwm_xevent_handle_key_press(vwm_t *vwm, XKeyPressedEvent *ev);
+void vwm_xevent_handle_key_release(vwm_t *vwm, XKeyReleasedEvent *ev);
+void vwm_xevent_handle_button_press(vwm_t *vwm, XButtonPressedEvent *ev);
+void vwm_xevent_handle_motion_notify(vwm_t *vwm, XMotionEvent *ev);
+void vwm_xevent_handle_button_release(vwm_t *vwm, XButtonReleasedEvent *ev);
+void vwm_xevent_handle_create_notify(vwm_t *vwm, XCreateWindowEvent *ev);
+void vwm_xevent_handle_destroy_notify(vwm_t *vwm, XDestroyWindowEvent *ev);
+void vwm_xevent_handle_configure_request(vwm_t *vwm, XConfigureRequestEvent *ev);
+void vwm_xevent_handle_configure_notify(vwm_t *vwm, XConfigureEvent *ev);
+void vwm_xevent_handle_unmap_notify(vwm_t *vwm, XUnmapEvent *ev);
+void vwm_xevent_handle_map_notify(vwm_t *vwm, XMapEvent *ev);
+void vwm_xevent_handle_map_request(vwm_t *vwm, XMapRequestEvent *ev);
+void vwm_xevent_handle_property_notify(vwm_t *vwm, XPropertyEvent *ev);
+void vwm_xevent_handle_mapping_notify(vwm_t *vwm, XMappingEvent *ev);
diff --git a/src/xwindow.c b/src/xwindow.c
new file mode 100644
index 0000000..f646aac
--- /dev/null
+++ b/src/xwindow.c
@@ -0,0 +1,247 @@
+/*
+ * \/\/\
+ *
+ * Copyright (C) 2012-2016 Vito Caputo - <vcaputo@gnugeneration.com>
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+ /* bare X windows stuff, there's a distinction between bare xwindows and the vwm managed windows */
+#include <X11/Xatom.h>
+#include <X11/Xlib.h>
+#include <stdlib.h>
+
+#include "composite.h"
+#include "list.h"
+#include "vwm.h"
+#include "window.h"
+#include "xwindow.h"
+
+#define HONOR_OVERRIDE_REDIRECT
+
+/* send a client message to a window (currently used for WM_DELETE) */
+void vwm_xwin_message(vwm_t *vwm, vwm_xwindow_t *xwin, Atom type, long foo)
+{
+ XEvent event;
+
+ memset(&event, 0, sizeof(event));
+ event.xclient.type = ClientMessage;
+ event.xclient.window = xwin->id;
+ event.xclient.message_type = type;
+ event.xclient.format = 32;
+ event.xclient.data.l[0] = foo;
+ event.xclient.data.l[1] = CurrentTime; /* XXX TODO: is CurrentTime actually correct to use for this purpose? */
+
+ XSendEvent(vwm->display, xwin->id, False, 0, &event);
+}
+
+
+/* look up the X window in the global xwindows list (includes unmanaged windows like override_redirect/popup menus) */
+vwm_xwindow_t * vwm_xwin_lookup(vwm_t *vwm, Window win)
+{
+ vwm_xwindow_t *tmp, *xwin = NULL;
+
+ list_for_each_entry(tmp, &vwm->xwindows, xwindows) {
+ if (tmp->id == win) {
+ xwin = tmp;
+ break;
+ }
+ }
+
+ return xwin;
+}
+
+
+/* determine if a window is mapped (vwm-mapped) according to the current context */
+int vwm_xwin_is_mapped(vwm_t *vwm, vwm_xwindow_t *xwin)
+{
+ int ret = 0;
+
+ if (!xwin->mapped) return 0;
+
+ if (xwin->managed) {
+ switch (vwm->focused_context) {
+ case VWM_CONTEXT_SHELF:
+ if (vwm->focused_shelf == xwin->managed) ret = xwin->mapped;
+ break;
+
+ case VWM_CONTEXT_DESKTOP:
+ if (vwm->focused_desktop == xwin->managed->desktop && !xwin->managed->shelved) ret = xwin->mapped;
+ break;
+
+ default:
+ VWM_BUG("Unsupported context");
+ break;
+ }
+ } else { /* unmanaged xwins like popup dialogs when mapped are always visible */
+ ret = 1;
+ }
+
+ /* annoyingly, Xorg stops delivering VisibilityNotify events for redirected windows, so we don't conveniently know if a window is obscured or not :( */
+ /* I could maintain my own data structure for answering this question, but that's pretty annoying when Xorg already has that knowledge. */
+
+ return ret;
+}
+
+
+/* creates and potentially manages a new window (called in response to CreateNotify events, and during startup for all existing windows) */
+/* if the window is already mapped and not an override_redirect window, it becomes managed here. */
+vwm_xwindow_t * vwm_xwin_create(vwm_t *vwm, Window win, vwm_grab_mode_t grabbed)
+{
+ XWindowAttributes attrs;
+ vwm_xwindow_t *xwin = NULL;
+
+ VWM_TRACE("creating %#x", (unsigned int)win);
+
+ /* prevent races */
+ if (!grabbed) {
+ XGrabServer(vwm->display);
+ XSync(vwm->display, False);
+ }
+
+ /* verify the window still exists */
+ if (!XGetWindowAttributes(vwm->display, win, &attrs)) goto _out_grabbed;
+
+ /* don't create InputOnly windows */
+ if (attrs.class == InputOnly) goto _out_grabbed;
+
+ if (!(xwin = (vwm_xwindow_t *)malloc(sizeof(vwm_xwindow_t)))) {
+ VWM_PERROR("Failed to allocate xwin");
+ goto _out_grabbed;
+ }
+
+ xwin->id = win;
+ xwin->attrs = attrs;
+ xwin->managed = NULL;
+ xwin->name = NULL;
+ XFetchName(vwm->display, win, &xwin->name);
+
+ xwin->monitor = NULL;
+ xwin->overlay.width = xwin->overlay.height = xwin->overlay.phase = 0;
+ xwin->overlay.gen_last_composed = -1;
+
+ /* This is so we get the PropertyNotify event and can get the pid when it's set post-create,
+ * with my _NET_WM_PID patch the property is immediately available */
+ XSelectInput(vwm->display, win, PropertyChangeMask);
+
+ /* we must track the mapped-by-client state of the window independent of managed vs. unmanaged because
+ * in the case of override_redirect windows they may be unmapped (invisible) or mapped (visible) like menus without being managed.
+ * otherwise we could just use !xwin.managed to indicate unmapped, which is more vwm2-like, but insufficient when compositing. */
+ xwin->mapped = (attrs.map_state != IsUnmapped);
+
+ vwm_overlay_xwin_create(vwm, xwin);
+ vwm_composite_xwin_create(vwm, xwin);
+
+ list_add_tail(&xwin->xwindows, &vwm->xwindows); /* created windows are always placed on the top of the stacking order */
+
+#ifdef HONOR_OVERRIDE_REDIRECT
+ if (!attrs.override_redirect && xwin->mapped) vwm_win_manage_xwin(vwm, xwin);
+#else
+ if (xwin->mapped) vwm_win_manage_xwin(vwm, xwin);
+#endif
+_out_grabbed:
+ if (!grabbed) XUngrabServer(vwm->display);
+
+ return xwin;
+}
+
+
+/* destroy a window, called in response to DestroyNotify events */
+/* if the window is also managed it will be unmanaged first */
+void vwm_xwin_destroy(vwm_t *vwm, vwm_xwindow_t *xwin)
+{
+ XGrabServer(vwm->display);
+ XSync(vwm->display, False);
+
+ if (xwin->managed) vwm_win_unmanage(vwm, xwin->managed);
+
+ list_del(&xwin->xwindows);
+
+ if (xwin->name) XFree(xwin->name);
+
+ vwm_overlay_xwin_destroy(vwm, xwin);
+ vwm_composite_xwin_destroy(vwm, xwin);
+
+ free(xwin);
+
+ XUngrabServer(vwm->display);
+}
+
+
+/* maintain the stack-ordered xwindows list, when new_above is != None xwin is to be placed above new_above, when == None xwin goes to the bottom. */
+void vwm_xwin_restack(vwm_t *vwm, vwm_xwindow_t *xwin, Window new_above)
+{
+ Window old_above;
+#ifdef TRACE
+ vwm_xwindow_t *tmp;
+ fprintf(stderr, "restack of %#x new_above=%#x\n", (unsigned int)xwin->id, (unsigned int)new_above);
+ fprintf(stderr, "restack pre:");
+ list_for_each_entry(tmp, &vwm->xwindows, xwindows) {
+ fprintf(stderr, " %#x", (unsigned int)tmp->id);
+ }
+ fprintf(stderr, "\n");
+#endif
+ if (xwin->xwindows.prev != &vwm->xwindows) {
+ old_above = list_entry(xwin->xwindows.prev, vwm_xwindow_t, xwindows)->id;
+ } else {
+ old_above = None;
+ }
+
+ if (old_above != new_above) {
+ vwm_xwindow_t *new;
+
+ if (new_above == None) { /* to the bottom of the stack, so just above the &xwindows head */
+ list_move(&xwin->xwindows, &vwm->xwindows);
+ } else if ((new = vwm_xwin_lookup(vwm, new_above))) { /* to just above new_above */
+ list_move(&xwin->xwindows, &new->xwindows);
+ }
+ }
+#ifdef TRACE
+ fprintf(stderr, "restack post:");
+ list_for_each_entry(tmp, &vwm->xwindows, xwindows) {
+ fprintf(stderr, " %#x", (unsigned int)tmp->id);
+ }
+ fprintf(stderr, "\n\n");
+#endif
+}
+
+/* create xwindows for all existing windows (for startup) */
+int vwm_xwin_create_existing(vwm_t *vwm)
+{
+ Window root, parent;
+ Window *children = NULL;
+ unsigned int n_children, i;
+
+ /* TODO FIXME I don't think this is right anymore, not since we went compositing and split managed vs. bare xwindows... */
+ XGrabServer(vwm->display);
+ XSync(vwm->display, False);
+ XQueryTree(vwm->display, VWM_XROOT(vwm), &root, &parent, &children, &n_children);
+
+ for (i = 0; i < n_children; i++) {
+ if (children[i] == None) continue;
+
+ if ((vwm_xwin_create(vwm, children[i], VWM_GRABBED) == NULL)) goto _fail_grabbed;
+ }
+
+ XUngrabServer(vwm->display);
+
+ if (children) XFree(children);
+
+ return 1;
+
+_fail_grabbed:
+ XUngrabServer(vwm->display);
+
+ if (children) XFree(children);
+
+ return 0;
+}
diff --git a/src/xwindow.h b/src/xwindow.h
new file mode 100644
index 0000000..f3be681
--- /dev/null
+++ b/src/xwindow.h
@@ -0,0 +1,52 @@
+#ifndef _XWIN_H
+#define _XWIN_H
+
+#include <X11/extensions/Xdamage.h>
+#include <X11/extensions/Xrender.h>
+#include <X11/Xlib.h>
+
+#include "libvmon/vmon.h"
+#include "list.h"
+#include "overlay.h"
+
+typedef struct _vwm_t vwm_t;
+typedef struct _vwm_window_t vwm_window_t;
+
+/* every window gets this, even non-managed ones. For compositing vwm must track everything visible, even popup menus. */
+typedef struct _vwm_xwindow_t {
+ list_head_t xwindows; /* global list of all windows kept in X stacking order */
+
+ Window id; /* X Window backing this instance */
+ XWindowAttributes attrs; /* X window's current attributes, kept up-to-date in handling of ConfigureNotify events */
+ Damage damage; /* X damage object associated with the window (for compositing) */
+ Picture picture; /* X picture object representing the window (for compositing) */
+ Pixmap pixmap; /* X pixmap object representing the window (for compositing) */
+
+ vmon_proc_t *monitor; /* vmon process monitor handle, may be NULL if for example the X client doesn't supply a PID */
+ vwm_overlay_t overlay; /* monitoring overlay state */
+
+ char *name; /* client name */
+ unsigned int mapped:1; /* is the window currently mapped (by client) */
+ unsigned int occluded:1; /* is the window occluded entirely by another window? (used and valid only during paint_all()) */
+ /* if only Xorg could send VisibilityNotify events when requested for redirected windows :( */
+ vwm_window_t *managed; /* is the window "managed"? NULL or this points to the managed context of the window */
+} vwm_xwindow_t;
+
+/* creates and potentially manages a new window (called in response to CreateNotify events, and during startup for all existing windows) */
+/* if the window is already mapped and not an override_redirect window, it becomes managed here. */
+typedef enum _vwm_grab_mode_t {
+ VWM_NOT_GRABBED = 0,
+ VWM_GRABBED
+} vwm_grab_mode_t;
+
+void vwm_xwin_message(vwm_t *vwm, vwm_xwindow_t *xwin, Atom type, long foo);
+vwm_xwindow_t * vwm_xwin_lookup(vwm_t *vwm, Window win);
+int vwm_xwin_is_mapped(vwm_t *vwm, vwm_xwindow_t *xwin);
+void vwm_xwin_monitor(vwm_t *vwm, vwm_xwindow_t *xwin);
+vwm_xwindow_t * vwm_xwin_create(vwm_t *vwm, Window win, vwm_grab_mode_t grabbed);
+void vwm_xwin_destroy(vwm_t *vwm, vwm_xwindow_t *xwin);
+void vwm_xwin_restack(vwm_t *vwm, vwm_xwindow_t *xwin, Window new_above);
+int vwm_xwin_create_existing(vwm_t *vwm);
+
+
+#endif
diff --git a/vwm.c b/vwm.c
deleted file mode 100644
index f6aa4f7..0000000
--- a/vwm.c
+++ /dev/null
@@ -1,3285 +0,0 @@
-/*
- * \/\/\
- *
- * Copyright (C) 2012-2015 Vito Caputo - <vcaputo@gnugeneration.com>
- *
- * This program is free software: you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 3 as published
- * by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-/* The compositing code is heavily influenced by Keith Packard's xcompmgr.
- */
-
-#include <X11/Xlib.h>
-#include <X11/keysym.h>
-#include <X11/cursorfont.h>
-#include <X11/Xatom.h>
-#include <X11/extensions/sync.h> /* SYNC extension, enables us to give vwm the highest X client priority, helps keep vwm responsive at all times */
-#include <X11/extensions/Xinerama.h> /* XINERAMA extension, facilitates easy multihead awareness */
-#include <X11/extensions/Xrandr.h> /* RANDR extension, facilitates display configuration change awareness */
-#include <X11/extensions/Xdamage.h> /* Damage extension, enables receipt of damage events, reports visible regions needing updating (compositing) */
-#include <X11/extensions/Xrender.h> /* Render extension, enables use of alpha channels and accelerated rendering of surfaces having alpha (compositing) */
-#include <X11/extensions/Xcomposite.h> /* Composite extension, enables off-screen redirection of window rendering (compositing) */
-#include <X11/extensions/Xfixes.h> /* XFixes extension exposes things like regions (compositing) */
-#include <unistd.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdint.h>
-#include <stdarg.h>
-#include <string.h>
-#include <inttypes.h>
-#include <values.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <sys/wait.h>
-#include <sys/time.h>
-#include <sys/resource.h>
-#include <poll.h>
-#include "libvmon/vmon.h"
-#include "vwm.h"
-
-#define WINDOW_BORDER_WIDTH 1
-#define CONSOLE_WM_CLASS "VWMConsoleXTerm" /* the class we specify to the "console" xterm */
-#define CONSOLE_SESSION_STRING "_vwm_console.$DISPLAY" /* the unique console screen session identifier */
-
-#define WM_GRAB_MODIFIER Mod1Mask /* the modifier for invoking vwm's controls */
- /* Mod4Mask would be the windows key instead of Alt, but there's an assumption
- * in the code that grabs are being activated by Alt which complicates changing it,
- * search for XGetModifierMapping to see where, feel free to fix it. Or you can
- * just hack the code to expect the appropriate key instead of Alt, I didn't see the
- * value of making it modifier mapping aware if it's always Alt for me. */
-#define LAUNCHED_RELATIVE_PRIORITY 10 /* the wm priority plus this is used as the priority of launched processes */
-#define HONOR_OVERRIDE_REDIRECT /* search for HONOR_OVERRIDE_REDIRECT for understanding */
-
-
-#define OVERLAY_MASK_DEPTH 8 /* XXX: 1 would save memory, but Xorg isn't good at it */
-#define OVERLAY_MASK_FORMAT PictStandardA8
-#define OVERLAY_FIXED_FONT "-misc-fixed-medium-r-semicondensed--13-120-75-75-c-60-iso10646-1"
-#define OVERLAY_ROW_HEIGHT 15 /* this should always be larger than the font height */
-#define OVERLAY_GRAPH_MIN_WIDTH 200 /* always create graphs at least this large */
-#define OVERLAY_GRAPH_MIN_HEIGHT (4 * OVERLAY_ROW_HEIGHT)
-#define OVERLAY_ISTHREAD_ARGV "~" /* use this string to mark threads in the argv field */
-#define OVERLAY_NOCOMM_ARGV "#missed it!" /* use this string to substitute the command when missing in argv field */
-
-
-typedef enum _vwm_context_focus_t {
- VWM_CONTEXT_FOCUS_OTHER = 0, /* focus the other context relative to the current one */
- VWM_CONTEXT_FOCUS_DESKTOP, /* focus the desktop context */
- VWM_CONTEXT_FOCUS_SHELF /* focus the shelf context */
-} vwm_context_focus_t;
-
-typedef enum _vwm_compositing_mode_t {
- VWM_COMPOSITING_OFF = 0, /* non-composited, no redirected windows, most efficient */
- VWM_COMPOSITING_MONITORS = 1 /* composited process monitoring overlays, slower but really useful. */
-} vwm_compositing_mode_t;
-
-typedef XineramaScreenInfo vwm_screen_t; /* conveniently reuse the xinerama type for describing screens */
-
-static LIST_HEAD(desktops); /* global list of all (virtual) desktops in spatial created-in order */
-static LIST_HEAD(desktops_mru); /* global list of all (virtual) desktops in MRU order */
-static LIST_HEAD(windows_mru); /* global list of all managed windows kept in MRU order */
-static LIST_HEAD(xwindows); /* global list of all xwindows kept in the X server stacking order */
-static vwm_window_t *console = NULL; /* the console window */
-static vwm_window_t *focused_origin = NULL; /* the originating window in a grabbed operation/transaction */
-static vwm_desktop_t *focused_desktop = NULL; /* currently focused (virtual) desktop */
-static vwm_window_t *focused_shelf = NULL; /* currently focused shelved window */
-static vwm_context_focus_t focused_context = VWM_CONTEXT_FOCUS_DESKTOP; /* currently focused context */
-static vwm_compositing_mode_t compositing_mode = VWM_COMPOSITING_OFF; /* current compositing mode */
-static int key_is_grabbed = 0; /* flag for tracking keyboard grab state */
-static int priority; /* scheduling priority of the vwm process, launcher nices relative to this */
-static unsigned long fence_mask = 0; /* global mask state for vwm_win_focus_next(... VWM_FENCE_MASKED_VIOLATE),
- * if you use vwm on enough screens to overflow this, pics or it didn't happen. */
-
- /* Uninteresting stuff */
-static Display *display;
-static Colormap cmap;
-
-#define color(_sym, _str) \
-static XColor _sym ## _color;
-#include "colors.def"
-#undef color
-
-static int screen_num;
-static GC gc;
-static Atom wm_delete_atom;
-static Atom wm_protocols_atom;
-static Atom wm_pid_atom;
-static int sync_event, sync_error;
-
- /* Xinerama */
-static int xinerama_event, xinerama_error;
-static XineramaScreenInfo *xinerama_screens = NULL;
-static int xinerama_screens_cnt;
-static int randr_event, randr_error;
-
- /* Compositing */
-static int composite_event, composite_error, composite_opcode;
-static int damage_event, damage_error;
-static XserverRegion combined_damage = None;
-static Picture root_picture = None, root_buffer = None; /* compositing gets double buffered */
-static XWindowAttributes root_attrs;
-static XRenderPictureAttributes pa_inferiors = { .subwindow_mode = IncludeInferiors };
-
- /* Compositing / Overlays */
-static XFontStruct *overlay_font;
-static GC text_gc;
-static XRenderPictureAttributes pa_repeat = { .repeat = 1 };
-static XRenderPictureAttributes pa_no_repeat = { .repeat = 0 };
-static Picture overlay_shadow_fill, /* TODO: the repetition here smells like an XMacro waiting to happen */
- overlay_text_fill,
- overlay_bg_fill,
- overlay_snowflakes_text_fill,
- overlay_grapha_fill,
- overlay_graphb_fill,
- overlay_finish_fill;
-static XRenderColor overlay_visible_color = { 0xffff, 0xffff, 0xffff, 0xffff },
- overlay_shadow_color = { 0x0000, 0x0000, 0x0000, 0x8800},
- overlay_bg_color = { 0x0, 0x1000, 0x0, 0x9000},
- overlay_div_color = { 0x2000, 0x3000, 0x2000, 0x9000},
- overlay_snowflakes_visible_color = { 0xd000, 0xd000, 0xd000, 0x8000 },
- overlay_trans_color = {0x00, 0x00, 0x00, 0x00},
- overlay_grapha_color = { 0xff00, 0x0000, 0x0000, 0x3000 }, /* ~red */
- overlay_graphb_color = { 0x0000, 0xffff, 0xffff, 0x3000 }; /* ~cyan */
-
- /* libvmon */
-static struct timeval maybe_sample, last_sample, this_sample = {0,0};
-static typeof(((vmon_sys_stat_t *)0)->user) last_user_cpu;
-static typeof(((vmon_sys_stat_t *)0)->system) last_system_cpu;
-static unsigned long long last_total, this_total, total_delta;
-static unsigned long long last_idle, last_iowait, idle_delta, iowait_delta;
-static vmon_t vmon;
-
-static float sampling_intervals[] = {
- 1, /* ~1Hz */
- .1, /* ~10Hz */
- .05, /* ~20Hz */
- .025, /* ~40Hz */
- .01666}; /* ~60Hz */
-static int prev_sampling_interval = 1, sampling_interval = 1;
-
- /* some needed prototypes */
-static vwm_xwindow_t * vwm_xwin_lookup(Window win);
-static inline int vwm_xwin_is_mapped(vwm_xwindow_t *xwin);
-static void vwm_comp_damage_add(XserverRegion damage);
-static vwm_xwindow_t * vwm_win_unmanage(vwm_window_t *vwin);
-static vwm_window_t * vwm_win_manage_xwin(vwm_xwindow_t *xwin);
-static void vwm_win_unmap(vwm_window_t *vwin);
-static void vwm_win_map(vwm_window_t *vwin);
-static void vwm_win_focus(vwm_window_t *vwin);
-static void vwm_keypressed(Window win, XEvent *keypress);
-
-#define MIN(_a, _b) ((_a) < (_b) ? (_a) : (_b))
-#define MAX(_a, _b) ((_a) > (_b) ? (_a) : (_b))
-
-
- /* libvmon integration, warning: this gets a little crazy especially in the rendering. */
-
-/* space we need for every process being monitored */
-typedef struct _vwm_perproc_ctxt_t {
- typeof(vmon.generation) generation;
- typeof(((vmon_proc_stat_t *)0)->utime) last_utime;
- typeof(((vmon_proc_stat_t *)0)->stime) last_stime;
- typeof(((vmon_proc_stat_t *)0)->utime) utime_delta;
- typeof(((vmon_proc_stat_t *)0)->stime) stime_delta;
-} vwm_perproc_ctxt_t;
-
-
-/* moves what's below a given row up above it if specified, the row becoming discarded */
-static void snowflake_row(vwm_xwindow_t *xwin, Picture pic, int copy, int row)
-{
- VWM_TRACE("pid=%i xwin=%p row=%i copy=%i heirarhcy_end=%i", xwin->monitor->pid, xwin, row, copy, xwin->overlay.heirarchy_end);
-
- if (copy) {
- /* copy row to tmp */
- XRenderComposite(display, PictOpSrc, pic, None, xwin->overlay.tmp_picture,
- 0, row * OVERLAY_ROW_HEIGHT, /* src */
- 0, 0, /* mask */
- 0, 0, /* dest */
- xwin->overlay.width, OVERLAY_ROW_HEIGHT); /* dimensions */
- }
-
- /* shift up */
- XRenderChangePicture(display, pic, CPRepeat, &pa_no_repeat);
- XRenderComposite(display, PictOpSrc, pic, None, pic,
- 0, (1 + row) * OVERLAY_ROW_HEIGHT, /* src */
- 0, 0, /* mask */
- 0, row * OVERLAY_ROW_HEIGHT, /* dest */
- xwin->overlay.width, (1 + xwin->overlay.heirarchy_end) * OVERLAY_ROW_HEIGHT - (1 + row) * OVERLAY_ROW_HEIGHT); /* dimensions */
- XRenderChangePicture(display, pic, CPRepeat, &pa_repeat);
-
- if (copy) {
- /* copy tmp to top of snowflakes */
- XRenderComposite(display, PictOpSrc, xwin->overlay.tmp_picture, None, pic,
- 0, 0, /* src */
- 0, 0, /* mask */
- 0, (xwin->overlay.heirarchy_end) * OVERLAY_ROW_HEIGHT, /* dest */
- xwin->overlay.width, OVERLAY_ROW_HEIGHT); /* dimensions */
- } else {
- /* clear the snowflake row */
- XRenderFillRectangle(display, PictOpSrc, pic, &overlay_trans_color,
- 0, (xwin->overlay.heirarchy_end) * OVERLAY_ROW_HEIGHT, /* dest */
- xwin->overlay.width, OVERLAY_ROW_HEIGHT); /* dimensions */
- }
-}
-
-/* XXX TODO libvmon automagic children following races with explicit X client pid monitoring with different outcomes, it should be irrelevant which wins,
- * currently the only visible difference is the snowflakes gap (heirarchy_end) varies, which is why I haven't bothered to fix it, I barely even notice.
- */
-
-/* shifts what's below a given row down a row, and clears the row, preparing it for populating */
-static void allocate_row(vwm_xwindow_t *xwin, Picture pic, int row)
-{
- VWM_TRACE("pid=%i xwin=%p row=%i", xwin->monitor->pid, xwin, row);
-
- /* shift everything below the row down */
- XRenderComposite(display, PictOpSrc, pic, None, pic,
- 0, row * OVERLAY_ROW_HEIGHT, /* src */
- 0, 0, /* mask */
- 0, (1 + row) * OVERLAY_ROW_HEIGHT, /* dest */
- xwin->overlay.width, xwin->overlay.height - (1 + row) * OVERLAY_ROW_HEIGHT); /* dimensions */
- /* fill the space created with transparent pixels */
- XRenderFillRectangle(display, PictOpSrc, pic, &overlay_trans_color,
- 0, row * OVERLAY_ROW_HEIGHT, /* dest */
- xwin->overlay.width, OVERLAY_ROW_HEIGHT); /* dimensions */
-}
-
-
-/* shadow a row from the text layer in the shadow layer */
-static void shadow_row(vwm_xwindow_t *xwin, int row)
-{
- /* the current technique for creating the shadow is to simply render the text at +1/-1 pixel offsets on both axis in translucent black */
- XRenderComposite(display, PictOpSrc, overlay_shadow_fill, xwin->overlay.text_picture, xwin->overlay.shadow_picture,
- 0, 0,
- -1, row * OVERLAY_ROW_HEIGHT,
- 0, row * OVERLAY_ROW_HEIGHT,
- xwin->attrs.width, OVERLAY_ROW_HEIGHT);
-
- XRenderComposite(display, PictOpOver, overlay_shadow_fill, xwin->overlay.text_picture, xwin->overlay.shadow_picture,
- 0, 0,
- 0, -1 + row * OVERLAY_ROW_HEIGHT,
- 0, row * OVERLAY_ROW_HEIGHT,
- xwin->attrs.width, OVERLAY_ROW_HEIGHT);
-
- XRenderComposite(display, PictOpOver, overlay_shadow_fill, xwin->overlay.text_picture, xwin->overlay.shadow_picture,
- 0, 0,
- 1, row * OVERLAY_ROW_HEIGHT,
- 0, row * OVERLAY_ROW_HEIGHT,
- xwin->attrs.width, OVERLAY_ROW_HEIGHT);
-
- XRenderComposite(display, PictOpOver, overlay_shadow_fill, xwin->overlay.text_picture, xwin->overlay.shadow_picture,
- 0, 0,
- 0, 1 + row * OVERLAY_ROW_HEIGHT,
- 0, row * OVERLAY_ROW_HEIGHT,
- xwin->attrs.width, OVERLAY_ROW_HEIGHT);
-}
-
-
-/* simple helper to map the vmon per-proc argv array into an XTextItem array, deals with threads vs. processes and the possibility of the comm field not getting read in before the process exited... */
-static void argv2xtext(vmon_proc_t *proc, XTextItem *items, int *nr_items) /* XXX TODO: reallocate items when too small... */
-{
- int i;
- int nr = 0;
-
- if (proc->is_thread) { /* stick the thread marker at the start of threads */
- items[0].nchars = sizeof(OVERLAY_ISTHREAD_ARGV) - 1;
- items[0].chars = OVERLAY_ISTHREAD_ARGV;
- items[0].delta = 4;
- items[0].font = None;
- nr++;
- }
-
- if (((vmon_proc_stat_t *)proc->stores[VMON_STORE_PROC_STAT])->comm.len) {
- items[nr].nchars = ((vmon_proc_stat_t *)proc->stores[VMON_STORE_PROC_STAT])->comm.len - 1;
- items[nr].chars = ((vmon_proc_stat_t *)proc->stores[VMON_STORE_PROC_STAT])->comm.array;
- } else {
- /* sometimes a process is so ephemeral we don't manage to sample its comm, XXX TODO: we always have a pid, stringify it? */
- items[nr].nchars = sizeof(OVERLAY_NOCOMM_ARGV) - 1;
- items[nr].chars = OVERLAY_NOCOMM_ARGV;
- }
- items[nr].delta = 4;
- items[nr].font = None;
- nr++;
-
- for (i = 1; i < ((vmon_proc_stat_t *)proc->stores[VMON_STORE_PROC_STAT])->argc; nr++, i++) {
- items[nr].chars = ((vmon_proc_stat_t *)proc->stores[VMON_STORE_PROC_STAT])->argv[i];
- items[nr].nchars = strlen(((vmon_proc_stat_t *)proc->stores[VMON_STORE_PROC_STAT])->argv[i]); /* TODO: libvmon should inform us of the length */
- items[nr].delta = 4;
- items[nr].font = None;
- }
-
- (*nr_items) = nr;
-}
-
-
-/* helper for counting number of existing descendants subtrees */
-static int count_rows(vmon_proc_t *proc) {
- int count = 1; /* XXX maybe suppress proc->is_new? */
- vmon_proc_t *child;
-
- if (!proc->is_thread) {
- list_for_each_entry(child, &proc->threads, threads) {
- count += count_rows(child);
- }
- }
-
- list_for_each_entry(child, &proc->children, siblings) {
- count += count_rows(child);
- }
-
- return count;
-}
-
-
-/* recursive draw function for the consolidated version of the overlay rendering which also implements snowflakes */
-static void draw_overlay(vwm_xwindow_t *xwin, vmon_proc_t *proc, int *depth, int *row)
-{
- vmon_proc_t *child;
- vwm_perproc_ctxt_t *proc_ctxt = proc->foo;
- vmon_proc_stat_t *proc_stat = proc->stores[VMON_STORE_PROC_STAT];
-
- /* graph variables */
- int a_height, b_height;
- double utime_delta, stime_delta;
-
- /* text variables */
- char str[256];
- int str_len;
- XTextItem items[1024]; /* XXX TODO: dynamically allocate this and just keep it at the high water mark.. create a struct to encapsulate this, nr_items, and alloc_items... */
- int nr_items;
- int direction, ascent, descent;
- XCharStruct charstruct;
-
- if ((*row)) { /* except row 0 (Idle/IOWait graph), handle any stale and new processes/threads */
- if (proc->is_stale) {
- /* what to do when a process (subtree) has gone away */
- static int in_stale = 0;
- int in_stale_entrypoint = 0;
-
- /* I snowflake the stale processes from the leaves up for a more intuitive snowflake order...
- * (I expect the command at the root of the subtree to appear at the top of the snowflakes...) */
- /* This does require that I do a separate forward recursion to determine the number of rows
- * so I can correctly snowflake in reverse */
- if (!in_stale) {
- VWM_TRACE("entered stale at xwin=%p depth=%i row=%i", xwin, *depth, *row);
- in_stale_entrypoint = in_stale = 1;
- (*row) += count_rows(proc) - 1;
- }
-
- (*depth)++;
- list_for_each_entry_prev(child, &proc->children, siblings) {
- draw_overlay(xwin, child, depth, row);
- (*row)--;
- }
-
- if (!proc->is_thread) {
- list_for_each_entry_prev(child, &proc->threads, threads) {
- draw_overlay(xwin, child, depth, row);
- (*row)--;
- }
- }
- (*depth)--;
-
- VWM_TRACE("%i (%.*s) is stale @ depth %i row %i is_thread=%i", proc->pid,
- ((vmon_proc_stat_t *)proc->stores[VMON_STORE_PROC_STAT])->comm.len - 1,
- ((vmon_proc_stat_t *)proc->stores[VMON_STORE_PROC_STAT])->comm.array,
- (*depth), (*row), proc->is_thread);
-
- /* stamp the graphs with the finish line */
- XRenderComposite(display, PictOpSrc, overlay_finish_fill, None, xwin->overlay.grapha_picture,
- 0, 0, /* src x, y */
- 0, 0, /* mask x, y */
- xwin->overlay.phase, (*row) * OVERLAY_ROW_HEIGHT, /* dst x, y */
- 1, OVERLAY_ROW_HEIGHT - 1);
- XRenderComposite(display, PictOpSrc, overlay_finish_fill, None, xwin->overlay.graphb_picture,
- 0, 0, /* src x, y */
- 0, 0, /* mask x, y */
- xwin->overlay.phase, (*row) * OVERLAY_ROW_HEIGHT, /* dst x, y */
- 1, OVERLAY_ROW_HEIGHT - 1);
-
- /* extract the row from the various layers */
- snowflake_row(xwin, xwin->overlay.grapha_picture, 1, (*row));
- snowflake_row(xwin, xwin->overlay.graphb_picture, 1, (*row));
- snowflake_row(xwin, xwin->overlay.text_picture, 0, (*row));
- snowflake_row(xwin, xwin->overlay.shadow_picture, 0, (*row));
- xwin->overlay.snowflakes_cnt++;
-
- /* stamp the name (and whatever else we include) into overlay.text_picture */
- argv2xtext(proc, items, &nr_items);
- XDrawText(display, xwin->overlay.text_pixmap, text_gc,
- 5, (xwin->overlay.heirarchy_end + 1) * OVERLAY_ROW_HEIGHT - 3,/* dst x, y */
- items, nr_items);
- shadow_row(xwin, xwin->overlay.heirarchy_end);
-
- xwin->overlay.heirarchy_end--;
-
- if (in_stale_entrypoint) {
- VWM_TRACE("exited stale at xwin=%p depth=%i row=%i", xwin, *depth, *row);
- in_stale = 0;
- }
-
- return;
- } else if (proc->is_new) {
- /* what to do when a process has been introduced */
- VWM_TRACE("%i is new", proc->pid);
-
- allocate_row(xwin, xwin->overlay.grapha_picture, (*row));
- allocate_row(xwin, xwin->overlay.graphb_picture, (*row));
- allocate_row(xwin, xwin->overlay.text_picture, (*row));
- allocate_row(xwin, xwin->overlay.shadow_picture, (*row));
-
- xwin->overlay.heirarchy_end++;
- }
- }
-
-/* CPU utilization graphs */
- if (!(*row)) {
- /* XXX: sortof kludged in IOWait and Idle % @ row 0 */
- stime_delta = iowait_delta;
- utime_delta = idle_delta;
- } else {
- /* use the generation number to avoid recomputing this stuff for callbacks recurring on the same process in the same sample */
- if (proc_ctxt->generation != vmon.generation) {
- proc_ctxt->stime_delta = proc_stat->stime - proc_ctxt->last_stime;
- proc_ctxt->utime_delta = proc_stat->utime - proc_ctxt->last_utime;
- proc_ctxt->last_utime = proc_stat->utime;
- proc_ctxt->last_stime = proc_stat->stime;
-
- proc_ctxt->generation = vmon.generation;
- }
-
- if (proc->is_new) {
- /* we need a minimum of two samples before we can compute a delta to plot,
- * so we suppress that and instead mark the start of monitoring with an impossible 100% of both graph contexts, a starting line. */
- stime_delta = utime_delta = total_delta;
- } else {
- stime_delta = proc_ctxt->stime_delta;
- utime_delta = proc_ctxt->utime_delta;
- }
- }
-
- /* compute the bar heights for this sample */
- a_height = (stime_delta / total_delta * (double)(OVERLAY_ROW_HEIGHT - 1)); /* give up 1 pixel for the div */
- b_height = (utime_delta / total_delta * (double)(OVERLAY_ROW_HEIGHT - 1));
-
- /* round up to 1 pixel when the scaled result is a fraction less than 1,
- * I want to at least see 1 pixel blips for the slightest cpu utilization */
- if (stime_delta && !a_height) a_height = 1;
- if (utime_delta && !b_height) b_height = 1;
-
- /* draw the two bars for this sample at the current phase in the graphs, note the first is ceiling-based, second floor-based */
- XRenderFillRectangle(display, PictOpSrc, xwin->overlay.grapha_picture, &overlay_visible_color,
- xwin->overlay.phase, (*row) * OVERLAY_ROW_HEIGHT, /* dst x, y */
- 1, a_height); /* dst w, h */
- XRenderFillRectangle(display, PictOpSrc, xwin->overlay.graphb_picture, &overlay_visible_color,
- xwin->overlay.phase, (*row) * OVERLAY_ROW_HEIGHT + (OVERLAY_ROW_HEIGHT - b_height) - 1, /* dst x, y */
- 1, b_height); /* dst w, h */
-
- if (!(*row)) {
- /* here's where the Idle/IOWait row drawing concludes */
- if (compositing_mode) {
- snprintf(str, sizeof(str), "\\/\\/\\ %2iHz %n", (int)(sampling_interval < 0 ? 0 : 1 / sampling_intervals[sampling_interval]), &str_len);
- /* TODO: I clear and redraw this row every time, which is unnecessary, small optimization would be to only do so when:
- * - overlay resized, and then constrain the clear to the affected width
- * - Hz changed
- */
- XRenderFillRectangle(display, PictOpSrc, xwin->overlay.text_picture, &overlay_trans_color,
- 0, 0, /* dst x, y */
- xwin->attrs.width, OVERLAY_ROW_HEIGHT); /* dst w, h */
- XTextExtents(overlay_font, str, str_len, &direction, &ascent, &descent, &charstruct);
- XDrawString(display, xwin->overlay.text_pixmap, text_gc,
- xwin->attrs.width - charstruct.width, OVERLAY_ROW_HEIGHT - 3, /* dst x, y */
- str, str_len);
- shadow_row(xwin, 0);
- }
- (*row)++;
- draw_overlay(xwin, proc, depth, row);
- return;
- }
-
-/* process heirarchy text and accompanying per-process details like wchan/pid/state... */
- if (compositing_mode) { /* this stuff can be skipped when monitors aren't visible */
- /* TODO: make the columns interactively configurable @ runtime */
- if (!proc->is_new) {
- /* XXX for now always clear the row, this should be capable of being optimized in the future (if the datums driving the text haven't changed...) */
- XRenderFillRectangle(display, PictOpSrc, xwin->overlay.text_picture, &overlay_trans_color,
- 0, (*row) * OVERLAY_ROW_HEIGHT, /* dst x, y */
- xwin->overlay.width, OVERLAY_ROW_HEIGHT); /* dst w, h */
- }
-
- /* put the process' wchan, state, and PID columns @ the far right */
- if (proc->is_thread || list_empty(&proc->threads)) { /* only threads or non-threaded processes include the wchan and state */
- snprintf(str, sizeof(str), " %.*s %5i %c %n",
- proc_stat->wchan.len,
- proc_stat->wchan.len == 1 && proc_stat->wchan.array[0] == '0' ? "-" : proc_stat->wchan.array,
- proc->pid,
- proc_stat->state,
- &str_len);
- } else { /* we're a process having threads, suppress the wchan and state, as they will be displayed for the thread of same pid */
- snprintf(str, sizeof(str), " %5i %n", proc->pid, &str_len);
- }
-
- XTextExtents(overlay_font, str, str_len, &direction, &ascent, &descent, &charstruct);
-
- /* the process' comm label indented according to depth, followed with their respective argv's */
- argv2xtext(proc, items, &nr_items);
- XDrawText(display, xwin->overlay.text_pixmap, text_gc,
- (*depth) * (OVERLAY_ROW_HEIGHT / 2), ((*row) + 1) * OVERLAY_ROW_HEIGHT - 3, /* dst x, y */
- items, nr_items);
-
- /* ensure the area for the rest of the stuff is cleared, we don't put much text into thread rows so skip it for those. */
- if (!proc->is_thread) {
- XRenderFillRectangle(display, PictOpSrc, xwin->overlay.text_picture, &overlay_trans_color,
- xwin->attrs.width - charstruct.width, (*row) * OVERLAY_ROW_HEIGHT, /* dst x,y */
- xwin->overlay.width - (xwin->attrs.width - charstruct.width), OVERLAY_ROW_HEIGHT); /* dst w,h */
- }
-
- XDrawString(display, xwin->overlay.text_pixmap, text_gc,
- xwin->attrs.width - charstruct.width, ((*row) + 1) * OVERLAY_ROW_HEIGHT - 3, /* dst x, y */
- str, str_len);
-
- /* only if this process isn't the root process @ the window shall we consider all relational drawing conditions */
- if (proc != xwin->monitor) {
- vmon_proc_t *ancestor, *sibling, *last_sibling = NULL;
- struct list_head *rem;
- int needs_tee = 0;
- int bar_x = 0, bar_y = 0;
- int sub;
-
- /* XXX: everything done in this code block only dirties _this_ process' row in the rendered overlay output */
-
- /* walk up the ancestors until reaching xwin->monitor, any ancestors we encounter which have more siblings we draw a vertical bar for */
- /* this draws the |'s in something like: | | | | comm */
- for (sub = 1, ancestor = proc->parent; ancestor && ancestor != xwin->monitor; ancestor = ancestor->parent) {
- sub++;
- bar_x = ((*depth) - sub) * (OVERLAY_ROW_HEIGHT / 2) + 4;
- bar_y = ((*row) + 1) * OVERLAY_ROW_HEIGHT;
-
- /* determine if the ancestor has remaining siblings which are not stale, if so, draw a connecting bar at its depth */
- for (rem = ancestor->siblings.next; rem != &ancestor->parent->children; rem = rem->next) {
- if (!(list_entry(rem, vmon_proc_t, siblings)->is_stale)) {
- XDrawLine(display, xwin->overlay.text_pixmap, text_gc,
- bar_x, bar_y - OVERLAY_ROW_HEIGHT, /* dst x1, y1 */
- bar_x, bar_y); /* dst x2, y2 (vertical line) */
- break; /* stop looking for more siblings at this ancestor when we find one that isn't stale */
- }
- }
- }
-
- /* determine if _any_ of our siblings have children requiring us to draw a tee immediately before our comm string.
- * The only sibling which doesn't cause this to happen is the last one in the children list, if it has children it has no impact on its remaining
- * siblings, as there are none.
- *
- * This draws the + in something like: | | | | +comm
- */
-
- /* find the last sibling (this has to be done due to the potential for stale siblings at the tail, and we'd rather not repeatedly check for it) */
- list_for_each_entry(sibling, &proc->parent->children, siblings) {
- if (!sibling->is_stale) last_sibling = sibling;
- }
-
- /* now look for siblings with non-stale children to determine if a tee is needed, ignoring the last sibling */
- list_for_each_entry(sibling, &proc->parent->children, siblings) {
- /* skip stale siblings, they aren't interesting as they're invisible, and the last sibling has no bearing on wether we tee or not. */
- if (sibling->is_stale || sibling == last_sibling) continue;
-
- /* if any of the other siblings have children which are not stale, put a tee in front of our name, but ignore stale children */
- list_for_each_entry(child, &sibling->children, siblings) {
- if (!child->is_stale) {
- needs_tee = 1;
- break;
- }
- }
-
- /* if we still don't think we need a tee, check if there are threads */
- if (!needs_tee) {
- list_for_each_entry(child, &sibling->threads, threads) {
- if (!child->is_stale) {
- needs_tee = 1;
- break;
- }
- }
- }
-
- /* found a tee is necessary, all that's left is to determine if the tee is a corner and draw it accordingly, stopping the search. */
- if (needs_tee) {
- bar_x = ((*depth) - 1) * (OVERLAY_ROW_HEIGHT / 2) + 4;
-
- /* if we're the last sibling, corner the tee by shortening the vbar */
- if (proc == last_sibling) {
- XDrawLine(display, xwin->overlay.text_pixmap, text_gc,
- bar_x, bar_y - OVERLAY_ROW_HEIGHT, /* dst x1, y1 */
- bar_x, bar_y - 4); /* dst x2, y2 (vertical bar) */
- } else {
- XDrawLine(display, xwin->overlay.text_pixmap, text_gc,
- bar_x, bar_y - OVERLAY_ROW_HEIGHT, /* dst x1, y1 */
- bar_x, bar_y); /* dst x2, y2 (vertical bar) */
- }
-
- XDrawLine(display, xwin->overlay.text_pixmap, text_gc,
- bar_x, bar_y - 4, /* dst x1, y1 */
- bar_x + 2, bar_y - 4); /* dst x2, y2 (horizontal bar) */
-
- /* terminate the outer sibling loop upon drawing the tee... */
- break;
- }
- }
- }
- shadow_row(xwin, (*row));
- }
-
- (*row)++;
-
- /* recur any threads first, then any children processes */
- (*depth)++;
- if (!proc->is_thread) { /* XXX: the threads member serves as the list head only when not a thread */
- list_for_each_entry(child, &proc->threads, threads) {
- draw_overlay(xwin, child, depth, row);
- }
- }
-
- list_for_each_entry(child, &proc->children, siblings) {
- draw_overlay(xwin, child, depth, row);
- }
- (*depth)--;
-}
-
-
-/* consolidated version of overlay text and graph rendering, makes snowflakes integration cleaner, this always gets called regadless of the overlays mode */
-static void maintain_overlay(vwm_xwindow_t *xwin)
-{
- int row = 0, depth = 0;
-
- if (!xwin->monitor || !xwin->monitor->stores[VMON_STORE_PROC_STAT]) return;
-
- /* TODO:
- * I side effect of responding to window resizes in this function is there's a latency proportional to the current sample_interval.
- * Something to fix is to resize the overlays when the window resizes.
- * However, simply resizing the overlays is insufficient. Their contents need to be redrawn in the new dimensions, this is where it
- * gets annoying. The current maintain/draw_overlay makes assumptions about being run from the periodic vmon per-process callback.
- * There needs to be a redraw mode added where draw_overlay is just reconstructing the current state, which requires that we suppress
- * the phase advance and in maintain_overlay() and just enter draw_overlay() to redraw everything for the same generation.
- * So this probably requires some tweaking of draw_overlay() as well as maintain_overlay(). I want to be able tocall mainta_overlays()
- * from anywhere, and have it detect if it's being called on the same generation or if the generation has advanced.
- * For now, the monitors will just be a little latent in window resizes which is pretty harmless artifact.
- */
-
- /* if the window is larger than the overlays currently are, enlarge them */
- if (xwin->attrs.width > xwin->overlay.width || xwin->attrs.height > xwin->overlay.height) {
- vwm_overlay_t existing;
- Pixmap pixmap;
-
- existing = xwin->overlay;
-
- xwin->overlay.width = MAX(xwin->overlay.width, MAX(xwin->attrs.width, OVERLAY_GRAPH_MIN_WIDTH));
- xwin->overlay.height = MAX(xwin->overlay.height, MAX(xwin->attrs.height, OVERLAY_GRAPH_MIN_HEIGHT));
-
- pixmap = XCreatePixmap(display, RootWindow(display, screen_num), xwin->overlay.width, xwin->overlay.height, OVERLAY_MASK_DEPTH);
- xwin->overlay.grapha_picture = XRenderCreatePicture(display, pixmap, XRenderFindStandardFormat(display, OVERLAY_MASK_FORMAT), CPRepeat, &pa_repeat);
- XFreePixmap(display, pixmap);
- XRenderFillRectangle(display, PictOpSrc, xwin->overlay.grapha_picture, &overlay_trans_color, 0, 0, xwin->overlay.width, xwin->overlay.height);
-
- pixmap = XCreatePixmap(display, RootWindow(display, screen_num), xwin->overlay.width, xwin->overlay.height, OVERLAY_MASK_DEPTH);
- xwin->overlay.graphb_picture = XRenderCreatePicture(display, pixmap, XRenderFindStandardFormat(display, OVERLAY_MASK_FORMAT), CPRepeat, &pa_repeat);
- XFreePixmap(display, pixmap);
- XRenderFillRectangle(display, PictOpSrc, xwin->overlay.graphb_picture, &overlay_trans_color, 0, 0, xwin->overlay.width, xwin->overlay.height);
-
- pixmap = XCreatePixmap(display, RootWindow(display, screen_num), xwin->overlay.width, OVERLAY_ROW_HEIGHT, OVERLAY_MASK_DEPTH);
- xwin->overlay.tmp_picture = XRenderCreatePicture(display, pixmap, XRenderFindStandardFormat(display, OVERLAY_MASK_FORMAT), 0, NULL);
- XFreePixmap(display, pixmap);
-
- /* keep the text_pixmap reference around for XDrawText usage */
- xwin->overlay.text_pixmap = XCreatePixmap(display, RootWindow(display, screen_num), xwin->overlay.width, xwin->overlay.height, OVERLAY_MASK_DEPTH);
- xwin->overlay.text_picture = XRenderCreatePicture(display, xwin->overlay.text_pixmap, XRenderFindStandardFormat(display, OVERLAY_MASK_FORMAT), 0, NULL);
- XRenderFillRectangle(display, PictOpSrc, xwin->overlay.text_picture, &overlay_trans_color, 0, 0, xwin->overlay.width, xwin->overlay.height);
-
- pixmap = XCreatePixmap(display, RootWindow(display, screen_num), xwin->overlay.width, xwin->overlay.height, OVERLAY_MASK_DEPTH);
- xwin->overlay.shadow_picture = XRenderCreatePicture(display, pixmap, XRenderFindStandardFormat(display, OVERLAY_MASK_FORMAT), 0, NULL);
- XFreePixmap(display, pixmap);
- XRenderFillRectangle(display, PictOpSrc, xwin->overlay.shadow_picture, &overlay_trans_color, 0, 0, xwin->overlay.width, xwin->overlay.height);
-
- pixmap = XCreatePixmap(display, RootWindow(display, screen_num), xwin->overlay.width, xwin->overlay.height, 32);
- xwin->overlay.picture = XRenderCreatePicture(display, pixmap, XRenderFindStandardFormat(display, PictStandardARGB32), 0, NULL);
- XFreePixmap(display, pixmap);
-
- if (existing.width) {
- /* XXX: note the graph pictures are copied from their current phase in the x dimension */
- XRenderComposite(display, PictOpSrc, existing.grapha_picture, None, xwin->overlay.grapha_picture,
- existing.phase, 0, /* src x, y */
- 0, 0, /* mask x, y */
- 0, 0, /* dest x, y */
- existing.width, existing.height);
- XRenderComposite(display, PictOpSrc, existing.graphb_picture, None, xwin->overlay.graphb_picture,
- existing.phase, 0, /* src x, y */
- 0, 0, /* mask x, y */
- 0, 0, /* dest x, y */
- existing.width, existing.height);
- XRenderComposite(display, PictOpSrc, existing.text_picture, None, xwin->overlay.text_picture,
- 0, 0, /* src x, y */
- 0, 0, /* mask x, y */
- 0, 0, /* dest x, y */
- existing.width, existing.height);
- XRenderComposite(display, PictOpSrc, existing.shadow_picture, None, xwin->overlay.shadow_picture,
- 0, 0, /* src x, y */
- 0, 0, /* mask x, y */
- 0, 0, /* dest x, y */
- existing.width, existing.height);
- XRenderComposite(display, PictOpSrc, existing.picture, None, xwin->overlay.picture,
- 0, 0, /* src x, y */
- 0, 0, /* mask x, y */
- 0, 0, /* dest x, y */
- existing.width, existing.height);
- xwin->overlay.phase = 0; /* having unrolled the existing graph[ab] pictures into the larger ones, phase is reset to 0 */
- XRenderFreePicture(display, existing.grapha_picture);
- XRenderFreePicture(display, existing.graphb_picture);
- XRenderFreePicture(display, existing.tmp_picture);
- XRenderFreePicture(display, existing.text_picture);
- XFreePixmap(display, existing.text_pixmap);
- XRenderFreePicture(display, existing.shadow_picture);
- XRenderFreePicture(display, existing.picture);
- }
- }
-
- xwin->overlay.phase += (xwin->overlay.width - 1); /* simply change this to .phase++ to scroll the other direction */
- xwin->overlay.phase %= xwin->overlay.width;
- XRenderFillRectangle(display, PictOpSrc, xwin->overlay.grapha_picture, &overlay_trans_color, xwin->overlay.phase, 0, 1, xwin->overlay.height);
- XRenderFillRectangle(display, PictOpSrc, xwin->overlay.graphb_picture, &overlay_trans_color, xwin->overlay.phase, 0, 1, xwin->overlay.height);
-
- /* recursively draw the monitored processes to the overlay */
- draw_overlay(xwin, xwin->monitor, &depth, &row);
-}
-
-
-/* return the composed height of the overlay */
-static int overlay_composed_height(vwm_xwindow_t *xwin)
-{
- int snowflakes = xwin->overlay.snowflakes_cnt ? 1 + xwin->overlay.snowflakes_cnt : 0; /* don't include the separator row if there are no snowflakes */
- return MIN((xwin->overlay.heirarchy_end + snowflakes) * OVERLAY_ROW_HEIGHT, xwin->attrs.height);
-}
-
-
-/* this composes the maintained overlay into the window's overlay picture, this gets called from paint_all() on every repaint of xwin */
-/* we noop the call if the gen_last_composed and monitor->proc.generation numbers match, indicating there's nothing new to compose. */
-static void compose_overlay(vwm_xwindow_t *xwin)
-{
- XserverRegion region;
- XRectangle damage;
- int height;
-
- if (!xwin->overlay.width) return; /* prevent winning race with maintain_overlay() and using an unready overlay... */
-
- if (xwin->overlay.gen_last_composed == xwin->monitor->generation) return; /* noop if no sampling occurred since last compose */
- xwin->overlay.gen_last_composed = xwin->monitor->generation; /* remember this generation */
-
- //VWM_TRACE("composing %p", xwin);
-
- height = overlay_composed_height(xwin);
-
- /* fill the overlay picture with the background */
- XRenderComposite(display, PictOpSrc, overlay_bg_fill, None, xwin->overlay.picture,
- 0, 0,
- 0, 0,
- 0, 0,
- xwin->attrs.width, height);
-
- /* draw the graphs into the overlay through the stencils being maintained by the sample callbacks */
- XRenderComposite(display, PictOpOver, overlay_grapha_fill, xwin->overlay.grapha_picture, xwin->overlay.picture,
- 0, 0,
- xwin->overlay.phase, 0,
- 0, 0,
- xwin->attrs.width, height);
- XRenderComposite(display, PictOpOver, overlay_graphb_fill, xwin->overlay.graphb_picture, xwin->overlay.picture,
- 0, 0,
- xwin->overlay.phase, 0,
- 0, 0,
- xwin->attrs.width, height);
-
- /* draw the shadow into the overlay picture using a translucent black source drawn through the shadow mask */
- XRenderComposite(display, PictOpOver, overlay_shadow_fill, xwin->overlay.shadow_picture, xwin->overlay.picture,
- 0, 0,
- 0, 0,
- 0, 0,
- xwin->attrs.width, height);
-
- /* render overlay text into the overlay picture using a white source drawn through the overlay text as a mask, on top of everything */
- XRenderComposite(display, PictOpOver, overlay_text_fill, xwin->overlay.text_picture, xwin->overlay.picture,
- 0, 0,
- 0, 0,
- 0, 0,
- xwin->attrs.width, (xwin->overlay.heirarchy_end * OVERLAY_ROW_HEIGHT));
-
- XRenderComposite(display, PictOpOver, overlay_snowflakes_text_fill, xwin->overlay.text_picture, xwin->overlay.picture,
- 0, 0,
- 0, xwin->overlay.heirarchy_end * OVERLAY_ROW_HEIGHT,
- 0, xwin->overlay.heirarchy_end * OVERLAY_ROW_HEIGHT,
- xwin->attrs.width, height - (xwin->overlay.heirarchy_end * OVERLAY_ROW_HEIGHT));
-
- /* damage the window to ensure the updated overlay is drawn (TODO: this can be done more selectively/efficiently) */
- damage.x = xwin->attrs.x + xwin->attrs.border_width;
- damage.y = xwin->attrs.y + xwin->attrs.border_width;
- damage.width = xwin->attrs.width;
- damage.height = height;
- region = XFixesCreateRegion(display, &damage, 1);
- vwm_comp_damage_add(region);
-}
-
-
-/* this callback gets invoked at sample time for every process we've explicitly monitored (not autofollowed children/threads)
- * It's where we update the cumulative data for all windows, including the graph masks, regardless of their visibility
- * It's also where we compose the graphs and text for visible windows into a picture ready for compositing with the window contents */
-static void proc_sample_callback(vmon_t *vmon, vmon_proc_t *proc, vwm_xwindow_t *xwin)
-{
- //VWM_TRACE("proc=%p xwin=%p", proc, xwin);
- /* render the various always-updated overlays, this is the component we do regardless of the overlays mode and window visibility,
- * essentially the incrementally rendered/historic components */
- maintain_overlay(xwin);
-
- /* render other non-historic things and compose the various layers into an updated overlay */
- /* this leaves everything ready to be composed with the window contents in paint_all() */
- /* paint_all() also enters compose_overlay() to update the overlays on windows which become mapped (desktop switches) */
- if (compositing_mode && vwm_xwin_is_mapped(xwin)) compose_overlay(xwin);
-}
-
-
-/* this callback gets invoked at sample time once "per sys" */
-static void sample_callback(vmon_t *_vmon)
-{
- vmon_sys_stat_t *sys_stat = vmon.stores[VMON_STORE_SYS_STAT];
- this_total = sys_stat->user + sys_stat->nice + sys_stat->system +
- sys_stat->idle + sys_stat->iowait + sys_stat->irq +
- sys_stat->softirq + sys_stat->steal + sys_stat->guest;
-
- total_delta = this_total - last_total;
- idle_delta = sys_stat->idle - last_idle;
- iowait_delta = sys_stat->iowait - last_iowait;
-}
-
-
-/* these callbacks are invoked by the vmon library when process instances become monitored/unmonitored */
-static void vmon_ctor_cb(vmon_t *vmon, vmon_proc_t *proc)
-{
- VWM_TRACE("proc->pid=%i", proc->pid);
- proc->foo = calloc(1, sizeof(vwm_perproc_ctxt_t));
-}
-
-
-static void vmon_dtor_cb(vmon_t *vmon, vmon_proc_t *proc)
-{
- VWM_TRACE("proc->pid=%i", proc->pid);
- if (proc->foo) {
- free(proc->foo);
- proc->foo = NULL;
- }
-}
-
-
- /* Xinerama/multihead screen functions */
-
-/* return what fraction (0.0-1.0) of vwin overlaps with scr */
-static float vwm_screen_overlaps_xwin(const vwm_screen_t *scr, vwm_xwindow_t *xwin)
-{
- float pct = 0, xover = 0, yover = 0;
-
- if (scr->x_org + scr->width < xwin->attrs.x || scr->x_org > xwin->attrs.x + xwin->attrs.width ||
- scr->y_org + scr->height < xwin->attrs.y || scr->y_org > xwin->attrs.y + xwin->attrs.height)
- goto _out;
-
- /* they overlap, by how much? */
- xover = MIN(scr->x_org + scr->width, xwin->attrs.x + xwin->attrs.width) - MAX(scr->x_org, xwin->attrs.x);
- yover = MIN(scr->y_org + scr->height, xwin->attrs.y + xwin->attrs.height) - MAX(scr->y_org, xwin->attrs.y);
-
- pct = (xover * yover) / (xwin->attrs.width * xwin->attrs.height);
-_out:
- VWM_TRACE("xover=%f yover=%f width=%i height=%i pct=%.4f", xover, yover, xwin->attrs.width, xwin->attrs.height, pct);
- return pct;
-}
-
-
-/* return the appropriate screen, don't use the return value across event loops because randr events reallocate the array. */
-typedef enum _vwm_screen_rel_t {
- VWM_SCREEN_REL_XWIN, /* return the screen the supplied window most resides in */
- VWM_SCREEN_REL_POINTER, /* return the screen the pointer resides in */
- VWM_SCREEN_REL_TOTAL, /* return the bounding rectangle of all screens as one */
-} vwm_screen_rel_t;
-
-static const vwm_screen_t * vwm_screen_find(vwm_screen_rel_t rel, ...)
-{
- static vwm_screen_t faux;
- vwm_screen_t *scr, *best = &faux; /* default to faux as best */
- int i;
-
- faux.screen_number = 0;
- faux.x_org = 0;
- faux.y_org = 0;
- faux.width = WidthOfScreen(DefaultScreenOfDisplay(display));
- faux.height = HeightOfScreen(DefaultScreenOfDisplay(display));
-
- if (!xinerama_screens) goto _out;
-
-#define for_each_screen(_tmp) \
- for (i = 0, _tmp = xinerama_screens; i < xinerama_screens_cnt; _tmp = &xinerama_screens[++i])
-
- switch (rel) {
- case VWM_SCREEN_REL_XWIN: {
- va_list ap;
- vwm_xwindow_t *xwin;
- float best_pct = 0, this_pct;
-
- va_start(ap, rel);
- xwin = va_arg(ap, vwm_xwindow_t *);
- va_end(ap);
-
- for_each_screen(scr) {
- this_pct = vwm_screen_overlaps_xwin(scr, xwin);
- if (this_pct > best_pct) {
- best = scr;
- best_pct = this_pct;
- }
- }
- break;
- }
-
- case VWM_SCREEN_REL_POINTER: {
- int root_x, root_y, win_x, win_y;
- unsigned int mask;
- Window root, child;
-
- /* get the pointer coordinates and find which screen it's in */
- XQueryPointer(display, RootWindow(display, screen_num), &root, &child, &root_x, &root_y, &win_x, &win_y, &mask);
-
- for_each_screen(scr) {
- if (root_x >= scr->x_org && root_x < scr->x_org + scr->width &&
- root_y >= scr->y_org && root_y < scr->y_org + scr->height) {
- best = scr;
- break;
- }
- }
- break;
- }
-
- case VWM_SCREEN_REL_TOTAL: {
- short x1 = MAXSHORT, y1 = MAXSHORT, x2 = MINSHORT, y2 = MINSHORT;
- /* find the smallest x_org and y_org, the highest x_org + width and y_org + height, those are the two corners of the total rect */
- for_each_screen(scr) {
- if (scr->x_org < x1) x1 = scr->x_org;
- if (scr->y_org < y1) y1 = scr->y_org;
- if (scr->x_org + scr->width > x2) x2 = scr->x_org + scr->width;
- if (scr->y_org + scr->height > y2) y2 = scr->y_org + scr->height;
- }
- faux.x_org = x1;
- faux.y_org = y1;
- faux.width = x2 - x1;
- faux.height = y2 - y1;
- best = &faux;
- break;
- }
- }
-_out:
- VWM_TRACE("Found Screen #%i: %hix%hi @ %hi,%hi", best->screen_number, best->width, best->height, best->x_org, best->y_org);
-
- return best;
-}
-
-
-/* check if a screen contains any windows (assuming the current desktop) */
-static int vwm_screen_is_empty(const vwm_screen_t *scr)
-{
- vwm_xwindow_t *xwin;
- int is_empty = 1;
-
- list_for_each_entry(xwin, &xwindows, xwindows) {
- if (!xwin->mapped) continue;
- if (!xwin->managed || (xwin->managed->desktop == focused_desktop && !xwin->managed->shelved && !xwin->managed->configuring)) {
- /* XXX: it may make more sense to see what %age of the screen is overlapped by windows, and consider it empty if < some % */
- /* This is just seeing if any window is predominantly within the specified screen, the rationale being if you had a focusable
- * window on the screen you would have used the keyboard to make windows go there; this function is only used in determining
- * wether a new window should go where the pointer is or not. */
- if (vwm_screen_overlaps_xwin(scr, xwin) >= 0.05) {
- is_empty = 0;
- break;
- }
- }
- }
-
- return is_empty;
-}
-
-
- /* startup logo */
-
-/* animated \/\/\ logo done with simple XOR'd lines, a display of the WM being started and ready */
-#define VWM_LOGO_POINTS 6
-static void vwm_draw_logo(void)
-{
- int i;
- unsigned int width, height, yoff, xoff;
- XPoint points[VWM_LOGO_POINTS];
- const vwm_screen_t *scr = vwm_screen_find(VWM_SCREEN_REL_POINTER);
-
- XGrabServer(display);
-
- /* use the dimensions of the pointer-containing screen */
- width = scr->width;
- height = scr->height;
- xoff = scr->x_org;
- yoff = scr->y_org + ((float)height * .333);
- height /= 3;
-
- /* the logo gets shrunken vertically until it's essentially a flat line */
- while (height -= 2) {
- /* scale and center the points to the screen size */
- for (i = 0; i < VWM_LOGO_POINTS; i++) {
- points[i].x = xoff + (i * .2 * (float)width);
- points[i].y = (i % 2 * (float)height) + yoff;
- }
-
- XDrawLines(display, RootWindow(display, screen_num), gc, points, sizeof(points) / sizeof(XPoint), CoordModeOrigin);
- XFlush(display);
- usleep(3333);
- XDrawLines(display, RootWindow(display, screen_num), gc, points, sizeof(points) / sizeof(XPoint), CoordModeOrigin);
- XFlush(display);
-
- /* the width is shrunken as well, but only by as much as it is tall */
- yoff++;
- width -= 4;
- xoff += 2;
- }
-
- XUngrabServer(display);
-}
-
-
- /* launching of external processes / X clients */
-
-/* launch a child command specified in argv, mode decides if we wait for the child to exit before returning. */
-typedef enum _vwm_launch_mode_t {
- VWM_LAUNCH_MODE_FG,
- VWM_LAUNCH_MODE_BG,
-} vwm_launch_mode_t;
-
-static void vwm_launch(char **argv, vwm_launch_mode_t mode)
-{
- /* XXX: in BG mode I double fork and let init inherit the orphan so I don't have to collect the return status */
- if (mode == VWM_LAUNCH_MODE_FG || !fork()) {
- if (!fork()) {
- /* child */
- setpriority(PRIO_PROCESS, getpid(), priority + LAUNCHED_RELATIVE_PRIORITY);
- execvp(argv[0], argv);
- }
- if (mode == VWM_LAUNCH_MODE_BG) exit(0);
- }
- wait(NULL); /* TODO: could wait for the specific pid, particularly in FG mode ... */
-}
-
-
- /* desktop/shelf context handling */
-
-/* switch to the desired context if it isn't already the focused one, inform caller if anything happened */
-static int vwm_context_focus(vwm_context_focus_t desired_context)
-{
- vwm_context_focus_t entry_context = focused_context;
-
- switch (focused_context) {
- vwm_xwindow_t *xwin;
- vwm_window_t *vwin;
-
- case VWM_CONTEXT_FOCUS_SHELF:
- if (desired_context == VWM_CONTEXT_FOCUS_SHELF) break;
-
- /* desired == DESKTOP && focused == SHELF */
-
- VWM_TRACE("unmapping shelf window \"%s\"", focused_shelf->xwindow->name);
- vwm_win_unmap(focused_shelf);
- XFlush(display); /* for a more responsive feel */
-
- /* map the focused desktop, from the top of the stack down */
- list_for_each_entry_prev(xwin, &xwindows, xwindows) {
- if (!(vwin = xwin->managed)) continue;
- if (vwin->desktop == focused_desktop && !vwin->shelved) {
- VWM_TRACE("Mapping desktop window \"%s\"", xwin->name);
- vwm_win_map(vwin);
- }
- }
-
- if (focused_desktop->focused_window) {
- VWM_TRACE("Focusing \"%s\"", focused_desktop->focused_window->xwindow->name);
- XSetInputFocus(display, focused_desktop->focused_window->xwindow->id, RevertToPointerRoot, CurrentTime);
- }
-
- focused_context = VWM_CONTEXT_FOCUS_DESKTOP;
- break;
-
- case VWM_CONTEXT_FOCUS_DESKTOP:
- /* unmap everything, map the shelf */
- if (desired_context == VWM_CONTEXT_FOCUS_DESKTOP) break;
-
- /* desired == SHELF && focused == DESKTOP */
-
- /* there should be a focused shelf if the shelf contains any windows, we NOOP the switch if the shelf is empty. */
- if (focused_shelf) {
- /* unmap everything on the current desktop */
- list_for_each_entry(xwin, &xwindows, xwindows) {
- if (!(vwin = xwin->managed)) continue;
- if (vwin->desktop == focused_desktop) {
- VWM_TRACE("Unmapping desktop window \"%s\"", xwin->name);
- vwm_win_unmap(vwin);
- }
- }
-
- XFlush(display); /* for a more responsive feel */
-
- VWM_TRACE("Mapping shelf window \"%s\"", focused_shelf->xwindow->name);
- vwm_win_map(focused_shelf);
- vwm_win_focus(focused_shelf);
-
- focused_context = VWM_CONTEXT_FOCUS_SHELF;
- }
- break;
-
- default:
- VWM_BUG("unexpected focused context %x", focused_context);
- break;
- }
-
- /* return if the context has been changed, the caller may need to branch differently if nothing happened */
- return (focused_context != entry_context);
-}
-
-
- /* virtual desktops */
-
-/* make the specified desktop the most recently used one */
-static void vwm_desktop_mru(vwm_desktop_t *desktop)
-{
- VWM_TRACE("MRU desktop: %p", desktop);
- list_move(&desktop->desktops_mru, &desktops_mru);
-}
-
-
-/* focus a virtual desktop */
-/* this switches to the desktop context if necessary, maps and unmaps windows accordingly if necessary */
-static int vwm_desktop_focus(vwm_desktop_t *desktop)
-{
- XGrabServer(display);
- XSync(display, False);
-
- /* if the context switched and the focused desktop is the desired desktop there's nothing else to do */
- if ((vwm_context_focus(VWM_CONTEXT_FOCUS_DESKTOP) && focused_desktop != desktop) || focused_desktop != desktop) {
- vwm_xwindow_t *xwin;
- vwm_window_t *vwin;
-
- /* unmap the windows on the currently focused desktop, map those on the newly focused one */
- list_for_each_entry(xwin, &xwindows, xwindows) {
- if (!(vwin = xwin->managed) || vwin->shelved) continue;
- if (vwin->desktop == focused_desktop) vwm_win_unmap(vwin);
- }
-
- XFlush(display);
-
- list_for_each_entry_prev(xwin, &xwindows, xwindows) {
- if (!(vwin = xwin->managed) || vwin->shelved) continue;
- if (vwin->desktop == desktop) vwm_win_map(vwin);
- }
-
- focused_desktop = desktop;
- }
-
- /* directly focus the desktop's focused window if there is one, we don't use vwm_win_focus() intentionally XXX */
- if (focused_desktop->focused_window) {
- VWM_TRACE("Focusing \"%s\"", focused_desktop->focused_window->xwindow->name);
- XSetInputFocus(display, focused_desktop->focused_window->xwindow->id, RevertToPointerRoot, CurrentTime);
- }
-
- XUngrabServer(display);
-
- return 1;
-}
-
-
-/* create a virtual desktop */
-static vwm_desktop_t * vwm_desktop_create(char *name)
-{
- vwm_desktop_t *desktop;
-
- desktop = malloc(sizeof(vwm_desktop_t));
- if (desktop == NULL) {
- VWM_PERROR("Failed to allocate desktop");
- goto _fail;
- }
-
- desktop->name = name == NULL ? name : strdup(name);
- desktop->focused_window = NULL;
-
- list_add_tail(&desktop->desktops, &desktops);
- list_add_tail(&desktop->desktops_mru, &desktops_mru);
-
- return desktop;
-
-_fail:
- return NULL;
-}
-
-
-/* destroy a virtual desktop */
-static void vwm_desktop_destroy(vwm_desktop_t *desktop)
-{
- /* silently refuse to destroy a desktop having windows (for now) */
- /* there's _always_ a focused window on a desktop having mapped windows */
- /* also silently refuse to destroy the last desktop (for now) */
- if (desktop->focused_window || (desktop->desktops.next == desktop->desktops.prev)) return;
-
- /* focus the MRU desktop that isn't this one if we're the focused desktop */
- if (desktop == focused_desktop) {
- vwm_desktop_t *next_desktop;
-
- list_for_each_entry(next_desktop, &desktops_mru, desktops_mru) {
- if (next_desktop != desktop) {
- vwm_desktop_focus(next_desktop);
- break;
- }
- }
- }
-
- list_del(&desktop->desktops);
- list_del(&desktop->desktops_mru);
-}
-
-
- /* bare X windows stuff, there's a distinction between bare xwindows and the vwm managed windows */
-
-/* send a client message to a window (currently used for WM_DELETE) */
-static void vwm_xwin_message(vwm_xwindow_t *xwin, Atom type, long foo)
-{
- XEvent event;
-
- memset(&event, 0, sizeof(event));
- event.xclient.type = ClientMessage;
- event.xclient.window = xwin->id;
- event.xclient.message_type = type;
- event.xclient.format = 32;
- event.xclient.data.l[0] = foo;
- event.xclient.data.l[1] = CurrentTime; /* XXX TODO: is CurrentTime actually correct to use for this purpose? */
-
- XSendEvent(display, xwin->id, False, 0, &event);
-}
-
-
-/* look up the X window in the global xwindows list (includes unmanaged windows like override_redirect/popup menus) */
-static vwm_xwindow_t * vwm_xwin_lookup(Window win)
-{
- vwm_xwindow_t *tmp, *xwin = NULL;
-
- list_for_each_entry(tmp, &xwindows, xwindows) {
- if (tmp->id == win) {
- xwin = tmp;
- break;
- }
- }
-
- return xwin;
-}
-
-
-/* determine if a window is mapped (vwm-mapped) according to the current context */
-static inline int vwm_xwin_is_mapped(vwm_xwindow_t *xwin)
-{
- int ret = 0;
-
- if (!xwin->mapped) return 0;
-
- if (xwin->managed) {
- switch (focused_context) {
- case VWM_CONTEXT_FOCUS_SHELF:
- if (focused_shelf == xwin->managed) ret = xwin->mapped;
- break;
-
- case VWM_CONTEXT_FOCUS_DESKTOP:
- if (focused_desktop == xwin->managed->desktop && !xwin->managed->shelved) ret = xwin->mapped;
- break;
-
- default:
- VWM_BUG("Unsupported context");
- break;
- }
- } else { /* unmanaged xwins like popup dialogs when mapped are always visible */
- ret = 1;
- }
-
- /* annoyingly, Xorg stops delivering VisibilityNotify events for redirected windows, so we don't conveniently know if a window is obscured or not :( */
- /* I could maintain my own data structure for answering this question, but that's pretty annoying when Xorg already has that knowledge. */
-
- return ret;
-}
-
-
-/* bind the window to a "namewindowpixmap" and create a picture from it (compositing) */
-void vwm_xwin_bind_namewindow(vwm_xwindow_t *xwin)
-{
- xwin->pixmap = XCompositeNameWindowPixmap(display, xwin->id);
- xwin->picture = XRenderCreatePicture(display, xwin->pixmap,
- XRenderFindVisualFormat(display, xwin->attrs.visual),
- CPSubwindowMode, &pa_inferiors);
- XFreePixmap(display, xwin->pixmap);
-}
-
-
-/* free the window's picture for accessing its redirected contents (compositing) */
-void vwm_xwin_unbind_namewindow(vwm_xwindow_t *xwin)
-{
- XRenderFreePicture(display, xwin->picture);
-}
-
-
-/* install a monitor on the window if it doesn't already have one and has _NET_WM_PID set */
-static void vwm_xwin_monitor(vwm_xwindow_t *xwin)
-{
- Atom type;
- int fmt;
- unsigned long nitems;
- unsigned long nbytes;
- long *foo = NULL;
- int pid = -1;
-
- if (xwin->monitor) return;
-
- if (XGetWindowProperty(display, xwin->id, wm_pid_atom, 0, 1, False, XA_CARDINAL,
- &type, &fmt, &nitems, &nbytes, (unsigned char **)&foo) != Success || !foo) return;
-
- pid = *foo;
- XFree(foo);
-
- /* add the client process to the monitoring heirarchy */
- /* XXX note libvmon here maintains a unique callback for each unique callback+xwin pair, so multi-window processes work */
- xwin->monitor = vmon_proc_monitor(&vmon, NULL, pid, VMON_WANT_PROC_INHERIT, (void (*)(vmon_t *, vmon_proc_t *, void *))proc_sample_callback, xwin);
- /* FIXME: count_rows() isn't returning the right count sometimes (off by ~1), it seems to be related to racing with the automatic child monitoring */
- /* the result is an extra row sometimes appearing below the process heirarchy */
- xwin->overlay.heirarchy_end = 1 + count_rows(xwin->monitor);
- xwin->overlay.snowflakes_cnt = 0;
-}
-
-
-/* creates and potentially manages a new window (called in response to CreateNotify events, and during startup for all existing windows) */
-/* if the window is already mapped and not an override_redirect window, it becomes managed here. */
-typedef enum _vwm_grab_mode_t {
- VWM_NOT_GRABBED = 0,
- VWM_GRABBED
-} vwm_grab_mode_t;
-
-static vwm_xwindow_t * vwm_xwin_create(Window win, vwm_grab_mode_t grabbed)
-{
- XWindowAttributes attrs;
- vwm_xwindow_t *xwin = NULL;
-
- VWM_TRACE("creating %#x", (unsigned int)win);
-
- /* prevent races */
- if (!grabbed) {
- XGrabServer(display);
- XSync(display, False);
- }
-
- /* verify the window still exists */
- if (!XGetWindowAttributes(display, win, &attrs)) goto _out_grabbed;
-
- /* don't create InputOnly windows */
- if (attrs.class == InputOnly) goto _out_grabbed;
-
- if (!(xwin = (vwm_xwindow_t *)malloc(sizeof(vwm_xwindow_t)))) {
- VWM_PERROR("Failed to allocate xwin");
- goto _out_grabbed;
- }
-
- xwin->id = win;
- xwin->attrs = attrs;
- xwin->managed = NULL;
- xwin->name = NULL;
- XFetchName(display, win, &xwin->name);
-
- xwin->monitor = NULL;
- xwin->overlay.width = xwin->overlay.height = xwin->overlay.phase = 0;
- xwin->overlay.gen_last_composed = -1;
-
- /* This is so we get the PropertyNotify event and can get the pid when it's set post-create,
- * with my _NET_WM_PID patch the property is immediately available */
- XSelectInput(display, win, PropertyChangeMask);
-
- vwm_xwin_monitor(xwin);
-
- /* we must track the mapped-by-client state of the window independent of managed vs. unmanaged because
- * in the case of override_redirect windows they may be unmapped (invisible) or mapped (visible) like menus without being managed.
- * otherwise we could just use !xwin.managed to indicate unmapped, which is more vwm2-like, but insufficient when compositing. */
- xwin->mapped = (attrs.map_state != IsUnmapped);
-
- if (compositing_mode) {
- vwm_xwin_bind_namewindow(xwin);
- xwin->damage = XDamageCreate(display, xwin->id, XDamageReportNonEmpty);
- }
-
- list_add_tail(&xwin->xwindows, &xwindows); /* created windows are always placed on the top of the stacking order */
-
-#ifdef HONOR_OVERRIDE_REDIRECT
- if (!attrs.override_redirect && xwin->mapped) vwm_win_manage_xwin(xwin);
-#else
- if (xwin->mapped) vwm_win_manage_xwin(xwin);
-#endif
-_out_grabbed:
- if (!grabbed) XUngrabServer(display);
-
- return xwin;
-}
-
-
-/* destroy a window, called in response to DestroyNotify events */
-/* if the window is also managed it will be unmanaged first */
-static void vwm_xwin_destroy(vwm_xwindow_t *xwin)
-{
- XGrabServer(display);
- XSync(display, False);
-
- if (xwin->managed) vwm_win_unmanage(xwin->managed);
-
- list_del(&xwin->xwindows);
-
- if (xwin->monitor) vmon_proc_unmonitor(&vmon, xwin->monitor, (void (*)(vmon_t *, vmon_proc_t *, void *))proc_sample_callback, xwin);
- if (xwin->name) XFree(xwin->name);
-
- if (compositing_mode) {
- vwm_xwin_unbind_namewindow(xwin);
- XDamageDestroy(display, xwin->damage);
- }
- free(xwin);
-
- XUngrabServer(display);
-}
-
-
-/* maintain the stack-ordered xwindows list, when new_above is != None xwin is to be placed above new_above, when == None xwin goes to the bottom. */
-void vwm_xwin_restack(vwm_xwindow_t *xwin, Window new_above)
-{
- Window old_above;
-#ifdef TRACE
- vwm_xwindow_t *tmp;
- fprintf(stderr, "restack of %#x new_above=%#x\n", (unsigned int)xwin->id, (unsigned int)new_above);
- fprintf(stderr, "restack pre:");
- list_for_each_entry(tmp, &xwindows, xwindows) {
- fprintf(stderr, " %#x", (unsigned int)tmp->id);
- }
- fprintf(stderr, "\n");
-#endif
- if (xwin->xwindows.prev != &xwindows) {
- old_above = list_entry(xwin->xwindows.prev, vwm_xwindow_t, xwindows)->id;
- } else {
- old_above = None;
- }
-
- if (old_above != new_above) {
- vwm_xwindow_t *new;
-
- if (new_above == None) { /* to the bottom of the stack, so just above the &xwindows head */
- list_move(&xwin->xwindows, &xwindows);
- } else if ((new = vwm_xwin_lookup(new_above))) { /* to just above new_above */
- list_move(&xwin->xwindows, &new->xwindows);
- }
- }
-#ifdef TRACE
- fprintf(stderr, "restack post:");
- list_for_each_entry(tmp, &xwindows, xwindows) {
- fprintf(stderr, " %#x", (unsigned int)tmp->id);
- }
- fprintf(stderr, "\n\n");
-#endif
-}
-
-
- /* vwm "managed" windows (vwm_window_t) (which are built upon the "core" X windows (vwm_xwindow_t)) */
-
-/* unmap the specified window and set the unmapping-in-progress flag so we can discard vwm-generated UnmapNotify events */
-static void vwm_win_unmap(vwm_window_t *vwin)
-{
- if (!vwin->xwindow->mapped) {
- VWM_TRACE("inhibited unmap of \"%s\", not mapped by client", vwin->xwindow->name);
- return;
- }
- VWM_TRACE("Unmapping \"%s\"", vwin->xwindow->name);
- vwin->unmapping = 1;
- XUnmapWindow(display, vwin->xwindow->id);
-}
-
-
-/* map the specified window and set the mapping-in-progress flag so we can discard vwm-generated MapNotify events */
-static void vwm_win_map(vwm_window_t *vwin)
-{
- if (!vwin->xwindow->mapped) {
- VWM_TRACE("inhibited map of \"%s\", not mapped by client", vwin->xwindow->name);
- return;
- }
- VWM_TRACE("Mapping \"%s\"", vwin->xwindow->name);
- vwin->mapping = 1;
- XMapWindow(display, vwin->xwindow->id);
-}
-
-
-/* make the specified window the most recently used one */
-static void vwm_win_mru(vwm_window_t *vwin)
-{
- list_move(&vwin->windows_mru, &windows_mru);
-}
-
-
-/* look up the X window in the global managed windows list */
-static vwm_window_t * vwm_win_lookup(Window win)
-{
- vwm_window_t *tmp, *vwin = NULL;
-
- list_for_each_entry(tmp, &windows_mru, windows_mru) {
- if (tmp->xwindow->id == win) {
- vwin = tmp;
- break;
- }
- }
-
- return vwin;
-}
-
-
-/* return the currently focused window (considers current context...), may return NULL */
-static vwm_window_t * vwm_win_focused(void)
-{
- vwm_window_t *vwin = NULL;
-
- switch (focused_context) {
- case VWM_CONTEXT_FOCUS_SHELF:
- vwin = focused_shelf;
- break;
-
- case VWM_CONTEXT_FOCUS_DESKTOP:
- if (focused_desktop) vwin = focused_desktop->focused_window;
- break;
-
- default:
- VWM_BUG("Unsupported context");
- break;
- }
-
- return vwin;
-}
-
-
-/* "autoconfigure" windows (configuration shortcuts like fullscreen/halfscreen/quarterscreen) and restoring the window */
-typedef enum _vwm_win_autoconf_t {
- VWM_WIN_AUTOCONF_NONE, /* un-autoconfigured window (used to restore the configuration) */
- VWM_WIN_AUTOCONF_QUARTER, /* quarter-screened */
- VWM_WIN_AUTOCONF_HALF, /* half-screened */
- VWM_WIN_AUTOCONF_FULL, /* full-screened */
- VWM_WIN_AUTOCONF_ALL /* all-screened (borderless) */
-} vwm_win_autoconf_t;
-
-typedef enum _vwm_side_t {
- VWM_SIDE_TOP,
- VWM_SIDE_BOTTOM,
- VWM_SIDE_LEFT,
- VWM_SIDE_RIGHT
-} vwm_side_t;
-
-typedef enum _vwm_corner_t {
- VWM_CORNER_TOP_LEFT,
- VWM_CORNER_TOP_RIGHT,
- VWM_CORNER_BOTTOM_RIGHT,
- VWM_CORNER_BOTTOM_LEFT
-} vwm_corner_t;
-
-static void vwm_win_autoconf(vwm_window_t *vwin, vwm_screen_rel_t rel, vwm_win_autoconf_t conf, ...)
-{
- const vwm_screen_t *scr;
- va_list ap;
- XWindowChanges changes = { .border_width = WINDOW_BORDER_WIDTH };
-
- /* remember the current configuration as the "client" configuration if it's not an autoconfigured one. */
- if (vwin->autoconfigured == VWM_WIN_AUTOCONF_NONE) vwin->client = vwin->xwindow->attrs;
-
- scr = vwm_screen_find(rel, vwin->xwindow); /* XXX FIXME: this becomes a bug when vwm_screen_find() uses non-xwin va_args */
- va_start(ap, conf);
- switch (conf) {
- case VWM_WIN_AUTOCONF_QUARTER: {
- vwm_corner_t corner = va_arg(ap, vwm_corner_t);
- changes.width = scr->width / 2 - (WINDOW_BORDER_WIDTH * 2);
- changes.height = scr->height / 2 - (WINDOW_BORDER_WIDTH * 2);
- switch (corner) {
- case VWM_CORNER_TOP_LEFT:
- changes.x = scr->x_org;
- changes.y = scr->y_org;
- break;
-
- case VWM_CORNER_TOP_RIGHT:
- changes.x = scr->x_org + scr->width / 2;
- changes.y = scr->y_org;
- break;
-
- case VWM_CORNER_BOTTOM_RIGHT:
- changes.x = scr->x_org + scr->width / 2;
- changes.y = scr->y_org + scr->height / 2;
- break;
-
- case VWM_CORNER_BOTTOM_LEFT:
- changes.x = scr->x_org;
- changes.y = scr->y_org + scr->height / 2;
- break;
- }
- break;
- }
-
- case VWM_WIN_AUTOCONF_HALF: {
- vwm_side_t side = va_arg(ap, vwm_side_t);
- switch (side) {
- case VWM_SIDE_TOP:
- changes.width = scr->width - (WINDOW_BORDER_WIDTH * 2);
- changes.height = scr->height / 2 - (WINDOW_BORDER_WIDTH * 2);
- changes.x = scr->x_org;
- changes.y = scr->y_org;
- break;
-
- case VWM_SIDE_BOTTOM:
- changes.width = scr->width - (WINDOW_BORDER_WIDTH * 2);
- changes.height = scr->height / 2 - (WINDOW_BORDER_WIDTH * 2);
- changes.x = scr->x_org;
- changes.y = scr->y_org + scr->height / 2;
- break;
-
- case VWM_SIDE_LEFT:
- changes.width = scr->width / 2 - (WINDOW_BORDER_WIDTH * 2);
- changes.height = scr->height - (WINDOW_BORDER_WIDTH * 2);
- changes.x = scr->x_org;
- changes.y = scr->y_org;
- break;
-
- case VWM_SIDE_RIGHT:
- changes.width = scr->width / 2 - (WINDOW_BORDER_WIDTH * 2);
- changes.height = scr->height - (WINDOW_BORDER_WIDTH * 2);
- changes.x = scr->x_org + scr->width / 2;
- changes.y = scr->y_org;
- break;
- }
- break;
- }
-
- case VWM_WIN_AUTOCONF_FULL:
- changes.width = scr->width - WINDOW_BORDER_WIDTH * 2;
- changes.height = scr->height - WINDOW_BORDER_WIDTH * 2;
- changes.x = scr->x_org;
- changes.y = scr->y_org;
- break;
-
- case VWM_WIN_AUTOCONF_ALL:
- changes.width = scr->width;
- changes.height = scr->height;
- changes.x = scr->x_org;
- changes.y = scr->y_org;
- changes.border_width = 0;
- break;
-
- case VWM_WIN_AUTOCONF_NONE: /* restore window if autoconfigured */
- changes.width = vwin->client.width;
- changes.height = vwin->client.height;
- changes.x = vwin->client.x;
- changes.y = vwin->client.y;
- break;
- }
- va_end(ap);
-
- XConfigureWindow(display, vwin->xwindow->id, CWX | CWY | CWWidth | CWHeight | CWBorderWidth, &changes);
- vwin->autoconfigured = conf;
-}
-
-
-/* focus a window */
-/* this updates window border color as needed and the X input focus if mapped */
-static void vwm_win_focus(vwm_window_t *vwin)
-{
- VWM_TRACE("focusing: %#x", (unsigned int)vwin->xwindow->id);
-
- if (vwm_xwin_is_mapped(vwin->xwindow)) {
- /* if vwin is mapped give it the input focus */
- XSetInputFocus(display, vwin->xwindow->id, RevertToPointerRoot, CurrentTime);
- }
-
- /* update the border color accordingly */
- if (vwin->shelved) {
- /* set the border of the newly focused window to the shelved color */
- XSetWindowBorder(display, vwin->xwindow->id, vwin == console ? shelved_console_border_color.pixel : shelved_window_border_color.pixel);
- /* fullscreen windows in the shelf when focused, since we don't intend to overlap there */
- vwm_win_autoconf(vwin, VWM_SCREEN_REL_POINTER, VWM_WIN_AUTOCONF_FULL); /* XXX TODO: for now the shelf follows the pointer, it's simple. */
- } else {
- if (vwin->desktop->focused_window) {
- /* set the border of the previously focused window on the same desktop to the unfocused color */
- XSetWindowBorder(display, vwin->desktop->focused_window->xwindow->id, unfocused_window_border_color.pixel);
- }
-
- /* set the border of the newly focused window to the focused color */
- XSetWindowBorder(display, vwin->xwindow->id, focused_window_border_color.pixel);
-
- /* persist this on a per-desktop basis so it can be restored on desktop switches */
- vwin->desktop->focused_window = vwin;
- }
-}
-
-
-/* focus the next window on a virtual desktop relative to the supplied window, in the specified context, respecting screen boundaries according to fence. */
-typedef enum _vwm_fence_t {
- VWM_FENCE_IGNORE = 0, /* behave as if screen boundaries don't exist (like the pre-Xinerama code) */
- VWM_FENCE_RESPECT, /* confine operation to within the screen */
- VWM_FENCE_TRY_RESPECT, /* confine operation to within the screen, unless no options exist. */
- VWM_FENCE_VIOLATE, /* leave the screen for any other */
- VWM_FENCE_MASKED_VIOLATE /* leave the screen for any other not masked */
-} vwm_fence_t;
-
-static vwm_window_t * vwm_win_focus_next(vwm_window_t *vwin, vwm_fence_t fence)
-{
- const vwm_screen_t *scr = vwm_screen_find(VWM_SCREEN_REL_XWIN, vwin->xwindow), *next_scr = NULL;
- vwm_window_t *next;
- unsigned long visited_mask;
-
-_retry:
- visited_mask = 0;
- list_for_each_entry(next, &vwin->windows_mru, windows_mru) {
- /* searching for the next mapped window in this context, using vwin->windows as the head */
- if (&next->windows_mru == &windows_mru) continue; /* XXX: skip the containerless head, we're leveraging the circular list implementation */
-
- if ((vwin->shelved && next->shelved) ||
- ((!vwin->shelved && !next->shelved && next->desktop == vwin->desktop) &&
- (fence == VWM_FENCE_IGNORE ||
- ((fence == VWM_FENCE_RESPECT || fence == VWM_FENCE_TRY_RESPECT) && vwm_screen_find(VWM_SCREEN_REL_XWIN, next->xwindow) == scr) ||
- (fence == VWM_FENCE_VIOLATE && vwm_screen_find(VWM_SCREEN_REL_XWIN, next->xwindow) != scr) ||
- (fence == VWM_FENCE_MASKED_VIOLATE && (next_scr = vwm_screen_find(VWM_SCREEN_REL_XWIN, next->xwindow)) != scr &&
- !((1UL << next_scr->screen_number) & fence_mask))
- ))) break;
-
- if (fence == VWM_FENCE_MASKED_VIOLATE && next_scr && next_scr != scr) visited_mask |= (1UL << next_scr->screen_number);
- }
-
- if (fence == VWM_FENCE_TRY_RESPECT && next == vwin) {
- /* if we tried to respect the fence and failed to find a next, fallback to ignoring the fence and try again */
- fence = VWM_FENCE_IGNORE;
- goto _retry;
- } else if (fence == VWM_FENCE_MASKED_VIOLATE && next_scr) {
- /* if we did a masked violate update the mask with the potentially new screen number */
- if (next == vwin && visited_mask) {
- /* if we failed to find a next window on a different screen but had candidates we've exhausted screens and need to reset the mask then retry */
- VWM_TRACE("all candidate screens masked @ 0x%lx, resetting mask", fence_mask);
- fence_mask = 0;
- goto _retry;
- }
- fence_mask |= (1UL << next_scr->screen_number);
- VWM_TRACE("VWM_FENCE_MASKED_VIOLATE fence_mask now: 0x%lx\n", fence_mask);
- }
-
- if (vwin->shelved) {
- if (next != focused_shelf) {
- /* shelf switch, unmap the focused shelf and take it over */
- /* TODO FIXME: this makes assumptions about the shelf being focused calling unmap/map directly.. */
- vwm_win_unmap(focused_shelf);
-
- XFlush(display);
-
- vwm_win_map(next);
- focused_shelf = next;
- vwm_win_focus(next);
- }
- } else {
- if (next != next->desktop->focused_window) {
- /* focus the changed window */
- vwm_win_focus(next);
- XRaiseWindow(display, next->xwindow->id);
- }
- }
-
- VWM_TRACE("vwin=%p xwin=%p name=\"%s\"", next, next->xwindow, next->xwindow->name);
-
- return next;
-}
-
-
-/* shelves a window, if the window is focused we focus the next one (if possible) */
-static void vwm_win_shelve(vwm_window_t *vwin)
-{
- /* already shelved, NOOP */
- if (vwin->shelved) return;
-
- /* shelving focused window, focus the next window */
- if (vwin == vwin->desktop->focused_window) {
- vwm_win_mru(vwm_win_focus_next(vwin, VWM_FENCE_RESPECT));
- }
-
- if (vwin == vwin->desktop->focused_window) {
- /* TODO: we can probably put this into vwm_win_focus_next() and have it always handled there... */
- vwin->desktop->focused_window = NULL;
- }
-
- vwin->shelved = 1;
- vwm_win_mru(vwin);
-
- /* newly shelved windows always become the focused shelf */
- focused_shelf = vwin;
-
- vwm_win_unmap(vwin);
-}
-
-
-/* helper for (idempotently) unfocusing a window, deals with context switching etc... */
-static void vwm_win_unfocus(vwm_window_t *vwin)
-{
- /* if we're the focused shelved window, cycle the focus to the next shelved window if possible, if there's no more shelf, switch to the desktop */
- /* TODO: there's probably some icky behaviors for focused windows unmapping/destroying in unfocused contexts, we probably jump contexts suddenly. */
- if (vwin == focused_shelf) {
- VWM_TRACE("unfocusing focused shelf");
- vwm_win_focus_next(vwin, VWM_FENCE_IGNORE);
-
- if (vwin == focused_shelf) {
- VWM_TRACE("shelf empty, leaving");
- /* no other shelved windows, exit the shelf context */
- vwm_context_focus(VWM_CONTEXT_FOCUS_DESKTOP);
- focused_shelf = NULL;
- }
- }
-
- /* if we're the focused window cycle the focus to the next window on the desktop if possible */
- if (vwin->desktop->focused_window == vwin) {
- VWM_TRACE("unfocusing focused window");
- vwm_win_focus_next(vwin, VWM_FENCE_TRY_RESPECT);
- }
-
- if (vwin->desktop->focused_window == vwin) {
- VWM_TRACE("desktop empty");
- vwin->desktop->focused_window = NULL;
- }
-}
-
-
-/* demote an managed window to an unmanaged one */
-static vwm_xwindow_t * vwm_win_unmanage(vwm_window_t *vwin)
-{
- vwm_win_mru(vwin); /* bump vwin to the mru head before unfocusing so we always move focus to the current head on unmanage of the focused window */
- vwm_win_unfocus(vwin);
- list_del(&vwin->windows_mru);
-
- if (vwin == console) console = NULL;
- if (vwin == focused_origin) focused_origin = NULL;
-
- vwin->xwindow->managed = NULL;
-
- return vwin->xwindow;
-}
-
-
-/* promote an unmanaged window to a managed one */
-static vwm_window_t * vwm_win_manage_xwin(vwm_xwindow_t *xwin)
-{
- vwm_window_t *focused, *vwin = NULL;
-
- if (xwin->managed) {
- VWM_TRACE("suppressed re-management of xwin=%p", xwin);
- goto _fail;
- }
-
- if (!(vwin = (vwm_window_t *)malloc(sizeof(vwm_window_t)))) {
- VWM_PERROR("Failed to allocate vwin");
- goto _fail;
- }
-
- XUngrabButton(display, AnyButton, AnyModifier, xwin->id);
- XGrabButton(display, AnyButton, WM_GRAB_MODIFIER, xwin->id, False, (PointerMotionMask | ButtonPressMask | ButtonReleaseMask), GrabModeAsync, GrabModeAsync, None, None);
- XGrabKey(display, AnyKey, WM_GRAB_MODIFIER, xwin->id, False, GrabModeAsync, GrabModeAsync);
- XSetWindowBorder(display, xwin->id, unfocused_window_border_color.pixel);
-
- vwin->hints = XAllocSizeHints();
- if (!vwin->hints) {
- VWM_PERROR("Failed to allocate WM hints");
- goto _fail;
- }
- XGetWMNormalHints(display, xwin->id, vwin->hints, &vwin->hints_supplied);
-
- xwin->managed = vwin;
- vwin->xwindow = xwin;
-
- vwin->desktop = focused_desktop;
- vwin->autoconfigured = VWM_WIN_AUTOCONF_NONE;
- vwin->mapping = vwin->unmapping = vwin->configuring = 0;
- vwin->shelved = (focused_context == VWM_CONTEXT_FOCUS_SHELF); /* if we're in the shelf when the window is created, the window is shelved */
- vwin->client = xwin->attrs; /* remember whatever the current attributes are */
-
- VWM_TRACE("hints: flags=%lx x=%i y=%i w=%i h=%i minw=%i minh=%i maxw=%i maxh=%i winc=%i hinc=%i basew=%i baseh=%i grav=%x",
- vwin->hints->flags,
- vwin->hints->x, vwin->hints->y,
- vwin->hints->width, vwin->hints->height,
- vwin->hints->min_width, vwin->hints->min_height,
- vwin->hints->max_width, vwin->hints->max_height,
- vwin->hints->width_inc, vwin->hints->height_inc,
- vwin->hints->base_width, vwin->hints->base_height,
- vwin->hints->win_gravity);
-
- if ((vwin->hints_supplied & (USSize | PSize))) {
- vwin->client.width = vwin->hints->base_width;
- vwin->client.height = vwin->hints->base_height;
- }
-
- /* put it on the global windows_mru list, if there's a focused window insert the new one after it */
- if (!list_empty(&windows_mru) && (focused = vwm_win_focused())) {
- /* insert the vwin immediately after the focused window, so Mod1+Tab goes to the new window */
- list_add(&vwin->windows_mru, &focused->windows_mru);
- } else {
- list_add(&vwin->windows_mru, &windows_mru);
- }
-
- /* always raise newly managed windows so we know about them. */
- XRaiseWindow(display, xwin->id);
-
- /* if the desktop has no focused window yet, automatically focus the newly managed one, provided we're on the desktop context */
- if (!focused_desktop->focused_window && focused_context == VWM_CONTEXT_FOCUS_DESKTOP) {
- VWM_TRACE("Mapped new window \"%s\" is alone on desktop \"%s\", focusing", xwin->name, focused_desktop->name);
- vwm_win_focus(vwin);
- }
-
- return vwin;
-
-_fail:
- if (vwin) {
- if (vwin->hints) XFree(vwin->hints);
- free(vwin);
- }
- return NULL;
-}
-
-
-/* migrate a window to a new desktop, focuses the destination desktop as well */
-static void vwm_win_migrate(vwm_window_t *vwin, vwm_desktop_t *desktop)
-{
- vwm_win_unfocus(vwin); /* go through the motions of unfocusing the window if it is focused */
- vwin->shelved = 0; /* ensure not shelved */
- vwin->desktop = desktop; /* assign the new desktop */
- vwm_desktop_focus(desktop); /* currently we always focus the new desktop in a migrate */
-
- vwm_win_focus(vwin); /* focus the window so borders get updated */
- vwm_win_mru(vwin); /* TODO: is this right? shouldn't the Mod1 release be what's responsible for this? I migrate so infrequently it probably doesn't matter */
-
- XRaiseWindow(display, vwin->xwindow->id); /* ensure the window is raised */
-}
-
-
- /* compositing manager stuff */
-
-/* add damage to the global combined damage queue where we accumulate damage between calls to paint_all() */
-static void vwm_comp_damage_add(XserverRegion damage)
-{
- if (combined_damage != None) {
- XFixesUnionRegion(display, combined_damage, combined_damage, damage);
- XFixesDestroyRegion(display, damage);
- } else {
- combined_damage = damage;
- }
-}
-
-
-/* take what regions of the damaged window have been damaged, subtract them from the per-window damage object, and add them to the combined damage */
-static void vwm_comp_damage_event(XDamageNotifyEvent *ev)
-{
- XserverRegion region;
- vwm_xwindow_t *xwin;
-
- xwin = vwm_xwin_lookup(ev->drawable);
- if (!xwin) {
- VWM_ERROR("damaged unknown drawable %x", (unsigned int)ev->drawable);
- return;
- }
-
- region = XFixesCreateRegion(display, NULL, 0);
- XDamageSubtract(display, xwin->damage, None, region);
- XFixesTranslateRegion(display, region, xwin->attrs.x + xwin->attrs.border_width, xwin->attrs.y + xwin->attrs.border_width);
- vwm_comp_damage_add(region);
-}
-
-
-/* helper to damage an entire window including the border */
-static void vwm_comp_damage_win(vwm_xwindow_t *xwin)
-{
- XserverRegion region;
- XRectangle rect = { xwin->attrs.x,
- xwin->attrs.y,
- xwin->attrs.width + xwin->attrs.border_width * 2,
- xwin->attrs.height + xwin->attrs.border_width * 2 };
- region = XFixesCreateRegion(display, &rect, 1);
- vwm_comp_damage_add(region);
-}
-
-
-/* throw away our double buffered root pictures so they get recreated on the next paint_all() */
-/* used in response to screen configuration changes... */
-static void vwm_comp_invalidate_root()
-{
- if (root_picture) XRenderFreePicture(display, root_picture);
- root_picture = None;
- if (root_buffer) XRenderFreePicture(display, root_picture);
- root_buffer = None;
-}
-
-
-/* consume combined_damage by iterating the xwindows list from the top down, drawing visible windows as encountered, subtracting their area from combined_damage */
-/* when compositing is active this is the sole function responsible for making things show up on the screen */
-static void vwm_comp_paint_all()
-{
- vwm_xwindow_t *xwin;
- XRenderColor bgcolor = {0x0000, 0x00, 0x00, 0xffff};
- Region occluded = XCreateRegion();
- static XserverRegion undamage_region = None;
-
- if (!undamage_region) undamage_region = XFixesCreateRegion(display, NULL, 0);
-
- /* (re)create the root picture from the root window and allocate a double buffer for the off-screen composition of the damaged contents */
- if (root_picture == None) {
- Pixmap root_pixmap;
-
- XGetWindowAttributes(display, RootWindow(display, screen_num), &root_attrs);
- root_picture = XRenderCreatePicture(display, RootWindow(display, screen_num),
- XRenderFindVisualFormat(display, DefaultVisual(display, screen_num)),
- CPSubwindowMode, &pa_inferiors);
- root_pixmap = XCreatePixmap (display, RootWindow(display, screen_num), root_attrs.width, root_attrs.height, DefaultDepth (display, screen_num));
- root_buffer = XRenderCreatePicture (display, root_pixmap, XRenderFindVisualFormat (display, DefaultVisual (display, screen_num)), 0, 0);
- XFreePixmap(display, root_pixmap);
- }
-
- /* compose overlays for all visible windows up front in a separate pass (kind of lame, but it's simpler since compose_overlay() adds to combined_damage) */
- list_for_each_entry_prev(xwin, &xwindows, xwindows) {
- XRectangle r;
-
- if (!vwm_xwin_is_mapped(xwin)) continue; /* if !mapped skip */
-
- /* Everything mapped next goes through an occlusion check.
- * Since the composite extension stops delivery of VisibilityNotify events for redirected windows,
- * (it assumes redirected windows should be treated as part of a potentially transparent composite, and provides no api to alter this assumption)
- * we can't simply select the VisibilityNotify events on all windows and cache their visibility state in vwm_xwindow_t then skip
- * xwin->state==VisibilityFullyObscured windows here to avoid the cost of pointlessly composing overlays and rendering fully obscured windows :(.
- *
- * Instead we accumulate an occluded region (starting empty) of painted windows from the top-down on every paint_all().
- * Before we compose_overlay() a window, we check if the window's rectangle fits entirely within the occluded region.
- * If it does, no compose_overlay() is performed.
- * If it doesn't, compose_overlay() is called, and the window's rect is added to the occluded region.
- * The occluded knowledge is also cached for the XRenderComposite() loop immediately following, where we skip the rendering of
- * occluded windows as well.
- * This does technically break SHAPE windows (xeyes, xmms), but only when monitoring is enabled which covers them with rectangular overlays anyways.
- */
- r.x = xwin->attrs.x;
- r.y = xwin->attrs.y;
- r.width = xwin->attrs.width + xwin->attrs.border_width * 2;
- r.height = xwin->attrs.height + xwin->attrs.border_width * 2;
- if (XRectInRegion(occluded, r.x, r.y, r.width, r.height) != RectangleIn) {
- /* the window isn't fully occluded, compose it and add it to occluded */
- if (xwin->monitor && !xwin->attrs.override_redirect) compose_overlay(xwin);
- XUnionRectWithRegion(&r, occluded, occluded);
- xwin->occluded = 0;
- } else {
- xwin->occluded = 1;
- VWM_TRACE("window %#x occluded, skipping compose_overlay()", (int)xwin->id);
- }
- }
- XDestroyRegion(occluded);
-
- /* start with the clip regions set to the damaged area accumulated since the previous paint_all() */
- XFixesSetPictureClipRegion(display, root_buffer, 0, 0, combined_damage); /* this is the double buffer where the in-flight screen contents are staged */
- XFixesSetPictureClipRegion(display, root_picture, 0, 0, combined_damage); /* this is the visible root window */
-
- /* since translucent windows aren't supported in vwm, I can do this more efficiently */
- list_for_each_entry_prev(xwin, &xwindows, xwindows) {
- XRectangle r;
-
- if (!vwm_xwin_is_mapped(xwin) || xwin->occluded) continue; /* if !mapped or occluded skip */
-
- /* these coordinates + dimensions incorporate the border (since XCompositeNameWindowPixmap is being used) */
- r.x = xwin->attrs.x;
- r.y = xwin->attrs.y;
- r.width = xwin->attrs.width + xwin->attrs.border_width * 2;
- r.height = xwin->attrs.height + xwin->attrs.border_width * 2;
-
- /* render the redirected window contents into root_buffer, note root_buffer has the remaining combined_damage set as the clip region */
- XRenderComposite(display, PictOpSrc, xwin->picture, None, root_buffer,
- 0, 0, 0, 0, /* src x, y, mask x, y */
- r.x, r.y, /* dst x, y */
- r.width, r.height);
-
- if (xwin->monitor && !xwin->attrs.override_redirect && xwin->overlay.width) {
- /* draw the monitoring overlay atop the window, note we stay within the window borders here. */
- XRenderComposite(display, PictOpOver, xwin->overlay.picture, None, root_buffer,
- 0, 0, 0, 0, /* src x,y, maxk x, y */
- xwin->attrs.x + xwin->attrs.border_width, /* dst x */
- xwin->attrs.y + xwin->attrs.border_width, /* dst y */
- xwin->attrs.width, overlay_composed_height(xwin)); /* w, h */
- }
-
- /* subtract the region of the window from the combined damage and update the root_buffer clip region to reflect the remaining damage */
- XFixesSetRegion(display, undamage_region, &r, 1);
- XFixesSubtractRegion(display, combined_damage, combined_damage, undamage_region);
- XFixesSetPictureClipRegion(display, root_buffer, 0, 0, combined_damage);
- }
-
- /* at this point all of the visible windows have been subtracted from the clip region, so paint any root window damage (draw background) */
- XRenderFillRectangle(display, PictOpSrc, root_buffer, &bgcolor, 0, 0, root_attrs.width, root_attrs.height);
-
- /* discard the root_buffer clip region and copy root_buffer to root_picture, root_picture still has the combined damage as its clip region */
- XFixesSetPictureClipRegion(display, root_buffer, 0, 0, None);
- XRenderComposite(display, PictOpSrc, root_buffer, None, root_picture, 0, 0, 0, 0, 0, 0, root_attrs.width, root_attrs.height);
-
- /* fin */
- XFixesDestroyRegion(display, combined_damage);
- combined_damage = None;
-}
-
-
-/* toggle compositing/monitoring overlays on/off */
-static void vwm_comp_toggle(void)
-{
- vwm_xwindow_t *xwin;
-
- XGrabServer(display);
- XSync(display, False);
-
- switch (compositing_mode) {
- case VWM_COMPOSITING_OFF:
- VWM_TRACE("enabling compositing");
- compositing_mode = VWM_COMPOSITING_MONITORS;
- XCompositeRedirectSubwindows(display, RootWindow(display, screen_num), CompositeRedirectManual);
- list_for_each_entry_prev(xwin, &xwindows, xwindows) {
- vwm_xwin_bind_namewindow(xwin);
- xwin->damage = XDamageCreate(display, xwin->id, XDamageReportNonEmpty);
- }
- /* damage everything */
- /* TODO: keep a list of rects reflecting all the current screens and create a region from that... */
- vwm_comp_damage_add(XFixesCreateRegionFromWindow(display, RootWindow(display, screen_num), WindowRegionBounding));
- break;
-
- case VWM_COMPOSITING_MONITORS: {
- XEvent ev;
-
- VWM_TRACE("disabling compositing");
- compositing_mode = VWM_COMPOSITING_OFF;
- list_for_each_entry_prev(xwin, &xwindows, xwindows) {
- vwm_xwin_unbind_namewindow(xwin);
- XDamageDestroy(display, xwin->damage);
- }
- XCompositeUnredirectSubwindows(display, RootWindow(display, screen_num), CompositeRedirectManual);
- vwm_comp_invalidate_root();
-
- /* if there's any damage queued up discard it so we don't enter paint_all() until compositing is reenabled again. */
- if (combined_damage) {
- XFixesDestroyRegion(display, combined_damage);
- combined_damage = None;
- }
- while (XCheckTypedEvent(display, damage_event + XDamageNotify, &ev) != False);
- break;
- }
- }
-
- XUngrabServer(display);
-}
-
-
- /* input event handling stuff */
-
-/* simple little state machine for managing mouse click/drag/release sequences so we don't need another event loop */
-typedef enum _vwm_adjust_mode_t {
- VWM_ADJUST_RESIZE,
- VWM_ADJUST_MOVE
-} vwm_adjust_mode_t;
-
-typedef struct _vwm_clickety_t {
- vwm_window_t *vwin;
- vwm_adjust_mode_t mode;
- XWindowAttributes orig, lastrect;
- int impetus_x, impetus_y, impetus_x_root, impetus_y_root;
-} vwm_clickety_t;
-
-/* helper function for resizing the window, how the motion is applied depends on where in the window the impetus event occurred */
-static void compute_resize(vwm_clickety_t *clickety, XEvent *terminus, XWindowAttributes *new)
-{
- vwm_window_t *vwin;
- int dw = (clickety->orig.width / 2);
- int dh = (clickety->orig.height / 2);
- int xdelta = (terminus->xbutton.x_root - clickety->impetus_x_root);
- int ydelta = (terminus->xbutton.y_root - clickety->impetus_y_root);
- int min_width = 0, min_height = 0;
- int width_inc = 1, height_inc = 1;
-
- /* TODO: there's a problem here WRT border width, I should be considering it, I just haven't bothered to fix it since it doesn't seem to matter */
- if ((vwin = clickety->vwin) && vwin->hints) {
- if ((vwin->hints_supplied & PMinSize)) {
- min_width = vwin->hints->min_width;
- min_height = vwin->hints->min_height;
- VWM_TRACE("window size hints exist and minimum sizes are w=%i h=%i", min_width, min_height);
- }
-
- if ((vwin->hints_supplied & PResizeInc)) {
- width_inc = vwin->hints->width_inc;
- height_inc = vwin->hints->height_inc;
- VWM_TRACE("window size hints exist and resize increments are w=%i h=%i", width_inc, height_inc);
- if (!width_inc) width_inc = 1;
- if (!height_inc) height_inc = 1;
- }
- }
-
- xdelta = xdelta / width_inc * width_inc;
- ydelta = ydelta / height_inc * height_inc;
-
- if (clickety->impetus_x < dw && clickety->impetus_y < dh) {
- /* grabbed top left */
- new->x = clickety->orig.x + xdelta;
- new->y = clickety->orig.y + ydelta;
- new->width = clickety->orig.width - xdelta;
- new->height = clickety->orig.height - ydelta;
- } else if (clickety->impetus_x > dw && clickety->impetus_y < dh) {
- /* grabbed top right */
- new->x = clickety->orig.x;
- new->y = clickety->orig.y + ydelta;
- new->width = clickety->orig.width + xdelta;
- new->height = clickety->orig.height - ydelta;
- } else if (clickety->impetus_x < dw && clickety->impetus_y > dh) {
- /* grabbed bottom left */
- new->x = clickety->orig.x + xdelta;
- new->y = clickety->orig.y;
- new->width = clickety->orig.width - xdelta;
- new->height = clickety->orig.height + ydelta;
- } else if (clickety->impetus_x > dw && clickety->impetus_y > dh) {
- /* grabbed bottom right */
- new->x = clickety->orig.x;
- new->y = clickety->orig.y;
- new->width = clickety->orig.width + xdelta;
- new->height = clickety->orig.height + ydelta;
- }
-
- /* constrain the width and height of the window according to the minimums */
- if (new->width < min_width) {
- if (clickety->orig.x != new->x) new->x -= (min_width - new->width);
- new->width = min_width;
- }
-
- if (new->height < min_height) {
- if (clickety->orig.y != new->y) new->y -= (min_height - new->height);
- new->height = min_height;
- }
-}
-
-
-static void vwm_clickety_motion(vwm_clickety_t *clickety, Window win, XMotionEvent *motion)
-{
- XWindowChanges changes = { .border_width = WINDOW_BORDER_WIDTH };
-
- if (!clickety->vwin) return;
-
- /* TODO: verify win matches clickety->vwin? */
- switch (clickety->mode) {
- case VWM_ADJUST_MOVE:
- changes.x = clickety->orig.x + (motion->x_root - clickety->impetus_x_root);
- changes.y = clickety->orig.y + (motion->y_root - clickety->impetus_y_root);
- XConfigureWindow(display, win, CWX | CWY | CWBorderWidth, &changes);
- break;
-
- case VWM_ADJUST_RESIZE: {
- XWindowAttributes resized;
-
- /* XXX: it just so happens the XMotionEvent structure is identical to XButtonEvent in the fields
- * needed by compute_resize... */
- compute_resize(clickety, (XEvent *)motion, &resized);
- /* TODO: this is probably broken with compositing active, but it doesn't seem to be too messed up in practice */
- /* erase the last rectangle */
- XDrawRectangle(display, RootWindow(display, screen_num), gc, clickety->lastrect.x, clickety->lastrect.y, clickety->lastrect.width, clickety->lastrect.height);
- /* draw a frame @ resized coordinates */
- XDrawRectangle(display, RootWindow(display, screen_num), gc, resized.x, resized.y, resized.width, resized.height);
- /* remember the last rectangle */
- clickety->lastrect = resized;
- break;
- }
-
- default:
- break;
- }
-}
-
-
-static void vwm_clickety_released(vwm_clickety_t *clickety, Window win, XButtonPressedEvent *terminus)
-{
- XWindowChanges changes = { .border_width = WINDOW_BORDER_WIDTH };
-
- if (!clickety->vwin) return;
-
- switch (clickety->mode) {
- case VWM_ADJUST_MOVE:
- changes.x = clickety->orig.x + (terminus->x_root - clickety->impetus_x_root);
- changes.y = clickety->orig.y + (terminus->y_root - clickety->impetus_y_root);
- XConfigureWindow(display, win, CWX | CWY | CWBorderWidth, &changes);
- break;
-
- case VWM_ADJUST_RESIZE: {
- XWindowAttributes resized;
- compute_resize(clickety, (XEvent *)terminus, &resized);
- /* move and resize the window @ resized */
- XDrawRectangle(display, RootWindow(display, screen_num), gc, clickety->lastrect.x, clickety->lastrect.y, clickety->lastrect.width, clickety->lastrect.height);
- changes.x = resized.x;
- changes.y = resized.y;
- changes.width = resized.width;
- changes.height = resized.height;
- XConfigureWindow(display, win, CWX | CWY | CWWidth | CWHeight | CWBorderWidth, &changes);
- XUngrabServer(display);
- break;
- }
-
- default:
- break;
- }
- /* once you manipulate the window it's no longer fullscreened, simply hitting Mod1+Return once will restore fullscreened mode */
- clickety->vwin->autoconfigured = VWM_WIN_AUTOCONF_NONE;
-
- clickety->vwin = NULL; /* reset clickety */
-
- XFlush(display);
- XUngrabPointer(display, CurrentTime);
-}
-
-
-/* on pointer buttonpress we initiate a clickety sequence; setup clickety with the window and impetus coordinates.. */
-static int vwm_clickety_pressed(vwm_clickety_t *clickety, Window win, XButtonPressedEvent *impetus)
-{
- vwm_window_t *vwin;
-
- /* verify the window still exists */
- if (!XGetWindowAttributes(display, win, &clickety->orig)) goto _fail;
-
- if (!(vwin = vwm_win_lookup(win))) goto _fail;
-
- if (impetus->state & WM_GRAB_MODIFIER) {
-
- /* always set the input focus to the clicked window, note if we allow this to happen on the root window, it enters sloppy focus mode
- * until a non-root window is clicked, which is an interesting hybrid but not how I prefer it. */
- if (vwin != focused_desktop->focused_window && vwin->xwindow->id != RootWindow(display, screen_num)) {
- vwm_win_focus(vwin);
- vwm_win_mru(vwin);
- }
-
- switch (impetus->button) {
- case Button1:
- /* immediately raise the window if we're relocating,
- * resizes are supported without raising (which also enables NULL resizes to focus without raising) */
- clickety->mode = VWM_ADJUST_MOVE;
- XRaiseWindow(display, win);
- break;
-
- case Button3:
- /* grab the server on resize for the xor rubber-banding's sake */
- XGrabServer(display);
- XSync(display, False);
-
- /* FIXME: none of the resize DrawRectangle() calls consider the window border. */
- XDrawRectangle(display, RootWindow(display, screen_num), gc, clickety->orig.x, clickety->orig.y, clickety->orig.width, clickety->orig.height);
- clickety->lastrect = clickety->orig;
-
- clickety->mode = VWM_ADJUST_RESIZE;
- break;
-
- default:
- goto _fail;
- }
- clickety->vwin = vwin;
- clickety->impetus_x_root = impetus->x_root;
- clickety->impetus_y_root = impetus->y_root;
- clickety->impetus_x = impetus->x;
- clickety->impetus_y = impetus->y;
- }
-
- return 1;
-
-_fail:
- XUngrabPointer(display, CurrentTime);
-
- return 0;
-}
-
-/* Poll the keyboard state to see if _any_ keys are pressed */
-static int vwm_keyspressed() {
- int i;
- char state[32];
-
- XQueryKeymap(display, state);
-
- for (i = 0; i < sizeof(state); i++) {
- if (state[i]) return 1;
- }
-
- return 0;
-}
-
-/* Called in response to KeyRelease events, for now only interesting for detecting when Mod1 is released termintaing
- * window cycling for application of MRU on the focused window */
-static void vwm_keyreleased(Window win, XEvent *keyrelease)
-{
- vwm_window_t *vwin;
- KeySym sym;
-
- switch ((sym = XLookupKeysym(&keyrelease->xkey, 0))) {
- case XK_Alt_R:
- case XK_Alt_L: /* TODO: actually use the modifier mapping, for me XK_Alt_[LR] is Mod1. XGetModifierMapping()... */
- VWM_TRACE("XK_Alt_[LR] released");
-
- /* aborted? try restore focused_origin */
- if (key_is_grabbed > 1 && focused_origin) {
- VWM_TRACE("restoring %p on %p", focused_origin, focused_origin->desktop);
- vwm_desktop_focus(focused_origin->desktop);
- vwm_win_focus(focused_origin);
- }
-
- /* make the focused window the most recently used */
- if ((vwin = vwm_win_focused())) vwm_win_mru(vwin);
-
- /* make the focused desktop the most recently used */
- if (focused_context == VWM_CONTEXT_FOCUS_DESKTOP && focused_desktop) vwm_desktop_mru(focused_desktop);
-
- break;
-
- default:
- VWM_TRACE("Unhandled keycode: %x", (unsigned int)sym);
- break;
- }
-
- if (key_is_grabbed && !vwm_keyspressed()) {
- XUngrabKeyboard(display, CurrentTime);
- XFlush(display);
- key_is_grabbed = 0;
- fence_mask = 0; /* reset the fence mask on release for VWM_FENCE_MASKED_VIOLATE */
- }
-}
-
-
-/* Called in response to KeyPress events, I currenly only grab Mod1 keypress events */
-static void vwm_keypressed(Window win, XEvent *keypress)
-{
- vwm_window_t *vwin;
- KeySym sym;
- static KeySym last_sym;
- static typeof(keypress->xkey.state) last_state;
- static int repeat_cnt = 0;
- int do_grab = 0;
- char *quit_console_args[] = {"/bin/sh", "-c", "screen -dr " CONSOLE_SESSION_STRING " -X quit", NULL};
-
- sym = XLookupKeysym(&keypress->xkey, 0);
-
- /* detect repeaters, note repeaters do not span interrupted Mod1 sequences! */
- if (key_is_grabbed && sym == last_sym && keypress->xkey.state == last_state) {
- repeat_cnt++;
- } else {
- repeat_cnt = 0;
- }
-
- vwin = vwm_win_focused();
-
- switch (sym) {
-
-#define launcher(_sym, _label, _argv)\
- case _sym: \
- { \
- char *args[] = {"/bin/sh", "-c", "screen -dr " CONSOLE_SESSION_STRING " -X screen /bin/sh -i -x -c \"" _argv " || sleep 86400\"", NULL};\
- vwm_launch(args, VWM_LAUNCH_MODE_BG);\
- break; \
- }
-#include "launchers.def"
-#undef launcher
- case XK_Alt_L: /* transaction abort */
- case XK_Alt_R:
- if (key_is_grabbed) key_is_grabbed++;
- VWM_TRACE("aborting with origin %p", focused_origin);
- break;
-
- case XK_grave: /* toggle shelf visibility */
- vwm_context_focus(VWM_CONTEXT_FOCUS_OTHER);
- break;
-
- case XK_Tab: /* cycle focused window */
- do_grab = 1; /* update MRU window on commit (Mod1 release) */
-
- /* focus the next window, note this doesn't affect MRU yet, that happens on Mod1 release */
- if (vwin) {
- if (keypress->xkey.state & ShiftMask) {
- vwm_win_focus_next(vwin, VWM_FENCE_MASKED_VIOLATE);
- } else {
- vwm_win_focus_next(vwin, VWM_FENCE_RESPECT);
- }
- }
- break;
-
- case XK_space: { /* cycle focused desktop utilizing MRU */
- vwm_desktop_t *next_desktop = list_entry(focused_desktop->desktops_mru.next == &desktops_mru ? focused_desktop->desktops_mru.next->next : focused_desktop->desktops_mru.next, vwm_desktop_t, desktops_mru); /* XXX: note the sensitivity to the desktops_mru head here, we want to look past it. */
-
- do_grab = 1; /* update MRU desktop on commit (Mod1 release) */
-
- if (keypress->xkey.state & ShiftMask) {
- /* migrate the focused window with the desktop focus to the most recently used desktop */
- if (vwin) vwm_win_migrate(vwin, next_desktop);
- } else {
- vwm_desktop_focus(next_desktop);
- }
- break;
- }
-
- case XK_d: /* destroy focused */
- if (vwin) {
- if (keypress->xkey.state & ShiftMask) { /* brutally destroy the focused window */
- XKillClient(display, vwin->xwindow->id);
- } else { /* kindly destroy the focused window */
- vwm_xwin_message(vwin->xwindow, wm_protocols_atom, wm_delete_atom);
- }
- } else if (focused_context == VWM_CONTEXT_FOCUS_DESKTOP) {
- /* destroy the focused desktop when destroy occurs without any windows */
- vwm_desktop_destroy(focused_desktop);
- }
- break;
-
- case XK_Escape: /* leave VWM rudely, after triple press */
- do_grab = 1;
-
- if (repeat_cnt == 2) {
- vwm_launch(quit_console_args, VWM_LAUNCH_MODE_FG);
- exit(42);
- }
- break;
-
- case XK_v: /* instantiate (and focus) a new (potentially empty, unless migrating) virtual desktop */
- do_grab = 1; /* update MRU desktop on commit (Mod1 release) */
-
- if (keypress->xkey.state & ShiftMask) {
- if (vwin) {
- /* migrate the focused window to a newly created virtual desktop, focusing the new desktop simultaneously */
- vwm_win_migrate(vwin, vwm_desktop_create(NULL));
- }
- } else {
- vwm_desktop_focus(vwm_desktop_create(NULL));
- vwm_desktop_mru(focused_desktop);
- }
- break;
-
- case XK_h: /* previous virtual desktop, if we're in the shelf context this will simply switch to desktop context */
- do_grab = 1; /* update MRU desktop on commit (Mod1 release) */
-
- if (keypress->xkey.state & ShiftMask) {
- if (vwin && vwin->desktop->desktops.prev != &desktops) {
- /* migrate the focused window with the desktop focus to the previous desktop */
- vwm_win_migrate(vwin, list_entry(vwin->desktop->desktops.prev, vwm_desktop_t, desktops));
- }
- } else {
- if (focused_context == VWM_CONTEXT_FOCUS_SHELF) {
- /* focus the focused desktop instead of the shelf */
- vwm_context_focus(VWM_CONTEXT_FOCUS_DESKTOP);
- } else if (focused_desktop->desktops.prev != &desktops) {
- /* focus the previous desktop */
- vwm_desktop_focus(list_entry(focused_desktop->desktops.prev, vwm_desktop_t, desktops));
- }
- }
- break;
-
- case XK_l: /* next virtual desktop, if we're in the shelf context this will simply switch to desktop context */
- do_grab = 1; /* update MRU desktop on commit (Mod1 release) */
-
- if (keypress->xkey.state & ShiftMask) {
- if (vwin && vwin->desktop->desktops.next != &desktops) {
- /* migrate the focused window with the desktop focus to the next desktop */
- vwm_win_migrate(vwin, list_entry(vwin->desktop->desktops.next, vwm_desktop_t, desktops));
- }
- } else {
- if (focused_context == VWM_CONTEXT_FOCUS_SHELF) {
- /* focus the focused desktop instead of the shelf */
- vwm_context_focus(VWM_CONTEXT_FOCUS_DESKTOP);
- } else if (focused_desktop->desktops.next != &desktops) {
- /* focus the next desktop */
- vwm_desktop_focus(list_entry(focused_desktop->desktops.next, vwm_desktop_t, desktops));
- }
- }
- break;
-
- case XK_k: /* raise or shelve the focused window */
- if (vwin) {
- if (keypress->xkey.state & ShiftMask) { /* shelf the window and focus the shelf */
- if (focused_context != VWM_CONTEXT_FOCUS_SHELF) {
- /* shelve the focused window while focusing the shelf */
- vwm_win_shelve(vwin);
- vwm_context_focus(VWM_CONTEXT_FOCUS_SHELF);
- }
- } else {
- do_grab = 1;
-
- XRaiseWindow(display, vwin->xwindow->id);
-
- if (repeat_cnt == 1) {
- /* double: reraise & fullscreen */
- vwm_win_autoconf(vwin, VWM_SCREEN_REL_XWIN, VWM_WIN_AUTOCONF_FULL);
- } else if (repeat_cnt == 2) {
- /* triple: reraise & fullscreen w/borders obscured by screen perimiter */
- vwm_win_autoconf(vwin, VWM_SCREEN_REL_XWIN, VWM_WIN_AUTOCONF_ALL);
- } else if (xinerama_screens_cnt > 1) {
- if (repeat_cnt == 3) {
- /* triple: reraise & fullscreen across all screens */
- vwm_win_autoconf(vwin, VWM_SCREEN_REL_TOTAL, VWM_WIN_AUTOCONF_FULL);
- } else if (repeat_cnt == 4) {
- /* quadruple: reraise & fullscreen w/borders obscured by screen perimiter */
- vwm_win_autoconf(vwin, VWM_SCREEN_REL_TOTAL, VWM_WIN_AUTOCONF_ALL);
- }
- }
- XFlush(display);
- }
- }
- break;
-
- case XK_j: /* lower or unshelve the focused window */
- if (vwin) {
- if (keypress->xkey.state & ShiftMask) { /* unshelf the window to the focused desktop, and focus the desktop */
- if (focused_context == VWM_CONTEXT_FOCUS_SHELF) {
- /* unshelve the focused window, focus the desktop it went to */
- vwm_win_migrate(vwin, focused_desktop);
- }
- } else {
- if (vwin->autoconfigured == VWM_WIN_AUTOCONF_ALL) {
- vwm_win_autoconf(vwin, VWM_SCREEN_REL_XWIN, VWM_WIN_AUTOCONF_FULL);
- } else {
- XLowerWindow(display, vwin->xwindow->id);
- }
- XFlush(display);
- }
- }
- break;
-
- case XK_Return: /* (full-screen / restore) focused window */
- if (vwin) {
- if (vwin->autoconfigured) {
- vwm_win_autoconf(vwin, VWM_SCREEN_REL_XWIN, VWM_WIN_AUTOCONF_NONE);
- } else {
- vwm_win_autoconf(vwin, VWM_SCREEN_REL_XWIN, VWM_WIN_AUTOCONF_FULL);
- }
- }
- break;
-
- case XK_s: /* shelve focused window */
- if (vwin && !vwin->shelved) vwm_win_shelve(vwin);
- break;
-
- case XK_bracketleft: /* reconfigure the focused window to occupy the left or top half of the screen or left quarters on repeat */
- if (vwin) {
- do_grab = 1;
-
- if (keypress->xkey.state & ShiftMask) {
- if (!repeat_cnt) {
- vwm_win_autoconf(vwin, VWM_SCREEN_REL_XWIN, VWM_WIN_AUTOCONF_HALF, VWM_SIDE_TOP);
- } else {
- vwm_win_autoconf(vwin, VWM_SCREEN_REL_XWIN, VWM_WIN_AUTOCONF_QUARTER, VWM_CORNER_TOP_LEFT);
- }
- } else {
- if (!repeat_cnt) {
- vwm_win_autoconf(vwin, VWM_SCREEN_REL_XWIN, VWM_WIN_AUTOCONF_HALF, VWM_SIDE_LEFT);
- } else {
- vwm_win_autoconf(vwin, VWM_SCREEN_REL_XWIN, VWM_WIN_AUTOCONF_QUARTER, VWM_CORNER_BOTTOM_LEFT);
- }
- }
- }
- break;
-
- case XK_bracketright: /* reconfigure the focused window to occupy the right or bottom half of the screen or right quarters on repeat */
- if (vwin) {
- do_grab = 1;
-
- if (keypress->xkey.state & ShiftMask) {
- if (!repeat_cnt) {
- vwm_win_autoconf(vwin, VWM_SCREEN_REL_XWIN, VWM_WIN_AUTOCONF_HALF, VWM_SIDE_BOTTOM);
- } else {
- vwm_win_autoconf(vwin, VWM_SCREEN_REL_XWIN, VWM_WIN_AUTOCONF_QUARTER, VWM_CORNER_BOTTOM_RIGHT);
- }
- } else {
- if (!repeat_cnt) {
- vwm_win_autoconf(vwin, VWM_SCREEN_REL_XWIN, VWM_WIN_AUTOCONF_HALF, VWM_SIDE_RIGHT);
- } else {
- vwm_win_autoconf(vwin, VWM_SCREEN_REL_XWIN, VWM_WIN_AUTOCONF_QUARTER, VWM_CORNER_TOP_RIGHT);
- }
- }
- }
- break;
-
- case XK_semicolon: /* toggle composited overlays */
- vwm_comp_toggle();
- break;
-
- case XK_apostrophe: /* reset snowflakes of the focused window, suppressed when not compositing */
- if (vwin && compositing_mode && vwin->xwindow->overlay.snowflakes_cnt) {
- vwin->xwindow->overlay.snowflakes_cnt = 0;
- vwm_comp_damage_win(vwin->xwindow);
- }
- break;
-
- case XK_Right: /* increase sampling frequency */
- if (sampling_interval + 1 < sizeof(sampling_intervals) / sizeof(sampling_intervals[0])) sampling_interval++;
- break;
-
- case XK_Left: /* decrease sampling frequency, -1 pauses */
- if (sampling_interval >= 0) sampling_interval--;
- break;
- default:
- VWM_TRACE("Unhandled keycode: %x", (unsigned int)sym);
- break;
- }
-
- /* if what we're doing requests a grab, if not already grabbed, grab keyboard */
- if (!key_is_grabbed && do_grab) {
- VWM_TRACE("saving focused_origin of %p", vwin);
- focused_origin = vwin; /* for returning to on abort */
- XGrabKeyboard(display, RootWindow(display, screen_num), False, GrabModeAsync, GrabModeAsync, CurrentTime);
- key_is_grabbed = 1;
- }
-
- /* remember the symbol for repeater detection */
- last_sym = sym;
- last_state = keypress->xkey.state;
-}
-
-
- /* some randomish things called from main() */
-
-/* manage all existing windows (for startup) */
-static int vwm_manage_existing(void)
-{
- Window root, parent;
- Window *children = NULL;
- unsigned int n_children, i;
-
- XGrabServer(display);
- XSync(display, False);
- XQueryTree(display, RootWindow(display, screen_num), &root, &parent, &children, &n_children);
-
- for (i = 0; i < n_children; i++) {
- if (children[i] == None) continue;
-
- if ((vwm_xwin_create(children[i], VWM_GRABBED) == NULL)) goto _fail_grabbed;
- }
-
- XUngrabServer(display);
-
- if (children) XFree(children);
-
- return 1;
-
-_fail_grabbed:
- XUngrabServer(display);
-
- if (children) XFree(children);
-
- return 0;
-}
-
-
-/* comvenience function for returning the time delta as a seconds.fraction float */
-static float delta(struct timeval *cur, struct timeval *prev)
-{
- struct timeval res;
- float delta;
-
- /* determine the # of whole.fractional seconds between prev and cur */
- timersub(cur, prev, &res);
-
- delta = res.tv_sec;
- delta += (float)((float)res.tv_usec) / 1000000.0;
-
- return delta;
-}
-
-static int errhandler(Display *display, XErrorEvent *err)
-{
- /* TODO */
- return 1;
-}
-
-int main(int argc, char *argv[])
-{
- int err = 0;
- int done = 0;
- XEvent event;
- Cursor pointer;
- struct pollfd pfd;
- char *console_args[] = {"xterm", "-class", CONSOLE_WM_CLASS, "-e", "/bin/sh", "-c", "screen -D -RR " CONSOLE_SESSION_STRING, NULL};
- Window bitmask;
- vwm_clickety_t clickety = { .vwin = NULL };
-
-#define reterr_if(_cond, _fmt, _args...) \
- err++;\
- if (_cond) {\
- VWM_ERROR(_fmt, ##_args);\
- return err;\
- }
-
- /* open connection with the server */
- reterr_if((display = XOpenDisplay(NULL)) == NULL, "Cannot open display");
-
- /* prevent children from inheriting the X connection */
- reterr_if(fcntl(ConnectionNumber(display), F_SETFD, FD_CLOEXEC) < 0, "Cannot set FD_CLOEXEC on X connection");
-
- /* get our scheduling priority, clients are launched with a priority LAUNCHED_RELATIVE_PRIORITY nicer than this */
- reterr_if((priority = getpriority(PRIO_PROCESS, getpid())) == -1, "Cannot get scheduling priority");
-
- XSetErrorHandler(errhandler);
-
- screen_num = DefaultScreen(display);
-
- reterr_if(!XQueryExtension (display, COMPOSITE_NAME, &composite_opcode, &composite_event, &composite_error), "No composite extension available");
- reterr_if(!XDamageQueryExtension(display, &damage_event, &damage_error), "No damage extension available");
- if (XSyncQueryExtension(display, &sync_event, &sync_error)) {
- /* set the window manager to the maximum X client priority */
- XSyncSetPriority(display, RootWindow(display, screen_num), 0x7fffffff);
- }
-
- if (XineramaQueryExtension(display, &xinerama_event, &xinerama_error)) {
- xinerama_screens = XineramaQueryScreens(display, &xinerama_screens_cnt);
- }
-
- if (XRRQueryExtension(display, &randr_event, &randr_error)) {
- XRRSelectInput(display, RootWindow(display, screen_num), RRScreenChangeNotifyMask);
- }
-
- /* allocate colors, I make assumptions about the X server's color capabilities since I'll only use this on modern-ish computers... */
- cmap = DefaultColormap(display, screen_num);
-
-#define color(_sym, _str) \
- XAllocNamedColor(display, cmap, _str, &_sym ## _color, &_sym ## _color);
-#include "colors.def"
-#undef color
-
- wm_delete_atom = XInternAtom(display, "WM_DELETE_WINDOW", False);
- wm_protocols_atom = XInternAtom(display, "WM_PROTOCOLS", False);
- wm_pid_atom = XInternAtom(display, "_NET_WM_PID", False);
-
- XSelectInput(display, RootWindow(display, screen_num),
- PropertyChangeMask | SubstructureNotifyMask | SubstructureRedirectMask | PointerMotionMask | ButtonPressMask | ButtonReleaseMask);
- XGrabKey(display, AnyKey, WM_GRAB_MODIFIER, RootWindow(display, screen_num), False, GrabModeAsync, GrabModeAsync);
-
- XFlush(display);
-
- XSetInputFocus(display, RootWindow(display, screen_num), RevertToPointerRoot, CurrentTime);
-
- /* initialize libvmon */
- vmon_init(&vmon, VMON_FLAG_2PASS, VMON_WANT_SYS_STAT, (VMON_WANT_PROC_STAT | VMON_WANT_PROC_FOLLOW_CHILDREN | VMON_WANT_PROC_FOLLOW_THREADS));
- vmon.proc_ctor_cb = vmon_ctor_cb;
- vmon.proc_dtor_cb = vmon_dtor_cb;
- vmon.sample_cb = sample_callback;
-
- /* get all the text and graphics stuff setup for overlays */
- reterr_if(!(overlay_font = XLoadQueryFont(display, OVERLAY_FIXED_FONT)), "failed to open font: " OVERLAY_FIXED_FONT);
-
- /* create a GC for rendering the text using Xlib into the text overlay stencils */
- bitmask = XCreatePixmap(display, RootWindow(display, screen_num), 1, 1, OVERLAY_MASK_DEPTH);
- text_gc = XCreateGC(display, bitmask, 0, NULL);
- XSetForeground(display, text_gc, WhitePixel(display, screen_num));
- XFreePixmap(display, bitmask);
-
- /* create some repeating source fill pictures for drawing through the text and graph stencils */
- bitmask = XCreatePixmap(display, RootWindow(display, screen_num), 1, 1, 32);
- overlay_text_fill = XRenderCreatePicture(display, bitmask, XRenderFindStandardFormat(display, PictStandardARGB32), CPRepeat, &pa_repeat);
- XRenderFillRectangle(display, PictOpSrc, overlay_text_fill, &overlay_visible_color, 0, 0, 1, 1);
-
- bitmask = XCreatePixmap(display, RootWindow(display, screen_num), 1, 1, 32);
- overlay_shadow_fill = XRenderCreatePicture(display, bitmask, XRenderFindStandardFormat(display, PictStandardARGB32), CPRepeat, &pa_repeat);
- XRenderFillRectangle(display, PictOpSrc, overlay_shadow_fill, &overlay_shadow_color, 0, 0, 1, 1);
-
- bitmask = XCreatePixmap(display, RootWindow(display, screen_num), 1, OVERLAY_ROW_HEIGHT, 32);
- overlay_bg_fill = XRenderCreatePicture(display, bitmask, XRenderFindStandardFormat(display, PictStandardARGB32), CPRepeat, &pa_repeat);
- XRenderFillRectangle(display, PictOpSrc, overlay_bg_fill, &overlay_bg_color, 0, 0, 1, OVERLAY_ROW_HEIGHT);
- XRenderFillRectangle(display, PictOpSrc, overlay_bg_fill, &overlay_div_color, 0, OVERLAY_ROW_HEIGHT - 1, 1, 1);
-
- bitmask = XCreatePixmap(display, RootWindow(display, screen_num), 1, 1, 32);
- overlay_snowflakes_text_fill = XRenderCreatePicture(display, bitmask, XRenderFindStandardFormat(display, PictStandardARGB32), CPRepeat, &pa_repeat);
- XRenderFillRectangle(display, PictOpSrc, overlay_snowflakes_text_fill, &overlay_snowflakes_visible_color, 0, 0, 1, 1);
-
- bitmask = XCreatePixmap(display, RootWindow(display, screen_num), 1, 1, 32);
- overlay_grapha_fill = XRenderCreatePicture(display, bitmask, XRenderFindStandardFormat(display, PictStandardARGB32), CPRepeat, &pa_repeat);
- XRenderFillRectangle(display, PictOpSrc, overlay_grapha_fill, &overlay_grapha_color, 0, 0, 1, 1);
-
- bitmask = XCreatePixmap(display, RootWindow(display, screen_num), 1, 1, 32);
- overlay_graphb_fill = XRenderCreatePicture(display, bitmask, XRenderFindStandardFormat(display, PictStandardARGB32), CPRepeat, &pa_repeat);
- XRenderFillRectangle(display, PictOpSrc, overlay_graphb_fill, &overlay_graphb_color, 0, 0, 1, 1);
-
- bitmask = XCreatePixmap(display, RootWindow(display, screen_num), 1, 2, 32);
- overlay_finish_fill = XRenderCreatePicture(display, bitmask, XRenderFindStandardFormat(display, PictStandardARGB32), CPRepeat, &pa_repeat);
- XRenderFillRectangle(display, PictOpSrc, overlay_finish_fill, &overlay_visible_color, 0, 0, 1, 1);
- XRenderFillRectangle(display, PictOpSrc, overlay_finish_fill, &overlay_trans_color, 0, 1, 1, 1);
-
- /* create initial virtual desktop */
- vwm_desktop_focus(vwm_desktop_create(NULL));
- vwm_desktop_mru(focused_desktop);
-
- /* manage all preexisting windows */
- vwm_manage_existing();
-
- /* create GC for logo drawing and window rubber-banding */
- gc = XCreateGC(display, RootWindow(display, screen_num), 0, NULL);
- XSetSubwindowMode(display, gc, IncludeInferiors);
- XSetFunction(display, gc, GXxor);
-
- /* launch the console here so it's likely ready by the time the logo animation finishes (there's no need to synchronize with it currently) */
- vwm_launch(console_args, VWM_LAUNCH_MODE_BG);
-
- /* first the logo color is the foreground */
- XSetForeground(display, gc, logo_color.pixel);
- vwm_draw_logo();
-
- /* change to the rubber-banding foreground color */
- XSetForeground(display, gc, rubberband_color.pixel);
-
- XClearWindow(display, RootWindow(display, screen_num));
-
- /* set the pointer */
- pointer = XCreateFontCursor(display, XC_X_cursor);
- XDefineCursor(display, RootWindow(display, screen_num), pointer);
-
- pfd.events = POLLIN;
- pfd.revents = 0;
- pfd.fd = ConnectionNumber(display);
-
- gettimeofday(&this_sample, NULL);
- while (!done) {
- do {
- static int sampling_paused = 0;
- static int contiguous_drops = 0;
- float this_delta;
-
- gettimeofday(&maybe_sample, NULL);
- if ((sampling_interval == -1 && !sampling_paused) || /* XXX this is kind of a kludge to get the 0 Hz indicator drawn before pausing */
- (sampling_interval != -1 && ((this_delta = delta(&maybe_sample, &this_sample)) >= sampling_intervals[sampling_interval]))) {
- vmon_sys_stat_t *sys_stat;
-
- /* automatically lower the sample rate if we can't keep up with the current sample rate */
- if (sampling_interval != -1 && sampling_interval <= prev_sampling_interval &&
- this_delta >= (sampling_intervals[sampling_interval] * 1.5)) {
- contiguous_drops++;
- /* require > 1 contiguous drops before lowering the rate, tolerates spurious one-off stalls */
- if (contiguous_drops > 2) sampling_interval--;
- } else contiguous_drops = 0;
-
- /* age the sys-wide sample data into "last" variables, before the new sample overwrites them. */
- last_sample = this_sample;
- this_sample = maybe_sample;
- if ((sys_stat = vmon.stores[VMON_STORE_SYS_STAT])) {
- last_user_cpu = sys_stat->user;
- last_system_cpu = sys_stat->system;
- last_total = sys_stat->user +
- sys_stat->nice +
- sys_stat->system +
- sys_stat->idle +
- sys_stat->iowait +
- sys_stat->irq +
- sys_stat->softirq +
- sys_stat->steal +
- sys_stat->guest;
-
- last_idle = sys_stat->idle;
- last_iowait = sys_stat->iowait;
- }
-
- vmon_sample(&vmon); /* XXX: calls proc_sample_callback() for explicitly monitored processes after sampling their descendants */
- /* XXX: also calls sample_callback() per invocation after sampling the sys wants */
- sampling_paused = (sampling_interval == -1);
- prev_sampling_interval = sampling_interval;
- }
-
- XFlush(display);
-
- if (!XPending(display)) {
- /* TODO: make some effort to compute how long to sleep, but this is perfectly fine for now. */
- if (poll(&pfd, 1, sampling_interval != -1 ? sampling_intervals[sampling_interval] * 300.0 : -1) == 0) break;
- }
-
- XNextEvent(display, &event);
- switch (event.type) {
- case KeyPress:
- VWM_TRACE("keypress");
- vwm_keypressed(event.xkey.window, &event);
- break;
-
- case KeyRelease:
- VWM_TRACE("keyrelease");
- vwm_keyreleased(event.xkey.window, &event);
- break;
-
- case ButtonPress:
- VWM_TRACE("buttonpresss");
- vwm_clickety_pressed(&clickety, event.xbutton.window, &event.xbutton);
- break;
-
- case MotionNotify:
- //VWM_TRACE("motionnotify");
- vwm_clickety_motion(&clickety, event.xmotion.window, &event.xmotion);
- break;
-
- case ButtonRelease:
- VWM_TRACE("buttonrelease");
- vwm_clickety_released(&clickety, event.xbutton.window, &event.xbutton);
- break;
-
- case CreateNotify:
- VWM_TRACE("createnotify");
- vwm_xwin_create(event.xcreatewindow.window, VWM_NOT_GRABBED);
- break;
-
- case DestroyNotify: {
- vwm_xwindow_t *xwin;
- VWM_TRACE("destroynotify");
- if ((xwin = vwm_xwin_lookup(event.xdestroywindow.window))) {
- vwm_xwin_destroy(xwin);
- }
- break;
- }
-
- case ConfigureRequest: {
- XWindowChanges changes = {
- .x = event.xconfigurerequest.x, /* TODO: for now I don't manipulate anything */
- .y = event.xconfigurerequest.y,
- .width = event.xconfigurerequest.width,
- .height = event.xconfigurerequest.height,
- .border_width = WINDOW_BORDER_WIDTH /* except I do override whatever the border width may be */
- };
- unsigned long change_mask = (event.xconfigurerequest.value_mask & (CWX | CWY | CWWidth | CWHeight)) | CWBorderWidth;
- vwm_xwindow_t *xwin;
-
- /* XXX: windows raising themselves is annoying, so discard CWSibling and CWStackMode. */
- VWM_TRACE("configurerequest x=%i y=%i w=%i h=%i", changes.x, changes.y, changes.width, changes.height);
-
- if ((xwin = vwm_xwin_lookup(event.xconfigure.window)) &&
- xwin->managed &&
- xwin->managed->autoconfigured == VWM_WIN_AUTOCONF_ALL) {
- /* this is to allow auto-allscreen to succeed in getting a borderless window configured */
- change_mask &= ~CWBorderWidth;
- }
-
- XConfigureWindow(display, event.xconfigurerequest.window, change_mask, &changes);
- break;
- }
-
- case ConfigureNotify: {
- vwm_xwindow_t *xwin;
- VWM_TRACE("configurenotify");
- if ((xwin = vwm_xwin_lookup(event.xconfigure.window))) {
- XWindowAttributes attrs;
- vwm_xwin_restack(xwin, event.xconfigure.above);
- XGetWindowAttributes(display, event.xconfigure.window, &attrs);
- if (compositing_mode) {
- /* damage the old and new window areas */
- XserverRegion region;
- XRectangle rects[2] = { { xwin->attrs.x,
- xwin->attrs.y,
- xwin->attrs.width + xwin->attrs.border_width * 2,
- xwin->attrs.height + xwin->attrs.border_width * 2 },
- { attrs.x,
- attrs.y,
- attrs.width + attrs.border_width * 2,
- attrs.height + attrs.border_width * 2 } };
-
- region = XFixesCreateRegion(display, rects, 2);
- vwm_comp_damage_add(region);
- vwm_xwin_unbind_namewindow(xwin);
- vwm_xwin_bind_namewindow(xwin);
- }
- VWM_TRACE("pre x=%i y=%i w=%i h=%i\n", xwin->attrs.x, xwin->attrs.y, xwin->attrs.width, xwin->attrs.height);
- xwin->attrs = attrs;
- VWM_TRACE("post x=%i y=%i w=%i h=%i\n", xwin->attrs.x, xwin->attrs.y, xwin->attrs.width, xwin->attrs.height);
- }
- break;
- }
-
- case UnmapNotify: {
- vwm_xwindow_t *xwin;
- VWM_TRACE("unmapnotify");
- /* unlike MapRequest, we simply are notified when a window is unmapped. */
- if ((xwin = vwm_xwin_lookup(event.xunmap.window))) {
- if (xwin->managed) {
- if (xwin->managed->unmapping) {
- VWM_TRACE("swallowed vwm-induced UnmapNotify");
- xwin->managed->unmapping = 0;
- } else {
- /* client requested unmap, demote the window and note the unmapped state */
- vwm_win_unmanage(xwin->managed);
- xwin->mapped = 0;
- }
- } else {
- /* if it's not managed, we can't have caused the map */
- xwin->mapped = 0;
- }
-
- if (compositing_mode) vwm_comp_damage_win(xwin);
- }
- break;
- }
-
- case MapNotify: {
- vwm_xwindow_t *xwin;
- VWM_TRACE("mapnotify");
- if ((xwin = vwm_xwin_lookup(event.xmap.window))) {
- if (xwin->managed && xwin->managed->mapping) {
- VWM_TRACE("swallowed vwm-induced MapNotify");
- xwin->managed->mapping = 0;
- } else {
- /* some windows like popup dialog boxes bypass MapRequest */
- xwin->mapped = 1;
- }
-
- if (compositing_mode) {
- vwm_comp_damage_win(xwin);
- vwm_xwin_unbind_namewindow(xwin);
- vwm_xwin_bind_namewindow(xwin);
- }
- }
- break;
- }
-
- case MapRequest: {
- vwm_xwindow_t *xwin;
- vwm_window_t *vwin = NULL;
- int domap = 1;
- VWM_TRACE("maprequest");
- if ((xwin = vwm_xwin_lookup(event.xmap.window)) &&
- ((vwin = xwin->managed) || (vwin = vwm_win_manage_xwin(xwin)))) {
- XWindowAttributes attrs;
- XWindowChanges changes = {.x = 0, .y = 0};
- unsigned changes_mask = (CWX | CWY);
- XClassHint *classhint;
- const vwm_screen_t *scr = NULL;
-
- xwin->mapped = 1; /* note that the client mapped the window */
-
- /* figure out if the window is the console */
- if ((classhint = XAllocClassHint())) {
- if (XGetClassHint(display, event.xmap.window, classhint) && !strcmp(classhint->res_class, CONSOLE_WM_CLASS)) {
- console = vwin;
- vwm_win_shelve(vwin);
- vwm_win_autoconf(vwin, VWM_SCREEN_REL_XWIN, VWM_WIN_AUTOCONF_FULL);
- domap = 0;
- }
-
- if (classhint->res_class) XFree(classhint->res_class);
- if (classhint->res_name) XFree(classhint->res_name);
- XFree(classhint);
- }
-
- /* TODO: this is a good place to hook in a window placement algo */
-
- /* on client-requested mapping we place the window */
- if (!vwin->shelved) {
- /* we place the window on the screen containing the the pointer only if that screen is empty,
- * otherwise we place windows on the screen containing the currently focused window */
- /* since we query the geometry of windows in determining where to place them, a configuring
- * flag is used to exclude the window being configured from those queries */
- scr = vwm_screen_find(VWM_SCREEN_REL_POINTER);
- vwin->configuring = 1;
- if (vwm_screen_is_empty(scr)) {
- /* focus the new window if it isn't already focused when it's going to an empty screen */
- VWM_TRACE("window \"%s\" is alone on screen \"%i\", focusing", vwin->xwindow->name, scr->screen_number);
- vwm_win_focus(vwin);
- } else {
- scr = vwm_screen_find(VWM_SCREEN_REL_XWIN, focused_desktop->focused_window->xwindow);
- }
- vwin->configuring = 0;
-
- changes.x = scr->x_org;
- changes.y = scr->y_org;
- } else if (focused_context == VWM_CONTEXT_FOCUS_SHELF) {
- scr = vwm_screen_find(VWM_SCREEN_REL_XWIN, focused_shelf->xwindow);
- changes.x = scr->x_org;
- changes.y = scr->y_org;
- }
-
- /* XXX TODO: does this belong here? */
- XGetWMNormalHints(display, event.xmap.window, vwin->hints, &vwin->hints_supplied);
- XGetWindowAttributes(display, event.xmap.window, &attrs);
-
-
- /* if the window size is precisely the screen size then directly "allscreen" the window right here */
- if (!vwin->shelved && scr &&
- attrs.width == scr->width &&
- attrs.height == scr->height) {
- VWM_TRACE("auto-allscreened window \"%s\"", vwin->xwindow->name);
- changes.border_width = 0;
- changes_mask |= CWBorderWidth;
- vwin->autoconfigured = VWM_WIN_AUTOCONF_ALL;
- }
-
- vwin->client.x = changes.x;
- vwin->client.y = changes.y;
- vwin->client.height = attrs.height;
- vwin->client.width = attrs.width;
-
- XConfigureWindow(display, event.xmap.window, changes_mask, &changes);
- }
-
- if (domap) {
- XMapWindow(display, event.xmap.window);
- if (vwin && vwin->desktop->focused_window == vwin) {
- XSync(display, False);
- XSetInputFocus(display, vwin->xwindow->id, RevertToPointerRoot, CurrentTime);
- }
- }
- break;
- }
-
- case PropertyNotify: {
- vwm_xwindow_t *xwin;
- VWM_TRACE("property notify");
- if ((xwin = vwm_xwin_lookup(event.xproperty.window)) &&
- event.xproperty.atom == wm_pid_atom &&
- event.xproperty.state == PropertyNewValue) vwm_xwin_monitor(xwin);
- break;
- }
-
- case MappingNotify:
- VWM_TRACE("mapping notify");
- XRefreshKeyboardMapping(&event.xmapping);
- break;
-
- case Expose:
- VWM_TRACE("expose");
- break;
- case GravityNotify:
- VWM_TRACE("gravitynotify");
- break;
- case ReparentNotify:
- VWM_TRACE("reparentnotify");
- break;
- default:
- if (event.type == randr_event + RRScreenChangeNotify) {
- VWM_TRACE("rrscreenchangenotify");
- if (xinerama_screens) XFree(xinerama_screens);
- xinerama_screens = XineramaQueryScreens(display, &xinerama_screens_cnt);
-
- if (compositing_mode) vwm_comp_invalidate_root();
- } else if (event.type == damage_event + XDamageNotify) {
- //VWM_TRACE("damagenotify");
- vwm_comp_damage_event((XDamageNotifyEvent *)&event);
- } else {
- VWM_ERROR("Unhandled X op %i", event.type);
- }
- break;
- }
- } while (QLength(display));
-
- if (combined_damage != None) { /* if there's damage to repaint, do it, this only happens when compositing for overlays is enabled */
- vwm_comp_paint_all();
- XSync(display, False);
- }
- }
-
- /* close connection to server */
- XFlush(display);
- XCloseDisplay(display);
-
- return 0;
-}
diff --git a/vwm.h b/vwm.h
deleted file mode 100644
index 7a85360..0000000
--- a/vwm.h
+++ /dev/null
@@ -1,84 +0,0 @@
-#ifndef _VWM_H
-#define _VWM_H
-
-#include <stdio.h>
-#include <X11/Xlib.h>
-#include <X11/Xutil.h>
-#include <errno.h>
-
-#include "list.h"
-
-#define VWM_ERROR(_fmt, _args...) fprintf(stderr, "%s:%i\t%s() "_fmt"\n", __FILE__, __LINE__, __FUNCTION__, ##_args)
-#define VWM_PERROR(_fmt, _args...) fprintf(stderr, "%s:%i\t%s() "_fmt"; %s\n", __FILE__, __LINE__, __FUNCTION__, ##_args, strerror(errno))
-#define VWM_BUG(_fmt, _args...) fprintf(stderr, "BUG %s:%i\t%s() "_fmt"; %s\n", __FILE__, __LINE__, __FUNCTION__, ##_args, strerror(errno))
-
-#ifdef TRACE
-#define VWM_TRACE(_fmt, _args...) fprintf(stderr, "%s:%i\t%s() "_fmt"\n", __FILE__, __LINE__, __FUNCTION__, ##_args)
-#else
-#define VWM_TRACE(_fmt, _args...) do { } while(0)
-#endif
-
-typedef struct _vwm_desktop_t {
- list_head_t desktops; /* global list of (virtual) desktops */
- list_head_t desktops_mru; /* global list of (virtual) desktops in MRU order */
- char *name; /* name of the desktop (TODO) */
- struct _vwm_window_t *focused_window; /* the focused window on this virtual desktop */
-} vwm_desktop_t;
-
-/* everything needed by the per-window overlay's context */
-typedef struct _vwm_overlay_t {
- Pixmap text_pixmap; /* pixmap for overlayed text (kept around for XDrawText usage) */
- Picture text_picture; /* picture representation of text_pixmap */
- Picture shadow_picture; /* text shadow layer */
- Picture grapha_picture; /* graph A layer */
- Picture graphb_picture; /* graph B layer */
- Picture tmp_picture; /* 1 row worth of temporary picture space */
- Picture picture; /* overlay picture derived from the pixmap, for render compositing */
- int width; /* current width of the overlay */
- int height; /* current height of the overlay */
- int phase; /* current position within the (horizontally scrolling) graphs */
- int heirarchy_end; /* row where the process heirarchy currently ends */
- int snowflakes_cnt; /* count of snowflaked rows (reset to zero to truncate snowflakes display) */
- int gen_last_composed; /* the last composed vmon generation */
-} vwm_overlay_t;
-
-/* every window gets this, even non-managed ones. For compositing vwm must track everything visible, even popup menus. */
-typedef struct _vwm_xwindow_t {
- list_head_t xwindows; /* global list of all windows kept in X stacking order */
-
- Window id; /* X Window backing this instance */
- XWindowAttributes attrs; /* X window's current attributes, kept up-to-date in handling of ConfigureNotify events */
- Damage damage; /* X damage object associated with the window (for compositing) */
- Picture picture; /* X picture object representing the window (for compositing) */
- Pixmap pixmap; /* X pixmap object representing the window (for compositing) */
-
- vmon_proc_t *monitor; /* vmon process monitor handle, may be NULL if for example the X client doesn't supply a PID */
- vwm_overlay_t overlay; /* monitoring overlay state */
-
- char *name; /* client name */
- unsigned int mapped:1; /* is the window currently mapped (by client) */
- unsigned int occluded:1; /* is the window occluded entirely by another window? (used and valid only during paint_all()) */
- /* if only Xorg could send VisibilityNotify events when requested for redirected windows :( */
- struct _vwm_window_t *managed; /* is the window "managed"? NULL or this points to the managed context of the window */
-} vwm_xwindow_t;
-
-/* the managed window we create for every mapped window we actually manage */
-typedef struct _vwm_window_t {
- list_head_t windows_mru; /* global list of managed windows kept in MRU order */
-
- vwm_xwindow_t *xwindow; /* window being managed */
- vwm_desktop_t *desktop; /* desktop this window belongs to currently */
-
- XWindowAttributes client; /* attrs of the client-configured window */
-
- XSizeHints *hints; /* hints the client supplied */
- long hints_supplied; /* bitfield reflecting the hints the client supplied */
-
- unsigned int autoconfigured:3; /* autoconfigured window states (none/quarter/half/full/all) */
- unsigned int mapping:1; /* is the window being mapped? (by vwm) */
- unsigned int unmapping:1; /* is the window being unmapped? (by vwm) */
- unsigned int configuring:1; /* is the window being configured/placed? (by vwm) */
- unsigned int shelved:1; /* is the window shelved? */
-} vwm_window_t;
-
-#endif
© All Rights Reserved