MouseMux V2 SDK Manual

Share

Latest revision: January 2025

Welcome to the SDK documentation. The SDK is available for the industrial MouseMux editions - other versions have a 15 minute window for SDK functionality so you can test if the SDK/API is suitable for your needs. The SDK is enabled from versions 2.1.50 and upwards.

Enabling the SDK

To enable the API in MouseMux, open the main configuration file name mousemux-v2-main.json located in:

"c:\Users\USER_NAME\Documents\MouseMux V2\User\Config\"

There you will need to add the option "config.api.enable" to the "flags" list, see below for an example:

  "config": {
    "flags": [
      "config.mode.switched",
      "config.users.load",
      "config.users.save",
      "config.enable.api"
    ]
  },

Once you have enabled this, and start MouseMux, you can verify in the mousemux-main-* log the API is enabled.

The log is in

c:\Users\USER_NAME\Documents\MouseMux V2\User\Logs\

See the image below for what you can expect to see:

image

The SDK allows you to both capture input data ('getter') and also communicate with the MouseMux program ('setter').

Before you can capture from MouseMux you must first create message identifiers, you do this by calling the win32 API function with the call RegisterWindowMessage() which you feed the following list.

The following are the 'getter' functions - they are:

"mousemux.api.get.none"
"mousemux.api.get.user.create"
"mousemux.api.get.user.dispose"
"mousemux.api.get.user.list"
"mousemux.api.get.user.motion"
"mousemux.api.get.user.button"
"mousemux.api.get.user.v.wheel"
"mousemux.api.get.user.h.wheel"
"mousemux.api.get.user.keyboard"

For each of these these strings, RegisterWindowMessage() will return a value, and you can use that value to understand what MouseMux is sending you. The values created are the same as MouseMux generates, which you can see in the log (see above). Note that these values change every time so you cannot store them but have to call RegisterWindowMessage() each time you run your program.

Once you have registred these functions you must create a window with the name of:

"mousemux-api-window-getter"

You can also register the class with the same name, although this is not obligatory. Once you have registered this function then MouseMux will start sending data for each user for creation, disposing, motion, buttons, wheel and keyboard events.

So, whenever MouseMux gets pointer input it will post a message to your window using the identifier gotten from RegisterWindowMessage(). Let's assume RegisterWindowMessage("mousemux.api.get.user.motion") returned 1234 during your program startup then whenever you get a message 1234 in your window procedure you know you received a mouse move.

Here's a small example of how this would work:

enum {

  MESSAGE_CODE_MIN          = -1 ,

  /**/

  MESSAGE_CODE_NONE         =  0 ,

  MESSAGE_CODE_USER_CREATE       ,
  MESSAGE_CODE_USER_DISPOSE      ,
  MESSAGE_CODE_USER_LIST         ,
  MESSAGE_CODE_USER_MOTION       ,
  MESSAGE_CODE_USER_BUTTON       ,
  MESSAGE_CODE_USER_V_WHEEL      ,
  MESSAGE_CODE_USER_H_WHEEL      ,
  MESSAGE_CODE_USER_KEY          ,

  /**/

  MESSAGE_CODE_MAX

};

static struct {

  const char * name ;
  const int    code ;

  /* filled in with RegisterWindowMessage() */

