| 1 | /* evilwm - Minimalist Window Manager for X |
|---|
| 2 | * Copyright (C) 1999-2006 Ciaran Anscomb <evilwm@6809.org.uk> |
|---|
| 3 | * see README for license and other details. */ |
|---|
| 4 | |
|---|
| 5 | #include <stdlib.h> |
|---|
| 6 | #include <stdio.h> |
|---|
| 7 | #include <string.h> |
|---|
| 8 | #include <signal.h> |
|---|
| 9 | #include <X11/cursorfont.h> |
|---|
| 10 | #include "evilwm.h" |
|---|
| 11 | #include "log.h" |
|---|
| 12 | |
|---|
| 13 | /* Commonly used X information */ |
|---|
| 14 | Display *dpy; |
|---|
| 15 | XFontStruct *font; |
|---|
| 16 | Cursor move_curs; |
|---|
| 17 | Cursor resize_curs; |
|---|
| 18 | int num_screens; |
|---|
| 19 | ScreenInfo *screens; |
|---|
| 20 | #ifdef SHAPE |
|---|
| 21 | int have_shape, shape_event; |
|---|
| 22 | #endif |
|---|
| 23 | |
|---|
| 24 | /* Standard X protocol atoms */ |
|---|
| 25 | Atom xa_wm_state; |
|---|
| 26 | Atom xa_wm_protos; |
|---|
| 27 | Atom xa_wm_delete; |
|---|
| 28 | Atom xa_wm_cmapwins; |
|---|
| 29 | /* Motif atoms */ |
|---|
| 30 | Atom mwm_hints; |
|---|
| 31 | /* EWMH atoms */ |
|---|
| 32 | #ifdef VWM |
|---|
| 33 | Atom xa_net_wm_desktop; |
|---|
| 34 | Atom xa_net_wm_state; |
|---|
| 35 | Atom xa_net_wm_state_sticky; |
|---|
| 36 | #endif |
|---|
| 37 | |
|---|
| 38 | /* Things that affect user interaction */ |
|---|
| 39 | static const char *opt_display = ""; |
|---|
| 40 | static const char *opt_font = DEF_FONT; |
|---|
| 41 | static const char *opt_fg = DEF_FG; |
|---|
| 42 | static const char *opt_bg = DEF_BG; |
|---|
| 43 | #ifdef VWM |
|---|
| 44 | static const char *opt_fc = DEF_FC; |
|---|
| 45 | #endif |
|---|
| 46 | unsigned int numlockmask = 0; |
|---|
| 47 | unsigned int grabmask1 = ControlMask|Mod1Mask; |
|---|
| 48 | unsigned int grabmask2 = Mod1Mask; |
|---|
| 49 | unsigned int altmask = ShiftMask; |
|---|
| 50 | const char *opt_term[3] = { DEF_TERM, DEF_TERM, NULL }; |
|---|
| 51 | int opt_bw = DEF_BW; |
|---|
| 52 | #ifdef SNAP |
|---|
| 53 | int opt_snap = 0; |
|---|
| 54 | #endif |
|---|
| 55 | #ifdef SOLIDDRAG |
|---|
| 56 | int solid_drag = 1; /* use solid drag by default */ |
|---|
| 57 | #endif |
|---|
| 58 | Application *head_app = NULL; |
|---|
| 59 | |
|---|
| 60 | /* Client tracking information */ |
|---|
| 61 | Client *head_client = NULL; |
|---|
| 62 | Client *current = NULL; |
|---|
| 63 | volatile Window initialising = None; |
|---|
| 64 | |
|---|
| 65 | static void setup_display(void); |
|---|
| 66 | static void *xmalloc(size_t size); |
|---|
| 67 | static unsigned int parse_modifiers(char *s); |
|---|
| 68 | |
|---|
| 69 | int wm_main(int argc, char **argv) { |
|---|
| 70 | struct sigaction act; |
|---|
| 71 | int i; |
|---|
| 72 | |
|---|
| 73 | for (i = 1; i < argc; i++) { |
|---|
| 74 | if (!strcmp(argv[i], "-fn") && i+1<argc) |
|---|
| 75 | opt_font = argv[++i]; |
|---|
| 76 | else if (!strcmp(argv[i], "-display") && i+1<argc) { |
|---|
| 77 | opt_display = argv[++i]; |
|---|
| 78 | } |
|---|
| 79 | else if (!strcmp(argv[i], "-fg") && i+1<argc) |
|---|
| 80 | opt_fg = argv[++i]; |
|---|
| 81 | else if (!strcmp(argv[i], "-bg") && i+1<argc) |
|---|
| 82 | opt_bg = argv[++i]; |
|---|
| 83 | #ifdef VWM |
|---|
| 84 | else if (!strcmp(argv[i], "-fc") && i+1<argc) |
|---|
| 85 | opt_fc = argv[++i]; |
|---|
| 86 | #endif |
|---|
| 87 | else if (!strcmp(argv[i], "-bw") && i+1<argc) |
|---|
| 88 | opt_bw = atoi(argv[++i]); |
|---|
| 89 | else if (!strcmp(argv[i], "-term") && i+1<argc) { |
|---|
| 90 | opt_term[0] = argv[++i]; |
|---|
| 91 | opt_term[1] = opt_term[0]; |
|---|
| 92 | #ifdef SNAP |
|---|
| 93 | } else if (!strcmp(argv[i], "-snap") && i+1<argc) { |
|---|
| 94 | opt_snap = atoi(argv[++i]); |
|---|
| 95 | #endif |
|---|
| 96 | } else if (!strcmp(argv[i], "-app") && i+1<argc) { |
|---|
| 97 | Application *new = xmalloc(sizeof(Application)); |
|---|
| 98 | char *tmp; |
|---|
| 99 | i++; |
|---|
| 100 | new->res_name = new->res_class = NULL; |
|---|
| 101 | new->geometry_mask = 0; |
|---|
| 102 | #ifdef VWM |
|---|
| 103 | new->vdesk = -1; |
|---|
| 104 | new->sticky = 0; |
|---|
| 105 | #endif |
|---|
| 106 | if ((tmp = strchr(argv[i], '/'))) { |
|---|
| 107 | *(tmp++) = 0; |
|---|
| 108 | } |
|---|
| 109 | if (strlen(argv[i]) > 0) { |
|---|
| 110 | new->res_name = xmalloc(strlen(argv[i])+1); |
|---|
| 111 | strcpy(new->res_name, argv[i]); |
|---|
| 112 | } |
|---|
| 113 | if (tmp && strlen(tmp) > 0) { |
|---|
| 114 | new->res_class = xmalloc(strlen(tmp)+1); |
|---|
| 115 | strcpy(new->res_class, tmp); |
|---|
| 116 | } |
|---|
| 117 | new->next = head_app; |
|---|
| 118 | head_app = new; |
|---|
| 119 | } else if (!strcmp(argv[i], "-g") && i+1<argc) { |
|---|
| 120 | i++; |
|---|
| 121 | if (!head_app) |
|---|
| 122 | continue; |
|---|
| 123 | head_app->geometry_mask = XParseGeometry(argv[i], |
|---|
| 124 | &head_app->x, &head_app->y, |
|---|
| 125 | &head_app->width, &head_app->height); |
|---|
| 126 | #ifdef VWM |
|---|
| 127 | } else if (!strcmp(argv[i], "-v") && i+1<argc) { |
|---|
| 128 | int v = atoi(argv[++i]); |
|---|
| 129 | if (head_app && valid_vdesk(v)) |
|---|
| 130 | head_app->vdesk = v; |
|---|
| 131 | } else if (!strcmp(argv[i], "-s")) { |
|---|
| 132 | if (head_app) |
|---|
| 133 | head_app->sticky = 1; |
|---|
| 134 | #endif |
|---|
| 135 | } else if (!strcmp(argv[i], "-mask1") && i+1<argc) { |
|---|
| 136 | i++; |
|---|
| 137 | grabmask1 = parse_modifiers(argv[i]); |
|---|
| 138 | } else if (!strcmp(argv[i], "-mask2") && i+1<argc) { |
|---|
| 139 | i++; |
|---|
| 140 | grabmask2 = parse_modifiers(argv[i]); |
|---|
| 141 | } else if (!strcmp(argv[i], "-altmask") && i+1<argc) { |
|---|
| 142 | i++; |
|---|
| 143 | altmask = parse_modifiers(argv[i]); |
|---|
| 144 | #ifdef SOLIDDRAG |
|---|
| 145 | } else if (!strcmp(argv[i], "-nosoliddrag")) { |
|---|
| 146 | solid_drag = 0; |
|---|
| 147 | #endif |
|---|
| 148 | #ifdef STDIO |
|---|
| 149 | } else if (!strcmp(argv[i], "-V")) { |
|---|
| 150 | LOG_INFO("evilwm version " VERSION "\n"); |
|---|
| 151 | exit(0); |
|---|
| 152 | #endif |
|---|
| 153 | } else { |
|---|
| 154 | LOG_INFO("usage: evilwm [-display display] [-term termprog] [-fn fontname]\n"); |
|---|
| 155 | LOG_INFO(" [-fg foreground]"); |
|---|
| 156 | #ifdef VWM |
|---|
| 157 | LOG_INFO(" [-fc fixed]"); |
|---|
| 158 | #endif |
|---|
| 159 | LOG_INFO(" [-bg background] [-bw borderwidth]\n"); |
|---|
| 160 | LOG_INFO(" [-mask1 modifiers] [-mask2 modifiers] [-altmask modifiers]\n"); |
|---|
| 161 | LOG_INFO(" [-snap num]"); |
|---|
| 162 | #ifdef VWM |
|---|
| 163 | LOG_INFO(" [-app name/class] [-g geometry] [-v vdesk] [-s]"); |
|---|
| 164 | #endif |
|---|
| 165 | #ifdef SOLIDDRAG |
|---|
| 166 | LOG_INFO("\n [-nosoliddrag]"); |
|---|
| 167 | #endif |
|---|
| 168 | LOG_INFO(" [-V]\n"); |
|---|
| 169 | exit((!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help"))?0:1); |
|---|
| 170 | } |
|---|
| 171 | } |
|---|
| 172 | |
|---|
| 173 | act.sa_handler = handle_signal; |
|---|
| 174 | sigemptyset(&act.sa_mask); |
|---|
| 175 | act.sa_flags = 0; |
|---|
| 176 | sigaction(SIGTERM, &act, NULL); |
|---|
| 177 | sigaction(SIGINT, &act, NULL); |
|---|
| 178 | sigaction(SIGHUP, &act, NULL); |
|---|
| 179 | |
|---|
| 180 | setup_display(); |
|---|
| 181 | |
|---|
| 182 | event_main_loop(); |
|---|
| 183 | |
|---|
| 184 | return 1; |
|---|
| 185 | } |
|---|
| 186 | |
|---|
| 187 | static void *xmalloc(size_t size) { |
|---|
| 188 | void *ptr = malloc(size); |
|---|
| 189 | if (!ptr) { |
|---|
| 190 | /* C99 defines the 'z' printf modifier for variables of |
|---|
| 191 | * type size_t. Fall back to casting to unsigned long. */ |
|---|
| 192 | #if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L |
|---|
| 193 | LOG_ERROR("out of memory, looking for %zu bytes\n", size); |
|---|
| 194 | #else |
|---|
| 195 | LOG_ERROR("out of memory, looking for %lu bytes\n", |
|---|
| 196 | (unsigned long)size); |
|---|
| 197 | #endif |
|---|
| 198 | exit(1); |
|---|
| 199 | } |
|---|
| 200 | return ptr; |
|---|
| 201 | } |
|---|
| 202 | |
|---|
| 203 | static void setup_display(void) { |
|---|
| 204 | XGCValues gv; |
|---|
| 205 | XSetWindowAttributes attr; |
|---|
| 206 | XColor dummy; |
|---|
| 207 | XModifierKeymap *modmap; |
|---|
| 208 | /* used in scanning windows (XQueryTree) */ |
|---|
| 209 | unsigned int i, j, nwins; |
|---|
| 210 | Window dw1, dw2, *wins; |
|---|
| 211 | XWindowAttributes winattr; |
|---|
| 212 | |
|---|
| 213 | XInitThreads(); //todo ... |
|---|
| 214 | |
|---|
| 215 | dpy = XOpenDisplay(opt_display); |
|---|
| 216 | if (!dpy) { |
|---|
| 217 | LOG_ERROR("can't open display %s\n", opt_display); |
|---|
| 218 | exit(1); |
|---|
| 219 | } |
|---|
| 220 | XSetErrorHandler(handle_xerror); |
|---|
| 221 | /* XSynchronize(dpy, True); */ |
|---|
| 222 | |
|---|
| 223 | /* Standard X protocol atoms */ |
|---|
| 224 | xa_wm_state = XInternAtom(dpy, "WM_STATE", False); |
|---|
| 225 | xa_wm_protos = XInternAtom(dpy, "WM_PROTOCOLS", False); |
|---|
| 226 | xa_wm_delete = XInternAtom(dpy, "WM_DELETE_WINDOW", False); |
|---|
| 227 | #ifdef COLOURMAP |
|---|
| 228 | xa_wm_cmapwins = XInternAtom(dpy, "WM_COLORMAP_WINDOWS", False); |
|---|
| 229 | #endif |
|---|
| 230 | /* Motif atoms */ |
|---|
| 231 | mwm_hints = XInternAtom(dpy, _XA_MWM_HINTS, False); |
|---|
| 232 | /* EWMH atoms */ |
|---|
| 233 | #ifdef VWM |
|---|
| 234 | xa_net_wm_desktop = XInternAtom(dpy, "_NET_WM_DESKTOP", False); |
|---|
| 235 | xa_net_wm_state = XInternAtom(dpy, "_NET_WM_STATE", False); |
|---|
| 236 | xa_net_wm_state_sticky = XInternAtom(dpy, "_NET_WM_STATE_STICKY", False); |
|---|
| 237 | #endif |
|---|
| 238 | |
|---|
| 239 | font = XLoadQueryFont(dpy, opt_font); |
|---|
| 240 | if (!font) font = XLoadQueryFont(dpy, DEF_FONT); |
|---|
| 241 | if (!font) { |
|---|
| 242 | LOG_ERROR("couldn't find a font to use: try starting with -fn fontname\n"); |
|---|
| 243 | exit(1); |
|---|
| 244 | } |
|---|
| 245 | |
|---|
| 246 | move_curs = XCreateFontCursor(dpy, XC_fleur); |
|---|
| 247 | resize_curs = XCreateFontCursor(dpy, XC_plus); |
|---|
| 248 | |
|---|
| 249 | /* find out which modifier is NumLock - we'll use this when grabbing |
|---|
| 250 | * every combination of modifiers we can think of */ |
|---|
| 251 | modmap = XGetModifierMapping(dpy); |
|---|
| 252 | for (i = 0; i < 8; i++) { |
|---|
| 253 | for (j = 0; j < (unsigned int)modmap->max_keypermod; j++) { |
|---|
| 254 | if (modmap->modifiermap[i*modmap->max_keypermod+j] == XKeysymToKeycode(dpy, XK_Num_Lock)) { |
|---|
| 255 | numlockmask = (1<<i); |
|---|
| 256 | LOG_DEBUG("setup_display() : XK_Num_Lock is (1<<0x%02x)\n", i); |
|---|
| 257 | } |
|---|
| 258 | } |
|---|
| 259 | } |
|---|
| 260 | XFreeModifiermap(modmap); |
|---|
| 261 | |
|---|
| 262 | /* set up GC parameters - same for each screen */ |
|---|
| 263 | gv.function = GXinvert; |
|---|
| 264 | gv.subwindow_mode = IncludeInferiors; |
|---|
| 265 | gv.line_width = 1; /* opt_bw */ |
|---|
| 266 | gv.font = font->fid; |
|---|
| 267 | |
|---|
| 268 | /* set up root window attributes - same for each screen */ |
|---|
| 269 | #ifdef COLOURMAP |
|---|
| 270 | attr.event_mask = ChildMask | EnterWindowMask | ColormapChangeMask; |
|---|
| 271 | #else |
|---|
| 272 | attr.event_mask = ChildMask | EnterWindowMask; |
|---|
| 273 | #endif |
|---|
| 274 | |
|---|
| 275 | /* SHAPE extension? */ |
|---|
| 276 | #ifdef SHAPE |
|---|
| 277 | { |
|---|
| 278 | int e_dummy; |
|---|
| 279 | have_shape = XShapeQueryExtension(dpy, &shape_event, &e_dummy); |
|---|
| 280 | } |
|---|
| 281 | #endif |
|---|
| 282 | |
|---|
| 283 | /* now set up each screen in turn */ |
|---|
| 284 | num_screens = ScreenCount(dpy); |
|---|
| 285 | if (num_screens < 0) { |
|---|
| 286 | LOG_ERROR("Can't count screens\n"); |
|---|
| 287 | exit(1); |
|---|
| 288 | } |
|---|
| 289 | screens = xmalloc(num_screens * sizeof(ScreenInfo)); |
|---|
| 290 | for (i = 0; i < (unsigned int)num_screens; i++) { |
|---|
| 291 | char *ds, *colon, *dot; |
|---|
| 292 | ds = DisplayString(dpy); |
|---|
| 293 | /* set up DISPLAY environment variable to use */ |
|---|
| 294 | colon = strrchr(ds, ':'); |
|---|
| 295 | if (colon && num_screens > 1) { |
|---|
| 296 | screens[i].display = xmalloc(14 + strlen(ds)); |
|---|
| 297 | strcpy(screens[i].display, "DISPLAY="); |
|---|
| 298 | strcat(screens[i].display, ds); |
|---|
| 299 | colon = strrchr(screens[i].display, ':'); |
|---|
| 300 | dot = strchr(colon, '.'); |
|---|
| 301 | if (!dot) |
|---|
| 302 | dot = colon + strlen(colon); |
|---|
| 303 | snprintf(dot, 5, ".%d", i); |
|---|
| 304 | } else |
|---|
| 305 | screens[i].display = NULL; |
|---|
| 306 | |
|---|
| 307 | screens[i].screen = i; |
|---|
| 308 | screens[i].root = RootWindow(dpy, i); |
|---|
| 309 | #ifdef VWM |
|---|
| 310 | screens[i].vdesk = KEY_TO_VDESK(XK_1); |
|---|
| 311 | #endif |
|---|
| 312 | |
|---|
| 313 | XAllocNamedColor(dpy, DefaultColormap(dpy, i), opt_fg, &screens[i].fg, &dummy); |
|---|
| 314 | XAllocNamedColor(dpy, DefaultColormap(dpy, i), opt_bg, &screens[i].bg, &dummy); |
|---|
| 315 | #ifdef VWM |
|---|
| 316 | XAllocNamedColor(dpy, DefaultColormap(dpy, i), opt_fc, &screens[i].fc, &dummy); |
|---|
| 317 | #endif |
|---|
| 318 | |
|---|
| 319 | screens[i].invert_gc = XCreateGC(dpy, screens[i].root, GCFunction | GCSubwindowMode | GCLineWidth | GCFont, &gv); |
|---|
| 320 | |
|---|
| 321 | XChangeWindowAttributes(dpy, screens[i].root, CWEventMask, &attr); |
|---|
| 322 | grab_keys_for_screen(&screens[i]); |
|---|
| 323 | |
|---|
| 324 | /* scan all the windows on this screen */ |
|---|
| 325 | LOG_XDEBUG("main:XQueryTree(); "); |
|---|
| 326 | XQueryTree(dpy, screens[i].root, &dw1, &dw2, &wins, &nwins); |
|---|
| 327 | LOG_XDEBUG("%d windows\n", nwins); |
|---|
| 328 | for (j = 0; j < nwins; j++) { |
|---|
| 329 | XGetWindowAttributes(dpy, wins[j], &winattr); |
|---|
| 330 | if (!winattr.override_redirect && winattr.map_state == IsViewable) |
|---|
| 331 | make_new_client(wins[j], &screens[i]); |
|---|
| 332 | } |
|---|
| 333 | XFree(wins); |
|---|
| 334 | } |
|---|
| 335 | } |
|---|
| 336 | |
|---|
| 337 | /* Used for overriding the default WM modifiers */ |
|---|
| 338 | static unsigned int parse_modifiers(char *s) { |
|---|
| 339 | static struct { |
|---|
| 340 | const char *name; |
|---|
| 341 | unsigned int mask; |
|---|
| 342 | } modifiers[9] = { |
|---|
| 343 | { "shift", ShiftMask }, |
|---|
| 344 | { "lock", LockMask }, |
|---|
| 345 | { "control", ControlMask }, |
|---|
| 346 | { "alt", Mod1Mask }, |
|---|
| 347 | { "mod1", Mod1Mask }, |
|---|
| 348 | { "mod2", Mod2Mask }, |
|---|
| 349 | { "mod3", Mod3Mask }, |
|---|
| 350 | { "mod4", Mod4Mask }, |
|---|
| 351 | { "mod5", Mod5Mask } |
|---|
| 352 | }; |
|---|
| 353 | char *tmp = strtok(s, ",+"); |
|---|
| 354 | unsigned int ret = 0; |
|---|
| 355 | int i; |
|---|
| 356 | if (!tmp) |
|---|
| 357 | return 0; |
|---|
| 358 | do { |
|---|
| 359 | for (i = 0; i < 9; i++) { |
|---|
| 360 | if (!strcmp(modifiers[i].name, tmp)) |
|---|
| 361 | ret |= modifiers[i].mask; |
|---|
| 362 | } |
|---|
| 363 | tmp = strtok(NULL, ",+"); |
|---|
| 364 | } while (tmp); |
|---|
| 365 | return ret; |
|---|
| 366 | } |
|---|