Accessing the 4th and 5th mouse button?

Hello,

is there a way in the editor to access clicks on the 4th and 5th mouse button?

Event.current.button does not seem to be anything but 0, 1 or 2 (as stated in the docs).

Ciao, Imi.

PS: I am not talking about the runtime player nor about the upcoming Unity UI from 4.6. I like to access the button in editor scripts to use for my custom EditorWindow. :wink:

I’m hitting the same issue. For some reason, Unity believe a mouse only has 3 buttons.

Very late to the party, but I made a script for this, you can assign any commands to the mouse buttons:

// MouseButtonShortcuts in Assets/Editor

using System;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.InteropServices;
using UnityEditor;
using UnityEngine;
using UnityEngine.UIElements;

[FilePath("UserSettings/MouseButtonShortcuts.asset", FilePathAttribute.Location.PreferencesFolder)]
public class MouseButtonShortcuts : ScriptableSingleton<MouseButtonShortcuts>
{
    // Middle button
    [SerializeField] private string m_Button3 = "";
    // Back button
    [SerializeField] private string m_Button4 = "";
    // Forward button
    [SerializeField] private string m_Button5 = "";

#if UNITY_EDITOR_WIN
    // Windows P/Invoke
    [DllImport("user32.dll")]
    private static extern short GetAsyncKeyState(int vKey);
#elif UNITY_EDITOR_OSX
    // macOS NSEvent method signatures
    [DllImport("/System/Library/Frameworks/AppKit.framework/AppKit")]
    private static extern IntPtr objc_getClass(string className);

    [DllImport("/System/Library/Frameworks/AppKit.framework/AppKit")]
    private static extern IntPtr class_getClassMethod(IntPtr cls, IntPtr selector);

    [DllImport("/System/Library/Frameworks/AppKit.framework/AppKit")]
    private static extern IntPtr sel_registerName(string name);

    [DllImport("/System/Library/Frameworks/AppKit.framework/AppKit")]
    private static extern uint objc_msgSend_uint(IntPtr receiver, IntPtr selector);
#endif

    // Start as true to avoid triggering on initial state
    private static bool s_Button3Pressed = true;
    private static bool s_Button4Pressed = true;
    private static bool s_Button5Pressed = true;

    [InitializeOnLoadMethod]
    private static void Initialize()
    {
        EditorApplication.update += PollMouseButtons;
    }

    private static void PollMouseButtons()
    {
        bool button3State = false;
        bool button4State = false;
        bool button5State = false;
        try
        {
#if UNITY_EDITOR_WIN
            // Windows: Use GetAsyncKeyState
            button3State = (GetAsyncKeyState(0x04) & 0x8000) != 0; // Button 3
            button4State = (GetAsyncKeyState(0x05) & 0x8000) != 0; // Button 4
            button5State = (GetAsyncKeyState(0x06) & 0x8000) != 0; // Button 5
#elif UNITY_EDITOR_OSX
            // macOS: Use NSEvent pressedMouseButtons
            IntPtr nsEventClass = objc_getClass("NSEvent");
            IntPtr selector = sel_registerName("pressedMouseButtons");
            uint pressedButtons = objc_msgSend_uint(nsEventClass, selector);

            button3State = (pressedButtons & (1u << 2)) != 0; // Button 3 (middle)
            button4State = (pressedButtons & (1u << 3)) != 0; // Button 4
            button5State = (pressedButtons & (1u << 4)) != 0; // Button 5
#endif
        }
        catch
        {
            // Fallback: macOS event polling failed
        }

        if (instance && button3State && !s_Button3Pressed)
        {
            HandleMouseButtonEvent(instance.m_Button3);
        }

        if (instance && button4State && !s_Button4Pressed)
        {
            HandleMouseButtonEvent(instance.m_Button4);
        }

        if (instance && button5State && !s_Button5Pressed)
        {
            HandleMouseButtonEvent(instance.m_Button5);
        }

        s_Button3Pressed = button3State;
        s_Button4Pressed = button4State;
        s_Button5Pressed = button5State;
    }

    public void SaveShortcuts()
    {
        name = "MouseButtonShortcuts";
        Save(true);
    }

    private static void HandleMouseButtonEvent(string menuItemPath)
    {
        if (string.IsNullOrEmpty(menuItemPath))
        {
            return;
        }

        EditorApplication.delayCall += () =>
        {
            try
            {
                EditorApplication.ExecuteMenuItem(menuItemPath);
            }
            catch (Exception ex)
            {
                Debug.LogError($"Failed to execute menu item '{menuItemPath}': {ex.Message}");
            }
        };
    }

