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
17 /* ---------------------------- included header files ---------------------- */
23 #include "libs/fvwmlib.h"
24 #include "libs/charmap.h"
25 #include "libs/wcontext.h"
26 #include "libs/modifiers.h"
27 #include "libs/Parse.h"
28 #include "libs/Strings.h"
29 #include "libs/defaults.h"
33 #include "functions.h"
35 #include "module_interface.h"
39 #include "menubindings.h"
40 #include "move_resize.h" /* for placement_binding */
43 #endif /* HAVE_STROKE */
45 /* ---------------------------- local definitions -------------------------- */
47 /* ---------------------------- local macros ------------------------------- */
49 /* ---------------------------- imports ------------------------------------ */
51 /* ---------------------------- included code files ------------------------ */
53 /* ---------------------------- local types -------------------------------- */
55 /* ---------------------------- forward declarations ----------------------- */
57 /* ---------------------------- local variables ---------------------------- */
59 static int mods_unused
= DEFAULT_MODS_UNUSED
;
61 /* ---------------------------- exported variables (globals) --------------- */
63 /* ---------------------------- local functions ---------------------------- */
65 static void update_nr_buttons(
66 int contexts
, int *nr_left_buttons
, int *nr_right_buttons
, Bool do_set
)
69 int l
= *nr_left_buttons
;
70 int r
= *nr_right_buttons
;
72 if (contexts
== C_ALL
)
76 /* check for nr_left_buttons */
77 for (i
= 0; i
< NUMBER_OF_TITLE_BUTTONS
; i
+= 2)
79 if ((contexts
& (C_L1
<< i
)))
81 if (do_set
|| *nr_left_buttons
<= i
/ 2)
83 *nr_left_buttons
= i
/ 2 + 1;
87 /* check for nr_right_buttons */
88 for (i
= 1; i
< NUMBER_OF_TITLE_BUTTONS
; i
+= 2)
90 if ((contexts
& (C_L1
<< i
)))
92 if (do_set
|| *nr_right_buttons
<= i
/ 2)
94 *nr_right_buttons
= i
/ 2 + 1;
98 if (*nr_left_buttons
!= l
|| *nr_right_buttons
!= r
)
100 Scr
.flags
.do_need_window_update
= 1;
101 Scr
.flags
.has_nr_buttons_changed
= 1;
107 static int activate_binding(Binding
*binding
, binding_t type
, Bool do_grab
)
116 if (BIND_IS_PKEY_BINDING(type
) || binding
->Context
== C_ALL
)
118 /* necessary for key bindings that work over unfocused windows
120 GrabWindowKeyOrButton(
121 dpy
, Scr
.Root
, binding
,
122 C_WINDOW
| C_DECOR
| C_ROOT
| C_ICON
| C_EWMH_DESKTOP
,
123 GetUnusedModifiers(), None
, do_grab
);
124 if (do_grab
== False
)
129 if (do_grab
== False
&& BIND_IS_KEY_BINDING(type
) &&
130 (binding
->Context
& C_ROOT
))
134 if (fFvwmInStartup
== True
)
139 /* grab keys immediately */
140 for (t
= Scr
.FvwmRoot
.next
; t
!= NULL
; t
= t
->next
)
142 if (!IS_EWMH_DESKTOP(FW_W(t
)) &&
143 (binding
->Context
& (C_WINDOW
| C_DECOR
)) &&
144 BIND_IS_KEY_BINDING(type
))
147 dpy
, FW_W_FRAME(t
), binding
,
148 C_WINDOW
| C_DECOR
, GetUnusedModifiers(),
151 if (binding
->Context
& C_ICON
)
153 if (FW_W_ICON_TITLE(t
) != None
)
155 GrabWindowKeyOrButton(
156 dpy
, FW_W_ICON_TITLE(t
), binding
,
157 C_ICON
, GetUnusedModifiers(), None
,
160 if (FW_W_ICON_PIXMAP(t
) != None
)
162 GrabWindowKeyOrButton(
163 dpy
, FW_W_ICON_PIXMAP(t
), binding
,
164 C_ICON
, GetUnusedModifiers(), None
,
168 if (IS_EWMH_DESKTOP(FW_W(t
)) &&
169 (binding
->Context
& C_EWMH_DESKTOP
))
171 GrabWindowKeyOrButton(
172 dpy
, FW_W_PARENT(t
), binding
, C_EWMH_DESKTOP
,
173 GetUnusedModifiers(), None
, do_grab
);
180 static int bind_get_bound_button_contexts(
181 Binding
**pblist
, unsigned short *buttons_grabbed
)
188 *buttons_grabbed
= 0;
190 for (b
= *pblist
; b
!= NULL
; b
= b
->NextBinding
)
192 if (!BIND_IS_MOUSE_BINDING(b
->type
) &&
193 !BIND_IS_STROKE_BINDING(b
->type
))
197 if ((b
->Context
& (C_WINDOW
| C_EWMH_DESKTOP
)) &&
198 !(BIND_IS_STROKE_BINDING(b
->type
) && b
->Button_Key
== 0) &&
199 buttons_grabbed
!= NULL
)
201 if (b
->Button_Key
== 0)
205 NUMBER_OF_EXTENDED_MOUSE_BUTTONS
) -
210 *buttons_grabbed
|= (1 << (b
->Button_Key
- 1));
213 if (b
->Context
!= C_ALL
&& (b
->Context
& (C_LALL
| C_RALL
)))
215 bcontext
|= b
->Context
;
222 static void __rebind_global_key(Binding
**pblist
, int Button_Key
)
226 for (b
= *pblist
; b
!= NULL
; b
= b
->NextBinding
)
228 if (b
->Button_Key
== Button_Key
&&
229 (BIND_IS_PKEY_BINDING(b
->type
) || b
->Context
== C_ALL
))
231 activate_binding(b
, b
->type
, True
);
239 /* Parses a mouse or key binding */
240 static int ParseBinding(
241 Display
*dpy
, Binding
**pblist
, char *tline
, binding_t type
,
242 int *nr_left_buttons
, int *nr_right_buttons
,
243 unsigned short *buttons_grabbed
, Bool is_silent
)
245 char *action
, context_string
[20], modifier_string
[20], *ptr
, *token
;
246 char key_string
[201] = "", buffer
[80], *windowName
= NULL
, *p
;
252 KeySym keysym
= NoSymbol
;
253 Bool is_unbind_request
= False
;
254 Bool is_pass_through
= False
;
255 Bool is_binding_removed
= False
;
257 Binding
*rmlist
= NULL
;
258 STROKE_CODE(char stroke
[STROKE_MAX_SEQUENCE
+ 1] = "");
259 STROKE_CODE(int n4
=0);
262 /* tline points after the key word "Mouse" or "Key" */
263 token
= p
= PeekToken(tline
, &ptr
);
264 /* check to see if a window name has been specified. */
268 ERR
, "ParseBinding", "empty %s binding, ignored\n",tline
);
273 /* A window name has been specified for the binding. */
274 sscanf(p
+1, "%79s",buffer
);
284 "Syntax error in line %s -"
285 " missing ')'", tline
);
299 "Syntax error in line %s - trailing"
300 " text after specified window", tline
);
304 token
= PeekToken(ptr
, &ptr
);
309 if (BIND_IS_KEY_BINDING(type
))
311 /* see len of key_string above */
312 n1
= sscanf(token
,"%200s", key_string
);
315 else if (BIND_IS_STROKE_BINDING(type
))
322 if (token
[0] == 'N' && token
[1] != '\0')
327 while (n1
&& token
[j
] != '\0' &&
328 i
< STROKE_MAX_SEQUENCE
)
330 if (!isdigit(token
[j
]))
336 /* Numeric pad to Telephone */
337 if ('7' <= token
[j
] && token
[j
] <= '9')
341 else if ('1' <= token
[j
] &&
347 stroke
[i
] = token
[j
];
352 if (strlen(token
) > STROKE_MAX_SEQUENCE
+ num
)
357 WARN
, "ParseBinding",
358 "Too long stroke sequence in"
359 " line %s. Only %i elements"
360 " will be taken into"
362 tline
, STROKE_MAX_SEQUENCE
);
366 #endif /* HAVE_STROKE */
369 n1
= sscanf(token
, "%d", &button
);
376 "Illegal mouse button in line"
381 if (button
> NUMBER_OF_MOUSE_BUTTONS
)
386 WARN
, "ParseBinding",
387 "Got mouse button %d when the"
388 " maximum is %d.\n You can't"
389 " bind complex functions to"
390 " this button. To suppress"
391 " this warning, use:\n"
392 " Silent Mouse %s", button
,
393 NUMBER_OF_MOUSE_BUTTONS
, tline
);
400 if (BIND_IS_STROKE_BINDING(type
))
402 token
= PeekToken(ptr
, &ptr
);
405 n4
= sscanf(token
,"%d", &button
);
408 #endif /* HAVE_STROKE */
410 token
= PeekToken(ptr
, &ptr
);
413 n2
= sscanf(token
, "%19s", context_string
);
415 token
= PeekToken(ptr
, &action
);
418 n3
= sscanf(token
, "%19s", modifier_string
);
421 if (n1
!= 1 || n2
!= 1 || n3
!= 1
422 STROKE_CODE(|| (BIND_IS_STROKE_BINDING(type
) && n4
!= 1)))
427 ERR
, "ParseBinding", "Syntax error in line %s",
433 if (wcontext_string_to_wcontext(
434 context_string
, &context
) && !is_silent
)
437 WARN
, "ParseBinding", "Illegal context in line %s",
440 if (modifiers_string_to_modmask(modifier_string
, &modifier
) &&
444 WARN
, "ParseBinding", "Illegal modifier in line %s",
448 if (BIND_IS_KEY_BINDING(type
))
450 keysym
= FvwmStringToKeysym(dpy
, key_string
);
451 /* Don't let a 0 keycode go through, since that means AnyKey
452 * to the XGrabKey call. */
458 ERR
, "ParseBinding", "No such key: %s",
466 ** strip leading whitespace from action if necessary
468 while (*action
&& (*action
== ' ' || *action
== '\t'))
475 is_pass_through
= is_pass_through_action(action
);
478 /* pass-through actions indicate that the event be
479 * allowed to pass through to the underlying window. */
480 if (windowName
== NULL
)
482 /* It doesn't make sense to have a pass-through
483 * action on global bindings. */
485 fvwm_msg(ERR
, "ParseBinding",
486 "Illegal action for global "
487 "binding: %s", tline
);
492 /* see if it is an unbind request */
493 if (!action
|| (action
[0] == '-' && !is_pass_through
))
495 is_unbind_request
= True
;
498 /* short circuit menu bindings for now. */
499 if ((context
& C_MENU
) == C_MENU
)
501 menu_binding(dpy
, type
, button
, keysym
, context
,
502 modifier
, action
, windowName
);
503 /* ParseBinding returns the number of new bindings in pblist
504 * menu bindings does not add to pblist, and should return 0 */
507 /* short circuit placement bindings for now. */
508 if ((context
& C_PLACEMENT
) == C_PLACEMENT
)
510 placement_binding(button
,keysym
,modifier
,action
);
511 /* ParseBinding returns the number of new bindings in pblist
512 * placement bindings does not add to pblist, and should
517 ** Remove the "old" bindings if any
521 dpy
, pblist
, &rmlist
, type
, STROKE_ARG((void *)stroke
)
522 button
, keysym
, modifier
, context
, windowName
);
525 is_binding_removed
= True
;
526 if (is_unbind_request
)
530 /* remove the grabs for the key for unbind
532 for (b
= rmlist
; b
!= NULL
; b
= b
->NextBinding
)
534 /* release the grab */
535 rc
|= activate_binding(b
, type
, False
);
539 __rebind_global_key(pblist
, rmlist
->Button_Key
);
542 FreeBindingList(rmlist
);
544 if (is_binding_removed
)
548 bcontext
= bind_get_bound_button_contexts(
549 pblist
, buttons_grabbed
);
551 bcontext
, nr_left_buttons
, nr_right_buttons
,
554 /* return if it is an unbind request */
555 if (is_unbind_request
)
561 update_nr_buttons(context
, nr_left_buttons
, nr_right_buttons
, False
);
562 if ((modifier
& AnyModifier
)&&(modifier
&(~AnyModifier
)))
565 WARN
, "ParseBinding", "Binding specified AnyModifier"
566 " and other modifers too. Excess modifiers are"
568 modifier
= AnyModifier
;
570 if ((BIND_IS_MOUSE_BINDING(type
) ||
571 (BIND_IS_STROKE_BINDING(type
) && button
!= 0)) &&
572 (context
& (C_WINDOW
| C_EWMH_DESKTOP
)) && buttons_grabbed
!= NULL
)
577 ((1 << NUMBER_OF_EXTENDED_MOUSE_BUTTONS
) - 1);
581 *buttons_grabbed
|= (1 << (button
- 1));
585 dpy
, pblist
, type
, STROKE_ARG((void *)stroke
)
586 button
, keysym
, key_string
, modifier
, context
, (void *)action
,
592 static void binding_cmd(F_CMD_ARGS
, binding_t type
)
596 unsigned short btg
= Scr
.buttons2grab
;
598 count
= ParseBinding(
599 dpy
, &Scr
.AllBindings
, action
, type
, &Scr
.nr_left_buttons
,
600 &Scr
.nr_right_buttons
, &btg
, Scr
.flags
.are_functions_silent
);
601 if (btg
!= Scr
.buttons2grab
)
603 Scr
.flags
.do_need_window_update
= 1;
604 Scr
.flags
.has_mouse_binding_changed
= 1;
605 Scr
.buttons2grab
= btg
;
607 for (b
= Scr
.AllBindings
; count
> 0 && b
!= NULL
;
608 count
--, b
= b
->NextBinding
)
610 activate_binding(b
, type
, True
);
616 /* ---------------------------- interface functions ------------------------ */
618 /* Removes all unused modifiers from in_modifiers */
619 unsigned int MaskUsedModifiers(unsigned int in_modifiers
)
621 return in_modifiers
& ~mods_unused
;
624 unsigned int GetUnusedModifiers(void)
629 /* ---------------------------- builtin commands --------------------------- */
631 void CMD_Key(F_CMD_ARGS
)
633 binding_cmd(F_PASS_ARGS
, BIND_KEYPRESS
);
638 void CMD_PointerKey(F_CMD_ARGS
)
640 binding_cmd(F_PASS_ARGS
, BIND_PKEYPRESS
);
645 void CMD_Mouse(F_CMD_ARGS
)
647 binding_cmd(F_PASS_ARGS
, BIND_BUTTONPRESS
);
653 void CMD_Stroke(F_CMD_ARGS
)
655 binding_cmd(F_PASS_ARGS
, BIND_STROKE
);
659 #endif /* HAVE_STROKE */
661 /* Declares which X modifiers are actually locks and should be ignored when
662 * testing mouse/key binding modifiers. */
663 void CMD_IgnoreModifiers(F_CMD_ARGS
)
666 int mods_unused_old
= mods_unused
;
668 token
= PeekToken(action
, &action
);
673 else if (StrEquals(token
, "default"))
675 mods_unused
= DEFAULT_MODS_UNUSED
;
677 else if (modifiers_string_to_modmask(token
, &mods_unused
))
680 ERR
, "ignore_modifiers",
681 "illegal modifier in line %s\n", action
);
683 if (mods_unused
!= mods_unused_old
)
685 /* broadcast config to modules */
686 broadcast_ignore_modifiers();