Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Initial commit

  • Loading branch information...
commit c80261807fbbbbeeb98bd7d5d376b7f91aa272c2 0 parents
authored August 17, 2009
1  .gitignore
... ...
@@ -0,0 +1 @@
  1
+possum
5  COPYING
... ...
@@ -0,0 +1,5 @@
  1
+TinyWM is written by Nick Welch <mack@incise.org>, 2005.
  2
+
  3
+This software is in the public domain
  4
+and is provided AS IS, with NO WARRANTY.
  5
+
9  Makefile
... ...
@@ -0,0 +1,9 @@
  1
+PREFIX?=/usr
  2
+CFLAGS?=-Os -pedantic -Wall
  3
+
  4
+all:
  5
+	$(CC) $(CFLAGS) -I$(PREFIX)/include -L$(PREFIX)/lib -lX11 -o possum possum.c
  6
+
  7
+clean:
  8
+	rm -f possum
  9
+
47  README
... ...
@@ -0,0 +1,47 @@
  1
+TinyWM is written by Nick Welch <mack@incise.org>, 2005.
  2
+
  3
+TinyWM is a ridiculously tiny window manager implemented in nearly as few lines
  4
+of C as possible, without being obfuscated or entirely useless. It allows you
  5
+to move, resize, focus (sloppy), and raise windows -- that's it!  TinyWM's main
  6
+purpose is to serve as a quick example of some window manager programming
  7
+basics.
  8
+
  9
+Files:
  10
+
  11
+  Makefile: highly advanced build system
  12
+  tinywm.c: the code
  13
+  annotated.c: same, but with tons of rambling comments about everything
  14
+  tinywm.py: a python version (requires CVS python-xlib due to a bug)
  15
+
  16
+Usage:
  17
+
  18
+  Focus follows pointer.
  19
+  Alt+Button1, drag: interactive window move
  20
+  Alt+Button3, drag: interactive window resize
  21
+  Alt+F1: raise focused window
  22
+
  23
+Misc:
  24
+  
  25
+  A fella by the name of Trent Buck made a lisp version:
  26
+
  27
+    http://twb.ath.cx/~twb/src/misc/tinywm.lisp
  28
+
  29
+  I have started a wiki about window manager development, although I'm not sure
  30
+  the subject is popular enough for the wiki to keep from dying a horrible
  31
+  death.  In any case, it is here:
  32
+
  33
+    http://riters.com/WMDev/
  34
+
  35
+  Another very small window manager is failsafewm.  Originally I started
  36
+  hacking on it, as there was quite a bit of stuff in it that I thought was
  37
+  unneeded, and I wound up rewriting it from scratch, with just the bare
  38
+  necessities -- that's TinyWM.
  39
+
  40
+    http://freshmeat.net/projects/failsafewm/
  41
+
  42
+  Yet another small -- but in comparison, big -- window manager is aewm.  It's
  43
+  a good example for learning about window management, and the fact that so
  44
+  many WMs have spawned from it is proof.
  45
+
  46
+      http://www.red-bean.com/~decklin/aewm/
  47
+
199  possum.c
... ...
@@ -0,0 +1,199 @@
  1
+/* possum is copyright Nick Markwell, 2009.
  2
+ * Based on TinyWM by Nick Welch <mack@incise.org>
  3
+ * and is provided AS IS, with NO WARRANTY. */
  4
+
  5
+/* most X stuff will be included with Xlib.h, but a few things require other
  6
+ * headers, like Xmd.h, keysym.h, etc.
  7
+ */
  8
+#include <X11/Xlib.h>
  9
+#include <stdlib.h>
  10
+#include <string.h>
  11
+#include <stdio.h>
  12
+
  13
+/* Yay for a macro that lets you use KEYCODE("key") to generate the definition... 
  14
+ * KEYCODE("F1")
  15
+ * ->
  16
+ * KeyCode kc_F1 = XKeysymToKeycode(dpy, XStringToKeysym("F1"));
  17
+ */
  18
+#define KEYCODE(NAME) KeyCode kc_##NAME = XKeysymToKeycode(dpy, XStringToKeysym( #NAME ));
  19
+#define KEYGRAB(NAME) XGrabKey(dpy, kc_##NAME, Mod1Mask, root, True, GrabModeAsync, GrabModeAsync);
  20
+
  21
+#define MAX(a, b) ((a) > (b) ? (a) : (b))
  22
+
  23
+int main()
  24
