| 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 <sys/select.h> |
|---|
| 7 | //#include <X11/Intrinsic.h> |
|---|
| 8 | #include "evilwm.h" |
|---|
| 9 | #include "log.h" |
|---|
| 10 | |
|---|
| 11 | static void current_to_head(void) { |
|---|
| 12 | Client *c; |
|---|
| 13 | if (current && current != head_client) { |
|---|
| 14 | for (c = head_client; c; c = c->next) { |
|---|
| 15 | if (c->next == current) { |
|---|
| 16 | c->next = current->next; |
|---|
| 17 | current->next = head_client; |
|---|
| 18 | head_client = current; |
|---|
| 19 | break; |
|---|
| 20 | } |
|---|
| 21 | } |
|---|
| 22 | } |
|---|
| 23 | } |
|---|
| 24 | |
|---|
| 25 | |
|---|
| 26 | PyObject *key_hook = NULL; |
|---|
| 27 | PyObject* set_key_hook(PyObject *_, PyObject* pyargs) |
|---|
| 28 | { |
|---|
| 29 | if(!PyTuple_Check(pyargs)) return PyExc_TypeError; |
|---|
| 30 | if(PyTuple_Size(pyargs) != 1) return PyExc_TypeError; |
|---|
| 31 | |
|---|
| 32 | PyObject* hook = PyTuple_GetItem(pyargs, 0); |
|---|
| 33 | PyObject *ret = key_hook; |
|---|
| 34 | if(!PyCallable_Check(hook)) return PyExc_TypeError; |
|---|
| 35 | |
|---|
| 36 | Py_INCREF(hook); |
|---|
| 37 | key_hook = hook; |
|---|
| 38 | |
|---|
| 39 | //run_post_geometry_hook(NULL); |
|---|
| 40 | if(ret) return ret; |
|---|
| 41 | Py_RETURN_NONE; |
|---|
| 42 | } |
|---|
| 43 | |
|---|
| 44 | void run_key_hook(int code) |
|---|
| 45 | { |
|---|
| 46 | if(!key_hook) return; |
|---|
| 47 | PyGILState_STATE s = PyGILState_Ensure(); |
|---|
| 48 | PyObject *arg = PyTuple_New(1); |
|---|
| 49 | PyTuple_SetItem(arg, 0, PyLong_FromLong(code)); |
|---|
| 50 | PyObject_CallObject(key_hook, arg); |
|---|
| 51 | Py_DECREF(arg); |
|---|
| 52 | PyGILState_Release(s); |
|---|
| 53 | } |
|---|
| 54 | |
|---|
| 55 | static void handle_key_event2(XKeyEvent *e) |
|---|
| 56 | { |
|---|
| 57 | int i = 0; |
|---|
| 58 | |
|---|
| 59 | KeySym key = XKeycodeToKeysym(dpy,e->keycode,0); |
|---|
| 60 | for(i = 0; i < kbs_len; ++i) |
|---|
| 61 | if(kbs[i] && kbs[i]->key == key && kbs[i]->mask == e->state) { |
|---|
| 62 | PyObject *arg = PyTuple_New(0); |
|---|
| 63 | PyGILState_STATE s = PyGILState_Ensure(); |
|---|
| 64 | PyObject_CallObject(kbs[i]->cb, arg); |
|---|
| 65 | PyGILState_Release(s); |
|---|
| 66 | Py_DECREF(arg); |
|---|
| 67 | } |
|---|
| 68 | } |
|---|
| 69 | |
|---|
| 70 | static void handle_key_event(XKeyEvent *e) { |
|---|
| 71 | KeySym key = XKeycodeToKeysym(dpy,e->keycode,0); |
|---|
| 72 | Client *c; |
|---|
| 73 | int width_inc, height_inc; |
|---|
| 74 | #ifdef VWM |
|---|
| 75 | ScreenInfo *current_screen = find_current_screen(); |
|---|
| 76 | #endif |
|---|
| 77 | |
|---|
| 78 | run_key_hook(key); |
|---|
| 79 | run_key_hook(e->state); |
|---|
| 80 | switch(key) { |
|---|
| 81 | case KEY_NEW: |
|---|
| 82 | spawn(opt_term); |
|---|
| 83 | break; |
|---|
| 84 | case KEY_NEXT: |
|---|
| 85 | next(); |
|---|
| 86 | if (XGrabKeyboard(dpy, e->root, False, GrabModeAsync, GrabModeAsync, CurrentTime) == GrabSuccess) { |
|---|
| 87 | XEvent ev; |
|---|
| 88 | do { |
|---|
| 89 | XMaskEvent(dpy, KeyPressMask|KeyReleaseMask, &ev); |
|---|
| 90 | if (ev.type == KeyPress && XKeycodeToKeysym(dpy,ev.xkey.keycode,0) == KEY_NEXT) |
|---|
| 91 | next(); |
|---|
| 92 | } while (ev.type == KeyPress || XKeycodeToKeysym(dpy,ev.xkey.keycode,0) == KEY_NEXT); |
|---|
| 93 | XUngrabKeyboard(dpy, CurrentTime); |
|---|
| 94 | } |
|---|
| 95 | current_to_head(); |
|---|
| 96 | break; |
|---|
| 97 | #ifdef VWM |
|---|
| 98 | case XK_1: case XK_2: case XK_3: case XK_4: |
|---|
| 99 | case XK_5: case XK_6: case XK_7: case XK_8: |
|---|
| 100 | switch_vdesk(current_screen, KEY_TO_VDESK(key)); |
|---|
| 101 | break; |
|---|
| 102 | case KEY_PREVDESK: |
|---|
| 103 | if (current_screen->vdesk > KEY_TO_VDESK(XK_1)) { |
|---|
| 104 | switch_vdesk(current_screen, |
|---|
| 105 | current_screen->vdesk - 1); |
|---|
| 106 | } |
|---|
| 107 | break; |
|---|
| 108 | case KEY_NEXTDESK: |
|---|
| 109 | if (current_screen->vdesk < KEY_TO_VDESK(XK_8)) { |
|---|
| 110 | switch_vdesk(current_screen, |
|---|
| 111 | current_screen->vdesk + 1); |
|---|
| 112 | } |
|---|
| 113 | break; |
|---|
| 114 | #endif |
|---|
| 115 | default: break; |
|---|
| 116 | } |
|---|
| 117 | c = current; |
|---|
| 118 | if (c == NULL) return; |
|---|
| 119 | width_inc = (c->width_inc > 1) ? c->width_inc : 16; |
|---|
| 120 | height_inc = (c->height_inc > 1) ? c->height_inc : 16; |
|---|
| 121 | switch (key) { |
|---|
| 122 | case KEY_LEFT: |
|---|
| 123 | if (e->state & altmask) { |
|---|
| 124 | if ((c->width - width_inc) >= c->min_width) |
|---|
| 125 | c->width -= width_inc; |
|---|
| 126 | } else { |
|---|
| 127 | c->x -= 16; |
|---|
| 128 | } |
|---|
| 129 | goto move_client; |
|---|
| 130 | case KEY_DOWN: |
|---|
| 131 | if (e->state & altmask) { |
|---|
| 132 | if (!c->max_height || (c->height + height_inc) <= c->max_height) |
|---|
| 133 | c->height += height_inc; |
|---|
| 134 | } else { |
|---|
| 135 | c->y += 16; |
|---|
| 136 | } |
|---|
| 137 | goto move_client; |
|---|
| 138 | case KEY_UP: |
|---|
| 139 | if (e->state & altmask) { |
|---|
| 140 | if ((c->height - height_inc) >= c->min_height) |
|---|
| 141 | c->height -= height_inc; |
|---|
| 142 | } else { |
|---|
| 143 | c->y -= 16; |
|---|
| 144 | } |
|---|
| 145 | goto move_client; |
|---|
| 146 | case KEY_RIGHT: |
|---|
| 147 | if (e->state & altmask) { |
|---|
| 148 | if (!c->max_width || (c->width + width_inc) <= c->max_width) |
|---|
| 149 | c->width += width_inc; |
|---|
| 150 | } else { |
|---|
| 151 | c->x += 16; |
|---|
| 152 | } |
|---|
| 153 | goto move_client; |
|---|
| 154 | case KEY_TOPLEFT: |
|---|
| 155 | c->x = c->border; |
|---|
| 156 | c->y = c->border; |
|---|
| 157 | goto move_client; |
|---|
| 158 | case KEY_TOPRIGHT: |
|---|
| 159 | c->x = DisplayWidth(dpy, c->screen->screen) |
|---|
| 160 | - c->width-c->border; |
|---|
| 161 | c->y = c->border; |
|---|
| 162 | goto move_client; |
|---|
| 163 | case KEY_BOTTOMLEFT: |
|---|
| 164 | c->x = c->border; |
|---|
| 165 | c->y = DisplayHeight(dpy, c->screen->screen) |
|---|
| 166 | - c->height-c->border; |
|---|
| 167 | goto move_client; |
|---|
| 168 | case KEY_BOTTOMRIGHT: |
|---|
| 169 | c->x = DisplayWidth(dpy, c->screen->screen) |
|---|
| 170 | - c->width-c->border; |
|---|
| 171 | c->y = DisplayHeight(dpy, c->screen->screen) |
|---|
| 172 | - c->height-c->border; |
|---|
| 173 | goto move_client; |
|---|
| 174 | case KEY_KILL: |
|---|
| 175 | send_wm_delete(c, e->state & altmask); |
|---|
| 176 | break; |
|---|
| 177 | case KEY_LOWER: case KEY_ALTLOWER: |
|---|
| 178 | XLowerWindow(dpy, c->parent); |
|---|
| 179 | break; |
|---|
| 180 | case KEY_INFO: |
|---|
| 181 | show_info(c, key); |
|---|
| 182 | break; |
|---|
| 183 | case KEY_MAX: |
|---|
| 184 | maximise_client(c, MAXIMISE_HORZ|MAXIMISE_VERT); |
|---|
| 185 | break; |
|---|
| 186 | case KEY_MAXVERT: |
|---|
| 187 | maximise_client(c, MAXIMISE_VERT); |
|---|
| 188 | break; |
|---|
| 189 | #ifdef VWM |
|---|
| 190 | case KEY_FIX: |
|---|
| 191 | fix_client(c); |
|---|
| 192 | break; |
|---|
| 193 | #endif |
|---|
| 194 | default: break; |
|---|
| 195 | } |
|---|
| 196 | return; |
|---|
| 197 | move_client: |
|---|
| 198 | moveresize(c); |
|---|
| 199 | setmouse(c->window, c->width + c->border - 1, |
|---|
| 200 | c->height + c->border - 1); |
|---|
| 201 | discard_enter_events(); |
|---|
| 202 | return; |
|---|
| 203 | } |
|---|
| 204 | |
|---|
| 205 | #ifdef MOUSE |
|---|
| 206 | static void handle_button_event(XButtonEvent *e) { |
|---|
| 207 | Client *c = find_client(e->window); |
|---|
| 208 | |
|---|
| 209 | if (c) { |
|---|
| 210 | switch (e->button) { |
|---|
| 211 | case Button1: |
|---|
| 212 | drag(c); |
|---|
| 213 | run_mouse_button1_hook(c); |
|---|
| 214 | break; |
|---|
| 215 | case Button2: |
|---|
| 216 | sweep(c); |
|---|
| 217 | run_mouse_button2_hook(c); |
|---|
| 218 | break; |
|---|
| 219 | case Button3: |
|---|
| 220 | XLowerWindow(dpy, c->parent); |
|---|
| 221 | run_mouse_button3_hook(c); |
|---|
| 222 | break; |
|---|
| 223 | default: break; |
|---|
| 224 | } |
|---|
| 225 | } |
|---|
| 226 | } |
|---|
| 227 | #endif |
|---|
| 228 | |
|---|
| 229 | static void handle_configure_request(XConfigureRequestEvent *e) { |
|---|
| 230 | Client *c = find_client(e->window); |
|---|
| 231 | XWindowChanges wc; |
|---|
| 232 | unsigned int value_mask = e->value_mask; |
|---|
| 233 | |
|---|
| 234 | wc.sibling = e->above; |
|---|
| 235 | wc.stack_mode = e->detail; |
|---|
| 236 | wc.width = e->width; |
|---|
| 237 | wc.height = e->height; |
|---|
| 238 | if (c) { |
|---|
| 239 | ungravitate(c); |
|---|
| 240 | if (value_mask & CWWidth) c->width = e->width; |
|---|
| 241 | if (value_mask & CWHeight) c->height = e->height; |
|---|
| 242 | if (value_mask & CWX) c->x = e->x; |
|---|
| 243 | if (value_mask & CWY) c->y = e->y; |
|---|
| 244 | if (value_mask & CWStackMode && value_mask & CWSibling) { |
|---|
| 245 | Client *sibling = find_client(e->above); |
|---|
| 246 | if (sibling) { |
|---|
| 247 | wc.sibling = sibling->parent; |
|---|
| 248 | } |
|---|
| 249 | } |
|---|
| 250 | if (c->x == 0 && c->width >= DisplayWidth(dpy, c->screen->screen)) { |
|---|
| 251 | c->x -= c->border; |
|---|
| 252 | } |
|---|
| 253 | if (c->y == 0 && c->height >= DisplayHeight(dpy, c->screen->screen)) { |
|---|
| 254 | c->y -= c->border; |
|---|
| 255 | } |
|---|
| 256 | gravitate(c); |
|---|
| 257 | |
|---|
| 258 | wc.x = c->x - c->border; |
|---|
| 259 | wc.y = c->y - c->border; |
|---|
| 260 | wc.border_width = c->border; |
|---|
| 261 | LOG_XDEBUG("XConfigureWindow(dpy, parent(%x), %lx, &wc);\n", (unsigned int)c->parent, value_mask); |
|---|
| 262 | XConfigureWindow(dpy, c->parent, value_mask, &wc); |
|---|
| 263 | XMoveResizeWindow(dpy, c->window, 0, 0, c->width, c->height); |
|---|
| 264 | if ((value_mask & (CWX|CWY)) && !(value_mask & (CWWidth|CWHeight))) { |
|---|
| 265 | send_config(c); |
|---|
| 266 | } |
|---|
| 267 | wc.border_width = 0; |
|---|
| 268 | } else { |
|---|
| 269 | wc.x = c ? 0 : e->x; |
|---|
| 270 | wc.y = c ? 0 : e->y; |
|---|
| 271 | LOG_XDEBUG("XConfigureWindow(dpy, window(%x), %lx, &wc);\n", (unsigned int)e->window, value_mask); |
|---|
| 272 | XConfigureWindow(dpy, e->window, value_mask, &wc); |
|---|
| 273 | } |
|---|
| 274 | } |
|---|
| 275 | |
|---|
| 276 | static void handle_map_request(XMapRequestEvent *e) { |
|---|
| 277 | Client *c = find_client(e->window); |
|---|
| 278 | |
|---|
| 279 | if (c) { |
|---|
| 280 | #ifdef VWM |
|---|
| 281 | if (c->vdesk != c->screen->vdesk) |
|---|
| 282 | switch_vdesk(c->screen, c->vdesk); |
|---|
| 283 | #endif |
|---|
| 284 | unhide(c, RAISE); |
|---|
| 285 | } else { |
|---|
| 286 | XWindowAttributes attr; |
|---|
| 287 | LOG_DEBUG("handle_map_request() : don't know this window, calling make_new_client();\n"); |
|---|
| 288 | XGetWindowAttributes(dpy, e->window, &attr); |
|---|
| 289 | make_new_client(e->window, find_screen(attr.root)); |
|---|
| 290 | } |
|---|
| 291 | } |
|---|
| 292 | |
|---|
| 293 | static void handle_unmap_event(XUnmapEvent *e) { |
|---|
| 294 | Client *c = find_client(e->window); |
|---|
| 295 | |
|---|
| 296 | LOG_DEBUG("handle_unmap_event(): "); |
|---|
| 297 | if (c) { |
|---|
| 298 | if (c->ignore_unmap) { |
|---|
| 299 | c->ignore_unmap--; |
|---|
| 300 | LOG_DEBUG("ignored (%d ignores remaining)\n", c->ignore_unmap); |
|---|
| 301 | } else { |
|---|
| 302 | LOG_DEBUG("flagging client for removal\n"); |
|---|
| 303 | c->remove = 1; |
|---|
| 304 | need_client_tidy = 1; |
|---|
| 305 | } |
|---|
| 306 | } else { |
|---|
| 307 | LOG_DEBUG("unknown client!\n"); |
|---|
| 308 | } |
|---|
| 309 | } |
|---|
| 310 | |
|---|
| 311 | #ifdef COLOURMAP |
|---|
| 312 | static void handle_colormap_change(XColormapEvent *e) { |
|---|
| 313 | Client *c = find_client(e->window); |
|---|
| 314 | |
|---|
| 315 | if (c && e->new) { |
|---|
| 316 | c->cmap = e->colormap; |
|---|
| 317 | XInstallColormap(dpy, c->cmap); |
|---|
| 318 | } |
|---|
| 319 | } |
|---|
| 320 | #endif |
|---|
| 321 | |
|---|
| 322 | static void handle_property_change(XPropertyEvent *e) { |
|---|
| 323 | Client *c = find_client(e->window); |
|---|
| 324 | |
|---|
| 325 | if (c) { |
|---|
| 326 | if (e->atom == XA_WM_NORMAL_HINTS) { |
|---|
| 327 | get_wm_normal_hints(c); |
|---|
| 328 | } |
|---|
| 329 | } |
|---|
| 330 | } |
|---|
| 331 | |
|---|
| 332 | static void handle_leave_event(XCrossingEvent *e) { |
|---|
| 333 | Client *c; |
|---|
| 334 | |
|---|
| 335 | if ((c = find_client(e->window))) { |
|---|
| 336 | |
|---|
| 337 | #ifdef VWM |
|---|
| 338 | if (c->vdesk != c->screen->vdesk) |
|---|
| 339 | return; |
|---|
| 340 | #endif |
|---|
| 341 | run_mouse_leave_hook(c); |
|---|
| 342 | } |
|---|
| 343 | } |
|---|
| 344 | |
|---|
| 345 | static void handle_enter_event(XCrossingEvent *e) { |
|---|
| 346 | Client *c; |
|---|
| 347 | |
|---|
| 348 | if ((c = find_client(e->window))) { |
|---|
| 349 | |
|---|
| 350 | #ifdef VWM |
|---|
| 351 | if (c->vdesk != c->screen->vdesk) |
|---|
| 352 | return; |
|---|
| 353 | #endif |
|---|
| 354 | run_mouse_enter_hook(c); |
|---|
| 355 | } |
|---|
| 356 | } |
|---|
| 357 | |
|---|
| 358 | static void handle_mappingnotify_event(XMappingEvent *e) { |
|---|
| 359 | XRefreshKeyboardMapping(e); |
|---|
| 360 | if (e->request == MappingKeyboard) { |
|---|
| 361 | int i; |
|---|
| 362 | for (i = 0; i < num_screens; i++) { |
|---|
| 363 | grab_keys_for_screen(&screens[i]); |
|---|
| 364 | } |
|---|
| 365 | } |
|---|
| 366 | } |
|---|
| 367 | |
|---|
| 368 | #ifdef SHAPE |
|---|
| 369 | static void handle_shape_event(XShapeEvent *e) { |
|---|
| 370 | Client *c = find_client(e->window); |
|---|
| 371 | if (c) |
|---|
| 372 | set_shape(c); |
|---|
| 373 | } |
|---|
| 374 | #endif |
|---|
| 375 | |
|---|
| 376 | void event_main_loop(void) { |
|---|
| 377 | XEvent ev; |
|---|
| 378 | /* main event loop here */ |
|---|
| 379 | int fd = ConnectionNumber(dpy); |
|---|
| 380 | for (;;) { |
|---|
| 381 | fd_set rfset; |
|---|
| 382 | FD_ZERO(&rfset); |
|---|
| 383 | FD_SET(fd, &rfset); |
|---|
| 384 | struct timeval tv; |
|---|
| 385 | tv.tv_sec = 0; |
|---|
| 386 | tv.tv_usec = 1000*100; |
|---|
| 387 | |
|---|
| 388 | select(fd + 1, &rfset, NULL, NULL, &tv); |
|---|
| 389 | |
|---|
| 390 | //while(XEventsQueued(dpy, QueuedAfterReading) >= 0) { |
|---|
| 391 | while(XPending(dpy)) { |
|---|
| 392 | XNextEvent(dpy, &ev); |
|---|
| 393 | |
|---|
| 394 | switch (ev.type) { |
|---|
| 395 | case KeyPress: |
|---|
| 396 | handle_key_event2(&ev.xkey); break; |
|---|
| 397 | //handle_key_event(&ev.xkey); break; |
|---|
| 398 | #ifdef MOUSE |
|---|
| 399 | case ButtonPress: |
|---|
| 400 | handle_button_event(&ev.xbutton); break; |
|---|
| 401 | #endif |
|---|
| 402 | case ConfigureRequest: |
|---|
| 403 | handle_configure_request(&ev.xconfigurerequest); break; |
|---|
| 404 | case MapRequest: |
|---|
| 405 | handle_map_request(&ev.xmaprequest); break; |
|---|
| 406 | #ifdef COLOURMAP |
|---|
| 407 | case ColormapNotify: |
|---|
| 408 | handle_colormap_change(&ev.xcolormap); break; |
|---|
| 409 | #endif |
|---|
| 410 | case EnterNotify: |
|---|
| 411 | handle_enter_event(&ev.xcrossing); break; |
|---|
| 412 | case LeaveNotify: |
|---|
| 413 | handle_leave_event(&ev.xcrossing); break; |
|---|
| 414 | case PropertyNotify: |
|---|
| 415 | handle_property_change(&ev.xproperty); break; |
|---|
| 416 | case UnmapNotify: |
|---|
| 417 | handle_unmap_event(&ev.xunmap); break; |
|---|
| 418 | case MappingNotify: |
|---|
| 419 | handle_mappingnotify_event(&ev.xmapping); break; |
|---|
| 420 | #ifdef SHAPE |
|---|
| 421 | default: |
|---|
| 422 | if (have_shape && ev.type == shape_event) { |
|---|
| 423 | handle_shape_event((XShapeEvent *)&ev); |
|---|
| 424 | } |
|---|
| 425 | #endif |
|---|
| 426 | } |
|---|
| 427 | if (need_client_tidy) { |
|---|
| 428 | Client *c, *nc; |
|---|
| 429 | for (c = head_client; c; c = nc) { |
|---|
| 430 | nc = c->next; |
|---|
| 431 | if (c->remove) |
|---|
| 432 | remove_client(c); |
|---|
| 433 | } |
|---|
| 434 | } |
|---|
| 435 | } |
|---|
| 436 | } |
|---|
| 437 | } |
|---|