diff options
Diffstat (limited to 'maximus')
-rw-r--r-- | maximus/Makefile.am | 51 | ||||
-rw-r--r-- | maximus/eggaccelerators.c | 656 | ||||
-rw-r--r-- | maximus/eggaccelerators.h | 86 | ||||
-rw-r--r-- | maximus/main.c | 126 | ||||
-rw-r--r-- | maximus/mate-maximus-autostart.desktop | 12 | ||||
-rw-r--r-- | maximus/maximus-app.c | 577 | ||||
-rw-r--r-- | maximus/maximus-app.h | 66 | ||||
-rw-r--r-- | maximus/maximus-bind.c | 501 | ||||
-rw-r--r-- | maximus/maximus-bind.h | 65 | ||||
-rw-r--r-- | maximus/maximus.schemas | 54 | ||||
-rw-r--r-- | maximus/tomboykeybinder.c | 337 | ||||
-rw-r--r-- | maximus/tomboykeybinder.h | 43 | ||||
-rw-r--r-- | maximus/xutils.c | 102 | ||||
-rw-r--r-- | maximus/xutils.h | 49 |
14 files changed, 2725 insertions, 0 deletions
diff --git a/maximus/Makefile.am b/maximus/Makefile.am new file mode 100644 index 0000000..67192c9 --- /dev/null +++ b/maximus/Makefile.am @@ -0,0 +1,51 @@ +bin_PROGRAMS=mate-maximus + +PKGDATADIR = $(datadir)/mate-maximus +DATADIR = $(datadir) +SYSCONFDIR = $(sysconfdir) +AM_CFLAGS = \ + $(MAXIMUS_DEPS_CFLAGS) \ + $(GCC_FLAGS) \ + -I$(top_builddir) \ + -I$(top_srcdir) \ + -I$(top_srcdir)/tidy \ + -D_GNU_SOURCE \ + -DPKGDATADIR=\"$(PKGDATADIR)\" \ + -DDATADIR=\"$(DATADIR)\" \ + -DSYSCONFDIR=\"$(SYSCONFDIR)\" + +mate_maximus_LDADD = \ + $(MAXIMUS_DEPS_LIBS) + +mate_maximus_SOURCES = \ + main.c \ + eggaccelerators.c \ + eggaccelerators.h \ + maximus-app.c \ + maximus-app.h \ + maximus-bind.c \ + maximus-bind.h \ + tomboykeybinder.c \ + tomboykeybinder.h \ + xutils.c \ + xutils.h + +schemadir = $(sysconfdir)/mateconf/schemas +schema_DATA = maximus.schemas + +if MATECONF_SCHEMAS_INSTALL +install-data-local: + -MATECONF_CONFIG_SOURCE=$(MATECONF_SCHEMA_CONFIG_SOURCE) \ + $(MATECONFTOOL) --makefile-install-rule $(schema_DATA) +endif + +if MATECONF_SCHEMAS_INSTALL +uninstall-local: + -MATECONF_CONFIG_SOURCE=$(MATECONF_SCHEMA_CONFIG_SOURCE) \ + $(MATECONFTOOL) --makefile-uninstall-rule $(schema_DATA) +endif + +desktopdir=$(sysconfdir)/xdg/autostart +dist_desktop_DATA=mate-maximus-autostart.desktop + +EXTRA_DIST = $(schema_DATA) diff --git a/maximus/eggaccelerators.c b/maximus/eggaccelerators.c new file mode 100644 index 0000000..5410259 --- /dev/null +++ b/maximus/eggaccelerators.c @@ -0,0 +1,656 @@ +/* eggaccelerators.c + * Copyright (C) 2002 Red Hat, Inc.; Copyright 1998, 2001 Tim Janik + * Developed by Havoc Pennington, Tim Janik + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA + */ + +#include "eggaccelerators.h" + +#include <string.h> +#include <gdk/gdkx.h> +#include <gdk/gdkkeysyms.h> + +enum +{ + EGG_MODMAP_ENTRY_SHIFT = 0, + EGG_MODMAP_ENTRY_LOCK = 1, + EGG_MODMAP_ENTRY_CONTROL = 2, + EGG_MODMAP_ENTRY_MOD1 = 3, + EGG_MODMAP_ENTRY_MOD2 = 4, + EGG_MODMAP_ENTRY_MOD3 = 5, + EGG_MODMAP_ENTRY_MOD4 = 6, + EGG_MODMAP_ENTRY_MOD5 = 7, + EGG_MODMAP_ENTRY_LAST = 8 +}; + +#define MODMAP_ENTRY_TO_MODIFIER(x) (1 << (x)) + +typedef struct +{ + EggVirtualModifierType mapping[EGG_MODMAP_ENTRY_LAST]; + +} EggModmap; + +const EggModmap* egg_keymap_get_modmap (GdkKeymap *keymap); + +static inline gboolean +is_alt (const gchar *string) +{ + return ((string[0] == '<') && + (string[1] == 'a' || string[1] == 'A') && + (string[2] == 'l' || string[2] == 'L') && + (string[3] == 't' || string[3] == 'T') && + (string[4] == '>')); +} + +static inline gboolean +is_ctl (const gchar *string) +{ + return ((string[0] == '<') && + (string[1] == 'c' || string[1] == 'C') && + (string[2] == 't' || string[2] == 'T') && + (string[3] == 'l' || string[3] == 'L') && + (string[4] == '>')); +} + +static inline gboolean +is_modx (const gchar *string) +{ + return ((string[0] == '<') && + (string[1] == 'm' || string[1] == 'M') && + (string[2] == 'o' || string[2] == 'O') && + (string[3] == 'd' || string[3] == 'D') && + (string[4] >= '1' && string[4] <= '5') && + (string[5] == '>')); +} + +static inline gboolean +is_ctrl (const gchar *string) +{ + return ((string[0] == '<') && + (string[1] == 'c' || string[1] == 'C') && + (string[2] == 't' || string[2] == 'T') && + (string[3] == 'r' || string[3] == 'R') && + (string[4] == 'l' || string[4] == 'L') && + (string[5] == '>')); +} + +static inline gboolean +is_shft (const gchar *string) +{ + return ((string[0] == '<') && + (string[1] == 's' || string[1] == 'S') && + (string[2] == 'h' || string[2] == 'H') && + (string[3] == 'f' || string[3] == 'F') && + (string[4] == 't' || string[4] == 'T') && + (string[5] == '>')); +} + +static inline gboolean +is_shift (const gchar *string) +{ + return ((string[0] == '<') && + (string[1] == 's' || string[1] == 'S') && + (string[2] == 'h' || string[2] == 'H') && + (string[3] == 'i' || string[3] == 'I') && + (string[4] == 'f' || string[4] == 'F') && + (string[5] == 't' || string[5] == 'T') && + (string[6] == '>')); +} + +static inline gboolean +is_control (const gchar *string) +{ + return ((string[0] == '<') && + (string[1] == 'c' || string[1] == 'C') && + (string[2] == 'o' || string[2] == 'O') && + (string[3] == 'n' || string[3] == 'N') && + (string[4] == 't' || string[4] == 'T') && + (string[5] == 'r' || string[5] == 'R') && + (string[6] == 'o' || string[6] == 'O') && + (string[7] == 'l' || string[7] == 'L') && + (string[8] == '>')); +} + +static inline gboolean +is_release (const gchar *string) +{ + return ((string[0] == '<') && + (string[1] == 'r' || string[1] == 'R') && + (string[2] == 'e' || string[2] == 'E') && + (string[3] == 'l' || string[3] == 'L') && + (string[4] == 'e' || string[4] == 'E') && + (string[5] == 'a' || string[5] == 'A') && + (string[6] == 's' || string[6] == 'S') && + (string[7] == 'e' || string[7] == 'E') && + (string[8] == '>')); +} + +static inline gboolean +is_meta (const gchar *string) +{ + return ((string[0] == '<') && + (string[1] == 'm' || string[1] == 'M') && + (string[2] == 'e' || string[2] == 'E') && + (string[3] == 't' || string[3] == 'T') && + (string[4] == 'a' || string[4] == 'A') && + (string[5] == '>')); +} + +static inline gboolean +is_super (const gchar *string) +{ + return ((string[0] == '<') && + (string[1] == 's' || string[1] == 'S') && + (string[2] == 'u' || string[2] == 'U') && + (string[3] == 'p' || string[3] == 'P') && + (string[4] == 'e' || string[4] == 'E') && + (string[5] == 'r' || string[5] == 'R') && + (string[6] == '>')); +} + +static inline gboolean +is_hyper (const gchar *string) +{ + return ((string[0] == '<') && + (string[1] == 'h' || string[1] == 'H') && + (string[2] == 'y' || string[2] == 'Y') && + (string[3] == 'p' || string[3] == 'P') && + (string[4] == 'e' || string[4] == 'E') && + (string[5] == 'r' || string[5] == 'R') && + (string[6] == '>')); +} + +/** + * egg_accelerator_parse_virtual: + * @accelerator: string representing an accelerator + * @accelerator_key: return location for accelerator keyval + * @accelerator_mods: return location for accelerator modifier mask + * + * Parses a string representing a virtual accelerator. The format + * looks like "<Control>a" or "<Shift><Alt>F1" or + * "<Release>z" (the last one is for key release). The parser + * is fairly liberal and allows lower or upper case, and also + * abbreviations such as "<Ctl>" and "<Ctrl>". + * + * If the parse fails, @accelerator_key and @accelerator_mods will + * be set to 0 (zero) and %FALSE will be returned. If the string contains + * only modifiers, @accelerator_key will be set to 0 but %TRUE will be + * returned. + * + * The virtual vs. concrete accelerator distinction is a relic of + * how the X Window System works; there are modifiers Mod2-Mod5 that + * can represent various keyboard keys (numlock, meta, hyper, etc.), + * the virtual modifier represents the keyboard key, the concrete + * modifier the actual Mod2-Mod5 bits in the key press event. + * + * Returns: %TRUE on success. + */ +gboolean +egg_accelerator_parse_virtual (const gchar *accelerator, + guint *accelerator_key, + EggVirtualModifierType *accelerator_mods) +{ + guint keyval; + GdkModifierType mods; + gint len; + gboolean bad_keyval; + + if (accelerator_key) + *accelerator_key = 0; + if (accelerator_mods) + *accelerator_mods = 0; + + g_return_val_if_fail (accelerator != NULL, FALSE); + + bad_keyval = FALSE; + + keyval = 0; + mods = 0; + len = strlen (accelerator); + while (len) + { + if (*accelerator == '<') + { + if (len >= 9 && is_release (accelerator)) + { + accelerator += 9; + len -= 9; + mods |= EGG_VIRTUAL_RELEASE_MASK; + } + else if (len >= 9 && is_control (accelerator)) + { + accelerator += 9; + len -= 9; + mods |= EGG_VIRTUAL_CONTROL_MASK; + } + else if (len >= 7 && is_shift (accelerator)) + { + accelerator += 7; + len -= 7; + mods |= EGG_VIRTUAL_SHIFT_MASK; + } + else if (len >= 6 && is_shft (accelerator)) + { + accelerator += 6; + len -= 6; + mods |= EGG_VIRTUAL_SHIFT_MASK; + } + else if (len >= 6 && is_ctrl (accelerator)) + { + accelerator += 6; + len -= 6; + mods |= EGG_VIRTUAL_CONTROL_MASK; + } + else if (len >= 6 && is_modx (accelerator)) + { + static const guint mod_vals[] = { + EGG_VIRTUAL_ALT_MASK, EGG_VIRTUAL_MOD2_MASK, EGG_VIRTUAL_MOD3_MASK, + EGG_VIRTUAL_MOD4_MASK, EGG_VIRTUAL_MOD5_MASK + }; + + len -= 6; + accelerator += 4; + mods |= mod_vals[*accelerator - '1']; + accelerator += 2; + } + else if (len >= 5 && is_ctl (accelerator)) + { + accelerator += 5; + len -= 5; + mods |= EGG_VIRTUAL_CONTROL_MASK; + } + else if (len >= 5 && is_alt (accelerator)) + { + accelerator += 5; + len -= 5; + mods |= EGG_VIRTUAL_ALT_MASK; + } + else if (len >= 6 && is_meta (accelerator)) + { + accelerator += 6; + len -= 6; + mods |= EGG_VIRTUAL_META_MASK; + } + else if (len >= 7 && is_hyper (accelerator)) + { + accelerator += 7; + len -= 7; + mods |= EGG_VIRTUAL_HYPER_MASK; + } + else if (len >= 7 && is_super (accelerator)) + { + accelerator += 7; + len -= 7; + mods |= EGG_VIRTUAL_SUPER_MASK; + } + else + { + gchar last_ch; + + last_ch = *accelerator; + while (last_ch && last_ch != '>') + { + last_ch = *accelerator; + accelerator += 1; + len -= 1; + } + } + } + else + { + keyval = gdk_keyval_from_name (accelerator); + + if (keyval == 0) + bad_keyval = TRUE; + + accelerator += len; + len -= len; + } + } + + if (accelerator_key) + *accelerator_key = gdk_keyval_to_lower (keyval); + if (accelerator_mods) + *accelerator_mods = mods; + + return !bad_keyval; +} + + +/** + * egg_virtual_accelerator_name: + * @accelerator_key: accelerator keyval + * @accelerator_mods: accelerator modifier mask + * @returns: a newly-allocated accelerator name + * + * Converts an accelerator keyval and modifier mask + * into a string parseable by egg_accelerator_parse_virtual(). + * For example, if you pass in #GDK_q and #EGG_VIRTUAL_CONTROL_MASK, + * this function returns "<Control>q". + * + * The caller of this function must free the returned string. + */ +gchar* +egg_virtual_accelerator_name (guint accelerator_key, + EggVirtualModifierType accelerator_mods) +{ + static const gchar text_release[] = "<Release>"; + static const gchar text_shift[] = "<Shift>"; + static const gchar text_control[] = "<Control>"; + static const gchar text_mod1[] = "<Alt>"; + static const gchar text_mod2[] = "<Mod2>"; + static const gchar text_mod3[] = "<Mod3>"; + static const gchar text_mod4[] = "<Mod4>"; + static const gchar text_mod5[] = "<Mod5>"; + static const gchar text_meta[] = "<Meta>"; + static const gchar text_super[] = "<Super>"; + static const gchar text_hyper[] = "<Hyper>"; + guint l; + gchar *keyval_name; + gchar *accelerator; + + accelerator_mods &= EGG_VIRTUAL_MODIFIER_MASK; + + keyval_name = gdk_keyval_name (gdk_keyval_to_lower (accelerator_key)); + if (!keyval_name) + keyval_name = ""; + + l = 0; + if (accelerator_mods & EGG_VIRTUAL_RELEASE_MASK) + l += sizeof (text_release) - 1; + if (accelerator_mods & EGG_VIRTUAL_SHIFT_MASK) + l += sizeof (text_shift) - 1; + if (accelerator_mods & EGG_VIRTUAL_CONTROL_MASK) + l += sizeof (text_control) - 1; + if (accelerator_mods & EGG_VIRTUAL_ALT_MASK) + l += sizeof (text_mod1) - 1; + if (accelerator_mods & EGG_VIRTUAL_MOD2_MASK) + l += sizeof (text_mod2) - 1; + if (accelerator_mods & EGG_VIRTUAL_MOD3_MASK) + l += sizeof (text_mod3) - 1; + if (accelerator_mods & EGG_VIRTUAL_MOD4_MASK) + l += sizeof (text_mod4) - 1; + if (accelerator_mods & EGG_VIRTUAL_MOD5_MASK) + l += sizeof (text_mod5) - 1; + if (accelerator_mods & EGG_VIRTUAL_META_MASK) + l += sizeof (text_meta) - 1; + if (accelerator_mods & EGG_VIRTUAL_HYPER_MASK) + l += sizeof (text_hyper) - 1; + if (accelerator_mods & EGG_VIRTUAL_SUPER_MASK) + l += sizeof (text_super) - 1; + l += strlen (keyval_name); + + accelerator = g_new (gchar, l + 1); + + l = 0; + accelerator[l] = 0; + if (accelerator_mods & EGG_VIRTUAL_RELEASE_MASK) + { + strcpy (accelerator + l, text_release); + l += sizeof (text_release) - 1; + } + if (accelerator_mods & EGG_VIRTUAL_SHIFT_MASK) + { + strcpy (accelerator + l, text_shift); + l += sizeof (text_shift) - 1; + } + if (accelerator_mods & EGG_VIRTUAL_CONTROL_MASK) + { + strcpy (accelerator + l, text_control); + l += sizeof (text_control) - 1; + } + if (accelerator_mods & EGG_VIRTUAL_ALT_MASK) + { + strcpy (accelerator + l, text_mod1); + l += sizeof (text_mod1) - 1; + } + if (accelerator_mods & EGG_VIRTUAL_MOD2_MASK) + { + strcpy (accelerator + l, text_mod2); + l += sizeof (text_mod2) - 1; + } + if (accelerator_mods & EGG_VIRTUAL_MOD3_MASK) + { + strcpy (accelerator + l, text_mod3); + l += sizeof (text_mod3) - 1; + } + if (accelerator_mods & EGG_VIRTUAL_MOD4_MASK) + { + strcpy (accelerator + l, text_mod4); + l += sizeof (text_mod4) - 1; + } + if (accelerator_mods & EGG_VIRTUAL_MOD5_MASK) + { + strcpy (accelerator + l, text_mod5); + l += sizeof (text_mod5) - 1; + } + if (accelerator_mods & EGG_VIRTUAL_META_MASK) + { + strcpy (accelerator + l, text_meta); + l += sizeof (text_meta) - 1; + } + if (accelerator_mods & EGG_VIRTUAL_HYPER_MASK) + { + strcpy (accelerator + l, text_hyper); + l += sizeof (text_hyper) - 1; + } + if (accelerator_mods & EGG_VIRTUAL_SUPER_MASK) + { + strcpy (accelerator + l, text_super); + l += sizeof (text_super) - 1; + } + + strcpy (accelerator + l, keyval_name); + + return accelerator; +} + +void +egg_keymap_resolve_virtual_modifiers (GdkKeymap *keymap, + EggVirtualModifierType virtual_mods, + GdkModifierType *concrete_mods) +{ + GdkModifierType concrete; + int i; + const EggModmap *modmap; + + g_return_if_fail (GDK_IS_KEYMAP (keymap)); + g_return_if_fail (concrete_mods != NULL); + + modmap = egg_keymap_get_modmap (keymap); + + /* Not so sure about this algorithm. */ + + concrete = 0; + i = 0; + while (i < EGG_MODMAP_ENTRY_LAST) + { + if (modmap->mapping[i] & virtual_mods) + concrete |= (1 << i); + + ++i; + } + + *concrete_mods = concrete; +} + +void +egg_keymap_virtualize_modifiers (GdkKeymap *keymap, + GdkModifierType concrete_mods, + EggVirtualModifierType *virtual_mods) +{ + GdkModifierType virtual; + int i; + const EggModmap *modmap; + + g_return_if_fail (GDK_IS_KEYMAP (keymap)); + g_return_if_fail (virtual_mods != NULL); + + modmap = egg_keymap_get_modmap (keymap); + + /* Not so sure about this algorithm. */ + + virtual = 0; + i = 0; + while (i < EGG_MODMAP_ENTRY_LAST) + { + if ((1 << i) & concrete_mods) + { + EggVirtualModifierType cleaned; + + cleaned = modmap->mapping[i] & ~(EGG_VIRTUAL_MOD2_MASK | + EGG_VIRTUAL_MOD3_MASK | + EGG_VIRTUAL_MOD4_MASK | + EGG_VIRTUAL_MOD5_MASK); + + if (cleaned != 0) + { + virtual |= cleaned; + } + else + { + /* Rather than dropping mod2->mod5 if not bound, + * go ahead and use the concrete names + */ + virtual |= modmap->mapping[i]; + } + } + + ++i; + } + + *virtual_mods = virtual; +} + +static void +reload_modmap (GdkKeymap *keymap, + EggModmap *modmap) +{ + XModifierKeymap *xmodmap; + int map_size; + int i; + + /* FIXME multihead */ + xmodmap = XGetModifierMapping (gdk_x11_get_default_xdisplay ()); + + memset (modmap->mapping, 0, sizeof (modmap->mapping)); + + /* there are 8 modifiers, and the first 3 are shift, shift lock, + * and control + */ + map_size = 8 * xmodmap->max_keypermod; + i = 3 * xmodmap->max_keypermod; + while (i < map_size) + { + /* get the key code at this point in the map, + * see if its keysym is one we're interested in + */ + int keycode = xmodmap->modifiermap[i]; + GdkKeymapKey *keys; + guint *keyvals; + int n_entries; + int j; + EggVirtualModifierType mask; + + keys = NULL; + keyvals = NULL; + n_entries = 0; + + gdk_keymap_get_entries_for_keycode (keymap, + keycode, + &keys, &keyvals, &n_entries); + + mask = 0; + j = 0; + while (j < n_entries) + { + if (keyvals[j] == GDK_Num_Lock) + mask |= EGG_VIRTUAL_NUM_LOCK_MASK; + else if (keyvals[j] == GDK_Scroll_Lock) + mask |= EGG_VIRTUAL_SCROLL_LOCK_MASK; + else if (keyvals[j] == GDK_Meta_L || + keyvals[j] == GDK_Meta_R) + mask |= EGG_VIRTUAL_META_MASK; + else if (keyvals[j] == GDK_Hyper_L || + keyvals[j] == GDK_Hyper_R) + mask |= EGG_VIRTUAL_HYPER_MASK; + else if (keyvals[j] == GDK_Super_L || + keyvals[j] == GDK_Super_R) + mask |= EGG_VIRTUAL_SUPER_MASK; + else if (keyvals[j] == GDK_Mode_switch) + mask |= EGG_VIRTUAL_MODE_SWITCH_MASK; + + ++j; + } + + /* Mod1Mask is 1 << 3 for example, i.e. the + * fourth modifier, i / keyspermod is the modifier + * index + */ + modmap->mapping[i/xmodmap->max_keypermod] |= mask; + + g_free (keyvals); + g_free (keys); + + ++i; + } + + /* Add in the not-really-virtual fixed entries */ + modmap->mapping[EGG_MODMAP_ENTRY_SHIFT] |= EGG_VIRTUAL_SHIFT_MASK; + modmap->mapping[EGG_MODMAP_ENTRY_CONTROL] |= EGG_VIRTUAL_CONTROL_MASK; + modmap->mapping[EGG_MODMAP_ENTRY_LOCK] |= EGG_VIRTUAL_LOCK_MASK; + modmap->mapping[EGG_MODMAP_ENTRY_MOD1] |= EGG_VIRTUAL_ALT_MASK; + modmap->mapping[EGG_MODMAP_ENTRY_MOD2] |= EGG_VIRTUAL_MOD2_MASK; + modmap->mapping[EGG_MODMAP_ENTRY_MOD3] |= EGG_VIRTUAL_MOD3_MASK; + modmap->mapping[EGG_MODMAP_ENTRY_MOD4] |= EGG_VIRTUAL_MOD4_MASK; + modmap->mapping[EGG_MODMAP_ENTRY_MOD5] |= EGG_VIRTUAL_MOD5_MASK; + + XFreeModifiermap (xmodmap); +} + +const EggModmap* +egg_keymap_get_modmap (GdkKeymap *keymap) +{ + EggModmap *modmap; + + /* This is all a hack, much simpler when we can just + * modify GDK directly. + */ + + modmap = g_object_get_data (G_OBJECT (keymap), + "egg-modmap"); + + if (modmap == NULL) + { + modmap = g_new0 (EggModmap, 1); + + /* FIXME modify keymap change events with an event filter + * and force a reload if we get one + */ + + reload_modmap (keymap, modmap); + + g_object_set_data_full (G_OBJECT (keymap), + "egg-modmap", + modmap, + g_free); + } + + g_assert (modmap != NULL); + + return modmap; +} diff --git a/maximus/eggaccelerators.h b/maximus/eggaccelerators.h new file mode 100644 index 0000000..18e3ae0 --- /dev/null +++ b/maximus/eggaccelerators.h @@ -0,0 +1,86 @@ +/* eggaccelerators.h + * Copyright (C) 2002 Red Hat, Inc. + * Developed by Havoc Pennington + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA + */ + +#ifndef __EGG_ACCELERATORS_H__ +#define __EGG_ACCELERATORS_H__ + +#include <gtk/gtkaccelgroup.h> +#include <gdk/gdk.h> + +G_BEGIN_DECLS + +/* Where a value is also in GdkModifierType we coincide, + * otherwise we don't overlap. + */ +typedef enum +{ + EGG_VIRTUAL_SHIFT_MASK = 1 << 0, + EGG_VIRTUAL_LOCK_MASK = 1 << 1, + EGG_VIRTUAL_CONTROL_MASK = 1 << 2, + + EGG_VIRTUAL_ALT_MASK = 1 << 3, /* fixed as Mod1 */ + + EGG_VIRTUAL_MOD2_MASK = 1 << 4, + EGG_VIRTUAL_MOD3_MASK = 1 << 5, + EGG_VIRTUAL_MOD4_MASK = 1 << 6, + EGG_VIRTUAL_MOD5_MASK = 1 << 7, + +#if 0 + GDK_BUTTON1_MASK = 1 << 8, + GDK_BUTTON2_MASK = 1 << 9, + GDK_BUTTON3_MASK = 1 << 10, + GDK_BUTTON4_MASK = 1 << 11, + GDK_BUTTON5_MASK = 1 << 12, + /* 13, 14 are used by Xkb for the keyboard group */ +#endif + + EGG_VIRTUAL_META_MASK = 1 << 24, + EGG_VIRTUAL_SUPER_MASK = 1 << 25, + EGG_VIRTUAL_HYPER_MASK = 1 << 26, + EGG_VIRTUAL_MODE_SWITCH_MASK = 1 << 27, + EGG_VIRTUAL_NUM_LOCK_MASK = 1 << 28, + EGG_VIRTUAL_SCROLL_LOCK_MASK = 1 << 29, + + /* Also in GdkModifierType */ + EGG_VIRTUAL_RELEASE_MASK = 1 << 30, + + /* 28-31 24-27 20-23 16-19 12-15 8-11 4-7 0-3 + * 7 f 0 0 0 0 f f + */ + EGG_VIRTUAL_MODIFIER_MASK = 0x7f0000ff + +} EggVirtualModifierType; + +gboolean egg_accelerator_parse_virtual (const gchar *accelerator, + guint *accelerator_key, + EggVirtualModifierType *accelerator_mods); +void egg_keymap_resolve_virtual_modifiers (GdkKeymap *keymap, + EggVirtualModifierType virtual_mods, + GdkModifierType *concrete_mods); +void egg_keymap_virtualize_modifiers (GdkKeymap *keymap, + GdkModifierType concrete_mods, + EggVirtualModifierType *virtual_mods); + +gchar* egg_virtual_accelerator_name (guint accelerator_key, + EggVirtualModifierType accelerator_mods); + +G_END_DECLS + + +#endif /* __EGG_ACCELERATORS_H__ */ diff --git a/maximus/main.c b/maximus/main.c new file mode 100644 index 0000000..4855aac --- /dev/null +++ b/maximus/main.c @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2008 Canonical Ltd + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + * + * 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, see <http://www.gnu.org/licenses/>. + * + * Authored by Neil Jagdish Patel <[email protected]> + * + */ + +#include <glib.h> + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#include <gtk/gtk.h> +#include <gdk/gdkx.h> + +#include <libmateui/libmateui.h> +#include <unique/unique.h> + +#include "maximus-app.h" + +static gboolean on_sess_save (MateClient *client, + gint arg1, + MateSaveStyle arg2, + gboolean arg3, + MateInteractStyle arg4, + gboolean arg5); + +static gboolean no_restart = FALSE; +static gboolean version = FALSE; +gboolean no_maximize = FALSE; + +GOptionEntry entries[] = +{ + { + "no-restart", 's', + 0, G_OPTION_ARG_NONE, + &no_restart, + "Do not automatically restart (standalone)", NULL + }, + { + "version", 'v', + 0, G_OPTION_ARG_NONE, + &version, + "Prints the version number", NULL + }, + { + "no-maximize", 'm', + 0, G_OPTION_ARG_NONE, + &no_maximize, + "Do not automatically maximize every window", NULL + }, + { + NULL + } +}; + +gint main (gint argc, gchar *argv[]) +{ + UniqueApp *unique; + MaximusApp *app; + MateClient *client; + GOptionContext *context; + gchar *exec[] = {"maximus"}; + + g_thread_init (NULL); + g_set_application_name ("Maximus"); + + gtk_init (&argc, &argv); + + unique = unique_app_new ("com.canonical.Maximus", NULL); + + if (unique_app_is_running (unique)) + { + return 0; + } + + context = g_option_context_new ("- Maximus"); + g_option_context_add_main_entries (context, entries, "maximus"); + g_option_context_add_group (context, gtk_get_option_group (TRUE)); + g_option_context_parse (context, &argc, &argv, NULL); + g_option_context_free(context); + + if (!no_restart) + { + mate_program_init ("mate-maximus", "1.3.0", LIBMATEUI_MODULE, argc, argv, + MATE_PARAM_NONE, NULL); + + client = mate_master_client (); + mate_client_set_restart_command (client, 1, exec); + mate_client_set_restart_style (client, MATE_RESTART_IMMEDIATELY); + + g_signal_connect (client, "save-yourself", + G_CALLBACK (on_sess_save), NULL); + } + + gdk_error_trap_push (); + app = maximus_app_get_default (); + gdk_error_trap_pop (); + + gtk_main (); + + return EXIT_SUCCESS; +} + +static gboolean on_sess_save (MateClient *client, + gint arg1, + MateSaveStyle arg2, + gboolean arg3, + MateInteractStyle arg4, + gboolean arg5) +{ + return TRUE; +} diff --git a/maximus/mate-maximus-autostart.desktop b/maximus/mate-maximus-autostart.desktop new file mode 100644 index 0000000..c2c85a2 --- /dev/null +++ b/maximus/mate-maximus-autostart.desktop @@ -0,0 +1,12 @@ +[Desktop Entry] +Encoding=UTF-8 +Name=Maximus Window Management +Comment=Controls the displaying of windows +Icon=preferences-system-windows +Exec=mate-maximus +Terminal=false +Type=Application +Categories= +OnlyShowIn=MATE; +X-MATE-Autostart-Phase=Desktop +X-MATE-Autostart-Notify=false diff --git a/maximus/maximus-app.c b/maximus/maximus-app.c new file mode 100644 index 0000000..be9a2fc --- /dev/null +++ b/maximus/maximus-app.c @@ -0,0 +1,577 @@ +/* + * Copyright (C) 2008 Canonical Ltd + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + * + * 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, see <http://www.gnu.org/licenses/>. + * + * Authored by Neil Jagdish Patel <[email protected]> + * + */ + +#include <stdio.h> +#include <string.h> +#include <unistd.h> + +#include <gtk/gtk.h> +#include <gdk/gdkx.h> + +#include <mateconf/mateconf.h> +#include <mateconf/mateconf-client.h> + +#include "maximus-app.h" +#include "maximus-bind.h" +#include "xutils.h" + +G_DEFINE_TYPE (MaximusApp, maximus_app, G_TYPE_OBJECT); + +#define MAXIMUS_APP_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj),\ + MAXIMUS_TYPE_APP, \ + MaximusAppPrivate)) + +/* Gconf keys */ +#define APP_PATH "/apps/maximus" +#define APP_EXCLUDE_CLASS APP_PATH "/exclude_class" +#define APP_UNDECORATE APP_PATH "/undecorate" +#define APP_NO_MAXIMIZE APP_PATH "/no_maximize" + +/* A set of default exceptions */ +static gchar *default_exclude_classes[] = +{ + "Apport-gtk", + "Bluetooth-properties", + "Bluetooth-wizard", + "Download", /* Firefox Download Window */ + "Ekiga", + "Extension", /* Firefox Add-Ons/Extension Window */ + "Gcalctool", + "Gimp", + "Global", /* Firefox Error Console Window */ + "Mate-dictionary", + "Mate-language-selector", + "Mate-nettool", + "Mate-volume-control", + "Kiten", + "Kmplot", + "Nm-editor", + "Pidgin", + "Polkit-mate-authorization", + "Update-manager", + "Skype", + "Toplevel", /* Firefox "Clear Private Data" Window */ + "Transmission" +}; + +struct _MaximusAppPrivate +{ + MaximusBind *bind; + MatewnckScreen *screen; + + GSList *exclude_class_list; + gboolean undecorate; + gboolean no_maximize; +}; + +static GQuark was_decorated = 0; + +/* <TAKEN FROM GDK> */ +typedef struct { + unsigned long flags; + unsigned long functions; + unsigned long decorations; + long input_mode; + unsigned long status; +} MotifWmHints, MwmHints; + +#define MWM_HINTS_FUNCTIONS (1L << 0) +#define MWM_HINTS_DECORATIONS (1L << 1) +#define _XA_MOTIF_WM_HINTS "_MOTIF_WM_HINTS" + +static gboolean +matewnck_window_is_decorated (MatewnckWindow *window) +{ + GdkDisplay *display = gdk_display_get_default(); + Atom hints_atom = None; + guchar *data = NULL; + MotifWmHints *hints = NULL; + Atom type = None; + gint format; + gulong nitems; + gulong bytes_after; + gboolean retval; + + g_return_val_if_fail (MATEWNCK_IS_WINDOW (window), FALSE); + + hints_atom = gdk_x11_get_xatom_by_name_for_display (display, + _XA_MOTIF_WM_HINTS); + + XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display), + matewnck_window_get_xid (window), + hints_atom, 0, sizeof (MotifWmHints)/sizeof (long), + False, AnyPropertyType, &type, &format, &nitems, + &bytes_after, &data); + + if (type == None || !data) return TRUE; + + hints = (MotifWmHints *)data; + + retval = hints->decorations; + + if (data) + XFree (data); + + return retval; +} + +static void +gdk_window_set_mwm_hints (MatewnckWindow *window, + MotifWmHints *new_hints) +{ + GdkDisplay *display = gdk_display_get_default(); + Atom hints_atom = None; + guchar *data = NULL; + MotifWmHints *hints = NULL; + Atom type = None; + gint format; + gulong nitems; + gulong bytes_after; + + g_return_if_fail (MATEWNCK_IS_WINDOW (window)); + g_return_if_fail (GDK_IS_DISPLAY (display)); + + hints_atom = gdk_x11_get_xatom_by_name_for_display (display, + _XA_MOTIF_WM_HINTS); + + XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display), + matewnck_window_get_xid (window), + hints_atom, 0, sizeof (MotifWmHints)/sizeof (long), + False, AnyPropertyType, &type, &format, &nitems, + &bytes_after, &data); + + if (type != hints_atom || !data) + hints = new_hints; + else + { + hints = (MotifWmHints *)data; + + if (new_hints->flags & MWM_HINTS_FUNCTIONS) + { + hints->flags |= MWM_HINTS_FUNCTIONS; + hints->functions = new_hints->functions; + } + if (new_hints->flags & MWM_HINTS_DECORATIONS) + { + hints->flags |= MWM_HINTS_DECORATIONS; + hints->decorations = new_hints->decorations; + } + } + + _matewnck_error_trap_push (); + XChangeProperty (GDK_DISPLAY_XDISPLAY (display), + matewnck_window_get_xid (window), + hints_atom, hints_atom, 32, PropModeReplace, + (guchar *)hints, sizeof (MotifWmHints)/sizeof (long)); + gdk_flush (); + _matewnck_error_trap_pop (); + + if (data) + XFree (data); +} + +static void +_window_set_decorations (MatewnckWindow *window, + GdkWMDecoration decorations) +{ + MotifWmHints *hints; + + g_return_if_fail (MATEWNCK_IS_WINDOW (window)); + + /* initialize to zero to avoid writing uninitialized data to socket */ + hints = g_slice_new0 (MotifWmHints); + hints->flags = MWM_HINTS_DECORATIONS; + hints->decorations = decorations; + + gdk_window_set_mwm_hints (window, hints); + + g_slice_free (MotifWmHints, hints); +} + +/* </TAKEN FROM GDK> */ + +gboolean +window_is_too_large_for_screen (MatewnckWindow *window) +{ + static GdkScreen *screen = NULL; + gint x=0, y=0, w=0, h=0; + + g_return_val_if_fail (MATEWNCK_IS_WINDOW (window), FALSE); + + if (screen == NULL) + screen = gdk_screen_get_default (); + + matewnck_window_get_geometry (window, &x, &y, &w, &h); + + /* some wiggle room */ + return (screen && + (w > (gdk_screen_get_width (screen) + 20) || + h > (gdk_screen_get_height (screen)+20))); +} + +static gboolean +on_window_maximised_changed (MatewnckWindow *window) +{ + g_return_val_if_fail (MATEWNCK_IS_WINDOW (window), FALSE); + + if (window_is_too_large_for_screen (window)) + { + _window_set_decorations (window, 1); + matewnck_window_unmaximize (window); + } + else + { + _window_set_decorations (window, 0); + } + return FALSE; +} + +static gboolean +enable_window_decorations (MatewnckWindow *window) +{ + g_return_val_if_fail (MATEWNCK_IS_WINDOW (window), FALSE); + + _window_set_decorations (window, 1); + return FALSE; +} + +static void +on_window_state_changed (MatewnckWindow *window, + MatewnckWindowState change_mask, + MatewnckWindowState new_state, + MaximusApp *app) +{ + g_return_if_fail (MATEWNCK_IS_WINDOW (window)); + + if (GPOINTER_TO_INT (g_object_get_data (G_OBJECT (window), "exclude"))==1) + return; + + if (change_mask & MATEWNCK_WINDOW_STATE_MAXIMIZED_HORIZONTALLY + || change_mask & MATEWNCK_WINDOW_STATE_MAXIMIZED_VERTICALLY) + { + if (matewnck_window_is_maximized (window) && app->priv->undecorate) + { + g_idle_add ((GSourceFunc)on_window_maximised_changed, window); + } + else + { + g_idle_add ((GSourceFunc)enable_window_decorations, window); + } + } +} + +static gboolean +is_excluded (MaximusApp *app, MatewnckWindow *window) +{ + MaximusAppPrivate *priv; + MatewnckWindowType type; + MatewnckWindowActions actions; + gchar *res_name; + gchar *class_name; + GSList *c; + gint i; + + g_return_val_if_fail (MAXIMUS_IS_APP (app), TRUE); + g_return_val_if_fail (MATEWNCK_IS_WINDOW (window), TRUE); + priv = app->priv; + + type = matewnck_window_get_window_type (window); + if (type != MATEWNCK_WINDOW_NORMAL) + return TRUE; + + if (GPOINTER_TO_INT (g_object_get_data (G_OBJECT (window), "exclude"))==1) + return TRUE; + + /* Ignore if the window is already fullscreen */ + if (matewnck_window_is_fullscreen (window)) + { + g_debug ("Excluding (is fullscreen): %s\n",matewnck_window_get_name (window)); + return TRUE; + } + + /* Make sure the window supports maximising */ + actions = matewnck_window_get_actions (window); + if (actions & MATEWNCK_WINDOW_ACTION_RESIZE + && actions & MATEWNCK_WINDOW_ACTION_MAXIMIZE_HORIZONTALLY + && actions & MATEWNCK_WINDOW_ACTION_MAXIMIZE_VERTICALLY + && actions & MATEWNCK_WINDOW_ACTION_MAXIMIZE) + ; /* Is good to maximise */ + else + return TRUE; + + _matewnck_get_wmclass (matewnck_window_get_xid (window), &res_name, &class_name); + + g_debug ("Window opened: res_name=%s -- class_name=%s", res_name, class_name); + + /* Check internal list of class_ids */ + for (i = 0; i < G_N_ELEMENTS (default_exclude_classes); i++) + { + if ((class_name && default_exclude_classes[i] + && strstr (class_name, default_exclude_classes[i])) + || (res_name && default_exclude_classes[i] && strstr (res_name, + default_exclude_classes[i]))) + { + g_debug ("Excluding: %s\n", matewnck_window_get_name (window)); + return TRUE; + } + } + + /* Check user list */ + for (c = priv->exclude_class_list; c; c = c->next) + { + if ((class_name && c->data && strstr (class_name, c->data)) + || (res_name && c->data && strstr (res_name, c->data) )) + { + g_debug ("Excluding: %s\n", matewnck_window_get_name (window)); + return TRUE; + } + } + + g_free (res_name); + g_free (class_name); + return FALSE; +} + +extern gboolean no_maximize; + +static void +on_window_opened (MatewnckScreen *screen, + MatewnckWindow *window, + MaximusApp *app) +{ + MaximusAppPrivate *priv; + MatewnckWindowType type; + gint exclude = 0; + + g_return_if_fail (MAXIMUS_IS_APP (app)); + g_return_if_fail (MATEWNCK_IS_WINDOW (window)); + priv = app->priv; + + type = matewnck_window_get_window_type (window); + if (type != MATEWNCK_WINDOW_NORMAL) + return; + + /* Ignore undecorated windows */ + exclude = matewnck_window_is_decorated (window) ? 0 : 1; + if (matewnck_window_is_maximized (window)) + exclude = 0; + g_object_set_data (G_OBJECT (window), "exclude", GINT_TO_POINTER (exclude)); + + if (is_excluded (app, window)) + { + g_signal_connect (window, "state-changed", + G_CALLBACK (on_window_state_changed), app); + return; + } + + if (no_maximize || priv->no_maximize) + { + if (matewnck_window_is_maximized(window)) + { + _window_set_decorations (window, 0); + gdk_flush (); + } + g_signal_connect (window, "state-changed", + G_CALLBACK (on_window_state_changed), app); + return; + } + + if (priv->undecorate) + { + /* Only undecorate right now if the window is smaller than the screen */ + if (!window_is_too_large_for_screen (window)) + { + _window_set_decorations (window, 0); + gdk_flush (); + } + } + + matewnck_window_maximize (window); + + g_signal_connect (window, "state-changed", + G_CALLBACK (on_window_state_changed), app); +} + +static void +on_app_no_maximize_changed (MateConfClient *client, + guint cid, + MateConfEntry *entry, + MaximusApp *app) +{ + MaximusAppPrivate *priv; + MateConfValue* value; + + g_return_if_fail (MAXIMUS_IS_APP (app)); + priv = app->priv; + + if (entry == NULL) + { + priv->no_maximize = FALSE; + } + else + { + value = mateconf_entry_get_value(entry); + priv->no_maximize = value != NULL && mateconf_value_get_bool(value); + } +} + +/* MateConf Callbacks */ +static void +on_exclude_class_changed (MateConfClient *client, + guint cid, + MateConfEntry *entry, + MaximusApp *app) +{ + MaximusAppPrivate *priv; + + g_return_if_fail (MAXIMUS_IS_APP (app)); + priv = app->priv; + + if (priv->exclude_class_list) + g_slist_free (priv->exclude_class_list); + + priv->exclude_class_list= mateconf_client_get_list (client, + APP_EXCLUDE_CLASS, + MATECONF_VALUE_STRING, + NULL); +} + +static gboolean +show_desktop (MatewnckScreen *screen) +{ + g_return_val_if_fail (MATEWNCK_IS_SCREEN (screen), FALSE); + + matewnck_screen_toggle_showing_desktop (screen, TRUE); + return FALSE; +} + +static void +on_app_undecorate_changed (MateConfClient *client, + guint cid, + MateConfEntry *entry, + MaximusApp *app) +{ + MaximusAppPrivate *priv; + GList *windows, *w; + + g_return_if_fail (MAXIMUS_IS_APP (app)); + priv = app->priv; + g_return_if_fail (MATEWNCK_IS_SCREEN (priv->screen)); + + priv->undecorate = mateconf_client_get_bool (client, + APP_UNDECORATE, + NULL); + g_debug ("%s\n", priv->undecorate ? "Undecorating" : "Decorating"); + + windows = matewnck_screen_get_windows (priv->screen); + for (w = windows; w; w = w->next) + { + MatewnckWindow *window = w->data; + + if (!MATEWNCK_IS_WINDOW (window)) + continue; + + if (no_maximize || priv->no_maximize) + { + if (!matewnck_window_is_maximized(window)) + continue; + } + + if (!is_excluded (app, window)) + { + gdk_error_trap_push (); + _window_set_decorations (window, priv->undecorate ? 0 : 1); + matewnck_window_unmaximize (window); + matewnck_window_maximize (window); + gdk_flush (); + gdk_error_trap_pop (); + + sleep (1); + } + } + /* We want the user to be left on the launcher/desktop after switching modes*/ + g_timeout_add_seconds (1, (GSourceFunc)show_desktop, priv->screen); +} + + +/* GObject stuff */ +static void +maximus_app_class_init (MaximusAppClass *klass) +{ + GObjectClass *obj_class = G_OBJECT_CLASS (klass); + + g_type_class_add_private (obj_class, sizeof (MaximusAppPrivate)); +} + +static void +maximus_app_init (MaximusApp *app) +{ + MaximusAppPrivate *priv; + MateConfClient *client = mateconf_client_get_default (); + MatewnckScreen *screen; + + priv = app->priv = MAXIMUS_APP_GET_PRIVATE (app); + + priv->bind = maximus_bind_get_default (); + + was_decorated = g_quark_from_static_string ("was-decorated"); + + mateconf_client_add_dir (client, APP_PATH, MATECONF_CLIENT_PRELOAD_NONE, NULL); + + priv->exclude_class_list= mateconf_client_get_list (client, + APP_EXCLUDE_CLASS, + MATECONF_VALUE_STRING, + NULL); + mateconf_client_notify_add (client, APP_EXCLUDE_CLASS, + (MateConfClientNotifyFunc)on_exclude_class_changed, + app, NULL, NULL); + + priv->undecorate = mateconf_client_get_bool(client, + APP_UNDECORATE, + NULL); + mateconf_client_notify_add (client, APP_UNDECORATE, + (MateConfClientNotifyFunc)on_app_undecorate_changed, + app, NULL, NULL); + + + priv->screen = screen = matewnck_screen_get_default (); + g_signal_connect (screen, "window-opened", + G_CALLBACK (on_window_opened), app); + + priv->no_maximize = mateconf_client_get_bool(client, + APP_NO_MAXIMIZE, + NULL); + g_print ("no maximize: %s\n", priv->no_maximize ? "true" : "false"); + mateconf_client_notify_add (client, APP_NO_MAXIMIZE, + (MateConfClientNotifyFunc)on_app_no_maximize_changed, + app, NULL, NULL); +} + +MaximusApp * +maximus_app_get_default (void) + +{ + static MaximusApp *app = NULL; + + if (!app) + app = g_object_new (MAXIMUS_TYPE_APP, + NULL); + + return app; +} diff --git a/maximus/maximus-app.h b/maximus/maximus-app.h new file mode 100644 index 0000000..8eb64f7 --- /dev/null +++ b/maximus/maximus-app.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2008 Canonical Ltd + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + * + * 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, see <http://www.gnu.org/licenses/>. + * + * Authored by Neil Jagdish Patel <[email protected]> + * + */ + +#ifndef _MAXIMUS_APP_H_ +#define _MAXIMUS_APP_H_ + +#include <glib-object.h> +#include <gtk/gtk.h> +#include <libmatewnck/libmatewnck.h> + +#define MAXIMUS_TYPE_APP (maximus_app_get_type ()) + +#define MAXIMUS_APP(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj),\ + MAXIMUS_TYPE_APP, MaximusApp)) + +#define MAXIMUS_APP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass),\ + MAXIMUS_TYPE_APP, MaximusAppClass)) + +#define MAXIMUS_IS_APP(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj),\ + MAXIMUS_TYPE_APP)) + +#define MAXIMUS_IS_APP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),\ + MAXIMUS_TYPE_APP)) + +#define MAXIMUS_APP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj),\ + MAXIMUS_TYPE_APP, MaximusAppClass)) + +typedef struct _MaximusApp MaximusApp; +typedef struct _MaximusAppClass MaximusAppClass; +typedef struct _MaximusAppPrivate MaximusAppPrivate; + +struct _MaximusApp +{ + GObject parent; + + MaximusAppPrivate *priv; +}; + +struct _MaximusAppClass +{ + GObjectClass parent_class; +}; + +GType maximus_app_get_type (void) G_GNUC_CONST; + +MaximusApp * maximus_app_get_default (void); + + +#endif /* _MAXIMUS_APP_H_ */ + diff --git a/maximus/maximus-bind.c b/maximus/maximus-bind.c new file mode 100644 index 0000000..cbbcf2e --- /dev/null +++ b/maximus/maximus-bind.c @@ -0,0 +1,501 @@ +/* + * Copyright (C) 2008 Canonical Ltd + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + * + * 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, see <http://www.gnu.org/licenses/>. + * + * Authored by Neil Jagdish Patel <[email protected]> + * + */ + +#include <stdio.h> +#include <string.h> + +#include <gtk/gtk.h> +#include <gdk/gdkx.h> + +#include <gdk/gdkkeysyms.h> + +#include <mateconf/mateconf.h> +#include <mateconf/mateconf-client.h> + +#include <libmatewnck/libmatewnck.h> + +#include <X11/Xlib.h> +#include <X11/Xresource.h> +#include <X11/Xutil.h> +#include <X11/extensions/XTest.h> +#include <X11/keysymdef.h> +#include <X11/keysym.h> + +#include <fakekey/fakekey.h> + +#include "maximus-bind.h" + +#include "tomboykeybinder.h" +#include "eggaccelerators.h" + +G_DEFINE_TYPE (MaximusBind, maximus_bind, G_TYPE_OBJECT); + +#define MAXIMUS_BIND_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj),\ + MAXIMUS_TYPE_BIND, \ + MaximusBindPrivate)) + +#define KEY_RELEASE_TIMEOUT 300 +#define STATE_CHANGED_SLEEP 0.5 + +/* Gconf keys */ +#define BIND_PATH "/apps/maximus" +#define BIND_EXCLUDE_CLASS BIND_PATH "/binding" + +#define SYSRULESDIR SYSCONFDIR"/maximus" + +struct _MaximusBindPrivate +{ + FakeKey *fk; + MatewnckScreen *screen; + + gchar *binding; + + GList *rules; +}; + +typedef struct +{ + gchar *wm_class; + gchar *fullscreen; + gchar *unfullscreen; +} MaximusRule; + +static const gchar * +get_fullscreen_keystroke (GList *rules, MatewnckWindow *window) +{ + MatewnckClassGroup *group; + const gchar *class_name; + GList *r; + + group = matewnck_window_get_class_group (window); + class_name = matewnck_class_group_get_name (group); + + g_debug ("Searching rules for %s:\n", matewnck_window_get_name (window)); + + for (r = rules; r; r = r->next) + { + MaximusRule *rule = r->data; + + g_debug ("\t%s ?= %s", class_name, rule->wm_class); + + if (class_name && rule->wm_class && strstr (class_name, rule->wm_class)) + { + g_debug ("\tYES!\n"); + return rule->fullscreen; + } + g_debug ("\tNO!\n"); + } + + return NULL; +} + +static const gchar * +get_unfullscreen_keystroke (GList *rules, MatewnckWindow *window) +{ + MatewnckClassGroup *group; + const gchar *class_name; + GList *r; + + group = matewnck_window_get_class_group (window); + class_name = matewnck_class_group_get_name (group); + + for (r = rules; r; r = r->next) + { + MaximusRule *rule = r->data; + + if (class_name && rule->wm_class && strstr (class_name, rule->wm_class)) + { + return rule->unfullscreen; + } + } + + return NULL; +} +static gboolean +real_fullscreen (MaximusBind *bind) +{ + MaximusBindPrivate *priv; + GdkDisplay *display; + MatewnckWindow *active; + const gchar *keystroke; + + priv = bind->priv; + + display = gdk_display_get_default (); + active = matewnck_screen_get_active_window (priv->screen); + + if (!MATEWNCK_IS_WINDOW (active) + || matewnck_window_get_window_type (active) != MATEWNCK_WINDOW_NORMAL) + return FALSE; + + keystroke = get_fullscreen_keystroke (priv->rules, active); + + if (keystroke) + { + guint keysym = 0; + EggVirtualModifierType modifiers = 0; + + if (egg_accelerator_parse_virtual (keystroke, &keysym, &modifiers)) + { + guint mods = 0; + + if (modifiers & EGG_VIRTUAL_SHIFT_MASK) + mods |= FAKEKEYMOD_SHIFT; + if (modifiers & EGG_VIRTUAL_CONTROL_MASK) + mods |= FAKEKEYMOD_CONTROL; + if (modifiers & EGG_VIRTUAL_ALT_MASK) + mods |= FAKEKEYMOD_ALT; + if (modifiers & EGG_VIRTUAL_META_MASK) + mods |= FAKEKEYMOD_META; + + g_debug ("Sending fullscreen special event: %s = %d %d", + keystroke, keysym, mods); + fakekey_press_keysym (priv->fk, keysym, mods); + fakekey_release (priv->fk); + + return FALSE; + } + } + + if (!matewnck_window_is_fullscreen (active)) + { + g_debug ("Sending fullscreen F11 event"); + fakekey_press_keysym (priv->fk, XK_F11, 0); + fakekey_release (priv->fk); + } + + sleep (STATE_CHANGED_SLEEP); + + if (!matewnck_window_is_fullscreen (active)) + { + g_debug ("Forcing fullscreen matewnck event"); + matewnck_window_set_fullscreen (active, TRUE); + } + + return FALSE; +} + +static void +fullscreen (MaximusBind *bind, MatewnckWindow *window) +{ + MaximusBindPrivate *priv; + + priv = bind->priv; + + g_timeout_add (KEY_RELEASE_TIMEOUT, (GSourceFunc)real_fullscreen, bind); +} + +static gboolean +real_unfullscreen (MaximusBind *bind) +{ + MaximusBindPrivate *priv; + GdkDisplay *display; + MatewnckWindow *active; + const gchar *keystroke; + + priv = bind->priv; + + display = gdk_display_get_default (); + active = matewnck_screen_get_active_window (priv->screen); + + if (!MATEWNCK_IS_WINDOW (active) + || matewnck_window_get_window_type (active) != MATEWNCK_WINDOW_NORMAL) + return FALSE; + + keystroke = get_unfullscreen_keystroke (priv->rules, active); + + if (keystroke) + { + guint keysym = 0; + EggVirtualModifierType modifiers = 0; + + if (egg_accelerator_parse_virtual (keystroke, &keysym, &modifiers)) + { + guint mods = 0; + + if (modifiers & EGG_VIRTUAL_SHIFT_MASK) + mods |= FAKEKEYMOD_SHIFT; + if (modifiers & EGG_VIRTUAL_CONTROL_MASK) + mods |= FAKEKEYMOD_CONTROL; + if (modifiers & EGG_VIRTUAL_ALT_MASK) + mods |= FAKEKEYMOD_ALT; + if (modifiers & EGG_VIRTUAL_META_MASK) + mods |= FAKEKEYMOD_META; + + g_debug ("Sending fullscreen special event: %s = %d %d", + keystroke, keysym, mods); + fakekey_press_keysym (priv->fk, keysym, mods); + fakekey_release (priv->fk); + + return FALSE; + } + } + if (matewnck_window_is_fullscreen (active)) + { + g_debug ("Sending un-fullscreen F11 event"); + fakekey_press_keysym (priv->fk, XK_F11, 0); + fakekey_release (priv->fk); + } + + sleep (STATE_CHANGED_SLEEP); + + if (matewnck_window_is_fullscreen (active)) + { + g_debug ("Forcing un-fullscreen matewnck event"); + matewnck_window_set_fullscreen (active, FALSE); + } + + return FALSE; +} + +static void +unfullscreen (MaximusBind *bind, MatewnckWindow *window) +{ + MaximusBindPrivate *priv; + + priv = bind->priv; + + g_timeout_add (KEY_RELEASE_TIMEOUT, (GSourceFunc)real_unfullscreen, bind); +} + + +static void +on_binding_activated (gchar *keystring, MaximusBind *bind) +{ + MaximusBindPrivate *priv; + MatewnckWindow *active; + + g_return_if_fail (MAXIMUS_IS_BIND (bind)); + priv = bind->priv; + + active = matewnck_screen_get_active_window (priv->screen); + + if (matewnck_window_get_window_type (active) != MATEWNCK_WINDOW_NORMAL) + return; + + if (matewnck_window_is_fullscreen (active)) + { + unfullscreen (bind, active); + } + else + { + fullscreen (bind, active); + } +} + +/* Callbacks */ +static gboolean +binding_is_valid (const gchar *binding) +{ + gboolean retval = TRUE; + + if (!binding || strlen (binding) < 1 || strcmp (binding, "disabled") == 0) + retval = FALSE; + + return retval; +} + +static void +on_binding_changed (MateConfClient *client, + guint cid, + MateConfEntry *entry, + MaximusBind *bind) +{ + MaximusBindPrivate *priv; + + g_return_if_fail (MAXIMUS_IS_BIND (bind)); + priv = bind->priv; + + if (binding_is_valid (priv->binding)) + tomboy_keybinder_unbind (priv->binding, + (TomboyBindkeyHandler)on_binding_changed); + g_free (priv->binding); + + priv->binding = mateconf_client_get_string (client, + BIND_EXCLUDE_CLASS, + NULL); + + if (binding_is_valid (priv->binding)) + tomboy_keybinder_bind (priv->binding, + (TomboyBindkeyHandler)on_binding_activated, + bind); + + g_print ("Binding changed: %s\n", priv->binding); +} + + +/* GObject stuff */ +static void +create_rule (MaximusBind *bind, const gchar *filename) +{ +#define RULE_GROUP "Fullscreening" +#define RULE_WMCLASS "WMClass" +#define RULE_FULLSCREEN "Fullscreen" +#define RULE_UNFULLSCREEN "Unfullscreen" + MaximusBindPrivate *priv; + GKeyFile *file; + GError *error = NULL; + MaximusRule *rule; + + priv = bind->priv; + + file = g_key_file_new (); + g_key_file_load_from_file (file, filename, 0, &error); + if (error) + { + g_warning ("Unable to load %s: %s\n", filename, error->message); + g_error_free (error); + return; + } + + rule = g_slice_new0 (MaximusRule); + + rule->wm_class = g_key_file_get_string (file, + RULE_GROUP, RULE_WMCLASS, + NULL); + rule->fullscreen = g_key_file_get_string (file, + RULE_GROUP, RULE_FULLSCREEN, + NULL); + rule->unfullscreen = g_key_file_get_string (file, + RULE_GROUP, RULE_UNFULLSCREEN, + NULL); + if (!rule->wm_class || !rule->fullscreen || !rule->unfullscreen) + { + g_free (rule->wm_class); + g_free (rule->fullscreen); + g_free (rule->unfullscreen); + g_slice_free (MaximusRule, rule); + + g_warning ("Unable to load %s, missing strings", filename); + } + else + priv->rules = g_list_append (priv->rules, rule); + + g_key_file_free (file); +} + +static void +load_rules (MaximusBind *bind, const gchar *path) +{ + MaximusBindPrivate *priv; + GDir *dir; + const gchar *name; + + priv = bind->priv; + + dir = g_dir_open (path, 0, NULL); + + if (!dir) + return; + + while ((name = g_dir_read_name (dir))) + { + gchar *filename; + + filename= g_build_filename (path, name, NULL); + + create_rule (bind, filename); + + g_free (filename); + } + + + g_dir_close (dir); +} + +static void +maximus_bind_finalize (GObject *obj) +{ + MaximusBind *bind = MAXIMUS_BIND (obj); + MaximusBindPrivate *priv; + GList *r; + + g_return_if_fail (MAXIMUS_IS_BIND (bind)); + priv = bind->priv; + + for (r = priv->rules; r; r = r->next) + { + MaximusRule *rule = r->data; + + g_free (rule->wm_class); + g_free (rule->fullscreen); + g_free (rule->unfullscreen); + + g_slice_free (MaximusRule, rule); + } + g_free (priv->binding); + + G_OBJECT_CLASS (maximus_bind_parent_class)->finalize (obj); +} + +static void +maximus_bind_class_init (MaximusBindClass *klass) +{ + GObjectClass *obj_class = G_OBJECT_CLASS (klass); + + obj_class->finalize = maximus_bind_finalize; + + g_type_class_add_private (obj_class, sizeof (MaximusBindPrivate)); +} + +static void +maximus_bind_init (MaximusBind *bind) +{ + MaximusBindPrivate *priv; + MateConfClient *client = mateconf_client_get_default (); + GdkDisplay *display = gdk_display_get_default (); + MatewnckScreen *screen; + + priv = bind->priv = MAXIMUS_BIND_GET_PRIVATE (bind); + + priv->fk = fakekey_init (GDK_DISPLAY_XDISPLAY (display)); + priv->screen = screen = matewnck_screen_get_default (); + priv->rules = NULL; + + tomboy_keybinder_init (); + + mateconf_client_add_dir (client, BIND_PATH, MATECONF_CLIENT_PRELOAD_NONE, NULL); + + priv->binding = mateconf_client_get_string (client, + BIND_EXCLUDE_CLASS, + NULL); + mateconf_client_notify_add (client, BIND_EXCLUDE_CLASS, + (MateConfClientNotifyFunc)on_binding_changed, + bind, NULL, NULL); + + if (binding_is_valid (priv->binding)) + tomboy_keybinder_bind (priv->binding, + (TomboyBindkeyHandler)on_binding_activated, + bind); + + load_rules (bind, SYSRULESDIR); +} + +MaximusBind * +maximus_bind_get_default (void) + +{ + static MaximusBind *bind = NULL; + + if (!bind) + bind = g_object_new (MAXIMUS_TYPE_BIND, + NULL); + + return bind; +} diff --git a/maximus/maximus-bind.h b/maximus/maximus-bind.h new file mode 100644 index 0000000..d8aee9a --- /dev/null +++ b/maximus/maximus-bind.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2008 Canonical Ltd + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + * + * 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, see <http://www.gnu.org/licenses/>. + * + * Authored by Neil Jagdish Patel <[email protected]> + * + */ + +#ifndef _MAXIMUS_BIND_H_ +#define _MAXIMUS_BIND_H_ + +#include <glib-object.h> +#include <gtk/gtk.h> + +#define MAXIMUS_TYPE_BIND (maximus_bind_get_type ()) + +#define MAXIMUS_BIND(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj),\ + MAXIMUS_TYPE_BIND, MaximusBind)) + +#define MAXIMUS_BIND_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass),\ + MAXIMUS_TYPE_BIND, MaximusBindClass)) + +#define MAXIMUS_IS_BIND(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj),\ + MAXIMUS_TYPE_BIND)) + +#define MAXIMUS_IS_BIND_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),\ + MAXIMUS_TYPE_BIND)) + +#define MAXIMUS_BIND_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj),\ + MAXIMUS_TYPE_BIND, MaximusBindClass)) + +typedef struct _MaximusBind MaximusBind; +typedef struct _MaximusBindClass MaximusBindClass; +typedef struct _MaximusBindPrivate MaximusBindPrivate; + +struct _MaximusBind +{ + GObject parent; + + MaximusBindPrivate *priv; +}; + +struct _MaximusBindClass +{ + GObjectClass parent_class; +}; + +GType maximus_bind_get_type (void) G_GNUC_CONST; + +MaximusBind * maximus_bind_get_default (void); + + +#endif /* _MAXIMUS_BIND_H_ */ + diff --git a/maximus/maximus.schemas b/maximus/maximus.schemas new file mode 100644 index 0000000..a348e9b --- /dev/null +++ b/maximus/maximus.schemas @@ -0,0 +1,54 @@ +<mateconfschemafile> + <schemalist> + <schema> + <key>/schemas/apps/maximus/exclude_class</key> + <applyto>/apps/maximus/exclude_class</applyto> + <owner>maximus</owner> + <type>list</type> + <list_type>string</list_type> + <default>[Totem]</default> + <locale name="C"> + <short>A list of exclusion strings which apply to the window class.</short> + <long>A list of exclusion strings which apply to the window class</long> + </locale> + </schema> + + <schema> + <key>/schemas/apps/maximus/binding</key> + <applyto>/apps/maximus/binding</applyto> + <owner>maximus</owner> + <type>string</type> + <default>disabled</default> + <locale name="C"> + <short>A binding</short> + <long>A binding</long> + </locale> + </schema> + + <schema> + <key>/schemas/apps/maximus/undecorate</key> + <applyto>/apps/maximus/undecorate</applyto> + <owner>maximus</owner> + <type>bool</type> + <default>1</default> + <locale name="C"> + <short>Undecorate windows when maximized</short> + <long>Undecorate windows when maximized</long> + </locale> + </schema> + + <schema> + <key>/schemas/apps/maximus/no_maximize</key> + <applyto>/apps/maximus/no_maximize</applyto> + <owner>maximus</owner> + <type>bool</type> + <default>0</default> + <locale name="C"> + <short>Do not automatically maximize newly opened windows</short> + <long>Do not automatically maximize newly opened windows. If undecorate is set, will still undecorate maximised windows.</long> + </locale> + </schema> + + </schemalist> +</mateconfschemafile> + diff --git a/maximus/tomboykeybinder.c b/maximus/tomboykeybinder.c new file mode 100644 index 0000000..0c50cc0 --- /dev/null +++ b/maximus/tomboykeybinder.c @@ -0,0 +1,337 @@ +/* tomboykeybinder.c + * Copyright (C) 2008 Novell + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA + */ +#include <string.h> + +#include <gdk/gdk.h> +#include <gdk/gdkwindow.h> +#include <gdk/gdkx.h> +#include <X11/Xlib.h> + +#include "eggaccelerators.h" +#include "tomboykeybinder.h" + +/* Uncomment the next line to print a debug trace. */ +/* #define DEBUG */ + +#ifdef DEBUG +# define TRACE(x) x +#else +# define TRACE(x) do {} while (FALSE); +#endif + +typedef struct _Binding { + TomboyBindkeyHandler handler; + gpointer user_data; + char *keystring; + uint keycode; + uint modifiers; +} Binding; + +static GSList *bindings = NULL; +static guint32 last_event_time = 0; +static gboolean processing_event = FALSE; + +static guint num_lock_mask, caps_lock_mask, scroll_lock_mask; + +static void +lookup_ignorable_modifiers (GdkKeymap *keymap) +{ + egg_keymap_resolve_virtual_modifiers (keymap, + EGG_VIRTUAL_LOCK_MASK, + &caps_lock_mask); + + egg_keymap_resolve_virtual_modifiers (keymap, + EGG_VIRTUAL_NUM_LOCK_MASK, + &num_lock_mask); + + egg_keymap_resolve_virtual_modifiers (keymap, + EGG_VIRTUAL_SCROLL_LOCK_MASK, + &scroll_lock_mask); +} + +static void +grab_ungrab_with_ignorable_modifiers (GdkWindow *rootwin, + Binding *binding, + gboolean grab) +{ + guint mod_masks [] = { + 0, /* modifier only */ + num_lock_mask, + caps_lock_mask, + scroll_lock_mask, + num_lock_mask | caps_lock_mask, + num_lock_mask | scroll_lock_mask, + caps_lock_mask | scroll_lock_mask, + num_lock_mask | caps_lock_mask | scroll_lock_mask, + }; + int i; + + for (i = 0; i < G_N_ELEMENTS (mod_masks); i++) { + if (grab) { + XGrabKey (GDK_WINDOW_XDISPLAY (rootwin), + binding->keycode, + binding->modifiers | mod_masks [i], + GDK_WINDOW_XWINDOW (rootwin), + False, + GrabModeAsync, + GrabModeAsync); + } else { + XUngrabKey (GDK_WINDOW_XDISPLAY (rootwin), + binding->keycode, + binding->modifiers | mod_masks [i], + GDK_WINDOW_XWINDOW (rootwin)); + } + } +} + +static gboolean +do_grab_key (Binding *binding) +{ + GdkKeymap *keymap = gdk_keymap_get_default (); + GdkWindow *rootwin = gdk_get_default_root_window (); + + EggVirtualModifierType virtual_mods = 0; + guint keysym = 0; + + if (keymap == NULL || rootwin == NULL) + return FALSE; + + if (!egg_accelerator_parse_virtual (binding->keystring, + &keysym, + &virtual_mods)) + return FALSE; + + TRACE (g_print ("Got accel %d, %d\n", keysym, virtual_mods)); + + binding->keycode = XKeysymToKeycode (GDK_WINDOW_XDISPLAY (rootwin), + keysym); + if (binding->keycode == 0) + return FALSE; + + TRACE (g_print ("Got keycode %d\n", binding->keycode)); + + egg_keymap_resolve_virtual_modifiers (keymap, + virtual_mods, + &binding->modifiers); + + TRACE (g_print ("Got modmask %d\n", binding->modifiers)); + + gdk_error_trap_push (); + + grab_ungrab_with_ignorable_modifiers (rootwin, + binding, + TRUE /* grab */); + + gdk_flush (); + + if (gdk_error_trap_pop ()) { + g_warning ("Binding '%s' failed!\n", binding->keystring); + return FALSE; + } + + return TRUE; +} + +static gboolean +do_ungrab_key (Binding *binding) +{ + GdkWindow *rootwin = gdk_get_default_root_window (); + + TRACE (g_print ("Removing grab for '%s'\n", binding->keystring)); + + grab_ungrab_with_ignorable_modifiers (rootwin, + binding, + FALSE /* ungrab */); + + return TRUE; +} + +static GdkFilterReturn +filter_func (GdkXEvent *gdk_xevent, GdkEvent *event, gpointer data) +{ + GdkFilterReturn return_val = GDK_FILTER_CONTINUE; + XEvent *xevent = (XEvent *) gdk_xevent; + guint event_mods; + GSList *iter; + + TRACE (g_print ("Got Event! %d, %d\n", xevent->type, event->type)); + + switch (xevent->type) { + case KeyPress: + TRACE (g_print ("Got KeyPress! keycode: %d, modifiers: %d\n", + xevent->xkey.keycode, + xevent->xkey.state)); + + /* + * Set the last event time for use when showing + * windows to avoid anti-focus-stealing code. + */ + processing_event = TRUE; + last_event_time = xevent->xkey.time; + + event_mods = xevent->xkey.state & ~(num_lock_mask | + caps_lock_mask | + scroll_lock_mask); + + for (iter = bindings; iter != NULL; iter = iter->next) { + Binding *binding = (Binding *) iter->data; + + if (binding->keycode == xevent->xkey.keycode && + binding->modifiers == event_mods) { + + TRACE (g_print ("Calling handler for '%s'...\n", + binding->keystring)); + + (binding->handler) (binding->keystring, + binding->user_data); + } + } + + processing_event = FALSE; + break; + case KeyRelease: + TRACE (g_print ("Got KeyRelease! \n")); + break; + } + + return return_val; +} + +static void +keymap_changed (GdkKeymap *map) +{ + GdkKeymap *keymap = gdk_keymap_get_default (); + GSList *iter; + + TRACE (g_print ("Keymap changed! Regrabbing keys...")); + + for (iter = bindings; iter != NULL; iter = iter->next) { + Binding *binding = (Binding *) iter->data; + do_ungrab_key (binding); + } + + lookup_ignorable_modifiers (keymap); + + for (iter = bindings; iter != NULL; iter = iter->next) { + Binding *binding = (Binding *) iter->data; + do_grab_key (binding); + } +} + +void +tomboy_keybinder_init (void) +{ + GdkKeymap *keymap = gdk_keymap_get_default (); + GdkWindow *rootwin = gdk_get_default_root_window (); + + lookup_ignorable_modifiers (keymap); + + gdk_window_add_filter (rootwin, + filter_func, + NULL); + + g_signal_connect (keymap, + "keys_changed", + G_CALLBACK (keymap_changed), + NULL); +} + +void +tomboy_keybinder_bind (const char *keystring, + TomboyBindkeyHandler handler, + gpointer user_data) +{ + Binding *binding; + gboolean success; + + binding = g_new0 (Binding, 1); + binding->keystring = g_strdup (keystring); + binding->handler = handler; + binding->user_data = user_data; + + /* Sets the binding's keycode and modifiers */ + success = do_grab_key (binding); + + if (success) { + bindings = g_slist_prepend (bindings, binding); + } else { + g_free (binding->keystring); + g_free (binding); + } +} + +void +tomboy_keybinder_unbind (const char *keystring, + TomboyBindkeyHandler handler) +{ + GSList *iter; + + for (iter = bindings; iter != NULL; iter = iter->next) { + Binding *binding = (Binding *) iter->data; + + if (strcmp (keystring, binding->keystring) != 0 || + handler != binding->handler) + continue; + + do_ungrab_key (binding); + + bindings = g_slist_remove (bindings, binding); + + g_free (binding->keystring); + g_free (binding); + break; + } +} + +/* + * From eggcellrenderkeys.c. + */ +gboolean +tomboy_keybinder_is_modifier (guint keycode) +{ + gint i; + gint map_size; + XModifierKeymap *mod_keymap; + gboolean retval = FALSE; + + mod_keymap = XGetModifierMapping (gdk_display); + + map_size = 8 * mod_keymap->max_keypermod; + + i = 0; + while (i < map_size) { + if (keycode == mod_keymap->modifiermap[i]) { + retval = TRUE; + break; + } + ++i; + } + + XFreeModifiermap (mod_keymap); + + return retval; +} + +guint32 +tomboy_keybinder_get_current_event_time (void) +{ + if (processing_event) + return last_event_time; + else + return GDK_CURRENT_TIME; +} diff --git a/maximus/tomboykeybinder.h b/maximus/tomboykeybinder.h new file mode 100644 index 0000000..97c1887 --- /dev/null +++ b/maximus/tomboykeybinder.h @@ -0,0 +1,43 @@ +/* tomboykeybinder.h + * Copyright (C) 2008 Novell + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA + */ +#ifndef __TOMBOY_KEY_BINDER_H__ +#define __TOMBOY_KEY_BINDER_H__ + +#include <glib/gtypes.h> + +G_BEGIN_DECLS + +typedef void (* TomboyBindkeyHandler) (char *keystring, gpointer user_data); + +void tomboy_keybinder_init (void); + +void tomboy_keybinder_bind (const char *keystring, + TomboyBindkeyHandler handler, + gpointer user_data); + +void tomboy_keybinder_unbind (const char *keystring, + TomboyBindkeyHandler handler); + +gboolean tomboy_keybinder_is_modifier (guint keycode); + +guint32 tomboy_keybinder_get_current_event_time (void); + +G_END_DECLS + +#endif /* __TOMBOY_KEY_BINDER_H__ */ + diff --git a/maximus/xutils.c b/maximus/xutils.c new file mode 100644 index 0000000..f131f81 --- /dev/null +++ b/maximus/xutils.c @@ -0,0 +1,102 @@ +/* Xlib utils */ +/* vim: set sw=2 et: */ + +/* + * Copyright (C) 2001 Havoc Pennington + * Copyright (C) 2005-2007 Vincent Untz + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <config.h> +#include "xutils.h" +#include <string.h> +#include <stdio.h> +#include <libmatewnck/libmatewnck.h> + +void +_matewnck_error_trap_push (void) +{ + gdk_error_trap_push (); +} + +int +_matewnck_error_trap_pop (void) +{ + XSync (gdk_display, False); + return gdk_error_trap_pop (); +} + +static char* +latin1_to_utf8 (const char *latin1) +{ + GString *str; + const char *p; + + str = g_string_new (NULL); + + p = latin1; + while (*p) + { + g_string_append_unichar (str, (gunichar) *p); + ++p; + } + + return g_string_free (str, FALSE); +} + +void +_matewnck_get_wmclass (Window xwindow, + char **res_class, + char **res_name) +{ + XClassHint ch; + char *retval; + + _matewnck_error_trap_push (); + + ch.res_name = NULL; + ch.res_class = NULL; + + XGetClassHint (gdk_display, xwindow, + &ch); + + _matewnck_error_trap_pop (); + + retval = NULL; + + if (res_class) + *res_class = NULL; + + if (res_name) + *res_name = NULL; + + if (ch.res_name) + { + if (res_name) + *res_name = latin1_to_utf8 (ch.res_name); + + XFree (ch.res_name); + } + + if (ch.res_class) + { + if (res_class) + *res_class = latin1_to_utf8 (ch.res_class); + + XFree (ch.res_class); + } +} diff --git a/maximus/xutils.h b/maximus/xutils.h new file mode 100644 index 0000000..58e1d6e --- /dev/null +++ b/maximus/xutils.h @@ -0,0 +1,49 @@ +/* Xlib utilities */ +/* vim: set sw=2 et: */ + +/* + * Copyright (C) 2001 Havoc Pennington + * Copyright (C) 2005-2007 Vincent Untz + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef MATEWNCK_XUTILS_H +#define MATEWNCK_XUTILS_H + +#include <glib.h> +#include <X11/Xlib.h> +#include <X11/Xatom.h> +#include <gdk/gdk.h> +#include <gdk/gdkx.h> + +G_BEGIN_DECLS + +void +_matewnck_error_trap_push (void); + +int +_matewnck_error_trap_pop (void); + +void +_matewnck_get_wmclass (Window xwindow, + char **res_class, + char **res_name); + + +G_END_DECLS + +#endif /* MATEWNCK_XUTILS_H */ |