add a skeletal OBActions class for user actions
[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   if (client->ignore_unmaps == 0)
236     Openbox::instance->screen(client->screen())->unmanageWindow(client);
237   else
238     client->ignore_unmaps--;
239 }
240
241
242 void OBXEventHandler::destroyNotify(const XDestroyWindowEvent &e)
243 {
244   // XXX: window group leaders can come through here too!
245   
246   OBClient *client = Openbox::instance->findClient(e.window);
247   if (!client) return;
248   
249   Openbox::instance->screen(client->screen())->unmanageWindow(client);
250 }
251
252
253 void OBXEventHandler::reparentNotify(const XReparentEvent &e)
254 {
255   /*
256     this event is quite rare and is usually handled in unmapNotify
257     however, if the window is unmapped when the reparent event occurs
258     the window manager never sees it because an unmap event is not sent
259     to an already unmapped window.
260   */
261   OBClient *client = Openbox::instance->findClient(e.window);
262   if (!client) return;
263
264 /*
265   BlackboxWindow *win = searchWindow(e->xreparent.window);
266   if (win)
267     win->reparentNotifyEvent(&e->xreparent);
268 */
269 }
270
271
272 void OBXEventHandler::propertyNotify(const XPropertyEvent &e)
273 {
274   _lasttime = e.time;
275
276   OBClient *client = Openbox::instance->findClient(e.window);
277   if (!client) return;
278
279   client->update(e);
280 }
281
282
283 void OBXEventHandler::expose(const XExposeEvent &first)
284 {
285   OBClient *client = Openbox::instance->findClient(first.window);
286   if (!client) return;
287
288   // compress expose events
289   XEvent e; e.xexpose = first;
290   unsigned int i = 0;
291   otk::Rect area(e.xexpose.x, e.xexpose.y, e.xexpose.width,
292                  e.xexpose.height);
293   while (XCheckTypedWindowEvent(otk::OBDisplay::display,
294                                 e.xexpose.window, Expose, &e)) {
295     i++;
296     // merge expose area
297     area |= otk::Rect(e.xexpose.x, e.xexpose.y, e.xexpose.width,
298                       e.xexpose.height);
299   }
300   if ( i > 0 ) {
301     // use the merged area
302     e.xexpose.x = area.x();
303     e.xexpose.y = area.y();
304     e.xexpose.width = area.width();
305     e.xexpose.height = area.height();
306   }
307
308   // XXX: make the decorations redraw!
309 }
310
311
312 void OBXEventHandler::colormapNotify(const XColormapEvent &e)
313 {
314   (void)e;
315 /*
316   BScreen *screen = searchScreen(e->xcolormap.window);
317   if (screen)
318     screen->setRootColormapInstalled((e->xcolormap.state ==
319                                       ColormapInstalled) ? True : False);
320 */
321 }
322
323
324 void OBXEventHandler::focusIn(const XFocusChangeEvent &e)
325 {
326   if (e.detail != NotifyNonlinear &&
327       e.detail != NotifyAncestor) {
328     /*
329       don't process FocusIns when:
330       1. the new focus window isn't an ancestor or inferior of the old
331       focus window (NotifyNonlinear)
332       make sure to allow the FocusIn when the old focus window was an
333       ancestor but didn't have a parent, such as root (NotifyAncestor)
334     */
335     return;
336   }
337 /*
338   BlackboxWindow *win = searchWindow(e.window);
339   if (win) {
340     if (! win->isFocused())
341       win->setFocusFlag(True);
342 */
343     /*
344       set the event window to None.  when the FocusOut event handler calls
345       this function recursively, it uses this as an indication that focus
346       has moved to a known window.
347     */
348 /*
349     e->xfocus.window = None;
350
351     no_focus = False;   // focusing is back on
352   }
353 */
354 }
355
356
357 void OBXEventHandler::focusOut(const XFocusChangeEvent &e)
358 {
359   if (e.detail != NotifyNonlinear) {
360     /*
361       don't process FocusOuts when:
362       2. the new focus window isn't an ancestor or inferior of the old
363       focus window (NotifyNonlinear)
364     */
365     return;
366   }
367
368 /*
369   BlackboxWindow *win = searchWindow(e->xfocus.window);
370   if (win && win->isFocused()) {
371 */
372     /*
373       before we mark "win" as unfocused, we need to verify that focus is
374       going to a known location, is in a known location, or set focus
375       to a known location.
376     */
377 /*
378     XEvent event;
379     // don't check the current focus if FocusOut was generated during a grab
380     bool check_focus = (e->xfocus.mode == NotifyNormal);
381 */
382     /*
383       First, check if there is a pending FocusIn event waiting.  if there
384       is, process it and determine if focus has moved to another window
385       (the FocusIn event handler sets the window in the event
386       structure to None to indicate this).
387     */
388 /*
389     if (XCheckTypedEvent(otk::OBDisplay::display, FocusIn, &event)) {
390
391       process_event(&event);
392       if (event.xfocus.window == None) {
393         // focus has moved
394         check_focus = False;
395       }
396     }
397
398     if (check_focus) {
399 */
400       /*
401         Second, we query the X server for the current input focus.
402         to make sure that we keep a consistent state.
403       */
404 /*
405       BlackboxWindow *focus;
406       Window w;
407       int revert;
408       XGetInputFocus(otk::OBDisplay::display, &w, &revert);
409       focus = searchWindow(w);
410       if (focus) {
411 */
412         /*
413           focus got from "win" to "focus" under some very strange
414           circumstances, and we need to make sure that the focus indication
415           is correct.
416         */
417 /*
418         setFocusedWindow(focus);
419       } else {
420         // we have no idea where focus went... so we set it to somewhere
421         setFocusedWindow(0);
422       }
423     }
424   }
425 */
426 }
427
428
429 #ifdef    SHAPE
430 void OBXEventHandler::shapeEvent(const XShapeEvent &e)
431 {
432   printf("ShapeEvent\n");
433   if (e.kind != ShapeBounding) return;
434
435   OBClient *client = Openbox::instance->findClient(e.window);
436   if (!client) return;
437
438   client->update(e);
439   client->frame->update();
440 }
441 #endif // SHAPE
442
443
444 void OBXEventHandler::clientMessage(const XClientMessageEvent &e)
445 {
446   if (e.format != 32)
447     return;
448 /*  
449   } else if (e->xclient.message_type == 
450              xatom->getAtom(XAtom::blackbox_change_workspace) || 
451              e->xclient.message_type == 
452              xatom->getAtom(XAtom::net_current_desktop)) {
453     // NET_CURRENT_DESKTOP message
454     BScreen *screen = searchScreen(e->xclient.window);
455
456     unsigned int workspace = e->xclient.data.l[0];
457     if (screen && workspace < screen->getWorkspaceCount())
458       screen->changeWorkspaceID(workspace);
459   } else if (e->xclient.message_type == 
460              xatom->getAtom(XAtom::net_active_window)) {
461     // NET_ACTIVE_WINDOW
462     BlackboxWindow *win = searchWindow(e->xclient.window);
463
464     if (win) {
465       BScreen *screen = win->getScreen();
466
467       if (win->isIconic())
468         win->deiconify(False, False);
469       if (! win->isStuck() &&
470           (win->getWorkspaceNumber() != screen->getCurrentWorkspaceID())) {
471         no_focus = True;
472         screen->changeWorkspaceID(win->getWorkspaceNumber());
473       }
474       if (win->isVisible() && win->setInputFocus()) {
475         win->getScreen()->getWorkspace(win->getWorkspaceNumber())->
476           raiseWindow(win);
477         win->installColormap(True);
478       }
479     }
480   } else if (e->xclient.message_type == 
481              xatom->getAtom(XAtom::net_number_of_desktops)) {
482     // NET_NUMBER_OF_DESKTOPS
483     BScreen *screen = searchScreen(e->xclient.window);
484         
485     if (e->xclient.data.l[0] > 0)
486       screen->changeWorkspaceCount((unsigned) e->xclient.data.l[0]);
487   } else if (e->xclient.message_type ==
488              xatom->getAtom(XAtom::net_close_window)) {
489     // NET_CLOSE_WINDOW
490     BlackboxWindow *win = searchWindow(e->xclient.window);
491     if (win && win->validateClient())
492       win->close(); // could this be smarter?
493   } else if (e->xclient.message_type ==
494              xatom->getAtom(XAtom::net_wm_moveresize)) {
495     // NET_WM_MOVERESIZE
496     BlackboxWindow *win = searchWindow(e->xclient.window);
497     if (win && win->validateClient()) {
498       int x_root = e->xclient.data.l[0],
499         y_root = e->xclient.data.l[1];
500       if ((Atom) e->xclient.data.l[2] ==
501           xatom->getAtom(XAtom::net_wm_moveresize_move)) {
502         win->beginMove(x_root, y_root);
503       } else {
504         if ((Atom) e->xclient.data.l[2] ==
505             xatom->getAtom(XAtom::net_wm_moveresize_size_topleft))
506           win->beginResize(x_root, y_root, BlackboxWindow::TopLeft);
507         else if ((Atom) e->xclient.data.l[2] ==
508                  xatom->getAtom(XAtom::net_wm_moveresize_size_topright))
509           win->beginResize(x_root, y_root, BlackboxWindow::TopRight);
510         else if ((Atom) e->xclient.data.l[2] ==
511                  xatom->getAtom(XAtom::net_wm_moveresize_size_bottomleft))
512           win->beginResize(x_root, y_root, BlackboxWindow::BottomLeft);
513         else if ((Atom) e->xclient.data.l[2] ==
514                  xatom->getAtom(XAtom::net_wm_moveresize_size_bottomright))
515           win->beginResize(x_root, y_root, BlackboxWindow::BottomRight);
516       }
517     }
518   }
519 */
520 }
521
522
523 void OBXEventHandler::handle(const XEvent &e)
524 {
525   /* mouse button events can get translated into:
526        press - button was pressed down
527        release - buttons was released
528        click - button was pressed and released on the same window
529        double click - clicked twice on the same widget in a given time and area
530
531      key events are only bindable to presses. key releases are ignored.
532
533      mouse enter/leave can be bound to for the entire window
534   */
535   
536   switch (e.type) {
537
538     // These types of XEvent's can be bound to actions by the user, and so end
539     // up getting passed off to the OBBindingMapper class at some point
540     // IOW: THESE WILL HAVE GUILE HOOKS
541   case ButtonPress:
542     buttonPress(e.xbutton);
543     break;
544   case ButtonRelease:
545     buttonRelease(e.xbutton);
546     break;
547   case KeyPress:
548     keyPress(e.xkey);
549     break;
550   case MotionNotify:
551     motion(e.xmotion);
552     break;
553   case EnterNotify:
554     enterNotify(e.xcrossing);
555     break;
556   case LeaveNotify:
557     leaveNotify(e.xcrossing);
558     break;
559
560
561     // These types of XEvent's can not be bound to actions by the user and so
562     // will simply be handled in this class
563   case ConfigureRequest:
564     configureRequest(e.xconfigurerequest);
565     break;
566
567   case MapRequest:
568     mapRequest(e.xmaprequest);
569     break;
570     
571   case UnmapNotify:
572     unmapNotify(e.xunmap);
573     break;
574
575   case DestroyNotify:
576     destroyNotify(e.xdestroywindow);
577     break;
578
579   case ReparentNotify:
580     reparentNotify(e.xreparent);
581     break;
582
583   case PropertyNotify:
584     propertyNotify(e.xproperty);
585     break;
586
587   case Expose:
588     expose(e.xexpose);
589     break;
590
591   case ColormapNotify:
592     colormapNotify(e.xcolormap);
593     break;
594     
595   case FocusIn:
596     focusIn(e.xfocus);
597     break;
598
599   case FocusOut:
600     focusOut(e.xfocus);
601     break;
602
603   case ClientMessage:
604     clientMessage(e.xclient);
605
606   default:
607 #ifdef    SHAPE
608     if (e.type == otk::OBDisplay::shapeEventBase())
609       shapeEvent((*(XShapeEvent*)&e));
610 #endif // SHAPE
611     break;
612     
613 /*
614   case ClientMessage: {
615     break;
616   }
617
618 */
619   } // switch
620 }
621
622
623 }