]> icculus.org git repositories - dana/openbox.git/blob - src/actions.cc
No longer using otk widgets for the frame decorations.
[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 "openbox.hh"
9 #include "client.hh"
10 #include "frame.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   MouseContext::MC context;
77   EventHandler *h = openbox->findHandler(e.window);
78   Frame *f = dynamic_cast<Frame*>(h);
79   if (f)
80     context= f->mouseContext(e.window);
81   else if (dynamic_cast<Client*>(h))
82     context = MouseContext::Window;
83   else if (dynamic_cast<Screen*>(h))
84     context = MouseContext::Root;
85   else
86     return; // not a valid mouse context
87
88   // run the PRESS python hook
89   // kill off the Button1Mask etc, only want the modifiers
90   unsigned int state = e.state & (ControlMask | ShiftMask | Mod1Mask |
91                                   Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask);
92   int screen;
93   Client *c = openbox->findClient(e.window);
94   if (c)
95     screen = c->screen();
96   else
97     screen = otk::display->findScreen(e.root)->screen();
98   MouseData data(screen, c, e.time, state, e.button, context,
99                  MouseAction::Press);
100   openbox->bindings()->fireButton(&data);
101     
102   if (_button) return; // won't count toward CLICK events
103
104   _button = e.button;
105
106   if (context == MouseContext::Window) {
107     /*
108       Because of how events are grabbed on the client window, we can't get
109       ButtonRelease events, so instead we simply manufacture them here, so that
110       clicks/doubleclicks etc still work.
111     */
112     //XButtonEvent ev = e;
113     //ev.type = ButtonRelease;
114     buttonReleaseHandler(e);
115   }
116 }
117   
118
119 void Actions::buttonReleaseHandler(const XButtonEvent &e)
120 {
121   otk::EventHandler::buttonReleaseHandler(e);
122   removePress(e);
123   
124   MouseContext::MC context;
125   EventHandler *h = openbox->findHandler(e.window);
126   Frame *f = dynamic_cast<Frame*>(h);
127   if (f)
128     context= f->mouseContext(e.window);
129   else if (dynamic_cast<Client*>(h))
130     context = MouseContext::Window;
131   else if (dynamic_cast<Screen*>(h))
132     context = MouseContext::Root;
133   else
134     return; // not a valid mouse context
135
136   // run the RELEASE python hook
137   // kill off the Button1Mask etc, only want the modifiers
138   unsigned int state = e.state & (ControlMask | ShiftMask | Mod1Mask |
139                                   Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask);
140   int screen;
141   Client *c = openbox->findClient(e.window);
142   if (c)
143     screen = c->screen();
144   else
145     screen = otk::display->findScreen(e.root)->screen();
146   MouseData data(screen, c, e.time, state, e.button, context,
147                  MouseAction::Release);
148   openbox->bindings()->fireButton(&data);
149
150   // not for the button we're watching?
151   if (_button != e.button) return;
152
153   _button = 0;
154   _dragging = false;
155
156   // find the area of the window
157   XWindowAttributes attr;
158   if (!XGetWindowAttributes(**otk::display, e.window, &attr)) return;
159
160   // if not on the window any more, it isnt a CLICK
161   if (!(e.same_screen && e.x >= 0 && e.y >= 0 &&
162         e.x < attr.width && e.y < attr.height))
163     return;
164
165   // run the CLICK python hook
166   data.action = MouseAction::Click;
167   openbox->bindings()->fireButton(&data);
168     
169
170   // XXX: dont load this every time!!@*
171   long dblclick;
172   if (!python_get_long("DOUBLE_CLICK_DELAY", &dblclick))
173     dblclick = 300;
174
175   if (e.time - _release.time < (unsigned)dblclick &&
176       _release.win == e.window && _release.button == e.button) {
177
178     // run the DOUBLECLICK python hook
179     data.action = MouseAction::DoubleClick;
180     openbox->bindings()->fireButton(&data);
181     
182     // reset so you cant triple click for 2 doubleclicks
183     _release.win = 0;
184     _release.button = 0;
185     _release.time = 0;
186   } else {
187     // save the button release, might be part of a double click
188     _release.win = e.window;
189     _release.button = e.button;
190     _release.time = e.time;
191   }
192 }
193
194
195 void Actions::enterHandler(const XCrossingEvent &e)
196 {
197   otk::EventHandler::enterHandler(e);
198   
199   // run the ENTER 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::EnterWindow, e.state);
207   openbox->bindings()->fireEvent(&data);
208 }
209
210
211 void Actions::leaveHandler(const XCrossingEvent &e)
212 {
213   otk::EventHandler::leaveHandler(e);
214
215   // run the LEAVE python hook
216   int screen;
217   Client *c = openbox->findClient(e.window);
218   if (c)
219     screen = c->screen();
220   else
221     screen = otk::display->findScreen(e.root)->screen();
222   EventData data(screen, c, EventAction::LeaveWindow, e.state);
223   openbox->bindings()->fireEvent(&data);
224 }
225
226
227 void Actions::keyPressHandler(const XKeyEvent &e)
228 {
229   otk::EventHandler::keyPressHandler(e);
230
231   // kill off the Button1Mask etc, only want the modifiers
232   unsigned int state = e.state & (ControlMask | ShiftMask | Mod1Mask |
233                                   Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask);
234   openbox->bindings()->
235     fireKey(otk::display->findScreen(e.root)->screen(),
236             state, e.keycode, e.time, KeyAction::Press);
237 }
238
239
240 void Actions::keyReleaseHandler(const XKeyEvent &e)
241 {
242   otk::EventHandler::keyReleaseHandler(e);
243
244   // kill off the Button1Mask etc, only want the modifiers
245   unsigned int state = e.state & (ControlMask | ShiftMask | Mod1Mask |
246                                   Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask);
247
248   // remove from the state the mask of the modifier being released, if it is
249   // a modifier key being released (this is a little ugly..)
250   const XModifierKeymap *map = otk::display->modifierMap();
251   const int mask_table[] = {
252     ShiftMask, LockMask, ControlMask, Mod1Mask,
253     Mod2Mask, Mod3Mask, Mod4Mask, Mod5Mask
254   };
255   KeyCode *kp = map->modifiermap;
256   for (int i = 0, n = sizeof(mask_table)/sizeof(mask_table[0]); i < n; ++i) {
257     for (int k = 0; k < map->max_keypermod; ++k) {
258       if (*kp == e.keycode) { // found the keycode
259         state &= ~mask_table[i]; // remove the mask for it
260         i = n; // cause the first loop to break;
261         break; // get outta here!
262       }
263       ++kp;
264     }
265   }
266   
267   openbox->bindings()->
268     fireKey(otk::display->findScreen(e.root)->screen(),
269             state, e.keycode, e.time, KeyAction::Release);
270 }
271
272
273 void Actions::motionHandler(const XMotionEvent &e)
274 {
275   otk::EventHandler::motionHandler(e);
276
277   if (!e.same_screen) return; // this just gets stupid
278
279   MouseContext::MC context;
280   EventHandler *h = openbox->findHandler(e.window);
281   Frame *f = dynamic_cast<Frame*>(h);
282   if (f)
283     context= f->mouseContext(e.window);
284   else if (dynamic_cast<Client*>(h))
285     context = MouseContext::Window;
286   else if (dynamic_cast<Screen*>(h))
287     context = MouseContext::Root;
288   else
289     return; // not a valid mouse context
290
291   int x_root = e.x_root, y_root = e.y_root;
292   
293   // compress changes to a window into a single change
294   XEvent ce;
295   while (XCheckTypedWindowEvent(**otk::display, e.window, e.type, &ce)) {
296     x_root = e.x_root;
297     y_root = e.y_root;
298   }
299
300   if (!_dragging) {
301     long threshold;
302     int dx = x_root - _posqueue[0]->pos.x();
303     int dy = y_root - _posqueue[0]->pos.y();
304     // XXX: dont get this from python every time!
305     if (!python_get_long("DRAG_THRESHOLD", &threshold))
306       threshold = 0;
307     if (!(std::abs(dx) >= threshold || std::abs(dy) >= threshold))
308       return; // not at the threshold yet
309   }
310   _dragging = true; // in a drag now
311   
312   // check if the movement is more than the threshold
313
314   // run the MOTION python hook
315   // kill off the Button1Mask etc, only want the modifiers
316   unsigned int state = e.state & (ControlMask | ShiftMask | Mod1Mask |
317                                   Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask);
318   unsigned int button = _posqueue[0]->button;
319   int screen;
320   Client *c = openbox->findClient(e.window);
321   if (c)
322     screen = c->screen();
323   else
324     screen = otk::display->findScreen(e.root)->screen();
325   MouseData data(screen, c, e.time, state, button, context,
326                  MouseAction::Motion, x_root, y_root,
327                  _posqueue[0]->pos, _posqueue[0]->clientarea);
328   openbox->bindings()->fireButton(&data);
329 }
330
331 #ifdef    XKB
332 void Actions::xkbHandler(const XkbEvent &e)
333 {
334   Window w;
335   int screen;
336   
337   otk::EventHandler::xkbHandler(e);
338
339   switch (((XkbAnyEvent*)&e)->xkb_type) {
340   case XkbBellNotify:
341     w = ((XkbBellNotifyEvent*)&e)->window;
342     Client *c = openbox->findClient(w);
343     if (c)
344       screen = c->screen();
345     else
346       screen = openbox->focusedScreen()->number();
347     EventData data(screen, c, EventAction::Bell, 0);
348     openbox->bindings()->fireEvent(&data);
349     break;
350   }
351 }
352 #endif // XKB
353
354 }
355