capture mouse with Xlib - c

I want to write a simple Xlib program changing the mouse behavior (to give an example, invert vertical movement). I have a problem with capturing the events.
I would like the code to
capture changes in the controllers position (I move mouse upward, MotionEvent)
calculate new cursor position (new_x -= difference_x)
set new cursor position ( move pointer down, XWarpPointer, prevent event generation here)
The code below should capture a motion event every time the mouse is moved, but it generates the event only when the pointer moves from one window to another... How to capture all the movement events?
#include "X11/Xlib.h"
#include "stdio.h"
int main(int argc, char *argv[])
{
Display *display;
Window root_window;
XEvent event;
display = XOpenDisplay(0);
root_window = XRootWindow(display, 0);
XSelectInput(display, root_window, PointerMotionMask );
while(1) {
XNextEvent( display, &event );
switch( event.type ) {
case MotionNotify:
printf("x %d y %d\n", event.xmotion.x, event.xmotion.y );
break;
}
}
return 0;
}
Related:
X11: How do I REALLY grab the mouse pointer?

When your program receives mouse events, it receives a copy of the events; copies are also sent to other programs that are listening for those events (see XSelectInput(3)). You cannot override this without using XGrabPointer(3) to take exclusive ownership of the mouse, which will prevent other programs from receiving any mouse events. In short, you can't actually do what you are trying to do.
Note also that if a client has specified PointerMotion in its do-not-propagate mask for one of its windows, you will not receive any pointer motion events within its window (again, unless you do a grab).

If you want to change the behavior of the mouse when it is being moved, I suggest you to play with the input properties instead of trying to do the processing in your program.
xinput --list
xinput --list-props 'USB Optical Mouse'
xinput --set-prop 'USB Optical Mouse' 'Evdev Axis Inversion' 1 0
xinput --set-prop 'USB Optical Mouse' 'Evdev Axes Swap' 1
There's also the 'Coordinate Transformation Matrix' property but for some reason it's not working for me right now.
You don't need to call the xinput program yourself: you can use Xlib calls (look at xinput's source code).

Related

GTK4 - how to get coordinates of a moving window

In previous versions of GTK, we added a necessary event mask, attached to a configure-event.
gtk_widget_add_events(GTK_WIDGET(window), GDK_CONFIGURE);
g_signal_connect(G_OBJECT(window), "configure-event",
G_CALLBACK(configure_callback), NULL);
We got the corresponding x,y coordinates from the handler.
void configure_callback(GtkWindow *window,
GdkEvent *event, gpointer data) {
int x, y;
x = event->configure.x;
y = event->configure.y;
...
}
The closest thing that resembles this is GtkEventControllerMotion, but it is
for mouse pointer, not for window move events.
How to do it in GTK4?
You can't. This is because not all window managers/compositors provide this information, for privacy and/or technical reasons.
Wayland, for example, does not provide window coordinates, because not every Wayland compositor is even a 2D rectangle - for example, kiosk compositors like cage or gamescope, or VR compositors like wxrd.
Because of this, the functionality was removed from GTK4. You will need to use an X11-specific API to get this information, but note that you would need to force your application to use X11 (over XWayland on Wayland compositors)
https://docs.gtk.org/gtk4/migrating-3to4.html#adapt-to-gtkwindow-api-changes

Why does my window only get shown after I use SDL_PollEvent()?