  int message ;

} list[] = {

  { "mousemux.api.get.none"          , MESSAGE_CODE_NONE          , -1 },

  { "mousemux.api.get.user.create"   , MESSAGE_CODE_USER_CREATE   , -1 },
  { "mousemux.api.get.user.dispose"  , MESSAGE_CODE_USER_DISPOSE  , -1 },
  { "mousemux.api.get.user.list"     , MESSAGE_CODE_USER_LIST     , -1 },
  { "mousemux.api.get.user.motion"   , MESSAGE_CODE_USER_MOTION   , -1 },
  { "mousemux.api.get.user.button"   , MESSAGE_CODE_USER_BUTTON   , -1 },
  { "mousemux.api.get.user.v.wheel"  , MESSAGE_CODE_USER_V_WHEEL  , -1 },
  { "mousemux.api.get.user.h.wheel"  , MESSAGE_CODE_USER_H_WHEEL  , -1 },
  { "mousemux.api.get.user.keyboard" , MESSAGE_CODE_USER_KEY      , -1 },

  /**/

  { NULL }

};

  /* Register the messages */

  int i;

  for(i = 0; NULL != list[i].name; i++)
  {
    if(0 == (list[i].message = RegisterWindowMessage(list[i].name)))
    {
      assert(0);
    }
  }


  /* Your WndProc */

  LRESULT CALLBACK MainWndProc(HWND hwnd, UINT uMsg, WPARAM wParam,LPARAM lParam)
  {
    switch (uMsg)
    {
        case WM_CREATE:
            /* Initialize the window. */
            return 0;

        case WM_PAINT:
            /* Paint the window's client area. */
            return 0;

        case WM_SIZE:
            /* Set the size and position of the window. */
            return 0;

        case WM_DESTROY:
            /* Clean up window-specific data objects. */
            return 0;

        /* Process other messages. */

        default: if(1)
                 {
                   int i;

                   for(i = 0; NULL != list[i].name; i++)
                   {
                     if(uMsg == list[i].message)
                     {
                       /* handle the MouseMux message */

                       return 0;
                     }
                   }
                  }

                  return DefWindowProc(hwnd, uMsg, wParam, lParam);
    }

    return 0;
  }

The WPARAM and LPARAM parameters in your message contain additional information.

For the following calls:

"mousemux.api.get.user.create"
"mousemux.api.get.user.dispose"
"mousemux.api.get.user.list"

To obtain the hardware ID (HWID). Use the LOWORD in WPARAM, that contains the hashed 16 bit HWID, the HIWORD in WPARAM contains the user id and the LPARAM contains the full 32 bit HWID.


For the following calls:

"mousemux.api.get.user.motion"
"mousemux.api.get.user.button"
"mousemux.api.get.user.v.wheel"
"mousemux.api.get.user.h.wheel"
"mousemux.api.get.user.keyboard"

The LOWORD in WPARAM contains the hashed HWID and the HIWORD contains:

  • The cursor hit code for a motion event
  • The button (bit) flag for a button event
  • The wheel delta for vertical wheel event
  • The wheel delta for horizontal wheel event

And for each of these calls the LPARAM contains the coordinates, you can retrieve those by using the window calls:

int xPos = GET_X_LPARAM(lParam);
int yPos = GET_Y_LPARAM(lParam);

The button flags are defined as follows:

  enum {
    DATA_BUTTON_FLAG_1_DN  = 1 << 0  , /* left */
    DATA_BUTTON_FLAG_1_UP  = 1 << 1  ,
    DATA_BUTTON_FLAG_2_DN  = 1 << 2  , /* right */
    DATA_BUTTON_FLAG_2_UP  = 1 << 3  ,
    DATA_BUTTON_FLAG_3_DN  = 1 << 4  , /* middle */
    DATA_BUTTON_FLAG_3_UP  = 1 << 5  ,
    DATA_BUTTON_FLAG_4_DN  = 1 << 6  , /* X1 */
    DATA_BUTTON_FLAG_4_UP  = 1 << 7  ,
    DATA_BUTTON_FLAG_5_DN  = 1 << 8  , /* X2 */
    DATA_BUTTON_FLAG_5_UP  = 1 << 9  ,
  }

Calling functions in the SDK

Just as with getters you will need to use RegisterWindowMessage() to map these calls to window messages. Once you have done that you can use the FindWindow() function to find a window called:

mousemux-api-window-setter

Both the class as the window itself have this name. So for example in C:

const char * name = "mousemux-api-window-setter" ;

HWND hwnd = FindWindow(name, name);

You can cache the hwnd as it will remain the same during the runtime of MouseMux. To call a function to use the PostMessage() call.

for example:

void setter_send(void)
{
  const char * name = "mousemux-api-window-setter";

  HWND hwnd = FindWindow(name, name);

  if(NULL != hwnd)
  {
    int message ;

    printf("found %s window at %p\n", name, hwnd);

    if(0 == (message = RegisterWindowMessage("mousemux.api.set.window.refresh")))
    {
    }

    PostMessage(hwnd, message, 0, 0);
  }
}

So you will be using PostMessage to send the message MouseMux which will in turn run the function. You can pass a WPARAM and LPARAM in PostMessage() which are used as arguments in the calls in MouseMux.

SDK API calls for the application itself

Here you will find each API call explained.

