all repos — dwm @ 439e15d09f6fa9271d3b49ef97194f0c80ebe161

fork of suckless dynamic window manager

wm.c (view raw)

  1/*
  2 * (C)opyright MMVI Anselm R. Garbe <garbeam at gmail dot com>
  3 * See LICENSE file for license details.
  4 */
  5
  6#include <stdarg.h>
  7#include <stdio.h>
  8#include <stdlib.h>
  9
 10#include <X11/cursorfont.h>
 11#include <X11/Xatom.h>
 12#include <X11/Xproto.h>
 13
 14#include "wm.h"
 15
 16/* X structs */
 17Display *dpy;
 18Window root, barwin;
 19Atom wm_atom[WMLast], net_atom[NetLast];
 20Cursor cursor[CurLast];
 21XRectangle rect, barrect;
 22Bool running = True;
 23
 24char *bartext, *shell;
 25int screen, sel_screen;
 26unsigned int lock_mask, numlock_mask;
 27
 28/* draw structs */
 29Brush brush = {0};
 30
 31enum { WM_PROTOCOL_DELWIN = 1 };
 32
 33static Bool other_wm_running;
 34static int (*x_error_handler) (Display *, XErrorEvent *);
 35static char version[] = "gridwm - " VERSION ", (C)opyright MMVI Anselm R. Garbe\n";
 36
 37static void
 38usage()
 39{
 40	fputs("usage: gridwm [-v]\n", stderr);
 41	exit(1);
 42}
 43
 44static void
 45scan_wins()
 46{
 47	unsigned int i, num;
 48	Window *wins;
 49	XWindowAttributes wa;
 50	Window d1, d2;
 51
 52	if(XQueryTree(dpy, root, &d1, &d2, &wins, &num)) {
 53		for(i = 0; i < num; i++) {
 54			if(!XGetWindowAttributes(dpy, wins[i], &wa))
 55				continue;
 56			if(wa.override_redirect || XGetTransientForHint(dpy, wins[i], &d1))
 57				continue;
 58			if(wa.map_state == IsViewable)
 59				/*manage*/;
 60		}
 61	}
 62	if(wins)
 63		XFree(wins);
 64}
 65
 66static int
 67win_property(Window w, Atom a, Atom t, long l, unsigned char **prop)
 68{
 69	Atom real;
 70	int format;
 71	unsigned long res, extra;
 72	int status;
 73
 74	status = XGetWindowProperty(dpy, w, a, 0L, l, False, t, &real, &format,
 75			&res, &extra, prop);
 76
 77	if(status != Success || *prop == 0) {
 78		return 0;
 79	}
 80	if(res == 0) {
 81		free((void *) *prop);
 82	}
 83	return res;
 84}
 85
 86int
 87win_proto(Window w)
 88{
 89	Atom *protocols;
 90	long res;
 91	int protos = 0;
 92	int i;
 93
 94	res = win_property(w, wm_atom[WMProtocols], XA_ATOM, 20L,
 95			((unsigned char **) &protocols));
 96	if(res <= 0) {
 97		return protos;
 98	}
 99	for(i = 0; i < res; i++) {
100		if(protocols[i] == wm_atom[WMDelete])
101			protos |= WM_PROTOCOL_DELWIN;
102	}
103	free((char *) protocols);
104	return protos;
105}
106
107/*
108 * There's no way to check accesses to destroyed windows, thus
109 * those cases are ignored (especially on UnmapNotify's).
110 * Other types of errors call Xlib's default error handler, which
111 * calls exit().
112 */
113static int
114error_handler(Display *dpy, XErrorEvent *error)
115{
116	if(error->error_code == BadWindow
117			|| (error->request_code == X_SetInputFocus
118				&& error->error_code == BadMatch)
119			|| (error->request_code == X_PolyText8
120				&& error->error_code == BadDrawable)
121			|| (error->request_code == X_PolyFillRectangle
122				&& error->error_code == BadDrawable)
123			|| (error->request_code == X_PolySegment
124				&& error->error_code == BadDrawable)
125			|| (error->request_code == X_ConfigureWindow
126				&& error->error_code == BadMatch)
127			|| (error->request_code == X_GrabKey
128				&& error->error_code == BadAccess))
129		return 0;
130	fprintf(stderr, "gridwm: fatal error: request code=%d, error code=%d\n",
131			error->request_code, error->error_code);
132	return x_error_handler(dpy, error); /* may call exit() */
133}
134
135/*
136 * Startup Error handler to check if another window manager
137 * is already running.
138 */
139static int
140startup_error_handler(Display *dpy, XErrorEvent *error)
141{
142	other_wm_running = True;
143	return -1;
144}
145
146static void
147init_lock_keys()
148{
149	XModifierKeymap *modmap;
150	KeyCode numlock;
151	int i;
152	static int masks[] = {
153		ShiftMask, LockMask, ControlMask, Mod1Mask,
154		Mod2Mask, Mod3Mask, Mod4Mask, Mod5Mask
155	};
156
157	numlock_mask = 0;
158	modmap = XGetModifierMapping(dpy);
159	numlock = XKeysymToKeycode(dpy, XStringToKeysym("Num_Lock"));
160
161	if(modmap && modmap->max_keypermod > 0) {
162		int max = (sizeof(masks) / sizeof(int)) * modmap->max_keypermod;
163		for(i = 0; i < max; i++)
164			if(numlock && (modmap->modifiermap[i] == numlock))
165				numlock_mask = masks[i / modmap->max_keypermod];
166	}
167	XFreeModifiermap(modmap);
168
169	lock_mask = 255 & ~(numlock_mask | LockMask);
170}
171
172static void
173cleanup()
174{
175	/*
176	Client *c;
177	for(c=client; c; c=c->next)
178		reparent_client(c, root, c->sel->rect.x, c->sel->rect.y);
179	XSetInputFocus(dpy, PointerRoot, RevertToPointerRoot, CurrentTime);
180	*/
181}
182
183int
184main(int argc, char *argv[])
185{
186	int i;
187	XSetWindowAttributes wa;
188	unsigned int mask;
189	Window w;
190	XEvent ev;
191
192	/* command line args */
193	for(i = 1; (i < argc) && (argv[i][0] == '-'); i++) {
194		switch (argv[i][1]) {
195		case 'v':
196			fprintf(stdout, "%s", version);
197			exit(0);
198			break;
199		default:
200			usage();
201			break;
202		}
203	}
204
205	dpy = XOpenDisplay(0);
206	if(!dpy)
207		error("gridwm: cannot connect X server\n");
208
209	screen = DefaultScreen(dpy);
210	root = RootWindow(dpy, screen);
211
212	/* check if another WM is already running */
213	other_wm_running = False;
214	XSetErrorHandler(startup_error_handler);
215	/* this causes an error if some other WM is running */
216	XSelectInput(dpy, root, SubstructureRedirectMask);
217	XFlush(dpy);
218
219	if(other_wm_running)
220		error("gridwm: another window manager is already running\n");
221
222	if(!(shell = getenv("SHELL")))
223		shell = "/bin/sh";
224
225	rect.x = rect.y = 0;
226	rect.width = DisplayWidth(dpy, screen);
227	rect.height = DisplayHeight(dpy, screen);
228	sel_screen = XQueryPointer(dpy, root, &w, &w, &i, &i, &i, &i, &mask);
229
230	XSetErrorHandler(0);
231	x_error_handler = XSetErrorHandler(error_handler);
232
233	/* init atoms */
234	wm_atom[WMState] = XInternAtom(dpy, "WM_STATE", False);
235	wm_atom[WMProtocols] = XInternAtom(dpy, "WM_PROTOCOLS", False);
236	wm_atom[WMDelete] = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
237	net_atom[NetSupported] = XInternAtom(dpy, "_NET_SUPPORTED", False);
238	net_atom[NetWMName] = XInternAtom(dpy, "_NET_WM_NAME", False);
239
240	XChangeProperty(dpy, root, net_atom[NetSupported], XA_ATOM, 32,
241			PropModeReplace, (unsigned char *) net_atom, NetLast);
242
243
244	/* init cursors */
245	cursor[CurNormal] = XCreateFontCursor(dpy, XC_left_ptr);
246	cursor[CurResize] = XCreateFontCursor(dpy, XC_sizing);
247	cursor[CurMove] = XCreateFontCursor(dpy, XC_fleur);
248
249	init_lock_keys();
250
251	brush.drawable = XCreatePixmap(dpy, root, rect.width, rect.height,
252			DefaultDepth(dpy, screen));
253	brush.gc = XCreateGC(dpy, root, 0, 0);
254
255	/* style */
256	loadcolors(dpy, screen, &brush, BGCOLOR, FGCOLOR, BORDERCOLOR);
257	loadfont(dpy, &brush.font, FONT);
258
259	wa.override_redirect = 1;
260	wa.background_pixmap = ParentRelative;
261	wa.event_mask = ExposureMask;
262
263	barrect = rect;
264	barrect.height = labelheight(&brush.font);
265	barrect.y = rect.height - barrect.height;
266	barwin = XCreateWindow(dpy, root, barrect.x, barrect.y,
267			barrect.width, barrect.height, 0, DefaultDepth(dpy, screen),
268			CopyFromParent, DefaultVisual(dpy, screen),
269			CWOverrideRedirect | CWBackPixmap | CWEventMask, &wa);
270	bartext = 0;
271	XDefineCursor(dpy, barwin, cursor[CurNormal]);
272	XMapRaised(dpy, barwin);
273	draw_bar();
274
275	wa.event_mask = SubstructureRedirectMask | EnterWindowMask | LeaveWindowMask;
276	wa.cursor = cursor[CurNormal];
277	XChangeWindowAttributes(dpy, root, CWEventMask | CWCursor, &wa);
278
279	scan_wins();
280
281	while(running) {
282		XNextEvent(dpy, &ev);
283		if(handler[ev.type])
284			(handler[ev.type]) (&ev); /* call handler */
285	}
286
287	cleanup();
288	XCloseDisplay(dpy);
289
290	return 0;
291}