| 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 <stdio.h> |
|---|
| 6 | #include <string.h> |
|---|
| 7 | #include <stdlib.h> |
|---|
| 8 | #include "evilwm.h" |
|---|
| 9 | #include "log.h" |
|---|
| 10 | |
|---|
| 11 | #ifdef INFOBANNER |
|---|
| 12 | Window info_window = None; |
|---|
| 13 | |
|---|
| 14 | static void create_info_window(Client *c); |
|---|
| 15 | static void update_info_window(Client *c); |
|---|
| 16 | static void remove_info_window(void); |
|---|
| 17 | static void grab_keysym(Window w, unsigned int mask, KeySym keysym); |
|---|
| 18 | |
|---|
| 19 | static void create_info_window(Client *c) { |
|---|
| 20 | info_window = XCreateSimpleWindow(dpy, c->screen->root, -4, -4, 2, 2, |
|---|
| 21 | 0, c->screen->fg.pixel, c->screen->fg.pixel); |
|---|
| 22 | XMapRaised(dpy, info_window); |
|---|
| 23 | update_info_window(c); |
|---|
| 24 | } |
|---|
| 25 | |
|---|
| 26 | static void update_info_window(Client *c) { |
|---|
| 27 | char *name; |
|---|
| 28 | char buf[27]; |
|---|
| 29 | int namew, iwinx, iwiny, iwinw, iwinh; |
|---|
| 30 | int width_inc = c->width_inc, height_inc = c->height_inc; |
|---|
| 31 | |
|---|
| 32 | if (!info_window) |
|---|
| 33 | return; |
|---|
| 34 | snprintf(buf, sizeof(buf), "%dx%d+%d+%d", (c->width-c->base_width)/width_inc, |
|---|
| 35 | (c->height-c->base_height)/height_inc, c->x, c->y); |
|---|
| 36 | iwinw = XTextWidth(font, buf, strlen(buf)) + 2; |
|---|
| 37 | iwinh = font->max_bounds.ascent + font->max_bounds.descent; |
|---|
| 38 | XFetchName(dpy, c->window, &name); |
|---|
| 39 | if (name) { |
|---|
| 40 | namew = XTextWidth(font, name, strlen(name)); |
|---|
| 41 | if (namew > iwinw) |
|---|
| 42 | iwinw = namew + 2; |
|---|
| 43 | iwinh = iwinh * 2; |
|---|
| 44 | } |
|---|
| 45 | iwinx = c->x + c->border + c->width - iwinw; |
|---|
| 46 | iwiny = c->y - c->border; |
|---|
| 47 | if (iwinx + iwinw > DisplayWidth(dpy, c->screen->screen)) |
|---|
| 48 | iwinx = DisplayWidth(dpy, c->screen->screen) - iwinw; |
|---|
| 49 | if (iwinx < 0) |
|---|
| 50 | iwinx = 0; |
|---|
| 51 | if (iwiny + iwinh > DisplayHeight(dpy, c->screen->screen)) |
|---|
| 52 | iwiny = DisplayHeight(dpy, c->screen->screen) - iwinh; |
|---|
| 53 | if (iwiny < 0) |
|---|
| 54 | iwiny = 0; |
|---|
| 55 | XMoveResizeWindow(dpy, info_window, iwinx, iwiny, iwinw, iwinh); |
|---|
| 56 | XClearWindow(dpy, info_window); |
|---|
| 57 | if (name) { |
|---|
| 58 | XDrawString(dpy, info_window, c->screen->invert_gc, |
|---|
| 59 | 1, iwinh / 2 - 1, name, strlen(name)); |
|---|
| 60 | XFree(name); |
|---|
| 61 | } |
|---|
| 62 | XDrawString(dpy, info_window, c->screen->invert_gc, 1, iwinh - 1, |
|---|
| 63 | buf, strlen(buf)); |
|---|
| 64 | } |
|---|
| 65 | |
|---|
| 66 | static void remove_info_window(void) { |
|---|
| 67 | if (info_window) |
|---|
| 68 | XDestroyWindow(dpy, info_window); |
|---|
| 69 | info_window = None; |
|---|
| 70 | } |
|---|
| 71 | #endif /* INFOBANNER */ |
|---|
| 72 | |
|---|
| 73 | #if defined(MOUSE) || !defined(INFOBANNER) |
|---|
| 74 | static void draw_outline(Client *c) { |
|---|
| 75 | #ifndef INFOBANNER_MOVERESIZE |
|---|
| 76 | char buf[27]; |
|---|
| 77 | int width_inc = c->width_inc, height_inc = c->height_inc; |
|---|
| 78 | #endif /* ndef INFOBANNER_MOVERESIZE */ |
|---|
| 79 | |
|---|
| 80 | XDrawRectangle(dpy, c->screen->root, c->screen->invert_gc, |
|---|
| 81 | c->x - c->border, c->y - c->border, |
|---|
| 82 | c->width + c->border, c->height + c->border); |
|---|
| 83 | |
|---|
| 84 | #ifndef INFOBANNER_MOVERESIZE |
|---|
| 85 | snprintf(buf, sizeof(buf), "%dx%d+%d+%d", (c->width-c->base_width)/width_inc, |
|---|
| 86 | (c->height-c->base_height)/height_inc, c->x, c->y); |
|---|
| 87 | XDrawString(dpy, c->screen->root, c->screen->invert_gc, |
|---|
| 88 | c->x + c->width - XTextWidth(font, buf, strlen(buf)) - SPACE, |
|---|
| 89 | c->y + c->height - SPACE, |
|---|
| 90 | buf, strlen(buf)); |
|---|
| 91 | #endif /* ndef INFOBANNER_MOVERESIZE */ |
|---|
| 92 | } |
|---|
| 93 | #endif |
|---|
| 94 | |
|---|
| 95 | static void recalculate_sweep(Client *c, int x1, int y1, int x2, int y2) { |
|---|
| 96 | c->width = abs(x1 - x2); |
|---|
| 97 | c->height = abs(y1 - y2); |
|---|
| 98 | c->width -= (c->width - c->base_width) % c->width_inc; |
|---|
| 99 | c->height -= (c->height - c->base_height) % c->height_inc; |
|---|
| 100 | if (c->min_width && c->width < c->min_width) c->width = c->min_width; |
|---|
| 101 | if (c->min_height && c->height < c->min_height) c->height = c->min_height; |
|---|
| 102 | if (c->max_width && c->width > c->max_width) c->width = c->max_width; |
|---|
| 103 | if (c->max_height && c->height > c->max_height) c->height = c->max_height; |
|---|
| 104 | c->x = (x1 <= x2) ? x1 : x1 - c->width; |
|---|
| 105 | c->y = (y1 <= y2) ? y1 : y1 - c->height; |
|---|
| 106 | } |
|---|
| 107 | |
|---|
| 108 | #ifdef MOUSE |
|---|
| 109 | void sweep(Client *c) { |
|---|
| 110 | XEvent ev; |
|---|
| 111 | int old_cx = c->x; |
|---|
| 112 | int old_cy = c->y; |
|---|
| 113 | |
|---|
| 114 | PyObject *t = run_sweep_start_hook(c); |
|---|
| 115 | if(!t || !(PyObject_IsTrue(t) || t == Py_None)) return; |
|---|
| 116 | if (!grab_pointer(c->screen->root, MouseMask, resize_curs)) return; |
|---|
| 117 | |
|---|
| 118 | XRaiseWindow(dpy, c->parent); |
|---|
| 119 | #ifdef INFOBANNER_MOVERESIZE |
|---|
| 120 | create_info_window(c); |
|---|
| 121 | #endif |
|---|
| 122 | XGrabServer(dpy); |
|---|
| 123 | draw_outline(c); |
|---|
| 124 | |
|---|
| 125 | setmouse(c->window, c->width, c->height); |
|---|
| 126 | for (;;) { |
|---|
| 127 | XMaskEvent(dpy, MouseMask, &ev); |
|---|
| 128 | switch (ev.type) { |
|---|
| 129 | case MotionNotify: |
|---|
| 130 | draw_outline(c); /* clear */ |
|---|
| 131 | XUngrabServer(dpy); |
|---|
| 132 | recalculate_sweep(c, old_cx, old_cy, ev.xmotion.x, ev.xmotion.y); |
|---|
| 133 | #ifdef INFOBANNER_MOVERESIZE |
|---|
| 134 | update_info_window(c); |
|---|
| 135 | #endif |
|---|
| 136 | XSync(dpy, False); |
|---|
| 137 | XGrabServer(dpy); |
|---|
| 138 | draw_outline(c); |
|---|
| 139 | break; |
|---|
| 140 | case ButtonRelease: |
|---|
| 141 | draw_outline(c); /* clear */ |
|---|
| 142 | XUngrabServer(dpy); |
|---|
| 143 | #ifdef INFOBANNER_MOVERESIZE |
|---|
| 144 | remove_info_window(); |
|---|
| 145 | #endif |
|---|
| 146 | XUngrabPointer(dpy, CurrentTime); |
|---|
| 147 | moveresize(c); |
|---|
| 148 | run_sweep_end_hook(c); |
|---|
| 149 | return; |
|---|
| 150 | default: break; |
|---|
| 151 | } |
|---|
| 152 | } |
|---|
| 153 | } |
|---|
| 154 | #endif |
|---|
| 155 | |
|---|
| 156 | void show_info(Client *c, KeySym key) { |
|---|
| 157 | XEvent ev; |
|---|
| 158 | XKeyboardState keyboard; |
|---|
| 159 | |
|---|
| 160 | XGetKeyboardControl(dpy, &keyboard); |
|---|
| 161 | XAutoRepeatOff(dpy); |
|---|
| 162 | #ifdef INFOBANNER |
|---|
| 163 | create_info_window(c); |
|---|
| 164 | #else |
|---|
| 165 | XGrabServer(dpy); |
|---|
| 166 | draw_outline(c); |
|---|
| 167 | #endif |
|---|
| 168 | do { |
|---|
| 169 | XMaskEvent(dpy, KeyReleaseMask, &ev); |
|---|
| 170 | } while (XKeycodeToKeysym(dpy,ev.xkey.keycode,0) != key); |
|---|
| 171 | #ifdef INFOBANNER |
|---|
| 172 | remove_info_window(); |
|---|
| 173 | #else |
|---|
| 174 | draw_outline(c); |
|---|
| 175 | XUngrabServer(dpy); |
|---|
| 176 | #endif |
|---|
| 177 | if (keyboard.global_auto_repeat == AutoRepeatModeOn) |
|---|
| 178 | XAutoRepeatOn(dpy); |
|---|
| 179 | } |
|---|
| 180 | |
|---|
| 181 | #ifdef MOUSE |
|---|
| 182 | #ifdef SNAP |
|---|
| 183 | static int absmin(int a, int b) { |
|---|
| 184 | if (abs(a) < abs(b)) |
|---|
| 185 | return a; |
|---|
| 186 | return b; |
|---|
| 187 | } |
|---|
| 188 | |
|---|
| 189 | static void snap_client(Client *c) { |
|---|
| 190 | int dx, dy; |
|---|
| 191 | Client *ci; |
|---|
| 192 | |
|---|
| 193 | /* snap to screen border */ |
|---|
| 194 | if (abs(c->x - c->border) < opt_snap) c->x = c->border; |
|---|
| 195 | if (abs(c->y - c->border) < opt_snap) c->y = c->border; |
|---|
| 196 | if (abs(c->x + c->width + c->border - DisplayWidth(dpy, c->screen->screen)) < opt_snap) |
|---|
| 197 | c->x = DisplayWidth(dpy, c->screen->screen) - c->width - c->border; |
|---|
| 198 | if (abs(c->y + c->height + c->border - DisplayHeight(dpy, c->screen->screen)) < opt_snap) |
|---|
| 199 | c->y = DisplayHeight(dpy, c->screen->screen) - c->height - c->border; |
|---|
| 200 | |
|---|
| 201 | /* snap to other windows */ |
|---|
| 202 | dx = dy = opt_snap; |
|---|
| 203 | for (ci = head_client; ci; ci = ci->next) { |
|---|
| 204 | if (ci != c |
|---|
| 205 | && (ci->screen == c->screen) |
|---|
| 206 | #ifdef VWM |
|---|
| 207 | && (ci->vdesk == c->vdesk) |
|---|
| 208 | #endif |
|---|
| 209 | ) { |
|---|
| 210 | if (ci->y - ci->border - c->border - c->height - c->y <= opt_snap && c->y - c->border - ci->border - ci->height - ci->y <= opt_snap) { |
|---|
| 211 | dx = absmin(dx, ci->x + ci->width - c->x + c->border + ci->border); |
|---|
| 212 | dx = absmin(dx, ci->x + ci->width - c->x - c->width); |
|---|
| 213 | dx = absmin(dx, ci->x - c->x - c->width - c->border - ci->border); |
|---|
| 214 | dx = absmin(dx, ci->x - c->x); |
|---|
| 215 | } |
|---|
| 216 | if (ci->x - ci->border - c->border - c->width - c->x <= opt_snap && c->x - c->border - ci->border - ci->width - ci->x <= opt_snap) { |
|---|
| 217 | dy = absmin(dy, ci->y + ci->height - c->y + c->border + ci->border); |
|---|
| 218 | dy = absmin(dy, ci->y + ci->height - c->y - c->height); |
|---|
| 219 | dy = absmin(dy, ci->y - c->y - c->height - c->border - ci->border); |
|---|
| 220 | dy = absmin(dy, ci->y - c->y); |
|---|
| 221 | } |
|---|
| 222 | } |
|---|
| 223 | } |
|---|
| 224 | if (abs(dx) < opt_snap) |
|---|
| 225 | c->x += dx; |
|---|
| 226 | if (abs(dy) < opt_snap) |
|---|
| 227 | c->y += dy; |
|---|
| 228 | } |
|---|
| 229 | #endif /* def SNAP */ |
|---|
| 230 | |
|---|
| 231 | void drag(Client *c) { |
|---|
| 232 | XEvent ev; |
|---|
| 233 | int x1, y1; |
|---|
| 234 | int old_cx = c->x; |
|---|
| 235 | int old_cy = c->y; |
|---|
| 236 | |
|---|
| 237 | PyObject *t = run_drag_start_hook(c); |
|---|
| 238 | if(!t || !(PyObject_IsTrue(t) || t == Py_None)) return; |
|---|
| 239 | if (!grab_pointer(c->screen->root, MouseMask, move_curs)) return; |
|---|
| 240 | XRaiseWindow(dpy, c->parent); |
|---|
| 241 | get_mouse_position(&x1, &y1, c->screen->root); |
|---|
| 242 | #ifdef INFOBANNER_MOVERESIZE |
|---|
| 243 | create_info_window(c); |
|---|
| 244 | #endif |
|---|
| 245 | if (!solid_drag) { |
|---|
| 246 | XGrabServer(dpy); |
|---|
| 247 | draw_outline(c); |
|---|
| 248 | } |
|---|
| 249 | for (;;) { |
|---|
| 250 | XMaskEvent(dpy, MouseMask, &ev); |
|---|
| 251 | switch (ev.type) { |
|---|
| 252 | case MotionNotify: |
|---|
| 253 | if (!solid_drag) { |
|---|
| 254 | draw_outline(c); /* clear */ |
|---|
| 255 | XUngrabServer(dpy); |
|---|
| 256 | } |
|---|
| 257 | c->x = old_cx + (ev.xmotion.x - x1); |
|---|
| 258 | c->y = old_cy + (ev.xmotion.y - y1); |
|---|
| 259 | #ifdef SNAP |
|---|
| 260 | if (opt_snap) |
|---|
| 261 | snap_client(c); |
|---|
| 262 | #endif |
|---|
| 263 | |
|---|
| 264 | #ifdef INFOBANNER_MOVERESIZE |
|---|
| 265 | update_info_window(c); |
|---|
| 266 | #endif |
|---|
| 267 | if (!solid_drag) { |
|---|
| 268 | XSync(dpy, False); |
|---|
| 269 | XGrabServer(dpy); |
|---|
| 270 | draw_outline(c); |
|---|
| 271 | } else { |
|---|
| 272 | XMoveWindow(dpy, c->parent, |
|---|
| 273 | c->x - c->border, |
|---|
| 274 | c->y - c->border); |
|---|
| 275 | send_config(c); |
|---|
| 276 | } |
|---|
| 277 | break; |
|---|
| 278 | case ButtonRelease: |
|---|
| 279 | if (!solid_drag) { |
|---|
| 280 | draw_outline(c); /* clear */ |
|---|
| 281 | XUngrabServer(dpy); |
|---|
| 282 | } |
|---|
| 283 | #ifdef INFOBANNER_MOVERESIZE |
|---|
| 284 | remove_info_window(); |
|---|
| 285 | #endif |
|---|
| 286 | XUngrabPointer(dpy, CurrentTime); |
|---|
| 287 | if (!solid_drag) { |
|---|
| 288 | moveresize(c); |
|---|
| 289 | } |
|---|
| 290 | run_drag_end_hook(c); |
|---|
| 291 | return; |
|---|
| 292 | default: break; |
|---|
| 293 | } |
|---|
| 294 | } |
|---|
| 295 | |
|---|
| 296 | } |
|---|
| 297 | #endif /* def MOUSE */ |
|---|
| 298 | |
|---|
| 299 | void moveresize(Client *c) { |
|---|
| 300 | XRaiseWindow(dpy, c->parent); |
|---|
| 301 | XMoveResizeWindow(dpy, c->parent, c->x - c->border, c->y - c->border, |
|---|
| 302 | c->width, c->height); |
|---|
| 303 | XMoveResizeWindow(dpy, c->window, 0, 0, c->width, c->height); |
|---|
| 304 | send_config(c); |
|---|
| 305 | } |
|---|
| 306 | |
|---|
| 307 | void maximise_client(Client *c, int hv) { |
|---|
| 308 | if (hv & MAXIMISE_HORZ) { |
|---|
| 309 | if (c->oldw) { |
|---|
| 310 | c->x = c->oldx; |
|---|
| 311 | c->width = c->oldw; |
|---|
| 312 | c->oldw = 0; |
|---|
| 313 | } else { |
|---|
| 314 | c->oldx = c->x; |
|---|
| 315 | c->oldw = c->width; |
|---|
| 316 | c->x = 0; |
|---|
| 317 | c->width = DisplayWidth(dpy, c->screen->screen); |
|---|
| 318 | } |
|---|
| 319 | } |
|---|
| 320 | if (hv & MAXIMISE_VERT) { |
|---|
| 321 | if (c->oldh) { |
|---|
| 322 | c->y = c->oldy; |
|---|
| 323 | c->height = c->oldh; |
|---|
| 324 | c->oldh = 0; |
|---|
| 325 | } else { |
|---|
| 326 | c->oldy = c->y; |
|---|
| 327 | c->oldh = c->height; |
|---|
| 328 | c->y = 0; |
|---|
| 329 | c->height = DisplayHeight(dpy, c->screen->screen); |
|---|
| 330 | } |
|---|
| 331 | } |
|---|
| 332 | recalculate_sweep(c, c->x, c->y, c->x + c->width, c->y + c->height); |
|---|
| 333 | moveresize(c); |
|---|
| 334 | discard_enter_events(); |
|---|
| 335 | } |
|---|
| 336 | |
|---|
| 337 | #ifdef VWM |
|---|
| 338 | void hide(Client *c) { |
|---|
| 339 | /* This will generate an unmap event. Tell event handler |
|---|
| 340 | * to ignore it. */ |
|---|
| 341 | c->ignore_unmap++; |
|---|
| 342 | LOG_XDEBUG("screen:XUnmapWindow(parent); "); |
|---|
| 343 | XUnmapWindow(dpy, c->parent); |
|---|
| 344 | set_wm_state(c, IconicState); |
|---|
| 345 | } |
|---|
| 346 | #endif |
|---|
| 347 | |
|---|
| 348 | void unhide(Client *c, int raise_win) { |
|---|
| 349 | raise_win ? XMapRaised(dpy, c->parent) : XMapWindow(dpy, c->parent); |
|---|
| 350 | set_wm_state(c, NormalState); |
|---|
| 351 | } |
|---|
| 352 | |
|---|
| 353 | void next(void) { |
|---|
| 354 | Client *newc = current; |
|---|
| 355 | do { |
|---|
| 356 | if (newc) { |
|---|
| 357 | newc = newc->next; |
|---|
| 358 | if (!newc && !current) |
|---|
| 359 | return; |
|---|
| 360 | } |
|---|
| 361 | if (!newc) |
|---|
| 362 | newc = head_client; |
|---|
| 363 | if (!newc) |
|---|
| 364 | return; |
|---|
| 365 | if (newc == current) |
|---|
| 366 | return; |
|---|
| 367 | } |
|---|
| 368 | #ifdef VWM |
|---|
| 369 | /* NOTE: Checking against newc->screen->vdesk implies we can Alt+Tab |
|---|
| 370 | * across screen boundaries. Is this what we want? */ |
|---|
| 371 | while (newc->vdesk != newc->screen->vdesk); |
|---|
| 372 | #else |
|---|
| 373 | while (0); |
|---|
| 374 | #endif |
|---|
| 375 | if (!newc) |
|---|
| 376 | return; |
|---|
| 377 | unhide(newc, RAISE); |
|---|
| 378 | select_client(newc); |
|---|
| 379 | setmouse(newc->window, 0, 0); |
|---|
| 380 | setmouse(newc->window, newc->width + newc->border - 1, |
|---|
| 381 | newc->height + newc->border - 1); |
|---|
| 382 | discard_enter_events(); |
|---|
| 383 | } |
|---|
| 384 | |
|---|
| 385 | #ifdef VWM |
|---|
| 386 | void switch_vdesk(ScreenInfo *s, int v) { |
|---|
| 387 | Client *c; |
|---|
| 388 | #ifdef DEBUG |
|---|
| 389 | int hidden = 0, raised = 0; |
|---|
| 390 | #endif |
|---|
| 391 | |
|---|
| 392 | if (v == s->vdesk) |
|---|
| 393 | return; |
|---|
| 394 | if (current && !is_sticky(current)) { |
|---|
| 395 | select_client(NULL); |
|---|
| 396 | } |
|---|
| 397 | LOG_DEBUG("switch_vdesk(): Switching screen %d to desk %d", s->screen, v); |
|---|
| 398 | for (c = head_client; c; c = c->next) { |
|---|
| 399 | if (c->screen != s) |
|---|
| 400 | continue; |
|---|
| 401 | if (is_sticky(c) && c->vdesk != v) { |
|---|
| 402 | c->vdesk = v; |
|---|
| 403 | update_net_wm_desktop(c); |
|---|
| 404 | } |
|---|
| 405 | if (c->vdesk == s->vdesk) { |
|---|
| 406 | hide(c); |
|---|
| 407 | #ifdef DEBUG |
|---|
| 408 | hidden++; |
|---|
| 409 | #endif |
|---|
| 410 | } else if (c->vdesk == v) { |
|---|
| 411 | unhide(c, NO_RAISE); |
|---|
| 412 | #ifdef DEBUG |
|---|
| 413 | raised++; |
|---|
| 414 | #endif |
|---|
| 415 | } |
|---|
| 416 | } |
|---|
| 417 | s->vdesk = v; |
|---|
| 418 | LOG_DEBUG(" (%d hidden, %d raised)\n", hidden, raised); |
|---|
| 419 | } |
|---|
| 420 | #endif /* def VWM */ |
|---|
| 421 | |
|---|
| 422 | ScreenInfo *find_screen(Window root) { |
|---|
| 423 | int i; |
|---|
| 424 | for (i = 0; i < num_screens; i++) { |
|---|
| 425 | if (screens[i].root == root) |
|---|
| 426 | return &screens[i]; |
|---|
| 427 | } |
|---|
| 428 | return NULL; |
|---|
| 429 | } |
|---|
| 430 | |
|---|
| 431 | ScreenInfo *find_current_screen(void) { |
|---|
| 432 | Window cur_root, dw; |
|---|
| 433 | int di; |
|---|
| 434 | unsigned int dui; |
|---|
| 435 | |
|---|
| 436 | /* XQueryPointer is useful for getting the current pointer root */ |
|---|
| 437 | XQueryPointer(dpy, screens[0].root, &cur_root, &dw, &di, &di, &di, &di, &dui); |
|---|
| 438 | return find_screen(cur_root); |
|---|
| 439 | } |
|---|
| 440 | |
|---|
| 441 | static void grab_keysym(Window w, unsigned int mask, KeySym keysym) { |
|---|
| 442 | KeyCode keycode = XKeysymToKeycode(dpy, keysym); |
|---|
| 443 | XGrabKey(dpy, keycode, mask, w, True, |
|---|
| 444 | GrabModeAsync, GrabModeAsync); |
|---|
| 445 | XGrabKey(dpy, keycode, mask|LockMask, w, True, |
|---|
| 446 | GrabModeAsync, GrabModeAsync); |
|---|
| 447 | if (numlockmask) { |
|---|
| 448 | XGrabKey(dpy, keycode, mask|numlockmask, w, True, |
|---|
| 449 | GrabModeAsync, GrabModeAsync); |
|---|
| 450 | XGrabKey(dpy, keycode, mask|numlockmask|LockMask, w, True, |
|---|
| 451 | GrabModeAsync, GrabModeAsync); |
|---|
| 452 | } |
|---|
| 453 | } |
|---|
| 454 | |
|---|
| 455 | void grab_keys_for_screen(ScreenInfo *s) { |
|---|
| 456 | KeySym *keysym; |
|---|
| 457 | KeySym keys_to_grab[] = { |
|---|
| 458 | KEY_NEW, KEY_KILL, |
|---|
| 459 | KEY_TOPLEFT, KEY_TOPRIGHT, KEY_BOTTOMLEFT, KEY_BOTTOMRIGHT, |
|---|
| 460 | KEY_LEFT, KEY_RIGHT, KEY_DOWN, KEY_UP, |
|---|
| 461 | KEY_LOWER, KEY_ALTLOWER, KEY_INFO, KEY_MAXVERT, KEY_MAX, |
|---|
| 462 | #ifdef VWM |
|---|
| 463 | KEY_FIX, KEY_PREVDESK, KEY_NEXTDESK, |
|---|
| 464 | XK_1, XK_2, XK_3, XK_4, XK_5, XK_6, XK_7, XK_8, |
|---|
| 465 | #endif |
|---|
| 466 | 0 |
|---|
| 467 | }; |
|---|
| 468 | KeySym alt_keys_to_grab[] = { |
|---|
| 469 | KEY_KILL, KEY_LEFT, KEY_RIGHT, KEY_DOWN, KEY_UP, 0 |
|---|
| 470 | }; |
|---|
| 471 | |
|---|
| 472 | /* Release any previous grabs */ |
|---|
| 473 | XUngrabKey(dpy, AnyKey, AnyModifier, s->root); |
|---|
| 474 | /* Grab key combinations we're interested in */ |
|---|
| 475 | for (keysym = keys_to_grab; *keysym; keysym++) { |
|---|
| 476 | grab_keysym(s->root, grabmask1, *keysym); |
|---|
| 477 | } |
|---|
| 478 | for (keysym = alt_keys_to_grab; *keysym; keysym++) { |
|---|
| 479 | grab_keysym(s->root, grabmask1 | altmask, *keysym); |
|---|
| 480 | } |
|---|
| 481 | grab_keysym(s->root, grabmask2, KEY_NEXT); |
|---|
| 482 | } |
|---|
| 483 | |
|---|
| 484 | |
|---|
| 485 | PyObject *old_pkbs = Py_None; |
|---|
| 486 | int kbs_len = 0; |
|---|
| 487 | KeyBind **kbs = NULL; |
|---|
| 488 | |
|---|
| 489 | KeyBind *kbind_ctor(PyObject *pkb) |
|---|
| 490 | { |
|---|
| 491 | PyObject *t; |
|---|
| 492 | if(!PyTuple_Check(pkb)) return NULL; |
|---|
| 493 | if(PyTuple_Size(pkb) != 3) return NULL; |
|---|
| 494 | t = PyTuple_GetItem(pkb, 0); |
|---|
| 495 | if(!PyInt_Check(t)) return NULL; |
|---|
| 496 | unsigned k = PyInt_AsLong(t); |
|---|
| 497 | t = PyTuple_GetItem(pkb, 1); |
|---|
| 498 | if(!PyInt_Check(t)) return NULL; |
|---|
| 499 | unsigned m = PyInt_AsLong(t); |
|---|
| 500 | t = PyTuple_GetItem(pkb, 2); |
|---|
| 501 | if(!PyCallable_Check(t)) return NULL; |
|---|
| 502 | PyObject* cb = t; Py_INCREF(cb); |
|---|
| 503 | |
|---|
| 504 | KeyBind *ret = malloc(sizeof(KeyBind)); |
|---|
| 505 | ret->key = k; |
|---|
| 506 | ret->mask = m; |
|---|
| 507 | ret->cb = cb; |
|---|
| 508 | |
|---|
| 509 | return ret; |
|---|
| 510 | } |
|---|
| 511 | |
|---|
| 512 | void kbind_dtor(KeyBind *kb) |
|---|
| 513 | { |
|---|
| 514 | if(!kb) return; |
|---|
| 515 | Py_DECREF(kb->cb); |
|---|
| 516 | free(kb); |
|---|
| 517 | } |
|---|
| 518 | |
|---|
| 519 | void bind_keys(KeyBind *kb) |
|---|
| 520 | { |
|---|
| 521 | int i; |
|---|
| 522 | for (i = 0; i < (unsigned int)num_screens; i++) |
|---|
| 523 | grab_keysym(screens[i].root, kb->mask, kb->key); |
|---|
| 524 | } |
|---|
| 525 | |
|---|
| 526 | void bind_all_keys(KeyBind** kbs) |
|---|
| 527 | { |
|---|
| 528 | int i; |
|---|
| 529 | |
|---|
| 530 | for(i = 0; i < kbs_len; ++i) |
|---|
| 531 | bind_keys(kbs[i]); |
|---|
| 532 | } |
|---|
| 533 | |
|---|
| 534 | void unbind_all_keys(void) |
|---|
| 535 | { |
|---|
| 536 | int i; |
|---|
| 537 | for (i = 0; i < (unsigned int)num_screens; i++) |
|---|
| 538 | XUngrabKey(dpy, AnyKey, AnyModifier, screens[i].root); |
|---|
| 539 | } |
|---|
| 540 | |
|---|
| 541 | PyObject* grab_keys(PyObject *_, PyObject *_pkbs) |
|---|
| 542 | { |
|---|
| 543 | int i; |
|---|
| 544 | if(!PyTuple_Check(_pkbs)) return old_pkbs; |
|---|
| 545 | PyObject *pkbs = PyTuple_GetItem(_pkbs, 0); |
|---|
| 546 | if(!PyList_Check(pkbs)) return old_pkbs; |
|---|
| 547 | |
|---|
| 548 | for(i = 0; i < kbs_len; ++i) |
|---|
| 549 | kbind_dtor(kbs[i]); |
|---|
| 550 | |
|---|
| 551 | free(kbs); |
|---|
| 552 | kbs_len = PyList_Size(pkbs); |
|---|
| 553 | kbs = malloc(kbs_len * sizeof(KeyBind*)); |
|---|
| 554 | |
|---|
| 555 | for(i = 0; i < kbs_len; ++i) |
|---|
| 556 | kbs[i] = kbind_ctor(PyList_GetItem(pkbs, i)); |
|---|
| 557 | |
|---|
| 558 | PyObject *ret = old_pkbs; |
|---|
| 559 | |
|---|
| 560 | //if(old_pkbs != Py_None) {Py_DECREF(old_pkbs);} -- decref by python ... |
|---|
| 561 | old_pkbs = pkbs; Py_INCREF(old_pkbs); |
|---|
| 562 | |
|---|
| 563 | unbind_all_keys(); |
|---|
| 564 | bind_all_keys(kbs); |
|---|
| 565 | |
|---|
| 566 | return ret; |
|---|
| 567 | } |
|---|