mirror of
https://github.com/rustdesk/rustdesk.git
synced 2026-04-19 02:21:28 +03:00
Compare commits
5 Commits
2842315b1d
...
copilot/fi
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
36b4f765fd | ||
|
|
370b467b71 | ||
|
|
780c396541 | ||
|
|
db5a1f29e7 | ||
|
|
b7d25ef389 |
2
build.py
2
build.py
@@ -299,7 +299,7 @@ Version: %s
|
|||||||
Architecture: %s
|
Architecture: %s
|
||||||
Maintainer: rustdesk <info@rustdesk.com>
|
Maintainer: rustdesk <info@rustdesk.com>
|
||||||
Homepage: https://rustdesk.com
|
Homepage: https://rustdesk.com
|
||||||
Depends: libgtk-3-0, libxcb-randr0, libxdo3 | libxdo4, libxfixes3, libxcb-shape0, libxcb-xfixes0, libasound2, libsystemd0, curl, libva2, libva-drm2, libva-x11-2, libgstreamer-plugins-base1.0-0, libpam0g, gstreamer1.0-pipewire%s
|
Depends: libgtk-3-0, libxcb-randr0, libxdo3, libxfixes3, libxcb-shape0, libxcb-xfixes0, libasound2, libsystemd0, curl, libva2, libva-drm2, libva-x11-2, libgstreamer-plugins-base1.0-0, libpam0g, gstreamer1.0-pipewire%s
|
||||||
Recommends: libayatana-appindicator3-1
|
Recommends: libayatana-appindicator3-1
|
||||||
Description: A remote control software.
|
Description: A remote control software.
|
||||||
|
|
||||||
|
|||||||
@@ -55,7 +55,6 @@
|
|||||||
],
|
],
|
||||||
"finish-args": [
|
"finish-args": [
|
||||||
"--share=ipc",
|
"--share=ipc",
|
||||||
"--socket=wayland",
|
|
||||||
"--socket=x11",
|
"--socket=x11",
|
||||||
"--share=network",
|
"--share=network",
|
||||||
"--filesystem=home",
|
"--filesystem=home",
|
||||||
|
|||||||
@@ -2538,49 +2538,6 @@ class WaylandCard extends StatefulWidget {
|
|||||||
|
|
||||||
class _WaylandCardState extends State<WaylandCard> {
|
class _WaylandCardState extends State<WaylandCard> {
|
||||||
final restoreTokenKey = 'wayland-restore-token';
|
final restoreTokenKey = 'wayland-restore-token';
|
||||||
static const _kClearShortcutsInhibitorEventKey =
|
|
||||||
'clear-gnome-shortcuts-inhibitor-permission-res';
|
|
||||||
final _clearShortcutsInhibitorFailedMsg = ''.obs;
|
|
||||||
// Don't show the shortcuts permission reset button for now.
|
|
||||||
// Users can change it manually:
|
|
||||||
// "Settings" -> "Apps" -> "RustDesk" -> "Permissions" -> "Inhibit Shortcuts".
|
|
||||||
// For resetting(clearing) the permission from the portal permission store, you can
|
|
||||||
// use (replace <desktop-id> with the RustDesk desktop file ID):
|
|
||||||
// busctl --user call org.freedesktop.impl.portal.PermissionStore \
|
|
||||||
// /org/freedesktop/impl/portal/PermissionStore org.freedesktop.impl.portal.PermissionStore \
|
|
||||||
// DeletePermission sss "gnome" "shortcuts-inhibitor" "<desktop-id>"
|
|
||||||
// On a native install this is typically "rustdesk.desktop"; on Flatpak it is usually
|
|
||||||
// the exported desktop ID derived from the Flatpak app-id (e.g. "com.rustdesk.RustDesk.desktop").
|
|
||||||
//
|
|
||||||
// We may add it back in the future if needed.
|
|
||||||
final showResetInhibitorPermission = false;
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
if (showResetInhibitorPermission) {
|
|
||||||
platformFFI.registerEventHandler(
|
|
||||||
_kClearShortcutsInhibitorEventKey, _kClearShortcutsInhibitorEventKey,
|
|
||||||
(evt) async {
|
|
||||||
if (!mounted) return;
|
|
||||||
if (evt['success'] == true) {
|
|
||||||
setState(() {});
|
|
||||||
} else {
|
|
||||||
_clearShortcutsInhibitorFailedMsg.value =
|
|
||||||
evt['msg'] as String? ?? 'Unknown error';
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void dispose() {
|
|
||||||
if (showResetInhibitorPermission) {
|
|
||||||
platformFFI.unregisterEventHandler(
|
|
||||||
_kClearShortcutsInhibitorEventKey, _kClearShortcutsInhibitorEventKey);
|
|
||||||
}
|
|
||||||
super.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@@ -2588,16 +2545,9 @@ class _WaylandCardState extends State<WaylandCard> {
|
|||||||
future: bind.mainHandleWaylandScreencastRestoreToken(
|
future: bind.mainHandleWaylandScreencastRestoreToken(
|
||||||
key: restoreTokenKey, value: "get"),
|
key: restoreTokenKey, value: "get"),
|
||||||
hasData: (restoreToken) {
|
hasData: (restoreToken) {
|
||||||
final hasShortcutsPermission = showResetInhibitorPermission &&
|
|
||||||
bind.mainGetCommonSync(
|
|
||||||
key: "has-gnome-shortcuts-inhibitor-permission") ==
|
|
||||||
"true";
|
|
||||||
|
|
||||||
final children = [
|
final children = [
|
||||||
if (restoreToken.isNotEmpty)
|
if (restoreToken.isNotEmpty)
|
||||||
_buildClearScreenSelection(context, restoreToken),
|
_buildClearScreenSelection(context, restoreToken),
|
||||||
if (hasShortcutsPermission)
|
|
||||||
_buildClearShortcutsInhibitorPermission(context),
|
|
||||||
];
|
];
|
||||||
return Offstage(
|
return Offstage(
|
||||||
offstage: children.isEmpty,
|
offstage: children.isEmpty,
|
||||||
@@ -2642,50 +2592,6 @@ class _WaylandCardState extends State<WaylandCard> {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildClearShortcutsInhibitorPermission(BuildContext context) {
|
|
||||||
onConfirm() {
|
|
||||||
_clearShortcutsInhibitorFailedMsg.value = '';
|
|
||||||
bind.mainSetCommon(
|
|
||||||
key: "clear-gnome-shortcuts-inhibitor-permission", value: "");
|
|
||||||
gFFI.dialogManager.dismissAll();
|
|
||||||
}
|
|
||||||
|
|
||||||
showConfirmMsgBox() => msgBoxCommon(
|
|
||||||
gFFI.dialogManager,
|
|
||||||
'Confirmation',
|
|
||||||
Text(
|
|
||||||
translate('confirm-clear-shortcuts-inhibitor-permission-tip'),
|
|
||||||
),
|
|
||||||
[
|
|
||||||
dialogButton('OK', onPressed: onConfirm),
|
|
||||||
dialogButton('Cancel',
|
|
||||||
onPressed: () => gFFI.dialogManager.dismissAll())
|
|
||||||
]);
|
|
||||||
|
|
||||||
return Column(children: [
|
|
||||||
Obx(
|
|
||||||
() => _clearShortcutsInhibitorFailedMsg.value.isEmpty
|
|
||||||
? Offstage()
|
|
||||||
: Align(
|
|
||||||
alignment: Alignment.topLeft,
|
|
||||||
child: Text(_clearShortcutsInhibitorFailedMsg.value,
|
|
||||||
style: DefaultTextStyle.of(context)
|
|
||||||
.style
|
|
||||||
.copyWith(color: Colors.red))
|
|
||||||
.marginOnly(bottom: 10.0)),
|
|
||||||
),
|
|
||||||
_Button(
|
|
||||||
'Reset keyboard shortcuts permission',
|
|
||||||
showConfirmMsgBox,
|
|
||||||
tip: 'clear-shortcuts-inhibitor-permission-tip',
|
|
||||||
style: ButtonStyle(
|
|
||||||
backgroundColor: MaterialStateProperty.all<Color>(
|
|
||||||
Theme.of(context).colorScheme.error.withOpacity(0.75)),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ignore: non_constant_identifier_names
|
// ignore: non_constant_identifier_names
|
||||||
|
|||||||
@@ -68,7 +68,6 @@ class _RemotePageState extends State<RemotePage> with WidgetsBindingObserver {
|
|||||||
double _viewInsetsBottom = 0;
|
double _viewInsetsBottom = 0;
|
||||||
final _uniqueKey = UniqueKey();
|
final _uniqueKey = UniqueKey();
|
||||||
Timer? _timerDidChangeMetrics;
|
Timer? _timerDidChangeMetrics;
|
||||||
Timer? _iosKeyboardWorkaroundTimer;
|
|
||||||
|
|
||||||
final _blockableOverlayState = BlockableOverlayState();
|
final _blockableOverlayState = BlockableOverlayState();
|
||||||
|
|
||||||
@@ -141,7 +140,6 @@ class _RemotePageState extends State<RemotePage> with WidgetsBindingObserver {
|
|||||||
await gFFI.close();
|
await gFFI.close();
|
||||||
_timer?.cancel();
|
_timer?.cancel();
|
||||||
_timerDidChangeMetrics?.cancel();
|
_timerDidChangeMetrics?.cancel();
|
||||||
_iosKeyboardWorkaroundTimer?.cancel();
|
|
||||||
gFFI.dialogManager.dismissAll();
|
gFFI.dialogManager.dismissAll();
|
||||||
await SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual,
|
await SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual,
|
||||||
overlays: SystemUiOverlay.values);
|
overlays: SystemUiOverlay.values);
|
||||||
@@ -208,24 +206,7 @@ class _RemotePageState extends State<RemotePage> with WidgetsBindingObserver {
|
|||||||
gFFI.ffiModel.pi.version.isNotEmpty) {
|
gFFI.ffiModel.pi.version.isNotEmpty) {
|
||||||
gFFI.invokeMethod("enable_soft_keyboard", false);
|
gFFI.invokeMethod("enable_soft_keyboard", false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Workaround for iOS: physical keyboard input fails after virtual keyboard is hidden
|
|
||||||
// https://github.com/flutter/flutter/issues/39900
|
|
||||||
// https://github.com/rustdesk/rustdesk/discussions/11843#discussioncomment-13499698 - Virtual keyboard issue
|
|
||||||
if (isIOS) {
|
|
||||||
_iosKeyboardWorkaroundTimer?.cancel();
|
|
||||||
_iosKeyboardWorkaroundTimer = Timer(Duration(milliseconds: 100), () {
|
|
||||||
if (!mounted) return;
|
|
||||||
_physicalFocusNode.unfocus();
|
|
||||||
_iosKeyboardWorkaroundTimer = Timer(Duration(milliseconds: 50), () {
|
|
||||||
if (!mounted) return;
|
|
||||||
_physicalFocusNode.requestFocus();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
_iosKeyboardWorkaroundTimer?.cancel();
|
|
||||||
_iosKeyboardWorkaroundTimer = null;
|
|
||||||
_timer?.cancel();
|
_timer?.cancel();
|
||||||
_timer = Timer(kMobileDelaySoftKeyboardFocus, () {
|
_timer = Timer(kMobileDelaySoftKeyboardFocus, () {
|
||||||
SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual,
|
SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# Project-level configuration.
|
# Project-level configuration.
|
||||||
cmake_minimum_required(VERSION 3.10)
|
cmake_minimum_required(VERSION 3.10)
|
||||||
project(runner LANGUAGES C CXX)
|
project(runner LANGUAGES CXX)
|
||||||
|
|
||||||
# The name of the executable created for the application. Change this to change
|
# The name of the executable created for the application. Change this to change
|
||||||
# the on-disk name of your application.
|
# the on-disk name of your application.
|
||||||
@@ -54,55 +54,6 @@ add_subdirectory(${FLUTTER_MANAGED_DIR})
|
|||||||
find_package(PkgConfig REQUIRED)
|
find_package(PkgConfig REQUIRED)
|
||||||
pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0)
|
pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0)
|
||||||
|
|
||||||
# Wayland protocol for keyboard shortcuts inhibit
|
|
||||||
pkg_check_modules(WAYLAND_CLIENT IMPORTED_TARGET wayland-client)
|
|
||||||
pkg_check_modules(WAYLAND_PROTOCOLS_PKG QUIET wayland-protocols)
|
|
||||||
pkg_check_modules(WAYLAND_SCANNER_PKG QUIET wayland-scanner)
|
|
||||||
|
|
||||||
if(WAYLAND_PROTOCOLS_PKG_FOUND)
|
|
||||||
pkg_get_variable(WAYLAND_PROTOCOLS_DIR wayland-protocols pkgdatadir)
|
|
||||||
endif()
|
|
||||||
if(WAYLAND_SCANNER_PKG_FOUND)
|
|
||||||
pkg_get_variable(WAYLAND_SCANNER wayland-scanner wayland_scanner)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(WAYLAND_CLIENT_FOUND AND WAYLAND_PROTOCOLS_DIR AND WAYLAND_SCANNER)
|
|
||||||
set(KEYBOARD_SHORTCUTS_INHIBIT_PROTOCOL
|
|
||||||
"${WAYLAND_PROTOCOLS_DIR}/unstable/keyboard-shortcuts-inhibit/keyboard-shortcuts-inhibit-unstable-v1.xml")
|
|
||||||
|
|
||||||
if(EXISTS ${KEYBOARD_SHORTCUTS_INHIBIT_PROTOCOL})
|
|
||||||
set(WAYLAND_GENERATED_DIR "${CMAKE_CURRENT_BINARY_DIR}/wayland-protocols")
|
|
||||||
file(MAKE_DIRECTORY ${WAYLAND_GENERATED_DIR})
|
|
||||||
|
|
||||||
# Generate client header
|
|
||||||
add_custom_command(
|
|
||||||
OUTPUT "${WAYLAND_GENERATED_DIR}/keyboard-shortcuts-inhibit-unstable-v1-client-protocol.h"
|
|
||||||
COMMAND ${WAYLAND_SCANNER} client-header
|
|
||||||
${KEYBOARD_SHORTCUTS_INHIBIT_PROTOCOL}
|
|
||||||
"${WAYLAND_GENERATED_DIR}/keyboard-shortcuts-inhibit-unstable-v1-client-protocol.h"
|
|
||||||
DEPENDS ${KEYBOARD_SHORTCUTS_INHIBIT_PROTOCOL}
|
|
||||||
VERBATIM
|
|
||||||
)
|
|
||||||
|
|
||||||
# Generate protocol code
|
|
||||||
add_custom_command(
|
|
||||||
OUTPUT "${WAYLAND_GENERATED_DIR}/keyboard-shortcuts-inhibit-unstable-v1-protocol.c"
|
|
||||||
COMMAND ${WAYLAND_SCANNER} private-code
|
|
||||||
${KEYBOARD_SHORTCUTS_INHIBIT_PROTOCOL}
|
|
||||||
"${WAYLAND_GENERATED_DIR}/keyboard-shortcuts-inhibit-unstable-v1-protocol.c"
|
|
||||||
DEPENDS ${KEYBOARD_SHORTCUTS_INHIBIT_PROTOCOL}
|
|
||||||
VERBATIM
|
|
||||||
)
|
|
||||||
|
|
||||||
set(WAYLAND_PROTOCOL_SOURCES
|
|
||||||
"${WAYLAND_GENERATED_DIR}/keyboard-shortcuts-inhibit-unstable-v1-client-protocol.h"
|
|
||||||
"${WAYLAND_GENERATED_DIR}/keyboard-shortcuts-inhibit-unstable-v1-protocol.c"
|
|
||||||
)
|
|
||||||
|
|
||||||
set(HAS_KEYBOARD_SHORTCUTS_INHIBIT TRUE)
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}")
|
add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}")
|
||||||
|
|
||||||
# Define the application target. To change its name, change BINARY_NAME above,
|
# Define the application target. To change its name, change BINARY_NAME above,
|
||||||
@@ -112,11 +63,9 @@ add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}")
|
|||||||
add_executable(${BINARY_NAME}
|
add_executable(${BINARY_NAME}
|
||||||
"main.cc"
|
"main.cc"
|
||||||
"my_application.cc"
|
"my_application.cc"
|
||||||
"wayland_shortcuts_inhibit.cc"
|
|
||||||
"bump_mouse.cc"
|
"bump_mouse.cc"
|
||||||
"bump_mouse_x11.cc"
|
"bump_mouse_x11.cc"
|
||||||
"${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc"
|
"${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc"
|
||||||
${WAYLAND_PROTOCOL_SOURCES}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# Apply the standard set of build settings. This can be removed for applications
|
# Apply the standard set of build settings. This can be removed for applications
|
||||||
@@ -129,13 +78,6 @@ target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK)
|
|||||||
target_link_libraries(${BINARY_NAME} PRIVATE ${CMAKE_DL_LIBS})
|
target_link_libraries(${BINARY_NAME} PRIVATE ${CMAKE_DL_LIBS})
|
||||||
# target_link_libraries(${BINARY_NAME} PRIVATE librustdesk)
|
# target_link_libraries(${BINARY_NAME} PRIVATE librustdesk)
|
||||||
|
|
||||||
# Wayland support for keyboard shortcuts inhibit
|
|
||||||
if(HAS_KEYBOARD_SHORTCUTS_INHIBIT)
|
|
||||||
target_compile_definitions(${BINARY_NAME} PRIVATE HAS_KEYBOARD_SHORTCUTS_INHIBIT)
|
|
||||||
target_include_directories(${BINARY_NAME} PRIVATE ${WAYLAND_GENERATED_DIR})
|
|
||||||
target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::WAYLAND_CLIENT)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# Run the Flutter tool portions of the build. This must not be removed.
|
# Run the Flutter tool portions of the build. This must not be removed.
|
||||||
add_dependencies(${BINARY_NAME} flutter_assemble)
|
add_dependencies(${BINARY_NAME} flutter_assemble)
|
||||||
|
|
||||||
|
|||||||
@@ -6,11 +6,6 @@
|
|||||||
#ifdef GDK_WINDOWING_X11
|
#ifdef GDK_WINDOWING_X11
|
||||||
#include <gdk/gdkx.h>
|
#include <gdk/gdkx.h>
|
||||||
#endif
|
#endif
|
||||||
#if defined(GDK_WINDOWING_WAYLAND) && defined(HAS_KEYBOARD_SHORTCUTS_INHIBIT)
|
|
||||||
#include "wayland_shortcuts_inhibit.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <desktop_multi_window/desktop_multi_window_plugin.h>
|
|
||||||
|
|
||||||
#include "flutter/generated_plugin_registrant.h"
|
#include "flutter/generated_plugin_registrant.h"
|
||||||
|
|
||||||
@@ -96,13 +91,6 @@ static void my_application_activate(GApplication* application) {
|
|||||||
gtk_widget_show(GTK_WIDGET(window));
|
gtk_widget_show(GTK_WIDGET(window));
|
||||||
gtk_widget_show(GTK_WIDGET(view));
|
gtk_widget_show(GTK_WIDGET(view));
|
||||||
|
|
||||||
#if defined(GDK_WINDOWING_WAYLAND) && defined(HAS_KEYBOARD_SHORTCUTS_INHIBIT)
|
|
||||||
// Register callback for sub-windows created by desktop_multi_window plugin
|
|
||||||
// Only sub-windows (remote windows) need keyboard shortcuts inhibition
|
|
||||||
desktop_multi_window_plugin_set_window_created_callback(
|
|
||||||
(WindowCreatedCallback)wayland_shortcuts_inhibit_init_for_subwindow);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
fl_register_plugins(FL_PLUGIN_REGISTRY(view));
|
fl_register_plugins(FL_PLUGIN_REGISTRY(view));
|
||||||
|
|
||||||
g_autoptr(FlStandardMethodCodec) codec = fl_standard_method_codec_new();
|
g_autoptr(FlStandardMethodCodec) codec = fl_standard_method_codec_new();
|
||||||
|
|||||||
@@ -1,244 +0,0 @@
|
|||||||
// Wayland keyboard shortcuts inhibit implementation
|
|
||||||
// Uses the zwp_keyboard_shortcuts_inhibit_manager_v1 protocol to request
|
|
||||||
// the compositor to disable system shortcuts for specific windows.
|
|
||||||
|
|
||||||
#include "wayland_shortcuts_inhibit.h"
|
|
||||||
|
|
||||||
#if defined(GDK_WINDOWING_WAYLAND) && defined(HAS_KEYBOARD_SHORTCUTS_INHIBIT)
|
|
||||||
|
|
||||||
#include <cstring>
|
|
||||||
#include <gdk/gdkwayland.h>
|
|
||||||
#include <wayland-client.h>
|
|
||||||
#include "keyboard-shortcuts-inhibit-unstable-v1-client-protocol.h"
|
|
||||||
|
|
||||||
// Data structure to hold inhibitor state for each window
|
|
||||||
typedef struct {
|
|
||||||
struct zwp_keyboard_shortcuts_inhibit_manager_v1* manager;
|
|
||||||
struct zwp_keyboard_shortcuts_inhibitor_v1* inhibitor;
|
|
||||||
} ShortcutsInhibitData;
|
|
||||||
|
|
||||||
// Cleanup function for ShortcutsInhibitData
|
|
||||||
static void shortcuts_inhibit_data_free(gpointer data) {
|
|
||||||
ShortcutsInhibitData* inhibit_data = static_cast<ShortcutsInhibitData*>(data);
|
|
||||||
if (inhibit_data->inhibitor != NULL) {
|
|
||||||
zwp_keyboard_shortcuts_inhibitor_v1_destroy(inhibit_data->inhibitor);
|
|
||||||
}
|
|
||||||
if (inhibit_data->manager != NULL) {
|
|
||||||
zwp_keyboard_shortcuts_inhibit_manager_v1_destroy(inhibit_data->manager);
|
|
||||||
}
|
|
||||||
g_free(inhibit_data);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wayland registry handler to find the shortcuts inhibit manager
|
|
||||||
static void registry_handle_global(void* data, struct wl_registry* registry,
|
|
||||||
uint32_t name, const char* interface,
|
|
||||||
uint32_t /*version*/) {
|
|
||||||
ShortcutsInhibitData* inhibit_data = static_cast<ShortcutsInhibitData*>(data);
|
|
||||||
if (strcmp(interface,
|
|
||||||
zwp_keyboard_shortcuts_inhibit_manager_v1_interface.name) == 0) {
|
|
||||||
inhibit_data->manager =
|
|
||||||
static_cast<zwp_keyboard_shortcuts_inhibit_manager_v1*>(wl_registry_bind(
|
|
||||||
registry, name, &zwp_keyboard_shortcuts_inhibit_manager_v1_interface,
|
|
||||||
1));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void registry_handle_global_remove(void* /*data*/, struct wl_registry* /*registry*/,
|
|
||||||
uint32_t /*name*/) {
|
|
||||||
// Not needed for this use case
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct wl_registry_listener registry_listener = {
|
|
||||||
registry_handle_global,
|
|
||||||
registry_handle_global_remove,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Inhibitor event handlers
|
|
||||||
static void inhibitor_active(void* /*data*/,
|
|
||||||
struct zwp_keyboard_shortcuts_inhibitor_v1* /*inhibitor*/) {
|
|
||||||
// Inhibitor is now active, shortcuts are being captured
|
|
||||||
}
|
|
||||||
|
|
||||||
static void inhibitor_inactive(void* /*data*/,
|
|
||||||
struct zwp_keyboard_shortcuts_inhibitor_v1* /*inhibitor*/) {
|
|
||||||
// Inhibitor is now inactive, shortcuts restored to compositor
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct zwp_keyboard_shortcuts_inhibitor_v1_listener inhibitor_listener = {
|
|
||||||
inhibitor_active,
|
|
||||||
inhibitor_inactive,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Forward declaration
|
|
||||||
static void uninhibit_keyboard_shortcuts(GtkWindow* window);
|
|
||||||
|
|
||||||
// Inhibit keyboard shortcuts on Wayland for a specific window
|
|
||||||
static void inhibit_keyboard_shortcuts(GtkWindow* window) {
|
|
||||||
GdkDisplay* display = gtk_widget_get_display(GTK_WIDGET(window));
|
|
||||||
if (!GDK_IS_WAYLAND_DISPLAY(display)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if already inhibited for this window
|
|
||||||
if (g_object_get_data(G_OBJECT(window), "shortcuts-inhibit-data") != NULL) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ShortcutsInhibitData* inhibit_data = g_new0(ShortcutsInhibitData, 1);
|
|
||||||
|
|
||||||
struct wl_display* wl_display = gdk_wayland_display_get_wl_display(display);
|
|
||||||
if (wl_display == NULL) {
|
|
||||||
shortcuts_inhibit_data_free(inhibit_data);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct wl_registry* registry = wl_display_get_registry(wl_display);
|
|
||||||
if (registry == NULL) {
|
|
||||||
shortcuts_inhibit_data_free(inhibit_data);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
wl_registry_add_listener(registry, ®istry_listener, inhibit_data);
|
|
||||||
wl_display_roundtrip(wl_display);
|
|
||||||
|
|
||||||
if (inhibit_data->manager == NULL) {
|
|
||||||
wl_registry_destroy(registry);
|
|
||||||
shortcuts_inhibit_data_free(inhibit_data);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
GdkWindow* gdk_window = gtk_widget_get_window(GTK_WIDGET(window));
|
|
||||||
if (gdk_window == NULL) {
|
|
||||||
wl_registry_destroy(registry);
|
|
||||||
shortcuts_inhibit_data_free(inhibit_data);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct wl_surface* surface = gdk_wayland_window_get_wl_surface(gdk_window);
|
|
||||||
if (surface == NULL) {
|
|
||||||
wl_registry_destroy(registry);
|
|
||||||
shortcuts_inhibit_data_free(inhibit_data);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
GdkSeat* gdk_seat = gdk_display_get_default_seat(display);
|
|
||||||
if (gdk_seat == NULL) {
|
|
||||||
wl_registry_destroy(registry);
|
|
||||||
shortcuts_inhibit_data_free(inhibit_data);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct wl_seat* seat = gdk_wayland_seat_get_wl_seat(gdk_seat);
|
|
||||||
if (seat == NULL) {
|
|
||||||
wl_registry_destroy(registry);
|
|
||||||
shortcuts_inhibit_data_free(inhibit_data);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
inhibit_data->inhibitor =
|
|
||||||
zwp_keyboard_shortcuts_inhibit_manager_v1_inhibit_shortcuts(
|
|
||||||
inhibit_data->manager, surface, seat);
|
|
||||||
|
|
||||||
if (inhibit_data->inhibitor == NULL) {
|
|
||||||
wl_registry_destroy(registry);
|
|
||||||
shortcuts_inhibit_data_free(inhibit_data);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add listener to monitor active/inactive state
|
|
||||||
zwp_keyboard_shortcuts_inhibitor_v1_add_listener(
|
|
||||||
inhibit_data->inhibitor, &inhibitor_listener, window);
|
|
||||||
|
|
||||||
wl_display_roundtrip(wl_display);
|
|
||||||
wl_registry_destroy(registry);
|
|
||||||
|
|
||||||
// Associate the inhibit data with the window for cleanup on destroy
|
|
||||||
g_object_set_data_full(G_OBJECT(window), "shortcuts-inhibit-data",
|
|
||||||
inhibit_data, shortcuts_inhibit_data_free);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove keyboard shortcuts inhibitor from a window
|
|
||||||
static void uninhibit_keyboard_shortcuts(GtkWindow* window) {
|
|
||||||
ShortcutsInhibitData* inhibit_data = static_cast<ShortcutsInhibitData*>(
|
|
||||||
g_object_get_data(G_OBJECT(window), "shortcuts-inhibit-data"));
|
|
||||||
|
|
||||||
if (inhibit_data == NULL) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// This will trigger shortcuts_inhibit_data_free via g_object_set_data
|
|
||||||
g_object_set_data(G_OBJECT(window), "shortcuts-inhibit-data", NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Focus event handlers for dynamic inhibitor management
|
|
||||||
static gboolean on_window_focus_in(GtkWidget* widget, GdkEventFocus* /*event*/, gpointer /*user_data*/) {
|
|
||||||
if (GTK_IS_WINDOW(widget)) {
|
|
||||||
inhibit_keyboard_shortcuts(GTK_WINDOW(widget));
|
|
||||||
}
|
|
||||||
return FALSE; // Continue event propagation
|
|
||||||
}
|
|
||||||
|
|
||||||
static gboolean on_window_focus_out(GtkWidget* widget, GdkEventFocus* /*event*/, gpointer /*user_data*/) {
|
|
||||||
if (GTK_IS_WINDOW(widget)) {
|
|
||||||
uninhibit_keyboard_shortcuts(GTK_WINDOW(widget));
|
|
||||||
}
|
|
||||||
return FALSE; // Continue event propagation
|
|
||||||
}
|
|
||||||
|
|
||||||
// Key for marking window as having focus handlers connected
|
|
||||||
static const char* const kFocusHandlersConnectedKey = "shortcuts-inhibit-focus-handlers-connected";
|
|
||||||
// Key for marking window as having a pending realize handler
|
|
||||||
static const char* const kRealizeHandlerConnectedKey = "shortcuts-inhibit-realize-handler-connected";
|
|
||||||
|
|
||||||
// Callback when window is realized (mapped to screen)
|
|
||||||
// Sets up focus-based inhibitor management
|
|
||||||
static void on_window_realize(GtkWidget* widget, gpointer /*user_data*/) {
|
|
||||||
if (GTK_IS_WINDOW(widget)) {
|
|
||||||
// Check if focus handlers are already connected to avoid duplicates
|
|
||||||
if (g_object_get_data(G_OBJECT(widget), kFocusHandlersConnectedKey) != NULL) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Connect focus events for dynamic inhibitor management
|
|
||||||
g_signal_connect(widget, "focus-in-event",
|
|
||||||
G_CALLBACK(on_window_focus_in), NULL);
|
|
||||||
g_signal_connect(widget, "focus-out-event",
|
|
||||||
G_CALLBACK(on_window_focus_out), NULL);
|
|
||||||
|
|
||||||
// Mark as connected to prevent duplicate connections
|
|
||||||
g_object_set_data(G_OBJECT(widget), kFocusHandlersConnectedKey, GINT_TO_POINTER(1));
|
|
||||||
|
|
||||||
// If window already has focus, create inhibitor now
|
|
||||||
if (gtk_window_has_toplevel_focus(GTK_WINDOW(widget))) {
|
|
||||||
inhibit_keyboard_shortcuts(GTK_WINDOW(widget));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Public API: Initialize shortcuts inhibit for a sub-window
|
|
||||||
void wayland_shortcuts_inhibit_init_for_subwindow(void* view) {
|
|
||||||
GtkWidget* widget = GTK_WIDGET(view);
|
|
||||||
GtkWidget* toplevel = gtk_widget_get_toplevel(widget);
|
|
||||||
|
|
||||||
if (toplevel != NULL && GTK_IS_WINDOW(toplevel)) {
|
|
||||||
// Check if already initialized to avoid duplicate realize handlers
|
|
||||||
if (g_object_get_data(G_OBJECT(toplevel), kFocusHandlersConnectedKey) != NULL ||
|
|
||||||
g_object_get_data(G_OBJECT(toplevel), kRealizeHandlerConnectedKey) != NULL) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (gtk_widget_get_realized(toplevel)) {
|
|
||||||
// Window is already realized, set up focus handlers now
|
|
||||||
on_window_realize(toplevel, NULL);
|
|
||||||
} else {
|
|
||||||
// Mark realize handler as connected to prevent duplicate connections
|
|
||||||
// if called again before window is realized
|
|
||||||
g_object_set_data(G_OBJECT(toplevel), kRealizeHandlerConnectedKey, GINT_TO_POINTER(1));
|
|
||||||
// Wait for window to be realized
|
|
||||||
g_signal_connect(toplevel, "realize",
|
|
||||||
G_CALLBACK(on_window_realize), NULL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // defined(GDK_WINDOWING_WAYLAND) && defined(HAS_KEYBOARD_SHORTCUTS_INHIBIT)
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
// Wayland keyboard shortcuts inhibit support
|
|
||||||
// This module provides functionality to inhibit system keyboard shortcuts
|
|
||||||
// on Wayland compositors, allowing remote desktop windows to capture all
|
|
||||||
// key events including Super, Alt+Tab, etc.
|
|
||||||
|
|
||||||
#ifndef WAYLAND_SHORTCUTS_INHIBIT_H_
|
|
||||||
#define WAYLAND_SHORTCUTS_INHIBIT_H_
|
|
||||||
|
|
||||||
#include <gtk/gtk.h>
|
|
||||||
|
|
||||||
#if defined(GDK_WINDOWING_WAYLAND) && defined(HAS_KEYBOARD_SHORTCUTS_INHIBIT)
|
|
||||||
|
|
||||||
// Initialize shortcuts inhibit for a sub-window created by desktop_multi_window plugin.
|
|
||||||
// This sets up focus-based inhibitor management: inhibitor is created when
|
|
||||||
// the window gains focus and destroyed when it loses focus.
|
|
||||||
//
|
|
||||||
// @param view The FlView of the sub-window
|
|
||||||
void wayland_shortcuts_inhibit_init_for_subwindow(void* view);
|
|
||||||
|
|
||||||
#endif // defined(GDK_WINDOWING_WAYLAND) && defined(HAS_KEYBOARD_SHORTCUTS_INHIBIT)
|
|
||||||
|
|
||||||
#endif // WAYLAND_SHORTCUTS_INHIBIT_H_
|
|
||||||
@@ -209,7 +209,39 @@ class MainFlutterWindow: NSWindow {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
case "requestRecordAudio":
|
case "requestRecordAudio":
|
||||||
|
// Request microphone access and trigger system registration
|
||||||
|
// On macOS 13+, apps only appear in System Settings > Privacy & Security > Microphone
|
||||||
|
// after they actually attempt to use the microphone, not just request permission.
|
||||||
|
// We create a brief capture session to ensure proper registration.
|
||||||
AVCaptureDevice.requestAccess(for: .audio, completionHandler: { granted in
|
AVCaptureDevice.requestAccess(for: .audio, completionHandler: { granted in
|
||||||
|
if granted {
|
||||||
|
// Instantiate an audio capture session to trigger macOS registration
|
||||||
|
// This needs to run on main thread to ensure proper lifecycle
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
if let audioDevice = AVCaptureDevice.default(for: .audio) {
|
||||||
|
do {
|
||||||
|
let audioInput = try AVCaptureDeviceInput(device: audioDevice)
|
||||||
|
let captureSession = AVCaptureSession()
|
||||||
|
captureSession.beginConfiguration()
|
||||||
|
if captureSession.canAddInput(audioInput) {
|
||||||
|
captureSession.addInput(audioInput)
|
||||||
|
}
|
||||||
|
captureSession.commitConfiguration()
|
||||||
|
// Start and immediately stop the session to trigger registration
|
||||||
|
captureSession.startRunning()
|
||||||
|
// Minimum delay required for macOS to register the app in System Settings
|
||||||
|
let registrationDelay: TimeInterval = 0.1
|
||||||
|
// Keep a strong reference and stop after the registration delay
|
||||||
|
DispatchQueue.main.asyncAfter(deadline: .now() + registrationDelay) { [captureSession] in
|
||||||
|
captureSession.stopRunning()
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
NSLog("[RustDesk] Failed to create audio capture session: %@", error.localizedDescription)
|
||||||
|
NSLog("[RustDesk] The app may not appear in System Settings > Privacy & Security > Microphone")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
result(granted)
|
result(granted)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2759,11 +2759,6 @@ pub fn main_get_common(key: String) -> String {
|
|||||||
None => "",
|
None => "",
|
||||||
}
|
}
|
||||||
.to_string();
|
.to_string();
|
||||||
} else if key == "has-gnome-shortcuts-inhibitor-permission" {
|
|
||||||
#[cfg(target_os = "linux")]
|
|
||||||
return crate::platform::linux::has_gnome_shortcuts_inhibitor_permission().to_string();
|
|
||||||
#[cfg(not(target_os = "linux"))]
|
|
||||||
return false.to_string();
|
|
||||||
} else {
|
} else {
|
||||||
if key.starts_with("download-data-") {
|
if key.starts_with("download-data-") {
|
||||||
let id = key.replace("download-data-", "");
|
let id = key.replace("download-data-", "");
|
||||||
@@ -2925,29 +2920,6 @@ pub fn main_set_common(_key: String, _value: String) {
|
|||||||
} else if _key == "cancel-downloader" {
|
} else if _key == "cancel-downloader" {
|
||||||
crate::hbbs_http::downloader::cancel(&_value);
|
crate::hbbs_http::downloader::cancel(&_value);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_os = "linux")]
|
|
||||||
if _key == "clear-gnome-shortcuts-inhibitor-permission" {
|
|
||||||
std::thread::spawn(move || {
|
|
||||||
let (success, msg) =
|
|
||||||
match crate::platform::linux::clear_gnome_shortcuts_inhibitor_permission() {
|
|
||||||
Ok(_) => (true, "".to_owned()),
|
|
||||||
Err(e) => (false, e.to_string()),
|
|
||||||
};
|
|
||||||
let data = HashMap::from([
|
|
||||||
(
|
|
||||||
"name",
|
|
||||||
serde_json::json!("clear-gnome-shortcuts-inhibitor-permission-res"),
|
|
||||||
),
|
|
||||||
("success", serde_json::json!(success)),
|
|
||||||
("msg", serde_json::json!(msg)),
|
|
||||||
]);
|
|
||||||
let _res = flutter::push_global_event(
|
|
||||||
flutter::APP_TYPE_MAIN,
|
|
||||||
serde_json::ser::to_string(&data).unwrap_or("".to_owned()),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn session_get_common_sync(
|
pub fn session_get_common_sync(
|
||||||
|
|||||||
@@ -220,7 +220,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("default_proxy_tip", "Default protocol and port are Socks5 and 1080"),
|
("default_proxy_tip", "Default protocol and port are Socks5 and 1080"),
|
||||||
("no_audio_input_device_tip", "No audio input device found."),
|
("no_audio_input_device_tip", "No audio input device found."),
|
||||||
("clear_Wayland_screen_selection_tip", "After clearing the screen selection, you can reselect the screen to share."),
|
("clear_Wayland_screen_selection_tip", "After clearing the screen selection, you can reselect the screen to share."),
|
||||||
("confirm_clear_Wayland_screen_selection_tip", "Are you sure you want to clear the Wayland screen selection?"),
|
("confirm_clear_Wayland_screen_selection_tip", "Are you sure to clear the Wayland screen selection?"),
|
||||||
("android_new_voice_call_tip", "A new voice call request was received. If you accept, the audio will switch to voice communication."),
|
("android_new_voice_call_tip", "A new voice call request was received. If you accept, the audio will switch to voice communication."),
|
||||||
("texture_render_tip", "Use texture rendering to make the pictures smoother. You could try disabling this option if you encounter rendering issues."),
|
("texture_render_tip", "Use texture rendering to make the pictures smoother. You could try disabling this option if you encounter rendering issues."),
|
||||||
("floating_window_tip", "It helps to keep RustDesk background service"),
|
("floating_window_tip", "It helps to keep RustDesk background service"),
|
||||||
|
|||||||
@@ -738,6 +738,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Changelog", "변경 기록"),
|
("Changelog", "변경 기록"),
|
||||||
("keep-awake-during-outgoing-sessions-label", "발신 세션 중 화면 켜짐 유지"),
|
("keep-awake-during-outgoing-sessions-label", "발신 세션 중 화면 켜짐 유지"),
|
||||||
("keep-awake-during-incoming-sessions-label", "수신 세션 중 화면 켜짐 유지"),
|
("keep-awake-during-incoming-sessions-label", "수신 세션 중 화면 켜짐 유지"),
|
||||||
("Continue with {}", "{}(으)로 계속"),
|
("Continue with {}", "{} (으)로 계속"),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -673,21 +673,21 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("dont-show-again-tip", "Não mostrar novamente"),
|
("dont-show-again-tip", "Não mostrar novamente"),
|
||||||
("Take screenshot", "Capturar de tela"),
|
("Take screenshot", "Capturar de tela"),
|
||||||
("Taking screenshot", "Capturando tela"),
|
("Taking screenshot", "Capturando tela"),
|
||||||
("screenshot-merged-screen-not-supported-tip", "Mesclar a captura de tela de múltiplos monitores não é suportada no momento. Por favor, alterne para um único monitor e tente novamente."),
|
("screenshot-merged-screen-not-supported-tip", ""),
|
||||||
("screenshot-action-tip", "Por favor, selecione como seguir com a captura de tela."),
|
("screenshot-action-tip", ""),
|
||||||
("Save as", "Salvar como"),
|
("Save as", "Salvar como"),
|
||||||
("Copy to clipboard", "Copiar para área de transferência"),
|
("Copy to clipboard", "Copiar para área de transferência"),
|
||||||
("Enable remote printer", "Habilitar impressora remota"),
|
("Enable remote printer", "Habilitar impressora remota"),
|
||||||
("Downloading {}", "Baixando {}"),
|
("Downloading {}", ""),
|
||||||
("{} Update", "Atualização do {}"),
|
("{} Update", ""),
|
||||||
("{}-to-update-tip", "{} será fechado agora para instalar a nova versão."),
|
("{}-to-update-tip", ""),
|
||||||
("download-new-version-failed-tip", "Falha no download. Você pode tentar novamente ou clicar no botão \"Download\" para baixar da página releases e atualizar manualmente."),
|
("download-new-version-failed-tip", "Falha no download. Você pode tentar novamente ou clicar no botão \"Download\" para baixar da página releases e atualizar manualmente."),
|
||||||
("Auto update", "Atualização automática"),
|
("Auto update", "Atualização automática"),
|
||||||
("update-failed-check-msi-tip", "Falha na verificação do método de instalação. Clique no botão \"Download\" para baixar da página releases e atualizar manualmente."),
|
("update-failed-check-msi-tip", "Falha na verificação do método de instalação. Clique no botão \"Download\" para baixar da página releases e atualizar manualmente."),
|
||||||
("websocket_tip", "Usando WebSocket, apenas conexões via relay são suportadas."),
|
("websocket_tip", "Usando WebSocket, apenas conexões via relay são suportadas."),
|
||||||
("Use WebSocket", "Usar WebSocket"),
|
("Use WebSocket", "Usar WebSocket"),
|
||||||
("Trackpad speed", "Velocidade do trackpad"),
|
("Trackpad speed", "Velocidade do trackpad"),
|
||||||
("Default trackpad speed", "Velocidade padrão do trackpad"),
|
("Default trackpad speed", ""),
|
||||||
("Numeric one-time password", "Senha numérica de uso único"),
|
("Numeric one-time password", "Senha numérica de uso único"),
|
||||||
("Enable IPv6 P2P connection", "Habilitar conexão IPv6 P2P"),
|
("Enable IPv6 P2P connection", "Habilitar conexão IPv6 P2P"),
|
||||||
("Enable UDP hole punching", "Habilitar UDP hole punching"),
|
("Enable UDP hole punching", "Habilitar UDP hole punching"),
|
||||||
@@ -717,11 +717,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Virtual mouse size", "Tamanho do mouse virtual"),
|
("Virtual mouse size", "Tamanho do mouse virtual"),
|
||||||
("Small", "Pequeno"),
|
("Small", "Pequeno"),
|
||||||
("Large", "Grande"),
|
("Large", "Grande"),
|
||||||
("Show virtual joystick", "Mostrar joystick virtual"),
|
("Show virtual joystick", ""),
|
||||||
("Edit note", "Editar nota"),
|
("Edit note", "Editar nota"),
|
||||||
("Alias", "Apelido"),
|
("Alias", "Apelido"),
|
||||||
("ScrollEdge", "Rolagem nas bordas"),
|
("ScrollEdge", "Rolagem nas bordas"),
|
||||||
("Allow insecure TLS fallback", "Permitir fallback TLS inseguro"),
|
("Allow insecure TLS fallback", ""),
|
||||||
("allow-insecure-tls-fallback-tip", "Por padrão, o RustDesk verifica o certificado do servidor para protocolos que usam TLS.\nCom esta opção habilitada, o RustDesk ignorará a verificação e prosseguirá em caso de falha."),
|
("allow-insecure-tls-fallback-tip", "Por padrão, o RustDesk verifica o certificado do servidor para protocolos que usam TLS.\nCom esta opção habilitada, o RustDesk ignorará a verificação e prosseguirá em caso de falha."),
|
||||||
("Disable UDP", "Desabilitar UDP"),
|
("Disable UDP", "Desabilitar UDP"),
|
||||||
("disable-udp-tip", "Controla se deve usar somente TCP.\nCom esta opção habilitada, o RustDesk não usará mais UDP 21116, TCP 21116 será usado no lugar."),
|
("disable-udp-tip", "Controla se deve usar somente TCP.\nCom esta opção habilitada, o RustDesk não usará mais UDP 21116, TCP 21116 será usado no lugar."),
|
||||||
|
|||||||
@@ -2088,122 +2088,3 @@ pub fn is_selinux_enforcing() -> bool {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the app ID for shortcuts inhibitor permission.
|
|
||||||
/// Returns different ID based on whether running in Flatpak or native.
|
|
||||||
/// The ID must match the installed .desktop filename, as GNOME Shell's
|
|
||||||
/// inhibitShortcutsDialog uses `Shell.WindowTracker.get_window_app(window).get_id()`.
|
|
||||||
fn get_shortcuts_inhibitor_app_id() -> String {
|
|
||||||
if is_flatpak() {
|
|
||||||
// In Flatpak, FLATPAK_ID is set automatically by the runtime to the app ID
|
|
||||||
// (e.g., "com.rustdesk.RustDesk"). This is the most reliable source.
|
|
||||||
// Fall back to constructing from app name if not available.
|
|
||||||
match std::env::var("FLATPAK_ID") {
|
|
||||||
Ok(id) if !id.is_empty() => format!("{}.desktop", id),
|
|
||||||
_ => {
|
|
||||||
let app_name = crate::get_app_name();
|
|
||||||
format!("com.{}.{}.desktop", app_name.to_lowercase(), app_name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
format!("{}.desktop", crate::get_app_name().to_lowercase())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const PERMISSION_STORE_DEST: &str = "org.freedesktop.impl.portal.PermissionStore";
|
|
||||||
const PERMISSION_STORE_PATH: &str = "/org/freedesktop/impl/portal/PermissionStore";
|
|
||||||
const PERMISSION_STORE_IFACE: &str = "org.freedesktop.impl.portal.PermissionStore";
|
|
||||||
|
|
||||||
/// Clear GNOME shortcuts inhibitor permission via D-Bus.
|
|
||||||
/// This allows the permission dialog to be shown again.
|
|
||||||
pub fn clear_gnome_shortcuts_inhibitor_permission() -> ResultType<()> {
|
|
||||||
let app_id = get_shortcuts_inhibitor_app_id();
|
|
||||||
log::info!(
|
|
||||||
"Clearing shortcuts inhibitor permission for app_id: {}, is_flatpak: {}",
|
|
||||||
app_id,
|
|
||||||
is_flatpak()
|
|
||||||
);
|
|
||||||
|
|
||||||
let conn = dbus::blocking::Connection::new_session()?;
|
|
||||||
let proxy = conn.with_proxy(
|
|
||||||
PERMISSION_STORE_DEST,
|
|
||||||
PERMISSION_STORE_PATH,
|
|
||||||
std::time::Duration::from_secs(3),
|
|
||||||
);
|
|
||||||
|
|
||||||
// DeletePermission(s table, s id, s app) -> ()
|
|
||||||
let result: Result<(), dbus::Error> = proxy.method_call(
|
|
||||||
PERMISSION_STORE_IFACE,
|
|
||||||
"DeletePermission",
|
|
||||||
("gnome", "shortcuts-inhibitor", app_id.as_str()),
|
|
||||||
);
|
|
||||||
|
|
||||||
match result {
|
|
||||||
Ok(()) => {
|
|
||||||
log::info!("Successfully cleared GNOME shortcuts inhibitor permission");
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
let err_name = e.name().unwrap_or("");
|
|
||||||
// If the permission doesn't exist, that's also fine
|
|
||||||
if err_name == "org.freedesktop.portal.Error.NotFound"
|
|
||||||
|| err_name == "org.freedesktop.DBus.Error.UnknownObject"
|
|
||||||
|| err_name == "org.freedesktop.DBus.Error.ServiceUnknown"
|
|
||||||
{
|
|
||||||
log::info!("GNOME shortcuts inhibitor permission was not set ({})", err_name);
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
bail!("Failed to clear permission: {}", e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Check if GNOME shortcuts inhibitor permission exists.
|
|
||||||
pub fn has_gnome_shortcuts_inhibitor_permission() -> bool {
|
|
||||||
let app_id = get_shortcuts_inhibitor_app_id();
|
|
||||||
|
|
||||||
let conn = match dbus::blocking::Connection::new_session() {
|
|
||||||
Ok(c) => c,
|
|
||||||
Err(e) => {
|
|
||||||
log::debug!("Failed to connect to session bus: {}", e);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let proxy = conn.with_proxy(
|
|
||||||
PERMISSION_STORE_DEST,
|
|
||||||
PERMISSION_STORE_PATH,
|
|
||||||
std::time::Duration::from_secs(3),
|
|
||||||
);
|
|
||||||
|
|
||||||
// Lookup(s table, s id) -> (a{sas} permissions, v data)
|
|
||||||
// We only need the permissions dict; check if app_id is a key.
|
|
||||||
let result: Result<
|
|
||||||
(
|
|
||||||
std::collections::HashMap<String, Vec<String>>,
|
|
||||||
dbus::arg::Variant<Box<dyn dbus::arg::RefArg>>,
|
|
||||||
),
|
|
||||||
dbus::Error,
|
|
||||||
> = proxy.method_call(
|
|
||||||
PERMISSION_STORE_IFACE,
|
|
||||||
"Lookup",
|
|
||||||
("gnome", "shortcuts-inhibitor"),
|
|
||||||
);
|
|
||||||
|
|
||||||
match result {
|
|
||||||
Ok((permissions, _)) => {
|
|
||||||
let found = permissions.contains_key(&app_id);
|
|
||||||
log::debug!(
|
|
||||||
"Shortcuts inhibitor permission lookup: app_id={}, found={}, keys={:?}",
|
|
||||||
app_id,
|
|
||||||
found,
|
|
||||||
permissions.keys().collect::<Vec<_>>()
|
|
||||||
);
|
|
||||||
found
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
log::debug!("Failed to query shortcuts inhibitor permission: {}", e);
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user