mirror of
https://github.com/rustdesk/rustdesk.git
synced 2026-09-11 23:11:01 +03:00
feat(linux): measure the active system cursor size
This commit is contained in:
@@ -53,6 +53,7 @@ add_subdirectory(${FLUTTER_MANAGED_DIR})
|
||||
# System-level dependencies.
|
||||
find_package(PkgConfig REQUIRED)
|
||||
pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0)
|
||||
pkg_check_modules(XCURSOR REQUIRED IMPORTED_TARGET xcursor)
|
||||
|
||||
# Wayland protocol for keyboard shortcuts inhibit
|
||||
pkg_check_modules(WAYLAND_CLIENT IMPORTED_TARGET wayland-client)
|
||||
@@ -126,6 +127,7 @@ apply_standard_settings(${BINARY_NAME})
|
||||
# Add dependency libraries. Add any application-specific dependencies here.
|
||||
target_link_libraries(${BINARY_NAME} PRIVATE flutter)
|
||||
target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK)
|
||||
target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::XCURSOR)
|
||||
target_link_libraries(${BINARY_NAME} PRIVATE ${CMAKE_DL_LIBS})
|
||||
# target_link_libraries(${BINARY_NAME} PRIVATE librustdesk)
|
||||
|
||||
|
||||
106
flutter/linux/cursor_size.h
Normal file
106
flutter/linux/cursor_size.h
Normal file
@@ -0,0 +1,106 @@
|
||||
#ifndef RUSTDESK_CURSOR_SIZE_H_
|
||||
#define RUSTDESK_CURSOR_SIZE_H_
|
||||
|
||||
#include <flutter_linux/flutter_linux.h>
|
||||
#include <X11/Xcursor/Xcursor.h>
|
||||
#ifdef GDK_WINDOWING_WAYLAND
|
||||
#include <gdk/gdkwayland.h>
|
||||
#endif
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace cursor_size {
|
||||
|
||||
template <typename IsVisible>
|
||||
double VisibleSize(int width, int height, IsVisible visible) {
|
||||
int left = width, top = height, right = -1, bottom = -1;
|
||||
for (int y = 0; y < height; ++y) {
|
||||
for (int x = 0; x < width; ++x) {
|
||||
if (!visible(x, y)) continue;
|
||||
left = std::min(left, x);
|
||||
right = std::max(right, x);
|
||||
top = std::min(top, y);
|
||||
bottom = std::max(bottom, y);
|
||||
}
|
||||
}
|
||||
if (right < left) throw std::runtime_error("System cursor has no visible pixels");
|
||||
return std::max(right - left + 1, bottom - top + 1);
|
||||
}
|
||||
|
||||
inline double WaylandSize(GtkWidget* view) {
|
||||
g_autofree gchar* theme = nullptr;
|
||||
gint size = 0;
|
||||
g_object_get(gtk_widget_get_settings(view), "gtk-cursor-theme-name", &theme,
|
||||
"gtk-cursor-theme-size", &size, nullptr);
|
||||
// GTK uses 24 when the theme size is unset, and loads at the window scale.
|
||||
constexpr int kDefaultThemeSize = 24;
|
||||
if (size == 0) size = kDefaultThemeSize;
|
||||
const int scale = gtk_widget_get_scale_factor(view);
|
||||
if (size < 0 || scale <= 0 || size > G_MAXINT / scale) {
|
||||
throw std::runtime_error("Invalid GTK cursor theme size or window scale");
|
||||
}
|
||||
auto* loaded = XcursorLibraryLoadImage("default", theme, size * scale);
|
||||
// Match GTK's CSS default -> traditional left_ptr name mapping.
|
||||
if (!loaded) loaded = XcursorLibraryLoadImage("left_ptr", theme, size * scale);
|
||||
std::unique_ptr<XcursorImage, decltype(&XcursorImageDestroy)> image(
|
||||
loaded, XcursorImageDestroy);
|
||||
if (!image) throw std::runtime_error("Could not load the GTK system cursor");
|
||||
// GTK reduces the buffer scale until both theme dimensions are divisible.
|
||||
// For example, a 64px theme at 3x is displayed using a buffer scale of 2.
|
||||
int cursor_scale = scale;
|
||||
while (image->width % cursor_scale != 0 || image->height % cursor_scale != 0) {
|
||||
--cursor_scale;
|
||||
}
|
||||
constexpr XcursorPixel kAlphaMask = 0xff000000;
|
||||
return VisibleSize(image->width, image->height, [&](int x, int y) {
|
||||
return (image->pixels[y * image->width + x] & kAlphaMask) != 0;
|
||||
}) / cursor_scale;
|
||||
}
|
||||
|
||||
inline double SystemSize(GtkWidget* view) {
|
||||
GdkDisplay* display = gtk_widget_get_display(view);
|
||||
#ifdef GDK_WINDOWING_WAYLAND
|
||||
if (GDK_IS_WAYLAND_DISPLAY(display)) return WaylandSize(view);
|
||||
#endif
|
||||
g_autoptr(GdkCursor) cursor = gdk_cursor_new_from_name(display, "default");
|
||||
if (!cursor) throw std::runtime_error("Could not load the GDK system cursor");
|
||||
g_autoptr(GdkPixbuf) image = gdk_cursor_get_image(cursor);
|
||||
if (!image) throw std::runtime_error("Could not read the GDK system cursor");
|
||||
const auto* pixels = gdk_pixbuf_read_pixels(image);
|
||||
const int stride = gdk_pixbuf_get_rowstride(image);
|
||||
const int channels = gdk_pixbuf_get_n_channels(image);
|
||||
return VisibleSize(gdk_pixbuf_get_width(image), gdk_pixbuf_get_height(image),
|
||||
[&](int x, int y) {
|
||||
return !gdk_pixbuf_get_has_alpha(image) ||
|
||||
pixels[y * stride + x * channels + channels - 1] != 0;
|
||||
});
|
||||
}
|
||||
|
||||
inline void Register(FlView* view) {
|
||||
g_autoptr(FlStandardMethodCodec) codec = fl_standard_method_codec_new();
|
||||
FlMethodChannel* channel = fl_method_channel_new(
|
||||
fl_engine_get_binary_messenger(fl_view_get_engine(view)),
|
||||
"org.rustdesk.rustdesk/cursor", FL_METHOD_CODEC(codec));
|
||||
fl_method_channel_set_method_call_handler(channel,
|
||||
[](FlMethodChannel*, FlMethodCall* call, gpointer data) {
|
||||
g_autoptr(GError) error = nullptr;
|
||||
if (g_strcmp0(fl_method_call_get_name(call), "getSystemCursorSize") != 0) {
|
||||
fl_method_call_respond_not_implemented(call, &error);
|
||||
} else {
|
||||
try {
|
||||
g_autoptr(FlValue) value = fl_value_new_float(SystemSize(GTK_WIDGET(data)));
|
||||
fl_method_call_respond_success(call, value, &error);
|
||||
} catch (const std::exception& failure) {
|
||||
fl_method_call_respond_error(call, "cursor_size", failure.what(), nullptr, &error);
|
||||
}
|
||||
}
|
||||
if (error) g_warning("Cursor size response failed: %s", error->message);
|
||||
}, view, nullptr);
|
||||
g_object_set_data_full(G_OBJECT(view), "cursor-size-channel", channel, g_object_unref);
|
||||
}
|
||||
|
||||
} // namespace cursor_size
|
||||
|
||||
#endif // RUSTDESK_CURSOR_SIZE_H_
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "my_application.h"
|
||||
|
||||
#include "bump_mouse.h"
|
||||
#include "cursor_size.h"
|
||||
|
||||
#include <flutter_linux/flutter_linux.h>
|
||||
#ifdef GDK_WINDOWING_X11
|
||||
@@ -94,6 +95,7 @@ static void on_subwindow_created(FlPluginRegistry* registry) {
|
||||
// Set up side button forwarding for sub-windows.
|
||||
if (registry == NULL || !FL_IS_VIEW(registry)) return;
|
||||
FlView* view = FL_VIEW(registry);
|
||||
cursor_size::Register(view);
|
||||
GtkWidget* toplevel = gtk_widget_get_toplevel(GTK_WIDGET(view));
|
||||
if (toplevel != NULL && GTK_IS_WINDOW(toplevel)) {
|
||||
FlMethodChannel* channel = side_buttons_create_channel(fl_view_get_engine(view));
|
||||
@@ -178,6 +180,7 @@ static void my_application_activate(GApplication* application) {
|
||||
(WindowCreatedCallback)on_subwindow_created);
|
||||
|
||||
fl_register_plugins(FL_PLUGIN_REGISTRY(view));
|
||||
cursor_size::Register(view);
|
||||
|
||||
g_autoptr(FlStandardMethodCodec) codec = fl_standard_method_codec_new();
|
||||
self->host_channel = fl_method_channel_new(
|
||||
|
||||
Reference in New Issue
Block a user