Search code examples
cgtkkeypress

GTK keypress event propagation either causes recursion or does not work


I have a simple GTK program containing a single webkit2 widget in a window and nothing else. I want to be able to filter the keypresses going to the webkit2 widget. At first I added a key mask to the webkit widget and added a callback function, however after propogating the event in the callback function it just calls itself recursively which results in a crash. To resolve this I attached my key mask to the main-window, which is parent to my webkit widget, however this no longer prevents input from getting stopped and sometimes key release events recursively call each other. I was wondering how to achieve my goal? (I've used all different types of propogation mentioned on the GTK+ input event handeling documentation page.)

current key mask:

    gtk_widget_add_events(GTK_WIDGET(main_window), GDK_KEY_PRESS_MASK);
    g_signal_connect(G_OBJECT(GTK_WIDGET(main_window)), "key_press_event", G_CALLBACK(keypressHandeler), NULL);
    g_signal_connect(G_OBJECT(GTK_WIDGET(main_window)), "key_release_event", G_CALLBACK(keypressHandeler), NULL);

current callback function (the return is just to see if the propogation worked at the moment)

gboolean keypressHandeler(GtkWidget *widget, GdkEventKey *event, gpointer data){
    if(event->type == GDK_KEY_PRESS)
        printf("Key press\n");
    if(event->type == GDK_KEY_RELEASE)
        printf("Key release\n");
    return gtk_widget_event(GTK_WIDGET(webView), event);
}

Solution

  • The problem in your code is that you're manually simulating the signal through gtk_widget_event() (https://developer.gnome.org/gtk3/stable/GtkWidget.html#gtk-widget-event)

    so after the first time keypressHandeler gets called gtk_widget_event will simulate the same event again so it will run in an endless loop.

    if you return false or true in "keypressHandeler" your code will work

    #include <gtk/gtk.h>
    #include <webkit2/webkit2.h>
    
    
    
    WebKitWebView *webView;
    
    gboolean keypressHandeler(GtkWidget *widget, GdkEventKey *event, gpointer data)
    {
        if (event->type == GDK_KEY_PRESS)
            printf("Key press from mainwindow\n");
        if (event->type == GDK_KEY_RELEASE)
            printf("Key release\n");
    
        //for the key use want to propogate 
           if (dont wanna propogate )
            {
              return TRUE;
             } else {
              return FALSE;
            }
    
    }
    
    gboolean keypressHandeler2(GtkWidget *widget, GdkEventKey *event, gpointer data)
    {
        if (event->type == GDK_KEY_PRESS)
            printf("Key press from webkit\n");
        if (event->type == GDK_KEY_RELEASE)
            printf("Key release\n");
        // set it as TRUE to stop porpogation.. (for some reason it propogating to mainwindow once)
        return TRUE;
    }
    
    static void destroyWindowCb(GtkWidget* widget, GtkWidget* window);
    static gboolean closeWebViewCb(WebKitWebView* webView, GtkWidget* window);
    
    int main(int argc, char* argv[])
    {
        // Initialize GTK+
        gtk_init(&argc, &argv);
    
        // Create an 800x600 window that will contain the browser instance
        GtkWidget *main_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
        gtk_window_set_default_size(GTK_WINDOW(main_window), 800, 600);
    
        // Create a browser instance
        webView = WEBKIT_WEB_VIEW(webkit_web_view_new());
    
        // Put the browser area into the main window
        gtk_container_add(GTK_CONTAINER(main_window), GTK_WIDGET(webView));
    
        // Set up callbacks so that if either the main window or the browser instance is
        // closed, the program will exit
        gtk_widget_add_events(GTK_WIDGET(main_window), GDK_KEY_PRESS_MASK);
       g_signal_connect(G_OBJECT(GTK_WIDGET(main_window)), "key_press_event", G_CALLBACK(keypressHandeler), NULL);
        g_signal_connect(G_OBJECT(GTK_WIDGET(webView)), "key_press_event", G_CALLBACK(keypressHandeler2), NULL);
       g_signal_connect(G_OBJECT(GTK_WIDGET(main_window)), "key_release_event", G_CALLBACK(keypressHandeler), NULL);
        g_signal_connect(G_OBJECT(GTK_WIDGET(webView)), "key_release_event", G_CALLBACK(keypressHandeler2), NULL);
    
        g_signal_connect(main_window, "destroy", G_CALLBACK(destroyWindowCb), NULL);
        g_signal_connect(webView, "close", G_CALLBACK(closeWebViewCb), main_window);
    
        // Load a web page into the browser instance
        webkit_web_view_load_uri(webView, "http://www.webkitgtk.org/");
    
        // Make sure that when the browser area becomes visible, it will get mouse
        // and keyboard events
        gtk_widget_grab_focus(GTK_WIDGET(webView));
    
        // Make sure the main window and all its contents are visible
        gtk_widget_show_all(main_window);
    
        // Run the main GTK+ event loop
        gtk_main();
    
        return 0;
    }
    
    
    static void destroyWindowCb(GtkWidget* widget, GtkWidget* window)
    {
        gtk_main_quit();
    }
    
    static gboolean closeWebViewCb(WebKitWebView* webView, GtkWidget* window)
    {
        gtk_widget_destroy(window);
        return TRUE;
    }
    

    If this what your trying.. whats happening is that when you set GDK_KEY_PRESS_MASK it is also being reflected on the webkit.

    so if you comment " g_signal_connect(G_OBJECT(GTK_WIDGET(main_window)), "key_press_event", G_CALLBACK(keypressHandeler), NULL);" the program should work fine

    returning true for mainwindow handler will stop the propagation to the webview and returning false will allow propagation.