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