+{
  25
+    Display * dpy;
  26
+    Window root;
  27
+    XWindowAttributes attr;
  28
+
  29
+    /* we use this to save the pointer's state at the beginning of the
  30
+     * move/resize.
  31
+     */
  32
+    XButtonEvent start;
  33
+
  34
+    XEvent ev;
  35
+
  36
+	/* Get $DISPLAY from the environment */
  37
+	char *display_str = getenv("DISPLAY");
  38
+
  39
+	/* If $DISPLAY wasn't defined, use :0.0 */
  40
+	if ( !strcmp("", display_str) ) display_str = ":0.0";
  41
+
  42
+    /* return failure status if we can't connect */
  43
+    if(!(dpy = XOpenDisplay(display_str))) return 1;
  44
+
  45
+    root = DefaultRootWindow(dpy);
  46
+
  47
+    /* you could also include keysym.h and use the XK_F1 constant instead of
  48
+     * the call to XStringToKeysym, but this method is more "dynamic."  imagine
  49
+     * you have config files which specify key bindings.  instead of parsing
  50
+     * the key names and having a huge table or whatever to map strings to XK_*
  51
+     * constants, you can just take the user-specified string and hand it off
  52
+     * to XStringToKeysym.  XStringToKeysym will give you back the appropriate
  53
+     * keysym or tell you if it's an invalid key name.
  54
+     *
  55
+     * a keysym is basically a platform-independent numeric representation of a
  56
+     * key, like "F1", "a", "b", "L", "5", "Shift", etc.  a keycode is a
  57
+     * numeric representation of a key on the keyboard sent by the keyboard
  58
+     * driver (or something along those lines -- i'm no hardware/driver expert)
  59
+     * to X.  so we never want to hard-code keycodes, because they can and will
  60
+     * differ between systems.
  61
+     */
  62
+	KEYCODE(F1)
  63
+	KEYCODE(F2)
  64
+	KEYCODE(F3)
  65
+	KEYCODE(F4)
  66
+	KEYCODE(F5)
  67
+	KEYCODE(F6)
  68
+	KEYCODE(F7)
  69
+	KEYCODE(F8)
  70
+	KEYCODE(F9)
  71
+	KEYCODE(F10)
  72
+	KEYCODE(F11)
  73
+	KEYCODE(F12)
  74
+	KEYCODE(Enter)
  75
+    KEYGRAB(F1)
  76
+	KEYGRAB(F2)
  77
+	KEYGRAB(F3)
  78
+	KEYGRAB(F4)
  79
+	KEYGRAB(F5)
  80
+	KEYGRAB(F6)
  81
+	KEYGRAB(F7)
  82
+	KEYGRAB(F8)
  83
+	KEYGRAB(F9)
  84
+	KEYGRAB(F10)
  85
+	KEYGRAB(F11)
  86
+	KEYGRAB(F12)
  87
+	KEYGRAB(Enter)
  88
+	/*XGrabKey(dpy, kc_Enter, Mod1Mask, root, True, GrabModeAsync, GrabModeAsync);*/
  89
+
  90
+    /* XGrabKey and XGrabButton are basically ways of saying "when this
  91
+     * combination of modifiers and key/button is pressed, send me the events."
  92
+     * so we can safely assume that we'll receive Alt+F1 events, Alt+Button1
  93
+     * events, and Alt+Button3 events, but no others.  You can either do
  94
+     * individual grabs like these for key/mouse combinations, or you can use
  95
+     * XSelectInput with KeyPressMask/ButtonPressMask/etc to catch all events
  96
+     * of those types and filter them as you receive them.
  97
+     */
  98
+    XGrabButton(dpy, 1, Mod1Mask, root, True, ButtonPressMask, GrabModeAsync,
  99
+            GrabModeAsync, None, None);
  100
+	XGrabButton(dpy, 2, Mod1Mask, root, True, ButtonPressMask, GrabModeAsync,
  101
+            GrabModeAsync, None, None);
  102
+    XGrabButton(dpy, 3, Mod1Mask, root, True, ButtonPressMask, GrabModeAsync,
  103
+            GrabModeAsync, None, None);
  104
+
  105
+    for(;;)
  106
+    {
  107
+        /* this is the most basic way of looping through X events; you can be
  108
+         * more flexible by using XPending(), or ConnectionNumber() along with
  109
+         * select() (or poll() or whatever floats your boat).
  110
+         */
  111
+        XNextEvent(dpy, &ev);
  112
+
  113
+        /* this is our keybinding for raising windows.  as i saw someone
  114
+         * mention on the ratpoison wiki, it is pretty stupid; however, i
  115
+         * wanted to fit some sort of keyboard binding in here somewhere, and
  116
+         * this was the best fit for it.
  117
+         *
  118
+         * i was a little confused about .window vs. .subwindow for a while,
  119
+         * but a little RTFMing took care of that.  our passive grabs above
  120
+         * grabbed on the root window, so since we're only interested in events
  121
+         * for its child windows, we look at .subwindow.  when subwindow
  122
+         * None, that means that the window the event happened in was the same
  123
+         * window that was grabbed on -- in this case, the root window.
  124
+         */
  125
+        if(ev.type == KeyPress && ev.xkey.subwindow != None)
  126
+            XRaiseWindow(dpy, ev.xkey.subwindow);
  127
+        else if(ev.type == ButtonPress && ev.xbutton.subwindow != None)
  128
+        {
  129
+			fprintf(stderr, "BUTTON DOWN!!!!!\n");
  130
+			/* Raise the window */
  131
+			XRaiseWindow(dpy, ev.xbutton.subwindow);
  132
+			
  133
+            /* now we take command of the pointer, looking for motion and
  134
+             * button release events.
  135
+             */
  136
+            XGrabPointer(dpy, ev.xbutton.subwindow, True,
  137
+                    PointerMotionMask|ButtonReleaseMask, GrabModeAsync,
  138
+                    GrabModeAsync, None, None, CurrentTime);
  139
+
  140
+            /* we "remember" the position of the pointer at the beginning of
  141
+             * our move/resize, and the size/position of the window.  that way,
  142
+             * when the pointer moves, we can compare it to our initial data
  143
+             * and move/resize accordingly.
  144
+             */
  145
+            XGetWindowAttributes(dpy, ev.xbutton.subwindow, &attr);
  146
+            start = ev.xbutton;
  147
+        }
  148
+        /* the only way we'd receive a motion notify event is if we already did
  149
+         * a pointer grab and we're in move/resize mode, so we assume that. */
  150
+        else if(ev.type == MotionNotify)
  151
+        {
  152
+            int xdiff, ydiff;
  153
+
  154
+            /* here we "compress" motion notify events.  if there are 10 of
  155
+             * them waiting, it makes no sense to look at any of them but the
  156
+             * most recent.  in some cases -- if the window is really big or
  157
+             * things are just acting slowly in general -- failing to do this
  158
+             * can result in a lot of "drag lag."
  159
+             *
  160
+             * for window managers with things like desktop switching, it can
  161
+             * also be useful to compress EnterNotify events, so that you don't
  162
+             * get "focus flicker" as windows shuffle around underneath the
  163
+             * pointer.
  164
+             */
  165
+            while(XCheckTypedEvent(dpy, MotionNotify, &ev));
  166
+
  167
+            /* now we use the stuff we saved at the beginning of the
  168
+             * move/resize and compare it to the pointer's current position to
  169
+             * determine what the window's new size or position should be.
  170
+             *
  171
+             * if the initial button press was button 1, then we're moving.
  172
+             * otherwise it was 3 and we're resizing.
  173
+             *
  174
+             * we also make sure not to go negative with the window's
  175
+             * dimensions, resulting in "wrapping" which will make our window
  176
+             * something ridiculous like 65000 pixels wide (often accompanied
  177
+             * by lots of swapping and slowdown).
  178
+             *
  179
+             * even worse is if we get "lucky" and hit a width or height of
  180
+             * exactly zero, triggering an X error.  so we specify a minimum
  181
+             * width/height of 1 pixel.
  182
+             */
  183
+            xdiff = ev.xbutton.x_root - start.x_root;
  184
+            ydiff = ev.xbutton.y_root - start.y_root;
  185
+            XMoveResizeWindow(dpy, ev.xmotion.window,
  186
+                attr.x + (start.button==1 ? xdiff : 0),
  187
+                attr.y + (start.button==1 ? ydiff : 0),
  188
+                MAX(1, attr.width + (start.button==3 ? xdiff : 0)),
  189
+                MAX(1, attr.height + (start.button==3 ? ydiff : 0)));
  190
+        }
  191
+        /* like motion notifies, the only way we'll receive a button release is
  192
+         * during a move/resize, due to our pointer grab.  this ends the
  193
+         * move/resize.
  194
+         */
  195
+        else if(ev.type == ButtonRelease)
  196
+			if ( ev.xbutton.subwindow != None )
  197
+            XUngrabPointer(dpy, CurrentTime);
  198
+    }
  199
+}

0 notes on commit c802618

Please sign in to comment.
Something went wrong with that request. Please try again.