/* This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" #include #include "libs/fvwmlib.h" #include "fvwm.h" #include "externs.h" #include "cursor.h" #include "functions.h" #include "bindings.h" #include "defaults.h" #include "misc.h" #include "screen.h" #include "focus.h" #ifdef HAVE_STROKE #include "stroke.h" #endif /* HAVE_STROKE */ static void activate_binding( Binding *binding, BindingType type, Bool do_grab, Bool do_grab_root); #define MODS_UNUSED_DEFAULT LockMask static int mods_unused = MODS_UNUSED_DEFAULT; static void update_nr_buttons( int contexts, int *nr_left_buttons, int *nr_right_buttons, Bool do_set) { int i; int l = (nr_left_buttons) ? *nr_left_buttons : 0; int r = (nr_right_buttons) ? *nr_right_buttons : 0; if (contexts != C_ALL && (contexts & C_LALL) && nr_left_buttons != NULL) { /* check for nr_left_buttons */ for (i = 0; i < NUMBER_OF_BUTTONS; i += 2) { if ((contexts & (C_L1 << i)) && (do_set || *nr_left_buttons <= i / 2)) { *nr_left_buttons = i / 2 + 1; } } } if(contexts != C_ALL && (contexts & C_RALL) && nr_right_buttons != NULL) { /* check for nr_right_buttons */ for (i = 1; i < NUMBER_OF_BUTTONS; i += 2) { if ((contexts & (C_L1 << i)) && (do_set || *nr_right_buttons <= i / 2)) { *nr_right_buttons = i / 2 + 1; } } } if ((nr_left_buttons && *nr_left_buttons != l) || (nr_right_buttons && *nr_right_buttons != r)) { Scr.flags.do_need_window_update = 1; Scr.flags.has_nr_buttons_changed = 1; } } /**************************************************************************** * * Parses a mouse or key binding * ****************************************************************************/ int ParseBinding( Display *dpy, Binding **pblist, char *tline, BindingType type, int *nr_left_buttons, int *nr_right_buttons, unsigned char *buttons_grabbed, Bool do_ungrab_root, Bool is_silent) { char *action, context_string[20], modifier_string[20], *ptr, *token; char key_string[201] = ""; int button = 0; STROKE_CODE(char stroke[STROKE_MAX_SEQUENCE + 1] = "";) int n1=0,n2=0,n3=0; STROKE_CODE(int n4=0;) STROKE_CODE(int i;) KeySym keysym = NoSymbol; int context; int modifier; Bool is_unbind_request = False; int rc; /* tline points after the key word "Mouse" or "Key" */ ptr = GetNextToken(tline, &token); if (token != NULL) { if (type == KEY_BINDING) n1 = sscanf(token,"%200s", key_string); /* see len of key_string above */ #ifdef HAVE_STROKE else if (type == STROKE_BINDING) { int num = 0; int j; n1 = 1; i = 0; if (token[0] == 'N' && token[1] != '\0') num = 1; j=i+num; while(n1 && token[j] != '\0' && i < STROKE_MAX_SEQUENCE) { if (!isdigit(token[j])) n1 = 0; if (num) { /* Numeric pad to Telephone */ if ('7' <= token[j] && token[j] <= '9') token[j] -= 6; else if ('1' <= token[j] && token[j] <= '3') token[j] += 6; } stroke[i] = token[j]; i++; j=i+num; } stroke[i] = '\0'; if (strlen(token) > STROKE_MAX_SEQUENCE + num) { if (!is_silent) fvwm_msg(WARN, "ParseBinding", "Too long stroke sequence in line %s" "Only %i elements will be taken into account.\n", tline, STROKE_MAX_SEQUENCE); } } #endif /* HAVE_STROKE */ else { n1 = sscanf(token, "%d", &button); if (button < 0 || button > NUMBER_OF_MOUSE_BUTTONS) { if (!is_silent) fvwm_msg(ERR, "ParseBinding", "Illegal mouse button in line %s", tline); free(token); return 0; } } free(token); } #ifdef HAVE_STROKE if (type == STROKE_BINDING) { ptr = GetNextToken(ptr, &token); if (token != NULL) { n4 = sscanf(token,"%d", &button); free(token); } } #endif /* HAVE_STROKE */ ptr = GetNextToken(ptr, &token); if (token != NULL) { n2 = sscanf(token, "%19s", context_string); free(token); } action = GetNextToken(ptr, &token); if (token != NULL) { n3 = sscanf(token, "%19s", modifier_string); free(token); } if (n1 != 1 || n2 != 1 || n3 != 1 STROKE_CODE(|| (type == STROKE_BINDING && n4 != 1))) { if (!is_silent) fvwm_msg(ERR, "ParseBinding", "Syntax error in line %s", tline); return 0; } if (ParseContext(context_string, &context) && !is_silent) fvwm_msg(WARN, "ParseBinding", "Illegal context in line %s", tline); if (ParseModifiers(modifier_string, &modifier) && !is_silent) fvwm_msg(WARN, "ParseBinding", "Illegal modifier in line %s", tline); if (type == KEY_BINDING) { keysym = FvwmStringToKeysym(dpy, key_string); /* * Don't let a 0 keycode go through, since that means AnyKey to the * XGrabKey call. */ if (keysym == 0) { if (!is_silent) fvwm_msg(ERR, "ParseBinding", "No such key: %s", key_string); return 0; } } /* ** strip leading whitespace from action if necessary */ while (*action && (*action == ' ' || *action == '\t')) action++; /* see if it is an unbind request */ if (!action || action[0] == '-') is_unbind_request = True; /* ** Remove the "old" bindings if any */ { Bool is_binding_removed = False; Binding *b; Binding *rmlist = NULL; CollectBindingList( dpy, pblist, &rmlist, type, STROKE_ARG((void *)stroke) button, keysym, modifier, context); if (rmlist != NULL) { is_binding_removed = True; if (is_unbind_request) { /* remove the grabs for the key for unbind requests */ for (b = rmlist; b != NULL; b = b->NextBinding) { /* release the grab */ activate_binding(b, type, False, do_ungrab_root); } } FreeBindingList(rmlist); } if (is_binding_removed) { int bcontext = 0; if (buttons_grabbed) *buttons_grabbed = 0; for (b = *pblist; b != NULL; b = b->NextBinding) { if(b->type == MOUSE_BINDING && (b->Context & C_WINDOW) && (b->Modifier == 0 || b->Modifier == AnyModifier) && buttons_grabbed != NULL) { if (button == 0) { *buttons_grabbed |= ((1 << NUMBER_OF_MOUSE_BUTTONS) - 1); } else { *buttons_grabbed |= (1 << (button - 1)); } } if (nr_left_buttons != NULL || nr_right_buttons != NULL) { if (b->Context != C_ALL && (b->Context & (C_LALL | C_RALL)) && b->type == MOUSE_BINDING) { bcontext |= b->Context; } } } update_nr_buttons(bcontext, nr_left_buttons, nr_right_buttons, True); } } /* return if it is an unbind request */ if (is_unbind_request) return 0; update_nr_buttons(context, nr_left_buttons, nr_right_buttons, False); if((modifier & AnyModifier)&&(modifier&(~AnyModifier))) { fvwm_msg(WARN,"ParseBinding","Binding specified AnyModifier and other " "modifers too. Excess modifiers will be ignored."); modifier = AnyModifier; } if ((type == MOUSE_BINDING)&&(context & C_WINDOW)&& (((modifier==0)||modifier == AnyModifier)) && (buttons_grabbed != NULL)) { if (button == 0) { *buttons_grabbed |= ((1 << NUMBER_OF_MOUSE_BUTTONS) - 1); } else { *buttons_grabbed |= (1 << (button - 1)); } } return AddBinding( dpy, pblist, type, STROKE_ARG((void *)stroke) button, keysym, key_string, modifier, context, (void *)action, NULL); return rc; } static void activate_binding( Binding *binding, BindingType type, Bool do_grab, Bool do_grab_root) { FvwmWindow *t; if (binding == NULL) return; if (do_grab_root) { /* necessary for key bindings that work over unfocused windows */ GrabWindowKeyOrButton( dpy, Scr.Root, binding, C_WINDOW|C_DECOR|C_ROOT|C_ICON, GetUnusedModifiers(), None, do_grab); } if (fFvwmInStartup == True) return; /* grab keys immediately */ for (t = Scr.FvwmRoot.next; t != NULL; t = t->next) { if ((binding->Context & C_WINDOW) && (binding->type == MOUSE_BINDING STROKE_CODE(|| binding->type == STROKE_BINDING))) { GrabWindowButton( dpy, t->Parent, binding, C_WINDOW, GetUnusedModifiers(), None, do_grab); } if ((binding->Context & (C_WINDOW|C_DECOR)) && binding->type == KEY_BINDING) { GrabWindowKey( dpy, t->frame, binding, C_WINDOW|C_DECOR, GetUnusedModifiers(), do_grab); } if (binding->Context & C_ICON) { if(t->icon_w != None) { GrabWindowKeyOrButton( dpy, t->icon_w, binding, C_ICON, GetUnusedModifiers(), None, do_grab); } if(t->icon_pixmap_w != None) { GrabWindowKeyOrButton( dpy, t->icon_pixmap_w, binding, C_ICON, GetUnusedModifiers(), None, do_grab); } } } return; } static void binding_cmd(F_CMD_ARGS, BindingType type, Bool do_grab_root) { Binding *b; int count; unsigned char btg = Scr.buttons2grab; count = ParseBinding( dpy, &Scr.AllBindings, action, type, &Scr.nr_left_buttons, &Scr.nr_right_buttons, &btg, do_grab_root, Scr.flags.silent_functions); if (btg != Scr.buttons2grab) { Scr.flags.do_need_window_update = 1; Scr.flags.has_mouse_binding_changed = 1; Scr.buttons2grab = btg; } for (b = Scr.AllBindings; count > 0; count--, b = b->NextBinding) { activate_binding(b, type, True, do_grab_root); } return; } void CMD_Key(F_CMD_ARGS) { binding_cmd(F_PASS_ARGS, KEY_BINDING, False); } void CMD_PointerKey(F_CMD_ARGS) { binding_cmd(F_PASS_ARGS, KEY_BINDING, True); } void CMD_Mouse(F_CMD_ARGS) { binding_cmd(F_PASS_ARGS, MOUSE_BINDING, False); } #ifdef HAVE_STROKE void CMD_Stroke(F_CMD_ARGS) { binding_cmd(F_PASS_ARGS, STROKE_BINDING, False); } #endif /* HAVE_STROKE */ /* Removes all unused modifiers from in_modifiers */ unsigned int MaskUsedModifiers(unsigned int in_modifiers) { return in_modifiers & ~mods_unused; } unsigned int GetUnusedModifiers(void) { return mods_unused; } /*********************************************************************** * * SetModifierLocks : declares which X modifiers are actually locks * and should be ignored when testing mouse/key binding modifiers. * syntax : * ModifierLocks [S][L][C][M][1][2][3][4][5] * * S is shift * L is caps lock * C is control * M is meta, the same as mod1 * 1-5 are mod1-mod5 * The default is L25 (ie. mask out caps lock, num lock & scroll lock * for XFree86 3.3.3.1 * * Benoit TRIQUET 2/21/99 ***********************************************************************/ void CMD_IgnoreModifiers(F_CMD_ARGS) { char *token; mods_unused = 0; GetNextToken(action, &token); if (!token) return; if (StrEquals(token, "default")) { free(token); mods_unused = MODS_UNUSED_DEFAULT; return; } if (ParseModifiers(token, &mods_unused)) fvwm_msg(ERR, "ignore_modifiers", "illegal modifier in line %s\n", action); free(token); return; }