10

This is a follow-up to How to insert synthetic mouse events into X11 input queue

I'm trying to create a program that takes input from an external device and generates mouse clicks so that GTK+ will get and handle the events as if they mouse click happened normally.

Seems I can use a GdkEventButton structure: https://developer.gnome.org/gdk/stable/gdk-Event-Structures.html#GdkEventButton

But I'm not sure how to determine the values to enter for each field. I'm looking for a small snippet of sample code or advice from somebody that has used gtk_event_put() with a GdkEventButton structure.

EDIT: If anybody knows a different or better way than my answer, please let me know.

Community
  • 1
  • 1
Chimera
  • 5,884
  • 7
  • 49
  • 81

2 Answers2

6

There is another way, but this way I'm going to present, involve the use of pure X11, although seeing your question's tags, an answer that uses X11 is acceptable too.

That said, let me start by introducing the function where all happens, one thing, I'm going to be a bit verbose, in case of some other people that don't understand how use this function.


void
mouse_click(Display *display, int x, int y, int click_type, struct timeval *t);
  • display structure previously returned by XOpenDisplay() that contains all the information about the X server.
  • x the x coordinate of the pointer relative to the root of the screen.
  • y the y coordinate of the pointer relative to the root of the screen.
  • click_type the type of the click. For this answer, either MOUSE_RIGHT_CLICK or MOUSE_LEFT_CLICK
  • t timeval structure, specified the time interval the click should remain pressed.

Implementation

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#include <sys/types.h>
#include <sys/select.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>

#define portable_usleep(t) select(0, NULL, NULL,NULL, t)

enum { MOUSE_RIGHT_CLICK, MOUSE_LEFT_CLICK };

void
mouse_click(Display *display, int x, int y, int click_type, struct timeval *t)
{
        Window root;
        XEvent event;

        root = DefaultRootWindow(display);
        XWarpPointer(display, None, root, 0, 0, 0, 0, x, y);

        memset(&event, 0, sizeof(event));

        event.xbutton.type        = ButtonPress;
        event.xbutton.button      = click_type;
        event.xbutton.same_screen = True;

        XQueryPointer(display, root, &event.xbutton.root, &event.xbutton.window,
                     &event.xbutton.x_root, &event.xbutton.y_root,
                     &event.xbutton.x, &event.xbutton.y, &event.xbutton.state);

        event.xbutton.subwindow = event.xbutton.window;

        while(event.xbutton.subwindow) {
                event.xbutton.window = event.xbutton.subwindow;
                XQueryPointer(display, event.xbutton.window,&event.xbutton.root,
                &event.xbutton.subwindow, &event.xbutton.x_root,
                &event.xbutton.y_root, &event.xbutton.x, &event.xbutton.y,
                &event.xbutton.state);
        }

        if(XSendEvent(display, PointerWindow, True, 0xfff, &event)==0)
                fprintf(stderr, "XSendEvent()\n");

        XFlush(display);
        portable_usleep(t); /* keeps the click pressed */

        event.type = ButtonRelease;
        event.xbutton.state = 0x100;

        if(XSendEvent(display, PointerWindow, True, 0xfff, &event)==0)
                fprintf(stderr, "XSendEvent()\n");

        XFlush(display);
}

Well, this function it's pretty straight forward, for example, left click at position 500,645 button pressed for half a second, this is how:

int
main(void)
{
        int x;
        int y;
        Display        *display;
        struct timeval t;

        display = XOpenDisplay(NULL);

        if(!display) {
                fprintf(stderr, "Can't open display!\n");
                exit(EXIT_FAILURE);
        }

        x = 500;
        y = 645;

        t.tv_sec  = 0;
        t.tv_usec = 500000;  /* 0.5 secs */

        mouse_click(display, x, y, MOUSE_LEFT_CLICK, &t);

        XCloseDisplay(display);
        return 0;
}

Compile

$ gcc -o click click.c -lX11
yeyo
  • 2,954
  • 2
  • 29
  • 40
  • Thanks for the answer. I'm not sure I can make this happen in a GTK+/GDK environment. I will do some investigation into XSendEvent() and see if I can get GDK to provide the needed Display and Window parameters. – Chimera Jul 15 '13 at 00:43
  • Thank Kira. The left mouse button works. But the right mouse button doesn't work. Do you have any idea on how to solve this issue? :) – Juan Carlos Kuri Pinto Jul 01 '14 at 19:56
  • @JuanCarlosKuriPinto Sorry, I meant to reply you as soon as read your comment, unfortunatly I have not been able to test this code on my Mac ... I suppose I need a X11 package for that matter :\ Anyways, as soon as I get home from vacation, I'll check it. – yeyo Jul 03 '14 at 18:28
4

After some research I was able to learn that GTK+/GDK 2.14 and newer have some functions for creating automated testing suites for GTK+ applications.

I was able to use gdk_test_simulate_button to simulate mouse clicks. This may not be the optimum solution, but right now it appears to work well.

Chimera
  • 5,884
  • 7
  • 49
  • 81
  • The link is dead. On Sept 12 2014 I found a doc here: https://developer.gnome.org/gdk3/stable/gdk3-Testing.html#gdk-test-simulate-button by googling gdk_test_simulate_button Also of note there is something called gtk_test_widget_click() which I found a brief doc for here: https://people.gnome.org/~shaunm/girdoc/C/Gtk.test_widget_click.html – msouth Sep 13 '14 at 06:18