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