API func "mousemux.api.set.app.hwnd.finder"

Refresh the SDK listening window in MouseMux. This will tell MouseMux to look for the "mousemux-api-window-getter" window again. You may need this if your application exited and you want to have MouseMux send you data again.

  • WPARAM :
    • 0 : Reserved
  • LPARAM :
    • 0 : Reserved
API func "mousemux.api.set.app.mode.change"

Change MouseMux's mode of operation.

  • WPARAM :
    • 1 : Native Mode
    • 2 : Switched Mode
    • 3 : Multiplex Mode
  • LPARAM :
    • 0 : Reserved
API func "mousemux.api.set.app.hide"

Hide MouseMux's UI (minimize).

  • WPARAM :
    • 0 : Reserved
  • LPARAM :
    • 0 : Reserved
API func "mousemux.api.set.app.user.list"

List all current users. This will iterate the internal user list and for each user send a "mousemux.api.get.user.list" call.

  • WPARAM :
    • 0 : Reserved
  • LPARAM :
    • 0 : Reserved

SDK API calls for user objects


API func "mousemux.api.set.user.cursor.hide"

Hide or show the cursor of a user

  • WPARAM : HWID for this user (Make sure to not use the hashed HWID, but the actual HWID)
  • LPARAM : 1 for hiding, 0 to show again
API func "mousemux.api.set.user.cursor.type"

Change the type of the cursor of a user

  • WPARAM : HWID for this user (Make sure to not use the hashed HWID, but the actual HWID)
  • LPARAM : cursor type value, which can be:
  enum {
    CURSOR_TYPE_ARROW    =  0  ,
    CURSOR_TYPE_HELP     =  1  ,
    CURSOR_TYPE_CROSS    =  2  ,
    CURSOR_TYPE_TEXT     =  3  ,
    CURSOR_TYPE_LINK     =  4  ,
    CURSOR_TYPE_WORKING  =  5  ,
    CURSOR_TYPE_WAIT     =  6  ,
    CURSOR_TYPE_PEN      =  7  ,
    CURSOR_TYPE_UP       =  8  ,
    CURSOR_TYPE_NO       =  9  ,
    CURSOR_TYPE_MOVE     =  10 ,
    CURSOR_TYPE_SIZE_45  =  11 ,
    CURSOR_TYPE_SIZE_90  =  12 ,
    CURSOR_TYPE_SIZE_115 =  13 ,
    CURSOR_TYPE_SIZE_180 =  14
  }

Note that not all cursor themes may contain all the different types.

API func "mousemux.api.set.user.cursor.flag"

Change the flag of the cursor of a user

  • WPARAM : HWID for this user (Make sure to not use the hashed HWID, but the actual HWID)
  • LPARAM : cursor flag value (or combination of), which can be:
  enum {
    CURSOR_FLAG_NONE          = 0       ,

    CURSOR_FLAG_FLIP_X        = 1 << 1  ,
    CURSOR_FLAG_FLIP_Y        = 1 << 2  ,

    CURSOR_FLAG_TAG_BOX_A     = 1 << 3  ,
    CURSOR_FLAG_TAG_BOX_B     = 1 << 4  ,
    CURSOR_FLAG_TAG_BOX_C     = 1 << 5  ,
    CURSOR_FLAG_TAG_BOX_D     = 1 << 6  ,

    CURSOR_FLAG_TAG_LINE_A    = 1 << 7  ,
    CURSOR_FLAG_TAG_LINE_B    = 1 << 8  ,
    CURSOR_FLAG_TAG_LINE_C    = 1 << 9  ,
    CURSOR_FLAG_TAG_LINE_D    = 1 << 10 ,

    CURSOR_FLAG_TAG_CIRCLE_A  = 1 << 11 ,
    CURSOR_FLAG_TAG_CIRCLE_B  = 1 << 12 ,
    CURSOR_FLAG_TAG_CIRCLE_C  = 1 << 13 ,
    CURSOR_FLAG_TAG_CIRCLE_D  = 1 << 14 ,

    CURSOR_FLAG_TAG_NONE      = 1 << 20
  }
API func "mousemux.api.set.user.cursor.size"

Change the size of the cursor of a user

  • WPARAM : HWID for this user (Make sure to not use the hashed HWID, but the actual HWID)
  • LPARAM : cursor size value, which can be: 16, 24, 32, 48, 64, 72, 80, 96, 128 or 256
