#pragma once #include "core.h" #include namespace ks { inline constexpr const wchar_t *MouseButtonNames[] = {L"Left", L"Right", L"Middle", L"XButton1", L"XButton2"}; inline constexpr const wchar_t *MouseEvents[] = {L"Left", L"Right", L"Middle", L"XButton1", L"XButton2", L"Double left", L"Double right", L"Double middle"}; inline constexpr const wchar_t *MouseActions[] = {L"Standard behavior", L"Send key combination", L"Back", L"Forward", L"Volume up", L"Volume down", L"Mute", L"Launch application"}; inline constexpr wchar_t GlobalMouseProfile[] = L"*"; inline constexpr std::wstring_view NamedMouseProfilePrefix = L"name:"; bool validMouseApplication(const std::wstring &app); inline bool isNamedMouseProfile(std::wstring_view profile) { return profile.starts_with(NamedMouseProfilePrefix); } inline std::wstring mouseProfileName(const std::wstring &profile) { if (profile == GlobalMouseProfile) return L"Global"; return isNamedMouseProfile(profile) ? profile.substr(NamedMouseProfilePrefix.size()) : profile; } inline bool mouseActionApplies(const MouseAction &action, const std::wstring &app) { return action.applications.empty() || action.applications.contains(app); } inline const std::set &mouseActionScope(const Settings &settings, const std::wstring &profile, const MouseAction &action) { const auto scope = settings.mouseProfileApplications.find(profile); return scope == settings.mouseProfileApplications.end() ? action.applications : scope->second; } inline bool isMouseVolumeAction(const MouseAction *action) { return action && (action->type == MouseActionType::VolumeUp || action->type == MouseActionType::VolumeDown || action->type == MouseActionType::Mute); } inline bool isMouseVolumeStep(MouseActionType action) { return action == MouseActionType::VolumeUp || action == MouseActionType::VolumeDown; } inline constexpr UINT MouseVolumeHoldDelay = 350; inline UINT mouseVolumeRepeatInterval(ULONGLONG heldFor) { if (heldFor < 1000) return 140; if (heldFor < 2000) return 80; return 40; } inline const MouseAction *mouseBinding(const Settings &settings, const std::wstring &app, int event) { auto profile = settings.mouseBindings.find(app); if (profile == settings.mouseBindings.end()) return nullptr; auto action = profile->second.find(MouseEvents[event]); return action == profile->second.end() ? nullptr : &action->second; } inline const MouseAction *effectiveMouseAction(const Settings &settings, const std::wstring &app, int event, bool foreground = true, bool *profileScoped = nullptr) { if (profileScoped) *profileScoped = false; auto *action = mouseBinding(settings, app, event); if (!action) { const MouseAction *unrestricted = nullptr; for (auto profile = settings.mouseBindings.lower_bound(std::wstring(NamedMouseProfilePrefix)); profile != settings.mouseBindings.end() && isNamedMouseProfile(profile->first); ++profile) { if (settings.disabledMouseProfiles.contains(profile->first)) continue; const auto binding = profile->second.find(MouseEvents[event]); if (binding == profile->second.end()) continue; const auto &candidate = binding->second; const auto &scope = mouseActionScope(settings, profile->first, candidate); if (foreground && scope.contains(app)) { if (profileScoped) *profileScoped = true; return candidate.type == MouseActionType::Standard ? nullptr : &candidate; } if (!unrestricted && scope.empty()) unrestricted = &candidate; } const auto *global = mouseBinding(settings, GlobalMouseProfile, event); if (foreground && global && global->applications.contains(app)) action = global; else if (unrestricted) action = unrestricted; else if (global && global->applications.empty()) action = global; } return action && action->type != MouseActionType::Standard && mouseActionApplies(*action, app) ? action : nullptr; } inline bool doubleMouseClick(POINT first, POINT second, ULONGLONG elapsed) { return elapsed <= GetDoubleClickTime() && std::abs(static_cast(first.x) - second.x) * 2 <= GetSystemMetrics(SM_CXDOUBLECLK) && std::abs(static_cast(first.y) - second.y) * 2 <= GetSystemMetrics(SM_CYDOUBLECLK); } inline int mouseButton(UINT message, WORD xbutton) { switch (message) { case WM_LBUTTONDOWN: case WM_LBUTTONUP: return 0; case WM_RBUTTONDOWN: case WM_RBUTTONUP: return 1; case WM_MBUTTONDOWN: case WM_MBUTTONUP: return 2; case WM_XBUTTONDOWN: case WM_XBUTTONUP: if (xbutton == XBUTTON1) return 3; if (xbutton == XBUTTON2) return 4; } return -1; } inline bool mouseButtonDown(UINT message) { return message == WM_LBUTTONDOWN || message == WM_RBUTTONDOWN || message == WM_MBUTTONDOWN || message == WM_XBUTTONDOWN; } }