diff --git a/ChangeLog b/ChangeLog index b0181f8b6..3e123136a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,16 @@ +2002-07-11 Havoc Pennington + + * src/metacity-dialog.c (main): option to display error when a + command fails to run. + + * src/keybindings.c (handle_run_command): run commands + in response to keybindings. + + * src/prefs.c: add command keybinding stuff + + * src/metacity.schemas.in: add keybindings for running commands, + and keys to store the commands themselves. + 2002-07-10 Havoc Pennington * src/display.c: properly attribute selection code to Matthias diff --git a/src/keybindings.c b/src/keybindings.c index c9af5abe7..a0ce45252 100644 --- a/src/keybindings.c +++ b/src/keybindings.c @@ -102,6 +102,10 @@ static void handle_raise_or_lower (MetaDisplay *display, MetaWindow *window, XEvent *event, MetaKeyBinding *binding); +static void handle_run_command (MetaDisplay *display, + MetaWindow *window, + XEvent *event, + MetaKeyBinding *binding); /* debug */ static void handle_spew_mark (MetaDisplay *display, @@ -190,6 +194,30 @@ static const MetaKeyHandler screen_handlers[] = { GINT_TO_POINTER (META_TAB_LIST_DOCKS) }, { META_KEYBINDING_SHOW_DESKTOP, handle_toggle_desktop, NULL }, + { META_KEYBINDING_COMMAND_1, handle_run_command, + GINT_TO_POINTER (0) }, + { META_KEYBINDING_COMMAND_2, handle_run_command, + GINT_TO_POINTER (1) }, + { META_KEYBINDING_COMMAND_3, handle_run_command, + GINT_TO_POINTER (2) }, + { META_KEYBINDING_COMMAND_4, handle_run_command, + GINT_TO_POINTER (3) }, + { META_KEYBINDING_COMMAND_5, handle_run_command, + GINT_TO_POINTER (4) }, + { META_KEYBINDING_COMMAND_6, handle_run_command, + GINT_TO_POINTER (5) }, + { META_KEYBINDING_COMMAND_7, handle_run_command, + GINT_TO_POINTER (6) }, + { META_KEYBINDING_COMMAND_8, handle_run_command, + GINT_TO_POINTER (7) }, + { META_KEYBINDING_COMMAND_9, handle_run_command, + GINT_TO_POINTER (8) }, + { META_KEYBINDING_COMMAND_10, handle_run_command, + GINT_TO_POINTER (9) }, + { META_KEYBINDING_COMMAND_11, handle_run_command, + GINT_TO_POINTER (10) }, + { META_KEYBINDING_COMMAND_12, handle_run_command, + GINT_TO_POINTER (11) }, { NULL, NULL, NULL } }; @@ -1893,6 +1921,86 @@ handle_activate_workspace (MetaDisplay *display, } } +static void +error_on_command (int command_index, + const char *command, + const char *message) +{ + GError *err; + char *argv[6]; + char *key; + + meta_warning ("Error on command %d \"%s\": %s\n", + command_index, command, message); + + key = meta_prefs_get_gconf_key_for_command (command_index); + + argv[0] = METACITY_LIBEXECDIR"/metacity-dialog"; + argv[1] = "--command-failed-error"; + argv[2] = key; + argv[3] = (char*) (command ? command : ""); + argv[4] = (char*) message; + argv[5] = NULL; + + err = NULL; + if (!g_spawn_async_with_pipes ("/", + argv, + NULL, + 0, + NULL, NULL, + NULL, + NULL, + NULL, + NULL, + &err)) + { + meta_warning (_("Error launching metacity-dialog to print an error about a command: %s\n"), + err->message); + g_error_free (err); + } + + g_free (key); +} + +static void +handle_run_command (MetaDisplay *display, + MetaWindow *window, + XEvent *event, + MetaKeyBinding *binding) +{ + int which; + const char *command; + GError *err; + + which = GPOINTER_TO_INT (binding->handler->data); + + command = meta_prefs_get_command (which); + + if (command == NULL) + { + char *s; + + meta_topic (META_DEBUG_KEYBINDINGS, + "No command %d to run in response to keybinding press\n", + which); + + s = g_strdup_printf (_("No command %d has been defined.\n"), + which + 1); + error_on_command (which, NULL, s); + g_free (s); + + return; + } + + err = NULL; + if (!g_spawn_command_line_async (command, &err)) + { + error_on_command (which, command, err->message); + + g_error_free (err); + } +} + static gboolean process_workspace_switch_grab (MetaDisplay *display, XEvent *event, diff --git a/src/metacity-dialog.c b/src/metacity-dialog.c index 6a8a6a910..ff362ef87 100644 --- a/src/metacity-dialog.c +++ b/src/metacity-dialog.c @@ -259,6 +259,35 @@ warn_about_no_sm_support (char **lame_apps) return 0; } +static int +error_about_command (const char *gconf_key, + const char *command, + const char *error) +{ + GtkWidget *dialog; + + /* FIXME offer to change the value of the command's gconf key */ + + if (*command != '\0') + dialog = gtk_message_dialog_new (NULL, 0, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_CLOSE, + _("There was an error running \"%s\":\n" + "%s."), + command, error); + else + dialog = gtk_message_dialog_new (NULL, 0, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_CLOSE, + "%s", error); + + gtk_dialog_run (GTK_DIALOG (dialog)); + + gtk_widget_destroy (dialog); + + return 0; +} + int main (int argc, char **argv) { @@ -295,7 +324,21 @@ main (int argc, char **argv) return warn_about_no_sm_support (&argv[2]); } + else if (strcmp (argv[1], "--command-failed-error") == 0) + { + /* the args are the gconf key of the failed command, the text of + * the command, and the error message + */ + if (argc != 5) + { + g_printerr ("bad args to metacity-dialog\n"); + return 1; + } + + return error_about_command (argv[2], argv[3], argv[4]); + } + g_printerr ("bad args to metacity-dialog\n"); return 1; } diff --git a/src/metacity.schemas.in b/src/metacity.schemas.in index 58d7e7c05..79d363802 100644 --- a/src/metacity.schemas.in +++ b/src/metacity.schemas.in @@ -1231,6 +1231,71 @@ you set + + /schemas/apps/metacity/global_keybindings/run_command + /apps/metacity/global_keybindings/run_command_1 + /apps/metacity/global_keybindings/run_command_2 + /apps/metacity/global_keybindings/run_command_3 + /apps/metacity/global_keybindings/run_command_4 + /apps/metacity/global_keybindings/run_command_5 + /apps/metacity/global_keybindings/run_command_6 + /apps/metacity/global_keybindings/run_command_7 + /apps/metacity/global_keybindings/run_command_8 + /apps/metacity/global_keybindings/run_command_9 + /apps/metacity/global_keybindings/run_command_10 + /apps/metacity/global_keybindings/run_command_11 + /apps/metacity/global_keybindings/run_command_12 + metacity + string + disabled + + Run a defined command + + The keybinding that runs the correspondingly-numbered + command in /apps/metacity/keybinding_commands + + The format looks like "<Control>a" or + "<Shift><Alt>F1. + + The parser is fairly liberal and allows lower or upper case, + and also abbreviations such as "<Ctl>" and + "<Ctrl>". If you set the option to the special string + "disabled", then there will be no keybinding for this + action. + + + + + + + + /schemas/apps/metacity/keybinding_commands/command + /apps/metacity/keybinding_commands/command_1 + /apps/metacity/keybinding_commands/command_2 + /apps/metacity/keybinding_commands/command_3 + /apps/metacity/keybinding_commands/command_4 + /apps/metacity/keybinding_commands/command_5 + /apps/metacity/keybinding_commands/command_6 + /apps/metacity/keybinding_commands/command_7 + /apps/metacity/keybinding_commands/command_8 + /apps/metacity/keybinding_commands/command_9 + /apps/metacity/keybinding_commands/command_10 + /apps/metacity/keybinding_commands/command_11 + /apps/metacity/keybinding_commands/command_12 + metacity + string + + + Commands to run in response to keybindings + + The /apps/metacity/global_keybindings/run_command_N + keys define keybindings that correspond to these commands. + Pressing the keybinding for run_command_N will + execute command_N. + + + + diff --git a/src/prefs.c b/src/prefs.c index 26fc4b21e..666b75e9d 100644 --- a/src/prefs.c +++ b/src/prefs.c @@ -25,6 +25,7 @@ #include "util.h" #include #include +#include /* If you add a key, it needs updating in init() and in the gconf * notify listener and of course in the .schemas file @@ -39,6 +40,7 @@ #define KEY_APPLICATION_BASED "/apps/metacity/general/application_based" #define KEY_DISABLE_WORKAROUNDS "/apps/metacity/general/disable_workarounds" +#define KEY_COMMAND_PREFIX "/apps/metacity/keybinding_commands/command_" #define KEY_SCREEN_BINDINGS_PREFIX "/apps/metacity/global_keybindings" #define KEY_WINDOW_BINDINGS_PREFIX "/apps/metacity/window_keybindings" @@ -55,6 +57,9 @@ static gboolean application_based = FALSE; static gboolean disable_workarounds = FALSE; static gboolean auto_raise = FALSE; static gboolean auto_raise_delay = 500; +#define NUM_COMMANDS 12 +static char *commands[NUM_COMMANDS] = { NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL }; static gboolean update_use_system_font (gboolean value); static gboolean update_titlebar_font (const char *value); @@ -72,6 +77,9 @@ static gboolean update_screen_binding (const char *name, static void init_bindings (void); static gboolean update_binding (MetaKeyPref *binding, const char *value); +static gboolean update_command (const char *name, + const char *value); +static void init_commands (void); static void queue_changed (MetaPreference pref); static void change_notify (GConfClient *client, @@ -284,6 +292,9 @@ meta_prefs_init (void) /* Load keybindings prefs */ init_bindings (); + + /* commands */ + init_commands (); gconf_client_notify_add (default_client, "/apps/metacity", change_notify, @@ -505,6 +516,23 @@ change_notify (GConfClient *client, if (update_auto_raise_delay (d)) queue_changed (META_PREF_AUTO_RAISE_DELAY); + + } + else if (str_has_prefix (key, KEY_COMMAND_PREFIX)) + { + const char *str; + + if (value && value->type != GCONF_VALUE_STRING) + { + meta_warning (_("GConf key \"%s\" is set to an invalid type\n"), + key); + goto out; + } + + str = value ? gconf_value_get_string (value) : NULL; + + if (update_command (key, str)) + queue_changed (META_PREF_COMMANDS); } else { @@ -767,6 +795,9 @@ meta_preference_to_string (MetaPreference pref) case META_PREF_AUTO_RAISE_DELAY: return "AUTO_RAISE_DELAY"; + + case META_PREF_COMMANDS: + return "COMMANDS"; } return "(unknown)"; @@ -822,6 +853,18 @@ static MetaKeyPref screen_bindings[] = { { META_KEYBINDING_CYCLE_WINDOWS, 0, 0 }, { META_KEYBINDING_CYCLE_PANELS, 0, 0 }, { META_KEYBINDING_SHOW_DESKTOP, 0, 0 }, + { META_KEYBINDING_COMMAND_1, 0, 0 }, + { META_KEYBINDING_COMMAND_2, 0, 0 }, + { META_KEYBINDING_COMMAND_3, 0, 0 }, + { META_KEYBINDING_COMMAND_4, 0, 0 }, + { META_KEYBINDING_COMMAND_5, 0, 0 }, + { META_KEYBINDING_COMMAND_6, 0, 0 }, + { META_KEYBINDING_COMMAND_7, 0, 0 }, + { META_KEYBINDING_COMMAND_8, 0, 0 }, + { META_KEYBINDING_COMMAND_9, 0, 0 }, + { META_KEYBINDING_COMMAND_10, 0, 0 }, + { META_KEYBINDING_COMMAND_11, 0, 0 }, + { META_KEYBINDING_COMMAND_12, 0, 0 }, { NULL, 0, 0 } }; @@ -904,6 +947,33 @@ init_bindings (void) } } +static void +init_commands (void) +{ + int i; + GError *err; + + i = 0; + while (i < NUM_COMMANDS) + { + char *str_val; + char *key; + + key = meta_prefs_get_gconf_key_for_command (i); + + err = NULL; + str_val = gconf_client_get_string (default_client, key, &err); + cleanup_error (&err); + + update_command (key, str_val); + + g_free (str_val); + g_free (key); + + ++i; + } +} + static gboolean update_binding (MetaKeyPref *binding, const char *value) @@ -1004,6 +1074,68 @@ update_screen_binding (const char *name, return find_and_update_binding (screen_bindings, name, value); } +static gboolean +update_command (const char *name, + const char *value) +{ + char *p; + int i; + + p = strrchr (name, '_'); + if (p == NULL) + { + meta_topic (META_DEBUG_KEYBINDINGS, + "Command %s has no underscore?\n", name); + return FALSE; + } + + ++p; + + if (!g_ascii_isdigit (*p)) + { + meta_topic (META_DEBUG_KEYBINDINGS, + "Command %s doesn't end in number?\n", name); + return FALSE; + } + + i = atoi (p); + i -= 1; /* count from 0 not 1 */ + + if (i >= NUM_COMMANDS) + { + meta_topic (META_DEBUG_KEYBINDINGS, + "Command %d is too highly numbered, ignoring\n", i); + return FALSE; + } + + g_free (commands[i]); + commands[i] = g_strdup (value); + + meta_topic (META_DEBUG_KEYBINDINGS, + "Updated command %d to \"%s\"\n", + i, commands[i] ? commands[i] : "none"); + + return TRUE; +} + +const char* +meta_prefs_get_command (int i) +{ + g_return_val_if_fail (i >= 0 && i < NUM_COMMANDS, NULL); + + return commands[i]; +} + +char* +meta_prefs_get_gconf_key_for_command (int i) +{ + char *key; + + key = g_strdup_printf (KEY_COMMAND_PREFIX"%d", i + 1); + + return key; +} + void meta_prefs_get_screen_bindings (const MetaKeyPref **bindings, int *n_bindings) diff --git a/src/prefs.h b/src/prefs.h index bcc93e78a..2d1508e70 100644 --- a/src/prefs.h +++ b/src/prefs.h @@ -37,7 +37,8 @@ typedef enum META_PREF_APPLICATION_BASED, META_PREF_WINDOW_KEYBINDINGS, META_PREF_SCREEN_KEYBINDINGS, - META_PREF_DISABLE_WORKAROUNDS + META_PREF_DISABLE_WORKAROUNDS, + META_PREF_COMMANDS } MetaPreference; typedef void (* MetaPrefsChangedFunc) (MetaPreference pref, @@ -61,6 +62,10 @@ gboolean meta_prefs_get_disable_workarounds (void); gboolean meta_prefs_get_auto_raise (void); int meta_prefs_get_auto_raise_delay (void); +const char* meta_prefs_get_command (int i); + +char* meta_prefs_get_gconf_key_for_command (int i); + void meta_prefs_set_num_workspaces (int n_workspaces); /* Screen bindings */ @@ -85,6 +90,18 @@ void meta_prefs_set_num_workspaces (int n_workspaces); #define META_KEYBINDING_CYCLE_WINDOWS "cycle_windows" #define META_KEYBINDING_CYCLE_PANELS "cycle_panels" #define META_KEYBINDING_SHOW_DESKTOP "show_desktop" +#define META_KEYBINDING_COMMAND_1 "run_command_1" +#define META_KEYBINDING_COMMAND_2 "run_command_2" +#define META_KEYBINDING_COMMAND_3 "run_command_3" +#define META_KEYBINDING_COMMAND_4 "run_command_4" +#define META_KEYBINDING_COMMAND_5 "run_command_5" +#define META_KEYBINDING_COMMAND_6 "run_command_6" +#define META_KEYBINDING_COMMAND_7 "run_command_7" +#define META_KEYBINDING_COMMAND_8 "run_command_8" +#define META_KEYBINDING_COMMAND_9 "run_command_9" +#define META_KEYBINDING_COMMAND_10 "run_command_10" +#define META_KEYBINDING_COMMAND_11 "run_command_11" +#define META_KEYBINDING_COMMAND_12 "run_command_12" /* Window bindings */ #define META_KEYBINDING_WINDOW_MENU "activate_window_menu" @@ -137,7 +154,19 @@ typedef enum _MetaKeyBindingAction META_KEYBINDING_ACTION_SWITCH_PANELS, META_KEYBINDING_ACTION_CYCLE_WINDOWS, META_KEYBINDING_ACTION_CYCLE_PANELS, - META_KEYBINDING_ACTION_SHOW_DESKTOP + META_KEYBINDING_ACTION_SHOW_DESKTOP, + META_KEYBINDING_ACTION_COMMAND_1, + META_KEYBINDING_ACTION_COMMAND_2, + META_KEYBINDING_ACTION_COMMAND_3, + META_KEYBINDING_ACTION_COMMAND_4, + META_KEYBINDING_ACTION_COMMAND_5, + META_KEYBINDING_ACTION_COMMAND_6, + META_KEYBINDING_ACTION_COMMAND_7, + META_KEYBINDING_ACTION_COMMAND_8, + META_KEYBINDING_ACTION_COMMAND_9, + META_KEYBINDING_ACTION_COMMAND_10, + META_KEYBINDING_ACTION_COMMAND_11, + META_KEYBINDING_ACTION_COMMAND_12, } MetaKeyBindingAction; typedef struct