feat: release kswitcher 0.6.64
Add mouse button actions and application profiles, with volume controls and per-monitor settings layout fixes.
This commit is contained in:
1 parent
5a2106899d
commit
14f33012a3
23 files changed
+2233
-107
No files matched your search
+123
-1
@@ -4,12 +4,19 @@
|
||||
#include <fstream>
|
||||
#include <iomanip>
|
||||
#include <imm.h>
|
||||
#include <endpointvolume.h>
|
||||
#include <mmdeviceapi.h>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <set>
|
||||
#include <shellapi.h>
|
||||
#include <shlobj.h>
|
||||
#include <exdisp.h>
|
||||
#include <shldisp.h>
|
||||
#include <tlhelp32.h>
|
||||
#include <wincrypt.h>
|
||||
#include <wrl/client.h>
|
||||
|
||||
namespace ks
|
||||
{
|
||||
@@ -265,6 +272,83 @@ std::wstring processName(HWND window)
|
||||
CloseHandle(h);
|
||||
return ok ? std::filesystem::path(std::wstring(path, n)).stem().wstring() : L"";
|
||||
}
|
||||
std::wstring mouseApplicationName(HWND window)
|
||||
{
|
||||
auto name = lower(processName(window));
|
||||
if (name != L"applicationframehost") return name;
|
||||
std::wstring hosted;
|
||||
EnumChildWindows(window, [](HWND child, LPARAM output) -> BOOL {
|
||||
wchar_t kind[64]{};
|
||||
if (!GetClassNameW(child, kind, std::size(kind)) || wcscmp(kind, L"Windows.UI.Core.CoreWindow") != 0)
|
||||
return TRUE;
|
||||
auto &app = *reinterpret_cast<std::wstring *>(output);
|
||||
app = lower(processName(child));
|
||||
return app.empty();
|
||||
}, reinterpret_cast<LPARAM>(&hosted));
|
||||
return hosted.empty() ? name : hosted;
|
||||
}
|
||||
bool launchThroughExplorer(const std::wstring &path)
|
||||
{
|
||||
HANDLE token{};
|
||||
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token)) return false;
|
||||
TOKEN_ELEVATION elevation{};
|
||||
DWORD size{};
|
||||
const auto queried = GetTokenInformation(token, TokenElevation, &elevation, sizeof(elevation), &size);
|
||||
CloseHandle(token);
|
||||
if (!queried) return false;
|
||||
if (!elevation.TokenIsElevated)
|
||||
{
|
||||
SHELLEXECUTEINFOW launch{sizeof(launch)};
|
||||
launch.fMask = SEE_MASK_FLAG_NO_UI;
|
||||
launch.lpFile = path.c_str();
|
||||
launch.nShow = SW_SHOWNORMAL;
|
||||
return ShellExecuteExW(&launch) != FALSE;
|
||||
}
|
||||
|
||||
// An elevated resident delegates execution to the interactive desktop shell.
|
||||
auto release = [](IUnknown *object) { if (object) object->Release(); };
|
||||
IShellWindows *windowsRaw{};
|
||||
auto hr = CoCreateInstance(CLSID_ShellWindows, nullptr, CLSCTX_ALL, IID_IShellWindows,
|
||||
reinterpret_cast<void **>(&windowsRaw));
|
||||
std::unique_ptr<IShellWindows, decltype(release)> windows(windowsRaw, release);
|
||||
if (FAILED(hr) || !windows) return false;
|
||||
VARIANT empty{}, desktop{};
|
||||
desktop.vt = VT_I4;
|
||||
long windowHandle{};
|
||||
IDispatch *desktopRaw{};
|
||||
hr = windows->FindWindowSW(&desktop, &empty, SWC_DESKTOP, &windowHandle, SWFO_NEEDDISPATCH, &desktopRaw);
|
||||
std::unique_ptr<IDispatch, decltype(release)> dispatch(desktopRaw, release);
|
||||
if (FAILED(hr) || !dispatch) return false;
|
||||
IServiceProvider *providerRaw{};
|
||||
hr = dispatch->QueryInterface(IID_IServiceProvider, reinterpret_cast<void **>(&providerRaw));
|
||||
std::unique_ptr<IServiceProvider, decltype(release)> provider(providerRaw, release);
|
||||
if (FAILED(hr) || !provider) return false;
|
||||
IShellBrowser *browserRaw{};
|
||||
hr = provider->QueryService(SID_STopLevelBrowser, IID_IShellBrowser, reinterpret_cast<void **>(&browserRaw));
|
||||
std::unique_ptr<IShellBrowser, decltype(release)> browser(browserRaw, release);
|
||||
if (FAILED(hr) || !browser) return false;
|
||||
IShellView *viewRaw{};
|
||||
hr = browser->QueryActiveShellView(&viewRaw);
|
||||
std::unique_ptr<IShellView, decltype(release)> view(viewRaw, release);
|
||||
if (FAILED(hr) || !view) return false;
|
||||
IShellFolderViewDual *folderRaw{};
|
||||
hr = view->GetItemObject(SVGIO_BACKGROUND, IID_IShellFolderViewDual, reinterpret_cast<void **>(&folderRaw));
|
||||
std::unique_ptr<IShellFolderViewDual, decltype(release)> folder(folderRaw, release);
|
||||
if (FAILED(hr) || !folder) return false;
|
||||
IDispatch *applicationRaw{};
|
||||
hr = folder->get_Application(&applicationRaw);
|
||||
std::unique_ptr<IDispatch, decltype(release)> application(applicationRaw, release);
|
||||
if (FAILED(hr) || !application) return false;
|
||||
IShellDispatch2 *shellRaw{};
|
||||
hr = application->QueryInterface(IID_IShellDispatch2, reinterpret_cast<void **>(&shellRaw));
|
||||
std::unique_ptr<IShellDispatch2, decltype(release)> shell(shellRaw, release);
|
||||
if (FAILED(hr) || !shell) return false;
|
||||
BSTR executable = SysAllocString(path.c_str());
|
||||
if (!executable) return false;
|
||||
hr = shell->ShellExecute(executable, empty, empty, empty, empty);
|
||||
SysFreeString(executable);
|
||||
return SUCCEEDED(hr);
|
||||
}
|
||||
Clip readClipboard(HWND window, size_t limit, ClipboardRead mode, std::optional<DWORD> expected)
|
||||
{
|
||||
ClipboardLock lock(window);
|
||||
@@ -411,7 +495,9 @@ bool chord(WORD key, UINT mods)
|
||||
auto add = [&](WORD k, bool up)
|
||||
{
|
||||
INPUT i = keyboardInput(k, up ? KEYEVENTF_KEYUP : 0);
|
||||
if (k == VK_LEFT || k == VK_RIGHT || k == VK_INSERT)
|
||||
if ((k >= VK_PRIOR && k <= VK_DOWN) || k == VK_INSERT || k == VK_DELETE ||
|
||||
k == VK_DIVIDE || k == VK_LWIN || k == VK_SNAPSHOT ||
|
||||
(k >= VK_VOLUME_MUTE && k <= VK_VOLUME_UP))
|
||||
i.ki.dwFlags |= KEYEVENTF_EXTENDEDKEY;
|
||||
inputs.push_back(i);
|
||||
};
|
||||
@@ -431,6 +517,42 @@ bool chord(WORD key, UINT mods)
|
||||
add(k, true);
|
||||
return SendInput((UINT)inputs.size(), inputs.data(), sizeof(INPUT)) == inputs.size();
|
||||
}
|
||||
std::optional<SystemVolume> changeSystemVolume(MouseActionType action, int step)
|
||||
{
|
||||
using Microsoft::WRL::ComPtr;
|
||||
ComPtr<IMMDeviceEnumerator> devices;
|
||||
ComPtr<IMMDevice> output;
|
||||
ComPtr<IAudioEndpointVolume> volume;
|
||||
if (FAILED(CoCreateInstance(__uuidof(MMDeviceEnumerator), nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&devices))) ||
|
||||
FAILED(devices->GetDefaultAudioEndpoint(eRender, eMultimedia, &output)) ||
|
||||
FAILED(output->Activate(__uuidof(IAudioEndpointVolume), CLSCTX_INPROC_SERVER, nullptr,
|
||||
reinterpret_cast<void **>(volume.GetAddressOf()))))
|
||||
return std::nullopt;
|
||||
float level{};
|
||||
BOOL muted{};
|
||||
HRESULT changed;
|
||||
switch (action)
|
||||
{
|
||||
case MouseActionType::VolumeUp:
|
||||
case MouseActionType::VolumeDown:
|
||||
{
|
||||
if (FAILED(volume->GetMasterVolumeLevelScalar(&level))) return std::nullopt;
|
||||
const float delta = (action == MouseActionType::VolumeUp ? step : -step) / 100.0f;
|
||||
changed = volume->SetMasterVolumeLevelScalar(std::clamp(level + delta, 0.0f, 1.0f), nullptr);
|
||||
break;
|
||||
}
|
||||
case MouseActionType::Mute:
|
||||
{
|
||||
if (FAILED(volume->GetMute(&muted))) return std::nullopt;
|
||||
changed = volume->SetMute(!muted, nullptr);
|
||||
break;
|
||||
}
|
||||
default: return std::nullopt;
|
||||
}
|
||||
if (FAILED(changed) || FAILED(volume->GetMasterVolumeLevelScalar(&level)) || FAILED(volume->GetMute(&muted)))
|
||||
return std::nullopt;
|
||||
return SystemVolume{std::clamp(static_cast<int>(level * 100 + 0.5f), 0, 100), muted != FALSE};
|
||||
}
|
||||
bool needsSelectionDelay(HWND target)
|
||||
{
|
||||
const auto name = lower(processName(target));
|
||||
|
||||
Reference in new issue
Block a user