diff --git a/IntelPresentMon/AppCef/CefNano.vcxproj b/IntelPresentMon/AppCef/CefNano.vcxproj index 835029e3..8464054f 100644 --- a/IntelPresentMon/AppCef/CefNano.vcxproj +++ b/IntelPresentMon/AppCef/CefNano.vcxproj @@ -23,7 +23,6 @@ - @@ -44,6 +43,7 @@ + @@ -60,8 +60,6 @@ - - @@ -75,7 +73,6 @@ - diff --git a/IntelPresentMon/AppCef/ipm-ui-vue/src/core/api.ts b/IntelPresentMon/AppCef/ipm-ui-vue/src/core/api.ts index b9ad66dc..c00fe5ea 100644 --- a/IntelPresentMon/AppCef/ipm-ui-vue/src/core/api.ts +++ b/IntelPresentMon/AppCef/ipm-ui-vue/src/core/api.ts @@ -81,13 +81,10 @@ export class Api { return adapters; } static async bindHotkey(binding: Binding): Promise { - await this.invokeEndpointFuture('bindHotkey', binding); + await this.invokeEndpointFuture('BindHotkey', binding); } static async clearHotkey(action: Action): Promise { - await this.invokeEndpointFuture('clearHotkey', {action}); - } - static async launchKernel(): Promise { - await this.invokeEndpointFuture('launchKernel', {}); + await this.invokeEndpointFuture('ClearHotkey', {action}); } static async pushSpecification(spec: Spec): Promise { await this.invokeEndpointFuture('PushSpecification', spec); diff --git a/IntelPresentMon/AppCef/source/DataBindAccessor.cpp b/IntelPresentMon/AppCef/source/DataBindAccessor.cpp index abe7bb5f..0812d780 100644 --- a/IntelPresentMon/AppCef/source/DataBindAccessor.cpp +++ b/IntelPresentMon/AppCef/source/DataBindAccessor.cpp @@ -20,14 +20,7 @@ namespace p2c::client::cef : pBrowser{ std::move(pBrowser) }, pKernelWrapper{ pKernelWrapper_ } - { - pKernelWrapper->pHotkeys = std::make_unique(); - // set the hotkey listener component to call hotkey signal on the signal manager when a hotkey chord is detected - pKernelWrapper->pHotkeys->SetHandler([this](Action action) { - CefPostTask(TID_RENDERER, base::BindOnce(&util::SignalManager::SignalHotkeyFired, - base::Unretained(&pKernelWrapper->signals), uint32_t(action))); - }); - } + {} bool DataBindAccessor::Execute(const CefString& name, CefRefPtr object, const CefV8ValueList& arguments, CefRefPtr& retval, CefString& exception) { @@ -88,44 +81,6 @@ namespace p2c::client::cef } } - bool DataBindAccessor::BindHotkey(CefValue& pArgObj) - { - // {action:int, combination: {modifiers:[], key:int}} - - std::shared_lock lk{ kernelMtx }; - if (pKernelWrapper) { - const auto payload = pArgObj.GetDictionary(); - const auto comboJs = payload->GetDictionary("combination"); - const auto modsJs = comboJs->GetList("modifiers"); - - auto mods = win::Mod::Null; - for (int i = 0; i < modsJs->GetSize(); i++) { - const auto modCode = modsJs->GetValue(i)->GetInt(); - mods = mods | *win::ModSet::SingleModFromCode(modCode); - } - - return pKernelWrapper->pHotkeys->BindAction( - (Action)payload->GetInt("action"), - win::Key{ (win::Key::Code)comboJs->GetInt("key") }, - mods - ); - } - return false; - } - - bool DataBindAccessor::ClearHotkey(CefValue& pArgObj) - { - // {action:int} - - std::shared_lock lk{ kernelMtx }; - if (pKernelWrapper) { - return pKernelWrapper->pHotkeys->ClearAction( - (Action)pArgObj.GetDictionary()->GetInt("action") - ); - } - return false; - } - void DataBindAccessor::ClearKernelWrapper() { std::lock_guard lk{ kernelMtx }; diff --git a/IntelPresentMon/AppCef/source/DataBindAccessor.h b/IntelPresentMon/AppCef/source/DataBindAccessor.h index f2584a47..ced6059f 100644 --- a/IntelPresentMon/AppCef/source/DataBindAccessor.h +++ b/IntelPresentMon/AppCef/source/DataBindAccessor.h @@ -19,8 +19,6 @@ namespace p2c::client::cef CefRefPtr& retval, CefString& exception) override; void ResolveAsyncEndpoint(uint64_t uid, bool success, CefRefPtr pArgs); - bool BindHotkey(CefValue& pArgObj); - bool ClearHotkey(CefValue& pArgObj); void ClearKernelWrapper(); private: // data diff --git a/IntelPresentMon/AppCef/source/util/AsyncEndpointCollection.cpp b/IntelPresentMon/AppCef/source/util/AsyncEndpointCollection.cpp index ebcfa3ac..bc638ca1 100644 --- a/IntelPresentMon/AppCef/source/util/AsyncEndpointCollection.cpp +++ b/IntelPresentMon/AppCef/source/util/AsyncEndpointCollection.cpp @@ -6,8 +6,6 @@ #include "async/BrowseReadSpec.h" #include "async/BrowseStoreSpec.h" -#include "async/BindHotkey.h" -#include "async/ClearHotkey.h" #include "async/EnumerateProcesses.h" #include "async/EnumerateKeys.h" #include "async/EnumerateModifiers.h" @@ -34,8 +32,6 @@ namespace p2c::client::util using namespace async; AddEndpoint(); AddEndpoint(); - AddEndpoint(); - AddEndpoint(); AddEndpoint(); AddEndpoint(); AddEndpoint(); diff --git a/IntelPresentMon/AppCef/source/util/KernelActionRegistration.cpp b/IntelPresentMon/AppCef/source/util/KernelActionRegistration.cpp index 2b5ba9f0..55e8ca22 100644 --- a/IntelPresentMon/AppCef/source/util/KernelActionRegistration.cpp +++ b/IntelPresentMon/AppCef/source/util/KernelActionRegistration.cpp @@ -11,5 +11,7 @@ namespace p2c::client::util::kact { IpcActionRegistrator reg_ibind_PushSpecification_; IpcActionRegistrator reg_ibind_SetAdapter_; IpcActionRegistrator reg_ibind_SetCapture_; + IpcActionRegistrator reg_ibind_BindHotkey_; + IpcActionRegistrator reg_ibind_ClearHotkey_; } diff --git a/IntelPresentMon/AppCef/source/util/KernelWrapper.h b/IntelPresentMon/AppCef/source/util/KernelWrapper.h index 7f997c64..874c86fb 100644 --- a/IntelPresentMon/AppCef/source/util/KernelWrapper.h +++ b/IntelPresentMon/AppCef/source/util/KernelWrapper.h @@ -1,6 +1,5 @@ #pragma once #include -#include "HotkeyListener.h" #include "SignalManager.h" #include "AsyncEndpointManager.h" #include "ActionClientServer.h" @@ -11,7 +10,6 @@ namespace p2c::client::util { struct KernelWrapper { - std::unique_ptr pHotkeys; util::SignalManager signals; util::AsyncEndpointManager asyncEndpoints; std::unique_ptr pClient; diff --git a/IntelPresentMon/AppCef/source/util/async/BindHotkey.h b/IntelPresentMon/AppCef/source/util/async/BindHotkey.h deleted file mode 100644 index 5f835e44..00000000 --- a/IntelPresentMon/AppCef/source/util/async/BindHotkey.h +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright (C) 2022 Intel Corporation -// SPDX-License-Identifier: MIT -#pragma once -#include "../AsyncEndpoint.h" -#include -#include "include/base/cef_callback.h" -#include "include/wrapper/cef_closure_task.h" -#include "../../DataBindAccessor.h" -#include "../CefValues.h" - -namespace p2c::client::util::async -{ - class BindHotkey : public AsyncEndpoint - { - public: - static constexpr std::string GetKey() { return "bindHotkey"; } - BindHotkey() : AsyncEndpoint{ AsyncEndpoint::Environment::RenderProcess } {} - // {combination: {modifiers:[], hotkey:int}, action:int} => null - Result ExecuteOnRenderer(uint64_t uid, CefRefPtr pArgObj, cef::DataBindAccessor& accessor) const override - { - return Result{ accessor.BindHotkey(*pArgObj), CefValueNull() }; - } - }; -} \ No newline at end of file diff --git a/IntelPresentMon/AppCef/source/util/async/ClearHotkey.h b/IntelPresentMon/AppCef/source/util/async/ClearHotkey.h deleted file mode 100644 index 53f12d9b..00000000 --- a/IntelPresentMon/AppCef/source/util/async/ClearHotkey.h +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright (C) 2022 Intel Corporation -// SPDX-License-Identifier: MIT -#pragma once -#include "../AsyncEndpoint.h" -#include -#include "include/base/cef_callback.h" -#include "include/wrapper/cef_closure_task.h" -#include "../../DataBindAccessor.h" -#include "../CefValues.h" - -namespace p2c::client::util::async -{ - class ClearHotkey : public AsyncEndpoint - { - public: - static constexpr std::string GetKey() { return "clearHotkey"; } - ClearHotkey() : AsyncEndpoint{ AsyncEndpoint::Environment::RenderProcess } {} - // {action:int} => null - Result ExecuteOnRenderer(uint64_t uid, CefRefPtr pArgObj, cef::DataBindAccessor& accessor) const override - { - return Result{ accessor.ClearHotkey(*pArgObj), CefValueNull() }; - } - }; -} \ No newline at end of file diff --git a/IntelPresentMon/AppCef/source/util/async/EnumerateKeys.h b/IntelPresentMon/AppCef/source/util/async/EnumerateKeys.h index 28e23a5d..bcd2b40c 100644 --- a/IntelPresentMon/AppCef/source/util/async/EnumerateKeys.h +++ b/IntelPresentMon/AppCef/source/util/async/EnumerateKeys.h @@ -3,6 +3,8 @@ #pragma once #include "../AsyncEndpoint.h" #include "../CefValues.h" +#include +#include namespace p2c::client::util::async { diff --git a/IntelPresentMon/AppCef/source/util/async/EnumerateProcesses.h b/IntelPresentMon/AppCef/source/util/async/EnumerateProcesses.h index 1cfa04ba..fd86304d 100644 --- a/IntelPresentMon/AppCef/source/util/async/EnumerateProcesses.h +++ b/IntelPresentMon/AppCef/source/util/async/EnumerateProcesses.h @@ -2,8 +2,11 @@ // SPDX-License-Identifier: MIT #pragma once #include +#include +#include #include "../AsyncEndpoint.h" #include "../CefValues.h" +#include namespace p2c::client::util::async { @@ -15,6 +18,7 @@ namespace p2c::client::util::async // {} => {processes: [{name: string, pid: uint}]} Result ExecuteOnRenderer(uint64_t uid, CefRefPtr pArgObj, cef::DataBindAccessor&) const override { + namespace vi = std::views; // enumerate processes on system win::ProcessMapBuilder builder; builder.FillWindowHandles(); diff --git a/IntelPresentMon/AppCef/source/util/cact/CefActionRegistration.cpp b/IntelPresentMon/AppCef/source/util/cact/CefActionRegistration.cpp index 78e44eda..51b01979 100644 --- a/IntelPresentMon/AppCef/source/util/cact/CefActionRegistration.cpp +++ b/IntelPresentMon/AppCef/source/util/cact/CefActionRegistration.cpp @@ -2,4 +2,5 @@ #include "OverlayDiedAction.h" #include "PresentmonInitFailedAction.h" #include "StalePidAction.h" -#include "TargetLostAction.h" \ No newline at end of file +#include "TargetLostAction.h" +#include "HotkeyFiredAction.h" \ No newline at end of file diff --git a/IntelPresentMon/AppCef/source/util/cact/HotkeyFiredAction.h b/IntelPresentMon/AppCef/source/util/cact/HotkeyFiredAction.h new file mode 100644 index 00000000..1d6edd03 --- /dev/null +++ b/IntelPresentMon/AppCef/source/util/cact/HotkeyFiredAction.h @@ -0,0 +1,59 @@ +#pragma once +#include +#include "CefExecutionContext.h" +#include "../../Action.h" +#include + +#define ACT_NAME HotkeyFiredAction +#define ACT_EXEC_CTX CefExecutionContext +#define ACT_TYPE AsyncEventActionBase_ +#define ACT_NS ::p2c::client::util::cact + +namespace p2c::client::util::cact +{ + using namespace ::pmon::ipc::act; + + class ACT_NAME : public ACT_TYPE + { + public: + static constexpr const char* Identifier = STRINGIFY(ACT_NAME); + struct Params + { + int32_t actionId; + + template void serialize(A& ar) { + ar(actionId); + } + }; + private: + friend class ACT_TYPE; + static void Execute_(const ACT_EXEC_CTX& ctx, SessionContext& stx, Params&& in); + }; +} + +#ifdef PM_ASYNC_ACTION_REGISTRATION_ +#include +#include +#include +#include "../SignalManager.h" +namespace p2c::client::util::cact +{ + ACTION_REG(); + // need to put this function definition only in the cef-server-side since it contains + // references to CEF types we do not want to pollute other projects with + // TODO: consider a better approach to maintaining single-file actions like this + // while also enabling dependency decoupling more easily + void ACT_NAME::Execute_(const ACT_EXEC_CTX& ctx, SessionContext& stx, Params&& in) + { + CefPostTask(TID_RENDERER, base::BindOnce(&SignalManager::SignalHotkeyFired, + base::Unretained(ctx.pSignalManager), in.actionId)); + } +} +#endif + +ACTION_TRAITS_DEF(); + +#undef ACT_NAME +#undef ACT_EXEC_CTX +#undef ACT_NS +#undef ACT_TYPE \ No newline at end of file diff --git a/IntelPresentMon/Core/Core.vcxproj b/IntelPresentMon/Core/Core.vcxproj index 979b91ef..b866f46b 100644 --- a/IntelPresentMon/Core/Core.vcxproj +++ b/IntelPresentMon/Core/Core.vcxproj @@ -85,6 +85,7 @@ + @@ -147,6 +148,7 @@ + diff --git a/IntelPresentMon/Core/Core.vcxproj.filters b/IntelPresentMon/Core/Core.vcxproj.filters index 0701ec39..fed77fa8 100644 --- a/IntelPresentMon/Core/Core.vcxproj.filters +++ b/IntelPresentMon/Core/Core.vcxproj.filters @@ -91,6 +91,7 @@ + @@ -150,6 +151,7 @@ + diff --git a/IntelPresentMon/Core/source/infra/Logging.h b/IntelPresentMon/Core/source/infra/Logging.h index 1a8bb8d2..8016b9ca 100644 --- a/IntelPresentMon/Core/source/infra/Logging.h +++ b/IntelPresentMon/Core/source/infra/Logging.h @@ -26,4 +26,5 @@ namespace p2c::v #else inline constexpr bool metric = true; #endif + inline constexpr bool hotkey2 = false; } \ No newline at end of file diff --git a/IntelPresentMon/AppCef/source/util/HotkeyListener.cpp b/IntelPresentMon/Core/source/win/HotkeyListener.cpp similarity index 76% rename from IntelPresentMon/AppCef/source/util/HotkeyListener.cpp rename to IntelPresentMon/Core/source/win/HotkeyListener.cpp index 5dec741c..b01ca2b0 100644 --- a/IntelPresentMon/AppCef/source/util/HotkeyListener.cpp +++ b/IntelPresentMon/Core/source/win/HotkeyListener.cpp @@ -1,13 +1,10 @@ // Copyright (C) 2022 Intel Corporation // SPDX-License-Identifier: MIT #include "HotkeyListener.h" -#include "Logging.h" +#include #include #include #include -#include -#include "include/base/cef_callback.h" -#include "include/wrapper/cef_closure_task.h" namespace rn = std::ranges; namespace vi = rn::views; @@ -15,24 +12,24 @@ using namespace pmon::util; // TODO: general logging in this codebase (winapi calls like PostThreadMessage etc.) -namespace p2c::client::util +namespace p2c::win { Hotkeys::Hotkeys() : - thread_{ "hotkey", & Hotkeys::Kernel_, this} + thread_{ "hotkey", &Hotkeys::Kernel_, this } { startupSemaphore_.acquire(); - pmlog_verb(v::hotkey)("Hotkey process ctor complete"); + pmlog_verb(v::hotkey2)("Hotkey process ctor complete"); } Hotkeys::~Hotkeys() { - pmlog_verb(v::hotkey)("Destroying hotkey processor"); + pmlog_verb(v::hotkey2)("Destroying hotkey processor"); PostThreadMessageA(threadId_, WM_QUIT, 0, 0); } void Hotkeys::Kernel_() noexcept { try { - pmlog_verb(v::hotkey)("Hotkey processor kernel start"); + pmlog_verb(v::hotkey2)("Hotkey processor kernel start"); // capture thread id threadId_ = GetCurrentThreadId(); @@ -44,17 +41,17 @@ namespace p2c::client::util .hInstance = GetModuleHandle(nullptr), .lpszClassName = "$PresentmonMessageWindowClass$", }; - const auto atom = RegisterClassEx(&wx); + const auto atom = RegisterClassExA(&wx); if (!atom) { pmlog_error().hr(); return; } - pmlog_verb(v::hotkey)(std::format("Hotkey processor wndclass registered: {:X}", atom)); + pmlog_verb(v::hotkey2)(std::format("Hotkey processor wndclass registered: {:X}", atom)); // create message window - messageWindowHandle_ = CreateWindowExA( - 0, MAKEINTATOM(atom), "$PresentmonMessageWindow$", + messageWindowHandle_ = CreateWindowExW( + 0, MAKEINTATOM(atom), L"$PresentmonMessageWindow$", 0, 0, 0, 0, 0, HWND_MESSAGE, nullptr, nullptr, nullptr ); if (!messageWindowHandle_) { @@ -62,7 +59,7 @@ namespace p2c::client::util return; } - pmlog_verb(v::hotkey)(std::format("Hotkey processor wnd created: {:X}", + pmlog_verb(v::hotkey2)(std::format("Hotkey processor wnd created: {:X}", reinterpret_cast(messageWindowHandle_))); // register to receive raw keyboard input @@ -77,7 +74,7 @@ namespace p2c::client::util return; } - pmlog_verb(v::hotkey)("Raw input registered"); + pmlog_verb(v::hotkey2)("Raw input registered"); // signal that constuction is complete startupSemaphore_.release(); @@ -85,7 +82,7 @@ namespace p2c::client::util MSG msg; while (GetMessageA(&msg, nullptr, 0, 0)) { if (msg.message == WM_INPUT) { - pmlog_verb(v::hotkey)("WM_INPUT received"); + pmlog_verb(v::hotkey2)("WM_INPUT received"); auto& lParam = msg.lParam; // allocate memory for raw input data @@ -109,16 +106,16 @@ namespace p2c::client::util { const auto& keyboard = input.data.keyboard; - pmlog_verb(v::hotkey)(std::format("KBD| vk:{} msg:{}", keyboard.VKey, keyboard.Message)); + pmlog_verb(v::hotkey2)(std::format("KBD| vk:{} msg:{}", keyboard.VKey, keyboard.Message)); // check for keycode outside of range of our table if (keyboard.VKey >= win::Key::virtualKeyTableSize) { - pmlog_verb(v::hotkey)("KBD| vk out of range"); + pmlog_verb(v::hotkey2)("KBD| vk out of range"); continue; } // key presses if (keyboard.Message == WM_KEYDOWN || keyboard.Message == WM_SYSKEYDOWN) { - pmlog_verb(v::hotkey)("key press"); + pmlog_verb(v::hotkey2)("key press"); // don't handle autorepeat presses if (!pressedKeys_[keyboard.VKey]) { // mark key as in down state @@ -133,19 +130,19 @@ namespace p2c::client::util if (auto i = registeredHotkeys_.find({ *key, mods }); i != registeredHotkeys_.cend()) { // if match found dispatch action on renderer thread - pmlog_verb(v::hotkey)("hotkey dispatching"); + pmlog_verb(v::hotkey2)("hotkey dispatching"); DispatchHotkey_(i->second); } } } } else { - pmlog_verb(v::hotkey)("key repeat"); + pmlog_verb(v::hotkey2)("key repeat"); } } // key releases else if (keyboard.Message == WM_KEYUP || keyboard.Message == WM_SYSKEYUP) { - pmlog_verb(v::hotkey)("Key up"); + pmlog_verb(v::hotkey2)("Key up"); pressedKeys_[keyboard.VKey] = false; } } @@ -162,37 +159,37 @@ namespace p2c::client::util pmlog_warn("failed unreg class").hr(); } - pmlog_verb(v::hotkey)(); + pmlog_verb(v::hotkey2)(); } catch (...) { pmlog_error(ReportException()); } } - void Hotkeys::DispatchHotkey_(Action action) const + void Hotkeys::DispatchHotkey_(int action) const { if (Handler_) { - pmlog_dbg("hotkey action dispatched to handler").pmwatch(reflect::enum_name(action)); + pmlog_dbg("hotkey action dispatched to handler").pmwatch(action); Handler_(action); } else { pmlog_warn("Hotkey handler not set"); } } - void Hotkeys::SetHandler(std::function handler) + void Hotkeys::SetHandler(std::function handler) { std::lock_guard lk{ mtx_ }; - pmlog_verb(v::hotkey)("Hotkey handler set"); + pmlog_verb(v::hotkey2)("Hotkey handler set"); Handler_ = std::move(handler); } - bool Hotkeys::BindAction(Action action, win::Key key, win::ModSet mods) + bool Hotkeys::BindAction(int action, win::Key key, win::ModSet mods) { - pmlog_verb(v::hotkey)("Hotkey action binding"); + pmlog_verb(v::hotkey2)("Hotkey action binding"); std::lock_guard lk{ mtx_ }; // if action is already bound, remove it if (const auto i = rn::find_if(registeredHotkeys_, [action](const auto& i) { return i.second == action; }); i != registeredHotkeys_.cend()) { - pmlog_verb(v::hotkey)("Hotkey action binding remove existing action"); + pmlog_verb(v::hotkey2)("Hotkey action binding remove existing action"); registeredHotkeys_.erase(i); } // if hotkey combination is already bound, remove it @@ -205,14 +202,14 @@ namespace p2c::client::util // signal success return true; } - bool Hotkeys::ClearAction(Action action) + bool Hotkeys::ClearAction(int action) { - pmlog_verb(v::hotkey)("Hotkey action clearing"); + pmlog_verb(v::hotkey2)("Hotkey action clearing"); std::lock_guard lk{ mtx_ }; // if action is bound, remove it if (const auto i = rn::find_if(registeredHotkeys_, [action](const auto& i) { return i.second == action; - }); i != registeredHotkeys_.cend()) { + }); i != registeredHotkeys_.cend()) { registeredHotkeys_.erase(i); } else { diff --git a/IntelPresentMon/AppCef/source/util/HotkeyListener.h b/IntelPresentMon/Core/source/win/HotkeyListener.h similarity index 70% rename from IntelPresentMon/AppCef/source/util/HotkeyListener.h rename to IntelPresentMon/Core/source/win/HotkeyListener.h index 10caf036..1420a71d 100644 --- a/IntelPresentMon/AppCef/source/util/HotkeyListener.h +++ b/IntelPresentMon/Core/source/win/HotkeyListener.h @@ -12,20 +12,19 @@ #include #include #include -#include "../Action.h" -namespace p2c::client::util +namespace p2c::win { class Hotkeys { public: Hotkeys(); - Hotkeys(const Hotkeys&) = delete; - Hotkeys& operator=(const Hotkeys&) = delete; + Hotkeys(const Hotkeys&) = delete; + Hotkeys& operator=(const Hotkeys&) = delete; ~Hotkeys(); - bool BindAction(Action action, win::Key key, win::ModSet mods); - bool ClearAction(Action action); - void SetHandler(std::function handler); + bool BindAction(int action, win::Key key, win::ModSet mods); + bool ClearAction(int action); + void SetHandler(std::function handler); private: // types struct Hotkey @@ -37,10 +36,10 @@ namespace p2c::client::util private: win::Key key; win::ModSet mods; - }; + }; // functions void Kernel_() noexcept; - void DispatchHotkey_(Action action) const; + void DispatchHotkey_(int action) const; win::ModSet GatherModifiers_() const; // data std::binary_semaphore startupSemaphore_{ 0 }; @@ -50,7 +49,7 @@ namespace p2c::client::util std::bitset pressedKeys_; // control access to concurrent memory for key map / handler mutable std::mutex mtx_; - std::function Handler_; - std::map registeredHotkeys_; + std::function Handler_; + std::map registeredHotkeys_; }; } \ No newline at end of file diff --git a/IntelPresentMon/Core/source/win/ModSet.cpp b/IntelPresentMon/Core/source/win/ModSet.cpp index 413254e4..39743310 100644 --- a/IntelPresentMon/Core/source/win/ModSet.cpp +++ b/IntelPresentMon/Core/source/win/ModSet.cpp @@ -140,4 +140,13 @@ namespace p2c::win { return Registry::Get().EnumerateMods(); } + + ModSet ModSet::FromCodes(const std::vector& codes) + { + uint32_t combined = 0; + for (auto& code : codes) { + combined |= code; + } + return { combined }; + } } \ No newline at end of file diff --git a/IntelPresentMon/Core/source/win/ModSet.h b/IntelPresentMon/Core/source/win/ModSet.h index e9150521..07f2db88 100644 --- a/IntelPresentMon/Core/source/win/ModSet.h +++ b/IntelPresentMon/Core/source/win/ModSet.h @@ -30,6 +30,7 @@ namespace p2c::win std::vector GetModStrings(); static std::optional FromString(const std::string& s); static std::optional SingleModFromCode(uint32_t p); + static ModSet FromCodes(const std::vector& codes); static std::vector EnumerateMods(); private: // types diff --git a/IntelPresentMon/KernelProcess/KernelProcess.vcxproj b/IntelPresentMon/KernelProcess/KernelProcess.vcxproj index ea9ca498..eab27aae 100644 --- a/IntelPresentMon/KernelProcess/KernelProcess.vcxproj +++ b/IntelPresentMon/KernelProcess/KernelProcess.vcxproj @@ -158,6 +158,8 @@ + + diff --git a/IntelPresentMon/KernelProcess/KernelProcess.vcxproj.filters b/IntelPresentMon/KernelProcess/KernelProcess.vcxproj.filters index cfbbb7c1..9a313d17 100644 --- a/IntelPresentMon/KernelProcess/KernelProcess.vcxproj.filters +++ b/IntelPresentMon/KernelProcess/KernelProcess.vcxproj.filters @@ -56,6 +56,12 @@ Header Files + + Header Files + + + Header Files + diff --git a/IntelPresentMon/KernelProcess/kact/AllActions.h b/IntelPresentMon/KernelProcess/kact/AllActions.h index f4a4c95a..f3baf7df 100644 --- a/IntelPresentMon/KernelProcess/kact/AllActions.h +++ b/IntelPresentMon/KernelProcess/kact/AllActions.h @@ -4,4 +4,6 @@ #include "SetCapture.h" #include "EnumerateAdapters.h" #include "Introspect.h" -#include "PushSpecification.h" \ No newline at end of file +#include "PushSpecification.h" +#include "BindHotkey.h" +#include "ClearHotkey.h" \ No newline at end of file diff --git a/IntelPresentMon/KernelProcess/kact/BindHotkey.h b/IntelPresentMon/KernelProcess/kact/BindHotkey.h new file mode 100644 index 00000000..ccfcc048 --- /dev/null +++ b/IntelPresentMon/KernelProcess/kact/BindHotkey.h @@ -0,0 +1,60 @@ +#pragma once +#include "../../Interprocess/source/act/ActionHelper.h" +#include "KernelExecutionContext.h" +#include +#include "../../Core/source/kernel/Kernel.h" + +#define ACT_NAME BindHotkey +#define ACT_EXEC_CTX KernelExecutionContext +#define ACT_TYPE AsyncActionBase_ +#define ACT_NS kproc::kact + +namespace ACT_NS +{ + using namespace ::pmon::ipc::act; + + class ACT_NAME : public ACT_TYPE + { + public: + static constexpr const char* Identifier = STRINGIFY(ACT_NAME); + struct Params + { + struct Combination { + uint32_t key; + std::vector modifiers; + template void serialize(A& ar) { + ar(key, modifiers); + } + } combination; + int action; + + template void serialize(A& ar) { + ar(combination, action); + } + }; + struct Response { + template void serialize(A& ar) { + } + }; + private: + friend class ACT_TYPE; + static Response Execute_(const ACT_EXEC_CTX& ctx, SessionContext& stx, Params&& in) + { + ctx.pHotkeys->BindAction(in.action, + p2c::win::Key::Code(in.combination.key), + p2c::win::ModSet::FromCodes(in.combination.modifiers)); + return {}; + } + }; + +#ifdef PM_ASYNC_ACTION_REGISTRATION_ + ACTION_REG(); +#endif +} + +ACTION_TRAITS_DEF(); + +#undef ACT_NAME +#undef ACT_EXEC_CTX +#undef ACT_NS +#undef ACT_TYPE \ No newline at end of file diff --git a/IntelPresentMon/KernelProcess/kact/ClearHotkey.h b/IntelPresentMon/KernelProcess/kact/ClearHotkey.h new file mode 100644 index 00000000..3041ba70 --- /dev/null +++ b/IntelPresentMon/KernelProcess/kact/ClearHotkey.h @@ -0,0 +1,52 @@ +#pragma once +#include "../../Interprocess/source/act/ActionHelper.h" +#include "KernelExecutionContext.h" +#include +#include "../../Core/source/kernel/Kernel.h" + +#define ACT_NAME ClearHotkey +#define ACT_EXEC_CTX KernelExecutionContext +#define ACT_TYPE AsyncActionBase_ +#define ACT_NS kproc::kact + +namespace ACT_NS +{ + using namespace ::pmon::ipc::act; + + class ACT_NAME : public ACT_TYPE + { + public: + static constexpr const char* Identifier = STRINGIFY(ACT_NAME); + + struct Params + { + int action; + + template void serialize(A& ar) { + ar(action); + } + }; + struct Response { + template void serialize(A& ar) { + } + }; + private: + friend class ACT_TYPE; + static Response Execute_(const ACT_EXEC_CTX& ctx, SessionContext& stx, Params&& in) + { + ctx.pHotkeys->ClearAction(in.action); + return {}; + } + }; + +#ifdef PM_ASYNC_ACTION_REGISTRATION_ + ACTION_REG(); +#endif +} + +ACTION_TRAITS_DEF(); + +#undef ACT_NAME +#undef ACT_EXEC_CTX +#undef ACT_NS +#undef ACT_TYPE \ No newline at end of file diff --git a/IntelPresentMon/KernelProcess/kact/KernelExecutionContext.h b/IntelPresentMon/KernelProcess/kact/KernelExecutionContext.h index 995b0d6c..b3abcdca 100644 --- a/IntelPresentMon/KernelProcess/kact/KernelExecutionContext.h +++ b/IntelPresentMon/KernelProcess/kact/KernelExecutionContext.h @@ -9,6 +9,7 @@ #include #include #include "../../Core/source/kernel/Kernel.h" +#include "../../Core/source/win/HotkeyListener.h" namespace kproc::kact { @@ -32,5 +33,6 @@ namespace kproc::kact std::optional responseWriteTimeoutMs; p2c::kern::Kernel** ppKernel = nullptr; + p2c::win::Hotkeys* pHotkeys = nullptr; }; } \ No newline at end of file diff --git a/IntelPresentMon/KernelProcess/winmain.cpp b/IntelPresentMon/KernelProcess/winmain.cpp index b75b1fdf..2c202f5b 100644 --- a/IntelPresentMon/KernelProcess/winmain.cpp +++ b/IntelPresentMon/KernelProcess/winmain.cpp @@ -7,6 +7,7 @@ #include "../AppCef/source/util/cact/OverlayDiedAction.h" #include "../AppCef/source/util/cact/PresentmonInitFailedAction.h" #include "../AppCef/source/util/cact/StalePidAction.h" +#include "../AppCef/source/util/cact/HotkeyFiredAction.h" #include #include #include @@ -149,9 +150,17 @@ int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLi // this pointer serves as a way to set the kernel on the server exec context after the server is created p2c::kern::Kernel* pKernel = nullptr; + // active object that creates a window and sinks raw input messages to listen for hotkey presses + p2c::win::Hotkeys hotkeys; // this server receives a connection from the CEF render process const auto actName = std::format(R"(\\.\pipe\ipm-cef-channel-{})", GetCurrentProcessId()); - KernelServer server{ kact::KernelExecutionContext{.ppKernel = &pKernel }, actName, 1, "D:(A;;GA;;;WD)S:(ML;;NW;;;ME)" }; + KernelServer server{ kact::KernelExecutionContext{ .ppKernel = &pKernel, .pHotkeys = &hotkeys }, + actName, 1, "D:(A;;GA;;;WD)S:(ML;;NW;;;ME)" }; + // set the hotkey manager to send notifications via the action server + hotkeys.SetHandler([&](int action) { + pmlog_info("hey hot"); + server.DispatchDetached(p2c::client::util::cact::HotkeyFiredAction::Params{ .actionId = action }); + }); // this handler receives events from the kernel and transmits them to the render process via the server KernelHandler kernHandler{ server }; // the kernel manages the PresentMon data collection and the overlay rendering