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