GDK signal, keypress, and key masks

2019-08-05 08:20发布

问题:

I am trying to catch user key press Ctrl+d on a GUI window to quit. My code looks like this:

static gboolean
callback(GtkWidget   *widget,
         GdkEventKey *event,
         gpointer    data)
{
    if(event->state == GDK_CONTROL_MASK && event->keyval == 'd')
        gtk_main_quit();

    return FASLE;
}

This works on my laptop(Ubuntu 11.04, gcc 4.5.2, libgtk 2.24.4). But when I do the same thing on a newer system(Ubuntu 12.10, gcc 4.7.2, libgtk 2.24.13), it doesn't work.

I added g_print("%u\n", event->state); before the if statement, it shows that when I press Ctrl, the event->state is 20 instead of 4 or 1 << 2 in the documentation. If I change the GDK_CONTROL_MASK to 20, it works on the newer system but not the old one. Someone please tell me why this happen and how to fix it.

回答1:

event->state is a bitmap, which means that a value of 20 doesn't mean "20 instead of 4", but "4 and 16 at the same time". According to the headers, the value 16 (1 << 4) corresponds to the MOD2 modifier, which might correspond to the fn key present on laptops.

A simple fix is to use the & operator to check for control while ignoring other modifiers:

    if (event->state & GDK_CONTROL_MASK && event->keyval == 'd')

which will work on both systems.



回答2:

This happens because state also includes modifiers like Caps Lock and Num Lock.

The solution is documented at https://developer.gnome.org/gtk3/stable/checklist-modifiers.html:

Use gtk_accelerator_get_default_mod_mask() to get a bitmap of the modifiers that are also accelerator keys (Control, Alt, Shift, Super, Hyper, and Meta), then bitwise and the event state, e.g.:

GdkModifierType accel_mask = gtk_accelerator_get_default_mod_mask ();

if (event->state & accel_mask == GDK_CONTROL_MASK && event->keyval == 'd')
    ...