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
);
240 /* Parses a mouse or key binding */
241 static int ParseBinding(
242 Display
*dpy
, Binding
**pblist
, char *tline
, binding_t type
,
243 int *nr_left_buttons
, int *nr_right_buttons
,
244 unsigned short *buttons_grabbed
, Bool is_silent
)
247 char context_string
[20];
248 char modifier_string
[20];
251 char key_string
[201] = "";
253 char *window_name
= NULL
;
262 KeySym keysym
= NoSymbol
;
263 Bool is_unbind_request
= False
;
264 Bool is_pass_through
= False
;
265 Bool is_binding_removed
= False
;
267 Binding
*rmlist
= NULL
;
268 STROKE_CODE(char stroke
[STROKE_MAX_SEQUENCE
+ 1] = "");
269 STROKE_CODE(int n4
= 0);
272 /* tline points after the key word "Mouse" or "Key" */
273 token
= p
= PeekToken(tline
, &ptr
);
274 /* check to see if a window name has been specified. */
278 ERR
, "ParseBinding", "empty %s binding, ignored\n",
285 /* A window name has been specified for the binding. */
286 sscanf(p
+ 1, "%79s", buffer
);
296 "Syntax error in line %s -"
297 " missing ')'", tline
);
305 window_name
= buffer
;
312 "Syntax error in line %s - trailing"
313 " text after specified window", tline
);
318 token
= PeekToken(ptr
, &ptr
);
323 if (BIND_IS_KEY_BINDING(type
))
325 /* see len of key_string above */
326 n1
= sscanf(token
,"%200s", key_string
);
329 else if (BIND_IS_STROKE_BINDING(type
))
336 if (token
[0] == 'N' && token
[1] != '\0')
341 while (n1
&& token
[j
] != '\0' &&
342 i
< STROKE_MAX_SEQUENCE
)
344 if (!isdigit(token
[j
]))
350 /* Numeric pad to Telephone */
351 if ('7' <= token
[j
] && token
[j
] <= '9')
355 else if ('1' <= token
[j
] &&
361 stroke
[i
] = token
[j
];
366 if (strlen(token
) > STROKE_MAX_SEQUENCE
+ num
)
371 WARN
, "ParseBinding",
372 "Too long stroke sequence in"
373 " line %s. Only %i elements"
374 " will be taken into"
376 tline
, STROKE_MAX_SEQUENCE
);
380 #endif /* HAVE_STROKE */
383 n1
= sscanf(token
, "%d", &button
);
390 "Illegal mouse button in line"
395 if (button
> NUMBER_OF_MOUSE_BUTTONS
)
400 WARN
, "ParseBinding",
401 "Got mouse button %d when the"
402 " maximum is %d.\n You can't"
403 " bind complex functions to"
404 " this button. To suppress"
405 " this warning, use:\n"
406 " Silent Mouse %s", button
,
407 NUMBER_OF_MOUSE_BUTTONS
,
415 if (BIND_IS_STROKE_BINDING(type
))
417 token
= PeekToken(ptr
, &ptr
);
420 n4
= sscanf(token
,"%d", &button
);
423 #endif /* HAVE_STROKE */
425 token
= PeekToken(ptr
, &ptr
);
428 n2
= sscanf(token
, "%19s", context_string
);
430 token
= PeekToken(ptr
, &action
);
433 n3
= sscanf(token
, "%19s", modifier_string
);
436 if (n1
!= 1 || n2
!= 1 || n3
!= 1
437 STROKE_CODE(|| (BIND_IS_STROKE_BINDING(type
) && n4
!= 1)))
442 ERR
, "ParseBinding", "Syntax error in line %s",
448 if (wcontext_string_to_wcontext(
449 context_string
, &context
) && !is_silent
)
452 WARN
, "ParseBinding", "Illegal context in line %s",
455 if (modifiers_string_to_modmask(modifier_string
, &modifier
) &&
459 WARN
, "ParseBinding", "Illegal modifier in line %s",
463 if (BIND_IS_KEY_BINDING(type
))
465 keysym
= FvwmStringToKeysym(dpy
, key_string
);
466 /* Don't let a 0 keycode go through, since that means AnyKey
467 * to the XGrabKey call. */
473 ERR
, "ParseBinding", "No such key: %s",
481 ** strip leading whitespace from action if necessary
483 while (*action
&& (*action
== ' ' || *action
== '\t'))
490 is_pass_through
= is_pass_through_action(action
);
493 /* pass-through actions indicate that the event be
494 * allowed to pass through to the underlying window. */
495 if (window_name
== NULL
)
497 /* It doesn't make sense to have a pass-through
498 * action on global bindings. */
503 "Invalid action for global "
504 "binding: %s", tline
);
511 /* see if it is an unbind request */
512 if (!action
|| (action
[0] == '-' && !is_pass_through
))
514 is_unbind_request
= True
;
517 /* short circuit menu bindings for now. */
518 if ((context
& C_MENU
) == C_MENU
)
521 dpy
, type
, button
, keysym
, context
, modifier
, action
,
523 /* ParseBinding returns the number of new bindings in pblist
524 * menu bindings does not add to pblist, and should return 0 */
528 /* short circuit placement bindings for now. */
529 if ((context
& C_PLACEMENT
) == C_PLACEMENT
)
531 placement_binding(button
,keysym
,modifier
,action
);
532 /* ParseBinding returns the number of new bindings in pblist
533 * placement bindings does not add to pblist, and should
539 ** Remove the "old" bindings if any
543 dpy
, pblist
, &rmlist
, type
, STROKE_ARG((void *)stroke
)
544 button
, keysym
, modifier
, context
, window_name
);
547 is_binding_removed
= True
;
548 if (is_unbind_request
)
552 /* remove the grabs for the key for unbind
554 for (b
= rmlist
; b
!= NULL
; b
= b
->NextBinding
)
556 /* release the grab */
557 rc
|= activate_binding(b
, type
, False
);
562 pblist
, rmlist
->Button_Key
);
565 FreeBindingList(rmlist
);
567 if (is_binding_removed
)
571 bcontext
= bind_get_bound_button_contexts(
572 pblist
, buttons_grabbed
);
574 bcontext
, nr_left_buttons
, nr_right_buttons
,
577 /* return if it is an unbind request */
578 if (is_unbind_request
)
584 update_nr_buttons(context
, nr_left_buttons
, nr_right_buttons
, False
);
585 if ((modifier
& AnyModifier
)&&(modifier
&(~AnyModifier
)))
588 WARN
, "ParseBinding", "Binding specified AnyModifier"
589 " and other modifers too. Excess modifiers are"
591 modifier
= AnyModifier
;
594 (BIND_IS_MOUSE_BINDING(type
) ||
595 (BIND_IS_STROKE_BINDING(type
) && button
!= 0)) &&
596 (context
& (C_WINDOW
| C_EWMH_DESKTOP
)) &&
597 buttons_grabbed
!= NULL
)
602 ((1 << NUMBER_OF_EXTENDED_MOUSE_BUTTONS
) - 1);
606 *buttons_grabbed
|= (1 << (button
- 1));
610 dpy
, pblist
, type
, STROKE_ARG((void *)stroke
)
611 button
, keysym
, key_string
, modifier
, context
, (void *)action
,
617 static void binding_cmd(F_CMD_ARGS
, binding_t type
)
621 unsigned short btg
= Scr
.buttons2grab
;
623 count
= ParseBinding(
624 dpy
, &Scr
.AllBindings
, action
, type
, &Scr
.nr_left_buttons
,
625 &Scr
.nr_right_buttons
, &btg
, Scr
.flags
.are_functions_silent
);
626 if (btg
!= Scr
.buttons2grab
)
628 Scr
.flags
.do_need_window_update
= 1;
629 Scr
.flags
.has_mouse_binding_changed
= 1;
630 Scr
.buttons2grab
= btg
;
633 b
= Scr
.AllBindings
; count
> 0 && b
!= NULL
;
634 count
--, b
= b
->NextBinding
)
636 activate_binding(b
, type
, True
);
642 void print_bindings(void)
646 fprintf(stderr
, "Current list of bindings:\n\n");
647 for (b
= Scr
.AllBindings
; b
!= NULL
; b
= b
->NextBinding
)
652 fprintf(stderr
, "Key");
655 fprintf(stderr
, "PointerKey");
657 case BIND_BUTTONPRESS
:
658 case BIND_BUTTONRELEASE
:
659 fprintf(stderr
, "Mouse");
662 fprintf(stderr
, "Stroke");
666 ERR
, "print_bindings",
667 "invalid binding type %d", b
->type
);
670 if (b
->windowName
!= NULL
)
672 fprintf(stderr
, " (%s)", b
->windowName
);
678 fprintf(stderr
, "\t%s", b
->key_name
);
680 case BIND_BUTTONPRESS
:
681 case BIND_BUTTONRELEASE
:
682 fprintf(stderr
, "\t%d", b
->Button_Key
);
688 (char *)b
->Stroke_Seq
, b
->Button_Key
));
693 char *context_string
;
695 mod_string
= charmap_table_to_string(
696 MaskUsedModifiers(b
->Modifier
),key_modifiers
);
697 context_string
= charmap_table_to_string(
698 b
->Context
, win_contexts
);
700 stderr
, "\t%s\t%s\t%s\n", context_string
,
701 mod_string
, (char *)b
->Action
);
703 free(context_string
);
710 /* ---------------------------- interface functions ------------------------ */
712 /* Removes all unused modifiers from in_modifiers */
713 unsigned int MaskUsedModifiers(unsigned int in_modifiers
)
715 return in_modifiers
& ~mods_unused
;
718 unsigned int GetUnusedModifiers(void)
723 /* ---------------------------- builtin commands --------------------------- */
725 void CMD_Key(F_CMD_ARGS
)
727 binding_cmd(F_PASS_ARGS
, BIND_KEYPRESS
);
732 void CMD_PointerKey(F_CMD_ARGS
)
734 binding_cmd(F_PASS_ARGS
, BIND_PKEYPRESS
);
739 void CMD_Mouse(F_CMD_ARGS
)
741 binding_cmd(F_PASS_ARGS
, BIND_BUTTONPRESS
);
747 void CMD_Stroke(F_CMD_ARGS
)
749 binding_cmd(F_PASS_ARGS
, BIND_STROKE
);
753 #endif /* HAVE_STROKE */
755 /* Declares which X modifiers are actually locks and should be ignored when
756 * testing mouse/key binding modifiers. */
757 void CMD_IgnoreModifiers(F_CMD_ARGS
)
760 int mods_unused_old
= mods_unused
;
762 token
= PeekToken(action
, &action
);
767 else if (StrEquals(token
, "default"))
769 mods_unused
= DEFAULT_MODS_UNUSED
;
771 else if (modifiers_string_to_modmask(token
, &mods_unused
))
774 ERR
, "ignore_modifiers",
775 "illegal modifier in line %s\n", action
);
777 if (mods_unused
!= mods_unused_old
)
779 /* broadcast config to modules */
780 broadcast_ignore_modifiers();