Compare commits

..

5 Commits

Author SHA1 Message Date
copilot-swe-agent[bot]
36b4f765fd Improve code quality: use named constant and clearer error logs
- Extract 0.1 second delay to named constant 'registrationDelay' with explanatory comment
- Split error message into separate log lines for better readability

Co-authored-by: rustdesk <71636191+rustdesk@users.noreply.github.com>
2026-02-07 03:46:24 +00:00
copilot-swe-agent[bot]
370b467b71 Address code review feedback: fix memory lifecycle and improve error message
- Move capture session lifecycle to main thread
- Add strong capture of captureSession in asyncAfter block to prevent premature deallocation
- Improve error message to inform user about potential impact and suggested action

Co-authored-by: rustdesk <71636191+rustdesk@users.noreply.github.com>
2026-02-07 03:45:47 +00:00
copilot-swe-agent[bot]
780c396541 Add detailed comments explaining microphone permission fix
Co-authored-by: rustdesk <71636191+rustdesk@users.noreply.github.com>
2026-02-07 03:45:08 +00:00
copilot-swe-agent[bot]
db5a1f29e7 Fix macOS microphone permission registration
Instantiate AVCaptureSession when requesting audio permissions to ensure the app properly registers in System Settings > Privacy & Security > Microphone. This fix addresses the issue where RustDesk was not appearing in the microphone permissions list on macOS.

Co-authored-by: rustdesk <71636191+rustdesk@users.noreply.github.com>
2026-02-07 03:44:39 +00:00
copilot-swe-agent[bot]
b7d25ef389 Initial plan 2026-02-07 03:34:56 +00:00
4 changed files with 41 additions and 28 deletions

View File

@@ -68,7 +68,6 @@ class _RemotePageState extends State<RemotePage> with WidgetsBindingObserver {
double _viewInsetsBottom = 0;
final _uniqueKey = UniqueKey();
Timer? _timerDidChangeMetrics;
Timer? _iosKeyboardWorkaroundTimer;
final _blockableOverlayState = BlockableOverlayState();
@@ -141,7 +140,6 @@ class _RemotePageState extends State<RemotePage> with WidgetsBindingObserver {
await gFFI.close();
_timer?.cancel();
_timerDidChangeMetrics?.cancel();
_iosKeyboardWorkaroundTimer?.cancel();
gFFI.dialogManager.dismissAll();
await SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual,
overlays: SystemUiOverlay.values);
@@ -208,24 +206,7 @@ class _RemotePageState extends State<RemotePage> with WidgetsBindingObserver {
gFFI.ffiModel.pi.version.isNotEmpty) {
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 {
_iosKeyboardWorkaroundTimer?.cancel();
_iosKeyboardWorkaroundTimer = null;
_timer?.cancel();
_timer = Timer(kMobileDelaySoftKeyboardFocus, () {
SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual,

View File

@@ -209,7 +209,39 @@ class MainFlutterWindow: NSWindow {
break
}
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
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 {
result(granted)
}

View File

@@ -738,6 +738,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Changelog", "변경 기록"),
("keep-awake-during-outgoing-sessions-label", "발신 세션 중 화면 켜짐 유지"),
("keep-awake-during-incoming-sessions-label", "수신 세션 중 화면 켜짐 유지"),
("Continue with {}", "{}(으)로 계속"),
("Continue with {}", "{} (으)로 계속"),
].iter().cloned().collect();
}

View File

@@ -673,21 +673,21 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("dont-show-again-tip", "Não mostrar novamente"),
("Take screenshot", "Capturar de 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-action-tip", "Por favor, selecione como seguir com a captura de tela."),
("screenshot-merged-screen-not-supported-tip", ""),
("screenshot-action-tip", ""),
("Save as", "Salvar como"),
("Copy to clipboard", "Copiar para área de transferência"),
("Enable remote printer", "Habilitar impressora remota"),
("Downloading {}", "Baixando {}"),
("{} Update", "Atualização do {}"),
("{}-to-update-tip", "{} será fechado agora para instalar a nova versão."),
("Downloading {}", ""),
("{} Update", ""),
("{}-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."),
("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."),
("websocket_tip", "Usando WebSocket, apenas conexões via relay são suportadas."),
("Use WebSocket", "Usar WebSocket"),
("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"),
("Enable IPv6 P2P connection", "Habilitar conexão IPv6 P2P"),
("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"),
("Small", "Pequeno"),
("Large", "Grande"),
("Show virtual joystick", "Mostrar joystick virtual"),
("Show virtual joystick", ""),
("Edit note", "Editar nota"),
("Alias", "Apelido"),
("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."),
("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."),