5
votes

Consider the following code:

#include<gtk/gtk.h>
#include<stdio.h>


static void destroy(GtkWidget*, gpointer);
static gboolean mouse_moved(GtkWidget *widget,GdkEvent *event,gpointer user_data);

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

    GtkWidget *main_window;

    // initializing
    gtk_init(&argc, &argv);

    main_window=gtk_window_new(GTK_WINDOW_TOPLEVEL);
    gtk_window_set_title(GTK_WINDOW(main_window),"Test");
    gtk_widget_set_size_request (main_window, 500, 300);

    // connect the window with signals
    g_signal_connect (G_OBJECT (main_window), "destroy",G_CALLBACK (destroy), NULL);
    g_signal_connect (G_OBJECT (main_window), "motion-notify-event",G_CALLBACK (mouse_moved), NULL);

    gtk_widget_set_events(main_window, GDK_POINTER_MOTION_MASK);

    // show window
    gtk_widget_show_all (main_window);

    gtk_main();
    return 0;
}


static void destroy(GtkWidget *window,gpointer data) {
    gtk_main_quit ();
}


static gboolean mouse_moved(GtkWidget *widget,GdkEvent *event, gpointer user_data) {

    if (event->type==GDK_MOTION_NOTIFY) {
        GdkEventMotion* e=(GdkEventMotion*)event;
        printf("Coordinates: (%u,%u)\n", (guint)e->x,(guint)e->y);
    }
}

When I run this code from terminal, it opens an empty window, and prints out the mouse coordinates at each time.
Here's (part of) the output from the last execution:

Coordinates: (390,17)
Coordinates: (390,18)
Coordinates: (390,18)
Coordinates: (390,18)
Coordinates: (390,18)
Coordinates: (390,19)
Coordinates: (390,19)
Coordinates: (390,19)
Coordinates: (391,22)
Coordinates: (391,22)
Coordinates: (391,22)
Coordinates: (391,22)
Coordinates: (391,22)
Coordinates: (391,22)
Coordinates: (391,22)
Coordinates: (391,23)
Coordinates: (391,23)
Coordinates: (391,23)
Coordinates: (390,23)
Coordinates: (390,23)
Coordinates: (390,23)
Coordinates: (390,23)
Coordinates: (390,24)
Coordinates: (390,24)
Coordinates: (390,24)
Coordinates: (390,24)
Coordinates: (390,24)
Coordinates: (390,24)
Coordinates: (390,24)
Coordinates: (390,24)
Coordinates: (390,24)
Coordinates: (390,24)
Coordinates: (390,24)
Coordinates: (390,25)
Coordinates: (390,25)
Coordinates: (390,25)
Coordinates: (390,25)
Coordinates: (390,25)
Coordinates: (390,25)
Coordinates: (390,26)
Coordinates: (390,26)
Coordinates: (390,26)
Coordinates: (390,26)
Coordinates: (390,26)
Coordinates: (390,26)
Coordinates: (390,26)
Coordinates: (390,27)
Coordinates: (390,27)
Coordinates: (390,27)
Coordinates: (390,27)
Coordinates: (390,27)
Coordinates: (390,28)
Coordinates: (390,28)
Coordinates: (390,28)
Coordinates: (390,28)
Coordinates: (390,28)
Coordinates: (390,28)
Coordinates: (390,28)
Coordinates: (390,28)
Coordinates: (390,29)
Coordinates: (390,29)
Coordinates: (390,29)
Coordinates: (390,30)
Coordinates: (390,30)
Coordinates: (390,30)
Coordinates: (390,30)
Coordinates: (390,31)
Coordinates: (390,31)
Coordinates: (390,32)
Coordinates: (390,32)
Coordinates: (390,32)
Coordinates: (390,33)
Coordinates: (390,33)
Coordinates: (390,33)
Coordinates: (390,33)
Coordinates: (390,33)
Coordinates: (390,34)
Coordinates: (390,34)
Coordinates: (390,34)
Coordinates: (389,34)
Coordinates: (389,35)
Coordinates: (389,36)
Coordinates: (389,36)
Coordinates: (389,36)
Coordinates: (389,37)
Coordinates: (389,36)
Coordinates: (389,37)
Coordinates: (389,37)
Coordinates: (389,37)
Coordinates: (389,37)
Coordinates: (389,38)
Coordinates: (389,38)
Coordinates: (388,38)
Coordinates: (388,38)
Coordinates: (388,38)
Coordinates: (388,38)
Coordinates: (388,39)
Coordinates: (388,39)
Coordinates: (388,39)
Coordinates: (388,39)
Coordinates: (388,39)
Coordinates: (388,40)
Coordinates: (387,40)
Coordinates: (387,40)
Coordinates: (387,40)
Coordinates: (387,40)
Coordinates: (387,40)
Coordinates: (387,40)
Coordinates: (387,40)
Coordinates: (386,41)
Coordinates: (386,41)
Coordinates: (386,41)
Coordinates: (386,41)
Coordinates: (386,41)
Coordinates: (386,41)
Coordinates: (385,41)
Coordinates: (385,41)
Coordinates: (385,41)
Coordinates: (385,41)
Coordinates: (384,42)
Coordinates: (384,42)
Coordinates: (384,42)
Coordinates: (384,42)
Coordinates: (384,42)
Coordinates: (384,42)
Coordinates: (384,42)
Coordinates: (383,42)
Coordinates: (383,42)
Coordinates: (383,42)
Coordinates: (383,42)
Coordinates: (383,43)
Coordinates: (382,43)
Coordinates: (382,43)
Coordinates: (382,43)
Coordinates: (382,43)
Coordinates: (381,43)
Coordinates: (381,43)
Coordinates: (381,43)
Coordinates: (380,43)
Coordinates: (380,44)
Coordinates: (380,44)
Coordinates: (380,44)
Coordinates: (380,44)
Coordinates: (379,44)
Coordinates: (378,44)
Coordinates: (378,44)
Coordinates: (377,44)
Coordinates: (377,44)

