Cocoa: How to send proper mouse delta events? - macos

I am making an application to control the mouse using an analog stick from a gamepad for Mac OS X (10.7.3).
Currently it is working pretty well, I can control the cursor in desktop and most games. But in Team Fortress 2, I cant control the aim, but can control the cursor in the menu. Mouse wheel and clicks works everywhere.
Another strange thing is that when I move the real mouse, the aim "jumps" the traveled distance from my "virtual mouse" before aiming normally, so it somewhat is receiving the events.
The game option "Raw mouse input" is disabled (I think it is not even supported in osx). And a similar application can controle the aim successfully.
I suspect the game is looking for "delta movement" or "relative movement" events or anything similar, but my code sets the position of the cursor using absolute positions. Not sute how to change this.
Here is the snippet of code used to send mouseMoved events:
EDIT: Crappy code removed!
.
EDIT:
Also, because I did this way, I need to check the screen bounds manually to prevent the cursor going Crazy. So in multi-screen setups, and when the user change the resolution, it gets worst. Would be so much better if I can just send the amount of movement and let the OS take care of constraining the cursor.
.
The question is:
I am doing the mouse move events the wrong way?
How can I fix this?
EDIT2:
So, that was just a stupid bug, sorry =P

I just forgot to call CGSetIntegerValueField(kCGEventMouseDeltaX, ...) (and Y) with the delta values... =P

Finally got this working, after just a few short hours of Googling and experimenting :) Looks like Rodrigo was trying to say that you have to call CGEventSetIntegerValueField on kCGMouseEventDeltaX and kCGMouseEventDeltaY on the mouse event after constructing it.
My code, using PyObjC:
from Quartz.CoreGraphics import (
CGEventCreate,
CGEventCreateMouseEvent,
CGEventGetLocation,
CGEventPost,
CGEventSetIntegerValueField,
kCGEventMouseMoved,
kCGHIDEventTap,
kCGMouseEventDeltaX,
kCGMouseEventDeltaY,
)
def mouse_delta(delta_x, delta_y):
"""
Send a MouseMoved event with the given deltaX and deltaY attributes,
but without changing the cursor location.
"""
# Might be a better way to get the current mouse location
x, y = CGEventGetLocation(CGEventCreate(None))
# The `mouseButton` parameter to CGEventCreateMouseEvent is ignored
# unless `mouseType` is kCGEventOtherMouse{Down,Dragged,Up},
# so let's just pass the value 0
event = CGEventCreateMouseEvent(None, kCGEventMouseMoved, (x, y), 0)
CGEventSetIntegerValueField(event, kCGMouseEventDeltaX, delta_x)
CGEventSetIntegerValueField(event, kCGMouseEventDeltaY, delta_y)
return CGEventPost(kCGHIDEventTap, event)

Related

How to track mouse movements without limiting it to screen size?

I'm using WM_MOUSEMOVE to get changes in mouse position. When simulating "knobs" for example it's desired to let the user go up/down with mouse without any limits. In this cases I hide cursor and use SetCursorPos to change its position every time user moves with it and detect just the difference from the original position.
Unfortunately it doesn't seem to work - if I set the mouse position, it sometimes works, but sometimes is one or more pixels away, which is just wrong. And even bigger trouble is that after the call another WM_MOUSEMOVE seems to be delivered, which unfortunately does the same thing as it wants to move the cursor back to the original position again. So it ends up in an infinite cycle or settings mouse position and receiving messages until the user releases the mouse button.
What's the correct approach or what's the problem?
The raw input system can do this - it lets you register for raw mouse input that isn't clipped or confined to the screen boundaries.
Broadly speaking, you register for raw input using RegisterRawInputDevices(). Your window will then receive WM_INPUT messages, which you process using the GetRawInputData() function.
See Using Raw Input for an example.
I hide cursor and use SetCursorPos to change its position every time user moves with it and detect just the difference from the original position.
This is just plain wrong. Instead, use SetCapture() to capture the mouse. All movements will be reported as WM_MOUSEMOVE messages with coordinates that are relative to the specified window, even if the mouse is outside of that window, until you release the capture.
Asking the user to move the mouse continuously, even after the cursor hit the screen limit is a very bad idea in terms of User Interface, IMHO.
Some games have another approach: when the mouse hit the "limit", the game enter a special mode: things appears to function exactly as if the mouse was moving, even if the user don't move it. When the user wants to exit that mode, he just has to move the mouse of the limit.
Doing so requires a timer, armed when the mouse hit some limit, executing code periodically as if the mouse was moving. The timer is stopped when a real mouse movement makes it leaves the limit.
Ok folks, so I found a solution simple enough:
The main problem is that SetCursorPos may not set the coordinates accurately, I guess it's because of some high resolution processing, nevertheless it's probably a bug. Anyway if SetCursorPos doesn't set the coordinates correctly (but +-1 in x and/or y) it also sends WM_MOUSEMOVE to the target window. As a result the window performs the exact same operation as before and this goes on and on.
So the solution is to remove all WM_MOUSEMOVE messages right after SetCursorPos:
MSG msg;
while (::PeekMessage(&msg, NULL, WM_MOUSEMOVE, WM_MOUSEMOVE, PM_REMOVE)) { };
Then retrieving the current mouse cursor pos using ::GetCursorPos .
It's ugly but seems to fix the problem. It basically seems that in some position of the mouse, the system always adds or subtracts 1 in either coordinate, so this way you let system do the weird stuff and use the new coordinates without trying to persuade system that your coordinates are the correct ones :).

