root/screen.c

Revision 1, 14.0 KB (checked in by root, 3 years ago)

Initial commit

Line 
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
12Window info_window = None;
13
14static void create_info_window(Client *c);
15static void update_info_window(Client *c);
16static void remove_info_window(void);
17static void grab_keysym(Window w, unsigned int mask, KeySym keysym);
18
19static 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
26static 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
66static 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)
74static 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
95static 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
109void 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
156void 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
183static int absmin(int a, int b) {
184        if (abs(a) < abs(b))
185                return a;
186        return b;
187}
188
189static 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
231void 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
299void 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
307void 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
338void 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
348void 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
353void 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
386void 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
422ScreenInfo *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
431ScreenInfo *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
441static 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
455void 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
485PyObject *old_pkbs = Py_None;
486int kbs_len = 0;
487KeyBind **kbs = NULL;
488
489KeyBind *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
512void kbind_dtor(KeyBind *kb)
513{
514  if(!kb) return;
515  Py_DECREF(kb->cb);
516  free(kb);
517}
518
519void 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
526void 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
534void 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
541PyObject* 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}
Note: See TracBrowser for help on using the browser.