mouse-speed

I have a situation where I’d like the mouse speed to be lower than what I normally use, but only within the bounds of a given object. I’d like to get the automatic change happening for both Windows and Mac, maybe even Linux too.

Things going on in the region require finer-grade control, so a lower mouse speed setting is preferable. But I don’t require the user to change OS mouse settings.

It looks like the way to do this is through JNA.
Agreed?
I’m curious if others have used this library or have an alternative to recommend. I noticed that references to it on JGO are sometimes 10 or more years old.

First looks at this library has me somewhat daunted. I’ve done the simple thing of loading the current jna.jar and jna-platform.jar into Eclipse and can construct a command to report back on the OS. That’s pretty simple. Getting in deeper is a little scary.

This sounds like a bit of an odd use case;

First of all, I can not really imagine any context in which this would not be awful UX, as you are effectively taking away control from the user, and control of something that most people are used to having full control over, no less.
It may sound nice to you in theory now but might turn out to be nothing but annoying in practice.

Regardless, let’s assume you really do want to have a kind of “syrup pit” for cursors.
All JNA really does is help you access non-Java libraries and APIs. Do you have any particular one in mind you want to use here?

Also, are you sure you can not simply emulate the desired effect through such means as changing the cursor position? That would seem like a more sensible choice to me, as it does not try to actually interfere with the mouse at OS level and you would also not have to deal with per-OS weirdness resulting from your non-standard use case.

Regarding JNA itself, no experience on my part there; but if you have not done any work with JNI and native interop in general before, JNA might not be what you are looking for either. Anybody feel welcome to correct me on this.

[quote=Drenius]Also, are you sure you can not simply emulate the desired effect through such means as changing the cursor position?
[/quote]
This is feels, like much nicer and cleaner aproach, plus this solution packed with less headache :smiley:

I like the syrup pit image!

I agree than an unexplained, arbitrary UX change within an area of the screen would be quite annoying and disorienting. My plan was to make this factor “opt in”, with the degree of viscosity configurable, which should go a long way to making it acceptable.

I’m intrigued about the idea of doing this via changing the cursor position on the fly. It hadn’t occurred to me this might be possible. I’ll give it a try and report back. I’m curious what happens at the transitions on and off the syrup pit.

Using JNA is daunting as I’d have to figure out how each OS configures mouse-speed and how JNA provides access. I’ve not ever delved into JNA or JNI before and know very little about what either allows one access to.

Below is a simple code example. Where the cursor is changed to crosshairs, in the application it would be made invisible. I can make an artificial cursor that moves at half-speed easily enough, but am stumped as to how to handle the transition back to the non-syrupy area. It seems to me there is going to be a “dead area” at the bounds, on one side or the other.

public class NoJNA extends Application
{		 
	private PlayPad playPad;
	
    public static void main(String[] args) 
    {
    	Application.launch(args);
    }
		 
    @Override
    public void start(Stage primaryStage) 
    {
    	primaryStage.setTitle("mouse-speed test, using computation");
                
        Group keystone = new Group();  
        Scene scene = new Scene(keystone, 800, 
        		400, Color.YELLOW);        

        playPad = new PlayPad(400, 200, scene);

        GridPane grid = new GridPane();
        grid.setPadding(new Insets(100, 0, 0, 200));
        grid.add(playPad, 0, 0);
             
        keystone.getChildren().add(grid);
        primaryStage.setScene(scene);
        primaryStage.show();
    }    
}

class PlayPad extends Canvas 
{	
	private double ppWidth, ppHeight;
	private double mouseX, mouseY;
	private double adjX, adjY;
	
	private GraphicsContext gc;
	
	public PlayPad(double arg1, double arg2, Scene scene) 
	{
		super(arg1, arg2);
		
		gc = this.getGraphicsContext2D();
		updatePlayPadDisplay();
		
		// default starting values
		ppWidth = arg1;
		ppHeight = arg2;

		setOnMouseEntered( e -> scene.setCursor(Cursor.CROSSHAIR) );
		setOnMouseExited( e -> scene.setCursor(Cursor.DEFAULT) );
		setOnMouseMoved( e -> 
			updateMouseLocation((MouseEvent)e));
	}

	private void updateMouseLocation(MouseEvent e)
	{
		mouseX = e.getX();
		mouseY = e.getY();	
		adjX = mouseX / 2.0 + ppWidth/4.0 - 2;
		adjY = mouseY / 2.0 + ppHeight/4.0 - 2;
		updatePlayPadDisplay();
	}
	
	public void updatePlayPadDisplay()
	{	
		gc.setFill(Color.LIGHTGREY);
		gc.fillRect(0, 0, ppWidth, ppHeight);

		gc.setFill(Color.RED);
		gc.fillRect(adjX, adjY, 4, 4);
	}
}

Now there might be a way to have motions outside of the center area be sent in to control the artificial cursor. A flag could indicate (when over the outer area) whether the cursor is exterior or controlling the interior. When leaving the inner area, the cursor stays invisible until attempting to move the artificial cursor out of the middle area. At this point, the state flips, and one could use a Robot to move the mouse back to the boundary and turn the real cursor back to visible. That could plausibly work.

But the opposite direction would be kind of problematic. When moving back into the center area, if the real cursor goes invisible at the border and artificial cursor kicks in. But it would be starting at a position further into the interior. That jump would be rather disorienting I would think. Arrghh–the scaling equation could be a function of the entrance point, I suppose. But that would complicate things when exiting the area.

