Implement per screen configuration for statusbar.
[awesome.git] / event.c
blobf7a07db04df3f53bd35477e001f23e09a95b3e3c
1 /*
2 * event.c - event handlers
4 * Copyright © 2007 Julien Danjou <julien@danjou.info>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22 #include <X11/keysym.h>
23 #include <X11/Xatom.h>
24 #include <X11/Xutil.h>
25 #include <X11/extensions/shape.h>
26 #include <X11/extensions/Xrandr.h>
28 #include "screen.h"
29 #include "event.h"
30 #include "layout.h"
31 #include "tag.h"
32 #include "draw.h"
33 #include "statusbar.h"
34 #include "util.h"
35 #include "window.h"
36 #include "layouts/tile.h"
37 #include "layouts/floating.h"
39 #define CLEANMASK(mask, screen) (mask & ~(awesomeconf[screen].numlockmask | LockMask))
40 #define MOUSEMASK (BUTTONMASK | PointerMotionMask)
42 static void
43 movemouse(Client *c, awesome_config *awesomeconf)
45 int x1, y1, ocx, ocy, di, nx, ny;
46 unsigned int dui;
47 Window dummy;
48 XEvent ev;
49 ScreenInfo *si;
51 si = get_screen_info(c->display, c->screen, &awesomeconf[c->screen].statusbar);
53 ocx = nx = c->x;
54 ocy = ny = c->y;
55 if(XGrabPointer(c->display, RootWindow(c->display, c->phys_screen), False, MOUSEMASK, GrabModeAsync, GrabModeAsync,
56 None, awesomeconf[c->screen].cursor[CurMove], CurrentTime) != GrabSuccess)
57 return;
58 XWarpPointer(c->display, None, c->win, 0, 0, 0, 0, c->w / 2, c->h / 2);
59 XQueryPointer(c->display, RootWindow(c->display, c->phys_screen), &dummy, &dummy, &x1, &y1, &di, &di, &dui);
60 for(;;)
62 XMaskEvent(c->display, MOUSEMASK | ExposureMask | SubstructureRedirectMask, &ev);
63 switch (ev.type)
65 case ButtonRelease:
66 XUngrabPointer(c->display, CurrentTime);
67 p_delete(&si);
68 return;
69 case ConfigureRequest:
70 handle_event_configurerequest(&ev,awesomeconf);
71 break;
72 case Expose:
73 handle_event_expose(&ev, awesomeconf);
74 break;
75 case MapRequest:
76 handle_event_maprequest(&ev, awesomeconf);
77 break;
78 case MotionNotify:
79 XSync(c->display, False);
80 nx = ocx + (ev.xmotion.x - x1);
81 ny = ocy + (ev.xmotion.y - y1);
82 if(abs(nx) < awesomeconf[c->screen].snap + si[c->screen].x_org && nx > si[c->screen].x_org)
83 nx = si[c->screen].x_org;
84 else if(abs((si[c->screen].x_org + si[c->screen].width) - (nx + c->w + 2 * c->border)) < awesomeconf[c->screen].snap)
85 nx = si[c->screen].x_org + si[c->screen].width - c->w - 2 * c->border;
86 if(abs(ny) < awesomeconf[c->screen].snap + si[c->screen].y_org && ny > si[c->screen].y_org)
87 ny = si[c->screen].y_org;
88 else if(abs((si[c->screen].y_org + si[c->screen].height) - (ny + c->h + 2 * c->border)) < awesomeconf[c->screen].snap)
89 ny = si[c->screen].y_org + si[c->screen].height - c->h - 2 * c->border;
90 client_resize(c, nx, ny, c->w, c->h, &awesomeconf[c->screen], False, False);
91 break;
96 static void
97 resizemouse(Client * c, awesome_config *awesomeconf)
99 int ocx, ocy, nw, nh;
100 XEvent ev;
102 ocx = c->x;
103 ocy = c->y;
104 if(XGrabPointer(c->display, RootWindow(c->display, c->phys_screen),
105 False, MOUSEMASK, GrabModeAsync, GrabModeAsync,
106 None, awesomeconf[c->screen].cursor[CurResize], CurrentTime) != GrabSuccess)
107 return;
108 c->ismax = False;
109 XWarpPointer(c->display, None, c->win, 0, 0, 0, 0, c->w + c->border - 1, c->h + c->border - 1);
110 for(;;)
112 XMaskEvent(c->display, MOUSEMASK | ExposureMask | SubstructureRedirectMask, &ev);
113 switch (ev.type)
115 case ButtonRelease:
116 XWarpPointer(c->display, None, c->win, 0, 0, 0, 0, c->w + c->border - 1, c->h + c->border - 1);
117 XUngrabPointer(c->display, CurrentTime);
118 while(XCheckMaskEvent(c->display, EnterWindowMask, &ev));
119 return;
120 case ConfigureRequest:
121 handle_event_configurerequest(&ev,awesomeconf);
122 break;
123 case Expose:
124 handle_event_expose(&ev, awesomeconf);
125 break;
126 case MapRequest:
127 handle_event_maprequest(&ev, awesomeconf);
128 break;
129 case MotionNotify:
130 XSync(c->display, False);
131 if((nw = ev.xmotion.x - ocx - 2 * c->border + 1) <= 0)
132 nw = 1;
133 if((nh = ev.xmotion.y - ocy - 2 * c->border + 1) <= 0)
134 nh = 1;
135 client_resize(c, c->x, c->y, nw, nh, &awesomeconf[c->screen], True, False);
136 break;
141 void
142 handle_event_buttonpress(XEvent * e, awesome_config *awesomeconf)
144 int i, screen, x = 0, y = 0;
145 unsigned int udummy;
146 Client *c;
147 Window wdummy;
148 XButtonPressedEvent *ev = &e->xbutton;
150 for(screen = 0; screen < get_screen_count(e->xany.display); screen++)
151 if(awesomeconf[screen].statusbar.window == ev->window)
153 for(i = 0; i < awesomeconf[screen].ntags; i++)
155 x += textwidth(e->xany.display, awesomeconf[screen].font, awesomeconf[screen].tags[i].name);
156 if(ev->x < x)
158 if(ev->button == Button1)
160 if(ev->state & awesomeconf[screen].modkey)
161 uicb_tag(&awesomeconf[screen], awesomeconf[screen].tags[i].name);
162 else
163 uicb_view(&awesomeconf[screen], awesomeconf[screen].tags[i].name);
165 else if(ev->button == Button3)
167 if(ev->state & awesomeconf[screen].modkey)
168 uicb_toggletag(&awesomeconf[screen], awesomeconf[screen].tags[i].name);
169 else
170 uicb_toggleview(&awesomeconf[screen], awesomeconf[screen].tags[i].name);
172 else if(ev->button == Button4)
173 uicb_tag_viewnext(&awesomeconf[screen], NULL);
174 else if(ev->button == Button5)
175 uicb_tag_viewprev(&awesomeconf[screen], NULL);
176 return;
179 x += awesomeconf[screen].statusbar.txtlayoutwidth;
180 if(ev->x < x && (ev->button == Button1 || ev->button == Button4))
181 uicb_setlayout(&awesomeconf[screen], "+1");
182 else if(ev->x < x && (ev->button == Button3 || ev->button == Button5))
183 uicb_setlayout(&awesomeconf[screen], "-1");
184 else if(ev->button == Button4)
185 uicb_focusnext(&awesomeconf[screen], NULL);
186 else if(ev->button == Button5)
187 uicb_focusprev(&awesomeconf[screen], NULL);
188 return;
191 if((c = get_client_bywin(*awesomeconf->clients, ev->window)))
193 XAllowEvents(c->display, ReplayPointer, CurrentTime);
194 focus(c, ev->same_screen, &awesomeconf[c->screen]);
195 if(CLEANMASK(ev->state, c->screen) != awesomeconf[c->screen].modkey)
197 if (ev->button == Button1)
199 restack(&awesomeconf[c->screen]);
200 window_grabbuttons(c->display, c->phys_screen, c->win,
201 True, True, awesomeconf->modkey, awesomeconf->numlockmask);
204 else if(ev->button == Button1)
206 if((get_current_layout(awesomeconf[c->screen].tags,
207 awesomeconf[c->screen].ntags)->arrange != layout_floating)
208 && !c->isfloating)
209 uicb_togglefloating(&awesomeconf[c->screen], NULL);
210 else
211 restack(&awesomeconf[c->screen]);
212 movemouse(c, awesomeconf);
214 else if(ev->button == Button2)
216 if((get_current_layout(awesomeconf[c->screen].tags,
217 awesomeconf[c->screen].ntags)->arrange != layout_floating)
218 && !c->isfixed && c->isfloating)
219 uicb_togglefloating(&awesomeconf[c->screen], NULL);
220 else
221 uicb_zoom(&awesomeconf[c->screen], NULL);
223 else if(ev->button == Button3)
225 if((get_current_layout(awesomeconf[c->screen].tags,
226 awesomeconf[c->screen].ntags)->arrange != layout_floating)
227 && !c->isfloating)
228 uicb_togglefloating(&awesomeconf[c->screen], NULL);
229 else
230 restack(&awesomeconf[c->screen]);
231 resizemouse(c, awesomeconf);
233 else if(ev->button == Button4)
234 uicb_settrans(&awesomeconf[c->screen], "+5");
235 else if(ev->button == Button5)
236 uicb_settrans(&awesomeconf[c->screen], "-5");
238 else
239 for(screen = 0; screen < ScreenCount(e->xany.display); screen++)
240 if(RootWindow(e->xany.display, screen) == ev->window
241 && XQueryPointer(e->xany.display, ev->window, &wdummy, &wdummy, &x, &y, &i, &i, &udummy))
243 screen = get_screen_bycoord(e->xany.display, x, y);
244 if(ev->button == Button4)
245 uicb_tag_viewnext(&awesomeconf[screen], NULL);
246 else if(ev->button == Button5)
247 uicb_tag_viewprev(&awesomeconf[screen], NULL);
248 break;
252 void
253 handle_event_configurerequest(XEvent * e, awesome_config *awesomeconf)
255 Client *c;
256 XConfigureRequestEvent *ev = &e->xconfigurerequest;
257 XWindowChanges wc;
259 if((c = get_client_bywin(*awesomeconf->clients, ev->window)))
261 c->ismax = False;
262 if(ev->value_mask & CWBorderWidth)
263 c->border = ev->border_width;
264 if(c->isfixed || c->isfloating
265 || get_current_layout(awesomeconf[c->screen].tags,
266 awesomeconf[c->screen].ntags)->arrange == layout_floating)
268 if(ev->value_mask & CWX)
269 c->x = ev->x;
270 if(ev->value_mask & CWY)
271 c->y = ev->y;
272 if(ev->value_mask & CWWidth)
273 c->w = ev->width;
274 if(ev->value_mask & CWHeight)
275 c->h = ev->height;
276 if((c->x + c->w) > DisplayWidth(c->display, c->phys_screen) && c->isfloating)
277 c->x = DisplayWidth(c->display, c->phys_screen) / 2 - c->w / 2; /* center in x direction */
278 if((c->y + c->h) > DisplayHeight(c->display, c->phys_screen) && c->isfloating)
279 c->y = DisplayHeight(c->display, c->phys_screen) / 2 - c->h / 2; /* center in y direction */
280 if((ev->value_mask & (CWX | CWY)) && !(ev->value_mask & (CWWidth | CWHeight)))
281 window_configure(c->display, c->win, c->x, c->y, c->w, c->h, c->border);
282 if(isvisible(c, c->screen, awesomeconf[c->screen].tags, awesomeconf[c->screen].ntags))
283 XMoveResizeWindow(e->xany.display, c->win, c->x, c->y, c->w, c->h);
285 else
286 window_configure(c->display, c->win, c->x, c->y, c->w, c->h, c->border);
288 else
290 wc.x = ev->x;
291 wc.y = ev->y;
292 wc.width = ev->width;
293 wc.height = ev->height;
294 wc.border_width = ev->border_width;
295 wc.sibling = ev->above;
296 wc.stack_mode = ev->detail;
297 XConfigureWindow(e->xany.display, ev->window, ev->value_mask, &wc);
299 XSync(e->xany.display, False);
302 void
303 handle_event_configurenotify(XEvent * e, awesome_config *awesomeconf)
305 XConfigureEvent *ev = &e->xconfigure;
306 int screen;
307 ScreenInfo *si;
309 for(screen = 0; screen < ScreenCount(e->xany.display); screen++)
310 if(ev->window == RootWindow(e->xany.display, screen)
311 && (ev->width != DisplayWidth(e->xany.display, screen)
312 || ev->height != DisplayHeight(e->xany.display, screen)))
314 DisplayWidth(e->xany.display, screen) = ev->width;
315 DisplayHeight(e->xany.display, screen) = ev->height;
317 /* update statusbar */
318 XFreePixmap(e->xany.display, awesomeconf[screen].statusbar.drawable);
320 si = get_screen_info(e->xany.display, screen, NULL);
321 awesomeconf[screen].statusbar.width = si[screen].width;
322 p_delete(&si);
324 awesomeconf[screen].statusbar.drawable = XCreatePixmap(e->xany.display, RootWindow(e->xany.display, screen),
325 awesomeconf[screen].statusbar.width,
326 awesomeconf[screen].statusbar.height,
327 DefaultDepth(e->xany.display, screen));
328 XResizeWindow(e->xany.display,
329 awesomeconf[screen].statusbar.window,
330 awesomeconf[screen].statusbar.width,
331 awesomeconf[screen].statusbar.height);
333 updatebarpos(e->xany.display, awesomeconf[screen].statusbar);
334 arrange(&awesomeconf[screen]);
338 void
339 handle_event_destroynotify(XEvent * e, awesome_config *awesomeconf)
341 Client *c;
342 XDestroyWindowEvent *ev = &e->xdestroywindow;
344 if((c = get_client_bywin(*awesomeconf->clients, ev->window)))
345 client_unmanage(c, WithdrawnState, &awesomeconf[c->screen]);
348 void
349 handle_event_enternotify(XEvent * e, awesome_config *awesomeconf)
351 Client *c;
352 XCrossingEvent *ev = &e->xcrossing;
353 int screen;
355 if(ev->mode != NotifyNormal || ev->detail == NotifyInferior)
356 return;
357 if((c = get_client_bywin(*awesomeconf->clients, ev->window)))
359 focus(c, ev->same_screen, &awesomeconf[c->screen]);
360 if (c->isfloating
361 || get_current_layout(awesomeconf[c->screen].tags,
362 awesomeconf[c->screen].ntags)->arrange == layout_floating)
363 window_grabbuttons(c->display, c->phys_screen, c->win,
364 True, False, awesomeconf->modkey, awesomeconf->numlockmask);
366 else
367 for(screen = 0; screen < ScreenCount(e->xany.display); screen++)
368 if(ev->window == RootWindow(e->xany.display, screen))
369 focus(NULL, True, &awesomeconf[screen]);
372 void
373 handle_event_expose(XEvent * e, awesome_config *awesomeconf)
375 XExposeEvent *ev = &e->xexpose;
376 int screen;
378 if(!ev->count)
379 for(screen = 0; screen < get_screen_count(e->xany.display); screen++)
380 if(awesomeconf[screen].statusbar.window == ev->window)
381 drawstatusbar(&awesomeconf[screen]);
384 void
385 handle_event_keypress(XEvent * e, awesome_config *awesomeconf)
387 int i, screen, x, y, d;
388 unsigned int m;
389 KeySym keysym;
390 XKeyEvent *ev = &e->xkey;
391 Window dummy;
393 keysym = XKeycodeToKeysym(e->xany.display, (KeyCode) ev->keycode, 0);
395 /* find the right screen for this event */
396 for(screen = 0; screen < ScreenCount(e->xany.display); screen++)
397 if(XQueryPointer(e->xany.display, RootWindow(e->xany.display, screen), &dummy, &dummy, &x, &y, &d, &d, &m))
399 /* if screen is 0, we are on first Zaphod screen or on the
400 * only screen in Xinerama, so we can ask for a better screen
401 * number with get_screen_bycoord: we'll get 0 in Zaphod mode
402 * so it's the same, or maybe the real Xinerama screen */
403 if(screen == 0)
404 screen = get_screen_bycoord(e->xany.display, x, y);
405 break;
408 for(i = 0; i < awesomeconf[screen].nkeys; i++)
409 if(keysym == awesomeconf[screen].keys[i].keysym
410 && CLEANMASK(awesomeconf[screen].keys[i].mod, screen) == CLEANMASK(ev->state, screen) && awesomeconf[screen].keys[i].func)
411 awesomeconf[screen].keys[i].func(&awesomeconf[screen], awesomeconf[screen].keys[i].arg);
414 void
415 handle_event_leavenotify(XEvent * e, awesome_config *awesomeconf)
417 XCrossingEvent *ev = &e->xcrossing;
418 int screen;
420 for(screen = 0; screen < ScreenCount(e->xany.display); screen++)
421 if((ev->window == RootWindow(e->xany.display, screen)) && !ev->same_screen)
422 focus(NULL, ev->same_screen, &awesomeconf[screen]);
425 void
426 handle_event_mappingnotify(XEvent * e, awesome_config *awesomeconf)
428 XMappingEvent *ev = &e->xmapping;
429 int screen;
431 XRefreshKeyboardMapping(ev);
432 if(ev->request == MappingKeyboard)
433 for(screen = 0; screen < ScreenCount(e->xany.display); screen++)
434 grabkeys(&awesomeconf[screen]);
437 void
438 handle_event_maprequest(XEvent * e, awesome_config *awesomeconf)
440 static XWindowAttributes wa;
441 XMapRequestEvent *ev = &e->xmaprequest;
442 int screen, x, y, d;
443 unsigned int m;
444 Window dummy;
446 if(!XGetWindowAttributes(e->xany.display, ev->window, &wa))
447 return;
448 if(wa.override_redirect)
449 return;
450 if(!get_client_bywin(*awesomeconf->clients, ev->window))
452 for(screen = 0; wa.screen != ScreenOfDisplay(e->xany.display, screen); screen++);
453 if(screen == 0)
455 if(XQueryPointer(e->xany.display, RootWindow(e->xany.display, screen),
456 &dummy, &dummy, &x, &y, &d, &d, &m))
457 screen = get_screen_bycoord(e->xany.display, x, y);
460 client_manage(ev->window, &wa, &awesomeconf[screen]);
464 void
465 handle_event_propertynotify(XEvent * e, awesome_config *awesomeconf)
467 Client *c;
468 Window trans;
469 XPropertyEvent *ev = &e->xproperty;
471 if(ev->state == PropertyDelete)
472 return; /* ignore */
473 if((c = get_client_bywin(*awesomeconf->clients, ev->window)))
475 switch (ev->atom)
477 case XA_WM_TRANSIENT_FOR:
478 XGetTransientForHint(e->xany.display, c->win, &trans);
479 if(!c->isfloating && (c->isfloating = (get_client_bywin(*awesomeconf->clients, trans) != NULL)))
480 arrange(&awesomeconf[c->screen]);
481 break;
482 case XA_WM_NORMAL_HINTS:
483 updatesizehints(c);
484 break;
486 if(ev->atom == XA_WM_NAME || ev->atom == XInternAtom(c->display, "_NET_WM_NAME", False))
488 updatetitle(c);
489 if(c == get_current_tag(awesomeconf->tags, awesomeconf->ntags)->client_sel)
490 drawstatusbar(&awesomeconf[c->screen]);
495 void
496 handle_event_unmapnotify(XEvent * e, awesome_config *awesomeconf)
498 Client *c;
499 XUnmapEvent *ev = &e->xunmap;
501 if((c = get_client_bywin(*awesomeconf->clients, ev->window))
502 && ev->event == RootWindow(e->xany.display, c->phys_screen)
503 && ev->send_event && window_getstate(c->display, c->win) == NormalState)
504 client_unmanage(c, WithdrawnState, &awesomeconf[c->screen]);
507 void
508 handle_event_shape(XEvent * e,
509 awesome_config *awesomeconf __attribute__ ((unused)))
511 XShapeEvent *ev = (XShapeEvent *) e;
512 Client *c = get_client_bywin(*awesomeconf->clients, ev->window);
514 if(c)
515 window_setshape(c->display, c->phys_screen, c->win);
518 void
519 handle_event_randr_screen_change_notify(XEvent *e,
520 awesome_config *awesomeconf __attribute__ ((unused)))
522 XRRUpdateConfiguration(e);
525 void
526 grabkeys(awesome_config *awesomeconf)
528 int i;
529 KeyCode code;
531 XUngrabKey(awesomeconf->display, AnyKey, AnyModifier, RootWindow(awesomeconf->display, awesomeconf->phys_screen));
532 for(i = 0; i < awesomeconf->nkeys; i++)
534 if((code = XKeysymToKeycode(awesomeconf->display, awesomeconf->keys[i].keysym)) == NoSymbol)
535 continue;
536 XGrabKey(awesomeconf->display, code, awesomeconf->keys[i].mod, RootWindow(awesomeconf->display, awesomeconf->phys_screen), True, GrabModeAsync, GrabModeAsync);
537 XGrabKey(awesomeconf->display, code, awesomeconf->keys[i].mod | LockMask, RootWindow(awesomeconf->display, awesomeconf->phys_screen), True, GrabModeAsync, GrabModeAsync);
538 XGrabKey(awesomeconf->display, code, awesomeconf->keys[i].mod | awesomeconf->numlockmask, RootWindow(awesomeconf->display, awesomeconf->phys_screen), True, GrabModeAsync, GrabModeAsync);
539 XGrabKey(awesomeconf->display, code, awesomeconf->keys[i].mod | awesomeconf->numlockmask | LockMask, RootWindow(awesomeconf->display, awesomeconf->phys_screen), True,
540 GrabModeAsync, GrabModeAsync);
543 // vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99