mirror of
https://github.com/weyne85/rustdesk.git
synced 2025-10-29 17:00:05 +00:00
Merge pull request #964 from asur4s/master
Feat: Support new keyboard mode
This commit is contained in:
commit
47cefc57e6
892
Cargo.lock
generated
892
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@ -75,7 +75,7 @@ sciter-rs = { git = "https://github.com/open-trade/rust-sciter", branch = "dyn"
|
|||||||
sys-locale = "0.2"
|
sys-locale = "0.2"
|
||||||
enigo = { path = "libs/enigo", features = [ "with_serde" ] }
|
enigo = { path = "libs/enigo", features = [ "with_serde" ] }
|
||||||
clipboard = { path = "libs/clipboard" }
|
clipboard = { path = "libs/clipboard" }
|
||||||
rdev = { git = "https://github.com/open-trade/rdev" }
|
rdev = { git = "https://github.com/asur4s/rdev" }
|
||||||
ctrlc = "3.2"
|
ctrlc = "3.2"
|
||||||
arboard = "2.0"
|
arboard = "2.0"
|
||||||
#minreq = { version = "2.4", features = ["punycode", "https-native"] }
|
#minreq = { version = "2.4", features = ["punycode", "https-native"] }
|
||||||
@ -108,6 +108,7 @@ async-process = "1.3"
|
|||||||
mouce = { git="https://github.com/fufesou/mouce.git" }
|
mouce = { git="https://github.com/fufesou/mouce.git" }
|
||||||
evdev = { git="https://github.com/fufesou/evdev" }
|
evdev = { git="https://github.com/fufesou/evdev" }
|
||||||
|
|
||||||
|
|
||||||
[target.'cfg(target_os = "android")'.dependencies]
|
[target.'cfg(target_os = "android")'.dependencies]
|
||||||
android_logger = "0.11"
|
android_logger = "0.11"
|
||||||
jni = "0.19"
|
jni = "0.19"
|
||||||
|
|||||||
@ -12,9 +12,6 @@ if [ "$1" = configure ]; then
|
|||||||
fi
|
fi
|
||||||
version=$(python3 -V 2>&1 | grep -Po '(?<=Python )(.+)')
|
version=$(python3 -V 2>&1 | grep -Po '(?<=Python )(.+)')
|
||||||
parsedVersion=$(echo "${version//./}")
|
parsedVersion=$(echo "${version//./}")
|
||||||
if [[ "$parsedVersion" -gt "360" ]]; then
|
|
||||||
sudo -H pip3 install pynput
|
|
||||||
fi
|
|
||||||
cp /usr/share/rustdesk/files/systemd/rustdesk.service /usr/lib/systemd/system/rustdesk.service
|
cp /usr/share/rustdesk/files/systemd/rustdesk.service /usr/lib/systemd/system/rustdesk.service
|
||||||
systemctl daemon-reload
|
systemctl daemon-reload
|
||||||
systemctl enable rustdesk
|
systemctl enable rustdesk
|
||||||
|
|||||||
1
PKGBUILD
1
PKGBUILD
@ -26,6 +26,5 @@ package() {
|
|||||||
install -Dm 644 ${HBB}/libsciter-gtk.so -t "${pkgdir}/usr/lib/rustdesk"
|
install -Dm 644 ${HBB}/libsciter-gtk.so -t "${pkgdir}/usr/lib/rustdesk"
|
||||||
install -Dm 644 $HBB/rustdesk.service -t "${pkgdir}/usr/share/rustdesk/files"
|
install -Dm 644 $HBB/rustdesk.service -t "${pkgdir}/usr/share/rustdesk/files"
|
||||||
install -Dm 644 $HBB/rustdesk.desktop -t "${pkgdir}/usr/share/rustdesk/files"
|
install -Dm 644 $HBB/rustdesk.desktop -t "${pkgdir}/usr/share/rustdesk/files"
|
||||||
install -Dm 644 $HBB/pynput_service.py -t "${pkgdir}/usr/share/rustdesk/files"
|
|
||||||
install -Dm 644 $HBB/128x128@2x.png "${pkgdir}/usr/share/rustdesk/files/rustdesk.png"
|
install -Dm 644 $HBB/128x128@2x.png "${pkgdir}/usr/share/rustdesk/files/rustdesk.png"
|
||||||
}
|
}
|
||||||
|
|||||||
@ -80,11 +80,6 @@ sudo yum -y install gcc-c++ git curl wget nasm yasm gcc gtk3-devel clang libxcb-
|
|||||||
sudo pacman -Syu --needed unzip git cmake gcc curl wget yasm nasm zip make pkg-config clang gtk3 xdotool libxcb libxfixes alsa-lib pulseaudio
|
sudo pacman -Syu --needed unzip git cmake gcc curl wget yasm nasm zip make pkg-config clang gtk3 xdotool libxcb libxfixes alsa-lib pulseaudio
|
||||||
```
|
```
|
||||||
|
|
||||||
### pynput package تثبيت
|
|
||||||
|
|
||||||
```sh
|
|
||||||
pip3 install pynput
|
|
||||||
```
|
|
||||||
|
|
||||||
### vcpkg تثبيت
|
### vcpkg تثبيت
|
||||||
|
|
||||||
|
|||||||
@ -75,12 +75,6 @@ sudo yum -y install gcc-c++ git curl wget nasm yasm gcc gtk3-devel clang libxcb-
|
|||||||
sudo pacman -Syu --needed unzip git cmake gcc curl wget yasm nasm zip make pkg-config clang gtk3 xdotool libxcb libxfixes alsa-lib pulseaudio
|
sudo pacman -Syu --needed unzip git cmake gcc curl wget yasm nasm zip make pkg-config clang gtk3 xdotool libxcb libxfixes alsa-lib pulseaudio
|
||||||
```
|
```
|
||||||
|
|
||||||
### Instalace balíčku pynput
|
|
||||||
|
|
||||||
```sh
|
|
||||||
pip3 install pynput
|
|
||||||
```
|
|
||||||
|
|
||||||
### Instalace vcpkg
|
### Instalace vcpkg
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
|
|||||||
@ -78,12 +78,6 @@ sudo yum -y install gcc-c++ git curl wget nasm yasm gcc gtk3-devel clang libxcb-
|
|||||||
sudo pacman -Syu --needed unzip git cmake gcc curl wget yasm nasm zip make pkg-config clang gtk3 xdotool libxcb libxfixes alsa-lib pulseaudio
|
sudo pacman -Syu --needed unzip git cmake gcc curl wget yasm nasm zip make pkg-config clang gtk3 xdotool libxcb libxfixes alsa-lib pulseaudio
|
||||||
```
|
```
|
||||||
|
|
||||||
### بسته pynput را نصب کنید
|
|
||||||
|
|
||||||
```sh
|
|
||||||
pip3 install pynput
|
|
||||||
```
|
|
||||||
|
|
||||||
### نرم افزار vcpkg را نصب کنید
|
### نرم افزار vcpkg را نصب کنید
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
|
|||||||
@ -81,12 +81,6 @@ sudo yum -y install gcc-c++ git curl wget nasm yasm gcc gtk3-devel clang libxcb-
|
|||||||
sudo pacman -Syu --needed unzip git cmake gcc curl wget yasm nasm zip make pkg-config clang gtk3 xdotool libxcb libxfixes alsa-lib pulseaudio
|
sudo pacman -Syu --needed unzip git cmake gcc curl wget yasm nasm zip make pkg-config clang gtk3 xdotool libxcb libxfixes alsa-lib pulseaudio
|
||||||
```
|
```
|
||||||
|
|
||||||
### Telepítsd a pynput csomagot
|
|
||||||
|
|
||||||
```sh
|
|
||||||
pip3 install pynput
|
|
||||||
```
|
|
||||||
|
|
||||||
### Telepítsd a vcpkg-t
|
### Telepítsd a vcpkg-t
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
|
|||||||
@ -80,12 +80,6 @@ sudo yum -y install gcc-c++ git curl wget nasm yasm gcc gtk3-devel clang libxcb-
|
|||||||
sudo pacman -Syu --needed unzip git cmake gcc curl wget yasm nasm zip make pkg-config clang gtk3 xdotool libxcb libxfixes alsa-lib pulseaudio
|
sudo pacman -Syu --needed unzip git cmake gcc curl wget yasm nasm zip make pkg-config clang gtk3 xdotool libxcb libxfixes alsa-lib pulseaudio
|
||||||
```
|
```
|
||||||
|
|
||||||
### Install pynput package
|
|
||||||
|
|
||||||
```sh
|
|
||||||
pip3 install pynput
|
|
||||||
```
|
|
||||||
|
|
||||||
### Install vcpkg
|
### Install vcpkg
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
|
|||||||
@ -78,12 +78,6 @@ sudo yum -y install gcc-c++ git curl wget nasm yasm gcc gtk3-devel clang libxcb-
|
|||||||
sudo pacman -Syu --needed unzip git cmake gcc curl wget yasm nasm zip make pkg-config clang gtk3 xdotool libxcb libxfixes alsa-lib pulseaudio
|
sudo pacman -Syu --needed unzip git cmake gcc curl wget yasm nasm zip make pkg-config clang gtk3 xdotool libxcb libxfixes alsa-lib pulseaudio
|
||||||
```
|
```
|
||||||
|
|
||||||
### Install pynput package
|
|
||||||
|
|
||||||
```sh
|
|
||||||
pip3 install pynput
|
|
||||||
```
|
|
||||||
|
|
||||||
### Install vcpkg
|
### Install vcpkg
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
|
|||||||
@ -82,12 +82,6 @@ sudo yum -y install gcc-c++ git curl wget nasm yasm gcc gtk3-devel clang libxcb-
|
|||||||
sudo pacman -Syu --needed unzip git cmake gcc curl wget yasm nasm zip make pkg-config clang gtk3 xdotool libxcb libxfixes alsa-lib pulseaudio
|
sudo pacman -Syu --needed unzip git cmake gcc curl wget yasm nasm zip make pkg-config clang gtk3 xdotool libxcb libxfixes alsa-lib pulseaudio
|
||||||
```
|
```
|
||||||
|
|
||||||
### Cách tải về gói hàng pynput
|
|
||||||
|
|
||||||
```sh
|
|
||||||
pip3 install pynput
|
|
||||||
```
|
|
||||||
|
|
||||||
### Cách cài vcpkg
|
### Cách cài vcpkg
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
|
|||||||
34
README.md
34
README.md
@ -65,7 +65,9 @@ Please download sciter dynamic library yourself.
|
|||||||
### Ubuntu 18 (Debian 10)
|
### Ubuntu 18 (Debian 10)
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
sudo apt install -y g++ gcc git curl wget nasm yasm libgtk-3-dev clang libxcb-randr0-dev libxdo-dev libxfixes-dev libxcb-shape0-dev libxcb-xfixes0-dev libasound2-dev libpulse-dev cmake
|
sudo apt install -y g++ gcc git curl wget nasm yasm libgtk-3-dev clang libxcb-randr0-dev libxdo-dev \
|
||||||
|
libxfixes-dev libxcb-shape0-dev libxcb-xfixes0-dev libasound2-dev libpulse-dev cmake \
|
||||||
|
libclang-dev ninja-build libayatana-appindicator3-1 libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev libayatana-appindicator3-dev
|
||||||
```
|
```
|
||||||
|
|
||||||
### Fedora 28 (CentOS 8)
|
### Fedora 28 (CentOS 8)
|
||||||
@ -80,12 +82,6 @@ sudo yum -y install gcc-c++ git curl wget nasm yasm gcc gtk3-devel clang libxcb-
|
|||||||
sudo pacman -Syu --needed unzip git cmake gcc curl wget yasm nasm zip make pkg-config clang gtk3 xdotool libxcb libxfixes alsa-lib pulseaudio
|
sudo pacman -Syu --needed unzip git cmake gcc curl wget yasm nasm zip make pkg-config clang gtk3 xdotool libxcb libxfixes alsa-lib pulseaudio
|
||||||
```
|
```
|
||||||
|
|
||||||
### Install pynput package
|
|
||||||
|
|
||||||
```sh
|
|
||||||
pip3 install pynput
|
|
||||||
```
|
|
||||||
|
|
||||||
### Install vcpkg
|
### Install vcpkg
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
@ -128,6 +124,30 @@ VCPKG_ROOT=$HOME/vcpkg cargo run
|
|||||||
|
|
||||||
RustDesk does not support Wayland. Check [this](https://docs.fedoraproject.org/en-US/quick-docs/configuring-xorg-as-default-gnome-session/) to configuring Xorg as the default GNOME session.
|
RustDesk does not support Wayland. Check [this](https://docs.fedoraproject.org/en-US/quick-docs/configuring-xorg-as-default-gnome-session/) to configuring Xorg as the default GNOME session.
|
||||||
|
|
||||||
|
## Wayland support
|
||||||
|
|
||||||
|
Wayland does not seem to provide any API for sending keypresses to other windows. Therefore, the rustdesk uses an API from a lower level, namely the `/dev/uinput` device (Linux kernel level).
|
||||||
|
|
||||||
|
When wayland is the controlled side, you have to start in the following way:
|
||||||
|
```bash
|
||||||
|
# Start uinput service
|
||||||
|
$ sudo rustdesk --service
|
||||||
|
$ rustdesk
|
||||||
|
```
|
||||||
|
**Notice**: Wayland screen recording uses different interfaces, currently currently only supports org.freedesktop.portal.ScreenCast.
|
||||||
|
```bash
|
||||||
|
$ dbus-send --session --print-reply \
|
||||||
|
--dest=org.freedesktop.portal.Desktop \
|
||||||
|
/org/freedesktop/portal/desktop \
|
||||||
|
org.freedesktop.DBus.Properties.Get \
|
||||||
|
string:org.freedesktop.portal.ScreenCast string:version
|
||||||
|
# Not support
|
||||||
|
Error org.freedesktop.DBus.Error.InvalidArgs: No such interface “org.freedesktop.portal.ScreenCast”
|
||||||
|
# Support
|
||||||
|
method return time=1662544486.931020 sender=:1.54 -> destination=:1.139 serial=257 reply_serial=2
|
||||||
|
variant uint32 4
|
||||||
|
```
|
||||||
|
|
||||||
## How to build with Docker
|
## How to build with Docker
|
||||||
|
|
||||||
Begin by cloning the repository and building the docker container:
|
Begin by cloning the repository and building the docker container:
|
||||||
|
|||||||
@ -9,8 +9,6 @@ script:
|
|||||||
# Download sciter.so
|
# Download sciter.so
|
||||||
- mkdir -p AppDir/usr/lib/rustdesk/
|
- mkdir -p AppDir/usr/lib/rustdesk/
|
||||||
- pushd AppDir/usr/lib/rustdesk && wget https://github.com/c-smile/sciter-sdk/raw/29a598b6d20220b93848b5e8abab704619296857/bin.lnx/x64/libsciter-gtk.so && popd
|
- pushd AppDir/usr/lib/rustdesk && wget https://github.com/c-smile/sciter-sdk/raw/29a598b6d20220b93848b5e8abab704619296857/bin.lnx/x64/libsciter-gtk.so && popd
|
||||||
# pynput_service.py
|
|
||||||
- cp ../pynput_service.py ./AppDir/usr/lib/rustdesk
|
|
||||||
# Build rustdesk
|
# Build rustdesk
|
||||||
- pushd .. && python3 inline-sciter.py && cargo build --features inline,appimage --release && popd
|
- pushd .. && python3 inline-sciter.py && cargo build --features inline,appimage --release && popd
|
||||||
- mkdir -p AppDir/usr/bin
|
- mkdir -p AppDir/usr/bin
|
||||||
|
|||||||
@ -1 +0,0 @@
|
|||||||
pynput
|
|
||||||
6
build.py
6
build.py
@ -140,8 +140,6 @@ def build_flutter_deb(version):
|
|||||||
'cp rustdesk.service tmpdeb/usr/share/rustdesk/files/systemd/')
|
'cp rustdesk.service tmpdeb/usr/share/rustdesk/files/systemd/')
|
||||||
os.system(
|
os.system(
|
||||||
'cp rustdesk.service.user tmpdeb/usr/share/rustdesk/files/systemd/')
|
'cp rustdesk.service.user tmpdeb/usr/share/rustdesk/files/systemd/')
|
||||||
os.system(
|
|
||||||
'cp ../pynput_service.py tmpdeb/usr/share/rustdesk/files/')
|
|
||||||
os.system(
|
os.system(
|
||||||
'cp ../128x128@2x.png tmpdeb/usr/share/rustdesk/files/rustdesk.png')
|
'cp ../128x128@2x.png tmpdeb/usr/share/rustdesk/files/rustdesk.png')
|
||||||
os.system(
|
os.system(
|
||||||
@ -150,7 +148,6 @@ def build_flutter_deb(version):
|
|||||||
os.system('cp -a ../DEBIAN/* tmpdeb/DEBIAN/')
|
os.system('cp -a ../DEBIAN/* tmpdeb/DEBIAN/')
|
||||||
md5_file('usr/share/rustdesk/files/systemd/rustdesk.service')
|
md5_file('usr/share/rustdesk/files/systemd/rustdesk.service')
|
||||||
md5_file('usr/share/rustdesk/files/systemd/rustdesk.service.user')
|
md5_file('usr/share/rustdesk/files/systemd/rustdesk.service.user')
|
||||||
md5_file('usr/share/rustdesk/files/pynput_service.py')
|
|
||||||
os.system('dpkg-deb -b tmpdeb rustdesk.deb; /bin/rm -rf tmpdeb/')
|
os.system('dpkg-deb -b tmpdeb rustdesk.deb; /bin/rm -rf tmpdeb/')
|
||||||
os.rename('rustdesk.deb', '../rustdesk-%s.deb' % version)
|
os.rename('rustdesk.deb', '../rustdesk-%s.deb' % version)
|
||||||
os.chdir("..")
|
os.chdir("..")
|
||||||
@ -285,15 +282,12 @@ def main():
|
|||||||
'cp rustdesk.service tmpdeb/usr/share/rustdesk/files/systemd/')
|
'cp rustdesk.service tmpdeb/usr/share/rustdesk/files/systemd/')
|
||||||
os.system(
|
os.system(
|
||||||
'cp rustdesk.service.user tmpdeb/usr/share/rustdesk/files/systemd/')
|
'cp rustdesk.service.user tmpdeb/usr/share/rustdesk/files/systemd/')
|
||||||
os.system(
|
|
||||||
'cp pynput_service.py tmpdeb/usr/share/rustdesk/files/')
|
|
||||||
os.system('cp -a DEBIAN/* tmpdeb/DEBIAN/')
|
os.system('cp -a DEBIAN/* tmpdeb/DEBIAN/')
|
||||||
os.system('strip tmpdeb/usr/bin/rustdesk')
|
os.system('strip tmpdeb/usr/bin/rustdesk')
|
||||||
os.system('mkdir -p tmpdeb/usr/lib/rustdesk')
|
os.system('mkdir -p tmpdeb/usr/lib/rustdesk')
|
||||||
os.system('cp libsciter-gtk.so tmpdeb/usr/lib/rustdesk/')
|
os.system('cp libsciter-gtk.so tmpdeb/usr/lib/rustdesk/')
|
||||||
md5_file('usr/share/rustdesk/files/systemd/rustdesk.service')
|
md5_file('usr/share/rustdesk/files/systemd/rustdesk.service')
|
||||||
md5_file('usr/share/rustdesk/files/systemd/rustdesk.service.user')
|
md5_file('usr/share/rustdesk/files/systemd/rustdesk.service.user')
|
||||||
md5_file('usr/share/rustdesk/files/pynput_service.py')
|
|
||||||
md5_file('usr/lib/rustdesk/libsciter-gtk.so')
|
md5_file('usr/lib/rustdesk/libsciter-gtk.so')
|
||||||
os.system('dpkg-deb -b tmpdeb rustdesk.deb; /bin/rm -rf tmpdeb/')
|
os.system('dpkg-deb -b tmpdeb rustdesk.deb; /bin/rm -rf tmpdeb/')
|
||||||
os.rename('rustdesk.deb', 'rustdesk-%s.deb' % version)
|
os.rename('rustdesk.deb', 'rustdesk-%s.deb' % version)
|
||||||
|
|||||||
2
build.rs
2
build.rs
@ -116,4 +116,4 @@ fn main() {
|
|||||||
build_windows();
|
build_windows();
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
println!("cargo:rustc-link-lib=framework=ApplicationServices");
|
println!("cargo:rustc-link-lib=framework=ApplicationServices");
|
||||||
}
|
}
|
||||||
2
flutter/.gitignore
vendored
2
flutter/.gitignore
vendored
@ -36,11 +36,9 @@ lib/generated_plugin_registrant.dart
|
|||||||
|
|
||||||
# Symbolication related
|
# Symbolication related
|
||||||
app.*.symbols
|
app.*.symbols
|
||||||
|
|
||||||
# Obfuscation related
|
# Obfuscation related
|
||||||
app.*.map.json
|
app.*.map.json
|
||||||
jniLibs
|
jniLibs
|
||||||
|
|
||||||
.vscode
|
.vscode
|
||||||
|
|
||||||
# flutter rust bridge
|
# flutter rust bridge
|
||||||
|
|||||||
@ -41,6 +41,7 @@ class _RemotePageState extends State<RemotePage>
|
|||||||
with AutomaticKeepAliveClientMixin {
|
with AutomaticKeepAliveClientMixin {
|
||||||
Timer? _timer;
|
Timer? _timer;
|
||||||
String _value = '';
|
String _value = '';
|
||||||
|
String keyboardMode = "legacy";
|
||||||
final _cursorOverImage = false.obs;
|
final _cursorOverImage = false.obs;
|
||||||
late RxBool _showRemoteCursor;
|
late RxBool _showRemoteCursor;
|
||||||
late RxBool _remoteCursorMoved;
|
late RxBool _remoteCursorMoved;
|
||||||
@ -246,6 +247,92 @@ class _RemotePageState extends State<RemotePage>
|
|||||||
], child: buildBody(context)));
|
], child: buildBody(context)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
KeyEventResult handleRawKeyEvent(FocusNode data, RawKeyEvent e) {
|
||||||
|
bind.sessionGetKeyboardName(id: widget.id).then((result) {
|
||||||
|
setState(() {
|
||||||
|
keyboardMode = result.toString();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
if (keyboardMode == 'map') {
|
||||||
|
mapKeyboardMode(e);
|
||||||
|
} else if (keyboardMode == 'translate') {
|
||||||
|
legacyKeyboardMode(e);
|
||||||
|
} else {
|
||||||
|
legacyKeyboardMode(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return KeyEventResult.handled;
|
||||||
|
}
|
||||||
|
|
||||||
|
void mapKeyboardMode(RawKeyEvent e) {
|
||||||
|
int scanCode;
|
||||||
|
int keyCode;
|
||||||
|
bool down;
|
||||||
|
|
||||||
|
if (e.data is RawKeyEventDataMacOs) {
|
||||||
|
RawKeyEventDataMacOs newData = e.data as RawKeyEventDataMacOs;
|
||||||
|
scanCode = newData.keyCode;
|
||||||
|
keyCode = newData.keyCode;
|
||||||
|
} else if (e.data is RawKeyEventDataWindows) {
|
||||||
|
RawKeyEventDataWindows newData = e.data as RawKeyEventDataWindows;
|
||||||
|
scanCode = newData.scanCode;
|
||||||
|
keyCode = newData.keyCode;
|
||||||
|
} else if (e.data is RawKeyEventDataLinux) {
|
||||||
|
RawKeyEventDataLinux newData = e.data as RawKeyEventDataLinux;
|
||||||
|
scanCode = newData.scanCode;
|
||||||
|
keyCode = newData.keyCode;
|
||||||
|
} else {
|
||||||
|
scanCode = -1;
|
||||||
|
keyCode = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (e is RawKeyDownEvent) {
|
||||||
|
down = true;
|
||||||
|
} else {
|
||||||
|
down = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
_ffi.inputRawKey(e.character ?? "", keyCode, scanCode, down);
|
||||||
|
}
|
||||||
|
|
||||||
|
void legacyKeyboardMode(RawKeyEvent e) {
|
||||||
|
final key = e.logicalKey;
|
||||||
|
if (e is RawKeyDownEvent) {
|
||||||
|
if (e.repeat) {
|
||||||
|
sendRawKey(e, press: true);
|
||||||
|
} else {
|
||||||
|
if (e.isAltPressed && !_ffi.alt) {
|
||||||
|
_ffi.alt = true;
|
||||||
|
} else if (e.isControlPressed && !_ffi.ctrl) {
|
||||||
|
_ffi.ctrl = true;
|
||||||
|
} else if (e.isShiftPressed && !_ffi.shift) {
|
||||||
|
_ffi.shift = true;
|
||||||
|
} else if (e.isMetaPressed && !_ffi.command) {
|
||||||
|
_ffi.command = true;
|
||||||
|
}
|
||||||
|
sendRawKey(e, down: true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (e is RawKeyUpEvent) {
|
||||||
|
if (key == LogicalKeyboardKey.altLeft ||
|
||||||
|
key == LogicalKeyboardKey.altRight) {
|
||||||
|
_ffi.alt = false;
|
||||||
|
} else if (key == LogicalKeyboardKey.controlLeft ||
|
||||||
|
key == LogicalKeyboardKey.controlRight) {
|
||||||
|
_ffi.ctrl = false;
|
||||||
|
} else if (key == LogicalKeyboardKey.shiftRight ||
|
||||||
|
key == LogicalKeyboardKey.shiftLeft) {
|
||||||
|
_ffi.shift = false;
|
||||||
|
} else if (key == LogicalKeyboardKey.metaLeft ||
|
||||||
|
key == LogicalKeyboardKey.metaRight ||
|
||||||
|
key == LogicalKeyboardKey.superKey) {
|
||||||
|
_ffi.command = false;
|
||||||
|
}
|
||||||
|
sendRawKey(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Widget getRawPointerAndKeyBody(Widget child) {
|
Widget getRawPointerAndKeyBody(Widget child) {
|
||||||
return FocusScope(
|
return FocusScope(
|
||||||
autofocus: true,
|
autofocus: true,
|
||||||
@ -256,42 +343,7 @@ class _RemotePageState extends State<RemotePage>
|
|||||||
onFocusChange: (bool v) {
|
onFocusChange: (bool v) {
|
||||||
_imageFocused = v;
|
_imageFocused = v;
|
||||||
},
|
},
|
||||||
onKey: (data, e) {
|
onKey: handleRawKeyEvent,
|
||||||
final key = e.logicalKey;
|
|
||||||
if (e is RawKeyDownEvent) {
|
|
||||||
if (e.repeat) {
|
|
||||||
sendRawKey(e, press: true);
|
|
||||||
} else {
|
|
||||||
if (e.isAltPressed && !_ffi.alt) {
|
|
||||||
_ffi.alt = true;
|
|
||||||
} else if (e.isControlPressed && !_ffi.ctrl) {
|
|
||||||
_ffi.ctrl = true;
|
|
||||||
} else if (e.isShiftPressed && !_ffi.shift) {
|
|
||||||
_ffi.shift = true;
|
|
||||||
} else if (e.isMetaPressed && !_ffi.command) {
|
|
||||||
_ffi.command = true;
|
|
||||||
}
|
|
||||||
sendRawKey(e, down: true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (e is RawKeyUpEvent) {
|
|
||||||
if (key == LogicalKeyboardKey.altLeft ||
|
|
||||||
key == LogicalKeyboardKey.altRight) {
|
|
||||||
_ffi.alt = false;
|
|
||||||
} else if (key == LogicalKeyboardKey.controlLeft ||
|
|
||||||
key == LogicalKeyboardKey.controlRight) {
|
|
||||||
_ffi.ctrl = false;
|
|
||||||
} else if (key == LogicalKeyboardKey.shiftRight ||
|
|
||||||
key == LogicalKeyboardKey.shiftLeft) {
|
|
||||||
_ffi.shift = false;
|
|
||||||
} else if (key == LogicalKeyboardKey.metaLeft ||
|
|
||||||
key == LogicalKeyboardKey.metaRight) {
|
|
||||||
_ffi.command = false;
|
|
||||||
}
|
|
||||||
sendRawKey(e);
|
|
||||||
}
|
|
||||||
return KeyEventResult.handled;
|
|
||||||
},
|
|
||||||
child: child));
|
child: child));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -304,7 +356,6 @@ class _RemotePageState extends State<RemotePage>
|
|||||||
/// mouseMode only:
|
/// mouseMode only:
|
||||||
/// DoubleFiner -> right click
|
/// DoubleFiner -> right click
|
||||||
/// HoldDrag -> left drag
|
/// HoldDrag -> left drag
|
||||||
|
|
||||||
void _onPointHoverImage(PointerHoverEvent e) {
|
void _onPointHoverImage(PointerHoverEvent e) {
|
||||||
if (e.kind != ui.PointerDeviceKind.mouse) return;
|
if (e.kind != ui.PointerDeviceKind.mouse) return;
|
||||||
if (!_isPhysicalMouse) {
|
if (!_isPhysicalMouse) {
|
||||||
@ -367,6 +418,19 @@ class _RemotePageState extends State<RemotePage>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void enterView(PointerEnterEvent evt) {
|
||||||
|
if (!_imageFocused) {
|
||||||
|
_physicalFocusNode.requestFocus();
|
||||||
|
}
|
||||||
|
_cursorOverImage.value = true;
|
||||||
|
_ffi.enterOrLeave(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void leaveView(PointerExitEvent evt) {
|
||||||
|
_cursorOverImage.value = false;
|
||||||
|
_ffi.enterOrLeave(false);
|
||||||
|
}
|
||||||
|
|
||||||
Widget _buildImageListener(Widget child) {
|
Widget _buildImageListener(Widget child) {
|
||||||
return Listener(
|
return Listener(
|
||||||
onPointerHover: _onPointHoverImage,
|
onPointerHover: _onPointHoverImage,
|
||||||
@ -374,17 +438,8 @@ class _RemotePageState extends State<RemotePage>
|
|||||||
onPointerUp: _onPointUpImage,
|
onPointerUp: _onPointUpImage,
|
||||||
onPointerMove: _onPointMoveImage,
|
onPointerMove: _onPointMoveImage,
|
||||||
onPointerSignal: _onPointerSignalImage,
|
onPointerSignal: _onPointerSignalImage,
|
||||||
child: MouseRegion(
|
child:
|
||||||
onEnter: (evt) {
|
MouseRegion(onEnter: enterView, onExit: leaveView, child: child));
|
||||||
if (!_imageFocused) {
|
|
||||||
_physicalFocusNode.requestFocus();
|
|
||||||
}
|
|
||||||
_cursorOverImage.value = true;
|
|
||||||
},
|
|
||||||
onExit: (evt) {
|
|
||||||
_cursorOverImage.value = false;
|
|
||||||
},
|
|
||||||
child: child));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget getBodyForDesktop(BuildContext context) {
|
Widget getBodyForDesktop(BuildContext context) {
|
||||||
|
|||||||
@ -93,6 +93,7 @@ class _RemoteMenubarState extends State<RemoteMenubar> {
|
|||||||
menubarItems.add(_buildMonitor(context));
|
menubarItems.add(_buildMonitor(context));
|
||||||
menubarItems.add(_buildControl(context));
|
menubarItems.add(_buildControl(context));
|
||||||
menubarItems.add(_buildDisplay(context));
|
menubarItems.add(_buildDisplay(context));
|
||||||
|
menubarItems.add(_buildKeyboard(context));
|
||||||
if (!isWeb) {
|
if (!isWeb) {
|
||||||
menubarItems.add(_buildChat(context));
|
menubarItems.add(_buildChat(context));
|
||||||
}
|
}
|
||||||
@ -264,6 +265,29 @@ class _RemoteMenubarState extends State<RemoteMenubar> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget _buildKeyboard(BuildContext context) {
|
||||||
|
return mod_menu.PopupMenuButton(
|
||||||
|
padding: EdgeInsets.zero,
|
||||||
|
icon: const Icon(
|
||||||
|
Icons.keyboard,
|
||||||
|
color: _MenubarTheme.commonColor,
|
||||||
|
),
|
||||||
|
tooltip: translate('Keyboard Settings'),
|
||||||
|
position: mod_menu.PopupMenuPosition.under,
|
||||||
|
onSelected: (String item) {},
|
||||||
|
itemBuilder: (BuildContext context) => _getKeyboardMenu()
|
||||||
|
.map((entry) => entry.build(
|
||||||
|
context,
|
||||||
|
const MenuConfig(
|
||||||
|
commonColor: _MenubarTheme.commonColor,
|
||||||
|
height: _MenubarTheme.height,
|
||||||
|
dividerHeight: _MenubarTheme.dividerHeight,
|
||||||
|
)))
|
||||||
|
.expand((i) => i)
|
||||||
|
.toList(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
Widget _buildClose(BuildContext context) {
|
Widget _buildClose(BuildContext context) {
|
||||||
return IconButton(
|
return IconButton(
|
||||||
tooltip: translate('Close'),
|
tooltip: translate('Close'),
|
||||||
@ -577,6 +601,28 @@ class _RemoteMenubarState extends State<RemoteMenubar> {
|
|||||||
return displayMenu;
|
return displayMenu;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
List<MenuEntryBase<String>> _getKeyboardMenu() {
|
||||||
|
final keyboardMenu = [
|
||||||
|
MenuEntryRadios<String>(
|
||||||
|
text: translate('Ratio'),
|
||||||
|
optionsGetter: () => [
|
||||||
|
MenuEntryRadioOption(
|
||||||
|
text: translate('Legacy mode'), value: 'legacy'),
|
||||||
|
MenuEntryRadioOption(text: translate('Map mode'), value: 'map'),
|
||||||
|
],
|
||||||
|
curOptionGetter: () async {
|
||||||
|
return await bind.sessionGetKeyboardName(id: widget.id) ?? 'legacy';
|
||||||
|
},
|
||||||
|
optionSetter: (String oldValue, String newValue) async {
|
||||||
|
await bind.sessionSetKeyboardMode(
|
||||||
|
id: widget.id, keyboardMode: newValue);
|
||||||
|
widget.ffi.canvasModel.updateViewStyle();
|
||||||
|
})
|
||||||
|
];
|
||||||
|
|
||||||
|
return keyboardMenu;
|
||||||
|
}
|
||||||
|
|
||||||
MenuEntrySwitch<String> _createSwitchMenuEntry(String text, String option) {
|
MenuEntrySwitch<String> _createSwitchMenuEntry(String text, String option) {
|
||||||
return MenuEntrySwitch<String>(
|
return MenuEntrySwitch<String>(
|
||||||
text: translate(text),
|
text: translate(text),
|
||||||
|
|||||||
@ -972,6 +972,28 @@ class FFI {
|
|||||||
msg: json.encode(modify({'type': type, 'buttons': button.value})));
|
msg: json.encode(modify({'type': type, 'buttons': button.value})));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Raw Key
|
||||||
|
void inputRawKey(String name, int keyCode, int scanCode, bool down) {
|
||||||
|
bind.sessionHandleFlutterKeyEvent(
|
||||||
|
id: id,
|
||||||
|
name: name,
|
||||||
|
keycode: keyCode,
|
||||||
|
scancode: scanCode,
|
||||||
|
downOrUp: down);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<String> getKeyboardMode() {
|
||||||
|
return bind.sessionGetKeyboardName(id: id);
|
||||||
|
}
|
||||||
|
|
||||||
|
void enterOrLeave(bool enter) {
|
||||||
|
// Fix status
|
||||||
|
if (!enter) {
|
||||||
|
resetModifiers();
|
||||||
|
}
|
||||||
|
bind.sessionEnterOrLeave(id: id, enter: enter);
|
||||||
|
}
|
||||||
|
|
||||||
/// Send key stroke event.
|
/// Send key stroke event.
|
||||||
/// [down] indicates the key's state(down or up).
|
/// [down] indicates the key's state(down or up).
|
||||||
/// [press] indicates a click event(down and up).
|
/// [press] indicates a click event(down and up).
|
||||||
|
|||||||
@ -97,7 +97,7 @@ class PlatformFFI {
|
|||||||
final dylib = Platform.isAndroid
|
final dylib = Platform.isAndroid
|
||||||
? DynamicLibrary.open('librustdesk.so')
|
? DynamicLibrary.open('librustdesk.so')
|
||||||
: Platform.isLinux
|
: Platform.isLinux
|
||||||
? DynamicLibrary.open("/usr/lib/rustdesk/librustdesk.so")
|
? DynamicLibrary.open("librustdesk.so")
|
||||||
: Platform.isWindows
|
: Platform.isWindows
|
||||||
? DynamicLibrary.open("librustdesk.dll")
|
? DynamicLibrary.open("librustdesk.dll")
|
||||||
: Platform.isMacOS
|
: Platform.isMacOS
|
||||||
|
|||||||
@ -74,6 +74,8 @@ corrosion_import_crate(MANIFEST_PATH ../../Cargo.toml
|
|||||||
# [FEATURES <feature1> ... <featureN>]
|
# [FEATURES <feature1> ... <featureN>]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
set(BASE_RUSTDESK "librustdesk")
|
||||||
|
|
||||||
# Define the application target. To change its name, change BINARY_NAME above,
|
# Define the application target. To change its name, change BINARY_NAME above,
|
||||||
# not the value here, or `flutter run` will no longer work.
|
# not the value here, or `flutter run` will no longer work.
|
||||||
#
|
#
|
||||||
@ -91,8 +93,8 @@ apply_standard_settings(${BINARY_NAME})
|
|||||||
# Add dependency libraries. Add any application-specific dependencies here.
|
# Add dependency libraries. Add any application-specific dependencies here.
|
||||||
target_link_libraries(${BINARY_NAME} PRIVATE flutter)
|
target_link_libraries(${BINARY_NAME} PRIVATE flutter)
|
||||||
target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK)
|
target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK)
|
||||||
|
target_link_libraries(${BINARY_NAME} PRIVATE ${BASE_RUSTDESK})
|
||||||
target_link_libraries(${BINARY_NAME} PRIVATE librustdesk)
|
# target_link_libraries(${BINARY_NAME} PRIVATE librustdesk)
|
||||||
|
|
||||||
# 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)
|
||||||
@ -142,6 +144,8 @@ foreach(bundled_library ${PLUGIN_BUNDLED_LIBRARIES})
|
|||||||
COMPONENT Runtime)
|
COMPONENT Runtime)
|
||||||
endforeach(bundled_library)
|
endforeach(bundled_library)
|
||||||
|
|
||||||
|
install(FILES $<TARGET_FILE:${BASE_RUSTDESK}-shared> DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" COMPONENT Runtime RENAME librustdesk.so)
|
||||||
|
|
||||||
# Fully re-copy the assets directory on each build to avoid having stale files
|
# Fully re-copy the assets directory on each build to avoid having stale files
|
||||||
# from a previous install.
|
# from a previous install.
|
||||||
set(FLUTTER_ASSET_DIR_NAME "flutter_assets")
|
set(FLUTTER_ASSET_DIR_NAME "flutter_assets")
|
||||||
|
|||||||
@ -1,7 +1,8 @@
|
|||||||
#include <dlfcn.h>
|
#include <dlfcn.h>
|
||||||
#include "my_application.h"
|
#include "my_application.h"
|
||||||
|
|
||||||
#define RUSTDESK_LIB_PATH "/usr/lib/rustdesk/librustdesk.so"
|
#define RUSTDESK_LIB_PATH "ibrustdesk.so"
|
||||||
|
// #define RUSTDESK_LIB_PATH "/usr/lib/rustdesk/librustdesk.so"
|
||||||
typedef bool (*RustDeskCoreMain)();
|
typedef bool (*RustDeskCoreMain)();
|
||||||
|
|
||||||
bool flutter_rustdesk_core_main() {
|
bool flutter_rustdesk_core_main() {
|
||||||
|
|||||||
@ -1,78 +1,84 @@
|
|||||||
PODS:
|
PODS:
|
||||||
- bitsdojo_window_macos (0.0.1):
|
- desktop_drop (0.0.1):
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
- desktop_multi_window (0.0.1):
|
- desktop_multi_window (0.0.1):
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
- device_info_plus_macos (0.0.1):
|
- device_info_plus_macos (0.0.1):
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
- Firebase/Analytics (8.15.0):
|
- Firebase/Analytics (9.4.0):
|
||||||
- Firebase/Core
|
- Firebase/Core
|
||||||
- Firebase/Core (8.15.0):
|
- Firebase/Core (9.4.0):
|
||||||
- Firebase/CoreOnly
|
- Firebase/CoreOnly
|
||||||
- FirebaseAnalytics (~> 8.15.0)
|
- FirebaseAnalytics (~> 9.4.0)
|
||||||
- Firebase/CoreOnly (8.15.0):
|
- Firebase/CoreOnly (9.4.0):
|
||||||
- FirebaseCore (= 8.15.0)
|
- FirebaseCore (= 9.4.0)
|
||||||
- firebase_analytics (9.1.9):
|
- firebase_analytics (9.3.3):
|
||||||
- Firebase/Analytics (= 8.15.0)
|
- Firebase/Analytics (= 9.4.0)
|
||||||
- firebase_core
|
- firebase_core
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
- firebase_core (1.17.1):
|
- firebase_core (1.21.1):
|
||||||
- Firebase/CoreOnly (~> 8.15.0)
|
- Firebase/CoreOnly (~> 9.4.0)
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
- FirebaseAnalytics (8.15.0):
|
- FirebaseAnalytics (9.4.0):
|
||||||
- FirebaseAnalytics/AdIdSupport (= 8.15.0)
|
- FirebaseAnalytics/AdIdSupport (= 9.4.0)
|
||||||
- FirebaseCore (~> 8.0)
|
- FirebaseCore (~> 9.0)
|
||||||
- FirebaseInstallations (~> 8.0)
|
- FirebaseInstallations (~> 9.0)
|
||||||
- GoogleUtilities/AppDelegateSwizzler (~> 7.7)
|
- GoogleUtilities/AppDelegateSwizzler (~> 7.7)
|
||||||
- GoogleUtilities/MethodSwizzler (~> 7.7)
|
- GoogleUtilities/MethodSwizzler (~> 7.7)
|
||||||
- GoogleUtilities/Network (~> 7.7)
|
- GoogleUtilities/Network (~> 7.7)
|
||||||
- "GoogleUtilities/NSData+zlib (~> 7.7)"
|
- "GoogleUtilities/NSData+zlib (~> 7.7)"
|
||||||
- nanopb (~> 2.30908.0)
|
- nanopb (< 2.30910.0, >= 2.30908.0)
|
||||||
- FirebaseAnalytics/AdIdSupport (8.15.0):
|
- FirebaseAnalytics/AdIdSupport (9.4.0):
|
||||||
- FirebaseCore (~> 8.0)
|
- FirebaseCore (~> 9.0)
|
||||||
- FirebaseInstallations (~> 8.0)
|
- FirebaseInstallations (~> 9.0)
|
||||||
- GoogleAppMeasurement (= 8.15.0)
|
- GoogleAppMeasurement (= 9.4.0)
|
||||||
- GoogleUtilities/AppDelegateSwizzler (~> 7.7)
|
- GoogleUtilities/AppDelegateSwizzler (~> 7.7)
|
||||||
- GoogleUtilities/MethodSwizzler (~> 7.7)
|
- GoogleUtilities/MethodSwizzler (~> 7.7)
|
||||||
- GoogleUtilities/Network (~> 7.7)
|
- GoogleUtilities/Network (~> 7.7)
|
||||||
- "GoogleUtilities/NSData+zlib (~> 7.7)"
|
- "GoogleUtilities/NSData+zlib (~> 7.7)"
|
||||||
- nanopb (~> 2.30908.0)
|
- nanopb (< 2.30910.0, >= 2.30908.0)
|
||||||
- FirebaseCore (8.15.0):
|
- FirebaseCore (9.4.0):
|
||||||
- FirebaseCoreDiagnostics (~> 8.0)
|
- FirebaseCoreDiagnostics (~> 9.0)
|
||||||
|
- FirebaseCoreInternal (~> 9.0)
|
||||||
- GoogleUtilities/Environment (~> 7.7)
|
- GoogleUtilities/Environment (~> 7.7)
|
||||||
- GoogleUtilities/Logger (~> 7.7)
|
- GoogleUtilities/Logger (~> 7.7)
|
||||||
- FirebaseCoreDiagnostics (8.15.0):
|
- FirebaseCoreDiagnostics (9.5.0):
|
||||||
- GoogleDataTransport (~> 9.1)
|
- GoogleDataTransport (< 10.0.0, >= 9.1.4)
|
||||||
- GoogleUtilities/Environment (~> 7.7)
|
- GoogleUtilities/Environment (~> 7.7)
|
||||||
- GoogleUtilities/Logger (~> 7.7)
|
- GoogleUtilities/Logger (~> 7.7)
|
||||||
- nanopb (~> 2.30908.0)
|
- nanopb (< 2.30910.0, >= 2.30908.0)
|
||||||
- FirebaseInstallations (8.15.0):
|
- FirebaseCoreInternal (9.5.0):
|
||||||
- FirebaseCore (~> 8.0)
|
- "GoogleUtilities/NSData+zlib (~> 7.7)"
|
||||||
|
- FirebaseInstallations (9.5.0):
|
||||||
|
- FirebaseCore (~> 9.0)
|
||||||
- GoogleUtilities/Environment (~> 7.7)
|
- GoogleUtilities/Environment (~> 7.7)
|
||||||
- GoogleUtilities/UserDefaults (~> 7.7)
|
- GoogleUtilities/UserDefaults (~> 7.7)
|
||||||
- PromisesObjC (< 3.0, >= 1.2)
|
- PromisesObjC (~> 2.1)
|
||||||
- FlutterMacOS (1.0.0)
|
- FlutterMacOS (1.0.0)
|
||||||
- GoogleAppMeasurement (8.15.0):
|
- FMDB (2.7.5):
|
||||||
- GoogleAppMeasurement/AdIdSupport (= 8.15.0)
|
- FMDB/standard (= 2.7.5)
|
||||||
|
- FMDB/standard (2.7.5)
|
||||||
|
- GoogleAppMeasurement (9.4.0):
|
||||||
|
- GoogleAppMeasurement/AdIdSupport (= 9.4.0)
|
||||||
- GoogleUtilities/AppDelegateSwizzler (~> 7.7)
|
- GoogleUtilities/AppDelegateSwizzler (~> 7.7)
|
||||||
- GoogleUtilities/MethodSwizzler (~> 7.7)
|
- GoogleUtilities/MethodSwizzler (~> 7.7)
|
||||||
- GoogleUtilities/Network (~> 7.7)
|
- GoogleUtilities/Network (~> 7.7)
|
||||||
- "GoogleUtilities/NSData+zlib (~> 7.7)"
|
- "GoogleUtilities/NSData+zlib (~> 7.7)"
|
||||||
- nanopb (~> 2.30908.0)
|
- nanopb (< 2.30910.0, >= 2.30908.0)
|
||||||
- GoogleAppMeasurement/AdIdSupport (8.15.0):
|
- GoogleAppMeasurement/AdIdSupport (9.4.0):
|
||||||
- GoogleAppMeasurement/WithoutAdIdSupport (= 8.15.0)
|
- GoogleAppMeasurement/WithoutAdIdSupport (= 9.4.0)
|
||||||
- GoogleUtilities/AppDelegateSwizzler (~> 7.7)
|
- GoogleUtilities/AppDelegateSwizzler (~> 7.7)
|
||||||
- GoogleUtilities/MethodSwizzler (~> 7.7)
|
- GoogleUtilities/MethodSwizzler (~> 7.7)
|
||||||
- GoogleUtilities/Network (~> 7.7)
|
- GoogleUtilities/Network (~> 7.7)
|
||||||
- "GoogleUtilities/NSData+zlib (~> 7.7)"
|
- "GoogleUtilities/NSData+zlib (~> 7.7)"
|
||||||
- nanopb (~> 2.30908.0)
|
- nanopb (< 2.30910.0, >= 2.30908.0)
|
||||||
- GoogleAppMeasurement/WithoutAdIdSupport (8.15.0):
|
- GoogleAppMeasurement/WithoutAdIdSupport (9.4.0):
|
||||||
- GoogleUtilities/AppDelegateSwizzler (~> 7.7)
|
- GoogleUtilities/AppDelegateSwizzler (~> 7.7)
|
||||||
- GoogleUtilities/MethodSwizzler (~> 7.7)
|
- GoogleUtilities/MethodSwizzler (~> 7.7)
|
||||||
- GoogleUtilities/Network (~> 7.7)
|
- GoogleUtilities/Network (~> 7.7)
|
||||||
- "GoogleUtilities/NSData+zlib (~> 7.7)"
|
- "GoogleUtilities/NSData+zlib (~> 7.7)"
|
||||||
- nanopb (~> 2.30908.0)
|
- nanopb (< 2.30910.0, >= 2.30908.0)
|
||||||
- GoogleDataTransport (9.1.4):
|
- GoogleDataTransport (9.2.0):
|
||||||
- GoogleUtilities/Environment (~> 7.7)
|
- GoogleUtilities/Environment (~> 7.7)
|
||||||
- nanopb (< 2.30910.0, >= 2.30908.0)
|
- nanopb (< 2.30910.0, >= 2.30908.0)
|
||||||
- PromisesObjC (< 3.0, >= 1.2)
|
- PromisesObjC (< 3.0, >= 1.2)
|
||||||
@ -95,18 +101,25 @@ PODS:
|
|||||||
- GoogleUtilities/Logger
|
- GoogleUtilities/Logger
|
||||||
- GoogleUtilities/UserDefaults (7.7.0):
|
- GoogleUtilities/UserDefaults (7.7.0):
|
||||||
- GoogleUtilities/Logger
|
- GoogleUtilities/Logger
|
||||||
- nanopb (2.30908.0):
|
- nanopb (2.30909.0):
|
||||||
- nanopb/decode (= 2.30908.0)
|
- nanopb/decode (= 2.30909.0)
|
||||||
- nanopb/encode (= 2.30908.0)
|
- nanopb/encode (= 2.30909.0)
|
||||||
- nanopb/decode (2.30908.0)
|
- nanopb/decode (2.30909.0)
|
||||||
- nanopb/encode (2.30908.0)
|
- nanopb/encode (2.30909.0)
|
||||||
- package_info_plus_macos (0.0.1):
|
- package_info_plus_macos (0.0.1):
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
- path_provider_macos (0.0.1):
|
- path_provider_macos (0.0.1):
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
- PromisesObjC (2.1.0)
|
- PromisesObjC (2.1.1)
|
||||||
|
- screen_retriever (0.0.1):
|
||||||
|
- FlutterMacOS
|
||||||
- shared_preferences_macos (0.0.1):
|
- shared_preferences_macos (0.0.1):
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
|
- sqflite (0.0.2):
|
||||||
|
- FlutterMacOS
|
||||||
|
- FMDB (>= 2.7.5)
|
||||||
|
- tray_manager (0.0.1):
|
||||||
|
- FlutterMacOS
|
||||||
- url_launcher_macos (0.0.1):
|
- url_launcher_macos (0.0.1):
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
- wakelock_macos (0.0.1):
|
- wakelock_macos (0.0.1):
|
||||||
@ -115,7 +128,7 @@ PODS:
|
|||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
|
|
||||||
DEPENDENCIES:
|
DEPENDENCIES:
|
||||||
- bitsdojo_window_macos (from `Flutter/ephemeral/.symlinks/plugins/bitsdojo_window_macos/macos`)
|
- desktop_drop (from `Flutter/ephemeral/.symlinks/plugins/desktop_drop/macos`)
|
||||||
- desktop_multi_window (from `Flutter/ephemeral/.symlinks/plugins/desktop_multi_window/macos`)
|
- desktop_multi_window (from `Flutter/ephemeral/.symlinks/plugins/desktop_multi_window/macos`)
|
||||||
- device_info_plus_macos (from `Flutter/ephemeral/.symlinks/plugins/device_info_plus_macos/macos`)
|
- device_info_plus_macos (from `Flutter/ephemeral/.symlinks/plugins/device_info_plus_macos/macos`)
|
||||||
- firebase_analytics (from `Flutter/ephemeral/.symlinks/plugins/firebase_analytics/macos`)
|
- firebase_analytics (from `Flutter/ephemeral/.symlinks/plugins/firebase_analytics/macos`)
|
||||||
@ -123,7 +136,10 @@ DEPENDENCIES:
|
|||||||
- FlutterMacOS (from `Flutter/ephemeral`)
|
- FlutterMacOS (from `Flutter/ephemeral`)
|
||||||
- package_info_plus_macos (from `Flutter/ephemeral/.symlinks/plugins/package_info_plus_macos/macos`)
|
- package_info_plus_macos (from `Flutter/ephemeral/.symlinks/plugins/package_info_plus_macos/macos`)
|
||||||
- path_provider_macos (from `Flutter/ephemeral/.symlinks/plugins/path_provider_macos/macos`)
|
- path_provider_macos (from `Flutter/ephemeral/.symlinks/plugins/path_provider_macos/macos`)
|
||||||
|
- screen_retriever (from `Flutter/ephemeral/.symlinks/plugins/screen_retriever/macos`)
|
||||||
- shared_preferences_macos (from `Flutter/ephemeral/.symlinks/plugins/shared_preferences_macos/macos`)
|
- shared_preferences_macos (from `Flutter/ephemeral/.symlinks/plugins/shared_preferences_macos/macos`)
|
||||||
|
- sqflite (from `Flutter/ephemeral/.symlinks/plugins/sqflite/macos`)
|
||||||
|
- tray_manager (from `Flutter/ephemeral/.symlinks/plugins/tray_manager/macos`)
|
||||||
- url_launcher_macos (from `Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos`)
|
- url_launcher_macos (from `Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos`)
|
||||||
- wakelock_macos (from `Flutter/ephemeral/.symlinks/plugins/wakelock_macos/macos`)
|
- wakelock_macos (from `Flutter/ephemeral/.symlinks/plugins/wakelock_macos/macos`)
|
||||||
- window_manager (from `Flutter/ephemeral/.symlinks/plugins/window_manager/macos`)
|
- window_manager (from `Flutter/ephemeral/.symlinks/plugins/window_manager/macos`)
|
||||||
@ -134,7 +150,9 @@ SPEC REPOS:
|
|||||||
- FirebaseAnalytics
|
- FirebaseAnalytics
|
||||||
- FirebaseCore
|
- FirebaseCore
|
||||||
- FirebaseCoreDiagnostics
|
- FirebaseCoreDiagnostics
|
||||||
|
- FirebaseCoreInternal
|
||||||
- FirebaseInstallations
|
- FirebaseInstallations
|
||||||
|
- FMDB
|
||||||
- GoogleAppMeasurement
|
- GoogleAppMeasurement
|
||||||
- GoogleDataTransport
|
- GoogleDataTransport
|
||||||
- GoogleUtilities
|
- GoogleUtilities
|
||||||
@ -142,8 +160,8 @@ SPEC REPOS:
|
|||||||
- PromisesObjC
|
- PromisesObjC
|
||||||
|
|
||||||
EXTERNAL SOURCES:
|
EXTERNAL SOURCES:
|
||||||
bitsdojo_window_macos:
|
desktop_drop:
|
||||||
:path: Flutter/ephemeral/.symlinks/plugins/bitsdojo_window_macos/macos
|
:path: Flutter/ephemeral/.symlinks/plugins/desktop_drop/macos
|
||||||
desktop_multi_window:
|
desktop_multi_window:
|
||||||
:path: Flutter/ephemeral/.symlinks/plugins/desktop_multi_window/macos
|
:path: Flutter/ephemeral/.symlinks/plugins/desktop_multi_window/macos
|
||||||
device_info_plus_macos:
|
device_info_plus_macos:
|
||||||
@ -158,8 +176,14 @@ EXTERNAL SOURCES:
|
|||||||
:path: Flutter/ephemeral/.symlinks/plugins/package_info_plus_macos/macos
|
:path: Flutter/ephemeral/.symlinks/plugins/package_info_plus_macos/macos
|
||||||
path_provider_macos:
|
path_provider_macos:
|
||||||
:path: Flutter/ephemeral/.symlinks/plugins/path_provider_macos/macos
|
:path: Flutter/ephemeral/.symlinks/plugins/path_provider_macos/macos
|
||||||
|
screen_retriever:
|
||||||
|
:path: Flutter/ephemeral/.symlinks/plugins/screen_retriever/macos
|
||||||
shared_preferences_macos:
|
shared_preferences_macos:
|
||||||
:path: Flutter/ephemeral/.symlinks/plugins/shared_preferences_macos/macos
|
:path: Flutter/ephemeral/.symlinks/plugins/shared_preferences_macos/macos
|
||||||
|
sqflite:
|
||||||
|
:path: Flutter/ephemeral/.symlinks/plugins/sqflite/macos
|
||||||
|
tray_manager:
|
||||||
|
:path: Flutter/ephemeral/.symlinks/plugins/tray_manager/macos
|
||||||
url_launcher_macos:
|
url_launcher_macos:
|
||||||
:path: Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos
|
:path: Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos
|
||||||
wakelock_macos:
|
wakelock_macos:
|
||||||
@ -168,25 +192,30 @@ EXTERNAL SOURCES:
|
|||||||
:path: Flutter/ephemeral/.symlinks/plugins/window_manager/macos
|
:path: Flutter/ephemeral/.symlinks/plugins/window_manager/macos
|
||||||
|
|
||||||
SPEC CHECKSUMS:
|
SPEC CHECKSUMS:
|
||||||
bitsdojo_window_macos: 44e3b8fe3dd463820e0321f6256c5b1c16bb6a00
|
desktop_drop: 69eeff437544aa619c8db7f4481b3a65f7696898
|
||||||
desktop_multi_window: 566489c048b501134f9d7fb6a2354c60a9126486
|
desktop_multi_window: 566489c048b501134f9d7fb6a2354c60a9126486
|
||||||
device_info_plus_macos: 1ad388a1ef433505c4038e7dd9605aadd1e2e9c7
|
device_info_plus_macos: 1ad388a1ef433505c4038e7dd9605aadd1e2e9c7
|
||||||
Firebase: 5f8193dff4b5b7c5d5ef72ae54bb76c08e2b841d
|
Firebase: 7703fc4022824b6d6db1bf7bea58d13b8e17ec46
|
||||||
firebase_analytics: d448483150504ed84f25c5437a34af2591a7929e
|
firebase_analytics: 57144bae6cd39d3be367a8767a1b8857a037cee5
|
||||||
firebase_core: 7b87364e2d1eae70018a60698e89e7d6f5320bad
|
firebase_core: 822a1076483bf9764284322c9310daa98e1e6817
|
||||||
FirebaseAnalytics: 7761cbadb00a717d8d0939363eb46041526474fa
|
FirebaseAnalytics: a1a24e72b7ba7f47045a4633f1abb545c07bd29c
|
||||||
FirebaseCore: 5743c5785c074a794d35f2fff7ecc254a91e08b1
|
FirebaseCore: 9a2b10270a854731c4d4d8a97d0aa8380ec3458d
|
||||||
FirebaseCoreDiagnostics: 92e07a649aeb66352b319d43bdd2ee3942af84cb
|
FirebaseCoreDiagnostics: 17cbf4e72b1dbd64bfdc33d4b1f07bce4f16f1d8
|
||||||
FirebaseInstallations: 40bd9054049b2eae9a2c38ef1c3dd213df3605cd
|
FirebaseCoreInternal: 50a8e39cae8abf72d5145d07ea34c3244f70862b
|
||||||
|
FirebaseInstallations: 41f811b530c41dd90973d0174381cdb3fcb5e839
|
||||||
FlutterMacOS: 57701585bf7de1b3fc2bb61f6378d73bbdea8424
|
FlutterMacOS: 57701585bf7de1b3fc2bb61f6378d73bbdea8424
|
||||||
GoogleAppMeasurement: 4c19f031220c72464d460c9daa1fb5d1acce958e
|
FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a
|
||||||
GoogleDataTransport: 5fffe35792f8b96ec8d6775f5eccd83c998d5a3b
|
GoogleAppMeasurement: 5d69e04287fc2c10cc43724bfa4bf31fc12c3dff
|
||||||
|
GoogleDataTransport: 1c8145da7117bd68bbbed00cf304edb6a24de00f
|
||||||
GoogleUtilities: e0913149f6b0625b553d70dae12b49fc62914fd1
|
GoogleUtilities: e0913149f6b0625b553d70dae12b49fc62914fd1
|
||||||
nanopb: a0ba3315591a9ae0a16a309ee504766e90db0c96
|
nanopb: b552cce312b6c8484180ef47159bc0f65a1f0431
|
||||||
package_info_plus_macos: f010621b07802a241d96d01876d6705f15e77c1c
|
package_info_plus_macos: f010621b07802a241d96d01876d6705f15e77c1c
|
||||||
path_provider_macos: 3c0c3b4b0d4a76d2bf989a913c2de869c5641a19
|
path_provider_macos: 3c0c3b4b0d4a76d2bf989a913c2de869c5641a19
|
||||||
PromisesObjC: 99b6f43f9e1044bd87a95a60beff28c2c44ddb72
|
PromisesObjC: ab77feca74fa2823e7af4249b8326368e61014cb
|
||||||
|
screen_retriever: 59634572a57080243dd1bf715e55b6c54f241a38
|
||||||
shared_preferences_macos: a64dc611287ed6cbe28fd1297898db1336975727
|
shared_preferences_macos: a64dc611287ed6cbe28fd1297898db1336975727
|
||||||
|
sqflite: a5789cceda41d54d23f31d6de539d65bb14100ea
|
||||||
|
tray_manager: 9064e219c56d75c476e46b9a21182087930baf90
|
||||||
url_launcher_macos: 597e05b8e514239626bcf4a850fcf9ef5c856ec3
|
url_launcher_macos: 597e05b8e514239626bcf4a850fcf9ef5c856ec3
|
||||||
wakelock_macos: bc3f2a9bd8d2e6c89fee1e1822e7ddac3bd004a9
|
wakelock_macos: bc3f2a9bd8d2e6c89fee1e1822e7ddac3bd004a9
|
||||||
window_manager: 3a1844359a6295ab1e47659b1a777e36773cd6e8
|
window_manager: 3a1844359a6295ab1e47659b1a777e36773cd6e8
|
||||||
|
|||||||
@ -4,5 +4,7 @@
|
|||||||
<dict>
|
<dict>
|
||||||
<key>com.apple.security.app-sandbox</key>
|
<key>com.apple.security.app-sandbox</key>
|
||||||
<true/>
|
<true/>
|
||||||
|
<key>com.apple.security.network.client</key>
|
||||||
|
<true/>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|||||||
@ -33,7 +33,7 @@
|
|||||||
|
|
||||||
/* Begin PBXFileReference section */
|
/* Begin PBXFileReference section */
|
||||||
ADDEDBA66A6E1 /* libresolv.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libresolv.tbd; path = usr/lib/libresolv.tbd; sourceTree = SDKROOT; };
|
ADDEDBA66A6E1 /* libresolv.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libresolv.tbd; path = usr/lib/libresolv.tbd; sourceTree = SDKROOT; };
|
||||||
CA603C4309E13EF4668187A5 /* Cargo.toml */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = Cargo.toml; path = /Users/ruizruiz/Work/Code/Projects/RustDesk/rustdesk/Cargo.toml; sourceTree = "<group>"; };
|
CA603C4309E13EF4668187A5 /* Cargo.toml */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = Cargo.toml; path = /Users/mac/Documents/project/rustdesk/Cargo.toml; sourceTree = "<group>"; };
|
||||||
CA604C7415FB2A3731F5016A /* liblibrustdesk_static.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = liblibrustdesk_static.a; sourceTree = BUILT_PRODUCTS_DIR; };
|
CA604C7415FB2A3731F5016A /* liblibrustdesk_static.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = liblibrustdesk_static.a; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
CA6071B5A0F5A7A3EF2297AA /* librustdesk.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = librustdesk.dylib; sourceTree = BUILT_PRODUCTS_DIR; };
|
CA6071B5A0F5A7A3EF2297AA /* librustdesk.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = librustdesk.dylib; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
CA60D3BC5386B357B2AB834F /* rustdesk */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = rustdesk; sourceTree = BUILT_PRODUCTS_DIR; };
|
CA60D3BC5386B357B2AB834F /* rustdesk */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = rustdesk; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
|
|||||||
@ -28,7 +28,7 @@ packages:
|
|||||||
name: animations
|
name: animations
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.3"
|
version: "2.0.4"
|
||||||
archive:
|
archive:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -235,12 +235,10 @@ packages:
|
|||||||
dash_chat_2:
|
dash_chat_2:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
path: "."
|
name: dash_chat_2
|
||||||
ref: feat_maxWidth
|
url: "https://pub.flutter-io.cn"
|
||||||
resolved-ref: "3946ecf86d3600b54632fd80d0eb0ef0e74f2d6a"
|
source: hosted
|
||||||
url: "https://github.com/fufesou/Dash-Chat-2"
|
version: "0.0.14"
|
||||||
source: git
|
|
||||||
version: "0.0.12"
|
|
||||||
desktop_drop:
|
desktop_drop:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -347,42 +345,42 @@ packages:
|
|||||||
name: firebase_analytics
|
name: firebase_analytics
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "9.3.2"
|
version: "9.3.3"
|
||||||
firebase_analytics_platform_interface:
|
firebase_analytics_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: firebase_analytics_platform_interface
|
name: firebase_analytics_platform_interface
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.3.2"
|
version: "3.3.3"
|
||||||
firebase_analytics_web:
|
firebase_analytics_web:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: firebase_analytics_web
|
name: firebase_analytics_web
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.4.2+2"
|
version: "0.4.2+3"
|
||||||
firebase_core:
|
firebase_core:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: firebase_core
|
name: firebase_core
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.21.0"
|
version: "1.21.1"
|
||||||
firebase_core_platform_interface:
|
firebase_core_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: firebase_core_platform_interface
|
name: firebase_core_platform_interface
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.5.0"
|
version: "4.5.1"
|
||||||
firebase_core_web:
|
firebase_core_web:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: firebase_core_web
|
name: firebase_core_web
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.7.1"
|
version: "1.7.2"
|
||||||
fixnum:
|
fixnum:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -735,7 +733,7 @@ packages:
|
|||||||
name: path_provider_android
|
name: path_provider_android
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.19"
|
version: "2.0.20"
|
||||||
path_provider_ios:
|
path_provider_ios:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -849,7 +847,7 @@ packages:
|
|||||||
source: hosted
|
source: hosted
|
||||||
version: "3.1.0"
|
version: "3.1.0"
|
||||||
rxdart:
|
rxdart:
|
||||||
dependency: transitive
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: rxdart
|
name: rxdart
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
@ -1257,7 +1255,7 @@ packages:
|
|||||||
name: xdg_directories
|
name: xdg_directories
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.2.0+1"
|
version: "0.2.0+2"
|
||||||
xml:
|
xml:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|||||||
@ -22,6 +22,8 @@ appveyor = { repository = "pythoneer/enigo-85xiy" }
|
|||||||
serde = { version = "1.0", optional = true }
|
serde = { version = "1.0", optional = true }
|
||||||
serde_derive = { version = "1.0", optional = true }
|
serde_derive = { version = "1.0", optional = true }
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
|
rdev = { git = "https://github.com/asur4s/rdev" }
|
||||||
|
tfc = { git = "https://github.com/asur4s/The-Fat-Controller" }
|
||||||
hbb_common = { path = "../hbb_common" }
|
hbb_common = { path = "../hbb_common" }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
|
|||||||
@ -257,7 +257,7 @@ pub enum Key {
|
|||||||
Backspace,
|
Backspace,
|
||||||
/// caps lock key
|
/// caps lock key
|
||||||
CapsLock,
|
CapsLock,
|
||||||
#[deprecated(since = "0.0.12", note = "now renamed to Meta")]
|
// #[deprecated(since = "0.0.12", note = "now renamed to Meta")]
|
||||||
/// command key on macOS (super key on Linux, windows key on Windows)
|
/// command key on macOS (super key on Linux, windows key on Windows)
|
||||||
Command,
|
Command,
|
||||||
/// control key
|
/// control key
|
||||||
@ -314,14 +314,14 @@ pub enum Key {
|
|||||||
Shift,
|
Shift,
|
||||||
/// space key
|
/// space key
|
||||||
Space,
|
Space,
|
||||||
#[deprecated(since = "0.0.12", note = "now renamed to Meta")]
|
// #[deprecated(since = "0.0.12", note = "now renamed to Meta")]
|
||||||
/// super key on linux (command key on macOS, windows key on Windows)
|
/// super key on linux (command key on macOS, windows key on Windows)
|
||||||
Super,
|
Super,
|
||||||
/// tab key (tabulator)
|
/// tab key (tabulator)
|
||||||
Tab,
|
Tab,
|
||||||
/// up arrow key
|
/// up arrow key
|
||||||
UpArrow,
|
UpArrow,
|
||||||
#[deprecated(since = "0.0.12", note = "now renamed to Meta")]
|
// #[deprecated(since = "0.0.12", note = "now renamed to Meta")]
|
||||||
/// windows key on Windows (super key on Linux, command key on macOS)
|
/// windows key on Windows (super key on Linux, command key on macOS)
|
||||||
Windows,
|
Windows,
|
||||||
///
|
///
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
mod nix_impl;
|
mod nix_impl;
|
||||||
mod pynput;
|
|
||||||
mod xdo;
|
mod xdo;
|
||||||
|
|
||||||
pub use self::nix_impl::Enigo;
|
pub use self::nix_impl::Enigo;
|
||||||
|
|||||||
@ -1,12 +1,14 @@
|
|||||||
use super::{pynput::EnigoPynput, xdo::EnigoXdo};
|
use super::xdo::EnigoXdo;
|
||||||
use crate::{Key, KeyboardControllable, MouseButton, MouseControllable};
|
use crate::{Key, KeyboardControllable, MouseButton, MouseControllable};
|
||||||
|
use std::io::Read;
|
||||||
|
use tfc::{traits::*, Context as TFC_Context, Key as TFC_Key};
|
||||||
|
|
||||||
/// The main struct for handling the event emitting
|
/// The main struct for handling the event emitting
|
||||||
// #[derive(Default)]
|
// #[derive(Default)]
|
||||||
pub struct Enigo {
|
pub struct Enigo {
|
||||||
xdo: EnigoXdo,
|
xdo: EnigoXdo,
|
||||||
pynput: EnigoPynput,
|
|
||||||
is_x11: bool,
|
is_x11: bool,
|
||||||
|
tfc: TFC_Context,
|
||||||
uinput_keyboard: Option<Box<dyn KeyboardControllable + Send>>,
|
uinput_keyboard: Option<Box<dyn KeyboardControllable + Send>>,
|
||||||
uinput_mouse: Option<Box<dyn MouseControllable + Send>>,
|
uinput_mouse: Option<Box<dyn MouseControllable + Send>>,
|
||||||
}
|
}
|
||||||
@ -20,10 +22,6 @@ impl Enigo {
|
|||||||
pub fn set_delay(&mut self, delay: u64) {
|
pub fn set_delay(&mut self, delay: u64) {
|
||||||
self.xdo.set_delay(delay)
|
self.xdo.set_delay(delay)
|
||||||
}
|
}
|
||||||
/// Reset pynput.
|
|
||||||
pub fn reset(&mut self) {
|
|
||||||
self.pynput.reset();
|
|
||||||
}
|
|
||||||
/// Set uinput keyboard.
|
/// Set uinput keyboard.
|
||||||
pub fn set_uinput_keyboard(
|
pub fn set_uinput_keyboard(
|
||||||
&mut self,
|
&mut self,
|
||||||
@ -35,16 +33,51 @@ impl Enigo {
|
|||||||
pub fn set_uinput_mouse(&mut self, uinput_mouse: Option<Box<dyn MouseControllable + Send>>) {
|
pub fn set_uinput_mouse(&mut self, uinput_mouse: Option<Box<dyn MouseControllable + Send>>) {
|
||||||
self.uinput_mouse = uinput_mouse
|
self.uinput_mouse = uinput_mouse
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn tfc_key_down_or_up(&mut self, key: Key, down: bool, up: bool) -> bool {
|
||||||
|
if let Key::Layout(chr) = key {
|
||||||
|
if down {
|
||||||
|
if let Err(_) = self.tfc.unicode_char_down(chr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if up {
|
||||||
|
if let Err(_) = self.tfc.unicode_char_up(chr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
let key = match convert_to_tfc_key(key) {
|
||||||
|
Some(key) => key,
|
||||||
|
None => {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if down {
|
||||||
|
if let Err(_) = self.tfc.key_down(key) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if up {
|
||||||
|
if let Err(_) = self.tfc.key_up(key) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Enigo {
|
impl Default for Enigo {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
is_x11: "x11" == hbb_common::platform::linux::get_display_server(),
|
is_x11: "x11" == hbb_common::platform::linux::get_display_server(),
|
||||||
|
tfc: TFC_Context::new().expect("kbd context error"),
|
||||||
uinput_keyboard: None,
|
uinput_keyboard: None,
|
||||||
uinput_mouse: None,
|
uinput_mouse: None,
|
||||||
xdo: EnigoXdo::default(),
|
xdo: EnigoXdo::default(),
|
||||||
pynput: EnigoPynput::default(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -117,6 +150,26 @@ impl MouseControllable for Enigo {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_led_state(key: Key) -> bool {
|
||||||
|
let led_file = match key {
|
||||||
|
Key::CapsLock => "/sys/class/leds/input1::capslock/brightness",
|
||||||
|
Key::NumLock => "/sys/class/leds/input1::numlock/brightness",
|
||||||
|
_ => {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let status = if let Ok(mut file) = std::fs::File::open(&led_file) {
|
||||||
|
let mut content = String::new();
|
||||||
|
file.read_to_string(&mut content).ok();
|
||||||
|
let status = content.trim_end().to_string().parse::<i32>().unwrap_or(0);
|
||||||
|
status
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
};
|
||||||
|
status == 1
|
||||||
|
}
|
||||||
|
|
||||||
impl KeyboardControllable for Enigo {
|
impl KeyboardControllable for Enigo {
|
||||||
fn get_key_state(&mut self, key: Key) -> bool {
|
fn get_key_state(&mut self, key: Key) -> bool {
|
||||||
if self.is_x11 {
|
if self.is_x11 {
|
||||||
@ -125,7 +178,7 @@ impl KeyboardControllable for Enigo {
|
|||||||
if let Some(keyboard) = &mut self.uinput_keyboard {
|
if let Some(keyboard) = &mut self.uinput_keyboard {
|
||||||
keyboard.get_key_state(key)
|
keyboard.get_key_state(key)
|
||||||
} else {
|
} else {
|
||||||
false
|
get_led_state(key)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -142,10 +195,12 @@ impl KeyboardControllable for Enigo {
|
|||||||
|
|
||||||
fn key_down(&mut self, key: Key) -> crate::ResultType {
|
fn key_down(&mut self, key: Key) -> crate::ResultType {
|
||||||
if self.is_x11 {
|
if self.is_x11 {
|
||||||
if self.pynput.send_pynput(&key, true) {
|
let has_down = self.tfc_key_down_or_up(key, true, false);
|
||||||
return Ok(());
|
if !has_down {
|
||||||
|
self.xdo.key_down(key)
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
self.xdo.key_down(key)
|
|
||||||
} else {
|
} else {
|
||||||
if let Some(keyboard) = &mut self.uinput_keyboard {
|
if let Some(keyboard) = &mut self.uinput_keyboard {
|
||||||
keyboard.key_down(key)
|
keyboard.key_down(key)
|
||||||
@ -156,10 +211,10 @@ impl KeyboardControllable for Enigo {
|
|||||||
}
|
}
|
||||||
fn key_up(&mut self, key: Key) {
|
fn key_up(&mut self, key: Key) {
|
||||||
if self.is_x11 {
|
if self.is_x11 {
|
||||||
if self.pynput.send_pynput(&key, false) {
|
let has_down = self.tfc_key_down_or_up(key, false, true);
|
||||||
return;
|
if !has_down {
|
||||||
|
self.xdo.key_up(key)
|
||||||
}
|
}
|
||||||
self.xdo.key_up(key)
|
|
||||||
} else {
|
} else {
|
||||||
if let Some(keyboard) = &mut self.uinput_keyboard {
|
if let Some(keyboard) = &mut self.uinput_keyboard {
|
||||||
keyboard.key_up(key)
|
keyboard.key_up(key)
|
||||||
@ -167,12 +222,76 @@ impl KeyboardControllable for Enigo {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn key_click(&mut self, key: Key) {
|
fn key_click(&mut self, key: Key) {
|
||||||
if self.is_x11 {
|
self.key_down(key).ok();
|
||||||
self.xdo.key_click(key)
|
self.key_up(key);
|
||||||
} else {
|
|
||||||
if let Some(keyboard) = &mut self.uinput_keyboard {
|
|
||||||
keyboard.key_click(key)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn convert_to_tfc_key(key: Key) -> Option<TFC_Key> {
|
||||||
|
let key = match key {
|
||||||
|
Key::Alt => TFC_Key::Alt,
|
||||||
|
Key::Backspace => TFC_Key::DeleteOrBackspace,
|
||||||
|
Key::CapsLock => TFC_Key::CapsLock,
|
||||||
|
Key::Control => TFC_Key::Control,
|
||||||
|
Key::Delete => TFC_Key::ForwardDelete,
|
||||||
|
Key::DownArrow => TFC_Key::DownArrow,
|
||||||
|
Key::End => TFC_Key::End,
|
||||||
|
Key::Escape => TFC_Key::Escape,
|
||||||
|
Key::F1 => TFC_Key::F1,
|
||||||
|
Key::F10 => TFC_Key::F10,
|
||||||
|
Key::F11 => TFC_Key::F11,
|
||||||
|
Key::F12 => TFC_Key::F12,
|
||||||
|
Key::F2 => TFC_Key::F2,
|
||||||
|
Key::F3 => TFC_Key::F3,
|
||||||
|
Key::F4 => TFC_Key::F4,
|
||||||
|
Key::F5 => TFC_Key::F5,
|
||||||
|
Key::F6 => TFC_Key::F6,
|
||||||
|
Key::F7 => TFC_Key::F7,
|
||||||
|
Key::F8 => TFC_Key::F8,
|
||||||
|
Key::F9 => TFC_Key::F9,
|
||||||
|
Key::Home => TFC_Key::Home,
|
||||||
|
Key::LeftArrow => TFC_Key::LeftArrow,
|
||||||
|
Key::PageDown => TFC_Key::PageDown,
|
||||||
|
Key::PageUp => TFC_Key::PageUp,
|
||||||
|
Key::Return => TFC_Key::ReturnOrEnter,
|
||||||
|
Key::RightArrow => TFC_Key::RightArrow,
|
||||||
|
Key::Shift => TFC_Key::Shift,
|
||||||
|
Key::Space => TFC_Key::Space,
|
||||||
|
Key::Tab => TFC_Key::Tab,
|
||||||
|
Key::UpArrow => TFC_Key::UpArrow,
|
||||||
|
Key::Numpad0 => TFC_Key::N0,
|
||||||
|
Key::Numpad1 => TFC_Key::N1,
|
||||||
|
Key::Numpad2 => TFC_Key::N2,
|
||||||
|
Key::Numpad3 => TFC_Key::N3,
|
||||||
|
Key::Numpad4 => TFC_Key::N4,
|
||||||
|
Key::Numpad5 => TFC_Key::N5,
|
||||||
|
Key::Numpad6 => TFC_Key::N6,
|
||||||
|
Key::Numpad7 => TFC_Key::N7,
|
||||||
|
Key::Numpad8 => TFC_Key::N8,
|
||||||
|
Key::Numpad9 => TFC_Key::N9,
|
||||||
|
Key::Decimal => TFC_Key::NumpadDecimal,
|
||||||
|
Key::Clear => TFC_Key::NumpadClear,
|
||||||
|
Key::Pause => TFC_Key::PlayPause,
|
||||||
|
Key::Print => TFC_Key::Print,
|
||||||
|
Key::Snapshot => TFC_Key::PrintScreen,
|
||||||
|
Key::Insert => TFC_Key::Insert,
|
||||||
|
Key::Scroll => TFC_Key::ScrollLock,
|
||||||
|
Key::NumLock => TFC_Key::NumLock,
|
||||||
|
Key::RWin => TFC_Key::Meta,
|
||||||
|
Key::Apps => TFC_Key::Apps,
|
||||||
|
Key::Multiply => TFC_Key::NumpadMultiply,
|
||||||
|
Key::Add => TFC_Key::NumpadPlus,
|
||||||
|
Key::Subtract => TFC_Key::NumpadMinus,
|
||||||
|
Key::Divide => TFC_Key::NumpadDivide,
|
||||||
|
Key::Equals => TFC_Key::NumpadEquals,
|
||||||
|
Key::NumpadEnter => TFC_Key::NumpadEnter,
|
||||||
|
Key::RightShift => TFC_Key::RightShift,
|
||||||
|
Key::RightControl => TFC_Key::RightControl,
|
||||||
|
Key::RightAlt => TFC_Key::RightAlt,
|
||||||
|
Key::Command | Key::Super | Key::Windows | Key::Meta => TFC_Key::Meta,
|
||||||
|
_ => {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Some(key)
|
||||||
|
}
|
||||||
|
|||||||
@ -1,280 +0,0 @@
|
|||||||
use crate::Key;
|
|
||||||
use std::{io::prelude::*, sync::mpsc};
|
|
||||||
|
|
||||||
enum PyMsg {
|
|
||||||
Char(char),
|
|
||||||
Str(&'static str),
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The main struct for handling the event emitting
|
|
||||||
pub(super) struct EnigoPynput {
|
|
||||||
tx: mpsc::Sender<(PyMsg, bool)>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for EnigoPynput {
|
|
||||||
fn default() -> Self {
|
|
||||||
let (tx, rx) = mpsc::channel();
|
|
||||||
start_pynput_service(rx);
|
|
||||||
Self { tx }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl EnigoPynput {
|
|
||||||
pub(super) fn reset(&mut self) {
|
|
||||||
self.tx.send((PyMsg::Char('\0'), true)).ok();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub(super) fn send_pynput(&mut self, key: &Key, is_press: bool) -> bool {
|
|
||||||
if unsafe { PYNPUT_EXIT || !PYNPUT_REDAY } {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if let Key::Layout(c) = key {
|
|
||||||
return self.tx.send((PyMsg::Char(*c), is_press)).is_ok();
|
|
||||||
}
|
|
||||||
if let Key::Raw(_) = key {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
#[allow(deprecated)]
|
|
||||||
let s = match key {
|
|
||||||
Key::Alt => "Alt_L",
|
|
||||||
Key::Backspace => "BackSpace",
|
|
||||||
Key::CapsLock => "Caps_Lock",
|
|
||||||
Key::Control => "Control_L",
|
|
||||||
Key::Delete => "Delete",
|
|
||||||
Key::DownArrow => "Down",
|
|
||||||
Key::End => "End",
|
|
||||||
Key::Escape => "Escape",
|
|
||||||
Key::F1 => "F1",
|
|
||||||
Key::F10 => "F10",
|
|
||||||
Key::F11 => "F11",
|
|
||||||
Key::F12 => "F12",
|
|
||||||
Key::F2 => "F2",
|
|
||||||
Key::F3 => "F3",
|
|
||||||
Key::F4 => "F4",
|
|
||||||
Key::F5 => "F5",
|
|
||||||
Key::F6 => "F6",
|
|
||||||
Key::F7 => "F7",
|
|
||||||
Key::F8 => "F8",
|
|
||||||
Key::F9 => "F9",
|
|
||||||
Key::Home => "Home",
|
|
||||||
Key::LeftArrow => "Left",
|
|
||||||
Key::Option => "Option",
|
|
||||||
Key::PageDown => "Page_Down",
|
|
||||||
Key::PageUp => "Page_Up",
|
|
||||||
Key::Return => "Return",
|
|
||||||
Key::RightArrow => "Right",
|
|
||||||
Key::Shift => "Shift_L",
|
|
||||||
Key::Space => "space",
|
|
||||||
Key::Tab => "Tab",
|
|
||||||
Key::UpArrow => "Up",
|
|
||||||
Key::Numpad0 => "0",
|
|
||||||
Key::Numpad1 => "1",
|
|
||||||
Key::Numpad2 => "2",
|
|
||||||
Key::Numpad3 => "3",
|
|
||||||
Key::Numpad4 => "4",
|
|
||||||
Key::Numpad5 => "5",
|
|
||||||
Key::Numpad6 => "6",
|
|
||||||
Key::Numpad7 => "7",
|
|
||||||
Key::Numpad8 => "8",
|
|
||||||
Key::Numpad9 => "9",
|
|
||||||
Key::Decimal => "KP_Decimal",
|
|
||||||
Key::Cancel => "Cancel",
|
|
||||||
Key::Clear => "Clear",
|
|
||||||
Key::Pause => "Pause",
|
|
||||||
Key::Kana => "Kana",
|
|
||||||
Key::Hangul => "Hangul",
|
|
||||||
Key::Hanja => "Hanja",
|
|
||||||
Key::Kanji => "Kanji",
|
|
||||||
Key::Select => "Select",
|
|
||||||
Key::Print => "Print",
|
|
||||||
Key::Execute => "Execute",
|
|
||||||
Key::Snapshot => "3270_PrintScreen",
|
|
||||||
Key::Insert => "Insert",
|
|
||||||
Key::Help => "Help",
|
|
||||||
Key::Separator => "KP_Separator",
|
|
||||||
Key::Scroll => "Scroll_Lock",
|
|
||||||
Key::NumLock => "Num_Lock",
|
|
||||||
Key::RWin => "Super_R",
|
|
||||||
Key::Apps => "Menu",
|
|
||||||
Key::Multiply => "KP_Multiply",
|
|
||||||
Key::Add => "KP_Add",
|
|
||||||
Key::Subtract => "KP_Subtract",
|
|
||||||
Key::Divide => "KP_Divide",
|
|
||||||
Key::Equals => "KP_Equal",
|
|
||||||
Key::NumpadEnter => "KP_Enter",
|
|
||||||
Key::RightShift => "Shift_R",
|
|
||||||
Key::RightControl => "Control_R",
|
|
||||||
Key::RightAlt => "Mode_switch",
|
|
||||||
Key::Command | Key::Super | Key::Windows | Key::Meta => "Super_L",
|
|
||||||
_ => {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
log::info!("send pynput: {:?}", &s);
|
|
||||||
return self.tx.send((PyMsg::Str(s), is_press)).is_ok();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// impl MouseControllable for EnigoPynput {
|
|
||||||
// fn mouse_move_to(&mut self, _x: i32, _y: i32) {
|
|
||||||
// unimplemented!()
|
|
||||||
// }
|
|
||||||
// fn mouse_move_relative(&mut self, _x: i32, _y: i32) {
|
|
||||||
// unimplemented!()
|
|
||||||
// }
|
|
||||||
// fn mouse_down(&mut self, _button: MouseButton) -> crate::ResultType {
|
|
||||||
// unimplemented!()
|
|
||||||
// }
|
|
||||||
// fn mouse_up(&mut self, _button: MouseButton) {
|
|
||||||
// unimplemented!()
|
|
||||||
// }
|
|
||||||
// fn mouse_click(&mut self, _button: MouseButton) {
|
|
||||||
// unimplemented!()
|
|
||||||
// }
|
|
||||||
// fn mouse_scroll_x(&mut self, _length: i32) {
|
|
||||||
// unimplemented!()
|
|
||||||
// }
|
|
||||||
// fn mouse_scroll_y(&mut self, _length: i32) {
|
|
||||||
// unimplemented!()
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// impl KeyboardControllable for EnigoPynput {
|
|
||||||
// fn get_key_state(&mut self, _key: Key) -> bool {
|
|
||||||
// unimplemented!()
|
|
||||||
// }
|
|
||||||
|
|
||||||
// fn key_sequence(&mut self, _sequence: &str) {
|
|
||||||
// unimplemented!()
|
|
||||||
// }
|
|
||||||
// fn key_down(&mut self, key: Key) -> crate::ResultType {
|
|
||||||
// let _ = self.send_pynput(&key, true);
|
|
||||||
// Ok(())
|
|
||||||
// }
|
|
||||||
// fn key_up(&mut self, key: Key) {
|
|
||||||
// let _ = self.send_pynput(&key, false);
|
|
||||||
// }
|
|
||||||
// fn key_click(&mut self, _key: Key) {
|
|
||||||
// unimplemented!()
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
static mut PYNPUT_EXIT: bool = false;
|
|
||||||
static mut PYNPUT_REDAY: bool = false;
|
|
||||||
static IPC_FILE: &'static str = "/tmp/RustDesk/pynput_service";
|
|
||||||
|
|
||||||
fn start_pynput_service(rx: mpsc::Receiver<(PyMsg, bool)>) {
|
|
||||||
let mut py = "./pynput_service.py".to_owned();
|
|
||||||
if !std::path::Path::new(&py).exists() {
|
|
||||||
py = "/usr/share/rustdesk/files/pynput_service.py".to_owned();
|
|
||||||
if !std::path::Path::new(&py).exists() {
|
|
||||||
py = "/usr/lib/rustdesk/pynput_service.py".to_owned();
|
|
||||||
if !std::path::Path::new(&py).exists() {
|
|
||||||
log::error!("{} not exits", py);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
log::info!("pynput service: {}", py);
|
|
||||||
std::thread::spawn(move || {
|
|
||||||
let username = std::env::var("PYNPUT_USERNAME").unwrap_or("".to_owned());
|
|
||||||
let userid = std::env::var("PYNPUT_USERID").unwrap_or("".to_owned());
|
|
||||||
let status = if username.is_empty() {
|
|
||||||
std::process::Command::new("python3")
|
|
||||||
.arg(&py)
|
|
||||||
.arg(IPC_FILE)
|
|
||||||
.status()
|
|
||||||
.map(|x| x.success())
|
|
||||||
} else {
|
|
||||||
let mut status = Ok(true);
|
|
||||||
for i in 0..100 {
|
|
||||||
if i % 10 == 0 {
|
|
||||||
log::info!("#{} try to start pynput server", i);
|
|
||||||
}
|
|
||||||
status = std::process::Command::new("sudo")
|
|
||||||
.args(vec![
|
|
||||||
"-E",
|
|
||||||
&format!("XDG_RUNTIME_DIR=/run/user/{}", userid) as &str,
|
|
||||||
"-u",
|
|
||||||
&username,
|
|
||||||
"python3",
|
|
||||||
&py,
|
|
||||||
IPC_FILE,
|
|
||||||
])
|
|
||||||
.status()
|
|
||||||
.map(|x| x.success());
|
|
||||||
match status {
|
|
||||||
Ok(true) => break,
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
std::thread::sleep(std::time::Duration::from_millis(100));
|
|
||||||
}
|
|
||||||
status
|
|
||||||
};
|
|
||||||
log::info!(
|
|
||||||
"pynput server exit with username/id {}/{}: {:?}",
|
|
||||||
username,
|
|
||||||
userid,
|
|
||||||
status
|
|
||||||
);
|
|
||||||
unsafe {
|
|
||||||
PYNPUT_EXIT = true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
std::thread::spawn(move || {
|
|
||||||
for i in 0..300 {
|
|
||||||
std::thread::sleep(std::time::Duration::from_millis(100));
|
|
||||||
let mut conn = match std::os::unix::net::UnixStream::connect(IPC_FILE) {
|
|
||||||
Ok(conn) => conn,
|
|
||||||
Err(err) => {
|
|
||||||
if i % 15 == 0 {
|
|
||||||
log::warn!("Failed to connect to {}: {}", IPC_FILE, err);
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
if let Err(err) = conn.set_nonblocking(true) {
|
|
||||||
log::error!("Failed to set ipc nonblocking: {}", err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
log::info!("Conntected to pynput server");
|
|
||||||
let d = std::time::Duration::from_millis(30);
|
|
||||||
unsafe {
|
|
||||||
PYNPUT_REDAY = true;
|
|
||||||
}
|
|
||||||
let mut buf = [0u8; 1024];
|
|
||||||
loop {
|
|
||||||
if unsafe { PYNPUT_EXIT } {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
match rx.recv_timeout(d) {
|
|
||||||
Ok((msg, is_press)) => {
|
|
||||||
let msg = match msg {
|
|
||||||
PyMsg::Char(chr) => {
|
|
||||||
format!("{}{}", if is_press { 'p' } else { 'r' }, chr)
|
|
||||||
}
|
|
||||||
PyMsg::Str(s) => format!("{}{}", if is_press { 'p' } else { 'r' }, s),
|
|
||||||
};
|
|
||||||
let n = msg.len();
|
|
||||||
buf[0] = n as _;
|
|
||||||
buf[1..(n + 1)].copy_from_slice(msg.as_bytes());
|
|
||||||
if let Err(err) = conn.write_all(&buf[..n + 1]) {
|
|
||||||
log::error!("Failed to write to ipc: {}", err);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(err) => match err {
|
|
||||||
mpsc::RecvTimeoutError::Disconnected => {
|
|
||||||
log::error!("pynput sender disconnecte");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
unsafe {
|
|
||||||
PYNPUT_REDAY = false;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
@ -370,4 +370,19 @@ impl KeyboardControllable for EnigoXdo {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn key_sequence_parse(&mut self, sequence: &str)
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
{
|
||||||
|
self.key_sequence_parse_try(sequence)
|
||||||
|
.expect("Could not parse sequence");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn key_sequence_parse_try(&mut self, sequence: &str) -> Result<(), crate::dsl::ParseError>
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
{
|
||||||
|
crate::dsl::eval(self, sequence)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
use core_graphics;
|
use core_graphics;
|
||||||
|
|
||||||
// TODO(dustin): use only the things i need
|
// TODO(dustin): use only the things i need
|
||||||
|
|
||||||
use self::core_graphics::display::*;
|
use self::core_graphics::display::*;
|
||||||
|
|||||||
@ -1,9 +1,8 @@
|
|||||||
use winapi;
|
|
||||||
|
|
||||||
use self::winapi::ctypes::c_int;
|
use self::winapi::ctypes::c_int;
|
||||||
use self::winapi::shared::{basetsd::ULONG_PTR, minwindef::*, windef::*};
|
use self::winapi::shared::{basetsd::ULONG_PTR, minwindef::*, windef::*};
|
||||||
use self::winapi::um::winbase::*;
|
use self::winapi::um::winbase::*;
|
||||||
use self::winapi::um::winuser::*;
|
use self::winapi::um::winuser::*;
|
||||||
|
use winapi;
|
||||||
|
|
||||||
use crate::win::keycodes::*;
|
use crate::win::keycodes::*;
|
||||||
use crate::{Key, KeyboardControllable, MouseButton, MouseControllable};
|
use crate::{Key, KeyboardControllable, MouseButton, MouseControllable};
|
||||||
@ -200,7 +199,7 @@ impl KeyboardControllable for Enigo {
|
|||||||
fn key_down(&mut self, key: Key) -> crate::ResultType {
|
fn key_down(&mut self, key: Key) -> crate::ResultType {
|
||||||
let code = self.key_to_keycode(key);
|
let code = self.key_to_keycode(key);
|
||||||
if code == 0 || code == 65535 {
|
if code == 0 || code == 65535 {
|
||||||
return Err("".into());
|
return Err("".into());
|
||||||
}
|
}
|
||||||
let res = keybd_event(0, code, 0);
|
let res = keybd_event(0, code, 0);
|
||||||
if res == 0 {
|
if res == 0 {
|
||||||
@ -227,7 +226,8 @@ impl KeyboardControllable for Enigo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Enigo {
|
impl Enigo {
|
||||||
/// Gets the (width, height) of the main display in screen coordinates (pixels).
|
/// Gets the (width, height) of the main display in screen coordinates
|
||||||
|
/// (pixels).
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
|
|||||||
@ -105,6 +105,13 @@ message MouseEvent {
|
|||||||
repeated ControlKey modifiers = 4;
|
repeated ControlKey modifiers = 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum KeyboardMode{
|
||||||
|
Legacy = 0;
|
||||||
|
Map = 1;
|
||||||
|
Translate = 2;
|
||||||
|
Auto = 3;
|
||||||
|
}
|
||||||
|
|
||||||
enum ControlKey {
|
enum ControlKey {
|
||||||
Unknown = 0;
|
Unknown = 0;
|
||||||
Alt = 1;
|
Alt = 1;
|
||||||
@ -198,6 +205,7 @@ message KeyEvent {
|
|||||||
string seq = 6;
|
string seq = 6;
|
||||||
}
|
}
|
||||||
repeated ControlKey modifiers = 8;
|
repeated ControlKey modifiers = 8;
|
||||||
|
KeyboardMode mode = 9;
|
||||||
}
|
}
|
||||||
|
|
||||||
message CursorData {
|
message CursorData {
|
||||||
|
|||||||
@ -7,7 +7,7 @@ post_install() {
|
|||||||
# do something here
|
# do something here
|
||||||
cp /usr/share/rustdesk/files/rustdesk.service /etc/systemd/system/rustdesk.service
|
cp /usr/share/rustdesk/files/rustdesk.service /etc/systemd/system/rustdesk.service
|
||||||
cp /usr/share/rustdesk/files/rustdesk.desktop /usr/share/applications/
|
cp /usr/share/rustdesk/files/rustdesk.desktop /usr/share/applications/
|
||||||
sudo -H pip3 install pynput
|
sudo -H pip3 install
|
||||||
systemctl daemon-reload
|
systemctl daemon-reload
|
||||||
systemctl enable rustdesk
|
systemctl enable rustdesk
|
||||||
systemctl start rustdesk
|
systemctl start rustdesk
|
||||||
|
|||||||
@ -1,236 +0,0 @@
|
|||||||
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.
|
|
||||||
:param int keysym: The keysym to handle.
|
|
||||||
"""
|
|
||||||
event = Xlib.display.event.KeyPress if is_press \
|
|
||||||
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
|
|
||||||
|
|
||||||
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]
|
|
||||||
|
|
||||||
# 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
|
|
||||||
|
|
||||||
try:
|
|
||||||
with self.modifiers as modifiers:
|
|
||||||
alt_gr = Key.alt_gr in modifiers
|
|
||||||
# !!!: Send_event can't support lock screen, this condition cann't be modified
|
|
||||||
if alt_gr:
|
|
||||||
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)
|
|
||||||
|
|
||||||
|
|
||||||
keyboard = MyController()
|
|
||||||
|
|
||||||
server_address = sys.argv[1]
|
|
||||||
if not os.path.exists(os.path.dirname(server_address)):
|
|
||||||
os.makedirs(os.path.dirname(server_address))
|
|
||||||
|
|
||||||
try:
|
|
||||||
os.unlink(server_address)
|
|
||||||
except OSError:
|
|
||||||
if os.path.exists(server_address):
|
|
||||||
raise
|
|
||||||
|
|
||||||
server = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
|
|
||||||
server.bind(server_address)
|
|
||||||
server.listen(1)
|
|
||||||
clientsocket, address = server.accept()
|
|
||||||
os.system('chmod a+rw %s' % server_address)
|
|
||||||
print("Got pynput connection")
|
|
||||||
|
|
||||||
|
|
||||||
def loop():
|
|
||||||
global keyboard
|
|
||||||
buf = []
|
|
||||||
while True:
|
|
||||||
data = clientsocket.recv(1024)
|
|
||||||
if not data:
|
|
||||||
print("Connection broken")
|
|
||||||
break
|
|
||||||
buf.extend(data)
|
|
||||||
while buf:
|
|
||||||
n = buf[0]
|
|
||||||
n = n + 1
|
|
||||||
if len(buf) < n:
|
|
||||||
break
|
|
||||||
msg = bytearray(buf[1:n]).decode("utf-8")
|
|
||||||
buf = buf[n:]
|
|
||||||
if len(msg) < 2:
|
|
||||||
continue
|
|
||||||
if msg[1] == "\0":
|
|
||||||
keyboard = MyController()
|
|
||||||
print("Keyboard reset")
|
|
||||||
continue
|
|
||||||
if len(msg) == 2:
|
|
||||||
name = msg[1]
|
|
||||||
else:
|
|
||||||
name = KeyCode._from_symbol(msg[1:])
|
|
||||||
if str(name) == "<0>":
|
|
||||||
continue
|
|
||||||
try:
|
|
||||||
if msg[0] == "p":
|
|
||||||
keyboard.press(name)
|
|
||||||
else:
|
|
||||||
keyboard.release(name)
|
|
||||||
except Exception as e:
|
|
||||||
print('[x] error key',e)
|
|
||||||
|
|
||||||
|
|
||||||
loop()
|
|
||||||
clientsocket.close()
|
|
||||||
server.close()
|
|
||||||
@ -25,7 +25,6 @@ install $HBB/libsciter-gtk.so %{buildroot}/usr/lib/rustdesk/libsciter-gtk.so
|
|||||||
install $HBB/rustdesk.service %{buildroot}/usr/share/rustdesk/files/
|
install $HBB/rustdesk.service %{buildroot}/usr/share/rustdesk/files/
|
||||||
install $HBB/128x128@2x.png %{buildroot}/usr/share/rustdesk/files/rustdesk.png
|
install $HBB/128x128@2x.png %{buildroot}/usr/share/rustdesk/files/rustdesk.png
|
||||||
install $HBB/rustdesk.desktop %{buildroot}/usr/share/rustdesk/files/
|
install $HBB/rustdesk.desktop %{buildroot}/usr/share/rustdesk/files/
|
||||||
install $HBB/pynput_service.py %{buildroot}/usr/share/rustdesk/files/
|
|
||||||
|
|
||||||
%files
|
%files
|
||||||
/usr/bin/rustdesk
|
/usr/bin/rustdesk
|
||||||
@ -33,7 +32,6 @@ install $HBB/pynput_service.py %{buildroot}/usr/share/rustdesk/files/
|
|||||||
/usr/share/rustdesk/files/rustdesk.service
|
/usr/share/rustdesk/files/rustdesk.service
|
||||||
/usr/share/rustdesk/files/rustdesk.png
|
/usr/share/rustdesk/files/rustdesk.png
|
||||||
/usr/share/rustdesk/files/rustdesk.desktop
|
/usr/share/rustdesk/files/rustdesk.desktop
|
||||||
/usr/share/rustdesk/files/pynput_service.py
|
|
||||||
|
|
||||||
%changelog
|
%changelog
|
||||||
# let's skip this for now
|
# let's skip this for now
|
||||||
@ -54,7 +52,6 @@ esac
|
|||||||
%post
|
%post
|
||||||
cp /usr/share/rustdesk/files/rustdesk.service /etc/systemd/system/rustdesk.service
|
cp /usr/share/rustdesk/files/rustdesk.service /etc/systemd/system/rustdesk.service
|
||||||
cp /usr/share/rustdesk/files/rustdesk.desktop /usr/share/applications/
|
cp /usr/share/rustdesk/files/rustdesk.desktop /usr/share/applications/
|
||||||
sudo -H pip3 install pynput
|
|
||||||
systemctl daemon-reload
|
systemctl daemon-reload
|
||||||
systemctl enable rustdesk
|
systemctl enable rustdesk
|
||||||
systemctl start rustdesk
|
systemctl start rustdesk
|
||||||
|
|||||||
3
rpm.spec
3
rpm.spec
@ -25,7 +25,6 @@ install $HBB/libsciter-gtk.so %{buildroot}/usr/lib/rustdesk/libsciter-gtk.so
|
|||||||
install $HBB/rustdesk.service %{buildroot}/usr/share/rustdesk/files/
|
install $HBB/rustdesk.service %{buildroot}/usr/share/rustdesk/files/
|
||||||
install $HBB/128x128@2x.png %{buildroot}/usr/share/rustdesk/files/rustdesk.png
|
install $HBB/128x128@2x.png %{buildroot}/usr/share/rustdesk/files/rustdesk.png
|
||||||
install $HBB/rustdesk.desktop %{buildroot}/usr/share/rustdesk/files/
|
install $HBB/rustdesk.desktop %{buildroot}/usr/share/rustdesk/files/
|
||||||
install $HBB/pynput_service.py %{buildroot}/usr/share/rustdesk/files/
|
|
||||||
|
|
||||||
%files
|
%files
|
||||||
/usr/bin/rustdesk
|
/usr/bin/rustdesk
|
||||||
@ -33,7 +32,6 @@ install $HBB/pynput_service.py %{buildroot}/usr/share/rustdesk/files/
|
|||||||
/usr/share/rustdesk/files/rustdesk.service
|
/usr/share/rustdesk/files/rustdesk.service
|
||||||
/usr/share/rustdesk/files/rustdesk.png
|
/usr/share/rustdesk/files/rustdesk.png
|
||||||
/usr/share/rustdesk/files/rustdesk.desktop
|
/usr/share/rustdesk/files/rustdesk.desktop
|
||||||
/usr/share/rustdesk/files/pynput_service.py
|
|
||||||
/usr/share/rustdesk/files/__pycache__/*
|
/usr/share/rustdesk/files/__pycache__/*
|
||||||
|
|
||||||
%changelog
|
%changelog
|
||||||
@ -55,7 +53,6 @@ esac
|
|||||||
%post
|
%post
|
||||||
cp /usr/share/rustdesk/files/rustdesk.service /etc/systemd/system/rustdesk.service
|
cp /usr/share/rustdesk/files/rustdesk.service /etc/systemd/system/rustdesk.service
|
||||||
cp /usr/share/rustdesk/files/rustdesk.desktop /usr/share/applications/
|
cp /usr/share/rustdesk/files/rustdesk.desktop /usr/share/applications/
|
||||||
sudo -H pip3 install pynput
|
|
||||||
systemctl daemon-reload
|
systemctl daemon-reload
|
||||||
systemctl enable rustdesk
|
systemctl enable rustdesk
|
||||||
systemctl start rustdesk
|
systemctl start rustdesk
|
||||||
|
|||||||
@ -1,2 +1,2 @@
|
|||||||
[toolchain]
|
[toolchain]
|
||||||
channel = "1.62.0"
|
channel = "1.62.0"
|
||||||
@ -108,13 +108,7 @@ parts:
|
|||||||
plugin: nil
|
plugin: nil
|
||||||
override-pull: |
|
override-pull: |
|
||||||
mkdir -p ${SNAPCRAFT_PART_INSTALL}/usr/share/rustdesk/files/systemd/
|
mkdir -p ${SNAPCRAFT_PART_INSTALL}/usr/share/rustdesk/files/systemd/
|
||||||
cp ${SNAPCRAFT_PART_SRC}/../../rustdesk/src/pynput_service.py ${SNAPCRAFT_PART_INSTALL}/usr/share/rustdesk/files/
|
|
||||||
cp ${SNAPCRAFT_PART_SRC}/../../rustdesk/src/rustdesk.service ${SNAPCRAFT_PART_INSTALL}/usr/share/rustdesk/files/systemd/
|
cp ${SNAPCRAFT_PART_SRC}/../../rustdesk/src/rustdesk.service ${SNAPCRAFT_PART_INSTALL}/usr/share/rustdesk/files/systemd/
|
||||||
|
|
||||||
python3-deps:
|
|
||||||
plugin: python
|
|
||||||
python-packages:
|
|
||||||
- pynput == 1.7.6
|
|
||||||
|
|
||||||
layout:
|
layout:
|
||||||
/usr/share/rustdesk:
|
/usr/share/rustdesk:
|
||||||
|
|||||||
@ -154,7 +154,8 @@ impl Client {
|
|||||||
return Err(err);
|
return Err(err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(x) => Ok(x),
|
Ok(x) => {
|
||||||
|
Ok(x)},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1707,7 +1708,7 @@ pub enum Data {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Keycode for key events.
|
/// Keycode for key events.
|
||||||
#[derive(Clone)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum Key {
|
pub enum Key {
|
||||||
ControlKey(ControlKey),
|
ControlKey(ControlKey),
|
||||||
Chr(u32),
|
Chr(u32),
|
||||||
|
|||||||
@ -666,3 +666,14 @@ pub fn make_privacy_mode_msg(state: back_notification::PrivacyModeState) -> Mess
|
|||||||
msg_out.set_misc(misc);
|
msg_out.set_misc(misc);
|
||||||
msg_out
|
msg_out
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(target_os = "linux"))]
|
||||||
|
lazy_static::lazy_static! {
|
||||||
|
pub static ref IS_X11: Mutex<bool> = Mutex::new(false);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
lazy_static::lazy_static! {
|
||||||
|
pub static ref IS_X11: Mutex<bool> = Mutex::new("x11" == hbb_common::platform::linux::get_display_server());
|
||||||
|
}
|
||||||
@ -475,4 +475,4 @@ pub fn make_fd_flutter(id: i32, entries: &Vec<FileEntry>, only_count: bool) -> S
|
|||||||
}
|
}
|
||||||
m.insert("total_size".into(), json!(n as f64));
|
m.insert("total_size".into(), json!(n as f64));
|
||||||
serde_json::to_string(&m).unwrap_or("".into())
|
serde_json::to_string(&m).unwrap_or("".into())
|
||||||
}
|
}
|
||||||
@ -17,7 +17,7 @@ use crate::flutter::{self, SESSIONS};
|
|||||||
use crate::start_server;
|
use crate::start_server;
|
||||||
use crate::ui_interface;
|
use crate::ui_interface;
|
||||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||||
use crate::ui_interface::{change_id, check_connect_status, is_ok_change_id};
|
use crate::ui_interface::change_id;
|
||||||
use crate::ui_interface::{
|
use crate::ui_interface::{
|
||||||
check_mouse_time, check_super_user_permission, discover, forget_password, get_api_server,
|
check_mouse_time, check_super_user_permission, discover, forget_password, get_api_server,
|
||||||
get_app_name, get_async_job_status, get_connect_status, get_fav, get_id, get_lan_peers,
|
get_app_name, get_async_job_status, get_connect_status, get_fav, get_id, get_lan_peers,
|
||||||
@ -104,9 +104,9 @@ pub fn stop_global_event_stream(app_type: String) {
|
|||||||
.remove(&app_type);
|
.remove(&app_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn host_stop_system_key_propagate(stopped: bool) {
|
pub fn host_stop_system_key_propagate(_stopped: bool) {
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
crate::platform::windows::stop_system_key_propagate(stopped);
|
crate::platform::windows::stop_system_key_propagate(_stopped);
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: -> ResultType<()> cannot be parsed by frb_codegen
|
// FIXME: -> ResultType<()> cannot be parsed by frb_codegen
|
||||||
@ -233,6 +233,28 @@ pub fn session_switch_display(id: String, value: i32) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn session_handle_flutter_key_event(
|
||||||
|
id: String,
|
||||||
|
name: String,
|
||||||
|
keycode: i32,
|
||||||
|
scancode: i32,
|
||||||
|
down_or_up: bool,
|
||||||
|
) {
|
||||||
|
if let Some(session) = SESSIONS.read().unwrap().get(&id) {
|
||||||
|
session.handle_flutter_key_event(&name, keycode, scancode, down_or_up);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn session_enter_or_leave(id: String, enter: bool) {
|
||||||
|
if let Some(session) = SESSIONS.read().unwrap().get(&id) {
|
||||||
|
if enter {
|
||||||
|
session.enter();
|
||||||
|
} else {
|
||||||
|
session.leave();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn session_input_key(
|
pub fn session_input_key(
|
||||||
id: String,
|
id: String,
|
||||||
name: String,
|
name: String,
|
||||||
@ -274,6 +296,19 @@ pub fn session_get_peer_option(id: String, name: String) -> String {
|
|||||||
"".to_string()
|
"".to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn session_get_keyboard_name(id: String) -> String {
|
||||||
|
if let Some(session) = SESSIONS.read().unwrap().get(&id) {
|
||||||
|
return session.get_keyboard_mode();
|
||||||
|
}
|
||||||
|
"legacy".to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn session_set_keyboard_mode(id: String, keyboard_mode: String) {
|
||||||
|
if let Some(session) = SESSIONS.read().unwrap().get(&id) {
|
||||||
|
session.save_keyboard_mode(keyboard_mode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn session_input_os_password(id: String, value: String) {
|
pub fn session_input_os_password(id: String, value: String) {
|
||||||
if let Some(session) = SESSIONS.read().unwrap().get(&id) {
|
if let Some(session) = SESSIONS.read().unwrap().get(&id) {
|
||||||
session.input_os_password(value, true);
|
session.input_os_password(value, true);
|
||||||
|
|||||||
@ -290,6 +290,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Ignore Battery Optimizations", "忽略电池优化"),
|
("Ignore Battery Optimizations", "忽略电池优化"),
|
||||||
("android_open_battery_optimizations_tip", "如需关闭此功能,请在接下来的RustDesk应用设置页面中,找到并进入 [电源] 页面,取消勾选 [不受限制]"),
|
("android_open_battery_optimizations_tip", "如需关闭此功能,请在接下来的RustDesk应用设置页面中,找到并进入 [电源] 页面,取消勾选 [不受限制]"),
|
||||||
("Connection not allowed", "对方不允许连接"),
|
("Connection not allowed", "对方不允许连接"),
|
||||||
|
("Legacy mode", "传统模式"),
|
||||||
|
("Map mode", "1:1传输"),
|
||||||
|
("Translate mode", "翻译模式"),
|
||||||
("Use temporary password", "使用临时密码"),
|
("Use temporary password", "使用临时密码"),
|
||||||
("Use permanent password", "使用固定密码"),
|
("Use permanent password", "使用固定密码"),
|
||||||
("Use both passwords", "同时使用两种密码"),
|
("Use both passwords", "同时使用两种密码"),
|
||||||
|
|||||||
@ -290,6 +290,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Ignore Battery Optimizations", ""),
|
("Ignore Battery Optimizations", ""),
|
||||||
("android_open_battery_optimizations_tip", ""),
|
("android_open_battery_optimizations_tip", ""),
|
||||||
("Connection not allowed", ""),
|
("Connection not allowed", ""),
|
||||||
|
("Legacy mode", ""),
|
||||||
|
("Map mode", ""),
|
||||||
|
("Translate mode", ""),
|
||||||
("Use temporary password", ""),
|
("Use temporary password", ""),
|
||||||
("Use permanent password", ""),
|
("Use permanent password", ""),
|
||||||
("Use both passwords", ""),
|
("Use both passwords", ""),
|
||||||
|
|||||||
@ -290,6 +290,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Ignore Battery Optimizations", ""),
|
("Ignore Battery Optimizations", ""),
|
||||||
("android_open_battery_optimizations_tip", ""),
|
("android_open_battery_optimizations_tip", ""),
|
||||||
("Connection not allowed", ""),
|
("Connection not allowed", ""),
|
||||||
|
("Legacy mode", ""),
|
||||||
|
("Map mode", ""),
|
||||||
|
("Translate mode", ""),
|
||||||
("Use temporary password", ""),
|
("Use temporary password", ""),
|
||||||
("Use permanent password", ""),
|
("Use permanent password", ""),
|
||||||
("Use both passwords", ""),
|
("Use both passwords", ""),
|
||||||
|
|||||||
@ -290,6 +290,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Ignore Battery Optimizations", "Batterieoptimierung ignorieren"),
|
("Ignore Battery Optimizations", "Batterieoptimierung ignorieren"),
|
||||||
("android_open_battery_optimizations_tip", "Möchten Sie die Batterieopimierungs-Einstellungen öffnen?"),
|
("android_open_battery_optimizations_tip", "Möchten Sie die Batterieopimierungs-Einstellungen öffnen?"),
|
||||||
("Connection not allowed", "Verbindung abgelehnt"),
|
("Connection not allowed", "Verbindung abgelehnt"),
|
||||||
|
("Legacy mode", ""),
|
||||||
|
("Map mode", ""),
|
||||||
|
("Translate mode", ""),
|
||||||
("Use temporary password", "Temporäres Passwort verwenden"),
|
("Use temporary password", "Temporäres Passwort verwenden"),
|
||||||
("Use permanent password", "Dauerhaftes Passwort verwenden"),
|
("Use permanent password", "Dauerhaftes Passwort verwenden"),
|
||||||
("Use both passwords", "Beide Passwörter verwenden"),
|
("Use both passwords", "Beide Passwörter verwenden"),
|
||||||
|
|||||||
@ -290,6 +290,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Ignore Battery Optimizations", ""),
|
("Ignore Battery Optimizations", ""),
|
||||||
("android_open_battery_optimizations_tip", ""),
|
("android_open_battery_optimizations_tip", ""),
|
||||||
("Connection not allowed", ""),
|
("Connection not allowed", ""),
|
||||||
|
("Legacy mode", ""),
|
||||||
|
("Map mode", ""),
|
||||||
|
("Translate mode", ""),
|
||||||
("Use temporary password", ""),
|
("Use temporary password", ""),
|
||||||
("Use permanent password", ""),
|
("Use permanent password", ""),
|
||||||
("Use both passwords", ""),
|
("Use both passwords", ""),
|
||||||
|
|||||||
@ -289,6 +289,22 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Keep RustDesk background service", "Dejar RustDesk como Servicio en 2do plano"),
|
("Keep RustDesk background service", "Dejar RustDesk como Servicio en 2do plano"),
|
||||||
("Ignore Battery Optimizations", "Ignorar optimizacioens de bateria"),
|
("Ignore Battery Optimizations", "Ignorar optimizacioens de bateria"),
|
||||||
("android_open_battery_optimizations_tip", ""),
|
("android_open_battery_optimizations_tip", ""),
|
||||||
|
("Random Password After Session", ""),
|
||||||
|
("Keep", ""),
|
||||||
|
("Update", ""),
|
||||||
|
("Disable", ""),
|
||||||
|
("Onetime Password", ""),
|
||||||
|
("Verification Method", ""),
|
||||||
|
("Enable security password", ""),
|
||||||
|
("Enable random password", ""),
|
||||||
|
("Enable onetime password", ""),
|
||||||
|
("Disable onetime password", ""),
|
||||||
|
("Activate onetime password", ""),
|
||||||
|
("Set security password", ""),
|
||||||
|
("Connection not allowed", ""),
|
||||||
|
("Legacy mode", ""),
|
||||||
|
("Map mode", ""),
|
||||||
|
("Translate mode", ""),
|
||||||
("Connection not allowed", "Conexión no disponible"),
|
("Connection not allowed", "Conexión no disponible"),
|
||||||
("Use temporary password", "Usar contraseña temporal"),
|
("Use temporary password", "Usar contraseña temporal"),
|
||||||
("Use permanent password", "Usar contraseña permamente"),
|
("Use permanent password", "Usar contraseña permamente"),
|
||||||
|
|||||||
@ -290,6 +290,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Ignore Battery Optimizations", ""),
|
("Ignore Battery Optimizations", ""),
|
||||||
("android_open_battery_optimizations_tip", ""),
|
("android_open_battery_optimizations_tip", ""),
|
||||||
("Connection not allowed", ""),
|
("Connection not allowed", ""),
|
||||||
|
("Legacy mode", ""),
|
||||||
|
("Map mode", ""),
|
||||||
|
("Translate mode", ""),
|
||||||
("Use temporary password", ""),
|
("Use temporary password", ""),
|
||||||
("Use permanent password", ""),
|
("Use permanent password", ""),
|
||||||
("Use both passwords", ""),
|
("Use both passwords", ""),
|
||||||
|
|||||||
@ -290,6 +290,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Ignore Battery Optimizations", ""),
|
("Ignore Battery Optimizations", ""),
|
||||||
("android_open_battery_optimizations_tip", ""),
|
("android_open_battery_optimizations_tip", ""),
|
||||||
("Connection not allowed", ""),
|
("Connection not allowed", ""),
|
||||||
|
("Legacy mode", ""),
|
||||||
|
("Map mode", ""),
|
||||||
|
("Translate mode", ""),
|
||||||
("Use temporary password", ""),
|
("Use temporary password", ""),
|
||||||
("Use permanent password", ""),
|
("Use permanent password", ""),
|
||||||
("Use both passwords", ""),
|
("Use both passwords", ""),
|
||||||
|
|||||||
@ -289,6 +289,22 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Keep RustDesk background service", "Pertahankan RustDesk berjalan pada background service"),
|
("Keep RustDesk background service", "Pertahankan RustDesk berjalan pada background service"),
|
||||||
("Ignore Battery Optimizations", "Abaikan Pengoptimalan Baterai"),
|
("Ignore Battery Optimizations", "Abaikan Pengoptimalan Baterai"),
|
||||||
("android_open_battery_optimizations_tip", ""),
|
("android_open_battery_optimizations_tip", ""),
|
||||||
|
("Random Password After Session", ""),
|
||||||
|
("Keep", ""),
|
||||||
|
("Update", ""),
|
||||||
|
("Disable", ""),
|
||||||
|
("Onetime Password", ""),
|
||||||
|
("Verification Method", ""),
|
||||||
|
("Enable security password", ""),
|
||||||
|
("Enable random password", ""),
|
||||||
|
("Enable onetime password", ""),
|
||||||
|
("Disable onetime password", ""),
|
||||||
|
("Activate onetime password", ""),
|
||||||
|
("Set security password", ""),
|
||||||
|
("Connection not allowed", ""),
|
||||||
|
("Legacy mode", ""),
|
||||||
|
("Map mode", ""),
|
||||||
|
("Translate mode", ""),
|
||||||
("Connection not allowed", "Koneksi tidak dijinkan"),
|
("Connection not allowed", "Koneksi tidak dijinkan"),
|
||||||
("Use temporary password", "Gunakan kata sandi sementara"),
|
("Use temporary password", "Gunakan kata sandi sementara"),
|
||||||
("Use permanent password", "Gunakan kata sandi permanaen"),
|
("Use permanent password", "Gunakan kata sandi permanaen"),
|
||||||
|
|||||||
@ -318,5 +318,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Insecure Connection", "Connessione insicura"),
|
("Insecure Connection", "Connessione insicura"),
|
||||||
("Scale original", "Scala originale"),
|
("Scale original", "Scala originale"),
|
||||||
("Scale adaptive", "Scala adattiva"),
|
("Scale adaptive", "Scala adattiva"),
|
||||||
|
("Legacy mode", ""),
|
||||||
|
("Map mode", ""),
|
||||||
|
("Translate mode", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -288,6 +288,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Ignore Battery Optimizations", "バッテリーの最適化を無効にする"),
|
("Ignore Battery Optimizations", "バッテリーの最適化を無効にする"),
|
||||||
("android_open_battery_optimizations_tip", "この機能を使わない場合は、次のRestDeskアプリ設定ページから「バッテリー」に進み、「制限なし」の選択を外してください"),
|
("android_open_battery_optimizations_tip", "この機能を使わない場合は、次のRestDeskアプリ設定ページから「バッテリー」に進み、「制限なし」の選択を外してください"),
|
||||||
("Connection not allowed", "接続が許可されていません"),
|
("Connection not allowed", "接続が許可されていません"),
|
||||||
|
("Legacy mode", ""),
|
||||||
|
("Map mode", ""),
|
||||||
|
("Translate mode", ""),
|
||||||
("Use temporary password", "使い捨てのパスワードを使用"),
|
("Use temporary password", "使い捨てのパスワードを使用"),
|
||||||
("Use permanent password", "固定のパスワードを使用"),
|
("Use permanent password", "固定のパスワードを使用"),
|
||||||
("Use both passwords", "どちらのパスワードも使用"),
|
("Use both passwords", "どちらのパスワードも使用"),
|
||||||
|
|||||||
@ -290,6 +290,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Ignore Battery Optimizations", ""),
|
("Ignore Battery Optimizations", ""),
|
||||||
("android_open_battery_optimizations_tip", ""),
|
("android_open_battery_optimizations_tip", ""),
|
||||||
("Connection not allowed", ""),
|
("Connection not allowed", ""),
|
||||||
|
("Legacy mode", ""),
|
||||||
|
("Map mode", ""),
|
||||||
|
("Translate mode", ""),
|
||||||
("Use temporary password", ""),
|
("Use temporary password", ""),
|
||||||
("Use permanent password", ""),
|
("Use permanent password", ""),
|
||||||
("Use both passwords", ""),
|
("Use both passwords", ""),
|
||||||
|
|||||||
@ -290,6 +290,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Ignore Battery Optimizations", "Игнорировать оптимизацию батареи"),
|
("Ignore Battery Optimizations", "Игнорировать оптимизацию батареи"),
|
||||||
("android_open_battery_optimizations_tip", "Перейдите на следующую страницу настроек "),
|
("android_open_battery_optimizations_tip", "Перейдите на следующую страницу настроек "),
|
||||||
("Connection not allowed", "Подключение не разрешено"),
|
("Connection not allowed", "Подключение не разрешено"),
|
||||||
|
("Legacy mode", ""),
|
||||||
|
("Map mode", ""),
|
||||||
|
("Translate mode", ""),
|
||||||
("Use temporary password", "Использовать временный пароль"),
|
("Use temporary password", "Использовать временный пароль"),
|
||||||
("Use permanent password", "Использовать постоянный пароль"),
|
("Use permanent password", "Использовать постоянный пароль"),
|
||||||
("Use both passwords", "Использовать оба пароля"),
|
("Use both passwords", "Использовать оба пароля"),
|
||||||
|
|||||||
@ -290,6 +290,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Ignore Battery Optimizations", ""),
|
("Ignore Battery Optimizations", ""),
|
||||||
("android_open_battery_optimizations_tip", ""),
|
("android_open_battery_optimizations_tip", ""),
|
||||||
("Connection not allowed", ""),
|
("Connection not allowed", ""),
|
||||||
|
("Legacy mode", ""),
|
||||||
|
("Map mode", ""),
|
||||||
|
("Translate mode", ""),
|
||||||
("Use temporary password", ""),
|
("Use temporary password", ""),
|
||||||
("Use permanent password", ""),
|
("Use permanent password", ""),
|
||||||
("Use both passwords", ""),
|
("Use both passwords", ""),
|
||||||
|
|||||||
@ -290,6 +290,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Ignore Battery Optimizations", ""),
|
("Ignore Battery Optimizations", ""),
|
||||||
("android_open_battery_optimizations_tip", ""),
|
("android_open_battery_optimizations_tip", ""),
|
||||||
("Connection not allowed", ""),
|
("Connection not allowed", ""),
|
||||||
|
("Legacy mode", ""),
|
||||||
|
("Map mode", ""),
|
||||||
|
("Translate mode", ""),
|
||||||
("Use temporary password", ""),
|
("Use temporary password", ""),
|
||||||
("Use permanent password", ""),
|
("Use permanent password", ""),
|
||||||
("Use both passwords", ""),
|
("Use both passwords", ""),
|
||||||
|
|||||||
@ -289,6 +289,22 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Keep RustDesk background service", ""),
|
("Keep RustDesk background service", ""),
|
||||||
("Ignore Battery Optimizations", ""),
|
("Ignore Battery Optimizations", ""),
|
||||||
("android_open_battery_optimizations_tip", ""),
|
("android_open_battery_optimizations_tip", ""),
|
||||||
|
("Random Password After Session", ""),
|
||||||
|
("Keep", ""),
|
||||||
|
("Update", ""),
|
||||||
|
("Disable", ""),
|
||||||
|
("Onetime Password", ""),
|
||||||
|
("Verification Method", ""),
|
||||||
|
("Enable security password", ""),
|
||||||
|
("Enable random password", ""),
|
||||||
|
("Enable onetime password", ""),
|
||||||
|
("Disable onetime password", ""),
|
||||||
|
("Activate onetime password", ""),
|
||||||
|
("Set security password", ""),
|
||||||
|
("Connection not allowed", ""),
|
||||||
|
("Legacy mode", ""),
|
||||||
|
("Map mode", ""),
|
||||||
|
("Translate mode", ""),
|
||||||
("Connection not allowed", "bağlantıya izin verilmedi"),
|
("Connection not allowed", "bağlantıya izin verilmedi"),
|
||||||
("Use temporary password", "Geçici şifre kullan"),
|
("Use temporary password", "Geçici şifre kullan"),
|
||||||
("Use permanent password", "Kalıcı şifre kullan"),
|
("Use permanent password", "Kalıcı şifre kullan"),
|
||||||
|
|||||||
@ -290,6 +290,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Ignore Battery Optimizations", "忽略電池優化"),
|
("Ignore Battery Optimizations", "忽略電池優化"),
|
||||||
("android_open_battery_optimizations_tip", "如需關閉此功能,請在接下來的RustDesk應用設置頁面中,找到並進入 [電源] 頁面,取消勾選 [不受限制]"),
|
("android_open_battery_optimizations_tip", "如需關閉此功能,請在接下來的RustDesk應用設置頁面中,找到並進入 [電源] 頁面,取消勾選 [不受限制]"),
|
||||||
("Connection not allowed", "對方不允許連接"),
|
("Connection not allowed", "對方不允許連接"),
|
||||||
|
("Legacy mode", "傳統模式"),
|
||||||
|
("Map mode", "1:1傳輸"),
|
||||||
|
("Translate mode", "翻譯模式"),
|
||||||
("Use temporary password", "使用臨時密碼"),
|
("Use temporary password", "使用臨時密碼"),
|
||||||
("Use permanent password", "使用固定密碼"),
|
("Use permanent password", "使用固定密碼"),
|
||||||
("Use both passwords", "同時使用兩種密碼"),
|
("Use both passwords", "同時使用兩種密碼"),
|
||||||
|
|||||||
@ -290,6 +290,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Ignore Battery Optimizations", "Bỏ qua các tối ưu pin"),
|
("Ignore Battery Optimizations", "Bỏ qua các tối ưu pin"),
|
||||||
("android_open_battery_optimizations_tip", "Nếu bạn muốn tắt tính năng này, vui lòng chuyển đến trang cài đặt ứng dụng RustDesk tiếp theo, tìm và nhập [Pin], Bỏ chọn [Không hạn chế]"),
|
("android_open_battery_optimizations_tip", "Nếu bạn muốn tắt tính năng này, vui lòng chuyển đến trang cài đặt ứng dụng RustDesk tiếp theo, tìm và nhập [Pin], Bỏ chọn [Không hạn chế]"),
|
||||||
("Connection not allowed", "Kết nối không đuợc phép"),
|
("Connection not allowed", "Kết nối không đuợc phép"),
|
||||||
|
("Legacy mode", ""),
|
||||||
|
("Map mode", ""),
|
||||||
|
("Translate mode", ""),
|
||||||
("Use temporary password", "Sử dụng mật khẩu tạm thời"),
|
("Use temporary password", "Sử dụng mật khẩu tạm thời"),
|
||||||
("Use permanent password", "Sử dụng mật khẩu vĩnh viễn"),
|
("Use permanent password", "Sử dụng mật khẩu vĩnh viễn"),
|
||||||
("Use both passwords", "Sử dụng cả hai mật khẩu"),
|
("Use both passwords", "Sử dụng cả hai mật khẩu"),
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
use std::collections::HashMap;
|
|
||||||
use std::{
|
use std::{
|
||||||
net::SocketAddr,
|
net::SocketAddr,
|
||||||
sync::{
|
sync::{
|
||||||
@ -10,12 +9,10 @@ use std::{
|
|||||||
|
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use hbb_common::config::DiscoveryPeer;
|
|
||||||
use hbb_common::tcp::FramedStream;
|
use hbb_common::tcp::FramedStream;
|
||||||
use hbb_common::{
|
use hbb_common::{
|
||||||
allow_err,
|
allow_err,
|
||||||
anyhow::bail,
|
anyhow::bail,
|
||||||
config,
|
|
||||||
config::{Config, REG_INTERVAL, RENDEZVOUS_PORT, RENDEZVOUS_TIMEOUT},
|
config::{Config, REG_INTERVAL, RENDEZVOUS_PORT, RENDEZVOUS_TIMEOUT},
|
||||||
futures::future::join_all,
|
futures::future::join_all,
|
||||||
log,
|
log,
|
||||||
@ -571,6 +568,7 @@ pub fn get_mac() -> String {
|
|||||||
"".to_owned()
|
"".to_owned()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
fn lan_discovery() -> ResultType<()> {
|
fn lan_discovery() -> ResultType<()> {
|
||||||
let addr = SocketAddr::from(([0, 0, 0, 0], get_broadcast_port()));
|
let addr = SocketAddr::from(([0, 0, 0, 0], get_broadcast_port()));
|
||||||
let socket = std::net::UdpSocket::bind(addr)?;
|
let socket = std::net::UdpSocket::bind(addr)?;
|
||||||
@ -640,7 +638,7 @@ pub async fn query_online_states<F: FnOnce(Vec<String>, Vec<String>)>(ids: Vec<S
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn create_online_stream() -> ResultType<FramedStream> {
|
async fn create_online_stream() -> ResultType<FramedStream> {
|
||||||
let (mut rendezvous_server, servers, contained) = crate::get_rendezvous_server(1_000).await;
|
let (rendezvous_server, _servers, _contained) = crate::get_rendezvous_server(1_000).await;
|
||||||
let tmp: Vec<&str> = rendezvous_server.split(":").collect();
|
let tmp: Vec<&str> = rendezvous_server.split(":").collect();
|
||||||
if tmp.len() != 2 {
|
if tmp.len() != 2 {
|
||||||
bail!("Invalid server address: {}", rendezvous_server);
|
bail!("Invalid server address: {}", rendezvous_server);
|
||||||
|
|||||||
@ -448,11 +448,12 @@ impl Connection {
|
|||||||
handle_mouse(&msg, id);
|
handle_mouse(&msg, id);
|
||||||
}
|
}
|
||||||
MessageInput::Key((mut msg, press)) => {
|
MessageInput::Key((mut msg, press)) => {
|
||||||
if press {
|
// todo: press and down have similar meanings.
|
||||||
|
if press && msg.mode.unwrap() == KeyboardMode::Legacy {
|
||||||
msg.down = true;
|
msg.down = true;
|
||||||
}
|
}
|
||||||
handle_key(&msg);
|
handle_key(&msg);
|
||||||
if press {
|
if press && msg.mode.unwrap() == KeyboardMode::Legacy {
|
||||||
msg.down = false;
|
msg.down = false;
|
||||||
handle_key(&msg);
|
handle_key(&msg);
|
||||||
}
|
}
|
||||||
@ -632,7 +633,7 @@ impl Connection {
|
|||||||
let mut pi = PeerInfo {
|
let mut pi = PeerInfo {
|
||||||
username: username.clone(),
|
username: username.clone(),
|
||||||
conn_id: self.inner.id,
|
conn_id: self.inner.id,
|
||||||
version: crate::VERSION.to_owned(),
|
version: VERSION.to_owned(),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -1,8 +1,10 @@
|
|||||||
use super::*;
|
use super::*;
|
||||||
|
use crate::common::IS_X11;
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
use dispatch::Queue;
|
use dispatch::Queue;
|
||||||
use enigo::{Enigo, Key, KeyboardControllable, MouseButton, MouseControllable};
|
use enigo::{Enigo, Key, KeyboardControllable, MouseButton, MouseControllable};
|
||||||
use hbb_common::{config::COMPRESS_LEVEL, protobuf::EnumOrUnknown};
|
use hbb_common::{config::COMPRESS_LEVEL, protobuf::EnumOrUnknown};
|
||||||
|
use rdev::{simulate, EventType, Key as RdevKey};
|
||||||
use std::{
|
use std::{
|
||||||
convert::TryFrom,
|
convert::TryFrom,
|
||||||
sync::atomic::{AtomicBool, Ordering},
|
sync::atomic::{AtomicBool, Ordering},
|
||||||
@ -21,8 +23,6 @@ impl super::service::Reset for StateCursor {
|
|||||||
*self = Default::default();
|
*self = Default::default();
|
||||||
crate::platform::reset_input_cache();
|
crate::platform::reset_input_cache();
|
||||||
fix_key_down_timeout(true);
|
fix_key_down_timeout(true);
|
||||||
#[cfg(target_os = "linux")]
|
|
||||||
ENIGO.lock().unwrap().reset();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -145,7 +145,8 @@ fn run_cursor(sp: MouseCursorService, state: &mut StateCursor) -> ResultType<()>
|
|||||||
msg = cached.clone();
|
msg = cached.clone();
|
||||||
} else {
|
} else {
|
||||||
let mut data = crate::get_cursor_data(hcursor)?;
|
let mut data = crate::get_cursor_data(hcursor)?;
|
||||||
data.colors = hbb_common::compress::compress(&data.colors[..], COMPRESS_LEVEL).into();
|
data.colors =
|
||||||
|
hbb_common::compress::compress(&data.colors[..], COMPRESS_LEVEL).into();
|
||||||
let mut tmp = Message::new();
|
let mut tmp = Message::new();
|
||||||
tmp.set_cursor_data(data);
|
tmp.set_cursor_data(data);
|
||||||
msg = Arc::new(tmp);
|
msg = Arc::new(tmp);
|
||||||
@ -166,13 +167,6 @@ fn run_cursor(sp: MouseCursorService, state: &mut StateCursor) -> ResultType<()>
|
|||||||
|
|
||||||
lazy_static::lazy_static! {
|
lazy_static::lazy_static! {
|
||||||
static ref ENIGO: Arc<Mutex<Enigo>> = {
|
static ref ENIGO: Arc<Mutex<Enigo>> = {
|
||||||
#[cfg(target_os = "linux")]
|
|
||||||
{
|
|
||||||
if crate::platform::is_root() {
|
|
||||||
std::env::set_var("PYNPUT_USERNAME", crate::platform::linux::get_active_username());
|
|
||||||
std::env::set_var("PYNPUT_USERID", crate::platform::linux::get_active_userid());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Arc::new(Mutex::new(Enigo::new()))
|
Arc::new(Mutex::new(Enigo::new()))
|
||||||
};
|
};
|
||||||
static ref KEYS_DOWN: Arc<Mutex<HashMap<u64, Instant>>> = Default::default();
|
static ref KEYS_DOWN: Arc<Mutex<HashMap<u64, Instant>>> = Default::default();
|
||||||
@ -466,10 +460,14 @@ pub async fn lock_screen() {
|
|||||||
// loginctl lock-session also not work, they both work run rustdesk from cmd
|
// loginctl lock-session also not work, they both work run rustdesk from cmd
|
||||||
std::thread::spawn(|| {
|
std::thread::spawn(|| {
|
||||||
let mut key_event = KeyEvent::new();
|
let mut key_event = KeyEvent::new();
|
||||||
key_event.down = true;
|
|
||||||
key_event.set_chr('l' as _);
|
key_event.set_chr('l' as _);
|
||||||
key_event.modifiers.push(ControlKey::Meta.into());
|
key_event.modifiers.push(ControlKey::Meta.into());
|
||||||
|
key_event.mode = KeyboardMode::Legacy.into();
|
||||||
|
|
||||||
|
key_event.down = true;
|
||||||
handle_key(&key_event);
|
handle_key(&key_event);
|
||||||
|
|
||||||
key_event.down = false;
|
key_event.down = false;
|
||||||
handle_key(&key_event);
|
handle_key(&key_event);
|
||||||
});
|
});
|
||||||
@ -477,10 +475,13 @@ pub async fn lock_screen() {
|
|||||||
// CGSession -suspend not real lock screen, it is user switch
|
// CGSession -suspend not real lock screen, it is user switch
|
||||||
std::thread::spawn(|| {
|
std::thread::spawn(|| {
|
||||||
let mut key_event = KeyEvent::new();
|
let mut key_event = KeyEvent::new();
|
||||||
key_event.down = true;
|
|
||||||
key_event.set_chr('q' as _);
|
key_event.set_chr('q' as _);
|
||||||
key_event.modifiers.push(ControlKey::Meta.into());
|
key_event.modifiers.push(ControlKey::Meta.into());
|
||||||
key_event.modifiers.push(ControlKey::Control.into());
|
key_event.modifiers.push(ControlKey::Control.into());
|
||||||
|
key_event.mode = KeyboardMode::Legacy.into();
|
||||||
|
|
||||||
|
key_event.down = true;
|
||||||
handle_key(&key_event);
|
handle_key(&key_event);
|
||||||
key_event.down = false;
|
key_event.down = false;
|
||||||
handle_key(&key_event);
|
handle_key(&key_event);
|
||||||
@ -597,25 +598,125 @@ pub fn handle_key(evt: &KeyEvent) {
|
|||||||
handle_key_(evt);
|
handle_key_(evt);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_key_(evt: &KeyEvent) {
|
fn rdev_key_down_or_up(key: RdevKey, down_or_up: bool) {
|
||||||
if EXITING.load(Ordering::SeqCst) {
|
let event_type = match down_or_up {
|
||||||
|
true => EventType::KeyPress(key),
|
||||||
|
false => EventType::KeyRelease(key),
|
||||||
|
};
|
||||||
|
let delay = std::time::Duration::from_millis(20);
|
||||||
|
match simulate(&event_type) {
|
||||||
|
Ok(()) => (),
|
||||||
|
Err(_simulate_error) => {
|
||||||
|
log::error!("Could not send {:?}", &event_type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Let ths OS catchup (at least MacOS)
|
||||||
|
std::thread::sleep(delay);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn rdev_key_click(key: RdevKey) {
|
||||||
|
rdev_key_down_or_up(key, true);
|
||||||
|
rdev_key_down_or_up(key, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sync_status(evt: &KeyEvent) -> (bool, bool) {
|
||||||
|
let mut en = ENIGO.lock().unwrap();
|
||||||
|
|
||||||
|
// remote caps status
|
||||||
|
let caps_locking = evt
|
||||||
|
.modifiers
|
||||||
|
.iter()
|
||||||
|
.position(|&r| r == ControlKey::CapsLock.into())
|
||||||
|
.is_some();
|
||||||
|
// remote numpad status
|
||||||
|
let num_locking = evt
|
||||||
|
.modifiers
|
||||||
|
.iter()
|
||||||
|
.position(|&r| r == ControlKey::NumLock.into())
|
||||||
|
.is_some();
|
||||||
|
|
||||||
|
let click_capslock = (caps_locking && !en.get_key_state(enigo::Key::CapsLock))
|
||||||
|
|| (!caps_locking && en.get_key_state(enigo::Key::CapsLock));
|
||||||
|
let click_numlock = (num_locking && !en.get_key_state(enigo::Key::NumLock))
|
||||||
|
|| (!num_locking && en.get_key_state(enigo::Key::NumLock));
|
||||||
|
return (click_capslock, click_numlock);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn map_keyboard_mode(evt: &KeyEvent) {
|
||||||
|
// map mode(1): Send keycode according to the peer platform.
|
||||||
|
let (click_capslock, click_numlock) = sync_status(evt);
|
||||||
|
|
||||||
|
// Wayland
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
if !*IS_X11.lock().unwrap() {
|
||||||
|
let mut en = ENIGO.lock().unwrap();
|
||||||
|
let code = evt.chr() as u16;
|
||||||
|
|
||||||
|
#[cfg(not(target_os = "macos"))]
|
||||||
|
if click_capslock {
|
||||||
|
en.key_click(enigo::Key::CapsLock);
|
||||||
|
}
|
||||||
|
#[cfg(not(target_os = "macos"))]
|
||||||
|
if click_numlock {
|
||||||
|
en.key_click(enigo::Key::NumLock);
|
||||||
|
}
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
en.key_down(enigo::Key::CapsLock);
|
||||||
|
|
||||||
|
if evt.down {
|
||||||
|
en.key_down(enigo::Key::Raw(code)).ok();
|
||||||
|
} else {
|
||||||
|
en.key_up(enigo::Key::Raw(code));
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(target_os = "macos"))]
|
||||||
|
if click_capslock {
|
||||||
|
rdev_key_click(RdevKey::CapsLock);
|
||||||
|
}
|
||||||
|
#[cfg(not(target_os = "macos"))]
|
||||||
|
if click_numlock {
|
||||||
|
rdev_key_click(RdevKey::NumLock);
|
||||||
|
}
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
if evt.down && click_capslock {
|
||||||
|
rdev_key_down_or_up(RdevKey::CapsLock, evt.down);
|
||||||
|
}
|
||||||
|
|
||||||
|
rdev_key_down_or_up(RdevKey::Unknown(evt.chr()), evt.down);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn legacy_keyboard_mode(evt: &KeyEvent) {
|
||||||
|
let (click_capslock, click_numlock) = sync_status(evt);
|
||||||
|
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
crate::platform::windows::try_change_desktop();
|
crate::platform::windows::try_change_desktop();
|
||||||
let mut en = ENIGO.lock().unwrap();
|
let mut en = ENIGO.lock().unwrap();
|
||||||
|
if click_capslock {
|
||||||
|
en.key_click(Key::CapsLock);
|
||||||
|
}
|
||||||
|
if click_numlock {
|
||||||
|
en.key_click(Key::NumLock);
|
||||||
|
}
|
||||||
// disable numlock if press home etc when numlock is on,
|
// disable numlock if press home etc when numlock is on,
|
||||||
// because we will get numpad value (7,8,9 etc) if not
|
// because we will get numpad value (7,8,9 etc) if not
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
let mut disable_numlock = false;
|
let mut disable_numlock = false;
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
en.reset_flag();
|
en.reset_flag();
|
||||||
|
// When long-pressed the command key, then press and release
|
||||||
|
// the Tab key, there should be CGEventFlagCommand in the flag.
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
for ck in evt.modifiers.iter() {
|
||||||
|
if let Some(key) = KEY_MAP.get(&ck.value()) {
|
||||||
|
en.add_flag(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
#[cfg(not(target_os = "macos"))]
|
#[cfg(not(target_os = "macos"))]
|
||||||
let mut to_release = Vec::new();
|
let mut to_release = Vec::new();
|
||||||
#[cfg(not(target_os = "macos"))]
|
|
||||||
let mut has_cap = false;
|
|
||||||
#[cfg(windows)]
|
|
||||||
let mut has_numlock = false;
|
|
||||||
if evt.down {
|
if evt.down {
|
||||||
let ck = if let Some(key_event::Union::ControlKey(ck)) = evt.union {
|
let ck = if let Some(key_event::Union::ControlKey(ck)) = evt.union {
|
||||||
ck.value()
|
ck.value()
|
||||||
@ -637,40 +738,16 @@ fn handle_key_(evt: &KeyEvent) {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[cfg(target_os = "macos")]
|
|
||||||
en.add_flag(key);
|
|
||||||
#[cfg(not(target_os = "macos"))]
|
#[cfg(not(target_os = "macos"))]
|
||||||
{
|
if !get_modifier_state(key.clone(), &mut en) {
|
||||||
if key == &Key::CapsLock {
|
en.key_down(key.clone()).ok();
|
||||||
has_cap = true;
|
modifier_sleep();
|
||||||
} else if key == &Key::NumLock {
|
to_release.push(key);
|
||||||
#[cfg(windows)]
|
|
||||||
{
|
|
||||||
has_numlock = true;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if !get_modifier_state(key.clone(), &mut en) {
|
|
||||||
en.key_down(key.clone()).ok();
|
|
||||||
modifier_sleep();
|
|
||||||
to_release.push(key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[cfg(not(target_os = "macos"))]
|
|
||||||
if has_cap != en.get_key_state(Key::CapsLock) {
|
|
||||||
en.key_down(Key::CapsLock).ok();
|
|
||||||
en.key_up(Key::CapsLock);
|
|
||||||
}
|
|
||||||
#[cfg(windows)]
|
|
||||||
if crate::common::valid_for_numlock(evt) {
|
|
||||||
if has_numlock != en.get_key_state(Key::NumLock) {
|
|
||||||
en.key_down(Key::NumLock).ok();
|
|
||||||
en.key_up(Key::NumLock);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
match evt.union {
|
match evt.union {
|
||||||
Some(key_event::Union::ControlKey(ck)) => {
|
Some(key_event::Union::ControlKey(ck)) => {
|
||||||
if let Some(key) = KEY_MAP.get(&ck.value()) {
|
if let Some(key) = KEY_MAP.get(&ck.value()) {
|
||||||
@ -683,7 +760,7 @@ fn handle_key_(evt: &KeyEvent) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if evt.down {
|
if evt.down {
|
||||||
allow_err!(en.key_down(key.clone()));
|
en.key_down(key.clone()).ok();
|
||||||
KEYS_DOWN
|
KEYS_DOWN
|
||||||
.lock()
|
.lock()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
@ -719,6 +796,10 @@ fn handle_key_(evt: &KeyEvent) {
|
|||||||
en.key_sequence(&x);
|
en.key_sequence(&x);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
KEYS_DOWN
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.insert(chr as u64 + KEY_CHAR_START, Instant::now());
|
||||||
} else {
|
} else {
|
||||||
en.key_up(get_layout(chr));
|
en.key_up(get_layout(chr));
|
||||||
KEYS_DOWN
|
KEYS_DOWN
|
||||||
@ -741,10 +822,26 @@ fn handle_key_(evt: &KeyEvent) {
|
|||||||
for key in to_release {
|
for key in to_release {
|
||||||
en.key_up(key.clone());
|
en.key_up(key.clone());
|
||||||
}
|
}
|
||||||
#[cfg(windows)]
|
}
|
||||||
if disable_numlock {
|
|
||||||
en.key_down(Key::NumLock).ok();
|
fn handle_key_(evt: &KeyEvent) {
|
||||||
en.key_up(Key::NumLock);
|
if EXITING.load(Ordering::SeqCst) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
match evt.mode.unwrap() {
|
||||||
|
KeyboardMode::Legacy => {
|
||||||
|
legacy_keyboard_mode(evt);
|
||||||
|
}
|
||||||
|
KeyboardMode::Map => {
|
||||||
|
map_keyboard_mode(evt);
|
||||||
|
}
|
||||||
|
KeyboardMode::Translate => {
|
||||||
|
legacy_keyboard_mode(evt);
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
legacy_keyboard_mode(evt);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -759,3 +856,52 @@ async fn send_sas() -> ResultType<()> {
|
|||||||
timeout(1000, stream.send(&crate::ipc::Data::SAS)).await??;
|
timeout(1000, stream.send(&crate::ipc::Data::SAS)).await??;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::*;
|
||||||
|
use rdev::{listen, Event, EventType, Key};
|
||||||
|
use std::sync::mpsc;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_handle_key() {
|
||||||
|
// listen
|
||||||
|
let (tx, rx) = mpsc::channel();
|
||||||
|
std::thread::spawn(move || {
|
||||||
|
std::env::set_var("KEYBOARD_ONLY", "y");
|
||||||
|
let func = move |event: Event| {
|
||||||
|
tx.send(event).ok();
|
||||||
|
};
|
||||||
|
if let Err(error) = listen(func) {
|
||||||
|
println!("Error: {:?}", error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// set key/char base on char
|
||||||
|
let mut evt = KeyEvent::new();
|
||||||
|
evt.set_chr(66);
|
||||||
|
evt.mode = KeyboardMode::Legacy.into();
|
||||||
|
|
||||||
|
evt.modifiers.push(ControlKey::CapsLock.into());
|
||||||
|
|
||||||
|
// press
|
||||||
|
evt.down = true;
|
||||||
|
handle_key(&evt);
|
||||||
|
if let Ok(listen_evt) = rx.recv() {
|
||||||
|
assert_eq!(listen_evt.event_type, EventType::KeyPress(Key::Num1))
|
||||||
|
}
|
||||||
|
// release
|
||||||
|
evt.down = false;
|
||||||
|
handle_key(&evt);
|
||||||
|
if let Ok(listen_evt) = rx.recv() {
|
||||||
|
assert_eq!(listen_evt.event_type, EventType::KeyRelease(Key::Num1))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn test_get_key_state() {
|
||||||
|
let mut en = ENIGO.lock().unwrap();
|
||||||
|
println!(
|
||||||
|
"[*] test_get_key_state: {:?}",
|
||||||
|
en.get_key_state(enigo::Key::NumLock)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -350,6 +350,14 @@ pub mod service {
|
|||||||
DataKeyboard::Sequence(_seq) => {
|
DataKeyboard::Sequence(_seq) => {
|
||||||
// ignore
|
// ignore
|
||||||
}
|
}
|
||||||
|
DataKeyboard::KeyDown(enigo::Key::Raw(code)) => {
|
||||||
|
let down_event = InputEvent::new(EventType::KEY, *code - 8, 1);
|
||||||
|
allow_err!(keyboard.emit(&[down_event]));
|
||||||
|
}
|
||||||
|
DataKeyboard::KeyUp(enigo::Key::Raw(code)) => {
|
||||||
|
let down_event = InputEvent::new(EventType::KEY, *code - 8, 0);
|
||||||
|
allow_err!(keyboard.emit(&[down_event]));
|
||||||
|
}
|
||||||
DataKeyboard::KeyDown(key) => {
|
DataKeyboard::KeyDown(key) => {
|
||||||
if let Ok(k) = map_key(key) {
|
if let Ok(k) = map_key(key) {
|
||||||
let down_event = InputEvent::new(EventType::KEY, k.code(), 1);
|
let down_event = InputEvent::new(EventType::KEY, k.code(), 1);
|
||||||
@ -378,6 +386,14 @@ pub mod service {
|
|||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else if enigo::Key::NumLock == *key {
|
||||||
|
match keyboard.get_led_state() {
|
||||||
|
Ok(leds) => leds.contains(evdev::LedType::LED_NUML),
|
||||||
|
Err(_e) => {
|
||||||
|
// log::debug!("Failed to get led state {}", &_e);
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
match keyboard.get_key_state() {
|
match keyboard.get_key_state() {
|
||||||
Ok(keys) => match key {
|
Ok(keys) => match key {
|
||||||
@ -393,7 +409,6 @@ pub mod service {
|
|||||||
keys.contains(evdev::Key::KEY_LEFTALT)
|
keys.contains(evdev::Key::KEY_LEFTALT)
|
||||||
|| keys.contains(evdev::Key::KEY_RIGHTALT)
|
|| keys.contains(evdev::Key::KEY_RIGHTALT)
|
||||||
}
|
}
|
||||||
enigo::Key::NumLock => keys.contains(evdev::Key::KEY_NUMLOCK),
|
|
||||||
enigo::Key::Meta => {
|
enigo::Key::Meta => {
|
||||||
keys.contains(evdev::Key::KEY_LEFTMETA)
|
keys.contains(evdev::Key::KEY_LEFTMETA)
|
||||||
|| keys.contains(evdev::Key::KEY_RIGHTMETA)
|
|| keys.contains(evdev::Key::KEY_RIGHTMETA)
|
||||||
|
|||||||
28
src/ui.rs
28
src/ui.rs
@ -9,17 +9,16 @@ use sciter::Value;
|
|||||||
|
|
||||||
use hbb_common::{
|
use hbb_common::{
|
||||||
allow_err,
|
allow_err,
|
||||||
config::{self, Config, LocalConfig, PeerConfig, RENDEZVOUS_PORT, RENDEZVOUS_TIMEOUT},
|
config::{self, Config, PeerConfig, RENDEZVOUS_PORT, RENDEZVOUS_TIMEOUT},
|
||||||
futures::future::join_all,
|
futures::future::join_all,
|
||||||
log,
|
log,
|
||||||
protobuf::Message as _,
|
protobuf::Message as _,
|
||||||
rendezvous_proto::*,
|
rendezvous_proto::*,
|
||||||
sleep,
|
|
||||||
tcp::FramedStream,
|
tcp::FramedStream,
|
||||||
tokio::{self, sync::mpsc, time},
|
tokio::{self, sync::mpsc, time},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::common::{get_app_name, SOFTWARE_UPDATE_URL};
|
use crate::common::{get_app_name};
|
||||||
use crate::ipc;
|
use crate::ipc;
|
||||||
use crate::ui_interface::{
|
use crate::ui_interface::{
|
||||||
check_mouse_time, closing, create_shortcut, current_is_wayland, fix_login_wayland,
|
check_mouse_time, closing, create_shortcut, current_is_wayland, fix_login_wayland,
|
||||||
@ -59,6 +58,27 @@ lazy_static::lazy_static! {
|
|||||||
|
|
||||||
struct UIHostHandler;
|
struct UIHostHandler;
|
||||||
|
|
||||||
|
fn check_connect_status(
|
||||||
|
reconnect: bool,
|
||||||
|
) -> (
|
||||||
|
Arc<Mutex<Status>>,
|
||||||
|
Arc<Mutex<HashMap<String, String>>>,
|
||||||
|
mpsc::UnboundedSender<ipc::Data>,
|
||||||
|
Arc<Mutex<String>>,
|
||||||
|
) {
|
||||||
|
let status = Arc::new(Mutex::new((0, false, 0, "".to_owned())));
|
||||||
|
let options = Arc::new(Mutex::new(Config::get_options()));
|
||||||
|
let cloned = status.clone();
|
||||||
|
let cloned_options = options.clone();
|
||||||
|
let (tx, rx) = mpsc::unbounded_channel::<ipc::Data>();
|
||||||
|
let password = Arc::new(Mutex::new(String::default()));
|
||||||
|
let cloned_password = password.clone();
|
||||||
|
std::thread::spawn(move || {
|
||||||
|
crate::ui_interface::check_connect_status_(reconnect, rx)
|
||||||
|
});
|
||||||
|
(status, options, tx, password)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn start(args: &mut [String]) {
|
pub fn start(args: &mut [String]) {
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
if args.len() == 1 && args[0] == "--server" {
|
if args.len() == 1 && args[0] == "--server" {
|
||||||
@ -87,7 +107,7 @@ pub fn start(args: &mut [String]) {
|
|||||||
}
|
}
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
if args.len() > 0 && args[0] == "--tray" {
|
if args.len() > 0 && args[0] == "--tray" {
|
||||||
let options = crate::ui_interface::check_connect_status(false).1;
|
let options = check_connect_status(false).1;
|
||||||
crate::tray::start_tray(options);
|
crate::tray::start_tray(options);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -155,6 +155,7 @@ var svg_send = <svg viewBox="0 0 448 448">
|
|||||||
var svg_chat = <svg viewBox="0 0 511.07 511.07">
|
var svg_chat = <svg viewBox="0 0 511.07 511.07">
|
||||||
<path d="m74.39 480.54h-36.213l25.607-25.607c13.807-13.807 22.429-31.765 24.747-51.246-36.029-23.644-62.375-54.751-76.478-90.425-14.093-35.647-15.864-74.888-5.121-113.48 12.89-46.309 43.123-88.518 85.128-118.85 45.646-32.963 102.47-50.387 164.33-50.387 77.927 0 143.61 22.389 189.95 64.745 41.744 38.159 64.734 89.63 64.734 144.93 0 26.868-5.471 53.011-16.26 77.703-11.165 25.551-27.514 48.302-48.593 67.619-46.399 42.523-112.04 65-189.83 65-28.877 0-59.01-3.855-85.913-10.929-25.465 26.123-59.972 40.929-96.086 40.929zm182-420c-124.04 0-200.15 73.973-220.56 147.28-19.284 69.28 9.143 134.74 76.043 175.12l7.475 4.511-0.23 8.727c-0.456 17.274-4.574 33.912-11.945 48.952 17.949-6.073 34.236-17.083 46.99-32.151l6.342-7.493 9.405 2.813c26.393 7.894 57.104 12.241 86.477 12.241 154.37 0 224.68-93.473 224.68-180.32 0-46.776-19.524-90.384-54.976-122.79-40.713-37.216-99.397-56.888-169.71-56.888z"/>
|
<path d="m74.39 480.54h-36.213l25.607-25.607c13.807-13.807 22.429-31.765 24.747-51.246-36.029-23.644-62.375-54.751-76.478-90.425-14.093-35.647-15.864-74.888-5.121-113.48 12.89-46.309 43.123-88.518 85.128-118.85 45.646-32.963 102.47-50.387 164.33-50.387 77.927 0 143.61 22.389 189.95 64.745 41.744 38.159 64.734 89.63 64.734 144.93 0 26.868-5.471 53.011-16.26 77.703-11.165 25.551-27.514 48.302-48.593 67.619-46.399 42.523-112.04 65-189.83 65-28.877 0-59.01-3.855-85.913-10.929-25.465 26.123-59.972 40.929-96.086 40.929zm182-420c-124.04 0-200.15 73.973-220.56 147.28-19.284 69.28 9.143 134.74 76.043 175.12l7.475 4.511-0.23 8.727c-0.456 17.274-4.574 33.912-11.945 48.952 17.949-6.073 34.236-17.083 46.99-32.151l6.342-7.493 9.405 2.813c26.393 7.894 57.104 12.241 86.477 12.241 154.37 0 224.68-93.473 224.68-180.32 0-46.776-19.524-90.384-54.976-122.79-40.713-37.216-99.397-56.888-169.71-56.888z"/>
|
||||||
</svg>;
|
</svg>;
|
||||||
|
var svg_keyboard = <svg viewBox="0 0 511.07 511.07"><path d="M491.979 217.631H110.205a48.41 48.41 0 0 1 .637-4.061c4.408-21.755 23.676-38.152 46.71-38.152h149.314c39.306 0 71.282-32.246 71.282-71.552 0-11.28-9.145-20.56-20.426-20.56s-20.426 9.077-20.426 20.359c0 3.419-.575 6.941-1.619 10.01-4.082 11.998-15.451 20.893-28.812 20.893H157.553c-46.995 0-85.535 36.766-88.331 83.064H20.021C8.739 217.631 0 226.296 0 237.578v170.345c0 11.28 8.739 20.773 20.021 20.773H491.98c11.28 0 20.021-9.492 20.021-20.773V237.578c-.001-11.282-8.74-19.947-20.022-19.947zm-20.83 170.213H40.851V258.482h430.298v129.362z"/><path d="M113.021 273.461H89.872c-11.28 0-20.426 9.145-20.426 20.426s9.145 20.426 20.426 20.426h23.149c11.28 0 20.426-9.145 20.426-20.426s-9.145-20.426-20.426-20.426zM190.638 273.461h-23.149c-11.28 0-20.426 9.145-20.426 20.426s9.145 20.426 20.426 20.426h23.149c11.28 0 20.426-9.145 20.426-20.426s-9.145-20.426-20.426-20.426zM268.255 273.461h-23.149c-11.28 0-20.426 9.145-20.426 20.426s9.145 20.426 20.426 20.426h23.149c11.28 0 20.426-9.145 20.426-20.426s-9.145-20.426-20.426-20.426zM345.872 273.461h-23.149c-11.28 0-20.426 9.145-20.426 20.426s9.145 20.426 20.426 20.426h23.149c11.28 0 20.426-9.145 20.426-20.426s-9.145-20.426-20.426-20.426zM423.489 273.461H400.34c-11.28 0-20.426 9.145-20.426 20.426s9.145 20.426 20.426 20.426h23.149c11.28 0 20.426-9.145 20.426-20.426s-9.145-20.426-20.426-20.426zM113.021 325.206H89.872c-11.28 0-20.426 9.145-20.426 20.426s9.145 20.425 20.426 20.425h23.149c11.28 0 20.426-9.145 20.426-20.425s-9.145-20.426-20.426-20.426zM423.489 325.206H400.34c-11.28 0-20.426 9.145-20.426 20.426s9.145 20.425 20.426 20.425h23.149c11.28 0 20.426-9.145 20.426-20.425s-9.145-20.426-20.426-20.426zM345.872 329.291H167.489c-11.28 0-20.426 9.145-20.426 20.426s9.145 20.426 20.426 20.426h178.383c11.28 0 20.426-9.145 20.426-20.426s-9.145-20.426-20.426-20.426z"/></svg>;
|
||||||
|
|
||||||
function scrollToBottom(el) {
|
function scrollToBottom(el) {
|
||||||
var y = el.box(#height, #content) - el.box(#height, #client);
|
var y = el.box(#height, #content) - el.box(#height, #client);
|
||||||
|
|||||||
@ -139,11 +139,22 @@ class Header: Reactor.Component {
|
|||||||
<span #chat>{svg_chat}</span>
|
<span #chat>{svg_chat}</span>
|
||||||
<span #action>{svg_action}</span>
|
<span #action>{svg_action}</span>
|
||||||
<span #display>{svg_display}</span>
|
<span #display>{svg_display}</span>
|
||||||
|
<span #keyboard>{svg_keyboard}</span>
|
||||||
|
{this.renderKeyboardPop()}
|
||||||
{this.renderDisplayPop()}
|
{this.renderDisplayPop()}
|
||||||
{this.renderActionPop()}
|
{this.renderActionPop()}
|
||||||
</div>;
|
</div>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function renderKeyboardPop(){
|
||||||
|
return <popup>
|
||||||
|
<menu.context #keyboard-options>
|
||||||
|
<li #legacy><span>{svg_checkmark}</span>{translate('Legacy mode')}</li>
|
||||||
|
<li #map><span>{svg_checkmark}</span>{translate('Map mode')}</li>
|
||||||
|
</menu>
|
||||||
|
</popup>;
|
||||||
|
}
|
||||||
|
|
||||||
function renderDisplayPop() {
|
function renderDisplayPop() {
|
||||||
var codecs = handler.supported_hwcodec();
|
var codecs = handler.supported_hwcodec();
|
||||||
var show_codec = handler.has_hwcodec() && (codecs[0] || codecs[1]);
|
var show_codec = handler.has_hwcodec() && (codecs[0] || codecs[1]);
|
||||||
@ -263,6 +274,11 @@ class Header: Reactor.Component {
|
|||||||
me.popup(menu);
|
me.popup(menu);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
event click $(#keyboard) (_, me) {
|
||||||
|
var menu = $(menu#keyboard-options);
|
||||||
|
me.popup(menu);
|
||||||
|
}
|
||||||
|
|
||||||
event click $(#screen) (_, me) {
|
event click $(#screen) (_, me) {
|
||||||
if (pi.current_display == me.index) return;
|
if (pi.current_display == me.index) return;
|
||||||
handler.switch_display(me.index);
|
handler.switch_display(me.index);
|
||||||
@ -352,6 +368,17 @@ class Header: Reactor.Component {
|
|||||||
toggleMenuState();
|
toggleMenuState();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
event click $(menu#keyboard-options>li) (_, me) {
|
||||||
|
if (me.id == "legacy") {
|
||||||
|
handler.save_keyboard_mode("legacy");
|
||||||
|
} else if (me.id == "map") {
|
||||||
|
handler.save_keyboard_mode("map");
|
||||||
|
} else if (me.id == "translate") {
|
||||||
|
handler.save_keyboard_mode("translate");
|
||||||
|
}
|
||||||
|
toggleMenuState()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function handle_custom_image_quality() {
|
function handle_custom_image_quality() {
|
||||||
@ -375,12 +402,17 @@ function toggleMenuState() {
|
|||||||
var s = handler.get_view_style();
|
var s = handler.get_view_style();
|
||||||
if (!s) s = "original";
|
if (!s) s = "original";
|
||||||
values.push(s);
|
values.push(s);
|
||||||
|
var k = handler.get_keyboard_mode();
|
||||||
|
values.push(k);
|
||||||
var c = handler.get_option("codec-preference");
|
var c = handler.get_option("codec-preference");
|
||||||
if (!c) c = "auto";
|
if (!c) c = "auto";
|
||||||
values.push(c);
|
values.push(c);
|
||||||
for (var el in $$(menu#display-options li)) {
|
for (var el in $$(menu#display-options li)) {
|
||||||
el.attributes.toggleClass("selected", values.indexOf(el.id) >= 0);
|
el.attributes.toggleClass("selected", values.indexOf(el.id) >= 0);
|
||||||
}
|
}
|
||||||
|
for (var el in $$(menu#keyboard-options>li)) {
|
||||||
|
el.attributes.toggleClass("selected", values.indexOf(el.id) >= 0);
|
||||||
|
}
|
||||||
for (var id in ["show-remote-cursor", "show-quality-monitor", "disable-audio", "enable-file-transfer", "disable-clipboard", "lock-after-session-end"]) {
|
for (var id in ["show-remote-cursor", "show-quality-monitor", "disable-audio", "enable-file-transfer", "disable-clipboard", "lock-after-session-end"]) {
|
||||||
var el = self.select('#' + id);
|
var el = self.select('#' + id);
|
||||||
if (el) {
|
if (el) {
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
use std::{
|
use std::{
|
||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
ops::{Deref, DerefMut},
|
ops::{Deref, DerefMut},
|
||||||
sync::{atomic::Ordering, Arc, Mutex},
|
sync::{Arc, Mutex},
|
||||||
};
|
};
|
||||||
|
|
||||||
use sciter::{
|
use sciter::{
|
||||||
@ -28,7 +28,7 @@ use hbb_common::{
|
|||||||
use crate::clipboard_file::*;
|
use crate::clipboard_file::*;
|
||||||
use crate::{
|
use crate::{
|
||||||
client::*,
|
client::*,
|
||||||
ui_session_interface::{InvokeUiSession, Session, IS_IN},
|
ui_session_interface::{InvokeUiSession, Session},
|
||||||
};
|
};
|
||||||
|
|
||||||
type Video = AssetPtr<video_destination>;
|
type Video = AssetPtr<video_destination>;
|
||||||
@ -399,6 +399,8 @@ impl sciter::EventHandler for SciterSession {
|
|||||||
fn get_remember();
|
fn get_remember();
|
||||||
fn peer_platform();
|
fn peer_platform();
|
||||||
fn set_write_override(i32, i32, bool, bool, bool);
|
fn set_write_override(i32, i32, bool, bool, bool);
|
||||||
|
fn get_keyboard_mode();
|
||||||
|
fn save_keyboard_mode(String);
|
||||||
fn has_hwcodec();
|
fn has_hwcodec();
|
||||||
fn supported_hwcodec();
|
fn supported_hwcodec();
|
||||||
fn change_prefer_codec();
|
fn change_prefer_codec();
|
||||||
@ -560,18 +562,6 @@ impl SciterSession {
|
|||||||
self.close_state.insert(k, v);
|
self.close_state.insert(k, v);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn enter(&mut self) {
|
|
||||||
#[cfg(windows)]
|
|
||||||
crate::platform::windows::stop_system_key_propagate(true);
|
|
||||||
IS_IN.store(true, Ordering::SeqCst);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn leave(&mut self) {
|
|
||||||
#[cfg(windows)]
|
|
||||||
crate::platform::windows::stop_system_key_propagate(false);
|
|
||||||
IS_IN.store(false, Ordering::SeqCst);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_key_event(&self, down_or_up: i32, name: &str, code: i32) -> Option<KeyEvent> {
|
fn get_key_event(&self, down_or_up: i32, name: &str, code: i32) -> Option<KeyEvent> {
|
||||||
let mut key_event = KeyEvent::new();
|
let mut key_event = KeyEvent::new();
|
||||||
if down_or_up == 2 {
|
if down_or_up == 2 {
|
||||||
@ -732,4 +722,4 @@ pub fn make_fd(id: i32, entries: &Vec<FileEntry>, only_count: bool) -> Value {
|
|||||||
m.set_item("num_entries", entries.len() as i32);
|
m.set_item("num_entries", entries.len() as i32);
|
||||||
m.set_item("total_size", n as f64);
|
m.set_item("total_size", n as f64);
|
||||||
m
|
m
|
||||||
}
|
}
|
||||||
@ -683,6 +683,7 @@ pub fn check_super_user_permission() -> bool {
|
|||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
pub fn check_zombie(childs: Childs) {
|
pub fn check_zombie(childs: Childs) {
|
||||||
let mut deads = Vec::new();
|
let mut deads = Vec::new();
|
||||||
loop {
|
loop {
|
||||||
@ -714,7 +715,7 @@ pub(crate) fn check_connect_status(reconnect: bool) -> mpsc::UnboundedSender<ipc
|
|||||||
// notice: avoiding create ipc connecton repeatly,
|
// notice: avoiding create ipc connecton repeatly,
|
||||||
// because windows named pipe has serious memory leak issue.
|
// because windows named pipe has serious memory leak issue.
|
||||||
#[tokio::main(flavor = "current_thread")]
|
#[tokio::main(flavor = "current_thread")]
|
||||||
async fn check_connect_status_(reconnect: bool, rx: mpsc::UnboundedReceiver<ipc::Data>) {
|
pub(crate) async fn check_connect_status_(reconnect: bool, rx: mpsc::UnboundedReceiver<ipc::Data>) {
|
||||||
let mut key_confirmed = false;
|
let mut key_confirmed = false;
|
||||||
let mut rx = rx;
|
let mut rx = rx;
|
||||||
let mut mouse_time = 0;
|
let mut mouse_time = 0;
|
||||||
|
|||||||
@ -6,18 +6,16 @@ use crate::client::{
|
|||||||
load_config, send_mouse, start_video_audio_threads, FileManager, Key, LoginConfigHandler,
|
load_config, send_mouse, start_video_audio_threads, FileManager, Key, LoginConfigHandler,
|
||||||
QualityStatus, KEY_MAP, SERVER_KEYBOARD_ENABLED,
|
QualityStatus, KEY_MAP, SERVER_KEYBOARD_ENABLED,
|
||||||
};
|
};
|
||||||
use crate::common;
|
|
||||||
use crate::{client::Data, client::Interface};
|
use crate::{client::Data, client::Interface};
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
|
|
||||||
use hbb_common::config::{Config, LocalConfig, PeerConfig};
|
use hbb_common::config::{Config, LocalConfig, PeerConfig};
|
||||||
|
|
||||||
use hbb_common::rendezvous_proto::ConnType;
|
use hbb_common::rendezvous_proto::ConnType;
|
||||||
use hbb_common::tokio::{self, sync::mpsc};
|
use hbb_common::tokio::{self, sync::mpsc};
|
||||||
|
use rdev::{Event, EventType::*, Key as RdevKey, Keyboard as RdevKeyboard, KeyboardState};
|
||||||
|
|
||||||
use hbb_common::{allow_err, message_proto::*};
|
use hbb_common::{allow_err, message_proto::*};
|
||||||
use hbb_common::{fs, get_version_number, log, Stream};
|
use hbb_common::{fs, get_version_number, log, Stream};
|
||||||
use std::collections::HashMap;
|
use std::collections::{HashMap, HashSet};
|
||||||
use std::ops::{Deref, DerefMut};
|
use std::ops::{Deref, DerefMut};
|
||||||
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
|
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
|
||||||
use std::sync::{Arc, Mutex, RwLock};
|
use std::sync::{Arc, Mutex, RwLock};
|
||||||
@ -29,6 +27,12 @@ static KEYBOARD_HOOKED: AtomicBool = AtomicBool::new(false);
|
|||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
static mut IS_ALT_GR: bool = false;
|
static mut IS_ALT_GR: bool = false;
|
||||||
|
|
||||||
|
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||||
|
lazy_static::lazy_static! {
|
||||||
|
static ref TO_RELEASE: Arc<Mutex<HashSet<RdevKey>>> = Arc::new(Mutex::new(HashSet::<RdevKey>::new()));
|
||||||
|
static ref KEYBOARD: Arc<Mutex<RdevKeyboard>> = Arc::new(Mutex::new(RdevKeyboard::new().unwrap()));
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Default)]
|
#[derive(Clone, Default)]
|
||||||
pub struct Session<T: InvokeUiSession> {
|
pub struct Session<T: InvokeUiSession> {
|
||||||
pub cmd: String,
|
pub cmd: String,
|
||||||
@ -49,12 +53,21 @@ impl<T: InvokeUiSession> Session<T> {
|
|||||||
pub fn get_image_quality(&self) -> String {
|
pub fn get_image_quality(&self) -> String {
|
||||||
self.lc.read().unwrap().image_quality.clone()
|
self.lc.read().unwrap().image_quality.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get custom image quality.
|
/// Get custom image quality.
|
||||||
pub fn get_custom_image_quality(&self) -> Vec<i32> {
|
pub fn get_custom_image_quality(&self) -> Vec<i32> {
|
||||||
self.lc.read().unwrap().custom_image_quality.clone()
|
self.lc.read().unwrap().custom_image_quality.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_keyboard_mode(&self) -> String {
|
||||||
|
return std::env::var("KEYBOARD_MODE")
|
||||||
|
.unwrap_or(String::from("legacy"))
|
||||||
|
.to_lowercase();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn save_keyboard_mode(&self, value: String) {
|
||||||
|
std::env::set_var("KEYBOARD_MODE", value);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn save_view_style(&mut self, value: String) {
|
pub fn save_view_style(&mut self, value: String) {
|
||||||
self.lc.write().unwrap().save_view_style(value);
|
self.lc.write().unwrap().save_view_style(value);
|
||||||
}
|
}
|
||||||
@ -233,25 +246,144 @@ impl<T: InvokeUiSession> Session<T> {
|
|||||||
if self.peer_platform() == "Windows" {
|
if self.peer_platform() == "Windows" {
|
||||||
let mut key_event = KeyEvent::new();
|
let mut key_event = KeyEvent::new();
|
||||||
key_event.set_control_key(ControlKey::CtrlAltDel);
|
key_event.set_control_key(ControlKey::CtrlAltDel);
|
||||||
self.key_down_or_up(1, key_event, false, false, false, false);
|
// todo
|
||||||
|
key_event.down = true;
|
||||||
|
self.send_key_event(key_event, KeyboardMode::Legacy);
|
||||||
} else {
|
} else {
|
||||||
let mut key_event = KeyEvent::new();
|
let mut key_event = KeyEvent::new();
|
||||||
key_event.set_control_key(ControlKey::Delete);
|
key_event.set_control_key(ControlKey::Delete);
|
||||||
self.key_down_or_up(3, key_event, true, true, false, false);
|
self.legacy_modifiers(&mut key_event, true, true, false, false);
|
||||||
|
// todo
|
||||||
|
key_event.press = true;
|
||||||
|
self.send_key_event(key_event, KeyboardMode::Legacy);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn key_down_or_up(
|
fn send_key_event(&self, mut evt: KeyEvent, keyboard_mode: KeyboardMode) {
|
||||||
|
// mode: legacy(0), map(1), translate(2), auto(3)
|
||||||
|
evt.mode = keyboard_mode.into();
|
||||||
|
let mut msg_out = Message::new();
|
||||||
|
msg_out.set_key_event(evt);
|
||||||
|
self.send(Data::Message(msg_out));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
fn convert_numpad_keys(&self, key: RdevKey) -> RdevKey {
|
||||||
|
if get_key_state(enigo::Key::NumLock) {
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
match key {
|
||||||
|
RdevKey::Kp0 => RdevKey::Insert,
|
||||||
|
RdevKey::KpDecimal => RdevKey::Delete,
|
||||||
|
RdevKey::Kp1 => RdevKey::End,
|
||||||
|
RdevKey::Kp2 => RdevKey::DownArrow,
|
||||||
|
RdevKey::Kp3 => RdevKey::PageDown,
|
||||||
|
RdevKey::Kp4 => RdevKey::LeftArrow,
|
||||||
|
RdevKey::Kp5 => RdevKey::Clear,
|
||||||
|
RdevKey::Kp6 => RdevKey::RightArrow,
|
||||||
|
RdevKey::Kp7 => RdevKey::Home,
|
||||||
|
RdevKey::Kp8 => RdevKey::UpArrow,
|
||||||
|
RdevKey::Kp9 => RdevKey::PageUp,
|
||||||
|
_ => key,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn map_keyboard_mode(&self, down_or_up: bool, key: RdevKey, _evt: Option<Event>) {
|
||||||
|
// map mode(1): Send keycode according to the peer platform.
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
let key = if let Some(e) = _evt {
|
||||||
|
rdev::get_win_key(e.code.into(), e.scan_code)
|
||||||
|
} else {
|
||||||
|
key
|
||||||
|
};
|
||||||
|
#[cfg(not(windows))]
|
||||||
|
let key = self.convert_numpad_keys(key);
|
||||||
|
|
||||||
|
let peer = self.peer_platform();
|
||||||
|
let mut key_event = KeyEvent::new();
|
||||||
|
// According to peer platform.
|
||||||
|
let keycode: u32 = if peer == "Linux" {
|
||||||
|
rdev::linux_keycode_from_key(key).unwrap_or_default().into()
|
||||||
|
} else if peer == "Windows" {
|
||||||
|
rdev::win_keycode_from_key(key).unwrap_or_default().into()
|
||||||
|
} else {
|
||||||
|
// Without Clear Key on Mac OS
|
||||||
|
if key == rdev::Key::Clear {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
rdev::macos_keycode_from_key(key).unwrap_or_default().into()
|
||||||
|
};
|
||||||
|
|
||||||
|
key_event.set_chr(keycode);
|
||||||
|
key_event.down = down_or_up;
|
||||||
|
|
||||||
|
if get_key_state(enigo::Key::CapsLock) {
|
||||||
|
key_event.modifiers.push(ControlKey::CapsLock.into());
|
||||||
|
}
|
||||||
|
if get_key_state(enigo::Key::NumLock) {
|
||||||
|
key_event.modifiers.push(ControlKey::NumLock.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
self.send_key_event(key_event, KeyboardMode::Map);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn translate_keyboard_mode(&self, down_or_up: bool, key: RdevKey, evt: Event) {
|
||||||
|
// translate mode(2): locally generated characters are send to the peer.
|
||||||
|
|
||||||
|
// get char
|
||||||
|
let string = match KEYBOARD.lock() {
|
||||||
|
Ok(mut keyboard) => {
|
||||||
|
let string = keyboard.add(&evt.event_type).unwrap_or_default();
|
||||||
|
if keyboard.is_dead() && string == "" && down_or_up == true {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
string
|
||||||
|
}
|
||||||
|
Err(_) => "".to_owned(),
|
||||||
|
};
|
||||||
|
|
||||||
|
// maybe two string
|
||||||
|
let chars = if string == "" {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
let chars: Vec<char> = string.chars().collect();
|
||||||
|
Some(chars)
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(chars) = chars {
|
||||||
|
for chr in chars {
|
||||||
|
let mut key_event = KeyEvent::new();
|
||||||
|
key_event.set_chr(chr as _);
|
||||||
|
key_event.down = true;
|
||||||
|
key_event.press = false;
|
||||||
|
|
||||||
|
self.send_key_event(key_event, KeyboardMode::Translate);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let success = if down_or_up == true {
|
||||||
|
TO_RELEASE.lock().unwrap().insert(key)
|
||||||
|
} else {
|
||||||
|
TO_RELEASE.lock().unwrap().remove(&key)
|
||||||
|
};
|
||||||
|
|
||||||
|
// AltGr && LeftControl(SpecialKey) without action
|
||||||
|
if key == RdevKey::AltGr || evt.scan_code == 541 {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if success {
|
||||||
|
self.map_keyboard_mode(down_or_up, key, None);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn legacy_modifiers(
|
||||||
&self,
|
&self,
|
||||||
down_or_up: i32,
|
key_event: &mut KeyEvent,
|
||||||
evt: KeyEvent,
|
|
||||||
alt: bool,
|
alt: bool,
|
||||||
ctrl: bool,
|
ctrl: bool,
|
||||||
shift: bool,
|
shift: bool,
|
||||||
command: bool,
|
command: bool,
|
||||||
) {
|
) {
|
||||||
let mut key_event = evt;
|
|
||||||
|
|
||||||
if alt
|
if alt
|
||||||
&& !crate::is_control_key(&key_event, &ControlKey::Alt)
|
&& !crate::is_control_key(&key_event, &ControlKey::Alt)
|
||||||
&& !crate::is_control_key(&key_event, &ControlKey::RAlt)
|
&& !crate::is_control_key(&key_event, &ControlKey::RAlt)
|
||||||
@ -276,25 +408,253 @@ impl<T: InvokeUiSession> Session<T> {
|
|||||||
{
|
{
|
||||||
key_event.modifiers.push(ControlKey::Meta.into());
|
key_event.modifiers.push(ControlKey::Meta.into());
|
||||||
}
|
}
|
||||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
|
||||||
if get_key_state(enigo::Key::CapsLock) {
|
if get_key_state(enigo::Key::CapsLock) {
|
||||||
key_event.modifiers.push(ControlKey::CapsLock.into());
|
key_event.modifiers.push(ControlKey::CapsLock.into());
|
||||||
}
|
}
|
||||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
|
||||||
if self.peer_platform() != "Mac OS" {
|
if self.peer_platform() != "Mac OS" {
|
||||||
if get_key_state(enigo::Key::NumLock) && common::valid_for_numlock(&key_event) {
|
if get_key_state(enigo::Key::NumLock) {
|
||||||
key_event.modifiers.push(ControlKey::NumLock.into());
|
key_event.modifiers.push(ControlKey::NumLock.into());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if down_or_up == 1 {
|
}
|
||||||
key_event.down = true;
|
|
||||||
} else if down_or_up == 3 {
|
fn legacy_keyboard_mode(&self, down_or_up: bool, key: RdevKey, evt: Event) {
|
||||||
key_event.press = true;
|
// legacy mode(0): Generate characters locally, look for keycode on other side.
|
||||||
|
let peer = self.peer_platform();
|
||||||
|
let is_win = peer == "Windows";
|
||||||
|
|
||||||
|
let alt = get_key_state(enigo::Key::Alt);
|
||||||
|
#[cfg(windows)]
|
||||||
|
let ctrl = {
|
||||||
|
let mut tmp =
|
||||||
|
get_key_state(enigo::Key::Control) || get_key_state(enigo::Key::RightControl);
|
||||||
|
unsafe {
|
||||||
|
if IS_ALT_GR {
|
||||||
|
if alt || key == RdevKey::AltGr {
|
||||||
|
if tmp {
|
||||||
|
tmp = false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
IS_ALT_GR = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tmp
|
||||||
|
};
|
||||||
|
#[cfg(not(windows))]
|
||||||
|
let ctrl = get_key_state(enigo::Key::Control) || get_key_state(enigo::Key::RightControl);
|
||||||
|
let shift = get_key_state(enigo::Key::Shift) || get_key_state(enigo::Key::RightShift);
|
||||||
|
#[cfg(windows)]
|
||||||
|
let command = crate::platform::windows::get_win_key_state();
|
||||||
|
#[cfg(not(windows))]
|
||||||
|
let command = get_key_state(enigo::Key::Meta);
|
||||||
|
let control_key = match key {
|
||||||
|
RdevKey::Alt => Some(ControlKey::Alt),
|
||||||
|
RdevKey::AltGr => Some(ControlKey::RAlt),
|
||||||
|
RdevKey::Backspace => Some(ControlKey::Backspace),
|
||||||
|
RdevKey::ControlLeft => {
|
||||||
|
// when pressing AltGr, an extra VK_LCONTROL with a special
|
||||||
|
// scancode with bit 9 set is sent, let's ignore this.
|
||||||
|
#[cfg(windows)]
|
||||||
|
if evt.scan_code & 0x200 != 0 {
|
||||||
|
unsafe {
|
||||||
|
IS_ALT_GR = true;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Some(ControlKey::Control)
|
||||||
|
}
|
||||||
|
RdevKey::ControlRight => Some(ControlKey::RControl),
|
||||||
|
RdevKey::DownArrow => Some(ControlKey::DownArrow),
|
||||||
|
RdevKey::Escape => Some(ControlKey::Escape),
|
||||||
|
RdevKey::F1 => Some(ControlKey::F1),
|
||||||
|
RdevKey::F10 => Some(ControlKey::F10),
|
||||||
|
RdevKey::F11 => Some(ControlKey::F11),
|
||||||
|
RdevKey::F12 => Some(ControlKey::F12),
|
||||||
|
RdevKey::F2 => Some(ControlKey::F2),
|
||||||
|
RdevKey::F3 => Some(ControlKey::F3),
|
||||||
|
RdevKey::F4 => Some(ControlKey::F4),
|
||||||
|
RdevKey::F5 => Some(ControlKey::F5),
|
||||||
|
RdevKey::F6 => Some(ControlKey::F6),
|
||||||
|
RdevKey::F7 => Some(ControlKey::F7),
|
||||||
|
RdevKey::F8 => Some(ControlKey::F8),
|
||||||
|
RdevKey::F9 => Some(ControlKey::F9),
|
||||||
|
RdevKey::LeftArrow => Some(ControlKey::LeftArrow),
|
||||||
|
RdevKey::MetaLeft => Some(ControlKey::Meta),
|
||||||
|
RdevKey::MetaRight => Some(ControlKey::RWin),
|
||||||
|
RdevKey::Return => Some(ControlKey::Return),
|
||||||
|
RdevKey::RightArrow => Some(ControlKey::RightArrow),
|
||||||
|
RdevKey::ShiftLeft => Some(ControlKey::Shift),
|
||||||
|
RdevKey::ShiftRight => Some(ControlKey::RShift),
|
||||||
|
RdevKey::Space => Some(ControlKey::Space),
|
||||||
|
RdevKey::Tab => Some(ControlKey::Tab),
|
||||||
|
RdevKey::UpArrow => Some(ControlKey::UpArrow),
|
||||||
|
RdevKey::Delete => {
|
||||||
|
if is_win && ctrl && alt {
|
||||||
|
self.ctrl_alt_del();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Some(ControlKey::Delete)
|
||||||
|
}
|
||||||
|
RdevKey::Apps => Some(ControlKey::Apps),
|
||||||
|
RdevKey::Cancel => Some(ControlKey::Cancel),
|
||||||
|
RdevKey::Clear => Some(ControlKey::Clear),
|
||||||
|
RdevKey::Kana => Some(ControlKey::Kana),
|
||||||
|
RdevKey::Hangul => Some(ControlKey::Hangul),
|
||||||
|
RdevKey::Junja => Some(ControlKey::Junja),
|
||||||
|
RdevKey::Final => Some(ControlKey::Final),
|
||||||
|
RdevKey::Hanja => Some(ControlKey::Hanja),
|
||||||
|
RdevKey::Hanji => Some(ControlKey::Hanja),
|
||||||
|
RdevKey::Convert => Some(ControlKey::Convert),
|
||||||
|
RdevKey::Print => Some(ControlKey::Print),
|
||||||
|
RdevKey::Select => Some(ControlKey::Select),
|
||||||
|
RdevKey::Execute => Some(ControlKey::Execute),
|
||||||
|
RdevKey::PrintScreen => Some(ControlKey::Snapshot),
|
||||||
|
RdevKey::Help => Some(ControlKey::Help),
|
||||||
|
RdevKey::Sleep => Some(ControlKey::Sleep),
|
||||||
|
RdevKey::Separator => Some(ControlKey::Separator),
|
||||||
|
RdevKey::KpReturn => Some(ControlKey::NumpadEnter),
|
||||||
|
RdevKey::Kp0 => Some(ControlKey::Numpad0),
|
||||||
|
RdevKey::Kp1 => Some(ControlKey::Numpad1),
|
||||||
|
RdevKey::Kp2 => Some(ControlKey::Numpad2),
|
||||||
|
RdevKey::Kp3 => Some(ControlKey::Numpad3),
|
||||||
|
RdevKey::Kp4 => Some(ControlKey::Numpad4),
|
||||||
|
RdevKey::Kp5 => Some(ControlKey::Numpad5),
|
||||||
|
RdevKey::Kp6 => Some(ControlKey::Numpad6),
|
||||||
|
RdevKey::Kp7 => Some(ControlKey::Numpad7),
|
||||||
|
RdevKey::Kp8 => Some(ControlKey::Numpad8),
|
||||||
|
RdevKey::Kp9 => Some(ControlKey::Numpad9),
|
||||||
|
RdevKey::KpDivide => Some(ControlKey::Divide),
|
||||||
|
RdevKey::KpMultiply => Some(ControlKey::Multiply),
|
||||||
|
RdevKey::KpDecimal => Some(ControlKey::Decimal),
|
||||||
|
RdevKey::KpMinus => Some(ControlKey::Subtract),
|
||||||
|
RdevKey::KpPlus => Some(ControlKey::Add),
|
||||||
|
RdevKey::CapsLock | RdevKey::NumLock | RdevKey::ScrollLock => {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
RdevKey::Home => Some(ControlKey::Home),
|
||||||
|
RdevKey::End => Some(ControlKey::End),
|
||||||
|
RdevKey::Insert => Some(ControlKey::Insert),
|
||||||
|
RdevKey::PageUp => Some(ControlKey::PageUp),
|
||||||
|
RdevKey::PageDown => Some(ControlKey::PageDown),
|
||||||
|
RdevKey::Pause => Some(ControlKey::Pause),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
let mut key_event = KeyEvent::new();
|
||||||
|
if let Some(k) = control_key {
|
||||||
|
key_event.set_control_key(k);
|
||||||
|
} else {
|
||||||
|
let mut chr = match evt.name {
|
||||||
|
Some(ref s) => {
|
||||||
|
if s.len() <= 2 {
|
||||||
|
// exclude chinese characters
|
||||||
|
s.chars().next().unwrap_or('\0')
|
||||||
|
} else {
|
||||||
|
'\0'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => '\0',
|
||||||
|
};
|
||||||
|
if chr == '·' {
|
||||||
|
// special for Chinese
|
||||||
|
chr = '`';
|
||||||
|
}
|
||||||
|
if chr == '\0' {
|
||||||
|
chr = match key {
|
||||||
|
RdevKey::Num1 => '1',
|
||||||
|
RdevKey::Num2 => '2',
|
||||||
|
RdevKey::Num3 => '3',
|
||||||
|
RdevKey::Num4 => '4',
|
||||||
|
RdevKey::Num5 => '5',
|
||||||
|
RdevKey::Num6 => '6',
|
||||||
|
RdevKey::Num7 => '7',
|
||||||
|
RdevKey::Num8 => '8',
|
||||||
|
RdevKey::Num9 => '9',
|
||||||
|
RdevKey::Num0 => '0',
|
||||||
|
RdevKey::KeyA => 'a',
|
||||||
|
RdevKey::KeyB => 'b',
|
||||||
|
RdevKey::KeyC => 'c',
|
||||||
|
RdevKey::KeyD => 'd',
|
||||||
|
RdevKey::KeyE => 'e',
|
||||||
|
RdevKey::KeyF => 'f',
|
||||||
|
RdevKey::KeyG => 'g',
|
||||||
|
RdevKey::KeyH => 'h',
|
||||||
|
RdevKey::KeyI => 'i',
|
||||||
|
RdevKey::KeyJ => 'j',
|
||||||
|
RdevKey::KeyK => 'k',
|
||||||
|
RdevKey::KeyL => 'l',
|
||||||
|
RdevKey::KeyM => 'm',
|
||||||
|
RdevKey::KeyN => 'n',
|
||||||
|
RdevKey::KeyO => 'o',
|
||||||
|
RdevKey::KeyP => 'p',
|
||||||
|
RdevKey::KeyQ => 'q',
|
||||||
|
RdevKey::KeyR => 'r',
|
||||||
|
RdevKey::KeyS => 's',
|
||||||
|
RdevKey::KeyT => 't',
|
||||||
|
RdevKey::KeyU => 'u',
|
||||||
|
RdevKey::KeyV => 'v',
|
||||||
|
RdevKey::KeyW => 'w',
|
||||||
|
RdevKey::KeyX => 'x',
|
||||||
|
RdevKey::KeyY => 'y',
|
||||||
|
RdevKey::KeyZ => 'z',
|
||||||
|
RdevKey::Comma => ',',
|
||||||
|
RdevKey::Dot => '.',
|
||||||
|
RdevKey::SemiColon => ';',
|
||||||
|
RdevKey::Quote => '\'',
|
||||||
|
RdevKey::LeftBracket => '[',
|
||||||
|
RdevKey::RightBracket => ']',
|
||||||
|
RdevKey::BackSlash => '\\',
|
||||||
|
RdevKey::Minus => '-',
|
||||||
|
RdevKey::Equal => '=',
|
||||||
|
RdevKey::BackQuote => '`',
|
||||||
|
_ => '\0',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if chr != '\0' {
|
||||||
|
if chr == 'l' && is_win && command {
|
||||||
|
self.lock_screen();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
key_event.set_chr(chr as _);
|
||||||
|
} else {
|
||||||
|
log::error!("Unknown key {:?}", evt);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.legacy_modifiers(&mut key_event, alt, ctrl, shift, command);
|
||||||
|
|
||||||
|
if down_or_up == true {
|
||||||
|
key_event.down = true;
|
||||||
|
}
|
||||||
|
self.send_key_event(key_event, KeyboardMode::Legacy)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn key_down_or_up(&self, down_or_up: bool, key: RdevKey, evt: Event) {
|
||||||
|
// Call different functions according to keyboard mode.
|
||||||
|
let mode = match self.get_keyboard_mode().as_str() {
|
||||||
|
"map" => KeyboardMode::Map,
|
||||||
|
"legacy" => KeyboardMode::Legacy,
|
||||||
|
"translate" => KeyboardMode::Translate,
|
||||||
|
_ => KeyboardMode::Legacy,
|
||||||
|
};
|
||||||
|
|
||||||
|
match mode {
|
||||||
|
KeyboardMode::Map => {
|
||||||
|
if down_or_up == true {
|
||||||
|
TO_RELEASE.lock().unwrap().insert(key);
|
||||||
|
} else {
|
||||||
|
TO_RELEASE.lock().unwrap().remove(&key);
|
||||||
|
}
|
||||||
|
self.map_keyboard_mode(down_or_up, key, Some(evt));
|
||||||
|
}
|
||||||
|
KeyboardMode::Legacy => self.legacy_keyboard_mode(down_or_up, key, evt),
|
||||||
|
KeyboardMode::Translate => {
|
||||||
|
self.translate_keyboard_mode(down_or_up, key, evt);
|
||||||
|
}
|
||||||
|
_ => self.legacy_keyboard_mode(down_or_up, key, evt),
|
||||||
}
|
}
|
||||||
let mut msg_out = Message::new();
|
|
||||||
msg_out.set_key_event(key_event);
|
|
||||||
log::debug!("{:?}", msg_out);
|
|
||||||
self.send(Data::Message(msg_out));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_platform(&self, is_remote: bool) -> String {
|
pub fn get_platform(&self, is_remote: bool) -> String {
|
||||||
@ -350,7 +710,59 @@ impl<T: InvokeUiSession> Session<T> {
|
|||||||
pub fn lock_screen(&self) {
|
pub fn lock_screen(&self) {
|
||||||
let mut key_event = KeyEvent::new();
|
let mut key_event = KeyEvent::new();
|
||||||
key_event.set_control_key(ControlKey::LockScreen);
|
key_event.set_control_key(ControlKey::LockScreen);
|
||||||
self.key_down_or_up(1, key_event, false, false, false, false);
|
// todo
|
||||||
|
key_event.down = true;
|
||||||
|
self.send_key_event(key_event, KeyboardMode::Legacy);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn enter(&self) {
|
||||||
|
#[cfg(windows)]
|
||||||
|
crate::platform::windows::stop_system_key_propagate(true);
|
||||||
|
IS_IN.store(true, Ordering::SeqCst);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn leave(&self) {
|
||||||
|
for key in TO_RELEASE.lock().unwrap().iter() {
|
||||||
|
self.map_keyboard_mode(false, *key, None)
|
||||||
|
}
|
||||||
|
#[cfg(windows)]
|
||||||
|
crate::platform::windows::stop_system_key_propagate(false);
|
||||||
|
IS_IN.store(false, Ordering::SeqCst);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn handle_flutter_key_event(
|
||||||
|
&self,
|
||||||
|
name: &str,
|
||||||
|
keycode: i32,
|
||||||
|
scancode: i32,
|
||||||
|
down_or_up: bool,
|
||||||
|
) {
|
||||||
|
if scancode < 0 || keycode < 0 {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let keycode: u32 = keycode as u32;
|
||||||
|
let scancode: u32 = scancode as u32;
|
||||||
|
|
||||||
|
#[cfg(not(target_os = "windows"))]
|
||||||
|
let key = rdev::key_from_scancode(scancode) as RdevKey;
|
||||||
|
// Windows requires special handling
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
let key = rdev::get_win_key(keycode, scancode);
|
||||||
|
|
||||||
|
let event_type = if down_or_up {
|
||||||
|
KeyPress(key)
|
||||||
|
} else {
|
||||||
|
KeyRelease(key)
|
||||||
|
};
|
||||||
|
let evt = Event {
|
||||||
|
time: std::time::SystemTime::now(),
|
||||||
|
name: Option::Some(name.to_owned()),
|
||||||
|
code: keycode as _,
|
||||||
|
scan_code: scancode as _,
|
||||||
|
event_type: event_type,
|
||||||
|
};
|
||||||
|
|
||||||
|
self.key_down_or_up(down_or_up, key, evt)
|
||||||
}
|
}
|
||||||
|
|
||||||
// flutter only TODO new input
|
// flutter only TODO new input
|
||||||
@ -426,7 +838,14 @@ impl<T: InvokeUiSession> Session<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.key_down_or_up(v, key_event, alt, ctrl, shift, command);
|
self.legacy_modifiers(&mut key_event, alt, ctrl, shift, command);
|
||||||
|
if v == 1 {
|
||||||
|
key_event.down = true;
|
||||||
|
} else if v == 3 {
|
||||||
|
key_event.press = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.send_key_event(key_event, KeyboardMode::Legacy);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn send_mouse(
|
pub fn send_mouse(
|
||||||
@ -752,222 +1171,52 @@ impl<T: InvokeUiSession> Session<T> {
|
|||||||
}
|
}
|
||||||
log::info!("keyboard hooked");
|
log::info!("keyboard hooked");
|
||||||
let me = self.clone();
|
let me = self.clone();
|
||||||
let peer = self.peer_platform();
|
|
||||||
let is_win = peer == "Windows";
|
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
crate::platform::windows::enable_lowlevel_keyboard(std::ptr::null_mut() as _);
|
crate::platform::windows::enable_lowlevel_keyboard(std::ptr::null_mut() as _);
|
||||||
std::thread::spawn(move || {
|
std::thread::spawn(move || {
|
||||||
// This will block.
|
// This will block.
|
||||||
std::env::set_var("KEYBOARD_ONLY", "y"); // pass to rdev
|
std::env::set_var("KEYBOARD_ONLY", "y");
|
||||||
use rdev::{EventType::*, *};
|
lazy_static::lazy_static! {
|
||||||
|
static ref MUTEX_SPECIAL_KEYS: Mutex<HashMap<RdevKey, bool>> = {
|
||||||
|
let mut m = HashMap::new();
|
||||||
|
m.insert(RdevKey::ShiftLeft, false);
|
||||||
|
m.insert(RdevKey::ShiftRight, false);
|
||||||
|
m.insert(RdevKey::ControlLeft, false);
|
||||||
|
m.insert(RdevKey::ControlRight, false);
|
||||||
|
m.insert(RdevKey::Alt, false);
|
||||||
|
m.insert(RdevKey::AltGr, false);
|
||||||
|
m.insert(RdevKey::MetaLeft, false);
|
||||||
|
m.insert(RdevKey::MetaRight, false);
|
||||||
|
Mutex::new(m)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
let func = move |evt: Event| {
|
let func = move |evt: Event| {
|
||||||
if !IS_IN.load(Ordering::SeqCst) || !SERVER_KEYBOARD_ENABLED.load(Ordering::SeqCst)
|
if !IS_IN.load(Ordering::SeqCst) || !SERVER_KEYBOARD_ENABLED.load(Ordering::SeqCst)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let (key, down) = match evt.event_type {
|
let (_key, down) = match evt.event_type {
|
||||||
KeyPress(k) => (k, 1),
|
KeyPress(k) => {
|
||||||
KeyRelease(k) => (k, 0),
|
// keyboard long press
|
||||||
|
if MUTEX_SPECIAL_KEYS.lock().unwrap().contains_key(&k) {
|
||||||
|
if *MUTEX_SPECIAL_KEYS.lock().unwrap().get(&k).unwrap() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
MUTEX_SPECIAL_KEYS.lock().unwrap().insert(k, true);
|
||||||
|
}
|
||||||
|
(k, true)
|
||||||
|
}
|
||||||
|
KeyRelease(k) => {
|
||||||
|
// keyboard long press
|
||||||
|
if MUTEX_SPECIAL_KEYS.lock().unwrap().contains_key(&k) {
|
||||||
|
MUTEX_SPECIAL_KEYS.lock().unwrap().insert(k, false);
|
||||||
|
}
|
||||||
|
(k, false)
|
||||||
|
}
|
||||||
_ => return,
|
_ => return,
|
||||||
};
|
};
|
||||||
let alt = get_key_state(enigo::Key::Alt);
|
me.key_down_or_up(down, _key, evt);
|
||||||
#[cfg(windows)]
|
|
||||||
let ctrl = {
|
|
||||||
let mut tmp = get_key_state(enigo::Key::Control);
|
|
||||||
unsafe {
|
|
||||||
if IS_ALT_GR {
|
|
||||||
if alt || key == Key::AltGr {
|
|
||||||
if tmp {
|
|
||||||
tmp = false;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
IS_ALT_GR = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
tmp
|
|
||||||
};
|
|
||||||
#[cfg(not(windows))]
|
|
||||||
let ctrl = get_key_state(enigo::Key::Control);
|
|
||||||
let shift = get_key_state(enigo::Key::Shift);
|
|
||||||
#[cfg(windows)]
|
|
||||||
let command = crate::platform::windows::get_win_key_state();
|
|
||||||
#[cfg(not(windows))]
|
|
||||||
let command = get_key_state(enigo::Key::Meta);
|
|
||||||
let control_key = match key {
|
|
||||||
Key::Alt => Some(ControlKey::Alt),
|
|
||||||
Key::AltGr => Some(ControlKey::RAlt),
|
|
||||||
Key::Backspace => Some(ControlKey::Backspace),
|
|
||||||
Key::ControlLeft => {
|
|
||||||
// when pressing AltGr, an extra VK_LCONTROL with a special
|
|
||||||
// scancode with bit 9 set is sent, let's ignore this.
|
|
||||||
#[cfg(windows)]
|
|
||||||
if evt.scan_code & 0x200 != 0 {
|
|
||||||
unsafe {
|
|
||||||
IS_ALT_GR = true;
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Some(ControlKey::Control)
|
|
||||||
}
|
|
||||||
Key::ControlRight => Some(ControlKey::RControl),
|
|
||||||
Key::DownArrow => Some(ControlKey::DownArrow),
|
|
||||||
Key::Escape => Some(ControlKey::Escape),
|
|
||||||
Key::F1 => Some(ControlKey::F1),
|
|
||||||
Key::F10 => Some(ControlKey::F10),
|
|
||||||
Key::F11 => Some(ControlKey::F11),
|
|
||||||
Key::F12 => Some(ControlKey::F12),
|
|
||||||
Key::F2 => Some(ControlKey::F2),
|
|
||||||
Key::F3 => Some(ControlKey::F3),
|
|
||||||
Key::F4 => Some(ControlKey::F4),
|
|
||||||
Key::F5 => Some(ControlKey::F5),
|
|
||||||
Key::F6 => Some(ControlKey::F6),
|
|
||||||
Key::F7 => Some(ControlKey::F7),
|
|
||||||
Key::F8 => Some(ControlKey::F8),
|
|
||||||
Key::F9 => Some(ControlKey::F9),
|
|
||||||
Key::LeftArrow => Some(ControlKey::LeftArrow),
|
|
||||||
Key::MetaLeft => Some(ControlKey::Meta),
|
|
||||||
Key::MetaRight => Some(ControlKey::RWin),
|
|
||||||
Key::Return => Some(ControlKey::Return),
|
|
||||||
Key::RightArrow => Some(ControlKey::RightArrow),
|
|
||||||
Key::ShiftLeft => Some(ControlKey::Shift),
|
|
||||||
Key::ShiftRight => Some(ControlKey::RShift),
|
|
||||||
Key::Space => Some(ControlKey::Space),
|
|
||||||
Key::Tab => Some(ControlKey::Tab),
|
|
||||||
Key::UpArrow => Some(ControlKey::UpArrow),
|
|
||||||
Key::Delete => {
|
|
||||||
if is_win && ctrl && alt {
|
|
||||||
me.ctrl_alt_del();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Some(ControlKey::Delete)
|
|
||||||
}
|
|
||||||
Key::Apps => Some(ControlKey::Apps),
|
|
||||||
Key::Cancel => Some(ControlKey::Cancel),
|
|
||||||
Key::Clear => Some(ControlKey::Clear),
|
|
||||||
Key::Kana => Some(ControlKey::Kana),
|
|
||||||
Key::Hangul => Some(ControlKey::Hangul),
|
|
||||||
Key::Junja => Some(ControlKey::Junja),
|
|
||||||
Key::Final => Some(ControlKey::Final),
|
|
||||||
Key::Hanja => Some(ControlKey::Hanja),
|
|
||||||
Key::Hanji => Some(ControlKey::Hanja),
|
|
||||||
Key::Convert => Some(ControlKey::Convert),
|
|
||||||
Key::Print => Some(ControlKey::Print),
|
|
||||||
Key::Select => Some(ControlKey::Select),
|
|
||||||
Key::Execute => Some(ControlKey::Execute),
|
|
||||||
Key::PrintScreen => Some(ControlKey::Snapshot),
|
|
||||||
Key::Help => Some(ControlKey::Help),
|
|
||||||
Key::Sleep => Some(ControlKey::Sleep),
|
|
||||||
Key::Separator => Some(ControlKey::Separator),
|
|
||||||
Key::KpReturn => Some(ControlKey::NumpadEnter),
|
|
||||||
Key::Kp0 => Some(ControlKey::Numpad0),
|
|
||||||
Key::Kp1 => Some(ControlKey::Numpad1),
|
|
||||||
Key::Kp2 => Some(ControlKey::Numpad2),
|
|
||||||
Key::Kp3 => Some(ControlKey::Numpad3),
|
|
||||||
Key::Kp4 => Some(ControlKey::Numpad4),
|
|
||||||
Key::Kp5 => Some(ControlKey::Numpad5),
|
|
||||||
Key::Kp6 => Some(ControlKey::Numpad6),
|
|
||||||
Key::Kp7 => Some(ControlKey::Numpad7),
|
|
||||||
Key::Kp8 => Some(ControlKey::Numpad8),
|
|
||||||
Key::Kp9 => Some(ControlKey::Numpad9),
|
|
||||||
Key::KpDivide => Some(ControlKey::Divide),
|
|
||||||
Key::KpMultiply => Some(ControlKey::Multiply),
|
|
||||||
Key::KpDecimal => Some(ControlKey::Decimal),
|
|
||||||
Key::KpMinus => Some(ControlKey::Subtract),
|
|
||||||
Key::KpPlus => Some(ControlKey::Add),
|
|
||||||
Key::CapsLock | Key::NumLock | Key::ScrollLock => {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Key::Home => Some(ControlKey::Home),
|
|
||||||
Key::End => Some(ControlKey::End),
|
|
||||||
Key::Insert => Some(ControlKey::Insert),
|
|
||||||
Key::PageUp => Some(ControlKey::PageUp),
|
|
||||||
Key::PageDown => Some(ControlKey::PageDown),
|
|
||||||
Key::Pause => Some(ControlKey::Pause),
|
|
||||||
_ => None,
|
|
||||||
};
|
|
||||||
let mut key_event = KeyEvent::new();
|
|
||||||
if let Some(k) = control_key {
|
|
||||||
key_event.set_control_key(k);
|
|
||||||
} else {
|
|
||||||
let mut chr = match evt.name {
|
|
||||||
Some(ref s) => {
|
|
||||||
if s.len() <= 2 {
|
|
||||||
// exclude chinese characters
|
|
||||||
s.chars().next().unwrap_or('\0')
|
|
||||||
} else {
|
|
||||||
'\0'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => '\0',
|
|
||||||
};
|
|
||||||
if chr == '·' {
|
|
||||||
// special for Chinese
|
|
||||||
chr = '`';
|
|
||||||
}
|
|
||||||
if chr == '\0' {
|
|
||||||
chr = match key {
|
|
||||||
Key::Num1 => '1',
|
|
||||||
Key::Num2 => '2',
|
|
||||||
Key::Num3 => '3',
|
|
||||||
Key::Num4 => '4',
|
|
||||||
Key::Num5 => '5',
|
|
||||||
Key::Num6 => '6',
|
|
||||||
Key::Num7 => '7',
|
|
||||||
Key::Num8 => '8',
|
|
||||||
Key::Num9 => '9',
|
|
||||||
Key::Num0 => '0',
|
|
||||||
Key::KeyA => 'a',
|
|
||||||
Key::KeyB => 'b',
|
|
||||||
Key::KeyC => 'c',
|
|
||||||
Key::KeyD => 'd',
|
|
||||||
Key::KeyE => 'e',
|
|
||||||
Key::KeyF => 'f',
|
|
||||||
Key::KeyG => 'g',
|
|
||||||
Key::KeyH => 'h',
|
|
||||||
Key::KeyI => 'i',
|
|
||||||
Key::KeyJ => 'j',
|
|
||||||
Key::KeyK => 'k',
|
|
||||||
Key::KeyL => 'l',
|
|
||||||
Key::KeyM => 'm',
|
|
||||||
Key::KeyN => 'n',
|
|
||||||
Key::KeyO => 'o',
|
|
||||||
Key::KeyP => 'p',
|
|
||||||
Key::KeyQ => 'q',
|
|
||||||
Key::KeyR => 'r',
|
|
||||||
Key::KeyS => 's',
|
|
||||||
Key::KeyT => 't',
|
|
||||||
Key::KeyU => 'u',
|
|
||||||
Key::KeyV => 'v',
|
|
||||||
Key::KeyW => 'w',
|
|
||||||
Key::KeyX => 'x',
|
|
||||||
Key::KeyY => 'y',
|
|
||||||
Key::KeyZ => 'z',
|
|
||||||
Key::Comma => ',',
|
|
||||||
Key::Dot => '.',
|
|
||||||
Key::SemiColon => ';',
|
|
||||||
Key::Quote => '\'',
|
|
||||||
Key::LeftBracket => '[',
|
|
||||||
Key::RightBracket => ']',
|
|
||||||
Key::BackSlash => '\\',
|
|
||||||
Key::Minus => '-',
|
|
||||||
Key::Equal => '=',
|
|
||||||
Key::BackQuote => '`',
|
|
||||||
_ => '\0',
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if chr != '\0' {
|
|
||||||
if chr == 'l' && is_win && command {
|
|
||||||
me.lock_screen();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
key_event.set_chr(chr as _);
|
|
||||||
} else {
|
|
||||||
log::error!("Unknown key {:?}", evt);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
me.key_down_or_up(down, key_event, alt, ctrl, shift, command); // TODO
|
|
||||||
};
|
};
|
||||||
if let Err(error) = rdev::listen(func) {
|
if let Err(error) = rdev::listen(func) {
|
||||||
log::error!("rdev: {:?}", error);
|
log::error!("rdev: {:?}", error);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user