Click here to Skip to main content
14,333,745 members

Mouse Smoothing

Rate this:
5.00 (1 vote)
Please Sign up or sign in to vote.
5.00 (1 vote)
3 Oct 2019CPOL
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.

Image 1

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:

EndPoint = StartPoint + ((EndPoint - StarPoint) * InterpSpeed))
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:

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:

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

 
-- There are no messages in this forum --
Article
Posted 1 Oct 2019

Tagged as

Stats

2.6K views
69 downloads
4 bookmarked