Click here to Skip to main content
15,881,581 members
Articles / Programming Languages / C#

Mouse Smoothing

Rate me:
Please Sign up or sign in to vote.
4.20/5 (3 votes)
28 Nov 2021CPOL2 min read 26.1K   532   12   11
Mouse smoothing feature to your mouse
Explaining how to implement a mouse hook at kernel level

(You need Visual Studio and 2 mouses)

Introduction

(This code is just a layer implemented into another code: https://github.com/djpnewton/busdog).

This program adds the function of mouse smoothing to your mouse, if you don't know what is mouse smoothing, it's basically a delay between your mouse physical moves and the mouse coordinates (camera in game).

It's used to prevent jitter mouse movement thus add camera movement realism.

Mouse acceleration - PCGamingWiki

To use it:

  • First thing first, you need to install the USB sniffer driver to be able to read HID packet of the mouse
  • Download it to https://github.com/djpnewton/busdog#downloads 
  • Run the program and install the driver when it's asked
  • Grab devcon there https://docs.microsoft.com/en-us/windows-hardware/drivers/devtest/devcon then put it into the Release folder
  • Compile the code and run it
  • Select your mouse according to its VID/PID values (Identify a VID PID for a USB Device - InterWorks)
  • Image 1
  • (Smooth Value slider modify the variable MouseSmoothingValue/InterpSpeed)
  • Connect a second mouse to your PC (else Windows will not display mouse cursor), but don't use this 2nd mouse, use your main mouse connected to the VID/PID you chosen
  • Enjoy the smoothed mouse
  • To use it for a game, you need to disable Raw Mouse Input in the option of the game if the game allow it, else you cannot use it
  • To close the program, unselect the Device you selected earlier then just Alt-F4 the app (Alt+Tab can help)

Background

This program idea came from a find I made when I played Star Citizen, Mouse Smoothing.

Image 2

Using the Code

The mouse smoothing is based on the interpolation equation below:

C#
Point   InterpTo(Point Current, Point Target, float InterpSpeed)
{
    // 0.90 < InterpSpeed < 1

    var DeltaMove = new Point()
    {
        X = (Int32)((Target.X - Current.X) * InterpSpeed),
        Y = (Int32)((Target.Y - Current.Y) * InterpSpeed),
    };

    Target.X = Current.X + DeltaMove.X;
    Target.Y = Current.Y + DeltaMove.Y;

    return Target;
}

And here's the code where mouse driver simulation is implemented:

C#
void    RecievedFilterTraces(object sender, FilterTraceArrivedEventArgs e)
{
    Native.MouseOffset.X = 0;
    Native.MouseOffset.Y = 0;

    foreach (FilterTrace filterTrace in e.Traces.ToArray())
    {
        if (filterTrace.Buffer[0] != 0x80)
        {
            // X Coordinates
            if (filterTrace.Buffer[3] == 0x00)          // UP
            {
                Native.MouseOffset.X += filterTrace.Buffer[2];
            }
            else if (filterTrace.Buffer[3] == 0xFF)     // DOWN
            {
                Native.MouseOffset.X -= 0x100 - filterTrace.Buffer[2];
            }

            // Y Coordinates
            if (filterTrace.Buffer[5] == 0x00)          // UP
            {
                Native.MouseOffset.Y += filterTrace.Buffer[4];
            }
            else if (filterTrace.Buffer[5] == 0xFF)     // DOWN
            {
                Native.MouseOffset.Y -= 0x100 - filterTrace.Buffer[4];
            }

            // MOUSE_L
            if (GetBit(filterTrace.Buffer[0], 0) == 1)
            {
                if (MouseButton[0] == 0)
                {
                    WinAPI.mouse_event(MOUSEEVENTF_LEFTDOWN, Native.MouseOffset.X,
                                        Native.MouseOffset.Y, 0, 0);
                }

                MouseButton[0] = 1;
            }
            else if (MouseButton[0] == 1)
            {
                WinAPI.mouse_event(MOUSEEVENTF_LEFTUP, Native.MouseOffset.X,
                                    Native.MouseOffset.Y, 0, 0);

                MouseButton[0] = 0;
            }

            // MOUSE_R
            if (GetBit(filterTrace.Buffer[0], 1) == 1)
            {
                if (MouseButton[0] == 0)
                {
                    WinAPI.mouse_event(MOUSEEVENTF_RIGHTDOWN, Native.MouseOffset.X,
                                        Native.MouseOffset.Y, 0, 0);
                }

                MouseButton[1] = 1;
            }
            else if (MouseButton[1] == 1)
            {
                WinAPI.mouse_event(MOUSEEVENTF_RIGHTUP, Native.MouseOffset.X,
                                    Native.MouseOffset.Y, 0, 0);

                MouseButton[1] = 0;
            }

            // MOUSE_M
            if (GetBit(filterTrace.Buffer[0], 2) == 1)
            {
                if (MouseButton[0] == 0)
                {
                    WinAPI.mouse_event(MOUSEEVENTF_MIDDLEDOWN, Native.MouseOffset.X,
                                        Native.MouseOffset.Y, 0, 0);
                }

                MouseButton[2] = 1;
            }
            else if (MouseButton[2] == 1)
            {
                WinAPI.mouse_event(MOUSEEVENTF_MIDDLEUP, Native.MouseOffset.X,
                                    Native.MouseOffset.Y, 0, 0);

                MouseButton[2] = 0;
            }

            // MOUSE_B
            if (GetBit(filterTrace.Buffer[0], 3) == 1)
            {
                if (MouseButton[0] == 0)
                {
                    WinAPI.mouse_event(MOUSEEVENTF_XDOWN, Native.MouseOffset.X,
                                        Native.MouseOffset.Y, 0x0001, 0);
                }

                MouseButton[3] = 1;
            }
            else if (MouseButton[3] == 1)
            {
                WinAPI.mouse_event(MOUSEEVENTF_XUP, Native.MouseOffset.X,
                                    Native.MouseOffset.Y, 0x0001, 0);

                MouseButton[3] = 0;
            }

            // MOUSE_F
            if (GetBit(filterTrace.Buffer[0], 4) == 1)
            {
                if (MouseButton[0] == 0)
                {
                    WinAPI.mouse_event(MOUSEEVENTF_XDOWN, Native.MouseOffset.X,
                                        Native.MouseOffset.Y, 0x0002, 0);
                }

                MouseButton[4] = 1;
            }
            else if (MouseButton[4] == 1)
            {
                WinAPI.mouse_event(MOUSEEVENTF_XUP, Native.MouseOffset.X,
                                    Native.MouseOffset.Y, 0x0002, 0);

                MouseButton[4] = 0;
            }

            // Wheel
            if (filterTrace.Buffer[6] == 0x01)          // UP
            {
                WinAPI.mouse_event(MOUSEEVENTF_WHEEL, 0, 0, 120, 0);
            }
            else if (filterTrace.Buffer[6] == 0xFF)     // DOWN
            {
                WinAPI.mouse_event(MOUSEEVENTF_WHEEL, 0, 0, -120, 0);
            }
        }
    }

    Byte GetBit(Byte b, Int32 bitNumber)
    {
        BitArray ba = new BitArray(new Byte[] { b });

        return Convert.ToByte(ba.Get(bitNumber));
    }
}

filterTrace.Buffer contains the HID packets of the mouse and the if() chain is used to parse the packets and do actions according to what I receive.

The rest of the code is just the code of BusDog that I didn't write, so I cannot explain well to you how it works.

But here's the main code of that program:

C#
void    TraceBufRead()
{
    var devIO = new DeviceIOCTL(busdogPath, true);

    while (true)
    {
        UInt32 bytesReturned;
                  
        // send the get trace buffer command to the driver
        devIO.DeviceIoControl(IOCTL_BUSDOG_GET_BUFFER, IntPtr.Zero, 0,
                              outBuffer, outBufferSize, out bytesReturned);

        if (MouseOffset == MousePitchYaw)
        {
            while (true)
            {
                // keep checking if the I/O request has been fufilled (abort after 500ms so our thread can be killed)
                if (devIO.WaitForOverlappedIo(500, out bytesReturned))
                {
                    break;
                }
            }
        }

        // we have a result so now we convert the buffer into a trace list and call the event
        FilterTraceArrived(this, new FilterTraceArrivedEventArgs(GetTraceList(outBuffer, outBufferSize, bytesReturned)));

        MousePitchYaw   = InterpTo(MouseOffset, MousePitchYaw, MouseSmoothingValue);

        Cursor.Position = new Point(MousePitchYaw.X + Cursor.Position.X,
                                    MousePitchYaw.Y + Cursor.Position.Y);

        // stall trace stream to allow main thread to have a turn now and then
        Thread.Sleep(10);
    }
}

FilterTraceArrived function is where the loop gather mouse coordinate through HID packets.

if (MouseOffset == MousePitchYaw) line is where the loop waits for mouse movement.

Points of Interest

Mathematic equations are interesting if we can found an application

Conclusion

Mouse smoothing adds a new perspective when playing video games, a new way to play.

History

  • 30th September, 2019: Initial version
  • 29th November, 2021: Update, made the tuto easier and the code as well

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
France France
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionMouse Smoothing Pin
Mason Winkler27-Jul-23 23:53
Mason Winkler27-Jul-23 23:53 
AnswerRe: Mouse Smoothing Pin
Orphraie10-Sep-23 0:10
Orphraie10-Sep-23 0:10 
QuestionAll mice get disabled Pin
Member 1578070527-Sep-22 9:04
Member 1578070527-Sep-22 9:04 
AnswerRe: All mice get disabled Pin
Orphraie27-Sep-22 22:51
Orphraie27-Sep-22 22:51 
QuestionVery Interesting, Indeed! Pin
Hyland Computer Systems30-Nov-21 3:53
Hyland Computer Systems30-Nov-21 3:53 
QuestionLocation of the demo download does not work... Pin
Member 1544971229-Nov-21 22:05
Member 1544971229-Nov-21 22:05 
QuestionMaybe an installing video? Pin
El ReKi14-Apr-21 21:33
El ReKi14-Apr-21 21:33 
AnswerRe: Maybe an installing video? Pin
Orphraie16-Apr-21 1:57
Orphraie16-Apr-21 1:57 
GeneralRe: Maybe an installing video? Pin
El ReKi17-Apr-21 21:13
El ReKi17-Apr-21 21:13 
QuestionI like the mouse interception code, but.. Pin
adudley25626-Jul-20 21:59
adudley25626-Jul-20 21:59 
Like the code, didn't know you could get the bytes of the mouse moving like that so thanks. I'm just using on screen mouse related things (not for games, for Repetitive strain injury on windows apps). So don't need that level of control yet, but using just the mouse move etc. means that Windows blocks some things, like clicking on UAC buttons.

A video of what smotthing vs non-smothing would have been great, as I really don't know what you mean!
AnswerRe: I like the mouse interception code, but.. Pin
Orphraie27-Jul-20 1:32
Orphraie27-Jul-20 1:32 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.