From 70bb3fed16f9850cbbeb8b6bd1aa85d6087d773c Mon Sep 17 00:00:00 2001 From: Asura Date: Tue, 28 Jun 2022 18:56:54 -0700 Subject: [PATCH 01/14] fix(pynput): Altgr uses send event, others use fake input --- pynput_service.py | 53 +++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 44 insertions(+), 9 deletions(-) diff --git a/pynput_service.py b/pynput_service.py index 90b8741ce..14a8f1793 100644 --- a/pynput_service.py +++ b/pynput_service.py @@ -1,12 +1,10 @@ from pynput.keyboard import Key, Controller from pynput.keyboard._xorg import KeyCode from pynput._util.xorg import display_manager +import Xlib import os import sys import socket -from Xlib.ext.xtest import fake_input -from Xlib import X -import Xlib KeyCode._from_symbol("\0") # test @@ -17,15 +15,52 @@ class MyController(Controller): :param event: The *X* keyboard event. :param int keysym: The keysym to handle. """ + event = Xlib.display.event.KeyPress if is_press \ + else Xlib.display.event.KeyRelease keysym = self._keysym(key) - keycode = self._display.keysym_to_keycode(keysym) - with display_manager(self._display) as dm: - Xlib.ext.xtest.fake_input( - dm, - Xlib.X.KeyPress if is_press else Xlib.X.KeyRelease, - keycode) + # Make sure to verify that the key was resolved + if keysym is None: + raise self.InvalidKeyException(key) + # If the key has a virtual key code, use that immediately with + # fake_input; fake input,being an X server extension, has access to + # more internal state that we do + if key.vk is not None: + with display_manager(self._display) as dm: + Xlib.ext.xtest.fake_input( + dm, + Xlib.X.KeyPress if is_press else Xlib.X.KeyRelease, + dm.keysym_to_keycode(key.vk)) + + # Otherwise use XSendEvent; we need to use this in the general case to + # work around problems with keyboard layouts + else: + try: + keycode = self._display.keysym_to_keycode(keysym) + with self.modifiers as modifiers: + alt_gr = Key.alt_gr in modifiers + if alt_gr: + keycode, shift_state = self.keyboard_mapping[keysym] + self._send_key(event, keycode, shift_state) + else: + with display_manager(self._display) as dm: + Xlib.ext.xtest.fake_input( + dm, + Xlib.X.KeyPress if is_press else Xlib.X.KeyRelease, + keycode) + except KeyError: + with self._borrow_lock: + keycode, index, count = self._borrows[keysym] + self._send_key( + event, + keycode, + index_to_shift(self._display, index)) + count += 1 if is_press else -1 + self._borrows[keysym] = (keycode, index, count) + + # Notify any running listeners + self._emit('_on_fake_event', key, is_press) keyboard = MyController() From c745bf41114b46dd69f3b854a72a6fa262fc3cfd Mon Sep 17 00:00:00 2001 From: rustdesk Date: Thu, 30 Jun 2022 01:19:38 +0800 Subject: [PATCH 02/14] https://github.com/rustdesk/rustdesk/issues541/ --- src/lang.rs | 26 ++++++++++++++++++++++++-- src/lang/cn.rs | 1 + src/lang/cs.rs | 1 + src/lang/da.rs | 1 + src/lang/de.rs | 1 + src/lang/eo.rs | 1 + src/lang/es.rs | 1 + src/lang/fr.rs | 1 + src/lang/id.rs | 2 +- src/lang/it.rs | 1 + src/lang/ptbr.rs | 1 + src/lang/ru.rs | 3 ++- src/lang/sk.rs | 1 + src/lang/template.rs | 3 ++- src/lang/tr.rs | 1 + src/lang/tw.rs | 6 +++--- src/ui.rs | 9 +++++++-- src/ui/index.tis | 36 ++++++++++++++++++++++++++++++++++++ src/ui/remote.rs | 6 +++--- 19 files changed, 89 insertions(+), 13 deletions(-) diff --git a/src/lang.rs b/src/lang.rs index 4b4998fc1..b37c4beb6 100644 --- a/src/lang.rs +++ b/src/lang.rs @@ -1,21 +1,43 @@ +use serde_json::{json, value::Value}; use std::ops::Deref; mod cn; mod cs; mod da; -mod sk; mod de; mod en; -mod es; mod eo; +mod es; mod fr; mod id; mod it; mod ptbr; mod ru; +mod sk; mod tr; mod tw; +lazy_static::lazy_static! { + pub static ref LANGS: Value = + json!(vec![ + ("en", "English"), + ("it", "Italiano"), + ("fr", "Français"), + ("de", "Deutsch"), + ("cn", "简体中文"), + ("tw", "繁體中文"), + ("pt", "Português"), + ("es", "Español"), + ("ru", "Русский"), + ("sk", "Slovenčina"), + ("id", "Indonesia"), + ("cs", "Čeština"), + ("da", "Dansk"), + ("eo", "Esperanto"), + ("tr", "Türkçe"), + ]); +} + #[cfg(not(any(target_os = "android", target_os = "ios")))] pub fn translate(name: String) -> String { let locale = sys_locale::get_locale().unwrap_or_default().to_lowercase(); diff --git a/src/lang/cn.rs b/src/lang/cn.rs index a09ab8d39..606f2a6e7 100644 --- a/src/lang/cn.rs +++ b/src/lang/cn.rs @@ -279,5 +279,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Turned off", "退出"), ("In privacy mode", "进入隐私模式"), ("Out privacy mode", "退出隐私模式"), + ("Language", "语言"), ].iter().cloned().collect(); } diff --git a/src/lang/cs.rs b/src/lang/cs.rs index 80e1568eb..696eabe2a 100644 --- a/src/lang/cs.rs +++ b/src/lang/cs.rs @@ -279,5 +279,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Turned off", "Vypnutý"), ("In privacy mode", "v režimu soukromí"), ("Out privacy mode", "mimo režim soukromí"), + ("Language", ""), ].iter().cloned().collect(); } diff --git a/src/lang/da.rs b/src/lang/da.rs index 429b08b05..da191e8c1 100644 --- a/src/lang/da.rs +++ b/src/lang/da.rs @@ -279,5 +279,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Turned off", "Slukket"), ("In privacy mode", "I databeskyttelsestilstand"), ("Out privacy mode", "Databeskyttelsestilstand fra"), + ("Language", ""), ].iter().cloned().collect(); } diff --git a/src/lang/de.rs b/src/lang/de.rs index 8bbfbb4c4..0307501b2 100644 --- a/src/lang/de.rs +++ b/src/lang/de.rs @@ -279,5 +279,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Turned off", "Ausgeschaltet"), ("In privacy mode", "im Datenschutzmodus"), ("Out privacy mode", "Datenschutzmodus aus"), + ("Language", "Sprache"), ].iter().cloned().collect(); } diff --git a/src/lang/eo.rs b/src/lang/eo.rs index 49c9f38fa..75f3bc084 100644 --- a/src/lang/eo.rs +++ b/src/lang/eo.rs @@ -279,5 +279,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Turned off", ""), ("In privacy mode", ""), ("Out privacy mode", ""), + ("Language", ""), ].iter().cloned().collect(); } diff --git a/src/lang/es.rs b/src/lang/es.rs index ba4c671a8..f55071fda 100644 --- a/src/lang/es.rs +++ b/src/lang/es.rs @@ -279,5 +279,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Turned off", "Apagado"), ("In privacy mode", "En modo de privacidad"), ("Out privacy mode", "Fuera del modo de privacidad"), + ("Language", ""), ].iter().cloned().collect(); } diff --git a/src/lang/fr.rs b/src/lang/fr.rs index d2d41c550..64500f9a6 100644 --- a/src/lang/fr.rs +++ b/src/lang/fr.rs @@ -279,5 +279,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Turned off", "Éteindre"), ("In privacy mode", "en mode privé"), ("Out privacy mode", "hors mode de confidentialité"), + ("Language", "Langue"), ].iter().cloned().collect(); } diff --git a/src/lang/id.rs b/src/lang/id.rs index 2e2f2fd1c..d3da71833 100644 --- a/src/lang/id.rs +++ b/src/lang/id.rs @@ -158,7 +158,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Allow using clipboard", "Izinkan menggunakan papan klip"), ("Allow hearing sound", "Izinkan mendengarkan suara"), ("Allow file copy and paste", "Izinkan penyalinan dan tempel file"), - ("File transfer", "Transfer file"), ("Connected", "Terkoneksi"), ("Direct and encrypted connection", "Koneksi langsung dan terenkripsi"), ("Relayed and encrypted connection", "Koneksi relai dan terenkripsi"), @@ -280,5 +279,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Turned off", "Matikan"), ("In privacy mode", "Dalam mode privasi"), ("Out privacy mode", "Keluar dari mode privasi"), + ("Language", ""), ].iter().cloned().collect(); } diff --git a/src/lang/it.rs b/src/lang/it.rs index 4bf692169..fa4c99e84 100644 --- a/src/lang/it.rs +++ b/src/lang/it.rs @@ -279,5 +279,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Turned off", "Spegni"), ("In privacy mode", "In modalità privacy"), ("Out privacy mode", "Fuori modalità privacy"), + ("Language", "Linguaggio"), ].iter().cloned().collect(); } diff --git a/src/lang/ptbr.rs b/src/lang/ptbr.rs index e51d5f5f9..6ff30a3cd 100644 --- a/src/lang/ptbr.rs +++ b/src/lang/ptbr.rs @@ -279,5 +279,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Turned off", "Desligado"), ("In privacy mode", "No modo de privacidade"), ("Out privacy mode", "Fora do modo de privacidade"), + ("Language", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ru.rs b/src/lang/ru.rs index 88245b1d0..a50563674 100644 --- a/src/lang/ru.rs +++ b/src/lang/ru.rs @@ -263,9 +263,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("android_version_audio_tip", "Текущая версия Android не поддерживает захват звука, обновите ее до Android 10 или выше."), ("android_start_service_tip", "Нажмите [Запуск промежуточного сервера] или ОТКРЫТЬ разрешение [Захват экрана], чтобы запустить службу демонстрации экрана."), ("Account", "Аккаунт"), - ("Quit", "Выйти"), ("Overwrite", "Перезаписать"), ("This file exists, skip or overwrite this file?", "Этот файл существует, пропустить или перезаписать этот файл?"), + ("Quit", "Выйти"), ("doc_mac_permission", "https://rustdesk.com/docs/ru/manual/mac/#включение-разрешений"), ("Help", "Помощь"), ("Failed", "Неуспешный"), @@ -279,5 +279,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Turned off", "Выключен"), ("In privacy mode", "В режиме конфиденциальности"), ("Out privacy mode", "Выход из режима конфиденциальности"), + ("Language", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sk.rs b/src/lang/sk.rs index 6cea1479b..252faaf17 100644 --- a/src/lang/sk.rs +++ b/src/lang/sk.rs @@ -279,5 +279,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Turned off", "Vypnutý"), ("In privacy mode", "V režime súkromia"), ("Out privacy mode", "Mimo režimu súkromia"), + ("Language", ""), ].iter().cloned().collect(); } diff --git a/src/lang/template.rs b/src/lang/template.rs index 91c572e44..6ad92db6e 100644 --- a/src/lang/template.rs +++ b/src/lang/template.rs @@ -264,7 +264,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("android_start_service_tip", ""), ("Account", ""), ("Overwrite", ""), - ("This file exists, skip or overwrite this file?", "") + ("This file exists, skip or overwrite this file?", ""), ("Quit", ""), ("doc_mac_permission", ""), ("Help", ""), @@ -279,5 +279,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Turned off", ""), ("In privacy mode", ""), ("Out privacy mode", ""), + ("Language", ""), ].iter().cloned().collect(); } diff --git a/src/lang/tr.rs b/src/lang/tr.rs index d82901298..37502567c 100644 --- a/src/lang/tr.rs +++ b/src/lang/tr.rs @@ -279,5 +279,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Turned off", "Kapalı"), ("In privacy mode", "Gizlilik modunda"), ("Out privacy mode", "Gizlilik modu dışında"), + ("Language", ""), ].iter().cloned().collect(); } diff --git a/src/lang/tw.rs b/src/lang/tw.rs index 2de3f4414..0d81d7a8b 100644 --- a/src/lang/tw.rs +++ b/src/lang/tw.rs @@ -262,13 +262,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("android_stop_service_tip", "關閉服務將自動關閉所有已建立的連接。"), ("android_version_audio_tip", "目前的 Android 版本不支持音訊錄製,請升級至 Android 10 或以上版本。"), ("android_start_service_tip", "點擊 「啟動服務」 或啟用 「畫面錄製」 權限以開啟手機畫面共享服務。"), - ("Account", "帳號"), - ("Quit", "退出"), + ("Account", "帳戶"), ("Overwrite", "覆寫"), ("This file exists, skip or overwrite this file?", "此檔案/資料夾已存在,要跳過或是覆寫此檔案嗎?"), + ("Quit", "退出"), ("doc_mac_permission", "https://rustdesk.com/docs/zh-tw/manual/mac/#啟用權限"), ("Help", "幫助"), - ("Account", "帳戶"), ("Failed", "失敗"), ("Succeeded", "成功"), ("Someone turns on privacy mode, exit", "其他用戶開啟隱私模式,退出"), @@ -280,5 +279,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Turned off", "退出"), ("In privacy mode", "開啟隱私模式"), ("Out privacy mode", "退出隱私模式"), + ("Language", "語言"), ].iter().cloned().collect(); } diff --git a/src/ui.rs b/src/ui.rs index b93c11d44..fc56d437c 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -3,9 +3,9 @@ mod cm; mod inline; #[cfg(target_os = "macos")] mod macos; +pub mod remote; #[cfg(target_os = "windows")] pub mod win_privacy; -pub mod remote; use crate::common::SOFTWARE_UPDATE_URL; use crate::ipc; use hbb_common::{ @@ -702,7 +702,7 @@ impl UI { let p = "explorer"; #[cfg(target_os = "macos")] let p = "open"; - #[cfg(target_os = "linux")] + #[cfg(target_os = "linux")] let p = if std::path::Path::new("/usr/bin/firefox").exists() { "firefox" } else { @@ -753,6 +753,10 @@ impl UI { self.get_option_("custom-rendezvous-server"), ) } + + fn get_langs(&self) -> String { + crate::lang::LANGS.to_string() + } } impl sciter::EventHandler for UI { @@ -829,6 +833,7 @@ impl sciter::EventHandler for UI { fn discover(); fn get_lan_peers(); fn get_uuid(); + fn get_langs(); } } diff --git a/src/ui/index.tis b/src/ui/index.tis index 70cc8f870..34ae91fac 100644 --- a/src/ui/index.tis +++ b/src/ui/index.tis @@ -163,8 +163,43 @@ class AudioInputs: Reactor.Component { } this.toggleMenuState(); } +}; + +class Languages: Reactor.Component { + function render() { + var langs = JSON.parse(handler.get_langs()); + var me = this; + self.timer(1ms, function() { me.toggleMenuState() }); + return
  • {translate('Language')} + +
  • {svg_checkmark}Default
  • +
    + {langs.map(function(lang) { + return
  • {svg_checkmark}{lang[1]}
  • ; + })} +
    +
  • ; + } + + + function toggleMenuState() { + var cur = handler.get_local_option("lang") || "default"; + for (var el in this.$$(menu#languages>li)) { + var selected = cur == el.id; + el.attributes.toggleClass("selected", selected); + } + } + + event click $(menu#languages>li) (_, me) { + var v = me.id; + if (v == "default") v = ""; + handler.set_local_option("lang", v); + app.update(); + this.toggleMenuState(); + } } + function getUserName() { try { return JSON.parse(handler.get_local_option("user_info")).name; @@ -223,6 +258,7 @@ class MyIdMenu: Reactor.Component {
  • {svg_checkmark}{translate('Dark Theme')}
  • +
  • {translate('About')} {" "}{handler.get_app_name()}
  • ; diff --git a/src/ui/remote.rs b/src/ui/remote.rs index a073b81c6..96cca5301 100644 --- a/src/ui/remote.rs +++ b/src/ui/remote.rs @@ -1955,11 +1955,11 @@ impl Remote { let mut config: PeerConfig = self.handler.load_config(); let mut transfer_metas = TransferSerde::default(); for job in self.read_jobs.iter() { - let json_str = serde_json::to_string(&job.gen_meta()).unwrap(); + let json_str = serde_json::to_string(&job.gen_meta()).unwrap_or_default(); transfer_metas.read_jobs.push(json_str); } for job in self.write_jobs.iter() { - let json_str = serde_json::to_string(&job.gen_meta()).unwrap(); + let json_str = serde_json::to_string(&job.gen_meta()).unwrap_or_default(); transfer_metas.write_jobs.push(json_str); } log::info!("meta: {:?}", transfer_metas); @@ -2370,7 +2370,7 @@ impl Remote { } back_notification::PrivacyModeState::OffSucceeded => { self.handler - .msgbox("custom-nocancel", "Privacy mode", "Out privacy mode"); + .msgbox("custom-nocancel", "Privacy mode", "Out privacy mode"); self.update_privacy_mode(false); } back_notification::PrivacyModeState::OffByPeer => { From 89711d452384275e9608896f855c93797d922b9d Mon Sep 17 00:00:00 2001 From: rustdesk Date: Thu, 30 Jun 2022 01:27:30 +0800 Subject: [PATCH 03/14] remove one seperator --- src/ui/index.tis | 1 - 1 file changed, 1 deletion(-) diff --git a/src/ui/index.tis b/src/ui/index.tis index 34ae91fac..6a9573ca4 100644 --- a/src/ui/index.tis +++ b/src/ui/index.tis @@ -257,7 +257,6 @@ class MyIdMenu: Reactor.Component { {handler.is_ok_change_id() && key_confirmed ?
  • {translate('Change ID')}
  • : ""}
  • {svg_checkmark}{translate('Dark Theme')}
  • -
  • {translate('About')} {" "}{handler.get_app_name()}
  • From 6772128dd9b3ed90eca9b9761f18b6cd7f78d81e Mon Sep 17 00:00:00 2001 From: Asura Date: Thu, 30 Jun 2022 06:00:12 -0700 Subject: [PATCH 04/14] feat(pynput): Support dead key --- pynput_service.py | 170 +++++++++++++++++++++++++++++++++++++--------- 1 file changed, 139 insertions(+), 31 deletions(-) diff --git a/pynput_service.py b/pynput_service.py index 14a8f1793..3aa4f6175 100644 --- a/pynput_service.py +++ b/pynput_service.py @@ -2,14 +2,114 @@ from pynput.keyboard import Key, Controller from pynput.keyboard._xorg import KeyCode from pynput._util.xorg import display_manager import Xlib +from pynput._util.xorg import * +import Xlib import os import sys import socket KeyCode._from_symbol("\0") # test +DEAD_KEYS = { + '`': 65104, + '´': 65105, + '^': 65106, + '~': 65107, + '¯': 65108, + '˘': 65109, + '˙': 65110, + '¨': 65111, + '˚': 65112, + '˝': 65113, + 'ˇ': 65114, + '¸': 65115, + '˛': 65116, + '℩': 65117, # ? + '゛': 65118, # ? + '゚ ': 65119, + 'ٜ': 65120, + '↪': 65121, + ' ̛': 65122, +} + + +def my_keyboard_mapping(display): + """Generates a mapping from *keysyms* to *key codes* and required + modifier shift states. + + :param Xlib.display.Display display: The display for which to retrieve the + keyboard mapping. + + :return: the keyboard mapping + """ + mapping = {} + + shift_mask = 1 << 0 + group_mask = alt_gr_mask(display) + + # Iterate over all keysym lists in the keyboard mapping + min_keycode = display.display.info.min_keycode + keycode_count = display.display.info.max_keycode - min_keycode + 1 + for index, keysyms in enumerate(display.get_keyboard_mapping( + min_keycode, keycode_count)): + key_code = index + min_keycode + + # Normalise the keysym list to yield a tuple containing the two groups + normalized = keysym_normalize(keysyms) + if not normalized: + continue + + # Iterate over the groups to extract the shift and modifier state + for groups, group in zip(normalized, (False, True)): + for keysym, shift in zip(groups, (False, True)): + + if not keysym: + continue + shift_state = 0 \ + | (shift_mask if shift else 0) \ + | (group_mask if group else 0) + + # !!!: Save all keycode combinations of keysym + if keysym in mapping: + mapping[keysym].append((key_code, shift_state)) + else: + mapping[keysym] = [(key_code, shift_state)] + return mapping + class MyController(Controller): + def _update_keyboard_mapping(self): + """Updates the keyboard mapping. + """ + with display_manager(self._display) as dm: + self._keyboard_mapping = my_keyboard_mapping(dm) + + def send_event(self, event, keycode, shift_state): + with display_manager(self._display) as dm, self.modifiers as modifiers: + # Under certain cimcumstances, such as when running under Xephyr, + # the value returned by dm.get_input_focus is an int + window = dm.get_input_focus().focus + send_event = getattr( + window, + 'send_event', + lambda event: dm.send_event(window, event)) + send_event(event( + detail=keycode, + state=shift_state | self._shift_mask(modifiers), + time=0, + root=dm.screen().root, + window=window, + same_screen=0, + child=Xlib.X.NONE, + root_x=0, root_y=0, event_x=0, event_y=0)) + + def fake_input(self, keycode, is_press): + with display_manager(self._display) as dm: + Xlib.ext.xtest.fake_input( + dm, + Xlib.X.KeyPress if is_press else Xlib.X.KeyRelease, + keycode) + def _handle(self, key, is_press): """Resolves a key identifier and sends a keyboard event. :param event: The *X* keyboard event. @@ -19,45 +119,53 @@ class MyController(Controller): else Xlib.display.event.KeyRelease keysym = self._keysym(key) + if key.vk is not None: + keycode = self._display.keysym_to_keycode(key.vk) + self.fake_input(keycode, is_press) + # Otherwise use XSendEvent; we need to use this in the general case to + # work around problems with keyboard layouts + self._emit('_on_fake_event', key, is_press) + return + # Make sure to verify that the key was resolved if keysym is None: raise self.InvalidKeyException(key) + # There may be multiple keycodes for keysym in keyboard_mapping + keycode_flag = len(self.keyboard_mapping[keysym]) == 1 + if keycode_flag: + keycode, shift_state = self.keyboard_mapping[keysym][0] + else: + keycode, shift_state = self._display.keysym_to_keycode(keysym), 0 + + # The keycode of the dead key is inconsistent + if keycode != self._display.keysym_to_keycode(keysym): + deakkey_chr = str(key).replace("'", '') + keysym = DEAD_KEYS[deakkey_chr] + keycode, shift_state = self.keyboard_mapping[keysym][0] + # If the key has a virtual key code, use that immediately with # fake_input; fake input,being an X server extension, has access to # more internal state that we do - if key.vk is not None: - with display_manager(self._display) as dm: - Xlib.ext.xtest.fake_input( - dm, - Xlib.X.KeyPress if is_press else Xlib.X.KeyRelease, - dm.keysym_to_keycode(key.vk)) - # Otherwise use XSendEvent; we need to use this in the general case to - # work around problems with keyboard layouts - else: - try: - keycode = self._display.keysym_to_keycode(keysym) - with self.modifiers as modifiers: - alt_gr = Key.alt_gr in modifiers - if alt_gr: - keycode, shift_state = self.keyboard_mapping[keysym] - self._send_key(event, keycode, shift_state) - else: - with display_manager(self._display) as dm: - Xlib.ext.xtest.fake_input( - dm, - Xlib.X.KeyPress if is_press else Xlib.X.KeyRelease, - keycode) - except KeyError: - with self._borrow_lock: - keycode, index, count = self._borrows[keysym] - self._send_key( - event, - keycode, - index_to_shift(self._display, index)) - count += 1 if is_press else -1 - self._borrows[keysym] = (keycode, index, count) + try: + with self.modifiers as modifiers: + alt_gr = Key.alt_gr in modifiers + + if alt_gr or keycode_flag: + self.send_event( + event, keycode, shift_state) + else: + self.fake_input(keycode, is_press) + except KeyError: + with self._borrow_lock: + keycode, index, count = self._borrows[keysym] + self._send_key( + event, + keycode, + index_to_shift(self._display, index)) + count += 1 if is_press else -1 + self._borrows[keysym] = (keycode, index, count) # Notify any running listeners self._emit('_on_fake_event', key, is_press) From 002f06a767c4d80787d691065d9a4f2a335898de Mon Sep 17 00:00:00 2001 From: Asura Date: Fri, 1 Jul 2022 00:16:08 -0700 Subject: [PATCH 05/14] fix: stupid bug, remove keycode_flag --- pynput_service.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pynput_service.py b/pynput_service.py index 3aa4f6175..dd12b7df8 100644 --- a/pynput_service.py +++ b/pynput_service.py @@ -33,6 +33,7 @@ DEAD_KEYS = { } + def my_keyboard_mapping(display): """Generates a mapping from *keysyms* to *key codes* and required modifier shift states. @@ -151,8 +152,8 @@ class MyController(Controller): try: with self.modifiers as modifiers: alt_gr = Key.alt_gr in modifiers - - if alt_gr or keycode_flag: + # !!!: Send_event can't support lock screen, this condition cann't be modified + if alt_gr: self.send_event( event, keycode, shift_state) else: From 759753c000775ba7a3c7b4af8e6ab17c1388ac56 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Fri, 1 Jul 2022 23:22:28 +0800 Subject: [PATCH 06/14] https://github.com/rustdesk/rustdesk/issues/895 --- src/platform/windows.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/platform/windows.rs b/src/platform/windows.rs index 4e8f7e16a..ab267440e 100644 --- a/src/platform/windows.rs +++ b/src/platform/windows.rs @@ -1059,6 +1059,7 @@ sc start {app_name} sc stop {app_name} sc delete {app_name} {after_install} +{sleep} ", uninstall_str=uninstall_str, path=path, @@ -1081,6 +1082,11 @@ sc delete {app_name} config_path=Config::file().to_str().unwrap_or(""), lic=register_licence(), after_install=get_after_install(&exe), + sleep=if debug { + "timeout 300" + } else { + "" + } ); run_cmds(cmds, debug, "install")?; std::thread::sleep(std::time::Duration::from_millis(2000)); From 4a0e047c03b0cb80cfdaf0d3481afdf8d90fb8c1 Mon Sep 17 00:00:00 2001 From: csf Date: Sat, 2 Jul 2022 21:24:49 +0800 Subject: [PATCH 07/14] opt gesture - opt:Auto recover cursor;Expand scale limit. fix:twoFinger mistake --- flutter/lib/models/model.dart | 8 +-- flutter/lib/pages/remote_page.dart | 16 ++++- flutter/lib/widgets/gestures.dart | 108 +++++++++++++++++------------ 3 files changed, 82 insertions(+), 50 deletions(-) diff --git a/flutter/lib/models/model.dart b/flutter/lib/models/model.dart index fd3f97ec6..5960eb1c3 100644 --- a/flutter/lib/models/model.dart +++ b/flutter/lib/models/model.dart @@ -310,19 +310,19 @@ class ImageModel with ChangeNotifier { } double get maxScale { - if (_image == null) return 1.0; + if (_image == null) return 1.5; final size = MediaQueryData.fromWindow(ui.window).size; final xscale = size.width / _image!.width; final yscale = size.height / _image!.height; - return max(1.0, max(xscale, yscale)); + return max(1.5, max(xscale, yscale)); } double get minScale { - if (_image == null) return 1.0; + if (_image == null) return 1.5; final size = MediaQueryData.fromWindow(ui.window).size; final xscale = size.width / _image!.width; final yscale = size.height / _image!.height; - return min(xscale, yscale); + return min(xscale, yscale) / 1.5; } } diff --git a/flutter/lib/pages/remote_page.dart b/flutter/lib/pages/remote_page.dart index b93069203..b06990e68 100644 --- a/flutter/lib/pages/remote_page.dart +++ b/flutter/lib/pages/remote_page.dart @@ -481,6 +481,7 @@ class _RemotePageState extends State { /// DoubleFiner -> right click /// HoldDrag -> left drag + Offset _cacheLongPressPosition = Offset(0, 0); Widget getBodyForMobileWithGesture() { final touchMode = FFI.ffiModel.touchMode; return getMixinGestureDetector( @@ -504,10 +505,14 @@ class _RemotePageState extends State { }, onLongPressDown: (d) { if (touchMode) { - FFI.cursorModel.move(d.localPosition.dx, d.localPosition.dy); + _cacheLongPressPosition = d.localPosition; } }, onLongPress: () { + if (touchMode) { + FFI.cursorModel + .move(_cacheLongPressPosition.dx, _cacheLongPressPosition.dy); + } FFI.tap(MouseButtons.right); }, onDoubleFinerTap: (d) { @@ -534,6 +539,15 @@ class _RemotePageState extends State { if (touchMode) { FFI.cursorModel.move(d.localPosition.dx, d.localPosition.dy); FFI.sendMouse('down', MouseButtons.left); + } else { + final cursorX = FFI.cursorModel.x; + final cursorY = FFI.cursorModel.y; + final visible = + FFI.cursorModel.getVisibleRect().inflate(1); // extend edges + final size = MediaQueryData.fromWindow(ui.window).size; + if (!visible.contains(Offset(cursorX, cursorY))) { + FFI.cursorModel.move(size.width / 2, size.height / 2); + } } }, onOneFingerPanUpdate: (d) { diff --git a/flutter/lib/widgets/gestures.dart b/flutter/lib/widgets/gestures.dart index 960439678..f689bd9ac 100644 --- a/flutter/lib/widgets/gestures.dart +++ b/flutter/lib/widgets/gestures.dart @@ -2,7 +2,7 @@ import 'dart:async'; import 'package:flutter/gestures.dart'; import 'package:flutter/widgets.dart'; -enum CustomTouchGestureState { +enum GestureState { none, oneFingerPan, twoFingerScale, @@ -35,64 +35,41 @@ class CustomTouchGestureRecognizer extends ScaleGestureRecognizer { GestureDragUpdateCallback? onThreeFingerVerticalDragUpdate; GestureDragEndCallback? onThreeFingerVerticalDragEnd; - var _currentState = CustomTouchGestureState.none; - Timer? _startEventDebounceTimer; + var _currentState = GestureState.none; + Timer? _debounceTimer; void _init() { debugPrint("CustomTouchGestureRecognizer init"); - onStart = (d) { - _startEventDebounceTimer?.cancel(); - if (d.pointerCount == 1) { - _currentState = CustomTouchGestureState.oneFingerPan; - if (onOneFingerPanStart != null) { - onOneFingerPanStart!(DragStartDetails( - localPosition: d.localFocalPoint, globalPosition: d.focalPoint)); - } - debugPrint("start oneFingerPan"); - } else if (d.pointerCount == 2) { - if (_currentState == CustomTouchGestureState.threeFingerVerticalDrag) { - // 3 -> 2 debounce - _startEventDebounceTimer = Timer(Duration(milliseconds: 200), () { - _currentState = CustomTouchGestureState.twoFingerScale; - if (onTwoFingerScaleStart != null) { - onTwoFingerScaleStart!(ScaleStartDetails( - localFocalPoint: d.localFocalPoint, - focalPoint: d.focalPoint)); - } - debugPrint("debounce start twoFingerScale success"); - }); - } - _currentState = CustomTouchGestureState.twoFingerScale; - // startWatchTimer(); - if (onTwoFingerScaleStart != null) { - onTwoFingerScaleStart!(ScaleStartDetails( - localFocalPoint: d.localFocalPoint, focalPoint: d.focalPoint)); - } - debugPrint("start twoFingerScale"); - } else if (d.pointerCount == 3) { - _currentState = CustomTouchGestureState.threeFingerVerticalDrag; + // onStart = (d) {}; + onUpdate = (d) { + _debounceTimer?.cancel(); + if (d.pointerCount == 1 && _currentState != GestureState.oneFingerPan) { + onOneFingerStartDebounce(d); + } else if (d.pointerCount == 2 && + _currentState != GestureState.twoFingerScale) { + onTwoFingerStartDebounce(d); + } else if (d.pointerCount == 3 && + _currentState != GestureState.threeFingerVerticalDrag) { + _currentState = GestureState.threeFingerVerticalDrag; if (onThreeFingerVerticalDragStart != null) { onThreeFingerVerticalDragStart!( DragStartDetails(globalPosition: d.localFocalPoint)); } debugPrint("start threeFingerScale"); - // _reset(); } - }; - onUpdate = (d) { - if (_currentState != CustomTouchGestureState.none) { + if (_currentState != GestureState.none) { switch (_currentState) { - case CustomTouchGestureState.oneFingerPan: + case GestureState.oneFingerPan: if (onOneFingerPanUpdate != null) { onOneFingerPanUpdate!(_getDragUpdateDetails(d)); } break; - case CustomTouchGestureState.twoFingerScale: + case GestureState.twoFingerScale: if (onTwoFingerScaleUpdate != null) { onTwoFingerScaleUpdate!(d); } break; - case CustomTouchGestureState.threeFingerVerticalDrag: + case GestureState.threeFingerVerticalDrag: if (onThreeFingerVerticalDragUpdate != null) { onThreeFingerVerticalDragUpdate!(_getDragUpdateDetails(d)); } @@ -105,21 +82,22 @@ class CustomTouchGestureRecognizer extends ScaleGestureRecognizer { }; onEnd = (d) { debugPrint("ScaleGestureRecognizer onEnd"); + _debounceTimer?.cancel(); // end switch (_currentState) { - case CustomTouchGestureState.oneFingerPan: + case GestureState.oneFingerPan: debugPrint("TwoFingerState.pan onEnd"); if (onOneFingerPanEnd != null) { onOneFingerPanEnd!(_getDragEndDetails(d)); } break; - case CustomTouchGestureState.twoFingerScale: + case GestureState.twoFingerScale: debugPrint("TwoFingerState.scale onEnd"); if (onTwoFingerScaleEnd != null) { onTwoFingerScaleEnd!(d); } break; - case CustomTouchGestureState.threeFingerVerticalDrag: + case GestureState.threeFingerVerticalDrag: debugPrint("ThreeFingerState.vertical onEnd"); if (onThreeFingerVerticalDragEnd != null) { onThreeFingerVerticalDragEnd!(_getDragEndDetails(d)); @@ -128,10 +106,50 @@ class CustomTouchGestureRecognizer extends ScaleGestureRecognizer { default: break; } - _currentState = CustomTouchGestureState.none; + _debounceTimer = Timer(Duration(milliseconds: 200), () { + _currentState = GestureState.none; + }); }; } + void onOneFingerStartDebounce(ScaleUpdateDetails d) { + final start = (ScaleUpdateDetails d) { + _currentState = GestureState.oneFingerPan; + if (onOneFingerPanStart != null) { + onOneFingerPanStart!(DragStartDetails( + localPosition: d.localFocalPoint, globalPosition: d.focalPoint)); + } + }; + if (_currentState != GestureState.none) { + _debounceTimer = Timer(Duration(milliseconds: 200), () { + start(d); + debugPrint("debounce start oneFingerPan"); + }); + } else { + start(d); + debugPrint("start oneFingerPan"); + } + } + + void onTwoFingerStartDebounce(ScaleUpdateDetails d) { + final start = (ScaleUpdateDetails d) { + _currentState = GestureState.twoFingerScale; + if (onTwoFingerScaleStart != null) { + onTwoFingerScaleStart!(ScaleStartDetails( + localFocalPoint: d.localFocalPoint, focalPoint: d.focalPoint)); + } + }; + if (_currentState == GestureState.threeFingerVerticalDrag) { + _debounceTimer = Timer(Duration(milliseconds: 200), () { + start(d); + debugPrint("debounce start twoFingerScale"); + }); + } else { + start(d); + debugPrint("start twoFingerScale"); + } + } + DragUpdateDetails _getDragUpdateDetails(ScaleUpdateDetails d) => DragUpdateDetails( globalPosition: d.focalPoint, From ff4bbbd1ed4ffba1c0f574395b78e748742725b6 Mon Sep 17 00:00:00 2001 From: fufesou Date: Mon, 4 Jul 2022 10:11:40 +0800 Subject: [PATCH 08/14] fix rpm upgrade Signed-off-by: fufesou --- rpm-suse.spec | 28 ++++++++++++++++++++++------ rpm.spec | 28 ++++++++++++++++++++++------ 2 files changed, 44 insertions(+), 12 deletions(-) diff --git a/rpm-suse.spec b/rpm-suse.spec index 8863f3da2..16c81ae90 100644 --- a/rpm-suse.spec +++ b/rpm-suse.spec @@ -47,7 +47,7 @@ case "$1" in ;; 2) # for upgrade - service rustdesk stop || true + systemctl stop rustdesk || true ;; esac @@ -61,10 +61,26 @@ systemctl start rustdesk update-desktop-database %preun -systemctl stop rustdesk || true -systemctl disable rustdesk || true -rm /etc/systemd/system/rustdesk.service || true +case "$1" in + 0) + # for uninstall + systemctl stop rustdesk || true + systemctl disable rustdesk || true + rm /etc/systemd/system/rustdesk.service || true + ;; + 1) + # for upgrade + ;; +esac %postun -rm /usr/share/applications/rustdesk.desktop || true -update-desktop-database +case "$1" in + 0) + # for uninstall + rm /usr/share/applications/rustdesk.desktop || true + update-desktop-database + ;; + 1) + # for upgrade + ;; +esac diff --git a/rpm.spec b/rpm.spec index ff32d7b97..707f0381a 100644 --- a/rpm.spec +++ b/rpm.spec @@ -48,7 +48,7 @@ case "$1" in ;; 2) # for upgrade - service rustdesk stop || true + systemctl stop rustdesk || true ;; esac @@ -62,10 +62,26 @@ systemctl start rustdesk update-desktop-database %preun -systemctl stop rustdesk || true -systemctl disable rustdesk || true -rm /etc/systemd/system/rustdesk.service || true +case "$1" in + 0) + # for uninstall + systemctl stop rustdesk || true + systemctl disable rustdesk || true + rm /etc/systemd/system/rustdesk.service || true + ;; + 1) + # for upgrade + ;; +esac %postun -rm /usr/share/applications/rustdesk.desktop || true -update-desktop-database +case "$1" in + 0) + # for uninstall + rm /usr/share/applications/rustdesk.desktop || true + update-desktop-database + ;; + 1) + # for upgrade + ;; +esac From 2f21661b74cec1a8272c458195733089715d9e3b Mon Sep 17 00:00:00 2001 From: csf Date: Mon, 4 Jul 2022 16:02:48 +0800 Subject: [PATCH 09/14] fix physical keyboard input twice; add configChanges navigation --- .../android/app/src/main/AndroidManifest.xml | 2 +- flutter/lib/models/model.dart | 18 +++++++++++------- flutter/lib/pages/remote_page.dart | 8 ++++++-- 3 files changed, 18 insertions(+), 10 deletions(-) diff --git a/flutter/android/app/src/main/AndroidManifest.xml b/flutter/android/app/src/main/AndroidManifest.xml index ad496edb0..1759a1ac0 100644 --- a/flutter/android/app/src/main/AndroidManifest.xml +++ b/flutter/android/app/src/main/AndroidManifest.xml @@ -43,7 +43,7 @@ out = Map(); + out['name'] = name; + // default: down = false + if (down == true) { + out['down'] = "true"; + } + // default: press = true + if (press != false) { + out['press'] = "true"; + } + setByName('input_key', json.encode(modify(out))); } static void moveMouse(double x, double y) { diff --git a/flutter/lib/pages/remote_page.dart b/flutter/lib/pages/remote_page.dart index b06990e68..c383bc361 100644 --- a/flutter/lib/pages/remote_page.dart +++ b/flutter/lib/pages/remote_page.dart @@ -343,9 +343,14 @@ class _RemotePageState extends State { onKey: (data, e) { final key = e.logicalKey; if (e is RawKeyDownEvent) { - if (e.repeat) { + if (e.repeat && + !e.isAltPressed && + !e.isControlPressed && + !e.isShiftPressed && + !e.isMetaPressed) { sendRawKey(e, press: true); } else { + sendRawKey(e, down: true); if (e.isAltPressed && !FFI.alt) { FFI.alt = true; } else if (e.isControlPressed && !FFI.ctrl) { @@ -355,7 +360,6 @@ class _RemotePageState extends State { } else if (e.isMetaPressed && !FFI.command) { FFI.command = true; } - sendRawKey(e, down: true); } } // [!_showEdit] workaround for soft-keyboard's control_key like Backspace / Enter From 9e8d8e43577ac28cd136b363a14bb7eaaf51e7b3 Mon Sep 17 00:00:00 2001 From: Asura Date: Mon, 4 Jul 2022 02:14:47 -0700 Subject: [PATCH 10/14] fix(pynput): Add dead key detection condition to support German keyboard --- pynput_service.py | 8 +++++--- src/ui/remote.rs | 1 + 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/pynput_service.py b/pynput_service.py index dd12b7df8..c51e9a524 100644 --- a/pynput_service.py +++ b/pynput_service.py @@ -139,8 +139,10 @@ class MyController(Controller): else: keycode, shift_state = self._display.keysym_to_keycode(keysym), 0 - # The keycode of the dead key is inconsistent - if keycode != self._display.keysym_to_keycode(keysym): + keycode_set = set(map(lambda x: x[0], self.keyboard_mapping[keysym])) + # The keycode of the dead key is inconsistent, The keysym has multiple combinations of a keycode. + if keycode != self._display.keysym_to_keycode(keysym) \ + or (keycode_flag == False and keycode == list(keycode_set)[0] and len(keycode_set) == 1): deakkey_chr = str(key).replace("'", '') keysym = DEAD_KEYS[deakkey_chr] keycode, shift_state = self.keyboard_mapping[keysym][0] @@ -226,7 +228,7 @@ def loop(): else: keyboard.release(name) except Exception as e: - print(e) + print('[x] error key',e) loop() diff --git a/src/ui/remote.rs b/src/ui/remote.rs index a073b81c6..5917314ca 100644 --- a/src/ui/remote.rs +++ b/src/ui/remote.rs @@ -276,6 +276,7 @@ impl Handler { KeyRelease(k) => (k, 0), _ => return, }; + log::debug!("{:?}", key); let alt = get_key_state(enigo::Key::Alt); #[cfg(windows)] let ctrl = { From 80a41e2ecd80ab82830795bd2d1d101439c70f9f Mon Sep 17 00:00:00 2001 From: Asura <99897242+asur4s@users.noreply.github.com> Date: Mon, 4 Jul 2022 17:50:47 +0800 Subject: [PATCH 11/14] style: Remove key log --- src/ui/remote.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/ui/remote.rs b/src/ui/remote.rs index 2561f1d3e..96cca5301 100644 --- a/src/ui/remote.rs +++ b/src/ui/remote.rs @@ -276,7 +276,6 @@ impl Handler { KeyRelease(k) => (k, 0), _ => return, }; - log::debug!("{:?}", key); let alt = get_key_state(enigo::Key::Alt); #[cfg(windows)] let ctrl = { From 18806a0b136248e707e77330c33cf91538880ff4 Mon Sep 17 00:00:00 2001 From: tsic404 Date: Mon, 4 Jul 2022 19:49:30 +0800 Subject: [PATCH 12/14] fix: README-ZH error char --- README-ZH.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README-ZH.md b/README-ZH.md index 8d4203b16..cd1a332c3 100644 --- a/README-ZH.md +++ b/README-ZH.md @@ -202,7 +202,7 @@ target/release/rustdesk - **[libs/scrap](https://github.com/rustdesk/rustdesk/tree/master/libs/scrap)**: 截屏 - **[libs/enigo](https://github.com/rustdesk/rustdesk/tree/master/libs/enigo)**: 平台相关的鼠标键盘输入 - **[src/ui](https://github.com/rustdesk/rustdesk/tree/master/src/ui)**: GUI -- **[src/server](https://github.com/rustdesk/rustdesk/tree/master/src/server)**: 被控端服务,audio/clipboard/input/video 服务, 已经连接实现 +- **[src/server](https://github.com/rustdesk/rustdesk/tree/master/src/server)**: 被控端服务,audio/clipboard/input/video 服务, 以及连接的实现 - **[src/client.rs](https://github.com/rustdesk/rustdesk/tree/master/src/client.rs)**: 控制端 - **[src/rendezvous_mediator.rs](https://github.com/rustdesk/rustdesk/tree/master/src/rendezvous_mediator.rs)**: 与[rustdesk-server](https://github.com/rustdesk/rustdesk-server)保持 UDP 通讯, 等待远程连接(通过打洞直连或者中继) - **[src/platform](https://github.com/rustdesk/rustdesk/tree/master/src/platform)**: 平台服务相关代码 From 83b3a0cbb7c5347ec8d8911a8e9fdf64381e6f15 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Wed, 6 Jul 2022 01:29:11 +0800 Subject: [PATCH 13/14] https://github.com/rustdesk/rustdesk/issues/895 --- src/platform/windows.rs | 44 ++++++++++++++++++++++++++--------------- 1 file changed, 28 insertions(+), 16 deletions(-) diff --git a/src/platform/windows.rs b/src/platform/windows.rs index ab267440e..792243c27 100644 --- a/src/platform/windows.rs +++ b/src/platform/windows.rs @@ -1020,6 +1020,21 @@ copy /Y \"{tmp_path}\\Uninstall {app_name}.lnk\" \"{start_menu}\\\" // https://docs.microsoft.com/zh-cn/windows/win32/msi/uninstall-registry-key?redirectedfrom=MSDNa // https://www.windowscentral.com/how-edit-registry-using-command-prompt-windows-10 // https://www.tenforums.com/tutorials/70903-add-remove-allowed-apps-through-windows-firewall-windows-10-a.html + let dels = format!( + " +if exist \"{mk_shortcut}\" del /f /q \"{mk_shortcut}\" +if exist \"{uninstall_shortcut}\" del /f /q \"{uninstall_shortcut}\" +if exist \"{tray_shortcut}\" del /f /q \"{tray_shortcut}\" +if exist \"{tmp_path}\\{app_name}.lnk\" del /f /q \"{tmp_path}\\{app_name}.lnk\" +if exist \"{tmp_path}\\Uninstall {app_name}.lnk\" del /f /q \"{tmp_path}\\Uninstall {app_name}.lnk\" +if exist \"{tmp_path}\\{app_name} Tray.lnk\" del /f /q \"{tmp_path}\\{app_name} Tray.lnk\" + ", + mk_shortcut = mk_shortcut, + uninstall_shortcut = uninstall_shortcut, + tray_shortcut = tray_shortcut, + tmp_path = tmp_path, + app_name = crate::get_app_name(), + ); let cmds = format!( " {uninstall_str} @@ -1048,12 +1063,7 @@ cscript \"{tray_shortcut}\" copy /Y \"{tmp_path}\\{app_name} Tray.lnk\" \"%PROGRAMDATA%\\Microsoft\\Windows\\Start Menu\\Programs\\Startup\\\" {shortcuts} copy /Y \"{tmp_path}\\Uninstall {app_name}.lnk\" \"{path}\\\" -del /f \"{mk_shortcut}\" -del /f \"{uninstall_shortcut}\" -del /f \"{tray_shortcut}\" -del /f \"{tmp_path}\\{app_name}.lnk\" -del /f \"{tmp_path}\\Uninstall {app_name}.lnk\" -del /f \"{tmp_path}\\{app_name} Tray.lnk\" +{dels} sc create {app_name} binpath= \"\\\"{exe}\\\" --import-config \\\"{config_path}\\\"\" start= auto DisplayName= \"{app_name} Service\" sc start {app_name} sc stop {app_name} @@ -1086,7 +1096,12 @@ sc delete {app_name} "timeout 300" } else { "" - } + }, + dels=if debug { + "" + } else { + &dels + }, ); run_cmds(cmds, debug, "install")?; std::thread::sleep(std::time::Duration::from_millis(2000)); @@ -1132,10 +1147,10 @@ fn get_uninstall() -> String { " {before_uninstall} reg delete {subkey} /f - rd /s /q \"{path}\" - rd /s /q \"{start_menu}\" - del /f /q \"%PUBLIC%\\Desktop\\{app_name}*\" - del /f /q \"%PROGRAMDATA%\\Microsoft\\Windows\\Start Menu\\Programs\\Startup\\{app_name} Tray.lnk\" + if exist \"{path}\" rd /s /q \"{path}\" + if exist \"{start_menu}\" rd /s /q \"{start_menu}\" + if exist \"%PUBLIC%\\Desktop\\{app_name}.lnk\" del /f /q \"%PUBLIC%\\Desktop\\{app_name}.lnk\" + if exist \"%PROGRAMDATA%\\Microsoft\\Windows\\Start Menu\\Programs\\Startup\\{app_name} Tray.lnk\" del /f /q \"%PROGRAMDATA%\\Microsoft\\Windows\\Start Menu\\Programs\\Startup\\{app_name} Tray.lnk\" ", before_uninstall=get_before_uninstall(), subkey=subkey, @@ -1182,11 +1197,8 @@ fn run_cmds(cmds: String, show: bool, tip: &str) -> ResultType<()> { .show(show) .force_prompt(true) .status(); - // leave the file for debug if execution failed - if let Ok(res) = res { - if res.success() { - allow_err!(std::fs::remove_file(tmp)); - } + if !show { + allow_err!(std::fs::remove_file(tmp)); } let _ = res?; Ok(()) From df32b8f07bf4fe7a6be34d86bec87ad6f4f6d4a6 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Wed, 6 Jul 2022 01:33:04 +0800 Subject: [PATCH 14/14] add comment on if exist in bat --- src/platform/windows.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/platform/windows.rs b/src/platform/windows.rs index 792243c27..c9f83389a 100644 --- a/src/platform/windows.rs +++ b/src/platform/windows.rs @@ -1020,6 +1020,7 @@ copy /Y \"{tmp_path}\\Uninstall {app_name}.lnk\" \"{start_menu}\\\" // https://docs.microsoft.com/zh-cn/windows/win32/msi/uninstall-registry-key?redirectedfrom=MSDNa // https://www.windowscentral.com/how-edit-registry-using-command-prompt-windows-10 // https://www.tenforums.com/tutorials/70903-add-remove-allowed-apps-through-windows-firewall-windows-10-a.html + // Note: without if exist, the bat may exit in advance on some Windows7 https://github.com/rustdesk/rustdesk/issues/895 let dels = format!( " if exist \"{mk_shortcut}\" del /f /q \"{mk_shortcut}\"