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