Click here to Skip to main content
15,038,451 members
Articles / Programming Languages / C#
Article
Posted 23 Jul 2020

Tagged as

Stats

13.2K views
300 downloads
8 bookmarked

Mouse Smoothing

Rate me:
Please Sign up or sign in to vote.
5.00/5 (2 votes)
23 Jul 2020CPOL3 min read
Mouse smoothing feature to your mouse

(You need Visual Studio and 2 mouses.)

Edit

Finally, I found a way to make this tool work with any video games that support the disable of Raw Mouse Input, like Call of Duty games.

I just needed to smooth the mouse offset directly draw from the HID packets and not smooth the screen coordinate made with the mouse offset.

Introduction

(This program is just a code I wrote, implemented into another program: 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 and camera realism.

And gamers (me) who wish to play games with it. :)

To use it:

  • First thing first, you need to install the USB sniffer driver to be able to read HID packet of the mouse.
  • Go to https://github.com/djpnewton/busdog#downloads and select the Windows bit version.
  • Run the program and install the driver when it's asked.

(Disclaimer: You can have all your USB devices down by installing it and cannot have them working back, so use it at your own risk.)

  • Comment line 48 of GUI.cs (Process.Start("devcon.exe", ...)
  • Compile, run the program and check your mouse device, (first check case) knows with the VID/PID of your mouse, below is a tutorial:
    https://honeywellaidc.force.com/supportppr/s/article/How-to-find-out-the-VID-and-PID-for-a-USB-device-on-Windows-7
  • Close the program and uncomment the previous line commented
  • Go to Device Manager, find your mouse, go to Properties > Events > Information
  • And copy your Mouse name that begins with:
    HID\VID_
  • Then replace the line section (#48) below by the name you picked:
    \"@HID\\VID_046D&PID_C246&MI_00\\7&344D0BE0&0&0000\"
  • Download https://docs.microsoft.com/en-us/windows-hardware/drivers/devtest/devcon and put it to the bin/Release folder
  • Compile and run
  • 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 smooth :)
  • To use it ingame, you need to disable Raw Mouse Input of the game
  • To close the program, you can Alt-F4 or just close the program (Alt+Tab for).

Background

This program idea came from the discovery I made when I played Star Citizen, Mouse Smoothing.

And since that, I love this idea of camera smoothing in game.

Lately, Call of Duty: Modern Warfare has introduced it, not as adjustable as SC, but it's a good step forward.

Using the Code

This program needs to desync cursor movement from the mouse physical moves.

And for that, I used devcon executable from WDK, very useful tool to manage the hardware connected to the PC.

It also needs the use of a kernel hooker: https://github.com/djpnewton/busdog to read HID packets from the mouse and simulate a mouse driver with a program (this).

The mouse smoothing is based on the interpolation equation below:

C#
EndPoint = StartPoint + ((EndPoint - StarPoint) * InterpSpeed))
C#
Point InterpTo(Point CurrentLocation, float InterpSpeed)
{
    // 0.90 < InterpSpeed < 1

    Delta.X = TargetLocation.X - CurrentLocation.X;
    Delta.Y = TargetLocation.Y - CurrentLocation.Y;

    DeltaMove.X = (int)(Delta.X * InterpSpeed);
    DeltaMove.Y = (int)(Delta.Y * InterpSpeed);

    TargetLocation.X = (CurrentLocation.X + DeltaMove.X);
    TargetLocation.Y = (CurrentLocation.Y + DeltaMove.Y);

    return TargetLocation;
}

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

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

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

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

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

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

                    MouseButton[0] = 0;
                }

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

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

                    MouseButton[1] = 0;
                }

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

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

                    MouseButton[2] = 0;
                }

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

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

                    MouseButton[3] = 0;
                }

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

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

                    MouseButton[4] = 0;
                }

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

    }
}

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()
{
    DeviceIOCTL devIO = new DeviceIOCTL(busdogPath, true);

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

        if (CurrentLocation == TargetLocation)
        {
            while (true)
            {
                // keep checking if the I/O request has been fulfilled 
                // (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)));

        Start = InterpTo(CurrentLocation, 0.971546f);

        int X = Cursor.Position.X;
        int Y = Cursor.Position.Y;

        Start.X += X;
        Start.Y += Y;

        Cursor.Position = Start;

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

FilterTraceArrived line is where the loop gather mouse coordinate through HID packet.

if (CurrentLocation == TargetLocation) line is where the loop waits for mouse physics action.

Points of Interest

Mathematic equations are fun, but only if we 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

License

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

Share

About the Author

wqaxs36
France France
No Biography provided

Comments and Discussions

 
QuestionMaybe an installing video? Pin
El ReKi14-Apr-21 21:33
MemberEl ReKi14-Apr-21 21:33 
AnswerRe: Maybe an installing video? Pin
wqaxs3616-Apr-21 1:57
Memberwqaxs3616-Apr-21 1:57 
GeneralRe: Maybe an installing video? Pin
El ReKi17-Apr-21 21:13
MemberEl ReKi17-Apr-21 21:13 
GeneralRe: Maybe an installing video? Pin
wqaxs3621-May-21 7:01
Memberwqaxs3621-May-21 7:01 
QuestionI like the mouse interception code, but.. Pin
adudley25626-Jul-20 21:59
Memberadudley25626-Jul-20 21:59 
AnswerRe: I like the mouse interception code, but.. Pin
wqaxs3627-Jul-20 1:32
Memberwqaxs3627-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.