and so on...

What confuses me is this: How can there be two consecutive events holding the same coordinates? For example, take these two lines:

Coordinates: (380,44)
Coordinates: (380,44)  

This basically saying that the mouse hasn't been moving (it went from (380,44) to (380,44)), so how could there possibly be a movement event to launch the handler for that second line of input?


Another less important and (maybe) unrelated issue:
Why is this line necessary?

gtk_widget_set_events(main_window, GDK_POINTER_MOTION_MASK);

In the book Foundation Of Gtk+ Development it says:

Next, you need to add an event mask to the event box so that it knows what type of events the widget will receive. Values for the GdkEventMask enumeration that specify event masks are shown in Table 3-3. A bitwise list of GdkEventMask values can be passed to gtk_widget_set_events() if you need to set more than one.

But isn't this call redundant, given that we already have g_signal_connect()? which is, according to the documentation:

Connects a GCallback function to a signal for a particular object.

The handler will be called before the default handler of the signal.

Why do I need to register for a signal twice?
Once with gtk_widget_set_events() and second with g_signal_connect()?

2
Not a complete answer, but: the gtk_widget_set_events() and g_signal_connect() isn't "register for a signal twice". The gtk_widget_set_events() call is you asking to receive GK events, which are delivered as signals you connect to with g_signal_connect(). Why you have to explicitly ask for GDK events, however, is something I don't know. (You may also consider gtk_widget_add_events(), which avoids the need to know what events you get by default/keep track of events you add, as an alternative to gtk_widget_set_events().) - andlabs
I don't know for sure either, but I believe the reason for gtk_widget_set_events() is that some events are not emitted by default for performance reasons. (If nobody wants them then you may as well avoid the performance overhead of emitting them, especially if they are very frequent like pointer motion events.) - ptomato
@ptomato I think you're right... When I comment out gtk_widget_set_events() I don't get notified for the mouse movement any more... - so.very.tired

2 Answers

4
votes

I tried monitoring the mouse movement in the X server directly using xev and it seems to be the case that xorg reports multiple mouse events with the same coordinates but different timestamps. When using the pointing stick on the keyboard however it didn't do this, only with the trackpad or an external mouse.

My guess would be that the precision is actually higher, but the events are reported for a pixel on the screen. This might cause the driver to report more mouse events then needed.

0
votes

Why is this line necessary?

gtk_widget_set_events(main_window, GDK_POINTER_MOTION_MASK);

But isn't this call redundant, given that we already have g_signal_connect()? which is, according to the documentation:

Think for example about a GtkButton like this one. If you are clicking on the Signals Link you will notice that there are 6 signals available:

Signals
    void    activate    Action
    void    clicked     Action
    void    enter       Run First
    void    leave       Run First
    void    pressed     Run First
    void    released    Run First

None of them is scroll_event from GdkEventScroll , this means that the following program does not work as (probably) espected:

#include <gtk/gtk.h>

gboolean scroll_callback        ( GtkWidget *widget, GdkEvent  *event, gpointer   user_data );

int main ( void )
{
    GtkWidget *window;
    GtkWidget *button;
    gtk_init( NULL, NULL );
    /// ***
    window = gtk_window_new( GTK_WINDOW_TOPLEVEL );
    gtk_window_set_default_size( GTK_WINDOW( window ), 300, 250 );
    g_signal_connect( window, "destroy", gtk_main_quit, NULL );
    gtk_container_set_border_width( GTK_CONTAINER( window ), 25 );
    /// ***
    button = gtk_button_new_with_mnemonic( "_Click me" );
    g_signal_connect( button, "clicked", gtk_main_quit, NULL );
    gtk_container_add( GTK_CONTAINER( window ), button );
    /// ***
    g_signal_connect( button, "scroll_event", G_CALLBACK( scroll_callback ), window );
    /// ***
    gtk_widget_show_all( window );
    gtk_main();
}

gboolean scroll_callback      ( GtkWidget *widget, GdkEvent  *event, gpointer data )
{
    (void)widget;
    if ( event->type == GDK_SCROLL ) /// Scroll the was Catched
    {
        if ( event->scroll.direction == GDK_SCROLL_DOWN )
        {
            g_print( "Scroll-Down Detected\n" );
            gtk_window_set_title( GTK_WINDOW( data ), "Scroll-Down Detected" );
        }

        if ( event->scroll.direction == GDK_SCROLL_UP )
        {
            g_print( "Scroll-UP Detected\n" );
            gtk_window_set_title( GTK_WINDOW( data ), "Scroll-UP Detected" );
        }
        return FALSE;
    }
    return TRUE;
}

Here we try to catch the scroll_event signal event, but the widget (button) it self does not has this type of Signal.

To fix it, we set/add events to the button just after we create it:

button = gtk_button_new_with_mnemonic( "_Click me" );
gtk_widget_set_events( button, GDK_SCROLL_MASK );

And the program works fine:

Scroll-Down Detected
Scroll-Down Detected
Scroll-Down Detected
Scroll-UP Detected
Scroll-UP Detected
Scroll-UP Detected