3

I'm trying to get the current keyboard modifiers state through gnome GDK or GTK library in aim to implement an accessibility gnome shell extension that shows that state.

I know how to get thier state using xlib, but there is not full binding for gnome gjs.

The code below get only the initial state. It does not update state.

/*
 * compiling: gcc `pkg-config --cflags gdk-3.0` -o gdk_mod gdk_mod.c `pkg-config --libs gdk-3.0`
 */

#include <gdk/gdk.h>

int main (int argc, char **argv) {

    gdk_init(&argc, &argv);

    GdkDisplay * disp;
    disp = gdk_display_open(NULL);
    if (disp!=NULL) g_printf("display connected!\n");

    GdkKeymap * kmap;
    kmap = gdk_keymap_get_for_display(disp);

    guint state;
    state = gdk_keymap_get_modifier_state(kmap);
    g_printf("mod state: %x\n", state);

    while (1) {
        g_usleep(1000000);
        //kmap = gdk_keymap_get_for_display(disp);
        state = gdk_keymap_get_modifier_state(kmap);
        g_printf("mod state: %x\n", state);
    }

}

Here an example output with CAPS lock active then inactive but no change:

$ ./gdk_mod 
display found!
mod state: 2
mod state: 2
mod state: 2
mod state: 2
mod state: 2
^C

Currently using Kubuntu 15.04.

What's wrong with my code?

user.dz
  • 962
  • 2
  • 19
  • 39
  • 2
    You are likely going to need to run the GTK+ event loop for this to work. Try changing your `g_usleep()` loop to a `g_timeout_add()`-based [loop](https://developer.gnome.org/glib/stable/glib-The-Main-Event-Loop.html#G-SOURCE-CONTINUE:CAPS), using `gtk_init()` instead of `gdk_init()`, and running `gtk_main()`. – andlabs Aug 05 '15 at 13:24
  • @andlabs , you method works perfectly. Could you please post an answer. I'll wait little bit longer to accept it, because I suppose there some `gdk` level functions called by `gtk_main` loop to pull/update events from X to gdk event queue. If those functions exist, I can fix my original code and keep it using pure GDK. – user.dz Aug 06 '15 at 06:17
  • @andlabs , you deserve the credit and reputation of the answer. I've wrote one just to document my solution. If you copy that comment as answer I will accept it. – user.dz Aug 07 '15 at 22:07
  • Ah, I was going to investigate how to use GDK standalone, but you figured that out yourself, so I guess that works. – andlabs Aug 08 '15 at 00:09

2 Answers2

2
  • Indeed, I need an event loop as andlabs said in his comment. His suggestion to use GTK gtk_init() & gtk_main() works perfectly.

    /*
     * compiling: gcc `pkg-config --cflags gtk+-3.0` -o gtk_xkbmod3 gtk_xkbmod3.c `pkg-config --libs gtk+-3.0`
     */
    
    #include <gtk/gtk.h>
    
    static void update(GdkKeymap * kmap) {
        guint state;
        state = gdk_keymap_get_modifier_state(kmap);
        g_printf("%i\n", state);
    }
    
    int main (int argc, char **argv) {
    
        gtk_init(&argc, &argv);
    
        GdkKeymap * kmap;
        kmap = gdk_keymap_get_default();
    
        g_timeout_add_seconds(1, (GSourceFunc) update, kmap);
    
        gtk_main();
    
    }
    
  • I could also use GDK with GLib GMainLoop.

    /*
     * compiling: gcc `pkg-config --cflags gdk-3.0` -o gdk_xkbmod4 gdk_xkbmod4.c `pkg-config --libs gdk-3.0`
     */
    
    #include <gdk/gdk.h>
    
    GMainLoop *mainloop;
    
    static void update(GdkKeymap * kmap) {
        guint state;
        state = gdk_keymap_get_modifier_state(kmap);
        g_printf("%i\n", state);
    }
    
    int main (int argc, char **argv) {    
    
        gdk_init(&argc, &argv);
    
        GdkKeymap * kmap;
        kmap = gdk_keymap_get_default();
    
        g_timeout_add_seconds(1, (GSourceFunc) update, kmap);
    
        mainloop = g_main_loop_new(g_main_context_default(), FALSE);
        g_main_loop_run(mainloop);    
    }
    

References:

user.dz
  • 962
  • 2
  • 19
  • 39
2

You are going to need to run the GTK+ event loop for this to work. The event loop is part of GLib's main loop. When you call gtk_main(), this main loop is run. I don't know if it polls for events or has events pushed to it, but it won't ask for the keyboard state on the fly like you were trying to do.

The easiest way to set up GDK is to do it via GTK+ by using gtk_init() and gtk_main(). You can use GDK on its own, but I don't know how. You seemed to have figured it out, which works.

And instead of calling g_usleep(), which just blocks your program, you can hook a periodic timeout into the main loop. This is done with g_timeout_add(). The function that you pass to g_timeout_add() returns a Boolean value that decides whether the timer should be stopped or not, so you don't have to worry about rescheduling your function, as GLib will do that for you.

andlabs
  • 11,290
  • 1
  • 31
  • 52