6

I mean: whenever a letter in the text of a button is underlined, pressing Alt plus that letter clicks the button. I have gone through both the Keyboard and the Tweak Tool applets without success.

Pressing Alt does underline the letters, but I would prefer to have them underlined beforehand.

  • 1
    Afaik gtk-enable-mnemonics was deprecated in gtk 3.10 so there's no way to toggle it to ON. – don_crissti Jun 26 '15 at 23:07
  • You are right. This is unfortunate indeed. If you convert your comment to an answer, I will accept it. – Elena Jun 26 '15 at 23:15
6

For GTK 2, add the following line to ~/.gtkrc-2.0:

gtk-auto-mnemonic = 0

For GTK 3 up to GTK 3.9:

do_dconf /org/gnome/desktop/interface/automatic-mnemonics false

In GTK 3.10, the option was removed (in this commit, whose log message just states that the feature was removed). Looking at the source, there's no way to turn it on. The crazy behavior of showing the mnemonics only after Alt has been pressed for at least 300ms (the timeout is hardcoded as a compile-time constant) is the only possible one.

I set out to override this behavior by preloading a library wrapper that forces mnemonics to be visible. It wasn't easy, because most of the calls are internal. I finally found a way to make the query that tells the label displayer whether the widget wants to display mnemonics always return true. It mostly works, I can see the mnemonics all the time in GEdit and Evince's dialog boxes, but not completely: some menu accelerators are missing, I don't know why.

The code:

#define _GNU_SOURCE
#include <dlfcn.h>
#include <stdio.h>
#include <glib.h>
#include <gtk/gtk.h>

#define PROP_MNEMONICS_VISIBLE 31

#ifdef DEBUG
#define DBG(args...) printf(args)
#else
#define DBG(args...)
#endif

static void (*original_gtk_window_get_property)(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec);
static void wrap_gtk_window_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
{
    original_gtk_window_get_property(object, prop_id, value, pspec);
    if (prop_id == PROP_MNEMONICS_VISIBLE) {
        DBG("%s(%p, %u, &%u, %p)\n", __FUNCTION__, object, prop_id, *(unsigned*)value, pspec);
        g_value_set_boolean(value, TRUE);
    }
}

inline void override_gtk_window_get_property(GType object_type, GObject *obj)
{
    static int first_window_creation = 1;
    if (object_type == GTK_TYPE_WINDOW && first_window_creation) {
        first_window_creation = 0;
        GtkWidgetClass *gtk_widget_class = GTK_WIDGET_GET_CLASS(obj);
        GObjectClass *gobject_class = G_OBJECT_CLASS(gtk_widget_class);
        original_gtk_window_get_property = gobject_class->get_property;
        gobject_class->get_property = wrap_gtk_window_get_property;
    }
}

GObject* g_object_new_valist(GType object_type, const gchar *first_property_name, va_list var_args)
{
    static GObject* (*original_g_object_new_valist)(GType object_type, const gchar *first_property_name, va_list var_args) = NULL;
    if (original_g_object_new_valist == NULL) {
        original_g_object_new_valist = dlsym(RTLD_NEXT, "g_object_new_valist");
    }
    GObject* obj = original_g_object_new_valist(object_type, first_property_name, var_args);
    override_gtk_window_get_property(object_type, obj);
    return obj;
}
gpointer g_object_new(GType object_type, const gchar *first_property_name, ...)
{
    va_list var_args;
    va_start(var_args, first_property_name);
    gpointer obj = g_object_new_valist(object_type, first_property_name, var_args);
    va_end(var_args);
    return obj;
}
gpointer g_object_newv(GType object_type, guint n_parameters, GParameter *parameters)
{
    static gpointer (*original_g_object_newv)(GType object_type, guint n_parameters, GParameter *parameters);
    if (original_g_object_newv == NULL) {
        original_g_object_newv = dlsym(RTLD_NEXT, "g_object_newv");
    }
    GObject* obj = original_g_object_newv(object_type, n_parameters, parameters);
    override_gtk_window_get_property(object_type, obj);
    return obj;
}

How to use it: write this to a file called gtk_window_get_mnemonics_visible.c. Install GCC and the GTK3 development package (e.g. sudo apt-get install gcc libgtk-3-dev on Debian/Ubuntu). Compile with

gcc -Wall -fPIC -shared `pkg-config --cflags gtk+-3.0` `pkg-config --libs gtk+-3.0` -o gtk_window_get_mnemonics_visible.so gtk_window_get_mnemonics_visible.c

Run Gnome applications like this:

LD_PRELOAD=/path/to/gtk_window_get_mnemonics_visible.so gedit

or put this in your .profile:

export LD_PRELOAD=/path/to/gtk_window_get_mnemonics_visible.so

Note that LD_PRELOAD is a hack, it may cause weird behavior sometimes. I've only given this one a cursory test. Known bug:

  • If GTK_IM_MODULE is set to xim, then using LD_PRELOAD for anything (even an empty C file!) causes GEdit to hang when I press Alt (observed on Debian jessie). I have no idea why.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.