How do I allow the Leap Motion to control my cursor in an OS X application?

So I have a game for Mac OS X built in cocos2D.
I'm using gestures to simulate keyboard commands to control my character and it works really well.
I submitted my game to the AirSpace store and it got rejected on the grounds that the Leap should be used to control my menus as well which is fair enough I guess.
Thing is for the life of me I cannot figure out how this is done. There are no examples out there to show me how to implement it and nothing in the SDK example that makes it clear either.
Does anyone have any examples they'd care to share, I only need it to hijack my cursor and allow a click when held over a button. I really didn't think something so complex would be needed on simply for basic menu navigation.
If this is a Mac only game you should have access to the Quartz event api. This is the easiest way to generate mouse events in OS X...
I would recommend simply tracking the palm (hand) position, and moving the cursor based on that.
This is how I do my palm tracking:
float handX = ([[hand palmPosition] x]);
float handY = (-[[hand palmPosition] y] + 150);
The "+ 150" is the number of millimetres above the Leap device, to use as the '0' location. From there you can move the cursor based on the hand offset from 0.
The function I use to move the cursor (using Quartz):
- (void)mouseEventWithType:(CGEventType)type loc:(CGPoint)loc deltaX:(float)dX deltaY:(float)dY
{
CGEventRef theEvent = CGEventCreateMouseEvent(NULL, type, loc, kCGMouseButtonLeft);
CGEventSetIntegerValueField(theEvent, kCGMouseEventDeltaX, dX);
CGEventSetIntegerValueField(theEvent, kCGMouseEventDeltaY, dY);
CGEventPost(kCGHIDEventTap, theEvent);
CFRelease(theEvent);
}
and an example function call:
[self mouseEventWithType:kCGEventMouseMoved loc:CGPointMake(newLocX, newLocY) deltaX:dX deltaY:dY];
This call will move the mouse. Basically you just pass the new location of the mouse, and the corresponding deltas, relative to the last cursor position.
I can provide more examples such as examples for getting mouse location, clicking the mouse, or even a full mouse moving program...
EDIT 1:
To handle click and drag with Quartz, you can call the same function as above only pass in kCGEventLeftMouseDown.
The catch is that in order to drag you cannot call the kCGEventMouseMoved you must instead pass kCGEventLeftMouseDragged while the drag is happening.
Once the drag is done you must pass a kCGEventLeftMouseUp.
To do a single click (no drag) you simply call mouse down and then up right after, without any drag...

Simulated MouseEvent not working properly OSX