I'm using SDL2 in C and wanted to show something to the screen without the need for taking in inputs. But when I ran the code to present a black screen the window would not open. I proceeded to do what I knew would make it pop up and added a SDL_Event variable and used SDL_PollEvent(). I would like to know why I had to do this, and if there would be a way for me to use a SDL_Window without polling any events.
(For example: something like an animation).
The code in question is as follows:
#include <stdio.h>
#include <stdlib.h>
#include <SDL2/SDL.h>
int main()
{
if (SDL_Init(SDL_INIT_VIDEO))
{
fprintf(stderr, "Error while Initalizing SDL2: %s\n", SDL_GetError());
return EXIT_FAILURE;
}
SDL_Window *window = SDL_CreateWindow("Connect Four", 100, 100, 500, 500, SDL_WINDOW_SHOWN);
if (!window)
{
fprintf(stderr, "Error while Initalizing window: %s\n", SDL_GetError());
return EXIT_FAILURE;
}
SDL_Renderer *renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
if (!renderer)
{
SDL_DestroyWindow(window);
fprintf(stderr, "Error while Initalizing renderer: %s\n", SDL_GetError());
return EXIT_FAILURE;
}
SDL_Event events; //without these two lines
SDL_PollEvent(&events); //the window will not open on screen
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
SDL_RenderClear(renderer);
SDL_RenderPresent(renderer);
SDL_Delay(3000);
SDL_DestroyWindow(window);
SDL_Quit();
return EXIT_SUCCESS;
}
It may be confusing because some SDL examples (including SDL doc wiki) uses "draw&delay" code without event processing, but that is not a reliable way to display things on screen. That examples are overly simplified and was written long time ago when it kind of worked, but only for simplest things, as 3 seconds is probably not long enough for window manager to kill unresponsive program, or for user to notice window image collapses if it is minimised or covered by other windows.
Short answer to your question is no, your program have to communicate with window manager (side note - it may not be necessary on some operating/graphics systems); you have to get events and react to special window events (e.g. your window becoming visible or user requesting resize). In SDL, this is done with SDL_PumpEvents call, which internally does this communication and generates event queue that you can inspect later, so you need to call that frequently, either directly or indirectly via SDL_PollEvent or SDL_WaitEvent. If you don't, window manager probably will not be nice to you - depending on window manager, you may get "application not responding, let's kill it" dialog, greyed out window or, as in your case, no window at all (that particular case is because you've presented your rendering result before "your window is now visible" event was received, so your image is discarded; that's why processing events before rendering changed how things go).
But even so, processing events once is not correct either. What you have is still optimistic "let's hope nothing breaking will happen in 3 seconds". If you do animation, process events on every frame; but if you have static display code - it may be good idea to treat it like it is animated too. Basically you need rendering loop, with event processing at start, and redraw code to re-generate your image when you have to. This "have to" may be either unconditional (full redraw on every iteration), or, if you really don't want to (e.g. for processing cost reasons - most GUI programs don't do full redraw when idle) you still have to redraw when window manager says your previous image is no longer valid - SDL notifies you of that via SDL_WindowEvent of SDL_WINDOWEVENT_EXPOSED type. In that case you may want to use blocking SDL_WaitEvent to avoid unnecessary iterations.

Hide mouse cursor in client area with delay?

I am writing a Windows application in C.
I am hiding the mouse cursor in the client area of the window by handling the WM_SETCURSOR message:
case WM_SETCURSOR:
{
static BOOL HideCursor = FALSE;
if ((LOWORD(LParam) == HTCLIENT) && !HideCursor)
{
HideCursor = TRUE;
ShowCursor(FALSE);
}
else if ((LOWORD(LParam) != HTCLIENT) && HideCursor)
{
HideCursor = FALSE;
ShowCursor(TRUE);
}
Result = DefWindowProc(Window, Message, WParam, LParam);
break;
}
This works fine, but it is a little awkward, because the mouse cursor disappears instantly as soon as it crosses into the client area of the window. The user can easily lose track of where the mouse cursor "should be" when he or she is trying to move the cursor toward the toolbar buttons, or manually resize the window, for example.
How can I add a second or two of delay in there so that the user can move the mouse over the client area of the window without the mouse instantly disappearing, but if the mouse cursor hovers in the client area for more than a second or two, it disappears?
When you are notified that the mouse cursor entered the window, you could use SetTimer(hWnd, ID_MOUSE_TIMER, 2000, NULL) where hWnd is your window handle and ID_MOUSE_TIMER is an arbitrary identifier for a timer, to create a timer that will fire after 2000 milliseconds.
You can then respond to the WM_TIMER message and hide the cursor just as you did before, but also use KillTimer(hWnd, ID_MOUSE_TIMER) to prevent further calls.
When the mouse cursor leaves your window, you should also destroy the timer, and also restore visibility of the mouse cursor if it was hidden just as you did before.
To read up on timers, check the corresponding section in the MSDN.
You could use the WM_MOUSEHOVER message.

