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