]> icculus.org git repositories - mikachu/openbox.git/blob - src/xeventhandler.cc
handle events on all of the client's decor as for the client
[mikachu/openbox.git] / src / xeventhandler.cc
1 // -*- mode: C++; indent-tabs-mode: nil; -*-
2
3 #ifdef HAVE_CONFIG_H
4 # include "../config.h"
5 #endif
6
7 #include "xeventhandler.hh"
8 #include "client.hh"
9 #include "frame.hh"
10 #include "openbox.hh"
11 #include "otk/display.hh"
12 #include "otk/rect.hh"
13
14 // XXX: REMOVE THIS SOON!!#!
15 #include "blackbox.hh"
16 #include "screen.hh"
17
18 extern "C" {
19 #include <X11/Xlib.h>
20 #include <X11/Xutil.h>
21 }
22
23 namespace ob {
24
25
26 OBXEventHandler::OBXEventHandler()
27 {
28   _lasttime = 1; // 0 is CurrentTime, so set to minimum
29 }
30
31 void OBXEventHandler::buttonPress(const XButtonEvent &e)
32 {
33   _lasttime = e.time;
34
35 }
36
37
38 void OBXEventHandler::buttonRelease(const XButtonEvent &e)
39 {
40   _lasttime = e.time;
41
42 }
43
44
45 void OBXEventHandler::keyPress(const XKeyEvent &e)
46 {
47   _lasttime = e.time;
48 }
49
50
51 void OBXEventHandler::motion(const XMotionEvent &e)
52 {
53   _lasttime = e.time;
54
55   // the pointer is on the wrong screen
56   if (! e.same_screen) return;
57
58 }
59
60
61 void OBXEventHandler::enterNotify(const XCrossingEvent &e)
62 {
63   _lasttime = e.time;
64
65   OBClient *client = Openbox::instance->findClient(e.window);
66   if (!client) return;
67   
68 /*
69   BScreen *screen = (BScreen *) 0;
70   BlackboxWindow *win = (BlackboxWindow *) 0;
71
72   if (e->xcrossing.mode == NotifyGrab) break;
73
74   if ((e->xcrossing.window == e->xcrossing.root) &&
75       (screen = searchScreen(e->xcrossing.window))) {
76     screen->getImageControl()->installRootColormap();
77   } else if ((win = searchWindow(e->xcrossing.window))) {
78     if (! no_focus)
79       win->enterNotifyEvent(&e->xcrossing);
80   }
81 */
82 }
83
84
85 void OBXEventHandler::leaveNotify(const XCrossingEvent &e)
86 {
87   _lasttime = e.time;
88
89   OBClient *client = Openbox::instance->findClient(e.window);
90   if (!client) return;
91   
92 /*
93   BlackboxWindow *win = (BlackboxWindow *) 0;
94
95   if ((win = searchWindow(e->xcrossing.window)))
96     win->leaveNotifyEvent(&e->xcrossing);
97 */
98 }
99
100
101 void OBXEventHandler::configureRequest(const XConfigureRequestEvent &e)
102 {
103   OBClient *client = Openbox::instance->findClient(e.window);
104   if (!client) return;
105   
106 /*  BlackboxWindow *win = (BlackboxWindow *) 0;
107
108   if ((win = searchWindow(e->xconfigurerequest.window))) {
109     win->configureRequestEvent(&e->xconfigurerequest);
110   } else {
111     if (validateWindow(e->xconfigurerequest.window)) {
112       XWindowChanges xwc;
113
114       xwc.x = e->xconfigurerequest.x;
115       xwc.y = e->xconfigurerequest.y;
116       xwc.width = e->xconfigurerequest.width;
117       xwc.height = e->xconfigurerequest.height;
118       xwc.border_width = e->xconfigurerequest.border_width;
119       xwc.sibling = e->xconfigurerequest.above;
120       xwc.stack_mode = e->xconfigurerequest.detail;
121
122       XConfigureWindow(otk::OBDisplay::display, e->xconfigurerequest.window,
123                        e->xconfigurerequest.value_mask, &xwc);
124     }
125   }
126 */
127 }
128
129
130 // XXX: put this into the OBScreen or OBClient class!
131 void OBXEventHandler::manageWindow(int screen, Window window)
132 {
133   OBClient *client = 0;
134   XWMHints *wmhint;
135   XSetWindowAttributes attrib_set;
136
137   // XXX: manage the window, i.e. grab events n shit
138
139   // is the window a docking app
140   if ((wmhint = XGetWMHints(otk::OBDisplay::display, window))) {
141     if ((wmhint->flags & StateHint) &&
142         wmhint->initial_state == WithdrawnState) {
143       //slit->addClient(w); // XXX: make dock apps work!
144       XFree(wmhint);
145       return;
146     }
147     XFree(wmhint);
148   }
149
150   // choose the events we want to receive on the CLIENT window
151   attrib_set.event_mask = OBClient::event_mask;
152   attrib_set.do_not_propagate_mask = ButtonPressMask | ButtonReleaseMask |
153                                      ButtonMotionMask;
154   XChangeWindowAttributes(otk::OBDisplay::display, window,
155                           CWEventMask|CWDontPropagate, &attrib_set);
156
157   // create the OBClient class, which gets all of the hints on the window
158   Openbox::instance->addClient(window, client = new OBClient(screen, window));
159   // add all the client's decoration windows as event handlers for the client
160   Openbox::instance->addClient(client->frame->titlebar(), client);
161   Openbox::instance->addClient(client->frame->buttonIconify(), client);
162   Openbox::instance->addClient(client->frame->buttonMax(), client);
163   Openbox::instance->addClient(client->frame->buttonStick(), client);
164   Openbox::instance->addClient(client->frame->buttonClose(), client);
165   Openbox::instance->addClient(client->frame->label(), client);
166   Openbox::instance->addClient(client->frame->handle(), client);
167   Openbox::instance->addClient(client->frame->gripLeft(), client);
168   Openbox::instance->addClient(client->frame->gripRight(), client);
169
170   // we dont want a border on the client
171   XSetWindowBorderWidth(otk::OBDisplay::display, window, 0);
172   
173   // specify that if we exit, the window should not be destroyed and should be
174   // reparented back to root automatically
175   XChangeSaveSet(otk::OBDisplay::display, window, SetModeInsert);
176
177   if (!client->positionRequested()) {
178     // XXX: position the window intelligenty
179   }
180
181   // XXX: store a style somewheres cooler!!
182   otk::Style *style = ((Blackbox*)Openbox::instance)->
183     searchScreen(RootWindow(otk::OBDisplay::display, screen))->
184     getWindowStyle();
185   client->frame = new OBFrame(client, style);
186   
187   // XXX: if on the current desktop..
188   XMapWindow(otk::OBDisplay::display, client->frame->window());
189  
190   // XXX: handle any requested states such as shaded/maximized
191 }
192
193 // XXX: move this to the OBScreen or OBClient class!
194 void OBXEventHandler::unmanageWindow(OBClient *client)
195 {
196   OBFrame *frame = client->frame;
197
198   // XXX: pass around focus if this window was focused
199   
200   // remove the window from our save set
201   XChangeSaveSet(otk::OBDisplay::display, client->window(), SetModeDelete);
202
203   // we dont want events no more
204   XSelectInput(otk::OBDisplay::display, client->window(), NoEventMask);
205
206   XUnmapWindow(otk::OBDisplay::display, frame->window());
207   
208   // we dont want a border on the client
209   XSetWindowBorderWidth(otk::OBDisplay::display, client->window(),
210                         client->borderWidth());
211
212   // remove the client class from the search list
213   Openbox::instance->removeClient(client->window());
214   // remove the frame's decor elements as event handlers for the client
215   Openbox::instance->removeClient(frame->titlebar());
216   Openbox::instance->removeClient(frame->buttonIconify());
217   Openbox::instance->removeClient(frame->buttonMax());
218   Openbox::instance->removeClient(frame->buttonStick());
219   Openbox::instance->removeClient(frame->buttonClose());
220   Openbox::instance->removeClient(frame->label());
221   Openbox::instance->removeClient(frame->handle());
222   Openbox::instance->removeClient(frame->gripLeft());
223   Openbox::instance->removeClient(frame->gripRight());
224
225   delete client->frame;
226   client->frame = 0;
227
228   delete client;
229 }
230
231 void OBXEventHandler::mapRequest(const XMapRequestEvent &e)
232 {
233 #ifdef    DEBUG
234   printf("MapRequest for 0x%lx\n", e.window);
235 #endif // DEBUG
236
237   OBClient *client = Openbox::instance->findClient(e.window);
238
239   if (client) {
240     // XXX: uniconify and/or unshade the window
241   } else {
242     int screen = INT_MAX;
243
244     for (int i = 0; i < ScreenCount(otk::OBDisplay::display); ++i)
245       if (otk::OBDisplay::screenInfo(i)->getRootWindow() == e.parent) {
246         screen = i;
247         break;
248       }
249
250     if (screen >= ScreenCount(otk::OBDisplay::display)) {
251       /*
252         we got a map request for a window who's parent isn't root. this
253         can happen in only one circumstance:
254
255         a client window unmapped a managed window, and then remapped it
256         somewhere between unmapping the client window and reparenting it
257         to root.
258
259         regardless of how it happens, we need to find the screen that
260         the window is on
261       */
262       XWindowAttributes wattrib;
263       if (! XGetWindowAttributes(otk::OBDisplay::display, e.window,
264                                  &wattrib)) {
265         // failed to get the window attributes, perhaps the window has
266         // now been destroyed?
267         return;
268       }
269
270       for (int i = 0; i < ScreenCount(otk::OBDisplay::display); ++i)
271         if (otk::OBDisplay::screenInfo(i)->getRootWindow() == wattrib.root) {
272           screen = i;
273           break;
274         }
275     }
276
277     assert(screen < ScreenCount(otk::OBDisplay::display));
278
279     manageWindow(screen, e.window);
280   }
281   
282 /*
283   BlackboxWindow *win = searchWindow(e->xmaprequest.window);
284
285   if (win) {
286     bool focus = False;
287     if (win->isIconic()) {
288       win->deiconify();
289       focus = True;
290     }
291     if (win->isShaded()) {
292       win->shade();
293       focus = True;
294     }
295
296     if (focus && (win->isTransient() || win->getScreen()->doFocusNew()) &&
297         win->isVisible())
298       win->setInputFocus();
299   } else {
300     BScreen *screen = searchScreen(e->xmaprequest.parent);
301
302     if (! screen) {
303 */
304       /*
305         we got a map request for a window who's parent isn't root. this
306         can happen in only one circumstance:
307
308         a client window unmapped a managed window, and then remapped it
309         somewhere between unmapping the client window and reparenting it
310         to root.
311
312         regardless of how it happens, we need to find the screen that
313         the window is on
314       */
315 /*
316       XWindowAttributes wattrib;
317       if (! XGetWindowAttributes(otk::OBDisplay::display,
318                                  e->xmaprequest.window,
319                                  &wattrib)) {
320         // failed to get the window attributes, perhaps the window has
321         // now been destroyed?
322         break;
323       }
324
325       screen = searchScreen(wattrib.root);
326       assert(screen != 0); // this should never happen
327     }
328     screen->manageWindow(e->xmaprequest.window);
329   }
330 */
331 }
332
333
334 void OBXEventHandler::unmapNotify(const XUnmapEvent &e)
335 {
336   OBClient *client = Openbox::instance->findClient(e.window);
337   if (!client) return;
338   
339   unmanageWindow(client);
340 }
341
342
343 void OBXEventHandler::destroyNotify(const XDestroyWindowEvent &e)
344 {
345   // XXX: window group leaders can come through here too!
346   
347   OBClient *client = Openbox::instance->findClient(e.window);
348   if (!client) return;
349   
350   unmanageWindow(client);
351 }
352
353
354 void OBXEventHandler::reparentNotify(const XReparentEvent &e)
355 {
356   /*
357     this event is quite rare and is usually handled in unmapNotify
358     however, if the window is unmapped when the reparent event occurs
359     the window manager never sees it because an unmap event is not sent
360     to an already unmapped window.
361   */
362   OBClient *client = Openbox::instance->findClient(e.window);
363   if (!client) return;
364
365 /*
366   BlackboxWindow *win = searchWindow(e->xreparent.window);
367   if (win)
368     win->reparentNotifyEvent(&e->xreparent);
369 */
370 }
371
372
373 void OBXEventHandler::propertyNotify(const XPropertyEvent &e)
374 {
375   _lasttime = e.time;
376
377   OBClient *client = Openbox::instance->findClient(e.window);
378   if (!client) return;
379
380   client->update(e);
381 }
382
383
384 void OBXEventHandler::expose(const XExposeEvent &first)
385 {
386   OBClient *client = Openbox::instance->findClient(first.window);
387   if (!client) return;
388
389   // compress expose events
390   XEvent e; e.xexpose = first;
391   unsigned int i = 0;
392   otk::Rect area(e.xexpose.x, e.xexpose.y, e.xexpose.width,
393                  e.xexpose.height);
394   while (XCheckTypedWindowEvent(otk::OBDisplay::display,
395                                 e.xexpose.window, Expose, &e)) {
396     i++;
397     // merge expose area
398     area |= otk::Rect(e.xexpose.x, e.xexpose.y, e.xexpose.width,
399                       e.xexpose.height);
400   }
401   if ( i > 0 ) {
402     // use the merged area
403     e.xexpose.x = area.x();
404     e.xexpose.y = area.y();
405     e.xexpose.width = area.width();
406     e.xexpose.height = area.height();
407   }
408
409   // XXX: make the decorations redraw!
410 }
411
412
413 void OBXEventHandler::colormapNotify(const XColormapEvent &e)
414 {
415   (void)e;
416 /*
417   BScreen *screen = searchScreen(e->xcolormap.window);
418   if (screen)
419     screen->setRootColormapInstalled((e->xcolormap.state ==
420                                       ColormapInstalled) ? True : False);
421 */
422 }
423
424
425 void OBXEventHandler::focusIn(const XFocusChangeEvent &e)
426 {
427   if (e.detail != NotifyNonlinear &&
428       e.detail != NotifyAncestor) {
429     /*
430       don't process FocusIns when:
431       1. the new focus window isn't an ancestor or inferior of the old
432       focus window (NotifyNonlinear)
433       make sure to allow the FocusIn when the old focus window was an
434       ancestor but didn't have a parent, such as root (NotifyAncestor)
435     */
436     return;
437   }
438 /*
439   BlackboxWindow *win = searchWindow(e.window);
440   if (win) {
441     if (! win->isFocused())
442       win->setFocusFlag(True);
443 */
444     /*
445       set the event window to None.  when the FocusOut event handler calls
446       this function recursively, it uses this as an indication that focus
447       has moved to a known window.
448     */
449 /*
450     e->xfocus.window = None;
451
452     no_focus = False;   // focusing is back on
453   }
454 */
455 }
456
457
458 void OBXEventHandler::focusOut(const XFocusChangeEvent &e)
459 {
460   if (e.detail != NotifyNonlinear) {
461     /*
462       don't process FocusOuts when:
463       2. the new focus window isn't an ancestor or inferior of the old
464       focus window (NotifyNonlinear)
465     */
466     return;
467   }
468
469 /*
470   BlackboxWindow *win = searchWindow(e->xfocus.window);
471   if (win && win->isFocused()) {
472 */
473     /*
474       before we mark "win" as unfocused, we need to verify that focus is
475       going to a known location, is in a known location, or set focus
476       to a known location.
477     */
478 /*
479     XEvent event;
480     // don't check the current focus if FocusOut was generated during a grab
481     bool check_focus = (e->xfocus.mode == NotifyNormal);
482 */
483     /*
484       First, check if there is a pending FocusIn event waiting.  if there
485       is, process it and determine if focus has moved to another window
486       (the FocusIn event handler sets the window in the event
487       structure to None to indicate this).
488     */
489 /*
490     if (XCheckTypedEvent(otk::OBDisplay::display, FocusIn, &event)) {
491
492       process_event(&event);
493       if (event.xfocus.window == None) {
494         // focus has moved
495         check_focus = False;
496       }
497     }
498
499     if (check_focus) {
500 */
501       /*
502         Second, we query the X server for the current input focus.
503         to make sure that we keep a consistent state.
504       */
505 /*
506       BlackboxWindow *focus;
507       Window w;
508       int revert;
509       XGetInputFocus(otk::OBDisplay::display, &w, &revert);
510       focus = searchWindow(w);
511       if (focus) {
512 */
513         /*
514           focus got from "win" to "focus" under some very strange
515           circumstances, and we need to make sure that the focus indication
516           is correct.
517         */
518 /*
519         setFocusedWindow(focus);
520       } else {
521         // we have no idea where focus went... so we set it to somewhere
522         setFocusedWindow(0);
523       }
524     }
525   }
526 */
527 }
528
529
530 #ifdef    SHAPE
531 void OBXEventHandler::shapeEvent(const XShapeEvent &e)
532 {
533   printf("ShapeEvent\n");
534   if (e.kind != ShapeBounding) return;
535
536   OBClient *client = Openbox::instance->findClient(e.window);
537   if (!client) return;
538
539   client->update(e);
540   client->frame->update();
541 }
542 #endif // SHAPE
543
544
545 void OBXEventHandler::clientMessage(const XClientMessageEvent &e)
546 {
547   if (e.format != 32)
548     return;
549 /*  
550   } else if (e->xclient.message_type == 
551              xatom->getAtom(XAtom::blackbox_change_workspace) || 
552              e->xclient.message_type == 
553              xatom->getAtom(XAtom::net_current_desktop)) {
554     // NET_CURRENT_DESKTOP message
555     BScreen *screen = searchScreen(e->xclient.window);
556
557     unsigned int workspace = e->xclient.data.l[0];
558     if (screen && workspace < screen->getWorkspaceCount())
559       screen->changeWorkspaceID(workspace);
560   } else if (e->xclient.message_type == 
561              xatom->getAtom(XAtom::net_active_window)) {
562     // NET_ACTIVE_WINDOW
563     BlackboxWindow *win = searchWindow(e->xclient.window);
564
565     if (win) {
566       BScreen *screen = win->getScreen();
567
568       if (win->isIconic())
569         win->deiconify(False, False);
570       if (! win->isStuck() &&
571           (win->getWorkspaceNumber() != screen->getCurrentWorkspaceID())) {
572         no_focus = True;
573         screen->changeWorkspaceID(win->getWorkspaceNumber());
574       }
575       if (win->isVisible() && win->setInputFocus()) {
576         win->getScreen()->getWorkspace(win->getWorkspaceNumber())->
577           raiseWindow(win);
578         win->installColormap(True);
579       }
580     }
581   } else if (e->xclient.message_type == 
582              xatom->getAtom(XAtom::net_number_of_desktops)) {
583     // NET_NUMBER_OF_DESKTOPS
584     BScreen *screen = searchScreen(e->xclient.window);
585         
586     if (e->xclient.data.l[0] > 0)
587       screen->changeWorkspaceCount((unsigned) e->xclient.data.l[0]);
588   } else if (e->xclient.message_type ==
589              xatom->getAtom(XAtom::net_close_window)) {
590     // NET_CLOSE_WINDOW
591     BlackboxWindow *win = searchWindow(e->xclient.window);
592     if (win && win->validateClient())
593       win->close(); // could this be smarter?
594   } else if (e->xclient.message_type ==
595              xatom->getAtom(XAtom::net_wm_moveresize)) {
596     // NET_WM_MOVERESIZE
597     BlackboxWindow *win = searchWindow(e->xclient.window);
598     if (win && win->validateClient()) {
599       int x_root = e->xclient.data.l[0],
600         y_root = e->xclient.data.l[1];
601       if ((Atom) e->xclient.data.l[2] ==
602           xatom->getAtom(XAtom::net_wm_moveresize_move)) {
603         win->beginMove(x_root, y_root);
604       } else {
605         if ((Atom) e->xclient.data.l[2] ==
606             xatom->getAtom(XAtom::net_wm_moveresize_size_topleft))
607           win->beginResize(x_root, y_root, BlackboxWindow::TopLeft);
608         else if ((Atom) e->xclient.data.l[2] ==
609                  xatom->getAtom(XAtom::net_wm_moveresize_size_topright))
610           win->beginResize(x_root, y_root, BlackboxWindow::TopRight);
611         else if ((Atom) e->xclient.data.l[2] ==
612                  xatom->getAtom(XAtom::net_wm_moveresize_size_bottomleft))
613           win->beginResize(x_root, y_root, BlackboxWindow::BottomLeft);
614         else if ((Atom) e->xclient.data.l[2] ==
615                  xatom->getAtom(XAtom::net_wm_moveresize_size_bottomright))
616           win->beginResize(x_root, y_root, BlackboxWindow::BottomRight);
617       }
618     }
619   }
620 */
621 }
622
623
624 void OBXEventHandler::handle(const XEvent &e)
625 {
626   /* mouse button events can get translated into:
627        press - button was pressed down
628        release - buttons was released
629        click - button was pressed and released on the same window
630        double click - clicked twice on the same widget in a given time and area
631
632      key events are only bindable to presses. key releases are ignored.
633
634      mouse enter/leave can be bound to for the entire window
635   */
636   
637   switch (e.type) {
638
639     // These types of XEvent's can be bound to actions by the user, and so end
640     // up getting passed off to the OBBindingMapper class at some point
641     // IOW: THESE WILL HAVE GUILE HOOKS
642   case ButtonPress:
643     buttonPress(e.xbutton);
644     break;
645   case ButtonRelease:
646     buttonRelease(e.xbutton);
647     break;
648   case KeyPress:
649     keyPress(e.xkey);
650     break;
651   case MotionNotify:
652     motion(e.xmotion);
653     break;
654   case EnterNotify:
655     enterNotify(e.xcrossing);
656     break;
657   case LeaveNotify:
658     leaveNotify(e.xcrossing);
659     break;
660
661
662     // These types of XEvent's can not be bound to actions by the user and so
663     // will simply be handled in this class
664   case ConfigureRequest:
665     configureRequest(e.xconfigurerequest);
666     break;
667
668   case MapRequest:
669     mapRequest(e.xmaprequest);
670     break;
671     
672   case UnmapNotify:
673     unmapNotify(e.xunmap);
674     break;
675
676   case DestroyNotify:
677     destroyNotify(e.xdestroywindow);
678     break;
679
680   case ReparentNotify:
681     reparentNotify(e.xreparent);
682     break;
683
684   case PropertyNotify:
685     propertyNotify(e.xproperty);
686     break;
687
688   case Expose:
689     expose(e.xexpose);
690     break;
691
692   case ColormapNotify:
693     colormapNotify(e.xcolormap);
694     break;
695     
696   case FocusIn:
697     focusIn(e.xfocus);
698     break;
699
700   case FocusOut:
701     focusOut(e.xfocus);
702     break;
703
704   case ClientMessage:
705     clientMessage(e.xclient);
706
707   default:
708 #ifdef    SHAPE
709     if (e.type == otk::OBDisplay::shapeEventBase())
710       shapeEvent((*(XShapeEvent*)&e));
711 #endif // SHAPE
712     break;
713     
714 /*
715   case ClientMessage: {
716     break;
717   }
718
719 */
720   } // switch
721 }
722
723
724 }