Add mouse button actions and application profiles, with volume controls and per-monitor settings layout fixes.
124 lines
5.1 KiB
C++
124 lines
5.1 KiB
C++
#pragma once
|
|
#include "core.h"
|
|
#include <cstdlib>
|
|
|
|
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<std::wstring> &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<long long>(first.x) - second.x) * 2 <= GetSystemMetrics(SM_CXDOUBLECLK) &&
|
|
std::abs(static_cast<long long>(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;
|
|
}
|
|
}
|