[feat] enable screen switch on Linux platform

This commit is contained in:
dijunkun
2025-05-29 18:09:34 +08:00
parent d72c6d9df7
commit 094204361c
8 changed files with 111 additions and 52 deletions

View File

@@ -8,7 +8,8 @@ MouseController::MouseController() {}
MouseController::~MouseController() { Destroy(); } MouseController::~MouseController() { Destroy(); }
int MouseController::Init(int screen_width, int screen_height) { int MouseController::Init(std::vector<DisplayInfo> display_info_list) {
display_info_list_ = display_info_list;
display_ = XOpenDisplay(NULL); display_ = XOpenDisplay(NULL);
if (!display_) { if (!display_) {
LOG_ERROR("Cannot connect to X server"); LOG_ERROR("Cannot connect to X server");
@@ -16,8 +17,6 @@ int MouseController::Init(int screen_width, int screen_height) {
} }
root_ = DefaultRootWindow(display_); root_ = DefaultRootWindow(display_);
screen_width_ = screen_width;
screen_height_ = screen_height;
int event_base, error_base, major_version, minor_version; int event_base, error_base, major_version, minor_version;
if (!XTestQueryExtension(display_, &event_base, &error_base, &major_version, if (!XTestQueryExtension(display_, &event_base, &error_base, &major_version,
@@ -38,14 +37,19 @@ int MouseController::Destroy() {
return 0; return 0;
} }
int MouseController::SendMouseCommand(RemoteAction remote_action) { int MouseController::SendMouseCommand(RemoteAction remote_action,
int display_index) {
switch (remote_action.type) { switch (remote_action.type) {
case mouse: case mouse:
switch (remote_action.m.flag) { switch (remote_action.m.flag) {
case MouseFlag::move: case MouseFlag::move:
SetMousePosition( SetMousePosition(
static_cast<int>(remote_action.m.x * screen_width_), static_cast<int>(remote_action.m.x *
static_cast<int>(remote_action.m.y * screen_height_)); display_info_list_[display_index].width +
display_info_list_[display_index].left),
static_cast<int>(remote_action.m.y *
display_info_list_[display_index].height +
display_info_list_[display_index].top));
break; break;
case MouseFlag::left_down: case MouseFlag::left_down:
XTestFakeButtonEvent(display_, 1, True, CurrentTime); XTestFakeButtonEvent(display_, 1, True, CurrentTime);

View File

@@ -11,6 +11,8 @@
#include <X11/Xutil.h> #include <X11/Xutil.h>
#include <unistd.h> #include <unistd.h>
#include <vector>
#include "device_controller.h" #include "device_controller.h"
class MouseController : public DeviceController { class MouseController : public DeviceController {
@@ -19,9 +21,9 @@ class MouseController : public DeviceController {
virtual ~MouseController(); virtual ~MouseController();
public: public:
virtual int Init(int screen_width, int screen_height); virtual int Init(std::vector<DisplayInfo> display_info_list);
virtual int Destroy(); virtual int Destroy();
virtual int SendMouseCommand(RemoteAction remote_action); virtual int SendMouseCommand(RemoteAction remote_action, int display_index);
private: private:
void SimulateKeyDown(int kval); void SimulateKeyDown(int kval);
@@ -31,6 +33,7 @@ class MouseController : public DeviceController {
Display* display_ = nullptr; Display* display_ = nullptr;
Window root_ = 0; Window root_ = 0;
std::vector<DisplayInfo> display_info_list_;
int screen_width_ = 0; int screen_width_ = 0;
int screen_height_ = 0; int screen_height_ = 0;
}; };

View File

@@ -18,6 +18,34 @@ int ScreenCapturerX11::Init(const int fps, cb_desktop_data cb) {
} }
root_ = DefaultRootWindow(display_); 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; XWindowAttributes attr;
XGetWindowAttributes(display_, root_, &attr); XGetWindowAttributes(display_, root_, &attr);
@@ -40,7 +68,19 @@ int ScreenCapturerX11::Init(const int fps, cb_desktop_data cb) {
int ScreenCapturerX11::Destroy() { int ScreenCapturerX11::Destroy() {
Stop(); 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; return 0;
} }
@@ -63,21 +103,43 @@ int ScreenCapturerX11::Stop() {
return 0; return 0;
} }
int ScreenCapturerX11::Pause() { int ScreenCapturerX11::Pause(int monitor_index) {
paused_ = true; paused_ = true;
return 0; return 0;
} }
int ScreenCapturerX11::Resume() { int ScreenCapturerX11::Resume(int monitor_index) {
paused_ = false; paused_ = false;
return 0; return 0;
} }
void ScreenCapturerX11::OnFrame() { int ScreenCapturerX11::SwitchTo(int monitor_index) {
if (!display_) return; monitor_index_ = monitor_index;
return 0;
}
XImage* image = std::vector<DisplayInfo> ScreenCapturerX11::GetDisplayInfoList() {
XGetImage(display_, root_, 0, 0, width_, height_, AllPlanes, ZPixmap); 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; if (!image) return;
bool needs_copy = image->bytes_per_line != width_ * 4; 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()); nv12.insert(nv12.end(), uv_plane_.begin(), uv_plane_.end());
if (callback_) { 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); XDestroyImage(image);
} }
void ScreenCapturerX11::CleanUp() {
if (display_) {
XCloseDisplay(display_);
display_ = nullptr;
}
}

View File

@@ -9,6 +9,7 @@
#include <X11/Xlib.h> #include <X11/Xlib.h>
#include <X11/Xutil.h> #include <X11/Xutil.h>
#include <X11/extensions/Xrandr.h>
#include <atomic> #include <atomic>
#include <cstring> #include <cstring>
@@ -17,17 +18,7 @@
#include <thread> #include <thread>
#include <vector> #include <vector>
class ScreenCapturer { #include "screen_capturer.h"
public:
typedef std::function<void(unsigned char*, int width, int height, int stride)>
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;
};
class ScreenCapturerX11 : public ScreenCapturer { class ScreenCapturerX11 : public ScreenCapturer {
public: public:
@@ -40,24 +31,30 @@ class ScreenCapturerX11 : public ScreenCapturer {
int Start() override; int Start() override;
int Stop() override; int Stop() override;
int Pause(); int Pause(int monitor_index) override;
int Resume(); int Resume(int monitor_index) override;
int SwitchTo(int monitor_index) override;
std::vector<DisplayInfo> GetDisplayInfoList() override;
void OnFrame(); void OnFrame();
protected:
void CleanUp();
private: private:
Display* display_ = nullptr; Display* display_ = nullptr;
Window root_ = 0; Window root_ = 0;
XRRScreenResources* screen_res_ = nullptr;
int left_ = 0;
int top_ = 0;
int width_ = 0; int width_ = 0;
int height_ = 0; int height_ = 0;
std::thread thread_; std::thread thread_;
std::atomic<bool> running_{false}; std::atomic<bool> running_{false};
std::atomic<bool> paused_{false}; std::atomic<bool> paused_{false};
std::atomic<int> monitor_index_{0};
int fps_ = 30; int fps_ = 30;
cb_desktop_data callback_; cb_desktop_data callback_;
std::vector<DisplayInfo> display_info_list_;
// 缓冲区 // 缓冲区
std::vector<uint8_t> y_plane_; std::vector<uint8_t> y_plane_;

View File

@@ -22,13 +22,12 @@ class ScreenCapturerSck : public ScreenCapturer {
~ScreenCapturerSck(); ~ScreenCapturerSck();
public: public:
virtual int Init(const int fps, cb_desktop_data cb) override; int Init(const int fps, cb_desktop_data cb) override;
virtual int Destroy() override; int Destroy() override;
virtual int Start() override; int Start() override;
virtual int Stop() override; int Stop() override;
int Pause(int monitor_index) override; int Pause(int monitor_index) override;
int Resume(int monitor_index) override; int Resume(int monitor_index) override;
int SwitchTo(int monitor_index) override; int SwitchTo(int monitor_index) override;

View File

@@ -20,13 +20,13 @@ class ScreenCapturerWgc : public ScreenCapturer,
public: public:
bool IsWgcSupported(); bool IsWgcSupported();
virtual int Init(const int fps, cb_desktop_data cb) override; int Init(const int fps, cb_desktop_data cb) override;
virtual int Destroy() override; int Destroy() override;
virtual int Start() override; int Start() override;
virtual int Stop() override; int Stop() override;
virtual int Pause(int monitor_index) override; int Pause(int monitor_index) override;
virtual int Resume(int monitor_index) override; int Resume(int monitor_index) override;
std::vector<DisplayInfo> GetDisplayInfoList() { return display_info_list_; } std::vector<DisplayInfo> GetDisplayInfoList() { return display_info_list_; }

View File

@@ -25,7 +25,7 @@ void Render::DrawConnectionStatusText(
ImGui::SetCursorPos( ImGui::SetCursorPos(
ImVec2((size.x - text_size.x) * 0.5f, ImVec2((size.x - text_size.x) * 0.5f,
(size.y - text_size.y - title_bar_height_) * 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());
} }
} }

View File

@@ -30,7 +30,7 @@ elseif is_os("linux") then
add_requires("libyuv") add_requires("libyuv")
add_syslinks("pthread", "dl") add_syslinks("pthread", "dl")
add_linkdirs("thirdparty/projectx/thirdparty/nvcodec/lib/x64") 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") add_cxflags("-Wno-unused-variable")
elseif is_os("macosx") then elseif is_os("macosx") then
add_links("SDL2", "SDL2main") add_links("SDL2", "SDL2main")