    private static List<string> GetAllMenuItems()
    {
        List<string> menuItems = new();

        // Get all methods with MenuItem attribute using TypeCache
        foreach (MethodInfo method in TypeCache.GetMethodsWithAttribute<MenuItem>())
        {
            foreach (MenuItem attribute in method.GetCustomAttributes<MenuItem>())
            {
                string menuItem = attribute.menuItem;
                if (string.IsNullOrEmpty(menuItem))
                {
                    continue;
                }

                // Filter out unwanted prefixes
                if (menuItem.StartsWith("Assets/",     StringComparison.Ordinal)
                 || menuItem.StartsWith("CONTEXT/",    StringComparison.Ordinal)
                 || menuItem.StartsWith("internal:",   StringComparison.Ordinal)
                 || menuItem.StartsWith("GameObject/", StringComparison.Ordinal))
                {
                    continue;
                }

                // Remove shortcut characters according to Unity's MenuItem rules
                // Shortcuts come after a space character and use special modifiers:
                // % (Ctrl/Cmd), ^ (Ctrl), # (Shift), & (Alt), _ (no modifier)
                // Examples: "Menu/Item #&g", "Menu/Item _F1", "Menu/Item %#LEFT"
                int lastSpaceIndex = menuItem.LastIndexOf(' ');
                if (lastSpaceIndex >= 0)
                {
                    // Check if what comes after the space looks like a shortcut
                    string afterSpace = menuItem.Substring(lastSpaceIndex + 1);
                    if (afterSpace.Length > 0)
                    {
                        char firstChar = afterSpace[0];
                        // If it starts with a modifier (%, ^, #, &, _) it's a shortcut
                        if (firstChar is '%' or '^' or '#' or '&' or '_')
                        {
                            menuItem = menuItem.Substring(0, lastSpaceIndex);
                        }
                    }
                }

                menuItems.Add(menuItem);
            }
        }

        menuItems.Sort();
        return menuItems;
    }

    [SettingsProvider]
    public static SettingsProvider CreateProvider()
    {
        SettingsProvider provider = new("Preferences/Mouse Button Shortcuts", SettingsScope.User)
        {
            label = "Mouse Button Shortcuts",
            activateHandler = (_, rootElement) =>
            {
                MouseButtonShortcuts settings = instance;
                SerializedObject so = new(settings);

                // title
                Label title = new("Mouse Button Shortcuts");
                title.style.unityFontStyleAndWeight = FontStyle.Bold;
                title.style.fontSize = 14;
                rootElement.Add(title);

                HelpBox helpBox = new(
                    "Assign MenuItem paths to mouse buttons 3, 4, and 5. "
                  + "These shortcuts will be triggered anywhere in the Unity Editor.",
                    HelpBoxMessageType.Info);
                rootElement.Add(helpBox);

                rootElement.Add(
                    CreateShortcutField("Mouse Button 3 (Middle)", so.FindProperty(nameof(m_Button3))));
                rootElement.Add(
                    CreateShortcutField("Mouse Button 4 (Back)", so.FindProperty(nameof(m_Button4))));
                rootElement.Add(
                    CreateShortcutField("Mouse Button 5 (Forward)", so.FindProperty(nameof(m_Button5))));
            },
        };

        return provider;
    }

    private static VisualElement CreateShortcutField(string label, SerializedProperty prop)
    {
        VisualElement container = new();
        List<string> allMenuItems = GetAllMenuItems();
        List<string> choices = new() { "(None)" };
        choices.AddRange(allMenuItems);

        Label fieldLabel = new(label);
        fieldLabel.style.marginTop = 4;
        container.Add(fieldLabel);

        DropdownField dropdown = new();
        dropdown.choices = choices;

        string currentValue = prop.stringValue;
        dropdown.value = string.IsNullOrEmpty(currentValue) ? "(None)" : currentValue;

        dropdown.RegisterValueChangedCallback(evt =>
        {
            string newValue = evt.newValue == "(None)" ? "" : evt.newValue;
            prop.stringValue = newValue;
            prop.serializedObject.ApplyModifiedProperties();
            instance.SaveShortcuts();
        });

        container.Add(dropdown);

        return container;
    }
}