all repos — dwm @ 33996500763b04119a6867dfa4040a4236c21a41

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