1 #include "../../kernel/openbox.h"
2 #include "../../kernel/dispatch.h"
3 #include "../../kernel/action.h"
4 #include "../../kernel/event.h"
5 #include "../../kernel/client.h"
6 #include "../../kernel/frame.h"
7 #include "../../kernel/grab.h"
8 #include "../../kernel/engine.h"
9 #include "../../kernel/config.h"
10 #include "translate.h"
12 #include "mouserc_parse.h"
15 void plugin_setup_config()
17 config_def_set(config_def_new("mouse.dragThreshold", Config_Integer
,
19 "The drag threshold in pixels before a Drag "
21 config_def_set(config_def_new("mouse.doubleClickTime", Config_Integer
,
22 "Double Click Interval",
23 "The amount of time (in milliseconds) in "
24 "which two clicks must occur to cause a "
25 "DoubleClick event."));
28 /* GData of GSList*s of PointerBinding*s. */
29 static GData
*bound_contexts
;
31 struct foreach_grab_temp
{
36 static void foreach_grab(GQuark key
, gpointer data
, gpointer user_data
)
38 struct foreach_grab_temp
*d
= user_data
;
40 for (it
= data
; it
!= NULL
; it
= it
->next
) {
41 /* grab/ungrab the button */
42 MouseBinding
*b
= it
->data
;
47 if (key
== g_quark_try_string("frame")) {
48 win
= d
->client
->frame
->window
;
50 mask
= ButtonPressMask
| ButtonMotionMask
| ButtonReleaseMask
;
51 } else if (key
== g_quark_try_string("client")) {
52 win
= d
->client
->frame
->plate
;
53 mode
= GrabModeSync
; /* this is handled in event */
54 mask
= ButtonPressMask
; /* can't catch more than this with Sync
55 mode the release event is manufactured
60 grab_button(b
->button
, b
->state
, win
, mask
, mode
);
62 ungrab_button(b
->button
, b
->state
, win
);
66 static void grab_for_client(Client
*client
, gboolean grab
)
68 struct foreach_grab_temp bt
;
71 g_datalist_foreach(&bound_contexts
, foreach_grab
, &bt
);
74 static void grab_all_clients(gboolean grab
)
78 for (it
= client_list
; it
!= NULL
; it
= it
->next
)
79 grab_for_client(it
->data
, grab
);
82 static void foreach_clear(GQuark key
, gpointer data
, gpointer user_data
)
85 user_data
= user_data
;
86 for (it
= data
; it
!= NULL
; it
= it
->next
) {
89 MouseBinding
*b
= it
->data
;
90 for (i
= 0; i
< NUM_MOUSEACTION
; ++i
)
91 if (b
->action
[i
] != NULL
)
92 action_free(b
->action
[i
]);
98 static void fire_button(MouseAction a
, GQuark context
, Client
*c
, guint state
,
104 for (it
= g_datalist_id_get_data(&bound_contexts
, context
);
105 it
!= NULL
; it
= it
->next
) {
107 if (b
->state
== state
&& b
->button
== button
)
110 /* if not bound, then nothing to do! */
111 if (it
== NULL
) return;
113 if (b
->action
[a
] != NULL
&& b
->action
[a
]->func
!= NULL
) {
114 b
->action
[a
]->data
.any
.c
= c
;
116 g_assert(!(b
->action
[a
]->func
== action_move
||
117 b
->action
[a
]->func
== action_resize
));
119 b
->action
[a
]->func(&b
->action
[a
]->data
);
123 /* corner should be the opposite corner of the window in which the pointer
124 clicked, Corner_TopLeft if a good default if there is no client
125 Returns True or False for if a binding existed for the action or not.
127 static gboolean
fire_motion(MouseAction a
, GQuark context
, Client
*c
,
128 guint state
, guint button
, int cx
, int cy
,
129 int cw
, int ch
, int dx
, int dy
,
130 gboolean final
, Corner corner
)
135 for (it
= g_datalist_id_get_data(&bound_contexts
, context
);
136 it
!= NULL
; it
= it
->next
) {
138 if (b
->state
== state
&& b
->button
== button
)
141 /* if not bound, then nothing to do! */
142 if (it
== NULL
) return FALSE
;
144 if (b
->action
[a
] != NULL
&& b
->action
[a
]->func
!= NULL
) {
145 b
->action
[a
]->data
.any
.c
= c
;
147 if (b
->action
[a
]->func
== action_move
) {
148 b
->action
[a
]->data
.move
.x
= cx
+ dx
;
149 b
->action
[a
]->data
.move
.y
= cy
+ dy
;
150 b
->action
[a
]->data
.move
.final
= final
;
151 } else if (b
->action
[a
]->func
== action_resize
) {
152 b
->action
[a
]->data
.resize
.corner
= corner
;
155 b
->action
[a
]->data
.resize
.x
= cw
+ dx
;
156 b
->action
[a
]->data
.resize
.y
= ch
+ dy
;
158 case Corner_TopRight
:
159 b
->action
[a
]->data
.resize
.x
= cw
- dx
;
160 b
->action
[a
]->data
.resize
.y
= ch
+ dy
;
162 case Corner_BottomLeft
:
163 b
->action
[a
]->data
.resize
.x
= cw
+ dx
;
164 b
->action
[a
]->data
.resize
.y
= ch
- dy
;
166 case Corner_BottomRight
:
167 b
->action
[a
]->data
.resize
.x
= cw
- dx
;
168 b
->action
[a
]->data
.resize
.y
= ch
- dy
;
171 b
->action
[a
]->data
.resize
.final
= final
;
173 b
->action
[a
]->func(&b
->action
[a
]->data
);
179 static Corner
pick_corner(int x
, int y
, int cx
, int cy
, int cw
, int ch
)
181 if (x
- cx
< cw
/ 2) {
183 return Corner_BottomRight
;
185 return Corner_TopRight
;
188 return Corner_BottomLeft
;
190 return Corner_TopLeft
;
194 static void event(ObEvent
*e
, void *foo
)
197 static int px
, py
, cx
, cy
, cw
, ch
, dx
, dy
;
198 static guint button
= 0, lbutton
= 0;
199 static gboolean drag
= FALSE
, drag_used
= FALSE
;
200 static Corner corner
= Corner_TopLeft
;
201 ConfigValue doubleclicktime
;
202 ConfigValue dragthreshold
;
203 gboolean click
= FALSE
;
204 gboolean dclick
= FALSE
;
207 if (!config_get("mouse.dragThreshold", Config_Integer
, &dragthreshold
))
208 dragthreshold
.integer
= 3; /* default */
209 if (!config_get("mouse.doubleClickTime", Config_Integer
, &doubleclicktime
))
210 doubleclicktime
.integer
= 200; /* default */
213 case Event_Client_Mapped
:
214 grab_for_client(e
->data
.c
.client
, TRUE
);
217 case Event_Client_Destroy
:
218 grab_for_client(e
->data
.c
.client
, FALSE
);
221 case Event_X_ButtonPress
:
223 if (e
->data
.x
.client
!= NULL
) {
224 cx
= e
->data
.x
.client
->frame
->area
.x
;
225 cy
= e
->data
.x
.client
->frame
->area
.y
;
226 /* use the client size because the frame can be differently
227 sized (shaded windows) and we want this based on the clients
229 cw
= e
->data
.x
.client
->area
.width
+
230 e
->data
.x
.client
->frame
->size
.left
+
231 e
->data
.x
.client
->frame
->size
.right
;
232 ch
= e
->data
.x
.client
->area
.height
+
233 e
->data
.x
.client
->frame
->size
.top
+
234 e
->data
.x
.client
->frame
->size
.bottom
;
235 px
= e
->data
.x
.e
->xbutton
.x_root
;
236 py
= e
->data
.x
.e
->xbutton
.y_root
;
237 corner
= pick_corner(px
, py
, cx
, cy
, cw
, ch
);
239 button
= e
->data
.x
.e
->xbutton
.button
;
241 context
= engine_get_context(e
->data
.x
.client
,
242 e
->data
.x
.e
->xbutton
.window
);
244 fire_button(MouseAction_Press
, context
,
245 e
->data
.x
.client
, e
->data
.x
.e
->xbutton
.state
,
246 e
->data
.x
.e
->xbutton
.button
);
248 if (context
== g_quark_try_string("client")) {
249 /* Replay the event, so it goes to the client*/
250 XAllowEvents(ob_display
, ReplayPointer
, event_lasttime
);
251 /* Fall through to the release case! */
255 case Event_X_ButtonRelease
:
256 context
= engine_get_context(e
->data
.x
.client
,
257 e
->data
.x
.e
->xbutton
.window
);
258 if (e
->data
.x
.e
->xbutton
.button
== button
) {
261 fire_motion(MouseAction_Motion
, context
,
262 e
->data
.x
.client
, e
->data
.x
.e
->xbutton
.state
,
263 e
->data
.x
.e
->xbutton
.button
,
264 cx
, cy
, cw
, ch
, dx
, dy
, TRUE
, corner
);
265 drag
= drag_used
= FALSE
;
269 /* clicks are only valid if its released over the window */
272 guint ujunk
, b
, w
, h
;
273 XGetGeometry(ob_display
, e
->data
.x
.e
->xbutton
.window
,
274 &wjunk
, &junk
, &junk
, &w
, &h
, &b
, &ujunk
);
275 if (e
->data
.x
.e
->xbutton
.x
>= (signed)-b
&&
276 e
->data
.x
.e
->xbutton
.y
>= (signed)-b
&&
277 e
->data
.x
.e
->xbutton
.x
< (signed)(w
+b
) &&
278 e
->data
.x
.e
->xbutton
.y
< (signed)(h
+b
)) {
280 /* double clicks happen if there were 2 in a row! */
281 if (lbutton
== button
&&
282 e
->data
.x
.e
->xbutton
.time
- doubleclicktime
.integer
<=
293 ltime
= e
->data
.x
.e
->xbutton
.time
;
295 fire_button(MouseAction_Release
, context
,
296 e
->data
.x
.client
, e
->data
.x
.e
->xbutton
.state
,
297 e
->data
.x
.e
->xbutton
.button
);
299 fire_button(MouseAction_Click
, context
,
300 e
->data
.x
.client
, e
->data
.x
.e
->xbutton
.state
,
301 e
->data
.x
.e
->xbutton
.button
);
303 fire_button(MouseAction_DClick
, context
,
304 e
->data
.x
.client
, e
->data
.x
.e
->xbutton
.state
,
305 e
->data
.x
.e
->xbutton
.button
);
308 case Event_X_MotionNotify
:
310 dx
= e
->data
.x
.e
->xmotion
.x_root
- px
;
311 dy
= e
->data
.x
.e
->xmotion
.y_root
- py
;
313 (ABS(dx
) >= dragthreshold
.integer
||
314 ABS(dy
) >= dragthreshold
.integer
))
317 context
= engine_get_context(e
->data
.x
.client
,
318 e
->data
.x
.e
->xbutton
.window
);
319 drag_used
= fire_motion(MouseAction_Motion
, context
,
321 e
->data
.x
.e
->xmotion
.state
,
322 button
, cx
, cy
, cw
, ch
, dx
, dy
,
329 g_assert_not_reached();
333 gboolean
mbind(char *buttonstr
, char *contextstr
, MouseAction mact
,
341 if (!translate_button(buttonstr
, &state
, &button
)) {
342 g_warning("invalid button '%s'", buttonstr
);
346 contextstr
= g_ascii_strdown(contextstr
, -1);
347 context
= g_quark_try_string(contextstr
);
349 g_warning("invalid context '%s'", contextstr
);
355 for (it
= g_datalist_id_get_data(&bound_contexts
, context
);
356 it
!= NULL
; it
= it
->next
){
358 if (b
->state
== state
&& b
->button
== button
) {
360 if (b
->action
[mact
] != NULL
) {
361 g_warning("duplicate binding");
364 b
->action
[mact
] = action
;
369 grab_all_clients(FALSE
);
371 /* add the binding */
372 b
= g_new0(MouseBinding
, 1);
375 b
->action
[mact
] = action
;
376 g_datalist_id_set_data(&bound_contexts
, context
,
377 g_slist_append(g_datalist_id_get_data(&bound_contexts
, context
), b
));
379 grab_all_clients(TRUE
);
384 void plugin_startup()
386 dispatch_register(Event_Client_Mapped
| Event_Client_Destroy
|
387 Event_X_ButtonPress
| Event_X_ButtonRelease
|
388 Event_X_MotionNotify
, (EventHandler
)event
, NULL
);
393 void plugin_shutdown()
395 dispatch_register(0, (EventHandler
)event
, NULL
);
397 grab_all_clients(FALSE
);
398 g_datalist_foreach(&bound_contexts
, foreach_clear
, NULL
);