Release OpenScreen 1.4.2
This commit is contained in:
@@ -46,7 +46,7 @@ Build the Windows helper with:
|
||||
npm run build:native:win
|
||||
```
|
||||
|
||||
The build writes the CMake output to `electron/native/wgc-capture/build/wgc-capture.exe` and copies the redistributable binary to `electron/native/bin/win32-x64/wgc-capture.exe`.
|
||||
The build writes the CMake output to `electron/native/wgc-capture/build/wgc-capture.exe` and copies the redistributable binary to `electron/native/bin/win32-x64/wgc-capture.exe`. It also builds `cursor-sampler.exe` for editable cursor telemetry and `guide-hotkey-listener.exe` for the Guide Mode global Ctrl capture hook.
|
||||
|
||||
The helper contract is process-based: the app starts the process with one JSON argument and sends commands on stdin. `stop\n` finalizes the recording. During migration the helper prints both newline-delimited JSON events and the legacy text messages `Recording started` / `Recording stopped. Output path: <path>`.
|
||||
|
||||
|
||||
@@ -65,3 +65,19 @@ target_link_libraries(cursor-sampler PRIVATE
|
||||
gdi32
|
||||
gdiplus
|
||||
)
|
||||
|
||||
add_executable(guide-hotkey-listener
|
||||
src/guide-hotkey-listener.cpp
|
||||
)
|
||||
|
||||
target_compile_definitions(guide-hotkey-listener PRIVATE
|
||||
NOMINMAX
|
||||
WIN32_LEAN_AND_MEAN
|
||||
_WIN32_WINNT=0x0A00
|
||||
)
|
||||
|
||||
target_compile_options(guide-hotkey-listener PRIVATE /EHsc /W4 /utf-8)
|
||||
|
||||
target_link_libraries(guide-hotkey-listener PRIVATE
|
||||
user32
|
||||
)
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
#include <windows.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
#include <iostream>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
|
||||
static HHOOK g_keyboardHook = nullptr;
|
||||
static DWORD g_mainThreadId = 0;
|
||||
static std::atomic<bool> g_ctrlDown{false};
|
||||
static std::mutex g_stdoutMutex;
|
||||
|
||||
static int64_t nowMs() {
|
||||
return static_cast<int64_t>(
|
||||
std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
std::chrono::system_clock::now().time_since_epoch())
|
||||
.count());
|
||||
}
|
||||
|
||||
static void writeJsonLine(const std::string& json) {
|
||||
std::lock_guard<std::mutex> lock(g_stdoutMutex);
|
||||
std::cout << json << '\n';
|
||||
std::cout.flush();
|
||||
}
|
||||
|
||||
static bool isCtrlKey(DWORD vkCode) {
|
||||
return vkCode == VK_CONTROL || vkCode == VK_LCONTROL || vkCode == VK_RCONTROL;
|
||||
}
|
||||
|
||||
static LRESULT CALLBACK LowLevelKeyboardProc(int nCode, WPARAM wParam, LPARAM lParam) {
|
||||
if (nCode >= 0) {
|
||||
const auto* event = reinterpret_cast<KBDLLHOOKSTRUCT*>(lParam);
|
||||
if (event && isCtrlKey(event->vkCode)) {
|
||||
if (wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN) {
|
||||
const bool wasDown = g_ctrlDown.exchange(true, std::memory_order_acq_rel);
|
||||
if (!wasDown) {
|
||||
writeJsonLine(
|
||||
"{\"event\":\"guide-hotkey\",\"key\":\"control\",\"state\":\"down\",\"timeMs\":" +
|
||||
std::to_string(nowMs()) + "}");
|
||||
}
|
||||
} else if (wParam == WM_KEYUP || wParam == WM_SYSKEYUP) {
|
||||
g_ctrlDown.store(false, std::memory_order_release);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return CallNextHookEx(g_keyboardHook, nCode, wParam, lParam);
|
||||
}
|
||||
|
||||
static BOOL WINAPI consoleCtrlHandler(DWORD signal) {
|
||||
if (
|
||||
signal == CTRL_C_EVENT ||
|
||||
signal == CTRL_BREAK_EVENT ||
|
||||
signal == CTRL_CLOSE_EVENT ||
|
||||
signal == CTRL_LOGOFF_EVENT ||
|
||||
signal == CTRL_SHUTDOWN_EVENT
|
||||
) {
|
||||
PostThreadMessage(g_mainThreadId, WM_QUIT, 0, 0);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
int main() {
|
||||
g_mainThreadId = GetCurrentThreadId();
|
||||
SetConsoleCtrlHandler(consoleCtrlHandler, TRUE);
|
||||
|
||||
g_keyboardHook = SetWindowsHookExW(WH_KEYBOARD_LL, LowLevelKeyboardProc, GetModuleHandleW(nullptr), 0);
|
||||
if (!g_keyboardHook) {
|
||||
std::cerr << "Failed to install guide hotkey keyboard hook. error=" << GetLastError() << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
writeJsonLine("{\"event\":\"ready\"}");
|
||||
|
||||
MSG msg{};
|
||||
while (GetMessageW(&msg, nullptr, 0, 0) > 0) {
|
||||
TranslateMessage(&msg);
|
||||
DispatchMessageW(&msg);
|
||||
}
|
||||
|
||||
if (g_keyboardHook) {
|
||||
UnhookWindowsHookEx(g_keyboardHook);
|
||||
g_keyboardHook = nullptr;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -400,6 +400,7 @@ int main(int argc, char* argv[]) {
|
||||
if (config.sourceType == "display") {
|
||||
HMONITOR monitor = findMonitorForCapture(
|
||||
config.displayId,
|
||||
config.sourceId,
|
||||
config.hasDisplayBounds ? &config.bounds : nullptr);
|
||||
if (!monitor) {
|
||||
std::cerr << "ERROR: Could not resolve monitor" << std::endl;
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace {
|
||||
@@ -43,9 +44,36 @@ int64_t overlapArea(const RECT& rect, const MonitorBounds& bounds) {
|
||||
return static_cast<int64_t>(right - left) * static_cast<int64_t>(bottom - top);
|
||||
}
|
||||
|
||||
int parseScreenSourceIndex(const std::string& sourceId) {
|
||||
constexpr char prefix[] = "screen:";
|
||||
if (sourceId.rfind(prefix, 0) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
const size_t start = sizeof(prefix) - 1;
|
||||
const size_t end = sourceId.find(':', start);
|
||||
const std::string indexText = sourceId.substr(
|
||||
start,
|
||||
end == std::string::npos ? std::string::npos : end - start);
|
||||
if (indexText.empty()) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
try {
|
||||
size_t parsed = 0;
|
||||
const int index = std::stoi(indexText, &parsed, 10);
|
||||
return parsed == indexText.size() && index >= 0 ? index : -1;
|
||||
} catch (...) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
HMONITOR findMonitorForCapture(int64_t displayId, const MonitorBounds* bounds) {
|
||||
HMONITOR findMonitorForCapture(
|
||||
int64_t displayId,
|
||||
const std::string& sourceId,
|
||||
const MonitorBounds* bounds) {
|
||||
const auto monitors = enumerateMonitors();
|
||||
if (monitors.empty()) {
|
||||
return MonitorFromPoint({0, 0}, MONITOR_DEFAULTTOPRIMARY);
|
||||
@@ -84,5 +112,10 @@ HMONITOR findMonitorForCapture(int64_t displayId, const MonitorBounds* bounds) {
|
||||
}
|
||||
}
|
||||
|
||||
const int sourceIndex = parseScreenSourceIndex(sourceId);
|
||||
if (sourceIndex >= 0 && static_cast<size_t>(sourceIndex) < monitors.size()) {
|
||||
return monitors[static_cast<size_t>(sourceIndex)].monitor;
|
||||
}
|
||||
|
||||
return MonitorFromPoint({0, 0}, MONITOR_DEFAULTTOPRIMARY);
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <Windows.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
struct MonitorBounds {
|
||||
int x = 0;
|
||||
@@ -11,4 +12,7 @@ struct MonitorBounds {
|
||||
int height = 0;
|
||||
};
|
||||
|
||||
HMONITOR findMonitorForCapture(int64_t displayId, const MonitorBounds* bounds);
|
||||
HMONITOR findMonitorForCapture(
|
||||
int64_t displayId,
|
||||
const std::string& sourceId,
|
||||
const MonitorBounds* bounds);
|
||||
|
||||
Reference in New Issue
Block a user