mirror of
https://github.com/kunkundi/crossdesk.git
synced 2026-08-04 16:50:31 +08:00
[feat] improve secure desktop capture by streaming latest frames through shared memory
This commit is contained in:
@@ -33,7 +33,9 @@ constexpr DWORD kSecureDesktopStatusPipeTimeoutMs = 150;
|
|||||||
constexpr DWORD kSecureDesktopHelperPipeTimeoutMs = 120;
|
constexpr DWORD kSecureDesktopHelperPipeTimeoutMs = 120;
|
||||||
constexpr DWORD kSecureDesktopTransientErrorGraceMs = 1500;
|
constexpr DWORD kSecureDesktopTransientErrorGraceMs = 1500;
|
||||||
constexpr DWORD kSecureDesktopTransientErrorLogIntervalMs = 5000;
|
constexpr DWORD kSecureDesktopTransientErrorLogIntervalMs = 5000;
|
||||||
constexpr int kSecureDesktopCaptureMinIntervalMs = 100;
|
constexpr int kSecureDesktopCaptureMinFps = 30;
|
||||||
|
constexpr int kSecureDesktopCaptureMaxIntervalMs =
|
||||||
|
1000 / kSecureDesktopCaptureMinFps;
|
||||||
|
|
||||||
struct SecureDesktopServiceStatus {
|
struct SecureDesktopServiceStatus {
|
||||||
bool service_available = false;
|
bool service_available = false;
|
||||||
@@ -136,6 +138,16 @@ std::string BuildSecureCaptureCommand(int left, int top, int width, int height,
|
|||||||
return stream.str();
|
return stream.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string BuildSecureCaptureStartCommand(int left, int top, int width,
|
||||||
|
int height, bool show_cursor,
|
||||||
|
int fps) {
|
||||||
|
std::ostringstream stream;
|
||||||
|
stream << kCrossDeskSecureInputCaptureStartCommandPrefix << left << ":" << top
|
||||||
|
<< ":" << width << ":" << height << ":" << (show_cursor ? 1 : 0)
|
||||||
|
<< ":" << fps;
|
||||||
|
return stream.str();
|
||||||
|
}
|
||||||
|
|
||||||
std::string ExtractPipeTextResponse(const std::vector<uint8_t>& response) {
|
std::string ExtractPipeTextResponse(const std::vector<uint8_t>& response) {
|
||||||
if (response.empty() || response.front() != '{') {
|
if (response.empty() || response.front() != '{') {
|
||||||
return "<non-text-response>";
|
return "<non-text-response>";
|
||||||
@@ -274,17 +286,15 @@ bool QuerySecureDesktopServiceStatus(SecureDesktopServiceStatus* status) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool QuerySecureDesktopHelperFrame(DWORD session_id, int left, int top,
|
bool QuerySecureDesktopHelperCommand(DWORD session_id,
|
||||||
int width, int height, bool show_cursor,
|
const std::string& command,
|
||||||
std::vector<uint8_t>* nv12_frame_out,
|
std::vector<uint8_t>* response_out,
|
||||||
int* captured_width_out,
|
|
||||||
int* captured_height_out,
|
|
||||||
std::string* error_out) {
|
std::string* error_out) {
|
||||||
if (nv12_frame_out == nullptr || captured_width_out == nullptr ||
|
if (response_out == nullptr) {
|
||||||
captured_height_out == nullptr) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
response_out->clear();
|
||||||
const std::wstring pipe_name =
|
const std::wstring pipe_name =
|
||||||
GetCrossDeskSecureInputHelperPipeName(session_id);
|
GetCrossDeskSecureInputHelperPipeName(session_id);
|
||||||
if (!WaitNamedPipeW(pipe_name.c_str(), kSecureDesktopHelperPipeTimeoutMs)) {
|
if (!WaitNamedPipeW(pipe_name.c_str(), kSecureDesktopHelperPipeTimeoutMs)) {
|
||||||
@@ -306,8 +316,6 @@ bool QuerySecureDesktopHelperFrame(DWORD session_id, int left, int top,
|
|||||||
DWORD pipe_mode = PIPE_READMODE_MESSAGE;
|
DWORD pipe_mode = PIPE_READMODE_MESSAGE;
|
||||||
SetNamedPipeHandleState(pipe, &pipe_mode, nullptr, nullptr);
|
SetNamedPipeHandleState(pipe, &pipe_mode, nullptr, nullptr);
|
||||||
|
|
||||||
const std::string command =
|
|
||||||
BuildSecureCaptureCommand(left, top, width, height, show_cursor);
|
|
||||||
DWORD bytes_written = 0;
|
DWORD bytes_written = 0;
|
||||||
if (!WriteFile(pipe, command.data(), static_cast<DWORD>(command.size()),
|
if (!WriteFile(pipe, command.data(), static_cast<DWORD>(command.size()),
|
||||||
&bytes_written, nullptr)) {
|
&bytes_written, nullptr)) {
|
||||||
@@ -319,9 +327,8 @@ bool QuerySecureDesktopHelperFrame(DWORD session_id, int left, int top,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<uint8_t> response;
|
|
||||||
DWORD read_error = 0;
|
DWORD read_error = 0;
|
||||||
const bool read_ok = ReadPipeMessage(pipe, &response, &read_error);
|
const bool read_ok = ReadPipeMessage(pipe, response_out, &read_error);
|
||||||
CloseHandle(pipe);
|
CloseHandle(pipe);
|
||||||
if (!read_ok) {
|
if (!read_ok) {
|
||||||
if (error_out != nullptr) {
|
if (error_out != nullptr) {
|
||||||
@@ -330,6 +337,28 @@ bool QuerySecureDesktopHelperFrame(DWORD session_id, int left, int top,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool QuerySecureDesktopHelperFrame(DWORD session_id, int left, int top,
|
||||||
|
int width, int height, bool show_cursor,
|
||||||
|
std::vector<uint8_t>* nv12_frame_out,
|
||||||
|
int* captured_width_out,
|
||||||
|
int* captured_height_out,
|
||||||
|
std::string* error_out) {
|
||||||
|
if (nv12_frame_out == nullptr || captured_width_out == nullptr ||
|
||||||
|
captured_height_out == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string command =
|
||||||
|
BuildSecureCaptureCommand(left, top, width, height, show_cursor);
|
||||||
|
std::vector<uint8_t> response;
|
||||||
|
if (!QuerySecureDesktopHelperCommand(session_id, command, &response,
|
||||||
|
error_out)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return ParseSecureDesktopFrameResponse(response, nv12_frame_out,
|
return ParseSecureDesktopFrameResponse(response, nv12_frame_out,
|
||||||
captured_width_out,
|
captured_width_out,
|
||||||
captured_height_out, error_out);
|
captured_height_out, error_out);
|
||||||
@@ -496,6 +525,7 @@ int ScreenCapturerWin::Stop() {
|
|||||||
ret = impl_->Stop();
|
ret = impl_->Stop();
|
||||||
}
|
}
|
||||||
StopSecureCaptureThread();
|
StopSecureCaptureThread();
|
||||||
|
StopSecureDesktopSharedCapture(secure_shared_session_id_);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -616,10 +646,234 @@ bool ScreenCapturerWin::GetCurrentCaptureRegion(int* left, int* top, int* width,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ScreenCapturerWin::CloseSecureDesktopSharedFrame() {
|
||||||
|
if (secure_frame_view_ != nullptr) {
|
||||||
|
UnmapViewOfFile(secure_frame_view_);
|
||||||
|
secure_frame_view_ = nullptr;
|
||||||
|
}
|
||||||
|
if (secure_frame_ready_event_ != nullptr) {
|
||||||
|
CloseHandle(secure_frame_ready_event_);
|
||||||
|
secure_frame_ready_event_ = nullptr;
|
||||||
|
}
|
||||||
|
if (secure_frame_mapping_ != nullptr) {
|
||||||
|
CloseHandle(secure_frame_mapping_);
|
||||||
|
secure_frame_mapping_ = nullptr;
|
||||||
|
}
|
||||||
|
secure_frame_view_size_ = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScreenCapturerWin::StopSecureDesktopSharedCapture(DWORD session_id) {
|
||||||
|
DWORD target_session_id = session_id;
|
||||||
|
if (target_session_id == 0xFFFFFFFF) {
|
||||||
|
target_session_id = secure_shared_session_id_;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (secure_shared_capture_started_ &&
|
||||||
|
target_session_id != 0xFFFFFFFF) {
|
||||||
|
std::vector<uint8_t> response;
|
||||||
|
std::string error_message;
|
||||||
|
QuerySecureDesktopHelperCommand(
|
||||||
|
target_session_id, kCrossDeskSecureInputCaptureStopCommand, &response,
|
||||||
|
&error_message);
|
||||||
|
}
|
||||||
|
|
||||||
|
CloseSecureDesktopSharedFrame();
|
||||||
|
secure_shared_capture_started_ = false;
|
||||||
|
secure_shared_session_id_ = 0xFFFFFFFF;
|
||||||
|
secure_shared_left_ = 0;
|
||||||
|
secure_shared_top_ = 0;
|
||||||
|
secure_shared_width_ = 0;
|
||||||
|
secure_shared_height_ = 0;
|
||||||
|
secure_shared_fps_ = 0;
|
||||||
|
secure_shared_show_cursor_ = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ScreenCapturerWin::OpenSecureDesktopSharedFrame(DWORD session_id,
|
||||||
|
size_t min_size,
|
||||||
|
std::string* error_out) {
|
||||||
|
if (secure_frame_view_ != nullptr &&
|
||||||
|
secure_shared_session_id_ == session_id &&
|
||||||
|
secure_frame_view_size_ >= min_size) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
CloseSecureDesktopSharedFrame();
|
||||||
|
|
||||||
|
const std::wstring mapping_name =
|
||||||
|
GetCrossDeskSecureDesktopFrameMappingName(session_id);
|
||||||
|
HANDLE frame_mapping =
|
||||||
|
OpenFileMappingW(FILE_MAP_READ, FALSE, mapping_name.c_str());
|
||||||
|
if (frame_mapping == nullptr) {
|
||||||
|
if (error_out != nullptr) {
|
||||||
|
*error_out = "open_frame_mapping_failed:" +
|
||||||
|
std::to_string(GetLastError());
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto* frame_view =
|
||||||
|
static_cast<uint8_t*>(MapViewOfFile(frame_mapping, FILE_MAP_READ, 0, 0, 0));
|
||||||
|
if (frame_view == nullptr) {
|
||||||
|
const DWORD error = GetLastError();
|
||||||
|
CloseHandle(frame_mapping);
|
||||||
|
if (error_out != nullptr) {
|
||||||
|
*error_out = "map_frame_view_failed:" + std::to_string(error);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::wstring event_name =
|
||||||
|
GetCrossDeskSecureDesktopFrameReadyEventName(session_id);
|
||||||
|
HANDLE frame_ready_event =
|
||||||
|
OpenEventW(SYNCHRONIZE, FALSE, event_name.c_str());
|
||||||
|
if (frame_ready_event == nullptr) {
|
||||||
|
const DWORD error = GetLastError();
|
||||||
|
UnmapViewOfFile(frame_view);
|
||||||
|
CloseHandle(frame_mapping);
|
||||||
|
if (error_out != nullptr) {
|
||||||
|
*error_out = "open_frame_event_failed:" + std::to_string(error);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
secure_frame_mapping_ = frame_mapping;
|
||||||
|
secure_frame_ready_event_ = frame_ready_event;
|
||||||
|
secure_frame_view_ = frame_view;
|
||||||
|
secure_frame_view_size_ = min_size;
|
||||||
|
secure_shared_session_id_ = session_id;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ScreenCapturerWin::ReadSecureDesktopSharedFrame(
|
||||||
|
DWORD wait_ms, std::vector<uint8_t>* nv12_frame_out, int* width_out,
|
||||||
|
int* height_out, std::string* error_out) {
|
||||||
|
if (nv12_frame_out == nullptr || width_out == nullptr ||
|
||||||
|
height_out == nullptr || secure_frame_view_ == nullptr ||
|
||||||
|
secure_frame_ready_event_ == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const DWORD wait_result = WaitForSingleObject(secure_frame_ready_event_,
|
||||||
|
wait_ms);
|
||||||
|
if (wait_result == WAIT_TIMEOUT) {
|
||||||
|
if (error_out != nullptr) {
|
||||||
|
*error_out = "frame_wait_timeout";
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (wait_result != WAIT_OBJECT_0) {
|
||||||
|
if (error_out != nullptr) {
|
||||||
|
*error_out = "frame_wait_failed:" + std::to_string(GetLastError());
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto* header =
|
||||||
|
reinterpret_cast<CrossDeskSecureDesktopSharedFrameHeader*>(
|
||||||
|
secure_frame_view_);
|
||||||
|
if (header->magic != kCrossDeskSecureDesktopFrameMagic ||
|
||||||
|
header->version != kCrossDeskSecureDesktopFrameVersion) {
|
||||||
|
if (error_out != nullptr) {
|
||||||
|
*error_out = "invalid_shared_frame_header";
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (header->writing != 0) {
|
||||||
|
if (error_out != nullptr) {
|
||||||
|
*error_out = "shared_frame_write_in_progress";
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint32_t sequence = header->sequence;
|
||||||
|
const uint32_t payload_size = header->payload_size;
|
||||||
|
const uint32_t buffer_size = header->buffer_size;
|
||||||
|
if (payload_size == 0 || payload_size > buffer_size ||
|
||||||
|
sizeof(*header) + static_cast<size_t>(payload_size) >
|
||||||
|
secure_frame_view_size_) {
|
||||||
|
if (error_out != nullptr) {
|
||||||
|
*error_out = "invalid_shared_frame_size";
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
nv12_frame_out->resize(payload_size);
|
||||||
|
std::memcpy(nv12_frame_out->data(), secure_frame_view_ + sizeof(*header),
|
||||||
|
payload_size);
|
||||||
|
MemoryBarrier();
|
||||||
|
if (header->writing != 0 || header->sequence != sequence) {
|
||||||
|
if (error_out != nullptr) {
|
||||||
|
*error_out = "shared_frame_changed_during_read";
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
*width_out = static_cast<int>(header->width);
|
||||||
|
*height_out = static_cast<int>(header->height);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ScreenCapturerWin::StartSecureDesktopSharedCapture(
|
||||||
|
DWORD session_id, int left, int top, int width, int height,
|
||||||
|
bool show_cursor, int fps, std::string* error_out) {
|
||||||
|
const size_t payload_size = static_cast<size_t>(width) * height * 3 / 2;
|
||||||
|
const size_t mapping_size =
|
||||||
|
sizeof(CrossDeskSecureDesktopSharedFrameHeader) + payload_size;
|
||||||
|
if (payload_size == 0) {
|
||||||
|
if (error_out != nullptr) {
|
||||||
|
*error_out = "invalid_capture_size";
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (secure_shared_capture_started_ &&
|
||||||
|
secure_shared_session_id_ == session_id &&
|
||||||
|
secure_shared_left_ == left && secure_shared_top_ == top &&
|
||||||
|
secure_shared_width_ == width && secure_shared_height_ == height &&
|
||||||
|
secure_shared_show_cursor_ == show_cursor && secure_shared_fps_ == fps &&
|
||||||
|
OpenSecureDesktopSharedFrame(session_id, mapping_size, error_out)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
StopSecureDesktopSharedCapture(secure_shared_session_id_);
|
||||||
|
|
||||||
|
const std::string command =
|
||||||
|
BuildSecureCaptureStartCommand(left, top, width, height, show_cursor, fps);
|
||||||
|
std::vector<uint8_t> response;
|
||||||
|
if (!QuerySecureDesktopHelperCommand(session_id, command, &response,
|
||||||
|
error_out)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Json json = Json::parse(response.begin(), response.end(), nullptr, false);
|
||||||
|
if (json.is_discarded() || !json.value("ok", false)) {
|
||||||
|
if (error_out != nullptr) {
|
||||||
|
*error_out = ExtractPipeTextResponse(response);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
secure_shared_capture_started_ = true;
|
||||||
|
secure_shared_session_id_ = session_id;
|
||||||
|
secure_shared_left_ = left;
|
||||||
|
secure_shared_top_ = top;
|
||||||
|
secure_shared_width_ = width;
|
||||||
|
secure_shared_height_ = height;
|
||||||
|
secure_shared_show_cursor_ = show_cursor;
|
||||||
|
secure_shared_fps_ = fps;
|
||||||
|
|
||||||
|
if (!OpenSecureDesktopSharedFrame(session_id, mapping_size, error_out)) {
|
||||||
|
StopSecureDesktopSharedCapture(session_id);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void ScreenCapturerWin::SecureDesktopCaptureLoop() {
|
void ScreenCapturerWin::SecureDesktopCaptureLoop() {
|
||||||
const int frame_interval_ms =
|
const int frame_interval_ms =
|
||||||
fps_ > 0 ? (std::max)(kSecureDesktopCaptureMinIntervalMs, 1000 / fps_)
|
fps_ > 0 ? (std::min)(kSecureDesktopCaptureMaxIntervalMs, 1000 / fps_)
|
||||||
: kSecureDesktopCaptureMinIntervalMs;
|
: kSecureDesktopCaptureMaxIntervalMs;
|
||||||
ULONGLONG last_status_tick = 0;
|
ULONGLONG last_status_tick = 0;
|
||||||
ULONGLONG last_error_tick = 0;
|
ULONGLONG last_error_tick = 0;
|
||||||
bool last_capture_active = false;
|
bool last_capture_active = false;
|
||||||
@@ -686,12 +940,14 @@ void ScreenCapturerWin::SecureDesktopCaptureLoop() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!status.capture_active || status.active_session_id == 0xFFFFFFFF) {
|
if (!status.capture_active || status.active_session_id == 0xFFFFFFFF) {
|
||||||
|
StopSecureDesktopSharedCapture(secure_shared_session_id_);
|
||||||
std::this_thread::sleep_for(
|
std::this_thread::sleep_for(
|
||||||
std::chrono::milliseconds(status.service_available ? 50 : 200));
|
std::chrono::milliseconds(status.service_available ? 50 : 200));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!status.helper_running) {
|
if (!status.helper_running) {
|
||||||
|
StopSecureDesktopSharedCapture(secure_shared_session_id_);
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(30));
|
std::this_thread::sleep_for(std::chrono::milliseconds(30));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -702,6 +958,7 @@ void ScreenCapturerWin::SecureDesktopCaptureLoop() {
|
|||||||
int height = 0;
|
int height = 0;
|
||||||
std::string display_name;
|
std::string display_name;
|
||||||
if (!GetCurrentCaptureRegion(&left, &top, &width, &height, &display_name)) {
|
if (!GetCurrentCaptureRegion(&left, &top, &width, &height, &display_name)) {
|
||||||
|
StopSecureDesktopSharedCapture(secure_shared_session_id_);
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -709,15 +966,38 @@ void ScreenCapturerWin::SecureDesktopCaptureLoop() {
|
|||||||
int captured_width = 0;
|
int captured_width = 0;
|
||||||
int captured_height = 0;
|
int captured_height = 0;
|
||||||
std::string error_message;
|
std::string error_message;
|
||||||
if (QuerySecureDesktopHelperFrame(
|
bool frame_delivered = false;
|
||||||
status.active_session_id, left, top, width, height,
|
const bool show_cursor = show_cursor_.load(std::memory_order_relaxed);
|
||||||
show_cursor_.load(std::memory_order_relaxed), &secure_frame,
|
const int shared_fps =
|
||||||
|
fps_ > 0 ? (std::max)(kSecureDesktopCaptureMinFps, fps_)
|
||||||
|
: kSecureDesktopCaptureMinFps;
|
||||||
|
|
||||||
|
if (StartSecureDesktopSharedCapture(status.active_session_id, left, top,
|
||||||
|
width, height, show_cursor, shared_fps,
|
||||||
|
&error_message) &&
|
||||||
|
ReadSecureDesktopSharedFrame(
|
||||||
|
static_cast<DWORD>(frame_interval_ms + 20), &secure_frame,
|
||||||
&captured_width, &captured_height, &error_message)) {
|
&captured_width, &captured_height, &error_message)) {
|
||||||
if (cb_orig_ && !secure_frame.empty()) {
|
if (cb_orig_ && !secure_frame.empty()) {
|
||||||
cb_orig_(secure_frame.data(), static_cast<int>(secure_frame.size()),
|
cb_orig_(secure_frame.data(), static_cast<int>(secure_frame.size()),
|
||||||
captured_width, captured_height, display_name.c_str());
|
captured_width, captured_height, display_name.c_str());
|
||||||
}
|
}
|
||||||
} else {
|
frame_delivered = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!frame_delivered &&
|
||||||
|
QuerySecureDesktopHelperFrame(status.active_session_id, left, top,
|
||||||
|
width, height, show_cursor,
|
||||||
|
&secure_frame, &captured_width,
|
||||||
|
&captured_height, &error_message)) {
|
||||||
|
if (cb_orig_ && !secure_frame.empty()) {
|
||||||
|
cb_orig_(secure_frame.data(), static_cast<int>(secure_frame.size()),
|
||||||
|
captured_width, captured_height, display_name.c_str());
|
||||||
|
}
|
||||||
|
frame_delivered = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!frame_delivered) {
|
||||||
const bool transient_error =
|
const bool transient_error =
|
||||||
IsTransientSecureDesktopFrameError(error_message);
|
IsTransientSecureDesktopFrameError(error_message);
|
||||||
const bool in_grace_period = capture_stage_started_tick != 0 &&
|
const bool in_grace_period = capture_stage_started_tick != 0 &&
|
||||||
@@ -742,6 +1022,7 @@ void ScreenCapturerWin::SecureDesktopCaptureLoop() {
|
|||||||
std::this_thread::sleep_for(std::chrono::milliseconds(frame_interval_ms));
|
std::this_thread::sleep_for(std::chrono::milliseconds(frame_interval_ms));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
StopSecureDesktopSharedCapture(secure_shared_session_id_);
|
||||||
secure_desktop_capture_active_.store(false, std::memory_order_relaxed);
|
secure_desktop_capture_active_.store(false, std::memory_order_relaxed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
#include <Windows.h>
|
#include <Windows.h>
|
||||||
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
|
#include <cstdint>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
@@ -59,6 +60,18 @@ class ScreenCapturerWin : public ScreenCapturer {
|
|||||||
int initial_monitor_index_ = 0;
|
int initial_monitor_index_ = 0;
|
||||||
std::atomic<bool> secure_desktop_capture_active_{false};
|
std::atomic<bool> secure_desktop_capture_active_{false};
|
||||||
std::thread secure_capture_thread_;
|
std::thread secure_capture_thread_;
|
||||||
|
HANDLE secure_frame_mapping_ = nullptr;
|
||||||
|
HANDLE secure_frame_ready_event_ = nullptr;
|
||||||
|
uint8_t* secure_frame_view_ = nullptr;
|
||||||
|
size_t secure_frame_view_size_ = 0;
|
||||||
|
DWORD secure_shared_session_id_ = 0xFFFFFFFF;
|
||||||
|
int secure_shared_left_ = 0;
|
||||||
|
int secure_shared_top_ = 0;
|
||||||
|
int secure_shared_width_ = 0;
|
||||||
|
int secure_shared_height_ = 0;
|
||||||
|
int secure_shared_fps_ = 0;
|
||||||
|
bool secure_shared_show_cursor_ = true;
|
||||||
|
bool secure_shared_capture_started_ = false;
|
||||||
|
|
||||||
void BuildCanonicalFromImpl();
|
void BuildCanonicalFromImpl();
|
||||||
void RebuildAliasesFromImpl();
|
void RebuildAliasesFromImpl();
|
||||||
@@ -66,6 +79,18 @@ class ScreenCapturerWin : public ScreenCapturer {
|
|||||||
void SecureDesktopCaptureLoop();
|
void SecureDesktopCaptureLoop();
|
||||||
bool GetCurrentCaptureRegion(int* left, int* top, int* width, int* height,
|
bool GetCurrentCaptureRegion(int* left, int* top, int* width, int* height,
|
||||||
std::string* display_name);
|
std::string* display_name);
|
||||||
|
bool StartSecureDesktopSharedCapture(DWORD session_id, int left, int top,
|
||||||
|
int width, int height,
|
||||||
|
bool show_cursor, int fps,
|
||||||
|
std::string* error_out);
|
||||||
|
void StopSecureDesktopSharedCapture(DWORD session_id);
|
||||||
|
bool OpenSecureDesktopSharedFrame(DWORD session_id, size_t min_size,
|
||||||
|
std::string* error_out);
|
||||||
|
bool ReadSecureDesktopSharedFrame(DWORD wait_ms,
|
||||||
|
std::vector<uint8_t>* nv12_frame_out,
|
||||||
|
int* width_out, int* height_out,
|
||||||
|
std::string* error_out);
|
||||||
|
void CloseSecureDesktopSharedFrame();
|
||||||
};
|
};
|
||||||
} // namespace crossdesk
|
} // namespace crossdesk
|
||||||
#endif
|
#endif
|
||||||
@@ -6,9 +6,13 @@
|
|||||||
#include <libyuv.h>
|
#include <libyuv.h>
|
||||||
#include <sddl.h>
|
#include <sddl.h>
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <atomic>
|
||||||
|
#include <chrono>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <memory>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <nlohmann/json.hpp>
|
#include <nlohmann/json.hpp>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
@@ -53,6 +57,7 @@ struct SecureCaptureRequest {
|
|||||||
int width = 0;
|
int width = 0;
|
||||||
int height = 0;
|
int height = 0;
|
||||||
bool show_cursor = true;
|
bool show_cursor = true;
|
||||||
|
int fps = 30;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct SecureMouseRequest {
|
struct SecureMouseRequest {
|
||||||
@@ -66,6 +71,19 @@ struct SecureCaptureBuffers {
|
|||||||
std::vector<uint8_t> nv12_frame;
|
std::vector<uint8_t> nv12_frame;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct SecureSharedCaptureState {
|
||||||
|
std::mutex mutex;
|
||||||
|
std::thread capture_thread;
|
||||||
|
std::atomic<bool> stop_requested{false};
|
||||||
|
DWORD session_id = 0xFFFFFFFF;
|
||||||
|
SecureCaptureRequest request;
|
||||||
|
HANDLE frame_mapping = nullptr;
|
||||||
|
HANDLE frame_ready_event = nullptr;
|
||||||
|
uint8_t* frame_view = nullptr;
|
||||||
|
size_t frame_view_size = 0;
|
||||||
|
uint32_t sequence = 0;
|
||||||
|
};
|
||||||
|
|
||||||
struct PipeSecurityAttributes {
|
struct PipeSecurityAttributes {
|
||||||
PipeSecurityAttributes() = default;
|
PipeSecurityAttributes() = default;
|
||||||
~PipeSecurityAttributes() {
|
~PipeSecurityAttributes() {
|
||||||
@@ -718,6 +736,48 @@ bool ParseSecureInputCaptureCommand(const std::string& command,
|
|||||||
return request_out->width > 0 && request_out->height > 0;
|
return request_out->width > 0 && request_out->height > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ParseSecureInputCaptureStartCommand(const std::string& command,
|
||||||
|
SecureCaptureRequest* request_out) {
|
||||||
|
if (request_out == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (command.rfind(
|
||||||
|
crossdesk::kCrossDeskSecureInputCaptureStartCommandPrefix, 0) != 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const size_t values_begin = std::strlen(
|
||||||
|
crossdesk::kCrossDeskSecureInputCaptureStartCommandPrefix);
|
||||||
|
int parsed_values[6] = {0};
|
||||||
|
size_t token_begin = values_begin;
|
||||||
|
for (int index = 0; index < 6; ++index) {
|
||||||
|
const size_t separator = command.find(':', token_begin);
|
||||||
|
const bool is_last = index == 5;
|
||||||
|
const size_t token_end = is_last ? command.size() : separator;
|
||||||
|
if (token_end == std::string::npos || token_end <= token_begin) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
parsed_values[index] =
|
||||||
|
std::stoi(command.substr(token_begin, token_end - token_begin));
|
||||||
|
} catch (...) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
token_begin = token_end + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
request_out->left = parsed_values[0];
|
||||||
|
request_out->top = parsed_values[1];
|
||||||
|
request_out->width = parsed_values[2] & ~1;
|
||||||
|
request_out->height = parsed_values[3] & ~1;
|
||||||
|
request_out->show_cursor = parsed_values[4] != 0;
|
||||||
|
request_out->fps = parsed_values[5] > 0 ? parsed_values[5] : 30;
|
||||||
|
return request_out->width > 0 && request_out->height > 0;
|
||||||
|
}
|
||||||
|
|
||||||
int InjectMouseInput(const SecureMouseRequest& request) {
|
int InjectMouseInput(const SecureMouseRequest& request) {
|
||||||
SetCursorPos(request.x, request.y);
|
SetCursorPos(request.x, request.y);
|
||||||
|
|
||||||
@@ -874,12 +934,292 @@ std::vector<uint8_t> CaptureSecureDesktopFrame(
|
|||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CloseSecureDesktopSharedCaptureResourcesLocked(
|
||||||
|
SecureSharedCaptureState* capture_state) {
|
||||||
|
if (capture_state == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (capture_state->frame_view != nullptr) {
|
||||||
|
UnmapViewOfFile(capture_state->frame_view);
|
||||||
|
capture_state->frame_view = nullptr;
|
||||||
|
}
|
||||||
|
if (capture_state->frame_ready_event != nullptr) {
|
||||||
|
CloseHandle(capture_state->frame_ready_event);
|
||||||
|
capture_state->frame_ready_event = nullptr;
|
||||||
|
}
|
||||||
|
if (capture_state->frame_mapping != nullptr) {
|
||||||
|
CloseHandle(capture_state->frame_mapping);
|
||||||
|
capture_state->frame_mapping = nullptr;
|
||||||
|
}
|
||||||
|
capture_state->frame_view_size = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SecureDesktopSharedCaptureThread(
|
||||||
|
SecureSharedCaptureState* capture_state) {
|
||||||
|
if (capture_state == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SecureCaptureRequest request;
|
||||||
|
uint8_t* frame_view = nullptr;
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(capture_state->mutex);
|
||||||
|
request = capture_state->request;
|
||||||
|
frame_view = capture_state->frame_view;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (frame_view == nullptr || request.width <= 0 || request.height <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const int interval_ms =
|
||||||
|
request.fps > 0 ? (std::max)(1, 1000 / request.fps) : 33;
|
||||||
|
const size_t nv12_size =
|
||||||
|
static_cast<size_t>(request.width) * request.height * 3 / 2;
|
||||||
|
std::vector<uint8_t> nv12_frame(nv12_size);
|
||||||
|
|
||||||
|
HDC screen_dc = GetDC(nullptr);
|
||||||
|
if (screen_dc == nullptr) {
|
||||||
|
LOG_ERROR("Secure shared capture GetDC failed, error={}", GetLastError());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
HDC mem_dc = CreateCompatibleDC(screen_dc);
|
||||||
|
if (mem_dc == nullptr) {
|
||||||
|
LOG_ERROR("Secure shared capture CreateCompatibleDC failed, error={}",
|
||||||
|
GetLastError());
|
||||||
|
ReleaseDC(nullptr, screen_dc);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
BITMAPINFO bmi{};
|
||||||
|
bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
|
||||||
|
bmi.bmiHeader.biWidth = request.width;
|
||||||
|
bmi.bmiHeader.biHeight = -request.height;
|
||||||
|
bmi.bmiHeader.biPlanes = 1;
|
||||||
|
bmi.bmiHeader.biBitCount = 32;
|
||||||
|
bmi.bmiHeader.biCompression = BI_RGB;
|
||||||
|
|
||||||
|
void* bits = nullptr;
|
||||||
|
HBITMAP dib =
|
||||||
|
CreateDIBSection(mem_dc, &bmi, DIB_RGB_COLORS, &bits, nullptr, 0);
|
||||||
|
if (dib == nullptr || bits == nullptr) {
|
||||||
|
LOG_ERROR("Secure shared capture CreateDIBSection failed, error={}",
|
||||||
|
GetLastError());
|
||||||
|
DeleteDC(mem_dc);
|
||||||
|
ReleaseDC(nullptr, screen_dc);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
HGDIOBJ old_bitmap = SelectObject(mem_dc, dib);
|
||||||
|
while (!capture_state->stop_requested.load(std::memory_order_relaxed)) {
|
||||||
|
const auto frame_started = std::chrono::steady_clock::now();
|
||||||
|
if (BitBlt(mem_dc, 0, 0, request.width, request.height, screen_dc,
|
||||||
|
request.left, request.top, SRCCOPY | CAPTUREBLT)) {
|
||||||
|
if (request.show_cursor) {
|
||||||
|
CURSORINFO cursor_info{};
|
||||||
|
cursor_info.cbSize = sizeof(CURSORINFO);
|
||||||
|
if (GetCursorInfo(&cursor_info) &&
|
||||||
|
cursor_info.flags == CURSOR_SHOWING &&
|
||||||
|
cursor_info.hCursor != nullptr) {
|
||||||
|
const int cursor_x = cursor_info.ptScreenPos.x - request.left;
|
||||||
|
const int cursor_y = cursor_info.ptScreenPos.y - request.top;
|
||||||
|
if (cursor_x >= -64 && cursor_y >= -64 &&
|
||||||
|
cursor_x < request.width + 64 &&
|
||||||
|
cursor_y < request.height + 64) {
|
||||||
|
DrawIconEx(mem_dc, cursor_x, cursor_y, cursor_info.hCursor, 0, 0,
|
||||||
|
0, nullptr, DI_NORMAL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const int convert_result = libyuv::ARGBToNV12(
|
||||||
|
static_cast<const uint8_t*>(bits), request.width * 4,
|
||||||
|
nv12_frame.data(), request.width,
|
||||||
|
nv12_frame.data() + request.width * request.height, request.width,
|
||||||
|
request.width, request.height);
|
||||||
|
if (convert_result == 0) {
|
||||||
|
auto* header =
|
||||||
|
reinterpret_cast<crossdesk::CrossDeskSecureDesktopSharedFrameHeader*>(
|
||||||
|
frame_view);
|
||||||
|
uint8_t* payload = frame_view + sizeof(*header);
|
||||||
|
header->writing = 1;
|
||||||
|
MemoryBarrier();
|
||||||
|
header->magic = crossdesk::kCrossDeskSecureDesktopFrameMagic;
|
||||||
|
header->version = crossdesk::kCrossDeskSecureDesktopFrameVersion;
|
||||||
|
header->left = request.left;
|
||||||
|
header->top = request.top;
|
||||||
|
header->width = static_cast<uint32_t>(request.width);
|
||||||
|
header->height = static_cast<uint32_t>(request.height);
|
||||||
|
header->payload_size = static_cast<uint32_t>(nv12_frame.size());
|
||||||
|
std::memcpy(payload, nv12_frame.data(), nv12_frame.size());
|
||||||
|
header->sequence = ++capture_state->sequence;
|
||||||
|
MemoryBarrier();
|
||||||
|
header->writing = 0;
|
||||||
|
SetEvent(capture_state->frame_ready_event);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
LOG_WARN("Secure shared capture BitBlt failed, error={}", GetLastError());
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto elapsed_ms =
|
||||||
|
std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||||
|
std::chrono::steady_clock::now() - frame_started)
|
||||||
|
.count();
|
||||||
|
if (elapsed_ms < interval_ms) {
|
||||||
|
std::this_thread::sleep_for(
|
||||||
|
std::chrono::milliseconds(interval_ms - elapsed_ms));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SelectObject(mem_dc, old_bitmap);
|
||||||
|
DeleteObject(dib);
|
||||||
|
DeleteDC(mem_dc);
|
||||||
|
ReleaseDC(nullptr, screen_dc);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<uint8_t> StopSecureDesktopSharedCapture(
|
||||||
|
SecureSharedCaptureState* capture_state) {
|
||||||
|
if (capture_state == nullptr) {
|
||||||
|
return BuildTextResponseBytes(BuildErrorJson("invalid_capture_state"));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::thread thread_to_join;
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(capture_state->mutex);
|
||||||
|
capture_state->stop_requested.store(true, std::memory_order_relaxed);
|
||||||
|
if (capture_state->frame_ready_event != nullptr) {
|
||||||
|
SetEvent(capture_state->frame_ready_event);
|
||||||
|
}
|
||||||
|
if (capture_state->capture_thread.joinable()) {
|
||||||
|
thread_to_join = std::move(capture_state->capture_thread);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (thread_to_join.joinable()) {
|
||||||
|
thread_to_join.join();
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(capture_state->mutex);
|
||||||
|
CloseSecureDesktopSharedCaptureResourcesLocked(capture_state);
|
||||||
|
capture_state->stop_requested.store(false, std::memory_order_relaxed);
|
||||||
|
capture_state->sequence = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return BuildTextResponseBytes("{\"ok\":true,\"shared_capture\":\"stopped\"}");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<uint8_t> StartSecureDesktopSharedCapture(
|
||||||
|
const SecureCaptureRequest& request,
|
||||||
|
SecureSharedCaptureState* capture_state) {
|
||||||
|
if (capture_state == nullptr) {
|
||||||
|
return BuildTextResponseBytes(BuildErrorJson("invalid_capture_state"));
|
||||||
|
}
|
||||||
|
|
||||||
|
StopSecureDesktopSharedCapture(capture_state);
|
||||||
|
|
||||||
|
const size_t payload_size =
|
||||||
|
static_cast<size_t>(request.width) * request.height * 3 / 2;
|
||||||
|
const size_t mapping_size =
|
||||||
|
sizeof(crossdesk::CrossDeskSecureDesktopSharedFrameHeader) + payload_size;
|
||||||
|
if (payload_size == 0 || mapping_size > MAXDWORD) {
|
||||||
|
return BuildTextResponseBytes(BuildErrorJson("invalid_capture_size"));
|
||||||
|
}
|
||||||
|
|
||||||
|
PipeSecurityAttributes security_attributes;
|
||||||
|
SECURITY_ATTRIBUTES* attributes = nullptr;
|
||||||
|
if (security_attributes.Initialize()) {
|
||||||
|
attributes = security_attributes.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::wstring mapping_name =
|
||||||
|
crossdesk::GetCrossDeskSecureDesktopFrameMappingName(
|
||||||
|
capture_state->session_id);
|
||||||
|
const std::wstring event_name =
|
||||||
|
crossdesk::GetCrossDeskSecureDesktopFrameReadyEventName(
|
||||||
|
capture_state->session_id);
|
||||||
|
|
||||||
|
HANDLE frame_mapping =
|
||||||
|
CreateFileMappingW(INVALID_HANDLE_VALUE, attributes, PAGE_READWRITE, 0,
|
||||||
|
static_cast<DWORD>(mapping_size),
|
||||||
|
mapping_name.c_str());
|
||||||
|
if (frame_mapping == nullptr) {
|
||||||
|
return BuildTextResponseBytes(
|
||||||
|
BuildErrorJson("create_frame_mapping_failed", GetLastError()));
|
||||||
|
}
|
||||||
|
|
||||||
|
auto* frame_view = static_cast<uint8_t*>(
|
||||||
|
MapViewOfFile(frame_mapping, FILE_MAP_ALL_ACCESS, 0, 0, mapping_size));
|
||||||
|
if (frame_view == nullptr) {
|
||||||
|
const DWORD error = GetLastError();
|
||||||
|
CloseHandle(frame_mapping);
|
||||||
|
return BuildTextResponseBytes(BuildErrorJson("map_frame_view_failed",
|
||||||
|
error));
|
||||||
|
}
|
||||||
|
|
||||||
|
HANDLE frame_ready_event =
|
||||||
|
CreateEventW(attributes, FALSE, FALSE, event_name.c_str());
|
||||||
|
if (frame_ready_event == nullptr) {
|
||||||
|
const DWORD error = GetLastError();
|
||||||
|
UnmapViewOfFile(frame_view);
|
||||||
|
CloseHandle(frame_mapping);
|
||||||
|
return BuildTextResponseBytes(
|
||||||
|
BuildErrorJson("create_frame_event_failed", error));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::memset(frame_view, 0, mapping_size);
|
||||||
|
auto* header =
|
||||||
|
reinterpret_cast<crossdesk::CrossDeskSecureDesktopSharedFrameHeader*>(
|
||||||
|
frame_view);
|
||||||
|
header->magic = crossdesk::kCrossDeskSecureDesktopFrameMagic;
|
||||||
|
header->version = crossdesk::kCrossDeskSecureDesktopFrameVersion;
|
||||||
|
header->left = request.left;
|
||||||
|
header->top = request.top;
|
||||||
|
header->width = static_cast<uint32_t>(request.width);
|
||||||
|
header->height = static_cast<uint32_t>(request.height);
|
||||||
|
header->buffer_size = static_cast<uint32_t>(payload_size);
|
||||||
|
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(capture_state->mutex);
|
||||||
|
capture_state->request = request;
|
||||||
|
capture_state->frame_mapping = frame_mapping;
|
||||||
|
capture_state->frame_ready_event = frame_ready_event;
|
||||||
|
capture_state->frame_view = frame_view;
|
||||||
|
capture_state->frame_view_size = mapping_size;
|
||||||
|
capture_state->sequence = 0;
|
||||||
|
capture_state->stop_requested.store(false, std::memory_order_relaxed);
|
||||||
|
capture_state->capture_thread =
|
||||||
|
std::thread(SecureDesktopSharedCaptureThread, capture_state);
|
||||||
|
}
|
||||||
|
|
||||||
|
Json json;
|
||||||
|
json["ok"] = true;
|
||||||
|
json["shared_capture"] = "started";
|
||||||
|
json["width"] = request.width;
|
||||||
|
json["height"] = request.height;
|
||||||
|
json["fps"] = request.fps;
|
||||||
|
return BuildTextResponseBytes(json.dump());
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<uint8_t> HandleSecureInputHelperCommand(
|
std::vector<uint8_t> HandleSecureInputHelperCommand(
|
||||||
const std::string& command, SecureCaptureBuffers* capture_buffers) {
|
const std::string& command, SecureCaptureBuffers* capture_buffers,
|
||||||
|
SecureSharedCaptureState* capture_state) {
|
||||||
if (command == "ping") {
|
if (command == "ping") {
|
||||||
return BuildTextResponseBytes("{\"ok\":true,\"reply\":\"pong\"}");
|
return BuildTextResponseBytes("{\"ok\":true,\"reply\":\"pong\"}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (command == crossdesk::kCrossDeskSecureInputCaptureStopCommand) {
|
||||||
|
return StopSecureDesktopSharedCapture(capture_state);
|
||||||
|
}
|
||||||
|
|
||||||
|
SecureCaptureRequest capture_start_request;
|
||||||
|
if (ParseSecureInputCaptureStartCommand(command, &capture_start_request)) {
|
||||||
|
return StartSecureDesktopSharedCapture(capture_start_request,
|
||||||
|
capture_state);
|
||||||
|
}
|
||||||
|
|
||||||
int key_code = 0;
|
int key_code = 0;
|
||||||
bool is_down = false;
|
bool is_down = false;
|
||||||
uint32_t scan_code = 0;
|
uint32_t scan_code = 0;
|
||||||
@@ -940,14 +1280,17 @@ std::vector<uint8_t> HandleSecureInputHelperCommand(
|
|||||||
return BuildTextResponseBytes(BuildErrorJson("unknown_command"));
|
return BuildTextResponseBytes(BuildErrorJson("unknown_command"));
|
||||||
}
|
}
|
||||||
|
|
||||||
void HandleSecureInputHelperPipeClient(HANDLE pipe, HANDLE event_handle) {
|
void HandleSecureInputHelperPipeClient(
|
||||||
|
HANDLE pipe, HANDLE event_handle,
|
||||||
|
std::shared_ptr<SecureSharedCaptureState> capture_state) {
|
||||||
SecureCaptureBuffers capture_buffers;
|
SecureCaptureBuffers capture_buffers;
|
||||||
char buffer[1024] = {0};
|
char buffer[1024] = {0};
|
||||||
DWORD bytes_read = 0;
|
DWORD bytes_read = 0;
|
||||||
if (ReadFile(pipe, buffer, sizeof(buffer) - 1, &bytes_read, nullptr) &&
|
if (ReadFile(pipe, buffer, sizeof(buffer) - 1, &bytes_read, nullptr) &&
|
||||||
bytes_read > 0) {
|
bytes_read > 0) {
|
||||||
std::vector<uint8_t> response = HandleSecureInputHelperCommand(
|
std::vector<uint8_t> response = HandleSecureInputHelperCommand(
|
||||||
std::string(buffer, buffer + bytes_read), &capture_buffers);
|
std::string(buffer, buffer + bytes_read), &capture_buffers,
|
||||||
|
capture_state.get());
|
||||||
DWORD bytes_written = 0;
|
DWORD bytes_written = 0;
|
||||||
if (!response.empty()) {
|
if (!response.empty()) {
|
||||||
WriteFile(pipe, response.data(), static_cast<DWORD>(response.size()),
|
WriteFile(pipe, response.data(), static_cast<DWORD>(response.size()),
|
||||||
@@ -964,6 +1307,9 @@ void HandleSecureInputHelperPipeClient(HANDLE pipe, HANDLE event_handle) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void SecureInputHelperIpcServerLoop(HANDLE stop_event, DWORD session_id) {
|
void SecureInputHelperIpcServerLoop(HANDLE stop_event, DWORD session_id) {
|
||||||
|
auto capture_state = std::make_shared<SecureSharedCaptureState>();
|
||||||
|
capture_state->session_id = session_id;
|
||||||
|
|
||||||
PipeSecurityAttributes security_attributes;
|
PipeSecurityAttributes security_attributes;
|
||||||
SECURITY_ATTRIBUTES* pipe_attributes = nullptr;
|
SECURITY_ATTRIBUTES* pipe_attributes = nullptr;
|
||||||
if (security_attributes.Initialize()) {
|
if (security_attributes.Initialize()) {
|
||||||
@@ -1029,9 +1375,12 @@ void SecureInputHelperIpcServerLoop(HANDLE stop_event, DWORD session_id) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::thread(HandleSecureInputHelperPipeClient, pipe, overlapped.hEvent)
|
std::thread(HandleSecureInputHelperPipeClient, pipe, overlapped.hEvent,
|
||||||
|
capture_state)
|
||||||
.detach();
|
.detach();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
StopSecureDesktopSharedCapture(capture_state.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
void PrintUsage() {
|
void PrintUsage() {
|
||||||
|
|||||||
@@ -23,7 +23,15 @@ inline constexpr char kCrossDeskSecureInputKeyboardCommandPrefix[] =
|
|||||||
"keyboard:";
|
"keyboard:";
|
||||||
inline constexpr char kCrossDeskSecureInputMouseCommandPrefix[] = "mouse:";
|
inline constexpr char kCrossDeskSecureInputMouseCommandPrefix[] = "mouse:";
|
||||||
inline constexpr char kCrossDeskSecureInputCaptureCommandPrefix[] = "capture:";
|
inline constexpr char kCrossDeskSecureInputCaptureCommandPrefix[] = "capture:";
|
||||||
|
inline constexpr char kCrossDeskSecureInputCaptureStartCommandPrefix[] =
|
||||||
|
"capture-start:";
|
||||||
|
inline constexpr char kCrossDeskSecureInputCaptureStopCommand[] =
|
||||||
|
"capture-stop";
|
||||||
inline constexpr DWORD kCrossDeskSecureInputPipeBufferBytes = 16 * 1024 * 1024;
|
inline constexpr DWORD kCrossDeskSecureInputPipeBufferBytes = 16 * 1024 * 1024;
|
||||||
|
inline constexpr wchar_t kCrossDeskSecureDesktopFrameMappingPrefix[] =
|
||||||
|
L"Global\\CrossDeskSecureDesktopFrame-";
|
||||||
|
inline constexpr wchar_t kCrossDeskSecureDesktopFrameReadyEventPrefix[] =
|
||||||
|
L"Global\\CrossDeskSecureDesktopFrameReady-";
|
||||||
inline constexpr uint32_t kCrossDeskSecureDesktopFrameMagic = 0x50444358;
|
inline constexpr uint32_t kCrossDeskSecureDesktopFrameMagic = 0x50444358;
|
||||||
inline constexpr uint32_t kCrossDeskSecureDesktopFrameVersion = 1;
|
inline constexpr uint32_t kCrossDeskSecureDesktopFrameVersion = 1;
|
||||||
|
|
||||||
@@ -37,6 +45,19 @@ struct CrossDeskSecureDesktopFrameHeader {
|
|||||||
uint32_t height;
|
uint32_t height;
|
||||||
uint32_t payload_size;
|
uint32_t payload_size;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct CrossDeskSecureDesktopSharedFrameHeader {
|
||||||
|
uint32_t magic;
|
||||||
|
uint32_t version;
|
||||||
|
volatile uint32_t writing;
|
||||||
|
uint32_t sequence;
|
||||||
|
int32_t left;
|
||||||
|
int32_t top;
|
||||||
|
uint32_t width;
|
||||||
|
uint32_t height;
|
||||||
|
uint32_t payload_size;
|
||||||
|
uint32_t buffer_size;
|
||||||
|
};
|
||||||
#pragma pack(pop)
|
#pragma pack(pop)
|
||||||
|
|
||||||
inline std::wstring GetCrossDeskSessionHelperPipeName(DWORD session_id) {
|
inline std::wstring GetCrossDeskSessionHelperPipeName(DWORD session_id) {
|
||||||
@@ -49,6 +70,18 @@ inline std::wstring GetCrossDeskSecureInputHelperPipeName(DWORD session_id) {
|
|||||||
std::to_wstring(session_id);
|
std::to_wstring(session_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline std::wstring GetCrossDeskSecureDesktopFrameMappingName(
|
||||||
|
DWORD session_id) {
|
||||||
|
return std::wstring(kCrossDeskSecureDesktopFrameMappingPrefix) +
|
||||||
|
std::to_wstring(session_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::wstring GetCrossDeskSecureDesktopFrameReadyEventName(
|
||||||
|
DWORD session_id) {
|
||||||
|
return std::wstring(kCrossDeskSecureDesktopFrameReadyEventPrefix) +
|
||||||
|
std::to_wstring(session_id);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace crossdesk
|
} // namespace crossdesk
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
Reference in New Issue
Block a user