Back in 2010, Pierre asked this question (his accepted answer doesn't work for me).
I'm having the same problem: I am able to successfully move the mouse around (and off!?!) the screen programmatically from my Cocoa Application, however bringing the mouse to the location of my dock doesn't show it (and some other applications aren't registering the mouse moved event, eg. games that remove the mouse)
The method I am using is thus:
void PostMouseEvent(CGMouseButton button, CGEventType type, const CGPoint point)
{
CGEventRef theEvent = CGEventCreateMouseEvent(NULL, type, point, button);
CGEventSetType(theEvent, type);
CGEventPost(kCGSessionEventTap, theEvent);
CFRelease(theEvent);
}
And then when I want to move the mouse I run:
PostMouseEvent(0, kCGEventMouseMoved, mouseLocation);
Note that this code DOES generate mouseover events for things such as links.
Now that's it's 2013, is it possible to fix this issue?
Thanks for your time!
I would both warp the cursor and generate the mouse-move event. I know from experience, for example, that warping the cursor, while it doesn't generate an event itself, modifies the subsequent mouse move event to include the moved distance in its mouse delta. I don't know if your synthesized move event will include the proper delta values on its own.
OK, so evidently MacOSX needs the mouse to be at exactly the edge of the screen for the dock to show!
Because I keep my dock on the left-side of the screen (due to many programs keeping vital buttons at the bottom of their windows), all I had to do was say
if (mouseLocation.x < 0)
{
mouseLocation.x = 0;
}
And it worked!
I am also using KenThomases' idea to warp the cursor as well.
(this answer is marked correct as it allows me to show the dock - however there are still some applications that are not responding to mouse input)

X11: Removing event from queue

I'm creating an FPS demo using an Engine called: Gameplay. I'm currently trying to define a captureMouse() function into the engine so the player can look around the map. I've already been able to pin the cursor to the center of the window and turn it invisible, but as I move the mouse the screen (camera) seems to "vibrate" as it moves around. After a lot of tinkering with X11 functions I figured that the XWarpPointer() function I'm using to warp the cursor back to the center of the window is adding a "mouse moved" event to the event queue.
X11 Question: How can I identify and remove an event from the event queue before it is captured by the event cycle?
Question: Has anyone been a similar problem and solved in a different manner? If so, what did you do?
I'm sorry if I'm not being clear. I have no extensive knowledge of X11, but I really need to add this to the engine so I can, in turn, add it to my game.
I guess you're using XtAppMainLoop to handle your events.
This is actually a call to XtAppNextEvent followed by XtDispatchEvent.
If you replace the XtAppMainLoop with a loop calling XtAppNextEvent to get the next event and check its type (the type field of the XEvent structure).
If you want to handle the event call XtDispatchEvent, do nothing to ignore it.
The loop needs to exit when XtAppGetExitFlag returns true (or add your own exit flag).

Autohotkey Mousemove Wrong Monitor

I'm using mousegetpos to get the current mouse position. I click somewhere else. Then I try to restore the original postion with mousemove. The mouse moves to a different monitor. I tried the alternative method dllcall, with no success. How do I move the mouse back to the original monitor?
It's easier to help if you post your code - then people can see where you're going wrong.
This works fine for me when pressing the Ctrl-T hotkey:
CoordMode, Mouse, Screen
^t::
MouseGetPos, x, y
; Do Stuff Here.
MouseMove, x, y
return
The CoordMode, Mouse, Screen line sets the coordinates relative to the entire screen rather than the active window. I've tested this on my multiple monitor setup and the mouse goes back to the original location every time, even across monitors. Let me know if it's not working for you.
Also, just to make things a little smoother, you can set the mouse speed to '0' before moving the mouse with:
SetDefaultMouseSpeed, 0
This makes the mouse appear to move instantly which looks a little cleaner in most scripts.
I can confirm that Gary's answer works perfectly for anybody else out there having similar problems. Thanks, Gary!
I was myself having a problem like this with Breakaway Audio Enhancer...
For anybody that uses or knows Breakaway, you have to double-click on the toolbar (in the taskbar) to mute it. The way Breakaway works with the sound pipeline other standard AHK mute scripts won't work, so moving the mouse to the toolbar and double-clicking is really the only method of muting. I wanted Caps Lock to mute (or unmute) audio and preferably have the mouse return to where it originally was.
I've had countless problems trying to get this to work with multiple monitors until Gary's post, so here is my solution for anybody else having similar issues:
Capslock::
BlockInput On
CoordMode, Mouse, Screen
MouseGetPos, xpos, ypos
MouseClick, left, 42, 965, 2 ;change the co-ordinates to match your system
MouseMove, xpos, ypos
SetDefaultMouseSpeed, 0
BlockInput Off
Return

Resources