kellex

TinyWM

Oct 10th, 2014
371
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 8.53 KB | None | 0 0
  1. /* TinyWM is written by Nick Welch <[email protected]>, 2005.
  2.  *
  3.  * This software is in the public domain
  4.  * and is provided AS IS, with NO WARRANTY. */
  5.  
  6. /* much of tinywm's purpose is to serve as a very basic example of how to do X
  7.  * stuff and/or understand window managers, so i wanted to put comments in the
  8.  * code explaining things, but i really hate wading through code that is
  9.  * over-commented -- and for that matter, tinywm is supposed to be as concise
  10.  * as possible, so having lots of comments just wasn't really fitting for it.
  11.  * i want tinywm.c to be something you can just look at and go "wow, that's
  12.  * it?  cool!"  so what i did was just copy it over to annotated.c and comment
  13.  * the hell out of it.  ahh, but now i have to make every code change twice!
  14.  * oh well.  i could always use some sort of script to process the comments out
  15.  * of this and write it to tinywm.c ... nah.
  16.  */
  17.  
  18. /* most X stuff will be included with Xlib.h, but a few things require other
  19.  * headers, like Xmd.h, keysym.h, etc.
  20.  */
  21. #include <X11/Xlib.h>
  22.  
  23. #define MAX(a, b) ((a) > (b) ? (a) : (b))
  24.  
  25. int main()
  26. {
  27.     Display * dpy;
  28.     Window root;
  29.     XWindowAttributes attr;
  30.  
  31.     /* we use this to save the pointer's state at the beginning of the
  32.      * move/resize.
  33.      */
  34.     XButtonEvent start;
  35.  
  36.     XEvent ev;
  37.  
  38.  
  39.     /* return failure status if we can't connect */
  40.     if(!(dpy = XOpenDisplay(0x0))) return 1;
  41.  
  42.     /* you'll usually be referencing the root window a lot.  this is a somewhat
  43.      * naive approach that will only work on the default screen.  most people
  44.      * only have one screen, but not everyone.  if you run multi-head without
  45.      * xinerama then you quite possibly have multiple screens. (i'm not sure
  46.      * about vendor-specific implementations, like nvidia's)
  47.      *
  48.      * many, probably most window managers only handle one screen, so in
  49.      * reality this isn't really *that* naive.
  50.      *
  51.      * if you wanted to get the root window of a specific screen you'd use
  52.      * RootWindow(), but the user can also control which screen is our default:
  53.      * if they set $DISPLAY to ":0.foo", then our default screen number is
  54.      * whatever they specify "foo" as.
  55.      */
  56.     root = DefaultRootWindow(dpy);
  57.  
  58.     /* you could also include keysym.h and use the XK_F1 constant instead of
  59.      * the call to XStringToKeysym, but this method is more "dynamic."  imagine
  60.      * you have config files which specify key bindings.  instead of parsing
  61.      * the key names and having a huge table or whatever to map strings to XK_*
  62.      * constants, you can just take the user-specified string and hand it off
  63.      * to XStringToKeysym.  XStringToKeysym will give you back the appropriate
  64.      * keysym or tell you if it's an invalid key name.
  65.      *
  66.      * a keysym is basically a platform-independent numeric representation of a
  67.      * key, like "F1", "a", "b", "L", "5", "Shift", etc.  a keycode is a
  68.      * numeric representation of a key on the keyboard sent by the keyboard
  69.      * driver (or something along those lines -- i'm no hardware/driver expert)
  70.      * to X.  so we never want to hard-code keycodes, because they can and will
  71.      * differ between systems.
  72.      */
  73.     XGrabKey(dpy, XKeysymToKeycode(dpy, XStringToKeysym("F1")), Mod1Mask, root,
  74.             True, GrabModeAsync, GrabModeAsync);
  75.  
  76.     /* XGrabKey and XGrabButton are basically ways of saying "when this
  77.      * combination of modifiers and key/button is pressed, send me the events."
  78.      * so we can safely assume that we'll receive Alt+F1 events, Alt+Button1
  79.      * events, and Alt+Button3 events, but no others.  You can either do
  80.      * individual grabs like these for key/mouse combinations, or you can use
  81.      * XSelectInput with KeyPressMask/ButtonPressMask/etc to catch all events
  82.      * of those types and filter them as you receive them.
  83.      */
  84.     XGrabButton(dpy, 1, Mod1Mask, root, True, ButtonPressMask, GrabModeAsync,
  85.             GrabModeAsync, None, None);
  86.     XGrabButton(dpy, 3, Mod1Mask, root, True, ButtonPressMask, GrabModeAsync,
  87.             GrabModeAsync, None, None);
  88.  
  89.     for(;;)
  90.     {
  91.         /* this is the most basic way of looping through X events; you can be
  92.          * more flexible by using XPending(), or ConnectionNumber() along with
  93.          * select() (or poll() or whatever floats your boat).
  94.          */
  95.         XNextEvent(dpy, &ev);
  96.  
  97.         /* this is our keybinding for raising windows.  as i saw someone
  98.          * mention on the ratpoison wiki, it is pretty stupid; however, i
  99.          * wanted to fit some sort of keyboard binding in here somewhere, and
  100.          * this was the best fit for it.
  101.          *
  102.          * i was a little confused about .window vs. .subwindow for a while,
  103.          * but a little RTFMing took care of that.  our passive grabs above
  104.          * grabbed on the root window, so since we're only interested in events
  105.          * for its child windows, we look at .subwindow.  when subwindow
  106.          * None, that means that the window the event happened in was the same
  107.          * window that was grabbed on -- in this case, the root window.
  108.          */
  109.         if(ev.type == KeyPress && ev.xkey.subwindow != None)
  110.             XRaiseWindow(dpy, ev.xkey.subwindow);
  111.         else if(ev.type == ButtonPress && ev.xbutton.subwindow != None)
  112.         {
  113.             /* now we take command of the pointer, looking for motion and
  114.              * button release events.
  115.              */
  116.             XGrabPointer(dpy, ev.xbutton.subwindow, True,
  117.                     PointerMotionMask|ButtonReleaseMask, GrabModeAsync,
  118.                     GrabModeAsync, None, None, CurrentTime);
  119.  
  120.             /* we "remember" the position of the pointer at the beginning of
  121.              * our move/resize, and the size/position of the window.  that way,
  122.              * when the pointer moves, we can compare it to our initial data
  123.              * and move/resize accordingly.
  124.              */
  125.             XGetWindowAttributes(dpy, ev.xbutton.subwindow, &attr);
  126.             start = ev.xbutton;
  127.         }
  128.         /* the only way we'd receive a motion notify event is if we already did
  129.          * a pointer grab and we're in move/resize mode, so we assume that. */
  130.         else if(ev.type == MotionNotify)
  131.         {
  132.             int xdiff, ydiff;
  133.  
  134.             /* here we "compress" motion notify events.  if there are 10 of
  135.              * them waiting, it makes no sense to look at any of them but the
  136.              * most recent.  in some cases -- if the window is really big or
  137.              * things are just acting slowly in general -- failing to do this
  138.              * can result in a lot of "drag lag."
  139.              *
  140.              * for window managers with things like desktop switching, it can
  141.              * also be useful to compress EnterNotify events, so that you don't
  142.              * get "focus flicker" as windows shuffle around underneath the
  143.              * pointer.
  144.              */
  145.             while(XCheckTypedEvent(dpy, MotionNotify, &ev));
  146.  
  147.             /* now we use the stuff we saved at the beginning of the
  148.              * move/resize and compare it to the pointer's current position to
  149.              * determine what the window's new size or position should be.
  150.              *
  151.              * if the initial button press was button 1, then we're moving.
  152.              * otherwise it was 3 and we're resizing.
  153.              *
  154.              * we also make sure not to go negative with the window's
  155.              * dimensions, resulting in "wrapping" which will make our window
  156.              * something ridiculous like 65000 pixels wide (often accompanied
  157.              * by lots of swapping and slowdown).
  158.              *
  159.              * even worse is if we get "lucky" and hit a width or height of
  160.              * exactly zero, triggering an X error.  so we specify a minimum
  161.              * width/height of 1 pixel.
  162.              */
  163.             xdiff = ev.xbutton.x_root - start.x_root;
  164.             ydiff = ev.xbutton.y_root - start.y_root;
  165.             XMoveResizeWindow(dpy, ev.xmotion.window,
  166.                 attr.x + (start.button==1 ? xdiff : 0),
  167.                 attr.y + (start.button==1 ? ydiff : 0),
  168.                 MAX(1, attr.width + (start.button==3 ? xdiff : 0)),
  169.                 MAX(1, attr.height + (start.button==3 ? ydiff : 0)));
  170.         }
  171.         /* like motion notifies, the only way we'll receive a button release is
  172.          * during a move/resize, due to our pointer grab.  this ends the
  173.          * move/resize.
  174.          */
  175.         else if(ev.type == ButtonRelease)
  176.             XUngrabPointer(dpy, CurrentTime);
  177.     }
  178. }
Advertisement
Add Comment
Please, Sign In to add comment