2 /* This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or
5 * (at your option) any later version.
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 #include <X11/keysym.h>
25 #include "libs/fvwmlib.h"
26 #include "libs/Strings.h"
27 #include "libs/wild.h"
28 #include "libs/Grab.h"
29 #include "libs/Bindings.h"
30 #include "libs/charmap.h"
31 #include "libs/wcontext.h"
32 #include "libs/modifiers.h"
34 static Bool is_grabbing_everything
= False
;
36 static int key_min
= 0;
37 static int key_max
= 0;
39 /* Free the memory use by a binding. */
40 void FreeBindingStruct(Binding
*b
)
67 void FreeBindingList(Binding
*b
)
71 for (; b
!= NULL
; b
= t
)
80 /* Unlink a binding b from a binding list pblist. The previous binding in the
81 * list (prev) must be given also. Pass NULL at the beginning of the list.
82 * The *pblist pointer may be modified by this function. */
83 static void UnlinkBinding(Binding
**pblist
, Binding
*b
, Binding
*prev
)
87 if (!prev
&& b
!= *pblist
)
89 for (t
= *pblist
; t
&& t
!= b
; prev
= t
, t
= t
->NextBinding
)
91 /* Find the previous binding in the list. */
95 /* Binding not found */
103 prev
->NextBinding
= b
->NextBinding
;
107 /* must have been first one, set new start */
108 *pblist
= b
->NextBinding
;
114 /* To remove a binding from the global list (probably needs more processing
115 * for mouse binding lines though, like when context is a title bar button).
116 * Specify either button or keysym, depending on type. */
117 void RemoveBinding(Binding
**pblist
, Binding
*b
, Binding
*prev
)
119 UnlinkBinding(pblist
, b
, NULL
);
120 FreeBindingStruct(b
);
127 * Actually adds a new binding to a list (pblist) of existing bindings.
128 * Specify either button or keysym/key_name, depending on type.
129 * The parameters action and action2 are assumed to reside in malloced memory
130 * that will be freed in RemoveBinding. The key_name is copied into private
131 * memory and has to be freed by the caller.
135 Display
*dpy
, Binding
**pblist
, binding_t type
,
136 STROKE_ARG(void *stroke
)
137 int button
, KeySym keysym
, char *key_name
, int modifiers
, int contexts
,
138 void *action
, void *action2
, char *windowName
)
151 ** Unfortunately a keycode can be bound to multiple keysyms and a
152 ** keysym can bound to multiple keycodes. Thus we have to check every
153 ** keycode with any single modifier.
155 if (BIND_IS_KEY_BINDING(type
))
159 XDisplayKeycodes(dpy
, &key_min
, &key_max
);
171 for (i
= min
; i
<= max
; i
++)
173 unsigned int bound_mask
= 0;
175 /* If this is a mouse binding we'll fall through the for loop
176 * (maxmods is zero) and the if condition is always true (type
177 * is zero). Since min == max == button there is no loop at all
178 * is case of a mouse binding. */
179 for (m
= 0, tkeysym
= XK_Left
;
180 m
<= maxmods
&& tkeysym
!= NoSymbol
; m
++)
182 if (BIND_IS_MOUSE_BINDING(type
) ||
183 STROKE_CODE(BIND_IS_STROKE_BINDING(type
) ||)
184 (tkeysym
= XKeycodeToKeysym(dpy
, i
, m
)) == keysym
)
186 unsigned int add_modifiers
= 0;
187 unsigned int bind_mask
= 1;
188 unsigned int check_bound_mask
= 0;
193 /* key generates the key sym with no
194 * modifiers depressed - bind it */
197 /* key generates the key sym with shift
199 if (modifiers
!= AnyModifier
&&
200 !(modifiers
& ShiftMask
))
202 add_modifiers
= ShiftMask
;
203 bind_mask
= (1 << m
);
204 /* but don't bind it again if
205 * already bound without
207 check_bound_mask
= 1;
211 /* key generates the key sym with
212 * undefined modifiers depressed -
213 * let's make an educated guess at what
214 * modifiers the user expected
215 * based on the XFree86 default
217 mask
= modifier_mapindex_to_mask
[m
- 1];
218 if (modifiers
!= AnyModifier
&&
219 !(modifiers
& mask
) != mask
)
221 add_modifiers
= mask
;
222 bind_mask
= (1 << m
);
223 /* but don't bind it again if
224 * already bound without
226 check_bound_mask
= 1;
230 if ((bind_mask
& bound_mask
) ||
231 (check_bound_mask
& bound_mask
))
233 /* already bound, break out */
237 (*pblist
) = (Binding
*)safemalloc(
239 (*pblist
)->type
= type
;
240 (*pblist
)->Button_Key
= i
;
241 STROKE_CODE((*pblist
)->Stroke_Seq
= (stroke
) ?
242 (void *)stripcpy((char *)stroke
) :
244 if (BIND_IS_KEY_BINDING(type
) &&
247 (*pblist
)->key_name
=
252 (*pblist
)->key_name
= NULL
;
254 (*pblist
)->Context
= contexts
;
255 (*pblist
)->Modifier
= modifiers
| add_modifiers
;
257 (action
) ? stripcpy(action
) : NULL
;
259 (action2
) ? stripcpy(action2
) : NULL
;
260 (*pblist
)->windowName
=
261 windowName
? stripcpy(windowName
) :
263 (*pblist
)->NextBinding
= temp
;
264 bound_mask
|= bind_mask
;
273 * replacesBinding() - does the new binding, b1, replace a current
276 static Bool
replacesBinding(Binding
*b1
, Binding
*b2
)
278 if (b1
->type
!= b2
->type
)
282 if (b1
->Context
!= b2
->Context
)
286 if (b1
->Modifier
!= b2
->Modifier
)
290 if (b1
->Button_Key
!= b2
->Button_Key
)
295 /* definition: "global binding" => b->windowName == NULL
296 * definition: "window-specific binding" => b->windowName != NULL
298 if (b1
->windowName
&& b2
->windowName
)
300 /* Both bindings are window-specific. The existing binding, b2,
301 * is only replaced (by b1) if it applies to the same window */
302 if (strcmp(b1
->windowName
, b2
->windowName
) != 0)
305 else if (b1
->windowName
|| b2
->windowName
)
307 /* 1 binding is window-specific, the other is global - no need
308 * to replace this binding. */
312 if (BIND_IS_KEY_BINDING(b1
->type
) || BIND_IS_MOUSE_BINDING(b1
->type
))
316 if (1 STROKE_CODE(&& BIND_IS_STROKE_BINDING(b1
->type
) &&
317 strcmp(b1
->Stroke_Seq
, b2
->Stroke_Seq
) == 0))
326 * Does exactly the opposite of AddBinding: It removes the bindings that
327 * AddBinding would have added to the *pblist_src and collects them in the
328 * *pblist_dest. This can be used to remove a binding completely from the
329 * list. The bindings still have to be freed.
331 void CollectBindingList(
332 Display
*dpy
, Binding
**pblist_src
, Binding
**pblist_dest
,
333 binding_t type
, STROKE_ARG(void *stroke
)
334 int button
, KeySym keysym
, int modifiers
, int contexts
,
337 Binding
*tmplist
= NULL
;
343 /* generate a private list of bindings to be removed */
345 dpy
, &tmplist
, type
, STROKE_ARG(stroke
)
346 button
, keysym
, NULL
, modifiers
, contexts
, NULL
, NULL
,
348 /* now find equivalent bindings in the given binding list and move
349 * them to the new clist */
350 for (bold
= *pblist_src
, oldprev
= NULL
; bold
!= NULL
;
351 oldprev
= bold
, bold
= bold
->NextBinding
)
353 for (btmp
= tmplist
, tmpprev
= NULL
; btmp
!= NULL
;
354 tmpprev
= btmp
, btmp
= btmp
->NextBinding
)
356 if (replacesBinding(btmp
, bold
))
358 /* move matched binding from src list to dest
360 UnlinkBinding(pblist_src
, bold
, oldprev
);
361 bold
->NextBinding
= *pblist_dest
;
363 /* throw away the tmp binding */
364 UnlinkBinding(&tmplist
, btmp
, tmpprev
);
365 FreeBindingStruct(btmp
);
366 /* stop searching for this binding */
371 /* throw away the temporary list */
372 FreeBindingList(tmplist
);
378 * bindingAppliesToWindow()
380 * The Key/Mouse/PointerKey syntax (optionally) allows a window name
381 * (or class or resource) to be specified with the binding, denoting
382 * which windows the binding can be invoked in. This function determines
383 * if the binding actually applies to a window based on its
384 * name/class/resource.
386 static Bool
does_binding_apply_to_window(
387 Binding
*binding
, const XClassHint
*win_class
, const char *win_name
)
389 /* If no window name is specified with the binding then that means
390 * the binding applies to ALL windows. */
391 if (binding
->windowName
== NULL
)
395 else if (win_class
== NULL
|| win_name
== NULL
)
399 if (matchWildcards(binding
->windowName
, win_name
) == True
||
400 matchWildcards(binding
->windowName
, win_class
->res_name
) == True
||
401 matchWildcards(binding
->windowName
, win_class
->res_class
) == True
)
409 static Bool
__compare_binding(
410 Binding
*b
, STROKE_ARG(char *stroke
)
411 int button_keycode
, unsigned int modifier
, unsigned int used_modifiers
,
412 int Context
, binding_t type
, const XClassHint
*win_class
,
413 const char *win_name
)
415 if (b
->type
!= type
|| !(b
->Context
& Context
))
419 if ((b
->Modifier
& used_modifiers
) != modifier
&&
420 b
->Modifier
!= AnyModifier
)
424 if (BIND_IS_MOUSE_BINDING(type
) &&
425 (b
->Button_Key
!= button_keycode
&& b
->Button_Key
!= 0))
429 else if (BIND_IS_KEY_BINDING(type
) && b
->Button_Key
!= button_keycode
)
434 else if (BIND_IS_STROKE_BINDING(type
) &&
435 ((strcmp(b
->Stroke_Seq
, stroke
) != 0) ||
436 b
->Button_Key
!= button_keycode
))
441 if (!does_binding_apply_to_window(b
, win_class
, win_name
))
449 /* is_pass_through_action() - returns true if the action indicates that the
450 * binding should be ignored by fvwm & passed through to the underlying
452 * Note: it is only meaningful to check for pass-thru actions on
453 * window-specific bindings. */
454 Bool
is_pass_through_action(const char *action
)
456 /* action should never be NULL. */
457 return (strncmp(action
, "--", 2) == 0);
460 /* Check if something is bound to a key or button press and return the action
461 * to be executed or NULL if not. */
463 Binding
*blist
, STROKE_ARG(char *stroke
)
464 int button_keycode
, unsigned int modifier
,unsigned int dead_modifiers
,
465 int Context
, binding_t type
, const XClassHint
*win_class
,
466 const char *win_name
)
469 unsigned int used_modifiers
= ~dead_modifiers
;
472 modifier
&= (used_modifiers
& ALL_MODIFIERS
);
473 for (b
= blist
; b
!= NULL
; b
= b
->NextBinding
)
475 if (__compare_binding(
476 b
, STROKE_ARG(stroke
) button_keycode
, modifier
,
477 used_modifiers
, Context
, type
, win_class
,
480 /* If this is a global binding, keep searching <blist>
481 * in the hope of finding a window-specific binding.
482 * If we don't find a win-specific binding, we use the
483 * _first_ matching global binding we hit. */
484 if (action
== NULL
|| b
->windowName
)
489 if (is_pass_through_action(action
))
502 void *CheckTwoBindings(
503 Bool
*ret_is_second_binding
, Binding
*blist
, STROKE_ARG(char *stroke
)
504 int button_keycode
, unsigned int modifier
,unsigned int dead_modifiers
,
505 int Context
, binding_t type
, const XClassHint
*win_class
,
506 const char *win_name
, int Context2
, binding_t type2
,
507 const XClassHint
*win_class2
, const char *win_name2
)
510 unsigned int used_modifiers
= ~dead_modifiers
;
513 modifier
&= (used_modifiers
& ALL_MODIFIERS
);
514 for (b
= blist
; b
!= NULL
; b
= b
->NextBinding
)
516 if (__compare_binding(
517 b
, STROKE_ARG(stroke
) button_keycode
, modifier
,
518 used_modifiers
, Context
, type
, win_class
, win_name
)
521 if (action
== NULL
|| b
->windowName
)
523 *ret_is_second_binding
= False
;
527 if (is_pass_through_action(action
))
533 if (__compare_binding(
534 b
, STROKE_ARG(stroke
) button_keycode
, modifier
,
535 used_modifiers
, Context2
, type2
, win_class2
,
538 if (action
== NULL
|| b
->windowName
)
540 *ret_is_second_binding
= True
;
544 if (is_pass_through_action(action
))
558 * GrabWindowKey - grab needed keys for the window for one binding
559 * GrabAllWindowKeys - grab needed keys for the window for all bindings
561 * GrabWindowButton - same for mouse buttons
562 * GrabAllWindowButtons - same for mouse buttons
563 * GrabAllWindowKeysAndButtons - both of the above
566 * w - the window to use (the frame window)
567 * grab - 1 to grab, 0 to ungrab
568 * binding - pointer to the bindinge to grab/ungrab
569 * contexts - all context bits that shall receive bindings
570 * dead_modifiers - modifiers to ignore for 'AnyModifier'
571 * cursor - the mouse cursor to use when the pointer is on the
572 * grabbed area (mouse bindings only)
575 void GrabWindowKey(Display
*dpy
, Window w
, Binding
*binding
,
576 unsigned int contexts
, unsigned int dead_modifiers
,
579 /* remove unnecessary bits from dead_modifiers */
580 dead_modifiers
&= ~(binding
->Modifier
& dead_modifiers
);
581 dead_modifiers
&= ALL_MODIFIERS
;
583 if((binding
->Context
& contexts
) && BIND_IS_KEY_BINDING(binding
->type
))
588 dpy
, binding
->Button_Key
, binding
->Modifier
, w
,
589 True
, GrabModeAsync
, GrabModeAsync
);
594 dpy
, binding
->Button_Key
, binding
->Modifier
, w
);
596 if(binding
->Modifier
!= AnyModifier
&& dead_modifiers
!= 0)
598 register unsigned int mods
;
599 register unsigned int max
= dead_modifiers
;
600 register unsigned int living_modifiers
=
603 /* handle all bindings for the dead modifiers */
604 for (mods
= 1; mods
<= max
; mods
++)
606 /* Since mods starts with 1 we don't need to
607 * test if mods contains a dead modifier.
608 * Otherwise both, dead and living modifiers
609 * would be zero ==> mods == 0 */
610 if (mods
& living_modifiers
)
617 dpy
, binding
->Button_Key
,
618 mods
|binding
->Modifier
, w
,
625 dpy
, binding
->Button_Key
,
626 mods
|binding
->Modifier
, w
);
630 if (!is_grabbing_everything
)
639 void GrabAllWindowKeys(
640 Display
*dpy
, Window w
, Binding
*blist
, unsigned int contexts
,
641 unsigned int dead_modifiers
, Bool fGrab
)
644 is_grabbing_everything
= True
;
645 for ( ; blist
!= NULL
; blist
= blist
->NextBinding
)
647 GrabWindowKey(dpy
, w
, blist
, contexts
, dead_modifiers
, fGrab
);
649 is_grabbing_everything
= False
;
650 MyXUngrabServer(dpy
);
655 void GrabWindowButton(
656 Display
*dpy
, Window w
, Binding
*binding
, unsigned int contexts
,
657 unsigned int dead_modifiers
, Cursor cursor
, Bool fGrab
)
659 if (binding
->Action
== NULL
)
664 dead_modifiers
&= ~(binding
->Modifier
& dead_modifiers
); /* dje */
665 dead_modifiers
&= ALL_MODIFIERS
;
667 if ((binding
->Context
& contexts
) &&
668 ((BIND_IS_MOUSE_BINDING(binding
->type
) ||
669 (BIND_IS_STROKE_BINDING(binding
->type
) &&
670 binding
->Button_Key
!=0))))
673 int bmax
= NUMBER_OF_EXTENDED_MOUSE_BUTTONS
;
676 if(binding
->Button_Key
>0)
678 bmin
= bmax
= binding
->Button_Key
;
680 for (button
= bmin
; button
<= bmax
; button
++)
685 dpy
, button
, binding
->Modifier
, w
,
686 True
, ButtonPressMask
|
687 ButtonReleaseMask
, GrabModeSync
,
688 GrabModeAsync
, None
, cursor
);
693 dpy
, button
, binding
->Modifier
, w
);
695 if (binding
->Modifier
!= AnyModifier
&&
698 register unsigned int mods
;
699 register unsigned int max
= dead_modifiers
;
700 register unsigned int living_modifiers
=
703 /* handle all bindings for the dead modifiers */
704 for (mods
= 1; mods
<= max
; mods
++)
706 /* Since mods starts with 1 we don't
707 * need to test if mods contains a
708 * dead modifier. Otherwise both, dead
709 * and living modifiers would be zero
711 if (mods
& living_modifiers
)
719 mods
|binding
->Modifier
,
731 mods
|binding
->Modifier
,
736 if (!is_grabbing_everything
)
746 void GrabAllWindowButtons(
747 Display
*dpy
, Window w
, Binding
*blist
, unsigned int contexts
,
748 unsigned int dead_modifiers
, Cursor cursor
, Bool fGrab
)
751 is_grabbing_everything
= True
;
752 for ( ; blist
!= NULL
; blist
= blist
->NextBinding
)
754 GrabWindowButton(dpy
, w
, blist
, contexts
, dead_modifiers
,
757 is_grabbing_everything
= False
;
758 MyXUngrabServer(dpy
);
763 void GrabAllWindowKeysAndButtons(
764 Display
*dpy
, Window w
, Binding
*blist
, unsigned int contexts
,
765 unsigned int dead_modifiers
, Cursor cursor
, Bool fGrab
)
768 is_grabbing_everything
= True
;
769 for ( ; blist
!= NULL
; blist
= blist
->NextBinding
)
771 if (blist
->Context
& contexts
)
773 if (BIND_IS_MOUSE_BINDING(blist
->type
) ||
774 BIND_IS_STROKE_BINDING(blist
->type
))
777 dpy
, w
, blist
, contexts
,
778 dead_modifiers
, cursor
, fGrab
);
780 else if (BIND_IS_KEY_BINDING(blist
->type
))
783 dpy
, w
, blist
, contexts
,
784 dead_modifiers
, fGrab
);
788 is_grabbing_everything
= False
;
789 MyXUngrabServer(dpy
);
794 void GrabWindowKeyOrButton(
795 Display
*dpy
, Window w
, Binding
*binding
, unsigned int contexts
,
796 unsigned int dead_modifiers
, Cursor cursor
, Bool fGrab
)
798 if (BIND_IS_MOUSE_BINDING(binding
->type
) ||
799 BIND_IS_STROKE_BINDING(binding
->type
))
802 dpy
, w
, binding
, contexts
, dead_modifiers
, cursor
,
805 else if (BIND_IS_KEY_BINDING(binding
->type
))
808 dpy
, w
, binding
, contexts
, dead_modifiers
, fGrab
);
816 * Like XStringToKeysym, but allows some typos and does some additional
820 KeySym
FvwmStringToKeysym(Display
*dpy
, char *key
)
827 keysym
= XStringToKeysym(key
);
831 s
= alloca(strlen(key
) + 1);
833 /* always prefer the lower case spelling if it exists */
835 keysym
= XStringToKeysym(s
);
836 if (keysym
== NoSymbol
)
839 keysym
= XStringToKeysym(s
);
842 if (keysym
== NoSymbol
|| XKeysymToKeycode(dpy
, keysym
) == 0)