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