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