65.9K
CodeProject is changing. Read more.
Home

Mouse Smoothing

starIconstarIconstarIconstarIconstarIcon

5.00/5 (2 votes)

Jul 23, 2020

CPOL

2 min read

viewsIcon

29257

downloadIcon

580

Mouse smoothing feature to your mouse

(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)
  • (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.

Using the Code

The mouse smoothing is based on the interpolation equation below:

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:

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:

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