1 /* evilwm - Minimalist Window Manager for X
2 * Copyright (C) 1999-2009 Ciaran Anscomb <evilwm@6809.org.uk>
3 * see README for license and other details. */
9 #include <X11/cursorfont.h>
18 /* Commonly used X information */
26 int have_shape
, shape_event
;
29 int have_randr
, randr_event_base
;
32 /* Standard X protocol atoms */
42 Atom xa_evilwm_unmaximised_horz
;
43 Atom xa_evilwm_unmaximised_vert
;
45 /* Things that affect user interaction */
46 #define CONFIG_FILE ".evilwmrc"
47 static const char *opt_display
= "";
48 static const char *opt_font
= DEF_FONT
;
49 static const char *opt_fg
= DEF_FG
;
50 static const char *opt_bg
= DEF_BG
;
52 static const char *opt_fc
= DEF_FC
;
54 static char *opt_grabmask1
= NULL
;
55 static char *opt_grabmask2
= NULL
;
56 static char *opt_altmask
= NULL
;
57 unsigned int numlockmask
= 0;
58 unsigned int grabmask1
= ControlMask
|Mod1Mask
;
59 unsigned int grabmask2
= Mod1Mask
;
60 unsigned int altmask
= ShiftMask
;
61 const char *opt_term
[3] = { DEF_TERM
, DEF_TERM
, NULL
};
65 int no_solid_drag
= 0; /* use solid drag by default */
67 struct list
*applications
= NULL
;
69 /* Client tracking information */
70 struct list
*clients_tab_order
= NULL
;
71 struct list
*clients_mapping_order
= NULL
;
72 struct list
*clients_stacking_order
= NULL
;
73 Client
*current
= NULL
;
74 volatile Window initialising
= None
;
76 /* Event loop will run until this flag is set */
79 static void set_app(const char *arg
);
80 static void set_app_geometry(const char *arg
);
81 static void set_app_dock(void);
83 static void set_app_vdesk(const char *arg
);
84 static void set_app_fixed(void);
87 static struct xconfig_option evilwm_options
[] = {
88 { XCONFIG_STRING
, "fn", &opt_font
},
89 { XCONFIG_STRING
, "display", &opt_display
},
90 { XCONFIG_STRING
, "fg", &opt_fg
},
91 { XCONFIG_STRING
, "bg", &opt_bg
},
93 { XCONFIG_STRING
, "fc", &opt_fc
},
95 { XCONFIG_INT
, "bw", &opt_bw
},
96 { XCONFIG_STRING
, "term", &opt_term
[0] },
97 { XCONFIG_INT
, "snap", &opt_snap
},
98 { XCONFIG_STRING
, "mask1", &opt_grabmask1
},
99 { XCONFIG_STRING
, "mask2", &opt_grabmask2
},
100 { XCONFIG_STRING
, "altmask", &opt_altmask
},
101 { XCONFIG_CALL_1
, "app", &set_app
},
102 { XCONFIG_CALL_1
, "geometry", &set_app_geometry
},
103 { XCONFIG_CALL_1
, "g", &set_app_geometry
},
104 { XCONFIG_CALL_0
, "dock", &set_app_dock
},
106 { XCONFIG_CALL_1
, "vdesk", &set_app_vdesk
},
107 { XCONFIG_CALL_1
, "v", &set_app_vdesk
},
108 { XCONFIG_CALL_0
, "fixed", &set_app_fixed
},
109 { XCONFIG_CALL_0
, "f", &set_app_fixed
},
110 { XCONFIG_CALL_0
, "s", &set_app_fixed
},
113 { XCONFIG_BOOL
, "nosoliddrag", &no_solid_drag
},
115 { XCONFIG_END
, NULL
, NULL
}
118 static void setup_display(void);
119 static void *xmalloc(size_t size
);
120 static unsigned int parse_modifiers(char *s
);
123 static void helptext(void) {
125 "usage: evilwm [-display display] [-term termprog] [-fn fontname]\n"
130 " [-bg background] [-bw borderwidth]\n"
131 " [-mask1 modifiers] [-mask2 modifiers] [-altmask modifiers]\n"
133 " [-app name/class] [-g geometry] [-dock]\n"
147 int main(int argc
, char *argv
[]) {
148 struct sigaction act
;
152 const char *home
= getenv("HOME");
154 char *conffile
= xmalloc(strlen(home
) + sizeof(CONFIG_FILE
) + 2);
155 strcpy(conffile
, home
);
156 strcat(conffile
, "/" CONFIG_FILE
);
157 xconfig_parse_file(evilwm_options
, conffile
);
161 ret
= xconfig_parse_cli(evilwm_options
, argc
, argv
, &argn
);
162 if (ret
== XCONFIG_MISSING_ARG
) {
163 fprintf(stderr
, "%s: missing argument to `%s'\n", argv
[0], argv
[argn
]);
165 } else if (ret
== XCONFIG_BAD_OPTION
) {
166 if (0 == strcmp(argv
[argn
], "-h")
167 || 0 == strcmp(argv
[argn
], "--help")) {
171 } else if (0 == strcmp(argv
[argn
], "-V")
172 || 0 == strcmp(argv
[argn
], "--version")) {
173 LOG_INFO("evilwm version " VERSION
"\n");
182 opt_term
[1] = opt_term
[0];
183 if (opt_grabmask1
) grabmask1
= parse_modifiers(opt_grabmask1
);
184 if (opt_grabmask2
) grabmask2
= parse_modifiers(opt_grabmask2
);
185 if (opt_altmask
) altmask
= parse_modifiers(opt_altmask
);
188 act
.sa_handler
= handle_signal
;
189 sigemptyset(&act
.sa_mask
);
191 sigaction(SIGTERM
, &act
, NULL
);
192 sigaction(SIGINT
, &act
, NULL
);
193 sigaction(SIGHUP
, &act
, NULL
);
200 while (clients_stacking_order
)
201 remove_client(clients_stacking_order
->data
);
202 XSetInputFocus(dpy
, PointerRoot
, RevertToPointerRoot
, CurrentTime
);
203 if (font
) XFreeFont(dpy
, font
);
206 for (i
= 0; i
< num_screens
; i
++) {
207 ewmh_deinit_screen(&screens
[i
]);
208 XFreeGC(dpy
, screens
[i
].invert_gc
);
209 XInstallColormap(dpy
, DefaultColormap(dpy
, i
));
218 static void *xmalloc(size_t size
) {
219 void *ptr
= malloc(size
);
221 /* C99 defines the 'z' printf modifier for variables of
222 * type size_t. Fall back to casting to unsigned long. */
223 #if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
224 LOG_ERROR("out of memory, looking for %zu bytes\n", size
);
226 LOG_ERROR("out of memory, looking for %lu bytes\n",
227 (unsigned long)size
);
234 static void setup_display(void) {
236 XSetWindowAttributes attr
;
238 XModifierKeymap
*modmap
;
239 /* used in scanning windows (XQueryTree) */
240 unsigned int i
, j
, nwins
;
241 Window dw1
, dw2
, *wins
;
242 XWindowAttributes winattr
;
244 LOG_ENTER("setup_display()");
246 dpy
= XOpenDisplay(opt_display
);
248 LOG_ERROR("can't open display %s\n", opt_display
);
251 XSetErrorHandler(handle_xerror
);
252 /* XSynchronize(dpy, True); */
254 /* Standard X protocol atoms */
255 xa_wm_state
= XInternAtom(dpy
, "WM_STATE", False
);
256 xa_wm_protos
= XInternAtom(dpy
, "WM_PROTOCOLS", False
);
257 xa_wm_delete
= XInternAtom(dpy
, "WM_DELETE_WINDOW", False
);
258 xa_wm_cmapwins
= XInternAtom(dpy
, "WM_COLORMAP_WINDOWS", False
);
260 mwm_hints
= XInternAtom(dpy
, _XA_MWM_HINTS
, False
);
262 xa_evilwm_unmaximised_horz
= XInternAtom(dpy
, "_EVILWM_UNMAXIMISED_HORZ", False
);
263 xa_evilwm_unmaximised_vert
= XInternAtom(dpy
, "_EVILWM_UNMAXIMISED_VERT", False
);
267 font
= XLoadQueryFont(dpy
, opt_font
);
268 if (!font
) font
= XLoadQueryFont(dpy
, DEF_FONT
);
270 LOG_ERROR("couldn't find a font to use: try starting with -fn fontname\n");
274 move_curs
= XCreateFontCursor(dpy
, XC_fleur
);
275 resize_curs
= XCreateFontCursor(dpy
, XC_plus
);
277 /* find out which modifier is NumLock - we'll use this when grabbing
278 * every combination of modifiers we can think of */
279 modmap
= XGetModifierMapping(dpy
);
280 for (i
= 0; i
< 8; i
++) {
281 for (j
= 0; j
< (unsigned int)modmap
->max_keypermod
; j
++) {
282 if (modmap
->modifiermap
[i
*modmap
->max_keypermod
+j
] == XKeysymToKeycode(dpy
, XK_Num_Lock
)) {
283 numlockmask
= (1<<i
);
284 LOG_DEBUG("XK_Num_Lock is (1<<0x%02x)\n", i
);
288 XFreeModifiermap(modmap
);
290 /* set up GC parameters - same for each screen */
291 gv
.function
= GXinvert
;
292 gv
.subwindow_mode
= IncludeInferiors
;
293 gv
.line_width
= 1; /* opt_bw */
296 /* set up root window attributes - same for each screen */
297 attr
.event_mask
= ChildMask
| EnterWindowMask
| ColormapChangeMask
;
299 /* SHAPE extension? */
303 have_shape
= XShapeQueryExtension(dpy
, &shape_event
, &e_dummy
);
306 /* Xrandr extension? */
310 have_randr
= XRRQueryExtension(dpy
, &randr_event_base
, &e_dummy
);
312 LOG_DEBUG("XRandR is not supported on this display.\n");
317 /* now set up each screen in turn */
318 num_screens
= ScreenCount(dpy
);
319 if (num_screens
< 0) {
320 LOG_ERROR("Can't count screens\n");
323 screens
= xmalloc(num_screens
* sizeof(ScreenInfo
));
324 for (i
= 0; i
< (unsigned int)num_screens
; i
++) {
325 char *ds
, *colon
, *dot
;
326 ds
= DisplayString(dpy
);
327 /* set up DISPLAY environment variable to use */
328 colon
= strrchr(ds
, ':');
329 if (colon
&& num_screens
> 1) {
330 screens
[i
].display
= xmalloc(14 + strlen(ds
));
331 strcpy(screens
[i
].display
, "DISPLAY=");
332 strcat(screens
[i
].display
, ds
);
333 colon
= strrchr(screens
[i
].display
, ':');
334 dot
= strchr(colon
, '.');
336 dot
= colon
+ strlen(colon
);
337 snprintf(dot
, 5, ".%d", i
);
339 screens
[i
].display
= NULL
;
341 screens
[i
].screen
= i
;
342 screens
[i
].root
= RootWindow(dpy
, i
);
345 XRRSelectInput(dpy
, screens
[i
].root
, RRScreenChangeNotifyMask
);
349 screens
[i
].vdesk
= KEY_TO_VDESK(XK_1
);
352 XAllocNamedColor(dpy
, DefaultColormap(dpy
, i
), opt_fg
, &screens
[i
].fg
, &dummy
);
353 XAllocNamedColor(dpy
, DefaultColormap(dpy
, i
), opt_bg
, &screens
[i
].bg
, &dummy
);
355 XAllocNamedColor(dpy
, DefaultColormap(dpy
, i
), opt_fc
, &screens
[i
].fc
, &dummy
);
358 screens
[i
].invert_gc
= XCreateGC(dpy
, screens
[i
].root
, GCFunction
| GCSubwindowMode
| GCLineWidth
| GCFont
, &gv
);
360 XChangeWindowAttributes(dpy
, screens
[i
].root
, CWEventMask
, &attr
);
361 grab_keys_for_screen(&screens
[i
]);
362 screens
[i
].docks_visible
= 1;
364 /* scan all the windows on this screen */
365 LOG_XENTER("XQueryTree(screen=%d)", i
);
366 XQueryTree(dpy
, screens
[i
].root
, &dw1
, &dw2
, &wins
, &nwins
);
367 LOG_XDEBUG("%d windows\n", nwins
);
369 for (j
= 0; j
< nwins
; j
++) {
370 XGetWindowAttributes(dpy
, wins
[j
], &winattr
);
371 if (!winattr
.override_redirect
&& winattr
.map_state
== IsViewable
)
372 make_new_client(wins
[j
], &screens
[i
]);
375 ewmh_init_screen(&screens
[i
]);
377 ewmh_set_net_active_window(NULL
);
381 /**************************************************************************/
382 /* Option parsing callbacks */
384 static void set_app(const char *arg
) {
385 Application
*new = xmalloc(sizeof(Application
));
387 new->res_name
= new->res_class
= NULL
;
388 new->geometry_mask
= 0;
391 new->vdesk
= VDESK_NONE
;
393 if ((tmp
= strchr(arg
, '/'))) {
396 if (strlen(arg
) > 0) {
397 new->res_name
= xmalloc(strlen(arg
)+1);
398 strcpy(new->res_name
, arg
);
400 if (tmp
&& strlen(tmp
) > 0) {
401 new->res_class
= xmalloc(strlen(tmp
)+1);
402 strcpy(new->res_class
, tmp
);
404 applications
= list_prepend(applications
, new);
407 static void set_app_geometry(const char *arg
) {
409 Application
*app
= applications
->data
;
410 app
->geometry_mask
= XParseGeometry(arg
,
411 &app
->x
, &app
->y
, &app
->width
, &app
->height
);
415 static void set_app_dock(void) {
417 Application
*app
= applications
->data
;
423 static void set_app_vdesk(const char *arg
) {
424 unsigned int v
= atoi(arg
);
425 if (applications
&& valid_vdesk(v
)) {
426 Application
*app
= applications
->data
;
431 static void set_app_fixed(void) {
433 Application
*app
= applications
->data
;
434 app
->vdesk
= VDESK_FIXED
;
439 /* Used for overriding the default WM modifiers */
440 static unsigned int parse_modifiers(char *s
) {
445 { "shift", ShiftMask
},
446 { "lock", LockMask
},
447 { "control", ControlMask
},
449 { "mod1", Mod1Mask
},
450 { "mod2", Mod2Mask
},
451 { "mod3", Mod3Mask
},
452 { "mod4", Mod4Mask
},
455 char *tmp
= strtok(s
, ",+");
456 unsigned int ret
= 0;
461 for (i
= 0; i
< 9; i
++) {
462 if (!strcmp(modifiers
[i
].name
, tmp
))
463 ret
|= modifiers
[i
].mask
;
465 tmp
= strtok(NULL
, ",+");