]> icculus.org git repositories - dana/openbox.git/blob - src/xeventhandler.cc
maps and unmaps windows!
[dana/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 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   // handle any requested states such as shaded/maximized
172 }
173
174 static void unmanageWindow(OBClient *client)
175 {
176   bool remap = false; // remap the window when we're done?
177   
178   Window window = client->window();
179
180   // XXX: pass around focus if this window was focused
181   
182   // remove the window from our save set
183   XChangeSaveSet(otk::OBDisplay::display, window, SetModeDelete);
184
185   // we dont want events no more
186   XSelectInput(otk::OBDisplay::display, window, NoEventMask);
187
188   // XXX: XUnmapWindow(otk::OBDisplay::display, FRAME);
189   XUnmapWindow(otk::OBDisplay::display, window);
190   
191   // we dont want a border on the client
192   XSetWindowBorderWidth(otk::OBDisplay::display, window,client->borderWidth());
193
194   // remove the client class from the search list
195   Openbox::instance->removeClient(window);
196
197   // check if the app has already reparented its window to the root window
198   XEvent ev;
199   if (XCheckTypedWindowEvent(otk::OBDisplay::display, window, ReparentNotify,
200                              &ev)) {
201     remap = true; // XXX: why do we remap the window if they already
202                   // reparented to root?
203   } else {
204     // according to the ICCCM - if the client doesn't reparent to
205     // root, then we have to do it for them
206     XReparentWindow(otk::OBDisplay::display, window,
207                     RootWindow(otk::OBDisplay::display,
208                                DefaultScreen(otk::OBDisplay::display)),
209                     // XXX: screen->getRootWindow(),
210                     client->area().x(), client->area().y());
211   }
212
213   // if we want to remap the window, do so now
214   if (remap)
215     XMapWindow(otk::OBDisplay::display, window);
216
217   delete client;
218 }
219
220 void OBXEventHandler::mapRequest(const XMapRequestEvent &e)
221 {
222 #ifdef    DEBUG
223   printf("MapRequest for 0x%lx\n", e.window);
224 #endif // DEBUG
225
226   OBClient *client = Openbox::instance->findClient(e.window);
227
228   if (client) {
229     // XXX: uniconify and/or unshade the window
230   } else {
231     manageWindow(e.window);
232   }
233   
234 /*
235   BlackboxWindow *win = searchWindow(e->xmaprequest.window);
236
237   if (win) {
238     bool focus = False;
239     if (win->isIconic()) {
240       win->deiconify();
241       focus = True;
242     }
243     if (win->isShaded()) {
244       win->shade();
245       focus = True;
246     }
247
248     if (focus && (win->isTransient() || win->getScreen()->doFocusNew()) &&
249         win->isVisible())
250       win->setInputFocus();
251   } else {
252     BScreen *screen = searchScreen(e->xmaprequest.parent);
253
254     if (! screen) {
255 */
256       /*
257         we got a map request for a window who's parent isn't root. this
258         can happen in only one circumstance:
259
260         a client window unmapped a managed window, and then remapped it
261         somewhere between unmapping the client window and reparenting it
262         to root.
263
264         regardless of how it happens, we need to find the screen that
265         the window is on
266       */
267 /*
268       XWindowAttributes wattrib;
269       if (! XGetWindowAttributes(otk::OBDisplay::display,
270                                  e->xmaprequest.window,
271                                  &wattrib)) {
272         // failed to get the window attributes, perhaps the window has
273         // now been destroyed?
274         break;
275       }
276
277       screen = searchScreen(wattrib.root);
278       assert(screen != 0); // this should never happen
279     }
280     screen->manageWindow(e->xmaprequest.window);
281   }
282 */
283 }
284
285
286 void OBXEventHandler::unmapNotify(const XUnmapEvent &e)
287 {
288   OBClient *client = Openbox::instance->findClient(e.window);
289   if (!client) return;
290   
291   unmanageWindow(client);
292 }
293
294
295 void OBXEventHandler::destroyNotify(const XDestroyWindowEvent &e)
296 {
297   // XXX: window group leaders can come through here too!
298   
299   OBClient *client = Openbox::instance->findClient(e.window);
300   if (!client) return;
301   
302   unmanageWindow(client);
303 }
304
305
306 void OBXEventHandler::reparentNotify(const XReparentEvent &e)
307 {
308   /*
309     this event is quite rare and is usually handled in unmapNotify
310     however, if the window is unmapped when the reparent event occurs
311     the window manager never sees it because an unmap event is not sent
312     to an already unmapped window.
313   */
314   OBClient *client = Openbox::instance->findClient(e.window);
315   if (!client) return;
316
317 /*
318   BlackboxWindow *win = searchWindow(e->xreparent.window);
319   if (win)
320     win->reparentNotifyEvent(&e->xreparent);
321 */
322 }
323
324
325 void OBXEventHandler::propertyNotify(const XPropertyEvent &e)
326 {
327   _lasttime = e.time;
328
329   OBClient *client = Openbox::instance->findClient(e.window);
330   if (!client) return;
331
332   client->update(e);
333 }
334
335
336 void OBXEventHandler::expose(const XExposeEvent &first)
337 {
338   OBClient *client = Openbox::instance->findClient(first.window);
339   if (!client) return;
340
341   // compress expose events
342   XEvent e; e.xexpose = first;
343   unsigned int i = 0;
344   otk::Rect area(e.xexpose.x, e.xexpose.y, e.xexpose.width,
345                  e.xexpose.height);
346   while (XCheckTypedWindowEvent(otk::OBDisplay::display,
347                                 e.xexpose.window, Expose, &e)) {
348     i++;
349     // merge expose area
350     area |= otk::Rect(e.xexpose.x, e.xexpose.y, e.xexpose.width,
351                       e.xexpose.height);
352   }
353   if ( i > 0 ) {
354     // use the merged area
355     e.xexpose.x = area.x();
356     e.xexpose.y = area.y();
357     e.xexpose.width = area.width();
358     e.xexpose.height = area.height();
359   }
360
361   // XXX: make the decorations redraw!
362 }
363
364
365 void OBXEventHandler::colormapNotify(const XColormapEvent &e)
366 {
367   (void)e;
368 /*
369   BScreen *screen = searchScreen(e->xcolormap.window);
370   if (screen)
371     screen->setRootColormapInstalled((e->xcolormap.state ==
372                                       ColormapInstalled) ? True : False);
373 */
374 }
375
376
377 void OBXEventHandler::focusIn(const XFocusChangeEvent &e)
378 {
379   if (e.detail != NotifyNonlinear &&
380       e.detail != NotifyAncestor) {
381     /*
382       don't process FocusIns when:
383       1. the new focus window isn't an ancestor or inferior of the old
384       focus window (NotifyNonlinear)
385       make sure to allow the FocusIn when the old focus window was an
386       ancestor but didn't have a parent, such as root (NotifyAncestor)
387     */
388     return;
389   }
390 /*
391   BlackboxWindow *win = searchWindow(e.window);
392   if (win) {
393     if (! win->isFocused())
394       win->setFocusFlag(True);
395 */
396     /*
397       set the event window to None.  when the FocusOut event handler calls
398       this function recursively, it uses this as an indication that focus
399       has moved to a known window.
400     */
401 /*
402     e->xfocus.window = None;
403
404     no_focus = False;   // focusing is back on
405   }
406 */
407 }
408
409
410 void OBXEventHandler::focusOut(const XFocusChangeEvent &e)
411 {
412   if (e.detail != NotifyNonlinear) {
413     /*
414       don't process FocusOuts when:
415       2. the new focus window isn't an ancestor or inferior of the old
416       focus window (NotifyNonlinear)
417     */
418     return;
419   }
420
421 /*
422   BlackboxWindow *win = searchWindow(e->xfocus.window);
423   if (win && win->isFocused()) {
424 */
425     /*
426       before we mark "win" as unfocused, we need to verify that focus is
427       going to a known location, is in a known location, or set focus
428       to a known location.
429     */
430 /*
431     XEvent event;
432     // don't check the current focus if FocusOut was generated during a grab
433     bool check_focus = (e->xfocus.mode == NotifyNormal);
434 */
435     /*
436       First, check if there is a pending FocusIn event waiting.  if there
437       is, process it and determine if focus has moved to another window
438       (the FocusIn event handler sets the window in the event
439       structure to None to indicate this).
440     */
441 /*
442     if (XCheckTypedEvent(otk::OBDisplay::display, FocusIn, &event)) {
443
444       process_event(&event);
445       if (event.xfocus.window == None) {
446         // focus has moved
447         check_focus = False;
448       }
449     }
450
451     if (check_focus) {
452 */
453       /*
454         Second, we query the X server for the current input focus.
455         to make sure that we keep a consistent state.
456       */
457 /*
458       BlackboxWindow *focus;
459       Window w;
460       int revert;
461       XGetInputFocus(otk::OBDisplay::display, &w, &revert);
462       focus = searchWindow(w);
463       if (focus) {
464 */
465         /*
466           focus got from "win" to "focus" under some very strange
467           circumstances, and we need to make sure that the focus indication
468           is correct.
469         */
470 /*
471         setFocusedWindow(focus);
472       } else {
473         // we have no idea where focus went... so we set it to somewhere
474         setFocusedWindow(0);
475       }
476     }
477   }
478 */
479 }
480
481
482 #ifdef    SHAPE
483 void OBXEventHandler::shapeEvent(const XShapeEvent &e)
484 {
485   XShapeEvent *shape_event = (XShapeEvent *) e;
486   BlackboxWindow *win = searchWindow(e->xany.window);
487
488   if (win && shape_event->kind == ShapeBounding)
489     win->shapeEvent(shape_event);
490 }
491 #endif // SHAPE
492
493
494 void OBXEventHandler::clientMessage(const XClientMessageEvent &e)
495 {
496   if (e.format != 32)
497     return;
498 /*  
499   } else if (e->xclient.message_type == 
500              xatom->getAtom(XAtom::blackbox_change_workspace) || 
501              e->xclient.message_type == 
502              xatom->getAtom(XAtom::net_current_desktop)) {
503     // NET_CURRENT_DESKTOP message
504     BScreen *screen = searchScreen(e->xclient.window);
505
506     unsigned int workspace = e->xclient.data.l[0];
507     if (screen && workspace < screen->getWorkspaceCount())
508       screen->changeWorkspaceID(workspace);
509   } else if (e->xclient.message_type == 
510              xatom->getAtom(XAtom::net_active_window)) {
511     // NET_ACTIVE_WINDOW
512     BlackboxWindow *win = searchWindow(e->xclient.window);
513
514     if (win) {
515       BScreen *screen = win->getScreen();
516
517       if (win->isIconic())
518         win->deiconify(False, False);
519       if (! win->isStuck() &&
520           (win->getWorkspaceNumber() != screen->getCurrentWorkspaceID())) {
521         no_focus = True;
522         screen->changeWorkspaceID(win->getWorkspaceNumber());
523       }
524       if (win->isVisible() && win->setInputFocus()) {
525         win->getScreen()->getWorkspace(win->getWorkspaceNumber())->
526           raiseWindow(win);
527         win->installColormap(True);
528       }
529     }
530   } else if (e->xclient.message_type == 
531              xatom->getAtom(XAtom::net_number_of_desktops)) {
532     // NET_NUMBER_OF_DESKTOPS
533     BScreen *screen = searchScreen(e->xclient.window);
534         
535     if (e->xclient.data.l[0] > 0)
536       screen->changeWorkspaceCount((unsigned) e->xclient.data.l[0]);
537   } else if (e->xclient.message_type ==
538              xatom->getAtom(XAtom::net_close_window)) {
539     // NET_CLOSE_WINDOW
540     BlackboxWindow *win = searchWindow(e->xclient.window);
541     if (win && win->validateClient())
542       win->close(); // could this be smarter?
543   } else if (e->xclient.message_type ==
544              xatom->getAtom(XAtom::net_wm_moveresize)) {
545     // NET_WM_MOVERESIZE
546     BlackboxWindow *win = searchWindow(e->xclient.window);
547     if (win && win->validateClient()) {
548       int x_root = e->xclient.data.l[0],
549         y_root = e->xclient.data.l[1];
550       if ((Atom) e->xclient.data.l[2] ==
551           xatom->getAtom(XAtom::net_wm_moveresize_move)) {
552         win->beginMove(x_root, y_root);
553       } else {
554         if ((Atom) e->xclient.data.l[2] ==
555             xatom->getAtom(XAtom::net_wm_moveresize_size_topleft))
556           win->beginResize(x_root, y_root, BlackboxWindow::TopLeft);
557         else if ((Atom) e->xclient.data.l[2] ==
558                  xatom->getAtom(XAtom::net_wm_moveresize_size_topright))
559           win->beginResize(x_root, y_root, BlackboxWindow::TopRight);
560         else if ((Atom) e->xclient.data.l[2] ==
561                  xatom->getAtom(XAtom::net_wm_moveresize_size_bottomleft))
562           win->beginResize(x_root, y_root, BlackboxWindow::BottomLeft);
563         else if ((Atom) e->xclient.data.l[2] ==
564                  xatom->getAtom(XAtom::net_wm_moveresize_size_bottomright))
565           win->beginResize(x_root, y_root, BlackboxWindow::BottomRight);
566       }
567     }
568   }
569 */
570 }
571
572
573 void OBXEventHandler::handle(const XEvent &e)
574 {
575   /* mouse button events can get translated into:
576        press - button was pressed down
577        release - buttons was released
578        click - button was pressed and released on the same window
579        double click - clicked twice on the same widget in a given time and area
580
581      key events are only bindable to presses. key releases are ignored.
582
583      mouse enter/leave can be bound to for the entire window
584   */
585   
586   switch (e.type) {
587
588     // These types of XEvent's can be bound to actions by the user, and so end
589     // up getting passed off to the OBBindingMapper class at some point
590   case ButtonPress:
591     buttonPress(e.xbutton);
592     break;
593   case ButtonRelease:
594     buttonRelease(e.xbutton);
595     break;
596   case KeyPress:
597     keyPress(e.xkey);
598     break;
599   case MotionNotify:
600     motion(e.xmotion);
601     break;
602   case EnterNotify:
603     enterNotify(e.xcrossing);
604     break;
605   case LeaveNotify:
606     leaveNotify(e.xcrossing);
607     break;
608
609
610     // These types of XEvent's can not be bound to actions by the user and so
611     // will simply be handled in this class
612   case ConfigureRequest:
613     configureRequest(e.xconfigurerequest);
614     break;
615
616   case MapRequest:
617     mapRequest(e.xmaprequest);
618     break;
619     
620   case UnmapNotify:
621     unmapNotify(e.xunmap);
622     break;
623
624   case DestroyNotify:
625     destroyNotify(e.xdestroywindow);
626     break;
627
628   case ReparentNotify:
629     reparentNotify(e.xreparent);
630     break;
631
632   case PropertyNotify:
633     propertyNotify(e.xproperty);
634     break;
635
636   case Expose:
637     expose(e.xexpose);
638     break;
639
640   case ColormapNotify:
641     colormapNotify(e.xcolormap);
642     break;
643     
644   case FocusIn:
645     focusIn(e.xfocus);
646     break;
647
648   case FocusOut:
649     focusOut(e.xfocus);
650     break;
651
652   case ClientMessage:
653     clientMessage(e.xclient);
654
655   default:
656 #ifdef    SHAPE
657     if (e.type == otk::OBDisplay::shapeEventBase())
658       shapeEvent(e);
659 #endif // SHAPE
660     break;
661     
662 /*
663   case ClientMessage: {
664     break;
665   }
666
667 */
668   } // switch
669 }
670
671
672 }