API func "mousemux.api.set.user.cursor.theme"

Change the theme of the cursor of a user

  • WPARAM : HWID for this user (Make sure to not use the hashed HWID, but the actual HWID)
  • LPARAM : cursor theme value, anything from 0 to 255

The theme can be located either in the standard location in Program files or in the MouseMux V2 documents folder (c:\Users\USER_NAME\Documents\MouseMux V2\User\mouse\cursor\themes\*)

You can add custom folders here with the following naming format:

  custom-0
  custom-1
  ...
  custom-100

The numbers in the name correspond to the value you pass.

API func "mousemux.api.set.user.cursor.move.lock"

Lock or unlock the cursor of a user in the current position - the user will still be able to move, yet the cursor is motionless

  • WPARAM : HWID for this user (Make sure to not use the hashed HWID, but the actual HWID)
  • LPARAM : 1 for locking, 0 to unlock
API func "mousemux.api.set.user.cursor.type.lock"

Lock or unlock the cursor type - this will only lock cursor type changes by the system, you can still manually override the type

  • WPARAM : HWID for this user (Make sure to not use the hashed HWID, but the actual HWID)
  • LPARAM : 1 for locking, 0 to unlock
API func "mousemux.api.set.user.create"

Create a (virtual) user (with attached pointer and keyboard)

  • WPARAM : HWID for the pointer
  • LPARAM : HWID for the keyboard
API func "mousemux.api.set.user.dispose"

Dispose a (virtual) user (including removing attached pointer and keyboard)

  • WPARAM : HWID for the pointer
  • LPARAM : HWID for the keyboard
API func "mousemux.api.set.user.pointer.motion"

Move the pointer (cursor) for a user to new coordinates

  • WPARAM : HWID for this user (Make sure to not use the hashed HWID, but the actual HWID)
  • LPARAM : Use the MAKELPARAM(X, Y) windows function with the X and Y (absolute) coordinates
API func "mousemux.api.set.user.pointer.button"

Click the mouse button for a user

  • WPARAM : HWID for this user (Make sure to not use the hashed HWID, but the actual HWID)
  • LPARAM : Use the button flags mentioned above in this document
API func "mousemux.api.set.user.pointer.direct"

Pass a custom event for this pointer

  • WPARAM : HWID for this user (Make sure to not use the hashed HWID, but the actual HWID)
  • LPARAM : Custom event code

Code examples

Click to expand a C# example

using System;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Windows.Forms;

public class MouseMuxSDK : Form
{
    [DllImport("user32.dll")]
    public static extern int RegisterWindowMessage(string lpString);

