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.
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:
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:
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 ,
}
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.
Here you will find each API call explained.
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.
Change MouseMux's mode of operation.
Hide MouseMux's UI (minimize).
List all current users. This will iterate the internal user list and for each user send a "mousemux.api.get.user.list" call.
Hide or show the cursor of a user
Change the type of the cursor of a user
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.
Change the flag of the cursor of a user
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
}
Change the size of the cursor of a user
Change the theme of the cursor of a user
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.
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
Lock or unlock the cursor type - this will only lock cursor type changes by the system, you can still manually override the type
Create a (virtual) user (with attached pointer and keyboard)
Dispose a (virtual) user (including removing attached pointer and keyboard)
Move the pointer (cursor) for a user to new coordinates
Click the mouse button for a user
Pass a custom event for this pointer
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()