Merge branch 'master' into hwcodec
This commit is contained in:
commit
68204e0c56
@ -202,7 +202,7 @@ target/release/rustdesk
|
|||||||
- **[libs/scrap](https://github.com/rustdesk/rustdesk/tree/master/libs/scrap)**: 截屏
|
- **[libs/scrap](https://github.com/rustdesk/rustdesk/tree/master/libs/scrap)**: 截屏
|
||||||
- **[libs/enigo](https://github.com/rustdesk/rustdesk/tree/master/libs/enigo)**: 平台相关的鼠标键盘输入
|
- **[libs/enigo](https://github.com/rustdesk/rustdesk/tree/master/libs/enigo)**: 平台相关的鼠标键盘输入
|
||||||
- **[src/ui](https://github.com/rustdesk/rustdesk/tree/master/src/ui)**: GUI
|
- **[src/ui](https://github.com/rustdesk/rustdesk/tree/master/src/ui)**: GUI
|
||||||
- **[src/server](https://github.com/rustdesk/rustdesk/tree/master/src/server)**: 被控端服务,audio/clipboard/input/video 服务, 已经连接实现
|
- **[src/server](https://github.com/rustdesk/rustdesk/tree/master/src/server)**: 被控端服务,audio/clipboard/input/video 服务, 以及连接的实现
|
||||||
- **[src/client.rs](https://github.com/rustdesk/rustdesk/tree/master/src/client.rs)**: 控制端
|
- **[src/client.rs](https://github.com/rustdesk/rustdesk/tree/master/src/client.rs)**: 控制端
|
||||||
- **[src/rendezvous_mediator.rs](https://github.com/rustdesk/rustdesk/tree/master/src/rendezvous_mediator.rs)**: 与[rustdesk-server](https://github.com/rustdesk/rustdesk-server)保持 UDP 通讯, 等待远程连接(通过打洞直连或者中继)
|
- **[src/rendezvous_mediator.rs](https://github.com/rustdesk/rustdesk/tree/master/src/rendezvous_mediator.rs)**: 与[rustdesk-server](https://github.com/rustdesk/rustdesk-server)保持 UDP 通讯, 等待远程连接(通过打洞直连或者中继)
|
||||||
- **[src/platform](https://github.com/rustdesk/rustdesk/tree/master/src/platform)**: 平台服务相关代码
|
- **[src/platform](https://github.com/rustdesk/rustdesk/tree/master/src/platform)**: 平台服务相关代码
|
||||||
|
@ -43,7 +43,7 @@
|
|||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".MainActivity"
|
android:name=".MainActivity"
|
||||||
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
|
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode|navigation"
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
android:hardwareAccelerated="true"
|
android:hardwareAccelerated="true"
|
||||||
android:launchMode="singleTop"
|
android:launchMode="singleTop"
|
||||||
|
@ -310,19 +310,19 @@ class ImageModel with ChangeNotifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
double get maxScale {
|
double get maxScale {
|
||||||
if (_image == null) return 1.0;
|
if (_image == null) return 1.5;
|
||||||
final size = MediaQueryData.fromWindow(ui.window).size;
|
final size = MediaQueryData.fromWindow(ui.window).size;
|
||||||
final xscale = size.width / _image!.width;
|
final xscale = size.width / _image!.width;
|
||||||
final yscale = size.height / _image!.height;
|
final yscale = size.height / _image!.height;
|
||||||
return max(1.0, max(xscale, yscale));
|
return max(1.5, max(xscale, yscale));
|
||||||
}
|
}
|
||||||
|
|
||||||
double get minScale {
|
double get minScale {
|
||||||
if (_image == null) return 1.0;
|
if (_image == null) return 1.5;
|
||||||
final size = MediaQueryData.fromWindow(ui.window).size;
|
final size = MediaQueryData.fromWindow(ui.window).size;
|
||||||
final xscale = size.width / _image!.width;
|
final xscale = size.width / _image!.width;
|
||||||
final yscale = size.height / _image!.height;
|
final yscale = size.height / _image!.height;
|
||||||
return min(xscale, yscale);
|
return min(xscale, yscale) / 1.5;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -724,13 +724,17 @@ class FFI {
|
|||||||
|
|
||||||
static void inputKey(String name, {bool? down, bool? press}) {
|
static void inputKey(String name, {bool? down, bool? press}) {
|
||||||
if (!ffiModel.keyboard()) return;
|
if (!ffiModel.keyboard()) return;
|
||||||
setByName(
|
final Map<String, String> out = Map();
|
||||||
'input_key',
|
out['name'] = name;
|
||||||
json.encode(modify({
|
// default: down = false
|
||||||
'name': name,
|
if (down == true) {
|
||||||
'down': (down ?? false).toString(),
|
out['down'] = "true";
|
||||||
'press': (press ?? true).toString()
|
}
|
||||||
})));
|
// default: press = true
|
||||||
|
if (press != false) {
|
||||||
|
out['press'] = "true";
|
||||||
|
}
|
||||||
|
setByName('input_key', json.encode(modify(out)));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void moveMouse(double x, double y) {
|
static void moveMouse(double x, double y) {
|
||||||
|
@ -343,9 +343,14 @@ class _RemotePageState extends State<RemotePage> {
|
|||||||
onKey: (data, e) {
|
onKey: (data, e) {
|
||||||
final key = e.logicalKey;
|
final key = e.logicalKey;
|
||||||
if (e is RawKeyDownEvent) {
|
if (e is RawKeyDownEvent) {
|
||||||
if (e.repeat) {
|
if (e.repeat &&
|
||||||
|
!e.isAltPressed &&
|
||||||
|
!e.isControlPressed &&
|
||||||
|
!e.isShiftPressed &&
|
||||||
|
!e.isMetaPressed) {
|
||||||
sendRawKey(e, press: true);
|
sendRawKey(e, press: true);
|
||||||
} else {
|
} else {
|
||||||
|
sendRawKey(e, down: true);
|
||||||
if (e.isAltPressed && !FFI.alt) {
|
if (e.isAltPressed && !FFI.alt) {
|
||||||
FFI.alt = true;
|
FFI.alt = true;
|
||||||
} else if (e.isControlPressed && !FFI.ctrl) {
|
} else if (e.isControlPressed && !FFI.ctrl) {
|
||||||
@ -355,7 +360,6 @@ class _RemotePageState extends State<RemotePage> {
|
|||||||
} else if (e.isMetaPressed && !FFI.command) {
|
} else if (e.isMetaPressed && !FFI.command) {
|
||||||
FFI.command = true;
|
FFI.command = true;
|
||||||
}
|
}
|
||||||
sendRawKey(e, down: true);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// [!_showEdit] workaround for soft-keyboard's control_key like Backspace / Enter
|
// [!_showEdit] workaround for soft-keyboard's control_key like Backspace / Enter
|
||||||
@ -481,6 +485,7 @@ class _RemotePageState extends State<RemotePage> {
|
|||||||
/// DoubleFiner -> right click
|
/// DoubleFiner -> right click
|
||||||
/// HoldDrag -> left drag
|
/// HoldDrag -> left drag
|
||||||
|
|
||||||
|
Offset _cacheLongPressPosition = Offset(0, 0);
|
||||||
Widget getBodyForMobileWithGesture() {
|
Widget getBodyForMobileWithGesture() {
|
||||||
final touchMode = FFI.ffiModel.touchMode;
|
final touchMode = FFI.ffiModel.touchMode;
|
||||||
return getMixinGestureDetector(
|
return getMixinGestureDetector(
|
||||||
@ -504,10 +509,14 @@ class _RemotePageState extends State<RemotePage> {
|
|||||||
},
|
},
|
||||||
onLongPressDown: (d) {
|
onLongPressDown: (d) {
|
||||||
if (touchMode) {
|
if (touchMode) {
|
||||||
FFI.cursorModel.move(d.localPosition.dx, d.localPosition.dy);
|
_cacheLongPressPosition = d.localPosition;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onLongPress: () {
|
onLongPress: () {
|
||||||
|
if (touchMode) {
|
||||||
|
FFI.cursorModel
|
||||||
|
.move(_cacheLongPressPosition.dx, _cacheLongPressPosition.dy);
|
||||||
|
}
|
||||||
FFI.tap(MouseButtons.right);
|
FFI.tap(MouseButtons.right);
|
||||||
},
|
},
|
||||||
onDoubleFinerTap: (d) {
|
onDoubleFinerTap: (d) {
|
||||||
@ -534,6 +543,15 @@ class _RemotePageState extends State<RemotePage> {
|
|||||||
if (touchMode) {
|
if (touchMode) {
|
||||||
FFI.cursorModel.move(d.localPosition.dx, d.localPosition.dy);
|
FFI.cursorModel.move(d.localPosition.dx, d.localPosition.dy);
|
||||||
FFI.sendMouse('down', MouseButtons.left);
|
FFI.sendMouse('down', MouseButtons.left);
|
||||||
|
} else {
|
||||||
|
final cursorX = FFI.cursorModel.x;
|
||||||
|
final cursorY = FFI.cursorModel.y;
|
||||||
|
final visible =
|
||||||
|
FFI.cursorModel.getVisibleRect().inflate(1); // extend edges
|
||||||
|
final size = MediaQueryData.fromWindow(ui.window).size;
|
||||||
|
if (!visible.contains(Offset(cursorX, cursorY))) {
|
||||||
|
FFI.cursorModel.move(size.width / 2, size.height / 2);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onOneFingerPanUpdate: (d) {
|
onOneFingerPanUpdate: (d) {
|
||||||
|
@ -2,7 +2,7 @@ import 'dart:async';
|
|||||||
import 'package:flutter/gestures.dart';
|
import 'package:flutter/gestures.dart';
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
|
|
||||||
enum CustomTouchGestureState {
|
enum GestureState {
|
||||||
none,
|
none,
|
||||||
oneFingerPan,
|
oneFingerPan,
|
||||||
twoFingerScale,
|
twoFingerScale,
|
||||||
@ -35,64 +35,41 @@ class CustomTouchGestureRecognizer extends ScaleGestureRecognizer {
|
|||||||
GestureDragUpdateCallback? onThreeFingerVerticalDragUpdate;
|
GestureDragUpdateCallback? onThreeFingerVerticalDragUpdate;
|
||||||
GestureDragEndCallback? onThreeFingerVerticalDragEnd;
|
GestureDragEndCallback? onThreeFingerVerticalDragEnd;
|
||||||
|
|
||||||
var _currentState = CustomTouchGestureState.none;
|
var _currentState = GestureState.none;
|
||||||
Timer? _startEventDebounceTimer;
|
Timer? _debounceTimer;
|
||||||
|
|
||||||
void _init() {
|
void _init() {
|
||||||
debugPrint("CustomTouchGestureRecognizer init");
|
debugPrint("CustomTouchGestureRecognizer init");
|
||||||
onStart = (d) {
|
// onStart = (d) {};
|
||||||
_startEventDebounceTimer?.cancel();
|
onUpdate = (d) {
|
||||||
if (d.pointerCount == 1) {
|
_debounceTimer?.cancel();
|
||||||
_currentState = CustomTouchGestureState.oneFingerPan;
|
if (d.pointerCount == 1 && _currentState != GestureState.oneFingerPan) {
|
||||||
if (onOneFingerPanStart != null) {
|
onOneFingerStartDebounce(d);
|
||||||
onOneFingerPanStart!(DragStartDetails(
|
} else if (d.pointerCount == 2 &&
|
||||||
localPosition: d.localFocalPoint, globalPosition: d.focalPoint));
|
_currentState != GestureState.twoFingerScale) {
|
||||||
}
|
onTwoFingerStartDebounce(d);
|
||||||
debugPrint("start oneFingerPan");
|
} else if (d.pointerCount == 3 &&
|
||||||
} else if (d.pointerCount == 2) {
|
_currentState != GestureState.threeFingerVerticalDrag) {
|
||||||
if (_currentState == CustomTouchGestureState.threeFingerVerticalDrag) {
|
_currentState = GestureState.threeFingerVerticalDrag;
|
||||||
// 3 -> 2 debounce
|
|
||||||
_startEventDebounceTimer = Timer(Duration(milliseconds: 200), () {
|
|
||||||
_currentState = CustomTouchGestureState.twoFingerScale;
|
|
||||||
if (onTwoFingerScaleStart != null) {
|
|
||||||
onTwoFingerScaleStart!(ScaleStartDetails(
|
|
||||||
localFocalPoint: d.localFocalPoint,
|
|
||||||
focalPoint: d.focalPoint));
|
|
||||||
}
|
|
||||||
debugPrint("debounce start twoFingerScale success");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
_currentState = CustomTouchGestureState.twoFingerScale;
|
|
||||||
// startWatchTimer();
|
|
||||||
if (onTwoFingerScaleStart != null) {
|
|
||||||
onTwoFingerScaleStart!(ScaleStartDetails(
|
|
||||||
localFocalPoint: d.localFocalPoint, focalPoint: d.focalPoint));
|
|
||||||
}
|
|
||||||
debugPrint("start twoFingerScale");
|
|
||||||
} else if (d.pointerCount == 3) {
|
|
||||||
_currentState = CustomTouchGestureState.threeFingerVerticalDrag;
|
|
||||||
if (onThreeFingerVerticalDragStart != null) {
|
if (onThreeFingerVerticalDragStart != null) {
|
||||||
onThreeFingerVerticalDragStart!(
|
onThreeFingerVerticalDragStart!(
|
||||||
DragStartDetails(globalPosition: d.localFocalPoint));
|
DragStartDetails(globalPosition: d.localFocalPoint));
|
||||||
}
|
}
|
||||||
debugPrint("start threeFingerScale");
|
debugPrint("start threeFingerScale");
|
||||||
// _reset();
|
|
||||||
}
|
}
|
||||||
};
|
if (_currentState != GestureState.none) {
|
||||||
onUpdate = (d) {
|
|
||||||
if (_currentState != CustomTouchGestureState.none) {
|
|
||||||
switch (_currentState) {
|
switch (_currentState) {
|
||||||
case CustomTouchGestureState.oneFingerPan:
|
case GestureState.oneFingerPan:
|
||||||
if (onOneFingerPanUpdate != null) {
|
if (onOneFingerPanUpdate != null) {
|
||||||
onOneFingerPanUpdate!(_getDragUpdateDetails(d));
|
onOneFingerPanUpdate!(_getDragUpdateDetails(d));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case CustomTouchGestureState.twoFingerScale:
|
case GestureState.twoFingerScale:
|
||||||
if (onTwoFingerScaleUpdate != null) {
|
if (onTwoFingerScaleUpdate != null) {
|
||||||
onTwoFingerScaleUpdate!(d);
|
onTwoFingerScaleUpdate!(d);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case CustomTouchGestureState.threeFingerVerticalDrag:
|
case GestureState.threeFingerVerticalDrag:
|
||||||
if (onThreeFingerVerticalDragUpdate != null) {
|
if (onThreeFingerVerticalDragUpdate != null) {
|
||||||
onThreeFingerVerticalDragUpdate!(_getDragUpdateDetails(d));
|
onThreeFingerVerticalDragUpdate!(_getDragUpdateDetails(d));
|
||||||
}
|
}
|
||||||
@ -105,21 +82,22 @@ class CustomTouchGestureRecognizer extends ScaleGestureRecognizer {
|
|||||||
};
|
};
|
||||||
onEnd = (d) {
|
onEnd = (d) {
|
||||||
debugPrint("ScaleGestureRecognizer onEnd");
|
debugPrint("ScaleGestureRecognizer onEnd");
|
||||||
|
_debounceTimer?.cancel();
|
||||||
// end
|
// end
|
||||||
switch (_currentState) {
|
switch (_currentState) {
|
||||||
case CustomTouchGestureState.oneFingerPan:
|
case GestureState.oneFingerPan:
|
||||||
debugPrint("TwoFingerState.pan onEnd");
|
debugPrint("TwoFingerState.pan onEnd");
|
||||||
if (onOneFingerPanEnd != null) {
|
if (onOneFingerPanEnd != null) {
|
||||||
onOneFingerPanEnd!(_getDragEndDetails(d));
|
onOneFingerPanEnd!(_getDragEndDetails(d));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case CustomTouchGestureState.twoFingerScale:
|
case GestureState.twoFingerScale:
|
||||||
debugPrint("TwoFingerState.scale onEnd");
|
debugPrint("TwoFingerState.scale onEnd");
|
||||||
if (onTwoFingerScaleEnd != null) {
|
if (onTwoFingerScaleEnd != null) {
|
||||||
onTwoFingerScaleEnd!(d);
|
onTwoFingerScaleEnd!(d);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case CustomTouchGestureState.threeFingerVerticalDrag:
|
case GestureState.threeFingerVerticalDrag:
|
||||||
debugPrint("ThreeFingerState.vertical onEnd");
|
debugPrint("ThreeFingerState.vertical onEnd");
|
||||||
if (onThreeFingerVerticalDragEnd != null) {
|
if (onThreeFingerVerticalDragEnd != null) {
|
||||||
onThreeFingerVerticalDragEnd!(_getDragEndDetails(d));
|
onThreeFingerVerticalDragEnd!(_getDragEndDetails(d));
|
||||||
@ -128,10 +106,50 @@ class CustomTouchGestureRecognizer extends ScaleGestureRecognizer {
|
|||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
_currentState = CustomTouchGestureState.none;
|
_debounceTimer = Timer(Duration(milliseconds: 200), () {
|
||||||
|
_currentState = GestureState.none;
|
||||||
|
});
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void onOneFingerStartDebounce(ScaleUpdateDetails d) {
|
||||||
|
final start = (ScaleUpdateDetails d) {
|
||||||
|
_currentState = GestureState.oneFingerPan;
|
||||||
|
if (onOneFingerPanStart != null) {
|
||||||
|
onOneFingerPanStart!(DragStartDetails(
|
||||||
|
localPosition: d.localFocalPoint, globalPosition: d.focalPoint));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if (_currentState != GestureState.none) {
|
||||||
|
_debounceTimer = Timer(Duration(milliseconds: 200), () {
|
||||||
|
start(d);
|
||||||
|
debugPrint("debounce start oneFingerPan");
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
start(d);
|
||||||
|
debugPrint("start oneFingerPan");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void onTwoFingerStartDebounce(ScaleUpdateDetails d) {
|
||||||
|
final start = (ScaleUpdateDetails d) {
|
||||||
|
_currentState = GestureState.twoFingerScale;
|
||||||
|
if (onTwoFingerScaleStart != null) {
|
||||||
|
onTwoFingerScaleStart!(ScaleStartDetails(
|
||||||
|
localFocalPoint: d.localFocalPoint, focalPoint: d.focalPoint));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if (_currentState == GestureState.threeFingerVerticalDrag) {
|
||||||
|
_debounceTimer = Timer(Duration(milliseconds: 200), () {
|
||||||
|
start(d);
|
||||||
|
debugPrint("debounce start twoFingerScale");
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
start(d);
|
||||||
|
debugPrint("start twoFingerScale");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
DragUpdateDetails _getDragUpdateDetails(ScaleUpdateDetails d) =>
|
DragUpdateDetails _getDragUpdateDetails(ScaleUpdateDetails d) =>
|
||||||
DragUpdateDetails(
|
DragUpdateDetails(
|
||||||
globalPosition: d.focalPoint,
|
globalPosition: d.focalPoint,
|
||||||
|
@ -1,31 +1,177 @@
|
|||||||
from pynput.keyboard import Key, Controller
|
from pynput.keyboard import Key, Controller
|
||||||
from pynput.keyboard._xorg import KeyCode
|
from pynput.keyboard._xorg import KeyCode
|
||||||
from pynput._util.xorg import display_manager
|
from pynput._util.xorg import display_manager
|
||||||
|
import Xlib
|
||||||
|
from pynput._util.xorg import *
|
||||||
|
import Xlib
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import socket
|
import socket
|
||||||
from Xlib.ext.xtest import fake_input
|
|
||||||
from Xlib import X
|
|
||||||
import Xlib
|
|
||||||
|
|
||||||
KeyCode._from_symbol("\0") # test
|
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):
|
class MyController(Controller):
|
||||||
def _handle(self, key, is_press):
|
def _update_keyboard_mapping(self):
|
||||||
"""Resolves a key identifier and sends a keyboard event.
|
"""Updates the keyboard mapping.
|
||||||
:param event: The *X* keyboard event.
|
|
||||||
:param int keysym: The keysym to handle.
|
|
||||||
"""
|
"""
|
||||||
keysym = self._keysym(key)
|
with display_manager(self._display) as dm:
|
||||||
keycode = self._display.keysym_to_keycode(keysym)
|
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:
|
with display_manager(self._display) as dm:
|
||||||
Xlib.ext.xtest.fake_input(
|
Xlib.ext.xtest.fake_input(
|
||||||
dm,
|
dm,
|
||||||
Xlib.X.KeyPress if is_press else Xlib.X.KeyRelease,
|
Xlib.X.KeyPress if is_press else Xlib.X.KeyRelease,
|
||||||
keycode)
|
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()
|
keyboard = MyController()
|
||||||
@ -82,7 +228,7 @@ def loop():
|
|||||||
else:
|
else:
|
||||||
keyboard.release(name)
|
keyboard.release(name)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(e)
|
print('[x] error key',e)
|
||||||
|
|
||||||
|
|
||||||
loop()
|
loop()
|
||||||
|
@ -47,7 +47,7 @@ case "$1" in
|
|||||||
;;
|
;;
|
||||||
2)
|
2)
|
||||||
# for upgrade
|
# for upgrade
|
||||||
service rustdesk stop || true
|
systemctl stop rustdesk || true
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
@ -61,10 +61,26 @@ systemctl start rustdesk
|
|||||||
update-desktop-database
|
update-desktop-database
|
||||||
|
|
||||||
%preun
|
%preun
|
||||||
systemctl stop rustdesk || true
|
case "$1" in
|
||||||
systemctl disable rustdesk || true
|
0)
|
||||||
rm /etc/systemd/system/rustdesk.service || true
|
# for uninstall
|
||||||
|
systemctl stop rustdesk || true
|
||||||
|
systemctl disable rustdesk || true
|
||||||
|
rm /etc/systemd/system/rustdesk.service || true
|
||||||
|
;;
|
||||||
|
1)
|
||||||
|
# for upgrade
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
%postun
|
%postun
|
||||||
rm /usr/share/applications/rustdesk.desktop || true
|
case "$1" in
|
||||||
update-desktop-database
|
0)
|
||||||
|
# for uninstall
|
||||||
|
rm /usr/share/applications/rustdesk.desktop || true
|
||||||
|
update-desktop-database
|
||||||
|
;;
|
||||||
|
1)
|
||||||
|
# for upgrade
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
28
rpm.spec
28
rpm.spec
@ -48,7 +48,7 @@ case "$1" in
|
|||||||
;;
|
;;
|
||||||
2)
|
2)
|
||||||
# for upgrade
|
# for upgrade
|
||||||
service rustdesk stop || true
|
systemctl stop rustdesk || true
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
@ -62,10 +62,26 @@ systemctl start rustdesk
|
|||||||
update-desktop-database
|
update-desktop-database
|
||||||
|
|
||||||
%preun
|
%preun
|
||||||
systemctl stop rustdesk || true
|
case "$1" in
|
||||||
systemctl disable rustdesk || true
|
0)
|
||||||
rm /etc/systemd/system/rustdesk.service || true
|
# for uninstall
|
||||||
|
systemctl stop rustdesk || true
|
||||||
|
systemctl disable rustdesk || true
|
||||||
|
rm /etc/systemd/system/rustdesk.service || true
|
||||||
|
;;
|
||||||
|
1)
|
||||||
|
# for upgrade
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
%postun
|
%postun
|
||||||
rm /usr/share/applications/rustdesk.desktop || true
|
case "$1" in
|
||||||
update-desktop-database
|
0)
|
||||||
|
# for uninstall
|
||||||
|
rm /usr/share/applications/rustdesk.desktop || true
|
||||||
|
update-desktop-database
|
||||||
|
;;
|
||||||
|
1)
|
||||||
|
# for upgrade
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
26
src/lang.rs
26
src/lang.rs
@ -1,21 +1,43 @@
|
|||||||
|
use serde_json::{json, value::Value};
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
|
|
||||||
mod cn;
|
mod cn;
|
||||||
mod cs;
|
mod cs;
|
||||||
mod da;
|
mod da;
|
||||||
mod sk;
|
|
||||||
mod de;
|
mod de;
|
||||||
mod en;
|
mod en;
|
||||||
mod es;
|
|
||||||
mod eo;
|
mod eo;
|
||||||
|
mod es;
|
||||||
mod fr;
|
mod fr;
|
||||||
mod id;
|
mod id;
|
||||||
mod it;
|
mod it;
|
||||||
mod ptbr;
|
mod ptbr;
|
||||||
mod ru;
|
mod ru;
|
||||||
|
mod sk;
|
||||||
mod tr;
|
mod tr;
|
||||||
mod tw;
|
mod tw;
|
||||||
|
|
||||||
|
lazy_static::lazy_static! {
|
||||||
|
pub static ref LANGS: Value =
|
||||||
|
json!(vec![
|
||||||
|
("en", "English"),
|
||||||
|
("it", "Italiano"),
|
||||||
|
("fr", "Français"),
|
||||||
|
("de", "Deutsch"),
|
||||||
|
("cn", "简体中文"),
|
||||||
|
("tw", "繁體中文"),
|
||||||
|
("pt", "Português"),
|
||||||
|
("es", "Español"),
|
||||||
|
("ru", "Русский"),
|
||||||
|
("sk", "Slovenčina"),
|
||||||
|
("id", "Indonesia"),
|
||||||
|
("cs", "Čeština"),
|
||||||
|
("da", "Dansk"),
|
||||||
|
("eo", "Esperanto"),
|
||||||
|
("tr", "Türkçe"),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||||
pub fn translate(name: String) -> String {
|
pub fn translate(name: String) -> String {
|
||||||
let locale = sys_locale::get_locale().unwrap_or_default().to_lowercase();
|
let locale = sys_locale::get_locale().unwrap_or_default().to_lowercase();
|
||||||
|
@ -283,5 +283,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Turned off", "退出"),
|
("Turned off", "退出"),
|
||||||
("In privacy mode", "进入隐私模式"),
|
("In privacy mode", "进入隐私模式"),
|
||||||
("Out privacy mode", "退出隐私模式"),
|
("Out privacy mode", "退出隐私模式"),
|
||||||
|
("Language", "语言"),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -283,5 +283,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Turned off", "Vypnutý"),
|
("Turned off", "Vypnutý"),
|
||||||
("In privacy mode", "v režimu soukromí"),
|
("In privacy mode", "v režimu soukromí"),
|
||||||
("Out privacy mode", "mimo režim soukromí"),
|
("Out privacy mode", "mimo režim soukromí"),
|
||||||
|
("Language", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -283,5 +283,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Turned off", "Slukket"),
|
("Turned off", "Slukket"),
|
||||||
("In privacy mode", "I databeskyttelsestilstand"),
|
("In privacy mode", "I databeskyttelsestilstand"),
|
||||||
("Out privacy mode", "Databeskyttelsestilstand fra"),
|
("Out privacy mode", "Databeskyttelsestilstand fra"),
|
||||||
|
("Language", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -283,5 +283,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Turned off", "Ausgeschaltet"),
|
("Turned off", "Ausgeschaltet"),
|
||||||
("In privacy mode", "im Datenschutzmodus"),
|
("In privacy mode", "im Datenschutzmodus"),
|
||||||
("Out privacy mode", "Datenschutzmodus aus"),
|
("Out privacy mode", "Datenschutzmodus aus"),
|
||||||
|
("Language", "Sprache"),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -283,5 +283,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Turned off", ""),
|
("Turned off", ""),
|
||||||
("In privacy mode", ""),
|
("In privacy mode", ""),
|
||||||
("Out privacy mode", ""),
|
("Out privacy mode", ""),
|
||||||
|
("Language", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -283,5 +283,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Turned off", "Apagado"),
|
("Turned off", "Apagado"),
|
||||||
("In privacy mode", "En modo de privacidad"),
|
("In privacy mode", "En modo de privacidad"),
|
||||||
("Out privacy mode", "Fuera del modo de privacidad"),
|
("Out privacy mode", "Fuera del modo de privacidad"),
|
||||||
|
("Language", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -283,5 +283,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Turned off", "Éteindre"),
|
("Turned off", "Éteindre"),
|
||||||
("In privacy mode", "en mode privé"),
|
("In privacy mode", "en mode privé"),
|
||||||
("Out privacy mode", "hors mode de confidentialité"),
|
("Out privacy mode", "hors mode de confidentialité"),
|
||||||
|
("Language", "Langue"),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -283,5 +283,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Turned off", "Matikan"),
|
("Turned off", "Matikan"),
|
||||||
("In privacy mode", "Dalam mode privasi"),
|
("In privacy mode", "Dalam mode privasi"),
|
||||||
("Out privacy mode", "Keluar dari mode privasi"),
|
("Out privacy mode", "Keluar dari mode privasi"),
|
||||||
|
("Language", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -283,5 +283,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Turned off", "Spegni"),
|
("Turned off", "Spegni"),
|
||||||
("In privacy mode", "In modalità privacy"),
|
("In privacy mode", "In modalità privacy"),
|
||||||
("Out privacy mode", "Fuori modalità privacy"),
|
("Out privacy mode", "Fuori modalità privacy"),
|
||||||
|
("Language", "Linguaggio"),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -283,5 +283,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Turned off", "Desligado"),
|
("Turned off", "Desligado"),
|
||||||
("In privacy mode", "No modo de privacidade"),
|
("In privacy mode", "No modo de privacidade"),
|
||||||
("Out privacy mode", "Fora do modo de privacidade"),
|
("Out privacy mode", "Fora do modo de privacidade"),
|
||||||
|
("Language", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -283,5 +283,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Turned off", "Выключен"),
|
("Turned off", "Выключен"),
|
||||||
("In privacy mode", "В режиме конфиденциальности"),
|
("In privacy mode", "В режиме конфиденциальности"),
|
||||||
("Out privacy mode", "Выход из режима конфиденциальности"),
|
("Out privacy mode", "Выход из режима конфиденциальности"),
|
||||||
|
("Language", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -283,5 +283,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Turned off", "Vypnutý"),
|
("Turned off", "Vypnutý"),
|
||||||
("In privacy mode", "V režime súkromia"),
|
("In privacy mode", "V režime súkromia"),
|
||||||
("Out privacy mode", "Mimo režimu súkromia"),
|
("Out privacy mode", "Mimo režimu súkromia"),
|
||||||
|
("Language", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -283,5 +283,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Turned off", ""),
|
("Turned off", ""),
|
||||||
("In privacy mode", ""),
|
("In privacy mode", ""),
|
||||||
("Out privacy mode", ""),
|
("Out privacy mode", ""),
|
||||||
|
("Language", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -283,5 +283,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Turned off", "Kapalı"),
|
("Turned off", "Kapalı"),
|
||||||
("In privacy mode", "Gizlilik modunda"),
|
("In privacy mode", "Gizlilik modunda"),
|
||||||
("Out privacy mode", "Gizlilik modu dışında"),
|
("Out privacy mode", "Gizlilik modu dışında"),
|
||||||
|
("Language", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -283,5 +283,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Turned off", "退出"),
|
("Turned off", "退出"),
|
||||||
("In privacy mode", "開啟隱私模式"),
|
("In privacy mode", "開啟隱私模式"),
|
||||||
("Out privacy mode", "退出隱私模式"),
|
("Out privacy mode", "退出隱私模式"),
|
||||||
|
("Language", "語言"),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -1020,6 +1020,22 @@ copy /Y \"{tmp_path}\\Uninstall {app_name}.lnk\" \"{start_menu}\\\"
|
|||||||
// https://docs.microsoft.com/zh-cn/windows/win32/msi/uninstall-registry-key?redirectedfrom=MSDNa
|
// https://docs.microsoft.com/zh-cn/windows/win32/msi/uninstall-registry-key?redirectedfrom=MSDNa
|
||||||
// https://www.windowscentral.com/how-edit-registry-using-command-prompt-windows-10
|
// https://www.windowscentral.com/how-edit-registry-using-command-prompt-windows-10
|
||||||
// https://www.tenforums.com/tutorials/70903-add-remove-allowed-apps-through-windows-firewall-windows-10-a.html
|
// https://www.tenforums.com/tutorials/70903-add-remove-allowed-apps-through-windows-firewall-windows-10-a.html
|
||||||
|
// Note: without if exist, the bat may exit in advance on some Windows7 https://github.com/rustdesk/rustdesk/issues/895
|
||||||
|
let dels = format!(
|
||||||
|
"
|
||||||
|
if exist \"{mk_shortcut}\" del /f /q \"{mk_shortcut}\"
|
||||||
|
if exist \"{uninstall_shortcut}\" del /f /q \"{uninstall_shortcut}\"
|
||||||
|
if exist \"{tray_shortcut}\" del /f /q \"{tray_shortcut}\"
|
||||||
|
if exist \"{tmp_path}\\{app_name}.lnk\" del /f /q \"{tmp_path}\\{app_name}.lnk\"
|
||||||
|
if exist \"{tmp_path}\\Uninstall {app_name}.lnk\" del /f /q \"{tmp_path}\\Uninstall {app_name}.lnk\"
|
||||||
|
if exist \"{tmp_path}\\{app_name} Tray.lnk\" del /f /q \"{tmp_path}\\{app_name} Tray.lnk\"
|
||||||
|
",
|
||||||
|
mk_shortcut = mk_shortcut,
|
||||||
|
uninstall_shortcut = uninstall_shortcut,
|
||||||
|
tray_shortcut = tray_shortcut,
|
||||||
|
tmp_path = tmp_path,
|
||||||
|
app_name = crate::get_app_name(),
|
||||||
|
);
|
||||||
let cmds = format!(
|
let cmds = format!(
|
||||||
"
|
"
|
||||||
{uninstall_str}
|
{uninstall_str}
|
||||||
@ -1048,17 +1064,13 @@ cscript \"{tray_shortcut}\"
|
|||||||
copy /Y \"{tmp_path}\\{app_name} Tray.lnk\" \"%PROGRAMDATA%\\Microsoft\\Windows\\Start Menu\\Programs\\Startup\\\"
|
copy /Y \"{tmp_path}\\{app_name} Tray.lnk\" \"%PROGRAMDATA%\\Microsoft\\Windows\\Start Menu\\Programs\\Startup\\\"
|
||||||
{shortcuts}
|
{shortcuts}
|
||||||
copy /Y \"{tmp_path}\\Uninstall {app_name}.lnk\" \"{path}\\\"
|
copy /Y \"{tmp_path}\\Uninstall {app_name}.lnk\" \"{path}\\\"
|
||||||
del /f \"{mk_shortcut}\"
|
{dels}
|
||||||
del /f \"{uninstall_shortcut}\"
|
|
||||||
del /f \"{tray_shortcut}\"
|
|
||||||
del /f \"{tmp_path}\\{app_name}.lnk\"
|
|
||||||
del /f \"{tmp_path}\\Uninstall {app_name}.lnk\"
|
|
||||||
del /f \"{tmp_path}\\{app_name} Tray.lnk\"
|
|
||||||
sc create {app_name} binpath= \"\\\"{exe}\\\" --import-config \\\"{config_path}\\\"\" start= auto DisplayName= \"{app_name} Service\"
|
sc create {app_name} binpath= \"\\\"{exe}\\\" --import-config \\\"{config_path}\\\"\" start= auto DisplayName= \"{app_name} Service\"
|
||||||
sc start {app_name}
|
sc start {app_name}
|
||||||
sc stop {app_name}
|
sc stop {app_name}
|
||||||
sc delete {app_name}
|
sc delete {app_name}
|
||||||
{after_install}
|
{after_install}
|
||||||
|
{sleep}
|
||||||
",
|
",
|
||||||
uninstall_str=uninstall_str,
|
uninstall_str=uninstall_str,
|
||||||
path=path,
|
path=path,
|
||||||
@ -1081,6 +1093,16 @@ sc delete {app_name}
|
|||||||
config_path=Config::file().to_str().unwrap_or(""),
|
config_path=Config::file().to_str().unwrap_or(""),
|
||||||
lic=register_licence(),
|
lic=register_licence(),
|
||||||
after_install=get_after_install(&exe),
|
after_install=get_after_install(&exe),
|
||||||
|
sleep=if debug {
|
||||||
|
"timeout 300"
|
||||||
|
} else {
|
||||||
|
""
|
||||||
|
},
|
||||||
|
dels=if debug {
|
||||||
|
""
|
||||||
|
} else {
|
||||||
|
&dels
|
||||||
|
},
|
||||||
);
|
);
|
||||||
run_cmds(cmds, debug, "install")?;
|
run_cmds(cmds, debug, "install")?;
|
||||||
std::thread::sleep(std::time::Duration::from_millis(2000));
|
std::thread::sleep(std::time::Duration::from_millis(2000));
|
||||||
@ -1126,10 +1148,10 @@ fn get_uninstall() -> String {
|
|||||||
"
|
"
|
||||||
{before_uninstall}
|
{before_uninstall}
|
||||||
reg delete {subkey} /f
|
reg delete {subkey} /f
|
||||||
rd /s /q \"{path}\"
|
if exist \"{path}\" rd /s /q \"{path}\"
|
||||||
rd /s /q \"{start_menu}\"
|
if exist \"{start_menu}\" rd /s /q \"{start_menu}\"
|
||||||
del /f /q \"%PUBLIC%\\Desktop\\{app_name}*\"
|
if exist \"%PUBLIC%\\Desktop\\{app_name}.lnk\" del /f /q \"%PUBLIC%\\Desktop\\{app_name}.lnk\"
|
||||||
del /f /q \"%PROGRAMDATA%\\Microsoft\\Windows\\Start Menu\\Programs\\Startup\\{app_name} Tray.lnk\"
|
if exist \"%PROGRAMDATA%\\Microsoft\\Windows\\Start Menu\\Programs\\Startup\\{app_name} Tray.lnk\" del /f /q \"%PROGRAMDATA%\\Microsoft\\Windows\\Start Menu\\Programs\\Startup\\{app_name} Tray.lnk\"
|
||||||
",
|
",
|
||||||
before_uninstall=get_before_uninstall(),
|
before_uninstall=get_before_uninstall(),
|
||||||
subkey=subkey,
|
subkey=subkey,
|
||||||
@ -1176,11 +1198,8 @@ fn run_cmds(cmds: String, show: bool, tip: &str) -> ResultType<()> {
|
|||||||
.show(show)
|
.show(show)
|
||||||
.force_prompt(true)
|
.force_prompt(true)
|
||||||
.status();
|
.status();
|
||||||
// leave the file for debug if execution failed
|
if !show {
|
||||||
if let Ok(res) = res {
|
allow_err!(std::fs::remove_file(tmp));
|
||||||
if res.success() {
|
|
||||||
allow_err!(std::fs::remove_file(tmp));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
let _ = res?;
|
let _ = res?;
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -3,9 +3,9 @@ mod cm;
|
|||||||
mod inline;
|
mod inline;
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
mod macos;
|
mod macos;
|
||||||
|
pub mod remote;
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
pub mod win_privacy;
|
pub mod win_privacy;
|
||||||
pub mod remote;
|
|
||||||
use crate::common::SOFTWARE_UPDATE_URL;
|
use crate::common::SOFTWARE_UPDATE_URL;
|
||||||
use crate::ipc;
|
use crate::ipc;
|
||||||
use hbb_common::{
|
use hbb_common::{
|
||||||
@ -760,6 +760,10 @@ impl UI {
|
|||||||
#[cfg(feature = "hwcodec")]
|
#[cfg(feature = "hwcodec")]
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_langs(&self) -> String {
|
||||||
|
crate::lang::LANGS.to_string()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl sciter::EventHandler for UI {
|
impl sciter::EventHandler for UI {
|
||||||
@ -837,6 +841,7 @@ impl sciter::EventHandler for UI {
|
|||||||
fn get_lan_peers();
|
fn get_lan_peers();
|
||||||
fn get_uuid();
|
fn get_uuid();
|
||||||
fn has_hwcodec();
|
fn has_hwcodec();
|
||||||
|
fn get_langs();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -163,6 +163,40 @@ class AudioInputs: Reactor.Component {
|
|||||||
}
|
}
|
||||||
this.toggleMenuState();
|
this.toggleMenuState();
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class Languages: Reactor.Component {
|
||||||
|
function render() {
|
||||||
|
var langs = JSON.parse(handler.get_langs());
|
||||||
|
var me = this;
|
||||||
|
self.timer(1ms, function() { me.toggleMenuState() });
|
||||||
|
return <li>{translate('Language')}
|
||||||
|
<menu #languages key={langs.length}>
|
||||||
|
<li id="default"><span>{svg_checkmark}</span>Default</li>
|
||||||
|
<div .separator />
|
||||||
|
{langs.map(function(lang) {
|
||||||
|
return <li id={lang[0]}><span>{svg_checkmark}</span>{lang[1]}</li>;
|
||||||
|
})}
|
||||||
|
</menu>
|
||||||
|
</li>;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function toggleMenuState() {
|
||||||
|
var cur = handler.get_local_option("lang") || "default";
|
||||||
|
for (var el in this.$$(menu#languages>li)) {
|
||||||
|
var selected = cur == el.id;
|
||||||
|
el.attributes.toggleClass("selected", selected);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
event click $(menu#languages>li) (_, me) {
|
||||||
|
var v = me.id;
|
||||||
|
if (v == "default") v = "";
|
||||||
|
handler.set_local_option("lang", v);
|
||||||
|
app.update();
|
||||||
|
this.toggleMenuState();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var enhancementsMenu;
|
var enhancementsMenu;
|
||||||
@ -202,7 +236,6 @@ class Enhancements: Reactor.Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function getUserName() {
|
function getUserName() {
|
||||||
try {
|
try {
|
||||||
return JSON.parse(handler.get_local_option("user_info")).name;
|
return JSON.parse(handler.get_local_option("user_info")).name;
|
||||||
@ -261,7 +294,7 @@ class MyIdMenu: Reactor.Component {
|
|||||||
{handler.is_ok_change_id() && key_confirmed ? <li #change-id>{translate('Change ID')}</li> : ""}
|
{handler.is_ok_change_id() && key_confirmed ? <li #change-id>{translate('Change ID')}</li> : ""}
|
||||||
<div .separator />
|
<div .separator />
|
||||||
<li #allow-darktheme><span>{svg_checkmark}</span>{translate('Dark Theme')}</li>
|
<li #allow-darktheme><span>{svg_checkmark}</span>{translate('Dark Theme')}</li>
|
||||||
<div .separator />
|
<Languages />
|
||||||
<li #about>{translate('About')} {" "}{handler.get_app_name()}</li>
|
<li #about>{translate('About')} {" "}{handler.get_app_name()}</li>
|
||||||
</menu>
|
</menu>
|
||||||
</popup>;
|
</popup>;
|
||||||
|
@ -2001,11 +2001,11 @@ impl Remote {
|
|||||||
let mut config: PeerConfig = self.handler.load_config();
|
let mut config: PeerConfig = self.handler.load_config();
|
||||||
let mut transfer_metas = TransferSerde::default();
|
let mut transfer_metas = TransferSerde::default();
|
||||||
for job in self.read_jobs.iter() {
|
for job in self.read_jobs.iter() {
|
||||||
let json_str = serde_json::to_string(&job.gen_meta()).unwrap();
|
let json_str = serde_json::to_string(&job.gen_meta()).unwrap_or_default();
|
||||||
transfer_metas.read_jobs.push(json_str);
|
transfer_metas.read_jobs.push(json_str);
|
||||||
}
|
}
|
||||||
for job in self.write_jobs.iter() {
|
for job in self.write_jobs.iter() {
|
||||||
let json_str = serde_json::to_string(&job.gen_meta()).unwrap();
|
let json_str = serde_json::to_string(&job.gen_meta()).unwrap_or_default();
|
||||||
transfer_metas.write_jobs.push(json_str);
|
transfer_metas.write_jobs.push(json_str);
|
||||||
}
|
}
|
||||||
log::info!("meta: {:?}", transfer_metas);
|
log::info!("meta: {:?}", transfer_metas);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user