But maybe it could be made to work. Am starting to get glimmerings. Not sure this isn’t becoming more complicated than trying to use JNA.

When posting code, please emphasize or even limit it to the relevant parts?

A simple Robot-based approach:

  • treat every mouse-moved event as a vector
  • if start or end position is inside the pit, multiply the vector by viscosity (0<x<1)
  • add the multiplied vector to the start position, then use that as the new cursor position
  • adjust any additional cursor properties according to the final position

This has at least two major drawbacks:

  • you will need a good reaction time, as the cursor position is managed by the OS and you might see a lot of twitching. A “virtual” cursor instead of Robot should help there, though.
  • it should, indeed, behave odd around the pit’s edges. Having a high framerate would help a bit, splitting the vector down into segments that are and aren’t inside the pit and transforming them accordingly would help better, though that involves a few extra steps.

Perhaps you don’t even need that much accuracy for the edges, would recommend to just try and see. I assume that working with mouse-enter and mouse-leave events instead of actual positions should be quite a pain here.

At any rate, expect to fiddle around quite a bit considering that nobody wanted you to be trying this in the first place, including the authors of [checks GUI lib being used] JavaFX.

Again, please note that “using JNA” is not an answer to your problem. It is an answer to the problem that there are no native Java libraries we knew of that do this. You would still have to find some C library that actually does it; unless you have one in mind, you are not actually one step closer to accomplishing this.

A couple of things.
First, I’m wondering if I have hit a bug in JavaFX. I’ve sent an inquiry to openjfx-dev@openjdk.java.net. [EDIT: email submission was rejected. IDK yet if this was maybe due to my having read-only status or if they deemed the content off topic.]

AFAIK, the code below should generate the console message “main mouse move” when the mouse remains within the main area but is beyond the far edges of the nested, overridden Canvas, but doesn’t. Not being able to properly trigger mouse events over regions of the screen is a roadblock.

I’ve done my best to make the code example for the error as small as possible.

public class MouseMoveTest extends Application
{		 
    public static void main(String[] args) 
    {
    	Application.launch(args);
    }
		 
    @Override
    public void start(Stage primaryStage)
    {
        Group root = new Group();  
        Scene scene = new Scene(root, 500, 200, Color.DARKGREY);        

        root.setOnMouseMoved(e -> System.out.println("main mouse move"));
        
        CenterArea centerArea = new CenterArea(300, 100);
        GridPane grid = new GridPane();
        grid.setPadding(new Insets(50, 0, 0, 100));            
        grid.add(centerArea, 0, 0);
                
        centerArea.setFocusTraversable(true);
        root.getChildren().add(grid);
        primaryStage.setScene(scene);
        primaryStage.show();        
    }
}

class CenterArea extends Canvas {
	private GraphicsContext gc;
	
	public CenterArea(double arg1, double arg2) 
	{
		super(arg1, arg2);
		gc = getGraphicsContext2D();
		gc.setFill(Color.RED);
		gc.fillRect(0, 0, arg1, arg2);
		
		setOnMouseMoved( e -> System.out.println("center mouse move"));
	}
}

If the vector approach is used, it seems to me that considerable infrastructure would need to be rebuild, as I am making extensive use of buttons, dropdowns and sliders which were designed to respond to the mouse events that would have to be intercepted and interpreted.

I don’t know why I thought using a Robot might “reset” the mouse location. For example, I had the idea that if my mouse was at 100, 100 and the Robot moved the mouse to 200, 100, further mouse moves would proceed from 200,100. But as far as the system is concerned, the physical mouse is still at 100, 100.

As far as JNA libraries, it will be a chore, but there are some leads. For Windows, we have the SystemsParametersInfo() function which has been referenced in this stackoverflow thread, and some additional avenues to investigate as provided in this inquiry on the JNA google group. The responder points to likely routines to call for MacOS as well as also pointing to the same Windows-related code.

EDIT: another possibility that occurs to me is to do something such as make a double click or action put one into a mode where the cursor only operates in the syrup pit. Then the transitions don’t have to be coded in and out. Will ponder more. Regardless, I think I’m going to put this refinement on hold a bit while working on other aspects.

Just another +1 here: please don’t ever do anything to the user’s actual mouse location. Think of the mouse pointer as the user’s finger, or eyeballs. Now imagine grabbing it/them.

Cas :slight_smile:

Interesting question!

I was wondering about this, because there are lots of games, which provide you with a mouse speed setting, and get it awfully wrong :D. So I was curious, if there are APIs to alter mouse speed and expected it to be in either the UI library (such as in JavaFX or GLFW, GTK, etc.) or a library specialized on input devices in general, such as JInput. And I found nothing like that, and then I realized this:

There is something called raw mouse movement (see e.g. GLFW). Raw mouse movement is apparently the mouse movement provided by the mouse driver, which depends on the DPI resolution of the actual mouse hardware. I guess there might be some mouse drivers which support to switch between different DPI settings, but the general case will be, that this is a fixed value.

The mouse motion you usually get in callback functions from your UI library, is already adjusted, based on the screen resolution and my best guess is, that the operating system / window manager does that for all applications/windows/screens. This is why UI libraries usually do not provide means of changing it.

I believe, changing the mouse speed is something you have to do in software. That said, there could still be a library, which does exactly that for you.

1 Like