]> icculus.org git repositories - dana/openbox.git/blob - src/actions.cc
compress motion events better
[dana/openbox.git] / src / actions.cc
1 // -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*-
2
3 #ifdef HAVE_CONFIG_H
4 # include "../config.h"
5 #endif
6
7 #include "actions.hh"
8 #include "widgetbase.hh"
9 #include "openbox.hh"
10 #include "client.hh"
11 #include "screen.hh"
12 #include "python.hh"
13 #include "bindings.hh"
14 #include "otk/display.hh"
15
16 #include <stdio.h>
17 #include <algorithm>
18
19 namespace ob {
20
21 const int Actions::BUTTONS;
22
23 Actions::Actions()
24   : _button(0),
25     _dragging(false)
26 {
27   for (int i=0; i<BUTTONS; ++i)
28     _posqueue[i] = new ButtonPressAction();
29 }
30
31
32 Actions::~Actions()
33 {
34   for (int i=0; i<BUTTONS; ++i)
35     delete _posqueue[i];
36 }
37
38
39 void Actions::insertPress(const XButtonEvent &e)
40 {
41   ButtonPressAction *a = _posqueue[BUTTONS - 1];
42   // rm'd the last one, shift them all down one
43   for (int i = BUTTONS-1; i > 0; --i) {
44     _posqueue[i] = _posqueue[i-1];
45   }
46   _posqueue[0] = a;
47   a->button = e.button;
48   a->pos = otk::Point(e.x_root, e.y_root);
49
50   Client *c = openbox->findClient(e.window);
51   if (c) a->clientarea = c->area();
52 }
53
54 void Actions::removePress(const XButtonEvent &e)
55 {
56   int i;
57   ButtonPressAction *a = 0;
58   for (i=0; i<BUTTONS-1; ++i)
59     if (_posqueue[i]->button == e.button) {
60       a = _posqueue[i];
61       break;
62     }
63   if (a) { // found one, remove it and shift the rest up one
64     for (; i < BUTTONS-1; ++i)
65       _posqueue[i] = _posqueue[i+1];
66     _posqueue[BUTTONS-1] = a;
67   }
68   _posqueue[BUTTONS-1]->button = 0;
69 }
70
71 void Actions::buttonPressHandler(const XButtonEvent &e)
72 {
73   otk::EventHandler::buttonPressHandler(e);
74   insertPress(e);
75   
76   // run the PRESS python hook
77   WidgetBase *w = dynamic_cast<WidgetBase*>
78     (openbox->findHandler(e.window));
79   if (!w) return;
80
81   // kill off the Button1Mask etc, only want the modifiers
82   unsigned int state = e.state & (ControlMask | ShiftMask | Mod1Mask |
83                                   Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask);
84   int screen;
85   Client *c = openbox->findClient(e.window);
86   if (c)
87     screen = c->screen();
88   else
89     screen = otk::display->findScreen(e.root)->screen();
90   MouseData data(screen, c, e.time, state, e.button, w->mcontext(),
91                  MouseAction::Press);
92   openbox->bindings()->fireButton(&data);
93     
94   if (_button) return; // won't count toward CLICK events
95
96   _button = e.button;
97
98   if (w->mcontext() == MouseContext::Window) {
99     /*
100       Because of how events are grabbed on the client window, we can't get
101       ButtonRelease events, so instead we simply manufacture them here, so that
102       clicks/doubleclicks etc still work.
103     */
104     //XButtonEvent ev = e;
105     //ev.type = ButtonRelease;
106     buttonReleaseHandler(e);
107   }
108 }
109   
110
111 void Actions::buttonReleaseHandler(const XButtonEvent &e)
112 {
113   otk::EventHandler::buttonReleaseHandler(e);
114   removePress(e);
115   
116   WidgetBase *w = dynamic_cast<WidgetBase*>
117     (openbox->findHandler(e.window));
118   if (!w) return;
119
120   // run the RELEASE python hook
121   // kill off the Button1Mask etc, only want the modifiers
122   unsigned int state = e.state & (ControlMask | ShiftMask | Mod1Mask |
123                                   Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask);
124   int screen;
125   Client *c = openbox->findClient(e.window);
126   if (c)
127     screen = c->screen();
128   else
129     screen = otk::display->findScreen(e.root)->screen();
130   MouseData data(screen, c, e.time, state, e.button, w->mcontext(),
131                  MouseAction::Release);
132   openbox->bindings()->fireButton(&data);
133
134   // not for the button we're watching?
135   if (_button != e.button) return;
136
137   _button = 0;
138   _dragging = false;
139
140   // find the area of the window
141   XWindowAttributes attr;
142   if (!XGetWindowAttributes(**otk::display, e.window, &attr)) return;
143
144   // if not on the window any more, it isnt a CLICK
145   if (!(e.same_screen && e.x >= 0 && e.y >= 0 &&
146         e.x < attr.width && e.y < attr.height))
147     return;
148
149   // run the CLICK python hook
150   data.action = MouseAction::Click;
151   openbox->bindings()->fireButton(&data);
152     
153
154   // XXX: dont load this every time!!@*
155   long dblclick;
156   if (!python_get_long("DOUBLE_CLICK_DELAY", &dblclick))
157     dblclick = 300;
158
159   if (e.time - _release.time < (unsigned)dblclick &&
160       _release.win == e.window && _release.button == e.button) {
161
162     // run the DOUBLECLICK python hook
163     data.action = MouseAction::DoubleClick;
164     openbox->bindings()->fireButton(&data);
165     
166     // reset so you cant triple click for 2 doubleclicks
167     _release.win = 0;
168     _release.button = 0;
169     _release.time = 0;
170   } else {
171     // save the button release, might be part of a double click
172     _release.win = e.window;
173     _release.button = e.button;
174     _release.time = e.time;
175   }
176 }
177
178
179 void Actions::enterHandler(const XCrossingEvent &e)
180 {
181   otk::EventHandler::enterHandler(e);
182   
183   // run the ENTER python hook
184   int screen;
185   Client *c = openbox->findClient(e.window);
186   if (c)
187     screen = c->screen();
188   else
189     screen = otk::display->findScreen(e.root)->screen();
190   EventData data(screen, c, EventAction::EnterWindow, e.state);
191   openbox->bindings()->fireEvent(&data);
192 }
193
194
195 void Actions::leaveHandler(const XCrossingEvent &e)
196 {
197   otk::EventHandler::leaveHandler(e);
198
199   // run the LEAVE python hook
200   int screen;
201   Client *c = openbox->findClient(e.window);
202   if (c)
203     screen = c->screen();
204   else
205     screen = otk::display->findScreen(e.root)->screen();
206   EventData data(screen, c, EventAction::LeaveWindow, e.state);
207   openbox->bindings()->fireEvent(&data);
208 }
209
210
211 void Actions::keyPressHandler(const XKeyEvent &e)
212 {
213   otk::EventHandler::keyPressHandler(e);
214
215   // kill off the Button1Mask etc, only want the modifiers
216   unsigned int state = e.state & (ControlMask | ShiftMask | Mod1Mask |
217                                   Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask);
218   openbox->bindings()->
219     fireKey(otk::display->findScreen(e.root)->screen(),
220             state, e.keycode, e.time, KeyAction::Press);
221 }
222
223
224 void Actions::keyReleaseHandler(const XKeyEvent &e)
225 {
226   otk::EventHandler::keyReleaseHandler(e);
227
228   // kill off the Button1Mask etc, only want the modifiers
229   unsigned int state = e.state & (ControlMask | ShiftMask | Mod1Mask |
230                                   Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask);
231
232   // remove from the state the mask of the modifier being released, if it is
233   // a modifier key being released (this is a little ugly..)
234   const XModifierKeymap *map = otk::display->modifierMap();
235   const int mask_table[] = {
236     ShiftMask, LockMask, ControlMask, Mod1Mask,
237     Mod2Mask, Mod3Mask, Mod4Mask, Mod5Mask
238   };
239   KeyCode *kp = map->modifiermap;
240   for (int i = 0, n = sizeof(mask_table)/sizeof(mask_table[0]); i < n; ++i) {
241     for (int k = 0; k < map->max_keypermod; ++k) {
242       if (*kp == e.keycode) { // found the keycode
243         state &= ~mask_table[i]; // remove the mask for it
244         i = n; // cause the first loop to break;
245         break; // get outta here!
246       }
247       ++kp;
248     }
249   }
250   
251   openbox->bindings()->
252     fireKey(otk::display->findScreen(e.root)->screen(),
253             state, e.keycode, e.time, KeyAction::Release);
254 }
255
256
257 void Actions::motionHandler(const XMotionEvent &e)
258 {
259   otk::EventHandler::motionHandler(e);
260
261   if (!e.same_screen) return; // this just gets stupid
262
263   int x_root = e.x_root, y_root = e.y_root;
264   
265   // compress changes to a window into a single change
266   XEvent ce;
267   while (XCheckTypedWindowEvent(**otk::display, e.window, e.type, &ce)) {
268     x_root = e.x_root;
269     y_root = e.y_root;
270   }
271
272   WidgetBase *w = dynamic_cast<WidgetBase*>
273     (openbox->findHandler(e.window));
274   if (!w) return;
275
276   if (!_dragging) {
277     long threshold;
278     int dx = x_root - _posqueue[0]->pos.x();
279     int dy = y_root - _posqueue[0]->pos.y();
280     // XXX: dont get this from python every time!
281     if (!python_get_long("DRAG_THRESHOLD", &threshold))
282       threshold = 0;
283     if (!(std::abs(dx) >= threshold || std::abs(dy) >= threshold))
284       return; // not at the threshold yet
285   }
286   _dragging = true; // in a drag now
287   
288   // check if the movement is more than the threshold
289
290   // run the MOTION python hook
291   // kill off the Button1Mask etc, only want the modifiers
292   unsigned int state = e.state & (ControlMask | ShiftMask | Mod1Mask |
293                                   Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask);
294   unsigned int button = _posqueue[0]->button;
295   int screen;
296   Client *c = openbox->findClient(e.window);
297   if (c)
298     screen = c->screen();
299   else
300     screen = otk::display->findScreen(e.root)->screen();
301   MouseData data(screen, c, e.time, state, button, w->mcontext(),
302                  MouseAction::Motion, x_root, y_root,
303                  _posqueue[0]->pos, _posqueue[0]->clientarea);
304   openbox->bindings()->fireButton(&data);
305 }
306
307 #ifdef    XKB
308 void Actions::xkbHandler(const XkbEvent &e)
309 {
310   Window w;
311   int screen;
312   
313   otk::EventHandler::xkbHandler(e);
314
315   switch (((XkbAnyEvent*)&e)->xkb_type) {
316   case XkbBellNotify:
317     w = ((XkbBellNotifyEvent*)&e)->window;
318     Client *c = openbox->findClient(w);
319     if (c)
320       screen = c->screen();
321     else
322       screen = openbox->focusedScreen()->number();
323     EventData data(screen, c, EventAction::Bell, 0);
324     openbox->bindings()->fireEvent(&data);
325     break;
326   }
327 }
328 #endif // XKB
329
330 }
331