Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Simulate button click using GTK+ using gtk_event_put and a GdkEventButton structure

Tags:

c

x11

gtk

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.

like image 879
Chimera Avatar asked Jun 28 '13 19:06

Chimera


2 Answers

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
like image 81
yeyo Avatar answered Oct 20 '22 03:10

yeyo


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.

like image 32
Chimera Avatar answered Oct 20 '22 02:10

Chimera