Mouse movement events in NCurses

I wonder if there is such a thing as mouse movement events in NCurses, and if there is a way to catch them. Following the Interfacing with the mouse (from the NCurses programming HOWTO) it seems that by enabling the REPORT_MOUSE_POSITION bit in the call to mousemask, one can indeed catch mouse movement events.
So, I tried that and it does not seem to work. I have something like this:
int ch, count=0;
mmask_t old;
initscr ();
noecho ();
cbreak ();
mousemask (ALL_MOUSE_EVENTS | REPORT_MOUSE_POSITION, &old);
keypad (stdscr, TRUE);
while ((ch = getchar ()) != 'q')
{
count++;
if (ch == KEY_MOUSE)
{
MEVENT event;
assert (getmouse (&event) == OK);
mvprintw (0, 0, "Mouse Event!\n");
}
mvprintw (1, 1, "Event number %4d",count);
}
...
I expected that as I'll move my mouse cursor, I'll see the event counter increasing. But it didn't. I also tried moving it while mouse button 1 is down to see if generates "drag" events, and it also didn't do anything. The question is, if it's simply a problem of my terminal emulator? Or maybe I'm misunderstanding what NCurses considers as mouse movement events? All the other mouse events were received (and I can operate programs in the console that use the mouse).
I tried gnome-terminal, xterm, and some other stuff. I also tried a textual environment (without X) by going to the tty's of my linux machine (Fedora 15, Ctrl+Alt+F2) and that did not work.
Finally, assuming I do get this right and those events should be reported, what is the bstate field of a MEVENT for a mouse movement evenet?
Many thanks in advance!
You need:
a terminal which supports mouse event reporting;
$TERM pointing to a terminfo entry which has an appropriate XM entry to initialise the terminal correctly.
xterm at least satisfies (1); for (2), it's likely that you'll need to set a different value for TERM.
Try:
TERM=xterm-1002 to get a position event when the cursor moves to a different cell while a button is being held down; or
TERM=xterm-1003 to always get a position event whenever the cursor moves to a different cell, even if no button is pressed.
The resulting events have the REPORT_MOUSE_POSITION bit set on the bstate field.
(The "PORTABILITY" section of the curs_mouse(3x) man page describes the terminal initialisation, and the "Mouse Tracking" section of the Xterm Control Sequences documentation describes the relevant "private mode" extensions.)
The code that you've given above needs to use getch(), not getchar(); and needs a refresh() inside the loop! Other than that, it works for me with xterm when using one of the appropriate TERM settings.

SendMessage WM_MOUSEMOVE not working as expected

When sending the WM_MOUSEMOVE message to the client area of an application (in this case Open Office Writer) the image will flicker as if the mouse is over the specified coordinates, but does not stay in that state. The code is:
PostMessage(hWndClient, WM_MOUSEMOVE, 0, MAKEWORD(x, y))
where x and y are relative to the client area.
Sending this in a loop still does not work as the area highlighted by the mouse over event will just flicker.
Any help would be appreciated.
The app could be triggering on all sorts of other events. Maybe it wants to see WM_MOUSEHOVER etc... It's been long a while, but I remember there being something really klugy about how some of these events were implemented - like there was a separate thread that polled the mouse and generated WM_MOUSELEAVE events when the mouse was no longer over the window. You could be getting bitten by something like that too. It's also possible that the application itself is polling the mouse for its true position when it receives the event.
Depending on what you're trying to do, perhaps you could programmatically move the mouse instead of just trying to fake events. Unfortunately, I don't remember the API for this, but I'm sure it's possible.
I used sth like this in my main window and looks that helps... in WM_MOUSEMOVE:
POINT Point;
GetCursorPos(&Point);
ScreenToClient(hHwnd, &Point);
int X = Point.x;
int Y = Point.y;
Try this: PostMessage(hWndClient, WM_MOUSEMOVE, MK_LBUTTON, MAKELONG(x, y)), since postion is a 32-bit integer, the lower 16 bit is x, and the higher 16 bit is y, maybe you should use MAKELONG instead of MAKEWORD.
Check WM_MOUSEMOVE.

Resources