    [DllImport("user32.dll")]
    public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);

    [DllImport("user32.dll")]
    public static extern bool PostMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);

    private const int LIST_SIZE = 64;
    private int HWID = -1;
    private struct HWIDInfo
    {
        public int hash;
        public int hwid;
    }
    private HWIDInfo[] hlist = new HWIDInfo[LIST_SIZE];

    private Label hwidLabel;
    private Bitmap drawingBitmap;
    private Point? lastPoint = null;

    // Button flags as per SDK documentation
    [Flags]
    public enum DataButtonFlag
    {
        DATA_BUTTON_FLAG_1_DN = 1 << 0,  // left down
        DATA_BUTTON_FLAG_1_UP = 1 << 1,  // left up
        DATA_BUTTON_FLAG_2_DN = 1 << 2,  // right down
        DATA_BUTTON_FLAG_2_UP = 1 << 3,  // right up
        DATA_BUTTON_FLAG_3_DN = 1 << 4,  // middle down
        DATA_BUTTON_FLAG_3_UP = 1 << 5,  // middle up
        DATA_BUTTON_FLAG_4_DN = 1 << 6,  // X1 down
        DATA_BUTTON_FLAG_4_UP = 1 << 7,  // X1 up
        DATA_BUTTON_FLAG_5_DN = 1 << 8,  // X2 down
        DATA_BUTTON_FLAG_5_UP = 1 << 9   // X2 up
    }

    private static string[] messageNames = {
        "mousemux.api.get.none",
        "mousemux.api.get.user.create",
        "mousemux.api.get.user.dispose",
        "mousemux.api.get.user.list",
        "mousemux.api.get.user.motion",
        "mousemux.api.get.user.button",
        "mousemux.api.get.user.v.wheel",
        "mousemux.api.get.user.h.wheel",
        "mousemux.api.get.user.keyboard"
    };

    private static string[] setterMessageNames = {
        "mousemux.api.set.app.user.list"
    };

    private static int[] messageCodes = null;
    private static int[] setterMessageCodes = null;
    private const string SETTER_WINDOW_NAME = "mousemux-api-window-setter";

    public MouseMuxSDK()
    {
        this.Text = "mousemux-api-window-getter";
        this.Width = 800;
        this.Height = 600;
        this.DoubleBuffered = true;

        hwidLabel = new Label
        {
            Text = "HWID: -1",
            Dock = DockStyle.Top,
            Font = new Font("Consolas", 12, FontStyle.Bold),
            Height = 30
        };
        this.Controls.Add(hwidLabel);

        drawingBitmap = new Bitmap(this.ClientSize.Width, this.ClientSize.Height);
        this.Paint += (s, e) => e.Graphics.DrawImage(drawingBitmap, 0, 0);
        this.Resize += (s, e) =>
        {
            var newBitmap = new Bitmap(this.ClientSize.Width, this.ClientSize.Height);
            using (var g = Graphics.FromImage(newBitmap))
            {
                g.DrawImage(drawingBitmap, 0, 0);
            }
            drawingBitmap = newBitmap;
            this.Invalidate();
        };

        RegisterMessages();
        RequestUserList();
    }

    private void RegisterMessages()
    {
        messageCodes = new int[messageNames.Length];
        for (int i = 0; i < messageNames.Length; i++)
        {
            messageCodes[i] = RegisterWindowMessage(messageNames[i]);
            Console.WriteLine($"Registered getter {messageNames[i]}: {messageCodes[i]}");
        }

        setterMessageCodes = new int[setterMessageNames.Length];
        for (int i = 0; i < setterMessageNames.Length; i++)
        {
            setterMessageCodes[i] = RegisterWindowMessage(setterMessageNames[i]);
            Console.WriteLine($"Registered setter {setterMessageNames[i]}: {setterMessageCodes[i]}");
        }
    }

    private void RequestUserList()
    {
        IntPtr setterHwnd = FindWindow(SETTER_WINDOW_NAME, SETTER_WINDOW_NAME);
        if (setterHwnd != IntPtr.Zero)
        {
            int listMessage = setterMessageCodes[0]; // mousemux.api.set.app.user.list
            Console.WriteLine("Requesting user list from MouseMux...");
            PostMessage(setterHwnd, (uint)listMessage, IntPtr.Zero, IntPtr.Zero);
        }
        else
        {
            Console.WriteLine("Could not find MouseMux setter window");
        }
    }

    // Utility functions for parameter extraction
    private int ExtractHash(IntPtr wParam) => (int)(wParam.ToInt64() & 0xFFFF); // LOWORD
    private int ExtractUid(IntPtr wParam) => (int)((wParam.ToInt64() >> 16) & 0xFFFF); // HIWORD
    private int ExtractHwid(IntPtr lParam) => (int)lParam.ToInt64(); // full 32-bit HWID

    private (int x, int y) ExtractCoordinates(IntPtr lParam)
    {
        // Implement GET_X_LPARAM and GET_Y_LPARAM macro logic
        int x = (int)(short)(lParam.ToInt64() & 0xFFFF);        // GET_X_LPARAM
        int y = (int)(short)((lParam.ToInt64() >> 16) & 0xFFFF); // GET_Y_LPARAM
        return (x, y);
    }

    // Message handlers
    private void HandleUserCreate(string name, Message m)
    {
        int hash = ExtractHash(m.WParam); // hashed 16-bit HWID
        int uid = ExtractUid(m.WParam);   // user id
        int hwid = ExtractHwid(m.LParam); // full 32-bit HWID
        Console.WriteLine($"Call '{name}' hash:{hash:X} uid:{uid} hwid:{hwid:X}");
    }

    private void HandleUserDispose(string name, Message m)
    {
        int hash = ExtractHash(m.WParam);
        int uid = ExtractUid(m.WParam);
        int hwid = ExtractHwid(m.LParam);
        Console.WriteLine($"Call '{name}' hash:{hash:X} uid:{uid} hwid:{hwid:X}");
    }

    private void HandleUserList(string name, Message m)
    {
        int hash = ExtractHash(m.WParam);
        int uid = ExtractUid(m.WParam);
        int hwid = ExtractHwid(m.LParam);
        hlist[uid].hash = hash;
        hlist[uid].hwid = hwid;
        HWID = hwid;
        UpdateHwidLabel();
        Console.WriteLine($"Call '{name}' hash:{hash:X} uid:{uid} hwid:{hwid:X}");
    }

    private void HandleUserMotion(string name, Message m)
    {
        int hash = ExtractHash(m.WParam); // hashed HWID
        int hit = ExtractUid(m.WParam);   // cursor hit code
        var (x, y) = ExtractCoordinates(m.LParam);
        if (HWID != -1)
        {
            for (int i = 0; i < LIST_SIZE; i++)
            {
                if (hash == hlist[i].hash)
                {
                    HWID = hlist[i].hwid;
                    UpdateHwidLabel();
                    break;
                }
            }
        }
        DrawLine(x, y);
        Console.WriteLine($"Call '{name}' hash:{hash:X} hit:{hit} pos:({x},{y})");
    }

    private void HandleUserButton(string name, Message m)
    {
        int hash = ExtractHash(m.WParam); // hashed HWID
        int flag = ExtractUid(m.WParam);  // button (bit) flag
        var (x, y) = ExtractCoordinates(m.LParam);
        // Use the DataButtonFlag enum for clarity
        var buttonFlags = (DataButtonFlag)flag;
        // Example: reset drawing on left button up
        if (buttonFlags.HasFlag(DataButtonFlag.DATA_BUTTON_FLAG_1_UP))
            lastPoint = null;
        Console.WriteLine($"Call '{name}' hash:{hash:X} button:{buttonFlags} pos:({x},{y})");
    }

    private void HandleUserVWheel(string name, Message m)
    {
        int hash = ExtractHash(m.WParam);
        int vWheel = ExtractUid(m.WParam); // wheel delta
        var (x, y) = ExtractCoordinates(m.LParam);
        Console.WriteLine($"Call '{name}' hash:{hash:X} v.wheel:{vWheel} pos:({x},{y})");
    }

    private void HandleUserHWheel(string name, Message m)
    {
        int hash = ExtractHash(m.WParam);
        int hWheel = ExtractUid(m.WParam); // wheel delta
        var (x, y) = ExtractCoordinates(m.LParam);
        Console.WriteLine($"Call '{name}' hash:{hash:X} h.wheel:{hWheel} pos:({x},{y})");
    }

    private void HandleUserKeyboard(string name, Message m)
    {
        int hash = ExtractHash(m.WParam);
        int l = ExtractUid(m.WParam);
        Console.WriteLine($"Call '{name}' hash:{hash:X} L:{l:X}");
    }

    private void DrawLine(int x, int y)
    {
        if (lastPoint == null)
        {
            lastPoint = new Point(x, y);
            return;
        }
        using (var g = Graphics.FromImage(drawingBitmap))
        {
            g.DrawLine(Pens.Blue, lastPoint.Value, new Point(x, y));
        }
        lastPoint = new Point(x, y);
        this.Invalidate();
    }

    private void UpdateHwidLabel()
    {
        hwidLabel.Text = $"HWID: {HWID}";
    }

    protected override void WndProc(ref Message m)
    {
        if (messageCodes == null)
            RegisterMessages();
        for (int i = 0; i < messageCodes.Length; i++)
        {
            if (m.Msg == messageCodes[i])
            {
                switch (i)
                {
                    case 1: HandleUserCreate(messageNames[i], m); break;
                    case 2: HandleUserDispose(messageNames[i], m); break;
                    case 3: HandleUserList(messageNames[i], m); break;
                    case 4: HandleUserMotion(messageNames[i], m); break;
                    case 5: HandleUserButton(messageNames[i], m); break;
                    case 6: HandleUserVWheel(messageNames[i], m); break;
                    case 7: HandleUserHWheel(messageNames[i], m); break;
                    case 8: HandleUserKeyboard(messageNames[i], m); break;
                    default: break;
                }
                return;
            }
        }
        base.WndProc(ref m);
    }

    public static void RunSDK()
    {
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        Application.Run(new MouseMuxSDK());
    }
}

