From 094204361c9916830f28135299fe5daac1d461c8 Mon Sep 17 00:00:00 2001 From: dijunkun Date: Thu, 29 May 2025 18:09:34 +0800 Subject: [PATCH] [feat] enable screen switch on Linux platform --- .../mouse/linux/mouse_controller.cpp | 16 ++-- .../mouse/linux/mouse_controller.h | 7 +- .../linux/screen_capturer_x11.cpp | 86 +++++++++++++++---- .../linux/screen_capturer_x11.h | 29 +++---- .../macosx/screen_capturer_sck.h | 9 +- .../windows/screen_capturer_wgc.h | 12 +-- src/single_window/stream_window.cpp | 2 +- xmake.lua | 2 +- 8 files changed, 111 insertions(+), 52 deletions(-) diff --git a/src/device_controller/mouse/linux/mouse_controller.cpp b/src/device_controller/mouse/linux/mouse_controller.cpp index 839eea5..0ee060f 100644 --- a/src/device_controller/mouse/linux/mouse_controller.cpp +++ b/src/device_controller/mouse/linux/mouse_controller.cpp @@ -8,7 +8,8 @@ MouseController::MouseController() {} MouseController::~MouseController() { Destroy(); } -int MouseController::Init(int screen_width, int screen_height) { +int MouseController::Init(std::vector display_info_list) { + display_info_list_ = display_info_list; display_ = XOpenDisplay(NULL); if (!display_) { LOG_ERROR("Cannot connect to X server"); @@ -16,8 +17,6 @@ int MouseController::Init(int screen_width, int screen_height) { } root_ = DefaultRootWindow(display_); - screen_width_ = screen_width; - screen_height_ = screen_height; int event_base, error_base, major_version, minor_version; if (!XTestQueryExtension(display_, &event_base, &error_base, &major_version, @@ -38,14 +37,19 @@ int MouseController::Destroy() { return 0; } -int MouseController::SendMouseCommand(RemoteAction remote_action) { +int MouseController::SendMouseCommand(RemoteAction remote_action, + int display_index) { switch (remote_action.type) { case mouse: switch (remote_action.m.flag) { case MouseFlag::move: SetMousePosition( - static_cast(remote_action.m.x * screen_width_), - static_cast(remote_action.m.y * screen_height_)); + static_cast(remote_action.m.x * + display_info_list_[display_index].width + + display_info_list_[display_index].left), + static_cast(remote_action.m.y * + display_info_list_[display_index].height + + display_info_list_[display_index].top)); break; case MouseFlag::left_down: XTestFakeButtonEvent(display_, 1, True, CurrentTime); diff --git a/src/device_controller/mouse/linux/mouse_controller.h b/src/device_controller/mouse/linux/mouse_controller.h index 73a3d17..359e6d7 100644 --- a/src/device_controller/mouse/linux/mouse_controller.h +++ b/src/device_controller/mouse/linux/mouse_controller.h @@ -11,6 +11,8 @@ #include #include +#include + #include "device_controller.h" class MouseController : public DeviceController { @@ -19,9 +21,9 @@ class MouseController : public DeviceController { virtual ~MouseController(); public: - virtual int Init(int screen_width, int screen_height); + virtual int Init(std::vector display_info_list); virtual int Destroy(); - virtual int SendMouseCommand(RemoteAction remote_action); + virtual int SendMouseCommand(RemoteAction remote_action, int display_index); private: void SimulateKeyDown(int kval); @@ -31,6 +33,7 @@ class MouseController : public DeviceController { Display* display_ = nullptr; Window root_ = 0; + std::vector display_info_list_; int screen_width_ = 0; int screen_height_ = 0; }; diff --git a/src/screen_capturer/linux/screen_capturer_x11.cpp b/src/screen_capturer/linux/screen_capturer_x11.cpp index 704ce06..2195ea4 100644 --- a/src/screen_capturer/linux/screen_capturer_x11.cpp +++ b/src/screen_capturer/linux/screen_capturer_x11.cpp @@ -18,6 +18,34 @@ int ScreenCapturerX11::Init(const int fps, cb_desktop_data cb) { } root_ = DefaultRootWindow(display_); + screen_res_ = XRRGetScreenResources(display_, root_); + if (!screen_res_) { + LOG_ERROR("Failed to get screen resources"); + XCloseDisplay(display_); + return 1; + } + + for (int i = 0; i < screen_res_->noutput; ++i) { + RROutput output = screen_res_->outputs[i]; + XRROutputInfo* output_info = + XRRGetOutputInfo(display_, screen_res_, output); + + if (output_info->connection == RR_Connected && output_info->crtc != 0) { + XRRCrtcInfo* crtc_info = + XRRGetCrtcInfo(display_, screen_res_, output_info->crtc); + + display_info_list_.push_back( + DisplayInfo((void*)display_, output_info->name, true, crtc_info->x, + crtc_info->y, crtc_info->width, crtc_info->height)); + + XRRFreeCrtcInfo(crtc_info); + } + + if (output_info) { + XRRFreeOutputInfo(output_info); + } + } + XWindowAttributes attr; XGetWindowAttributes(display_, root_, &attr); @@ -40,7 +68,19 @@ int ScreenCapturerX11::Init(const int fps, cb_desktop_data cb) { int ScreenCapturerX11::Destroy() { Stop(); - CleanUp(); + + y_plane_.clear(); + uv_plane_.clear(); + + if (screen_res_) { + XRRFreeScreenResources(screen_res_); + screen_res_ = nullptr; + } + + if (display_) { + XCloseDisplay(display_); + display_ = nullptr; + } return 0; } @@ -63,21 +103,43 @@ int ScreenCapturerX11::Stop() { return 0; } -int ScreenCapturerX11::Pause() { +int ScreenCapturerX11::Pause(int monitor_index) { paused_ = true; return 0; } -int ScreenCapturerX11::Resume() { +int ScreenCapturerX11::Resume(int monitor_index) { paused_ = false; return 0; } -void ScreenCapturerX11::OnFrame() { - if (!display_) return; +int ScreenCapturerX11::SwitchTo(int monitor_index) { + monitor_index_ = monitor_index; + return 0; +} - XImage* image = - XGetImage(display_, root_, 0, 0, width_, height_, AllPlanes, ZPixmap); +std::vector ScreenCapturerX11::GetDisplayInfoList() { + return display_info_list_; +} + +void ScreenCapturerX11::OnFrame() { + if (!display_) { + LOG_ERROR("Display is not initialized"); + return; + } + + if (monitor_index_ < 0 || monitor_index_ >= display_info_list_.size()) { + LOG_ERROR("Invalid monitor index: {}", monitor_index_.load()); + return; + } + + left_ = display_info_list_[monitor_index_].left; + top_ = display_info_list_[monitor_index_].top; + width_ = display_info_list_[monitor_index_].width; + height_ = display_info_list_[monitor_index_].height; + + XImage* image = XGetImage(display_, root_, left_, top_, width_, height_, + AllPlanes, ZPixmap); if (!image) return; bool needs_copy = image->bytes_per_line != width_ * 4; @@ -104,15 +166,9 @@ void ScreenCapturerX11::OnFrame() { nv12.insert(nv12.end(), uv_plane_.begin(), uv_plane_.end()); if (callback_) { - callback_(nv12.data(), width_ * height_ * 3 / 2, width_, height_, ""); + callback_(nv12.data(), width_ * height_ * 3 / 2, width_, height_, + display_info_list_[monitor_index_].name.c_str()); } XDestroyImage(image); -} - -void ScreenCapturerX11::CleanUp() { - if (display_) { - XCloseDisplay(display_); - display_ = nullptr; - } } \ No newline at end of file diff --git a/src/screen_capturer/linux/screen_capturer_x11.h b/src/screen_capturer/linux/screen_capturer_x11.h index 7b7b123..fd9634e 100644 --- a/src/screen_capturer/linux/screen_capturer_x11.h +++ b/src/screen_capturer/linux/screen_capturer_x11.h @@ -9,6 +9,7 @@ #include #include +#include #include #include @@ -17,17 +18,7 @@ #include #include -class ScreenCapturer { - public: - typedef std::function - cb_desktop_data; - - virtual ~ScreenCapturer() {} - virtual int Init(const int fps, cb_desktop_data cb) = 0; - virtual int Destroy() = 0; - virtual int Start() = 0; - virtual int Stop() = 0; -}; +#include "screen_capturer.h" class ScreenCapturerX11 : public ScreenCapturer { public: @@ -40,24 +31,30 @@ class ScreenCapturerX11 : public ScreenCapturer { int Start() override; int Stop() override; - int Pause(); - int Resume(); + int Pause(int monitor_index) override; + int Resume(int monitor_index) override; + + int SwitchTo(int monitor_index) override; + + std::vector GetDisplayInfoList() override; void OnFrame(); - protected: - void CleanUp(); - private: Display* display_ = nullptr; Window root_ = 0; + XRRScreenResources* screen_res_ = nullptr; + int left_ = 0; + int top_ = 0; int width_ = 0; int height_ = 0; std::thread thread_; std::atomic running_{false}; std::atomic paused_{false}; + std::atomic monitor_index_{0}; int fps_ = 30; cb_desktop_data callback_; + std::vector display_info_list_; // 缓冲区 std::vector y_plane_; diff --git a/src/screen_capturer/macosx/screen_capturer_sck.h b/src/screen_capturer/macosx/screen_capturer_sck.h index 75858db..ad2659e 100644 --- a/src/screen_capturer/macosx/screen_capturer_sck.h +++ b/src/screen_capturer/macosx/screen_capturer_sck.h @@ -22,13 +22,12 @@ class ScreenCapturerSck : public ScreenCapturer { ~ScreenCapturerSck(); public: - virtual int Init(const int fps, cb_desktop_data cb) override; - virtual int Destroy() override; - virtual int Start() override; - virtual int Stop() override; + int Init(const int fps, cb_desktop_data cb) override; + int Destroy() override; + int Start() override; + int Stop() override; int Pause(int monitor_index) override; - int Resume(int monitor_index) override; int SwitchTo(int monitor_index) override; diff --git a/src/screen_capturer/windows/screen_capturer_wgc.h b/src/screen_capturer/windows/screen_capturer_wgc.h index ef8d2c6..d70d1c3 100644 --- a/src/screen_capturer/windows/screen_capturer_wgc.h +++ b/src/screen_capturer/windows/screen_capturer_wgc.h @@ -20,13 +20,13 @@ class ScreenCapturerWgc : public ScreenCapturer, public: bool IsWgcSupported(); - virtual int Init(const int fps, cb_desktop_data cb) override; - virtual int Destroy() override; - virtual int Start() override; - virtual int Stop() override; + int Init(const int fps, cb_desktop_data cb) override; + int Destroy() override; + int Start() override; + int Stop() override; - virtual int Pause(int monitor_index) override; - virtual int Resume(int monitor_index) override; + int Pause(int monitor_index) override; + int Resume(int monitor_index) override; std::vector GetDisplayInfoList() { return display_info_list_; } diff --git a/src/single_window/stream_window.cpp b/src/single_window/stream_window.cpp index 5db8cd5..a692ed9 100644 --- a/src/single_window/stream_window.cpp +++ b/src/single_window/stream_window.cpp @@ -25,7 +25,7 @@ void Render::DrawConnectionStatusText( ImGui::SetCursorPos( ImVec2((size.x - text_size.x) * 0.5f, (size.y - text_size.y - title_bar_height_) * 0.5f)); - ImGui::TextColored(ImVec4(1.0f, 1.0f, 1.0f, 1.0f), text.c_str()); + ImGui::TextColored(ImVec4(1.0f, 1.0f, 1.0f, 1.0f), "%s", text.c_str()); } } diff --git a/xmake.lua b/xmake.lua index 81799ae..9b3f358 100644 --- a/xmake.lua +++ b/xmake.lua @@ -30,7 +30,7 @@ elseif is_os("linux") then add_requires("libyuv") add_syslinks("pthread", "dl") add_linkdirs("thirdparty/projectx/thirdparty/nvcodec/lib/x64") - add_links("SDL2", "cuda", "nvidia-encode", "nvcuvid", "X11", "Xtst") + add_links("SDL2", "cuda", "nvidia-encode", "nvcuvid", "X11", "Xtst", "Xrandr") add_cxflags("-Wno-unused-variable") elseif is_os("macosx") then add_links("SDL2", "SDL2main")