Click to expand a Python example

"""
MouseMux SDK Python Example
---------------------------

This example demonstrates how to use the MouseMux SDK to monitor mouse events in real-time.
It creates a window that receives messages from MouseMux and displays:
- Mouse coordinates as the cursor moves
- Button clicks (left, right, middle, X1, X2)
- Vertical and horizontal wheel events
- User ID (hash) and HWID tracking

Requirements:
- Python 3.x
- pywin32 package (install with: pip install pywin32)
- MouseMux V2 with SDK enabled in the configuration

The program follows the SDK documentation for:
- Window message registration
- Parameter extraction from WPARAM and LPARAM
- Button flag definitions
- Event handling
- HWID tracking and lookup
- Using setter API to request user list

Usage:
1. Ensure MouseMux is running with SDK enabled
2. Run this script: python mousemux_monitor.py
3. Move the mouse and click buttons to see events in the console
4. Press Ctrl+C to exit

Note: This is a basic example focusing on mouse events. The SDK also supports
keyboard events and user creation/disposal events which are registered but not
handled in this example.
"""

import win32gui
import win32con
import win32api
import struct
from enum import IntFlag

# Windows coordinate extraction macros
def GET_X_LPARAM(lparam):
    return win32api.SignedLOWORD(lparam)

def GET_Y_LPARAM(lparam):
    return win32api.SignedHIWORD(lparam)

class DataButtonFlag(IntFlag):
    DATA_BUTTON_FLAG_1_DN = 1 << 0  # left down
    DATA_BUTTON_FLAG_1_UP = 1 << 1  # left up
    DATA_BUTTON_FLAG_2_DN = 1 << 2  # right down
    DATA_BUTTON_FLAG_2_UP = 1 << 3  # right up
    DATA_BUTTON_FLAG_3_DN = 1 << 4  # middle down
    DATA_BUTTON_FLAG_3_UP = 1 << 5  # middle up
    DATA_BUTTON_FLAG_4_DN = 1 << 6  # X1 down
    DATA_BUTTON_FLAG_4_UP = 1 << 7  # X1 up
    DATA_BUTTON_FLAG_5_DN = 1 << 8  # X2 down
    DATA_BUTTON_FLAG_5_UP = 1 << 9  # X2 up

class MouseMuxMonitor:
    def __init__(self):
        self.window_name = "mousemux-api-window-getter"
        self.setter_window_name = "mousemux-api-window-setter"
        self.message_names = [
            "mousemux.api.get.none",
            "mousemux.api.get.user.create",
            "mousemux.api.get.user.dispose",
            "mousemux.api.get.user.list",
            "mousemux.api.get.user.motion",
            "mousemux.api.get.user.button",
            "mousemux.api.get.user.v.wheel",
            "mousemux.api.get.user.h.wheel",
            "mousemux.api.get.user.keyboard"
        ]
        self.setter_message_names = [
            "mousemux.api.set.app.user.list"
        ]
        self.message_codes = {}
        self.setter_message_codes = {}
        self.register_messages()

        # HWID tracking
        self.LIST_SIZE = 64
        self.hwid_list = [{'hash': 0, 'hwid': 0} for _ in range(self.LIST_SIZE)]
        self.current_hwid = -1

    def register_messages(self):
        # Register getter messages
        for name in self.message_names:
            code = win32gui.RegisterWindowMessage(name)
            self.message_codes[code] = name
            print(f"Registered getter {name}: {code}")

        # Register setter messages
        for name in self.setter_message_names:
            code = win32gui.RegisterWindowMessage(name)
            self.setter_message_codes[code] = name
            print(f"Registered setter {name}: {code}")

    def request_user_list(self):
        """Request the list of current users from MouseMux"""
        setter_hwnd = win32gui.FindWindow(self.setter_window_name, self.setter_window_name)
        if setter_hwnd:
            list_message = self.setter_message_codes.get("mousemux.api.set.app.user.list")
            if list_message:
                print("Requesting user list from MouseMux...")
                win32gui.PostMessage(setter_hwnd, list_message, 0, 0)
            else:
                print("Failed to get user list message code")
        else:
            print("Could not find MouseMux setter window")

    def extract_coordinates(self, lparam):
        # Using Windows GET_X_LPARAM and GET_Y_LPARAM macros
        x = GET_X_LPARAM(lparam)
        y = GET_Y_LPARAM(lparam)
        return x, y

    def extract_params(self, wparam):
        hash = win32api.LOWORD(wparam)
        uid = win32api.HIWORD(wparam)
        return hash, uid

    def lookup_hwid(self, hash):
        """Look up the full HWID using the hashed HWID"""
        for entry in self.hwid_list:
            if entry['hash'] == hash:
                return entry['hwid']
        return -1

    def wnd_proc(self, hwnd, msg, wparam, lparam):
        if msg in self.message_codes:
            name = self.message_codes[msg]

            if name == "mousemux.api.get.user.create":
                hash = win32api.LOWORD(wparam)  # hashed 16-bit HWID
                uid = win32api.HIWORD(wparam)   # user id
                hwid = lparam                    # full 32-bit HWID
                print(f"User created - Hash: {hash:X}, UID: {uid}, HWID: {hwid:X}")

            elif name == "mousemux.api.get.user.dispose":
                hash = win32api.LOWORD(wparam)
                uid = win32api.HIWORD(wparam)
                hwid = lparam
                print(f"User disposed - Hash: {hash:X}, UID: {uid}, HWID: {hwid:X}")

            elif name == "mousemux.api.get.user.list":
                hash = win32api.LOWORD(wparam)
                uid = win32api.HIWORD(wparam)
                hwid = lparam
                self.hwid_list[uid]['hash'] = hash
                self.hwid_list[uid]['hwid'] = hwid
                self.current_hwid = hwid
                print(f"User listed - Hash: {hash:X}, UID: {uid}, HWID: {hwid:X}")

            elif name == "mousemux.api.get.user.motion":
                hash, uid = self.extract_params(wparam)
                x, y = self.extract_coordinates(lparam)
                hwid = self.lookup_hwid(hash)
                if hwid != -1:
                    self.current_hwid = hwid
                print(f"Mouse moved - Hash: {hash:X}, HWID: {hwid:X}, Position: ({x}, {y})")

            elif name == "mousemux.api.get.user.button":
                hash, uid = self.extract_params(wparam)
                x, y = self.extract_coordinates(lparam)
                button_flags = DataButtonFlag(uid)
                hwid = self.lookup_hwid(hash)
                if hwid != -1:
                    self.current_hwid = hwid
                print(f"Button event - Hash: {hash:X}, HWID: {hwid:X}, {button_flags} at ({x}, {y})")

            elif name == "mousemux.api.get.user.v.wheel":
                hash, delta = self.extract_params(wparam)
                x, y = self.extract_coordinates(lparam)
                hwid = self.lookup_hwid(hash)
                if hwid != -1:
                    self.current_hwid = hwid
                print(f"Vertical wheel - Hash: {hash:X}, HWID: {hwid:X}, Delta: {delta} at ({x}, {y})")

            elif name == "mousemux.api.get.user.h.wheel":
                hash, delta = self.extract_params(wparam)
                x, y = self.extract_coordinates(lparam)
                hwid = self.lookup_hwid(hash)
                if hwid != -1:
                    self.current_hwid = hwid
                print(f"Horizontal wheel - Hash: {hash:X}, HWID: {hwid:X}, Delta: {delta} at ({x}, {y})")

        return win32gui.DefWindowProc(hwnd, msg, wparam, lparam)

    def run(self):
        # Register window class
        wc = win32gui.WNDCLASS()
        wc.lpszClassName = self.window_name
        wc.lpfnWndProc = self.wnd_proc
        win32gui.RegisterClass(wc)

        # Create window
        hwnd = win32gui.CreateWindow(
            wc.lpszClassName,
            self.window_name,
            win32con.WS_OVERLAPPEDWINDOW,
            0, 0, 400, 300,
            0, 0, 0, None
        )

        print("MouseMux monitor started. Press Ctrl+C to exit.")
        win32gui.ShowWindow(hwnd, win32con.SW_SHOW)

        # Request initial user list
        self.request_user_list()

        try:
            while True:
                win32gui.PumpWaitingMessages()
        except KeyboardInterrupt:
            print("\nExiting...")
            win32gui.DestroyWindow(hwnd)

if __name__ == "__main__":
    monitor = MouseMuxMonitor()
    monitor.run()