Merge branch 'master' into modern-menubar
This commit is contained in:
commit
84f97fbf1f
3
Cargo.lock
generated
3
Cargo.lock
generated
@ -4554,12 +4554,13 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "rdev"
|
name = "rdev"
|
||||||
version = "0.5.0-2"
|
version = "0.5.0-2"
|
||||||
source = "git+https://github.com/fufesou/rdev#cedc4e62744566775026af4b434ef799804c1130"
|
source = "git+https://github.com/fufesou/rdev#5b9fb5e42117f44e0ce0fe7cf2bddf270c75f1dc"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cocoa",
|
"cocoa",
|
||||||
"core-foundation 0.9.3",
|
"core-foundation 0.9.3",
|
||||||
"core-foundation-sys 0.8.3",
|
"core-foundation-sys 0.8.3",
|
||||||
"core-graphics 0.22.3",
|
"core-graphics 0.22.3",
|
||||||
|
"dispatch",
|
||||||
"enum-map",
|
"enum-map",
|
||||||
"epoll",
|
"epoll",
|
||||||
"inotify",
|
"inotify",
|
||||||
|
@ -50,6 +50,18 @@ const int kMobileMaxDisplayHeight = 1280;
|
|||||||
const int kDesktopMaxDisplayWidth = 1920;
|
const int kDesktopMaxDisplayWidth = 1920;
|
||||||
const int kDesktopMaxDisplayHeight = 1080;
|
const int kDesktopMaxDisplayHeight = 1080;
|
||||||
|
|
||||||
|
const double kDesktopFileTransferNameColWidth = 200;
|
||||||
|
const double kDesktopFileTransferModifiedColWidth = 120;
|
||||||
|
const double kDesktopFileTransferRowHeight = 25.0;
|
||||||
|
const double kDesktopFileTransferHeaderHeight = 25.0;
|
||||||
|
|
||||||
|
// https://en.wikipedia.org/wiki/Non-breaking_space
|
||||||
|
const int $nbsp = 0x00A0;
|
||||||
|
|
||||||
|
extension StringExtension on String {
|
||||||
|
String get nonBreaking => replaceAll(' ', String.fromCharCode($nbsp));
|
||||||
|
}
|
||||||
|
|
||||||
const Size kConnectionManagerWindowSize = Size(300, 400);
|
const Size kConnectionManagerWindowSize = Size(300, 400);
|
||||||
// Tabbar transition duration, now we remove the duration
|
// Tabbar transition duration, now we remove the duration
|
||||||
const Duration kTabTransitionDuration = Duration.zero;
|
const Duration kTabTransitionDuration = Duration.zero;
|
||||||
|
@ -236,10 +236,7 @@ class _FileManagerPageState extends State<FileManagerPage>
|
|||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
child: SingleChildScrollView(
|
child: _buildFileList(context, isLocal, scrollController),
|
||||||
controller: scrollController,
|
|
||||||
child: _buildDataTable(context, isLocal, scrollController),
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
)),
|
)),
|
||||||
@ -248,25 +245,11 @@ class _FileManagerPageState extends State<FileManagerPage>
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildDataTable(
|
Widget _buildFileList(
|
||||||
BuildContext context, bool isLocal, ScrollController scrollController) {
|
BuildContext context, bool isLocal, ScrollController scrollController) {
|
||||||
const rowHeight = 25.0;
|
|
||||||
final fd = model.getCurrentDir(isLocal);
|
final fd = model.getCurrentDir(isLocal);
|
||||||
final entries = fd.entries;
|
final entries = fd.entries;
|
||||||
final sortIndex = (SortBy style) {
|
final selectedEntries = getSelectedItems(isLocal);
|
||||||
switch (style) {
|
|
||||||
case SortBy.name:
|
|
||||||
return 0;
|
|
||||||
case SortBy.type:
|
|
||||||
return 0;
|
|
||||||
case SortBy.modified:
|
|
||||||
return 1;
|
|
||||||
case SortBy.size:
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
}(model.getSortStyle(isLocal));
|
|
||||||
final sortAscending =
|
|
||||||
isLocal ? model.localSortAscending : model.remoteSortAscending;
|
|
||||||
|
|
||||||
return MouseRegion(
|
return MouseRegion(
|
||||||
onEnter: (evt) {
|
onEnter: (evt) {
|
||||||
@ -287,7 +270,6 @@ class _FileManagerPageState extends State<FileManagerPage>
|
|||||||
onNext: (buffer) {
|
onNext: (buffer) {
|
||||||
debugPrint("searching next for $buffer");
|
debugPrint("searching next for $buffer");
|
||||||
assert(buffer.length == 1);
|
assert(buffer.length == 1);
|
||||||
final selectedEntries = getSelectedItems(isLocal);
|
|
||||||
assert(selectedEntries.length <= 1);
|
assert(selectedEntries.length <= 1);
|
||||||
var skipCount = 0;
|
var skipCount = 0;
|
||||||
if (selectedEntries.items.isNotEmpty) {
|
if (selectedEntries.items.isNotEmpty) {
|
||||||
@ -312,7 +294,8 @@ class _FileManagerPageState extends State<FileManagerPage>
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_jumpToEntry(
|
_jumpToEntry(
|
||||||
isLocal, searchResult.first, scrollController, rowHeight, buffer);
|
isLocal, searchResult.first, scrollController,
|
||||||
|
kDesktopFileTransferRowHeight, buffer);
|
||||||
},
|
},
|
||||||
onSearch: (buffer) {
|
onSearch: (buffer) {
|
||||||
debugPrint("searching for $buffer");
|
debugPrint("searching for $buffer");
|
||||||
@ -327,7 +310,8 @@ class _FileManagerPageState extends State<FileManagerPage>
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_jumpToEntry(
|
_jumpToEntry(
|
||||||
isLocal, searchResult.first, scrollController, rowHeight, buffer);
|
isLocal, searchResult.first, scrollController,
|
||||||
|
kDesktopFileTransferRowHeight, buffer);
|
||||||
},
|
},
|
||||||
child: ObxValue<RxString>(
|
child: ObxValue<RxString>(
|
||||||
(searchText) {
|
(searchText) {
|
||||||
@ -336,118 +320,120 @@ class _FileManagerPageState extends State<FileManagerPage>
|
|||||||
return element.name.contains(searchText.value);
|
return element.name.contains(searchText.value);
|
||||||
}).toList(growable: false)
|
}).toList(growable: false)
|
||||||
: entries;
|
: entries;
|
||||||
return DataTable(
|
final rows = filteredEntries.map((entry) {
|
||||||
key: ValueKey(isLocal ? 0 : 1),
|
|
||||||
showCheckboxColumn: false,
|
|
||||||
dataRowHeight: rowHeight,
|
|
||||||
headingRowHeight: 30,
|
|
||||||
horizontalMargin: 8,
|
|
||||||
columnSpacing: 8,
|
|
||||||
showBottomBorder: true,
|
|
||||||
sortColumnIndex: sortIndex,
|
|
||||||
sortAscending: sortAscending,
|
|
||||||
columns: [
|
|
||||||
DataColumn(
|
|
||||||
label: Text(
|
|
||||||
translate("Name"),
|
|
||||||
).marginSymmetric(horizontal: 4),
|
|
||||||
onSort: (columnIndex, ascending) {
|
|
||||||
model.changeSortStyle(SortBy.name,
|
|
||||||
isLocal: isLocal, ascending: ascending);
|
|
||||||
}),
|
|
||||||
DataColumn(
|
|
||||||
label: Text(
|
|
||||||
translate("Modified"),
|
|
||||||
),
|
|
||||||
onSort: (columnIndex, ascending) {
|
|
||||||
model.changeSortStyle(SortBy.modified,
|
|
||||||
isLocal: isLocal, ascending: ascending);
|
|
||||||
}),
|
|
||||||
DataColumn(
|
|
||||||
label: Text(translate("Size")),
|
|
||||||
onSort: (columnIndex, ascending) {
|
|
||||||
model.changeSortStyle(SortBy.size,
|
|
||||||
isLocal: isLocal, ascending: ascending);
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
rows: filteredEntries.map((entry) {
|
|
||||||
final sizeStr =
|
final sizeStr =
|
||||||
entry.isFile ? readableFileSize(entry.size.toDouble()) : "";
|
entry.isFile ? readableFileSize(entry.size.toDouble()) : "";
|
||||||
final lastModifiedStr = entry.isDrive
|
final lastModifiedStr = entry.isDrive
|
||||||
? " "
|
? " "
|
||||||
: "${entry.lastModified().toString().replaceAll(".000", "")} ";
|
: "${entry.lastModified().toString().replaceAll(".000", "")} ";
|
||||||
return DataRow(
|
final isSelected = selectedEntries.contains(entry);
|
||||||
key: ValueKey(entry.name),
|
return SizedBox(
|
||||||
onSelectChanged: (s) {
|
key: ValueKey(entry.name),
|
||||||
_onSelectedChanged(getSelectedItems(isLocal),
|
height: kDesktopFileTransferRowHeight,
|
||||||
filteredEntries, entry, isLocal);
|
child: Column(
|
||||||
},
|
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||||
selected: getSelectedItems(isLocal).contains(entry),
|
children: [
|
||||||
cells: [
|
const Divider(
|
||||||
DataCell(
|
height: 1,
|
||||||
Container(
|
),
|
||||||
width: 200,
|
Expanded(
|
||||||
child: Tooltip(
|
child: Ink(
|
||||||
waitDuration: Duration(milliseconds: 500),
|
decoration: isSelected
|
||||||
message: entry.name,
|
? BoxDecoration(color: Theme.of(context).hoverColor)
|
||||||
child: Row(children: [
|
: null,
|
||||||
entry.isDrive
|
child: InkWell(
|
||||||
? Image(
|
child: Row(children: [
|
||||||
image: iconHardDrive,
|
GestureDetector(
|
||||||
fit: BoxFit.scaleDown,
|
child: Container(
|
||||||
color: Theme.of(context)
|
width: kDesktopFileTransferNameColWidth,
|
||||||
.iconTheme
|
child: Tooltip(
|
||||||
.color
|
waitDuration: Duration(milliseconds: 500),
|
||||||
?.withOpacity(0.7))
|
message: entry.name,
|
||||||
.paddingAll(4)
|
child: Row(children: [
|
||||||
: Icon(
|
entry.isDrive
|
||||||
entry.isFile
|
? Image(
|
||||||
? Icons.feed_outlined
|
image: iconHardDrive,
|
||||||
: Icons.folder,
|
fit: BoxFit.scaleDown,
|
||||||
size: 20,
|
color: Theme.of(context)
|
||||||
color: Theme.of(context)
|
.iconTheme
|
||||||
.iconTheme
|
.color
|
||||||
.color
|
?.withOpacity(0.7))
|
||||||
?.withOpacity(0.7),
|
.paddingAll(4)
|
||||||
).marginSymmetric(horizontal: 2),
|
: Icon(
|
||||||
Expanded(
|
entry.isFile
|
||||||
child: Text(entry.name,
|
? Icons.feed_outlined
|
||||||
overflow: TextOverflow.ellipsis))
|
: Icons.folder,
|
||||||
]),
|
size: 20,
|
||||||
|
color: Theme.of(context)
|
||||||
|
.iconTheme
|
||||||
|
.color
|
||||||
|
?.withOpacity(0.7),
|
||||||
|
).marginSymmetric(horizontal: 2),
|
||||||
|
Expanded(
|
||||||
|
child: Text(entry.name.nonBreaking,
|
||||||
|
overflow: TextOverflow.ellipsis))
|
||||||
|
]),
|
||||||
|
)),
|
||||||
|
onTap: () {
|
||||||
|
final items = getSelectedItems(isLocal);
|
||||||
|
// handle double click
|
||||||
|
if (_checkDoubleClick(entry)) {
|
||||||
|
openDirectory(entry.path, isLocal: isLocal);
|
||||||
|
items.clear();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_onSelectedChanged(
|
||||||
|
items, filteredEntries, entry, isLocal);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
GestureDetector(
|
||||||
|
child: SizedBox(
|
||||||
|
width: kDesktopFileTransferModifiedColWidth,
|
||||||
|
child: Tooltip(
|
||||||
|
waitDuration: Duration(milliseconds: 500),
|
||||||
|
message: lastModifiedStr,
|
||||||
|
child: Text(
|
||||||
|
lastModifiedStr,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 12, color: MyTheme.darkGray),
|
||||||
|
)),
|
||||||
)),
|
)),
|
||||||
onTap: () {
|
GestureDetector(
|
||||||
final items = getSelectedItems(isLocal);
|
child: Tooltip(
|
||||||
|
waitDuration: Duration(milliseconds: 500),
|
||||||
// handle double click
|
message: sizeStr,
|
||||||
if (_checkDoubleClick(entry)) {
|
child: Text(
|
||||||
openDirectory(entry.path, isLocal: isLocal);
|
sizeStr,
|
||||||
items.clear();
|
overflow: TextOverflow.ellipsis,
|
||||||
return;
|
style: TextStyle(
|
||||||
}
|
fontSize: 10,
|
||||||
_onSelectedChanged(
|
color: MyTheme.darkGray),
|
||||||
items, filteredEntries, entry, isLocal);
|
))),
|
||||||
},
|
]),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
DataCell(FittedBox(
|
),
|
||||||
child: Tooltip(
|
],
|
||||||
waitDuration: Duration(milliseconds: 500),
|
),
|
||||||
message: lastModifiedStr,
|
);
|
||||||
child: Text(
|
}).toList(growable: false);
|
||||||
lastModifiedStr,
|
|
||||||
style: TextStyle(
|
return Column(
|
||||||
fontSize: 12, color: MyTheme.darkGray),
|
children: [
|
||||||
)))),
|
// Header
|
||||||
DataCell(Tooltip(
|
_buildFileBrowserHeader(context, isLocal),
|
||||||
waitDuration: Duration(milliseconds: 500),
|
// Body
|
||||||
message: sizeStr,
|
Expanded(
|
||||||
child: Text(
|
child: ListView.builder(
|
||||||
sizeStr,
|
controller: scrollController,
|
||||||
overflow: TextOverflow.ellipsis,
|
itemExtent: kDesktopFileTransferRowHeight,
|
||||||
style: TextStyle(
|
itemBuilder: (context, index) {
|
||||||
fontSize: 10, color: MyTheme.darkGray),
|
return rows[index];
|
||||||
))),
|
},
|
||||||
]);
|
itemCount: rows.length,
|
||||||
}).toList(growable: false),
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
isLocal ? _searchTextLocal : _searchTextRemote,
|
isLocal ? _searchTextLocal : _searchTextRemote,
|
||||||
@ -1133,4 +1119,60 @@ class _FileManagerPageState extends State<FileManagerPage>
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget headerItemFunc(
|
||||||
|
double? width, SortBy sortBy, String name, bool isLocal) {
|
||||||
|
final headerTextStyle =
|
||||||
|
Theme.of(context).dataTableTheme.headingTextStyle ?? TextStyle();
|
||||||
|
return ObxValue<Rx<bool?>>(
|
||||||
|
(ascending) => InkWell(
|
||||||
|
onTap: () {
|
||||||
|
if (ascending.value == null) {
|
||||||
|
ascending.value = true;
|
||||||
|
} else {
|
||||||
|
ascending.value = !ascending.value!;
|
||||||
|
}
|
||||||
|
model.changeSortStyle(sortBy,
|
||||||
|
isLocal: isLocal, ascending: ascending.value!);
|
||||||
|
},
|
||||||
|
child: SizedBox(
|
||||||
|
width: width,
|
||||||
|
height: kDesktopFileTransferHeaderHeight,
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
name,
|
||||||
|
style: headerTextStyle,
|
||||||
|
).marginSymmetric(
|
||||||
|
horizontal: sortBy == SortBy.name ? 4 : 0.0),
|
||||||
|
ascending.value != null
|
||||||
|
? Icon(ascending.value!
|
||||||
|
? Icons.arrow_upward
|
||||||
|
: Icons.arrow_downward)
|
||||||
|
: const Offstage()
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
), () {
|
||||||
|
if (model.getSortStyle(isLocal) == sortBy) {
|
||||||
|
return model.getSortAscending(isLocal).obs;
|
||||||
|
} else {
|
||||||
|
return Rx<bool?>(null);
|
||||||
|
}
|
||||||
|
}());
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildFileBrowserHeader(BuildContext context, bool isLocal) {
|
||||||
|
return Row(
|
||||||
|
children: [
|
||||||
|
headerItemFunc(kDesktopFileTransferNameColWidth, SortBy.name,
|
||||||
|
translate("Name"), isLocal),
|
||||||
|
headerItemFunc(kDesktopFileTransferModifiedColWidth, SortBy.modified,
|
||||||
|
translate("Modified"), isLocal),
|
||||||
|
Expanded(
|
||||||
|
child:
|
||||||
|
headerItemFunc(null, SortBy.size, translate("Size"), isLocal))
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -447,13 +447,16 @@ class _RemoteMenubarState extends State<RemoteMenubar> {
|
|||||||
bottom: Radius.circular(10),
|
bottom: Radius.circular(10),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
child: Row(
|
child: SingleChildScrollView(
|
||||||
mainAxisSize: MainAxisSize.min,
|
scrollDirection: Axis.horizontal,
|
||||||
children: [
|
child: Row(
|
||||||
SizedBox(width: 2.5),
|
mainAxisSize: MainAxisSize.min,
|
||||||
...menubarItems,
|
children: [
|
||||||
SizedBox(width: 2.5)
|
SizedBox(width: 2.5),
|
||||||
],
|
...menubarItems,
|
||||||
|
SizedBox(width: 2.5)
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
_buildDraggableShowHide(context),
|
_buildDraggableShowHide(context),
|
||||||
@ -1513,13 +1516,16 @@ class _RemoteMenubarState extends State<RemoteMenubar> {
|
|||||||
if (bind.sessionIsKeyboardModeSupported(
|
if (bind.sessionIsKeyboardModeSupported(
|
||||||
id: widget.id, mode: mode.key)) {
|
id: widget.id, mode: mode.key)) {
|
||||||
if (mode.key == 'translate') {
|
if (mode.key == 'translate') {
|
||||||
if (!Platform.isWindows ||
|
if (Platform.isLinux ||
|
||||||
widget.ffi.ffiModel.pi.platform != kPeerPlatformWindows) {
|
widget.ffi.ffiModel.pi.platform == kPeerPlatformLinux) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
list.add(MenuEntryRadioOption(
|
var text = translate(mode.menu);
|
||||||
text: translate(mode.menu), value: mode.key));
|
if (mode.key == 'translate') {
|
||||||
|
text = '$text beta';
|
||||||
|
}
|
||||||
|
list.add(MenuEntryRadioOption(text: text, value: mode.key));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return list;
|
return list;
|
||||||
|
@ -75,6 +75,10 @@ class FileModel extends ChangeNotifier {
|
|||||||
return isLocal ? _localSortStyle : _remoteSortStyle;
|
return isLocal ? _localSortStyle : _remoteSortStyle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool getSortAscending(bool isLocal) {
|
||||||
|
return isLocal ? _localSortAscending : _remoteSortAscending;
|
||||||
|
}
|
||||||
|
|
||||||
FileDirectory _currentLocalDir = FileDirectory();
|
FileDirectory _currentLocalDir = FileDirectory();
|
||||||
|
|
||||||
FileDirectory get currentLocalDir => _currentLocalDir;
|
FileDirectory get currentLocalDir => _currentLocalDir;
|
||||||
|
@ -438,15 +438,17 @@ class ImageModel with ChangeNotifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final pid = parent.target?.id;
|
final pid = parent.target?.id;
|
||||||
ui.decodeImageFromPixels(
|
img.decodeImageFromPixels(
|
||||||
rgba,
|
rgba,
|
||||||
parent.target?.ffiModel.display.width ?? 0,
|
parent.target?.ffiModel.display.width ?? 0,
|
||||||
parent.target?.ffiModel.display.height ?? 0,
|
parent.target?.ffiModel.display.height ?? 0,
|
||||||
isWeb ? ui.PixelFormat.rgba8888 : ui.PixelFormat.bgra8888, (image) {
|
isWeb ? ui.PixelFormat.rgba8888 : ui.PixelFormat.bgra8888,
|
||||||
|
onPixelsCopied: () {
|
||||||
|
// Unlock the rgba memory from rust codes.
|
||||||
|
platformFFI.nextRgba(id);
|
||||||
|
}).then((image) {
|
||||||
if (parent.target?.id != pid) return;
|
if (parent.target?.id != pid) return;
|
||||||
try {
|
try {
|
||||||
// Unlock the rgba memory from rust codes.
|
|
||||||
platformFFI.nextRgba(id);
|
|
||||||
// my throw exception, because the listener maybe already dispose
|
// my throw exception, because the listener maybe already dispose
|
||||||
update(image);
|
update(image);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
@ -11,6 +11,7 @@ Future<ui.Image> decodeImageFromPixels(
|
|||||||
int? rowBytes,
|
int? rowBytes,
|
||||||
int? targetWidth,
|
int? targetWidth,
|
||||||
int? targetHeight,
|
int? targetHeight,
|
||||||
|
VoidCallback? onPixelsCopied,
|
||||||
bool allowUpscaling = true,
|
bool allowUpscaling = true,
|
||||||
}) async {
|
}) async {
|
||||||
if (targetWidth != null) {
|
if (targetWidth != null) {
|
||||||
@ -22,6 +23,7 @@ Future<ui.Image> decodeImageFromPixels(
|
|||||||
|
|
||||||
final ui.ImmutableBuffer buffer =
|
final ui.ImmutableBuffer buffer =
|
||||||
await ui.ImmutableBuffer.fromUint8List(pixels);
|
await ui.ImmutableBuffer.fromUint8List(pixels);
|
||||||
|
onPixelsCopied?.call();
|
||||||
final ui.ImageDescriptor descriptor = ui.ImageDescriptor.raw(
|
final ui.ImageDescriptor descriptor = ui.ImageDescriptor.raw(
|
||||||
buffer,
|
buffer,
|
||||||
width: width,
|
width: width,
|
||||||
|
@ -39,7 +39,7 @@ fn mouse_event(flags: u32, data: u32, dx: i32, dy: i32) -> DWORD {
|
|||||||
unsafe { SendInput(1, &mut input as LPINPUT, size_of::<INPUT>() as c_int) }
|
unsafe { SendInput(1, &mut input as LPINPUT, size_of::<INPUT>() as c_int) }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn keybd_event(flags: u32, vk: u16, scan: u16) -> DWORD {
|
fn keybd_event(mut flags: u32, vk: u16, scan: u16) -> DWORD {
|
||||||
let mut scan = scan;
|
let mut scan = scan;
|
||||||
unsafe {
|
unsafe {
|
||||||
// https://github.com/rustdesk/rustdesk/issues/366
|
// https://github.com/rustdesk/rustdesk/issues/366
|
||||||
@ -52,35 +52,33 @@ fn keybd_event(flags: u32, vk: u16, scan: u16) -> DWORD {
|
|||||||
scan = MapVirtualKeyExW(vk as _, 0, LAYOUT) as _;
|
scan = MapVirtualKeyExW(vk as _, 0, LAYOUT) as _;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let mut input: INPUT = unsafe { std::mem::MaybeUninit::zeroed().assume_init() };
|
|
||||||
input.type_ = INPUT_KEYBOARD;
|
|
||||||
unsafe {
|
|
||||||
let dst_ptr = (&mut input.u as *mut _) as *mut u8;
|
|
||||||
let flags = match vk as _ {
|
|
||||||
winapi::um::winuser::VK_HOME |
|
|
||||||
winapi::um::winuser::VK_UP |
|
|
||||||
winapi::um::winuser::VK_PRIOR |
|
|
||||||
winapi::um::winuser::VK_LEFT |
|
|
||||||
winapi::um::winuser::VK_RIGHT |
|
|
||||||
winapi::um::winuser::VK_END |
|
|
||||||
winapi::um::winuser::VK_DOWN |
|
|
||||||
winapi::um::winuser::VK_NEXT |
|
|
||||||
winapi::um::winuser::VK_INSERT |
|
|
||||||
winapi::um::winuser::VK_DELETE => flags | winapi::um::winuser::KEYEVENTF_EXTENDEDKEY,
|
|
||||||
_ => flags,
|
|
||||||
};
|
|
||||||
|
|
||||||
let k = KEYBDINPUT {
|
if flags & KEYEVENTF_UNICODE == 0 {
|
||||||
|
if scan >> 8 == 0xE0 || scan >> 8 == 0xE1 {
|
||||||
|
flags |= winapi::um::winuser::KEYEVENTF_EXTENDEDKEY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let mut union: INPUT_u = unsafe { std::mem::zeroed() };
|
||||||
|
unsafe {
|
||||||
|
*union.ki_mut() = KEYBDINPUT {
|
||||||
wVk: vk,
|
wVk: vk,
|
||||||
wScan: scan,
|
wScan: scan,
|
||||||
dwFlags: flags,
|
dwFlags: flags,
|
||||||
time: 0,
|
time: 0,
|
||||||
dwExtraInfo: ENIGO_INPUT_EXTRA_VALUE,
|
dwExtraInfo: ENIGO_INPUT_EXTRA_VALUE,
|
||||||
};
|
};
|
||||||
let src_ptr = (&k as *const _) as *const u8;
|
|
||||||
std::ptr::copy_nonoverlapping(src_ptr, dst_ptr, size_of::<KEYBDINPUT>());
|
|
||||||
}
|
}
|
||||||
unsafe { SendInput(1, &mut input as LPINPUT, size_of::<INPUT>() as c_int) }
|
let mut inputs = [INPUT {
|
||||||
|
type_: INPUT_KEYBOARD,
|
||||||
|
u: union,
|
||||||
|
}; 1];
|
||||||
|
unsafe {
|
||||||
|
SendInput(
|
||||||
|
inputs.len() as UINT,
|
||||||
|
inputs.as_mut_ptr(),
|
||||||
|
size_of::<INPUT>() as c_int,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_error() -> String {
|
fn get_error() -> String {
|
||||||
|
@ -201,6 +201,8 @@ message KeyEvent {
|
|||||||
bool press = 2;
|
bool press = 2;
|
||||||
oneof union {
|
oneof union {
|
||||||
ControlKey control_key = 3;
|
ControlKey control_key = 3;
|
||||||
|
// high word, sym key code. win: virtual-key code, linux: keysym ?, macos:
|
||||||
|
// low word, position key code. win: scancode, linux: key code, macos: key code
|
||||||
uint32 chr = 4;
|
uint32 chr = 4;
|
||||||
uint32 unicode = 5;
|
uint32 unicode = 5;
|
||||||
string seq = 6;
|
string seq = 6;
|
||||||
|
@ -56,8 +56,74 @@ InstallDir "$PROGRAMFILES64\${PRODUCT_NAME}"
|
|||||||
####################################################################
|
####################################################################
|
||||||
# Language
|
# Language
|
||||||
|
|
||||||
!insertmacro MUI_LANGUAGE "English"
|
!insertmacro MUI_LANGUAGE "English" ; The first language is the default language
|
||||||
|
!insertmacro MUI_LANGUAGE "French"
|
||||||
|
!insertmacro MUI_LANGUAGE "German"
|
||||||
|
!insertmacro MUI_LANGUAGE "Spanish"
|
||||||
|
!insertmacro MUI_LANGUAGE "SpanishInternational"
|
||||||
!insertmacro MUI_LANGUAGE "SimpChinese"
|
!insertmacro MUI_LANGUAGE "SimpChinese"
|
||||||
|
!insertmacro MUI_LANGUAGE "TradChinese"
|
||||||
|
!insertmacro MUI_LANGUAGE "Japanese"
|
||||||
|
!insertmacro MUI_LANGUAGE "Korean"
|
||||||
|
!insertmacro MUI_LANGUAGE "Italian"
|
||||||
|
!insertmacro MUI_LANGUAGE "Dutch"
|
||||||
|
!insertmacro MUI_LANGUAGE "Danish"
|
||||||
|
!insertmacro MUI_LANGUAGE "Swedish"
|
||||||
|
!insertmacro MUI_LANGUAGE "Norwegian"
|
||||||
|
!insertmacro MUI_LANGUAGE "NorwegianNynorsk"
|
||||||
|
!insertmacro MUI_LANGUAGE "Finnish"
|
||||||
|
!insertmacro MUI_LANGUAGE "Greek"
|
||||||
|
!insertmacro MUI_LANGUAGE "Russian"
|
||||||
|
!insertmacro MUI_LANGUAGE "Portuguese"
|
||||||
|
!insertmacro MUI_LANGUAGE "PortugueseBR"
|
||||||
|
!insertmacro MUI_LANGUAGE "Polish"
|
||||||
|
!insertmacro MUI_LANGUAGE "Ukrainian"
|
||||||
|
!insertmacro MUI_LANGUAGE "Czech"
|
||||||
|
!insertmacro MUI_LANGUAGE "Slovak"
|
||||||
|
!insertmacro MUI_LANGUAGE "Croatian"
|
||||||
|
!insertmacro MUI_LANGUAGE "Bulgarian"
|
||||||
|
!insertmacro MUI_LANGUAGE "Hungarian"
|
||||||
|
!insertmacro MUI_LANGUAGE "Thai"
|
||||||
|
!insertmacro MUI_LANGUAGE "Romanian"
|
||||||
|
!insertmacro MUI_LANGUAGE "Latvian"
|
||||||
|
!insertmacro MUI_LANGUAGE "Macedonian"
|
||||||
|
!insertmacro MUI_LANGUAGE "Estonian"
|
||||||
|
!insertmacro MUI_LANGUAGE "Turkish"
|
||||||
|
!insertmacro MUI_LANGUAGE "Lithuanian"
|
||||||
|
!insertmacro MUI_LANGUAGE "Slovenian"
|
||||||
|
!insertmacro MUI_LANGUAGE "Serbian"
|
||||||
|
!insertmacro MUI_LANGUAGE "SerbianLatin"
|
||||||
|
!insertmacro MUI_LANGUAGE "Arabic"
|
||||||
|
!insertmacro MUI_LANGUAGE "Farsi"
|
||||||
|
!insertmacro MUI_LANGUAGE "Hebrew"
|
||||||
|
!insertmacro MUI_LANGUAGE "Indonesian"
|
||||||
|
!insertmacro MUI_LANGUAGE "Mongolian"
|
||||||
|
!insertmacro MUI_LANGUAGE "Luxembourgish"
|
||||||
|
!insertmacro MUI_LANGUAGE "Albanian"
|
||||||
|
!insertmacro MUI_LANGUAGE "Breton"
|
||||||
|
!insertmacro MUI_LANGUAGE "Belarusian"
|
||||||
|
!insertmacro MUI_LANGUAGE "Icelandic"
|
||||||
|
!insertmacro MUI_LANGUAGE "Malay"
|
||||||
|
!insertmacro MUI_LANGUAGE "Bosnian"
|
||||||
|
!insertmacro MUI_LANGUAGE "Kurdish"
|
||||||
|
!insertmacro MUI_LANGUAGE "Irish"
|
||||||
|
!insertmacro MUI_LANGUAGE "Uzbek"
|
||||||
|
!insertmacro MUI_LANGUAGE "Galician"
|
||||||
|
!insertmacro MUI_LANGUAGE "Afrikaans"
|
||||||
|
!insertmacro MUI_LANGUAGE "Catalan"
|
||||||
|
!insertmacro MUI_LANGUAGE "Esperanto"
|
||||||
|
!insertmacro MUI_LANGUAGE "Asturian"
|
||||||
|
!insertmacro MUI_LANGUAGE "Basque"
|
||||||
|
!insertmacro MUI_LANGUAGE "Pashto"
|
||||||
|
!insertmacro MUI_LANGUAGE "ScotsGaelic"
|
||||||
|
!insertmacro MUI_LANGUAGE "Georgian"
|
||||||
|
!insertmacro MUI_LANGUAGE "Vietnamese"
|
||||||
|
!insertmacro MUI_LANGUAGE "Welsh"
|
||||||
|
!insertmacro MUI_LANGUAGE "Armenian"
|
||||||
|
!insertmacro MUI_LANGUAGE "Corsican"
|
||||||
|
!insertmacro MUI_LANGUAGE "Tatar"
|
||||||
|
!insertmacro MUI_LANGUAGE "Hindi"
|
||||||
|
|
||||||
|
|
||||||
####################################################################
|
####################################################################
|
||||||
# Sections
|
# Sections
|
||||||
|
@ -20,7 +20,6 @@ use std::{
|
|||||||
os::raw::c_char,
|
os::raw::c_char,
|
||||||
str::FromStr,
|
str::FromStr,
|
||||||
};
|
};
|
||||||
use crate::ui_session_interface::InvokeUiSession;
|
|
||||||
|
|
||||||
// use crate::hbbs_http::account::AuthResult;
|
// use crate::hbbs_http::account::AuthResult;
|
||||||
|
|
||||||
|
129
src/keyboard.rs
129
src/keyboard.rs
@ -18,6 +18,13 @@ use std::{
|
|||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
static mut IS_ALT_GR: bool = false;
|
static mut IS_ALT_GR: bool = false;
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
const OS_LOWER_WINDOWS: &str = "windows";
|
||||||
|
#[allow(dead_code)]
|
||||||
|
const OS_LOWER_LINUX: &str = "linux";
|
||||||
|
#[allow(dead_code)]
|
||||||
|
const OS_LOWER_MACOS: &str = "macos";
|
||||||
|
|
||||||
#[cfg(any(target_os = "windows", target_os = "macos"))]
|
#[cfg(any(target_os = "windows", target_os = "macos"))]
|
||||||
static KEYBOARD_HOOKED: AtomicBool = AtomicBool::new(false);
|
static KEYBOARD_HOOKED: AtomicBool = AtomicBool::new(false);
|
||||||
|
|
||||||
@ -202,6 +209,9 @@ pub fn update_grab_get_key_name() {
|
|||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
static mut IS_0X021D_DOWN: bool = false;
|
static mut IS_0X021D_DOWN: bool = false;
|
||||||
|
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
static mut IS_LEFT_OPTION_DOWN: bool = false;
|
||||||
|
|
||||||
pub fn start_grab_loop() {
|
pub fn start_grab_loop() {
|
||||||
#[cfg(any(target_os = "windows", target_os = "macos"))]
|
#[cfg(any(target_os = "windows", target_os = "macos"))]
|
||||||
std::thread::spawn(move || {
|
std::thread::spawn(move || {
|
||||||
@ -213,6 +223,7 @@ pub fn start_grab_loop() {
|
|||||||
|
|
||||||
let mut _keyboard_mode = KeyboardMode::Map;
|
let mut _keyboard_mode = KeyboardMode::Map;
|
||||||
let _scan_code = event.scan_code;
|
let _scan_code = event.scan_code;
|
||||||
|
let _code = event.code;
|
||||||
let res = if KEYBOARD_HOOKED.load(Ordering::SeqCst) {
|
let res = if KEYBOARD_HOOKED.load(Ordering::SeqCst) {
|
||||||
_keyboard_mode = client::process_event(&event, None);
|
_keyboard_mode = client::process_event(&event, None);
|
||||||
if is_press {
|
if is_press {
|
||||||
@ -246,6 +257,13 @@ pub fn start_grab_loop() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
unsafe {
|
||||||
|
if _code as u32 == rdev::kVK_Option {
|
||||||
|
IS_LEFT_OPTION_DOWN = is_press;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
};
|
};
|
||||||
let func = move |event: Event| match event.event_type {
|
let func = move |event: Event| match event.event_type {
|
||||||
@ -253,11 +271,13 @@ pub fn start_grab_loop() {
|
|||||||
EventType::KeyRelease(key) => try_handle_keyboard(event, key, false),
|
EventType::KeyRelease(key) => try_handle_keyboard(event, key, false),
|
||||||
_ => Some(event),
|
_ => Some(event),
|
||||||
};
|
};
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
rdev::set_is_main_thread(false);
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
rdev::set_event_popup(false);
|
||||||
if let Err(error) = rdev::grab(func) {
|
if let Err(error) = rdev::grab(func) {
|
||||||
log::error!("rdev Error: {:?}", error)
|
log::error!("rdev Error: {:?}", error)
|
||||||
}
|
}
|
||||||
#[cfg(target_os = "windows")]
|
|
||||||
rdev::set_event_popup(false);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
@ -395,13 +415,16 @@ pub fn event_to_key_events(
|
|||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let mut peer = get_peer_platform().to_lowercase();
|
||||||
|
peer.retain(|c| !c.is_whitespace());
|
||||||
|
|
||||||
key_event.mode = keyboard_mode.into();
|
key_event.mode = keyboard_mode.into();
|
||||||
let mut key_events = match keyboard_mode {
|
let mut key_events = match keyboard_mode {
|
||||||
KeyboardMode::Map => match map_keyboard_mode(event, key_event) {
|
KeyboardMode::Map => match map_keyboard_mode(peer.as_str(), event, key_event) {
|
||||||
Some(event) => [event].to_vec(),
|
Some(event) => [event].to_vec(),
|
||||||
None => Vec::new(),
|
None => Vec::new(),
|
||||||
},
|
},
|
||||||
KeyboardMode::Translate => translate_keyboard_mode(event, key_event),
|
KeyboardMode::Translate => translate_keyboard_mode(peer.as_str(), event, key_event),
|
||||||
_ => {
|
_ => {
|
||||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||||
{
|
{
|
||||||
@ -424,7 +447,6 @@ pub fn event_to_key_events(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
key_events
|
key_events
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -698,7 +720,7 @@ pub fn legacy_keyboard_mode(event: &Event, mut key_event: KeyEvent) -> Vec<KeyEv
|
|||||||
events
|
events
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn map_keyboard_mode(event: &Event, mut key_event: KeyEvent) -> Option<KeyEvent> {
|
pub fn map_keyboard_mode(peer: &str, event: &Event, mut key_event: KeyEvent) -> Option<KeyEvent> {
|
||||||
match event.event_type {
|
match event.event_type {
|
||||||
EventType::KeyPress(..) => {
|
EventType::KeyPress(..) => {
|
||||||
key_event.down = true;
|
key_event.down = true;
|
||||||
@ -709,12 +731,9 @@ pub fn map_keyboard_mode(event: &Event, mut key_event: KeyEvent) -> Option<KeyEv
|
|||||||
_ => return None,
|
_ => return None,
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut peer = get_peer_platform().to_lowercase();
|
|
||||||
peer.retain(|c| !c.is_whitespace());
|
|
||||||
|
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
let keycode = match peer.as_str() {
|
let keycode = match peer {
|
||||||
"windows" => {
|
OS_LOWER_WINDOWS => {
|
||||||
// https://github.com/rustdesk/rustdesk/issues/1371
|
// https://github.com/rustdesk/rustdesk/issues/1371
|
||||||
// Filter scancodes that are greater than 255 and the hight word is not 0xE0.
|
// Filter scancodes that are greater than 255 and the hight word is not 0xE0.
|
||||||
if event.scan_code > 255 && (event.scan_code >> 8) != 0xE0 {
|
if event.scan_code > 255 && (event.scan_code >> 8) != 0xE0 {
|
||||||
@ -722,7 +741,7 @@ pub fn map_keyboard_mode(event: &Event, mut key_event: KeyEvent) -> Option<KeyEv
|
|||||||
}
|
}
|
||||||
event.scan_code
|
event.scan_code
|
||||||
}
|
}
|
||||||
"macos" => {
|
OS_LOWER_MACOS => {
|
||||||
if hbb_common::config::LocalConfig::get_kb_layout_type() == "ISO" {
|
if hbb_common::config::LocalConfig::get_kb_layout_type() == "ISO" {
|
||||||
rdev::win_scancode_to_macos_iso_code(event.scan_code)?
|
rdev::win_scancode_to_macos_iso_code(event.scan_code)?
|
||||||
} else {
|
} else {
|
||||||
@ -732,15 +751,15 @@ pub fn map_keyboard_mode(event: &Event, mut key_event: KeyEvent) -> Option<KeyEv
|
|||||||
_ => rdev::win_scancode_to_linux_code(event.scan_code)?,
|
_ => rdev::win_scancode_to_linux_code(event.scan_code)?,
|
||||||
};
|
};
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
let keycode = match peer.as_str() {
|
let keycode = match peer {
|
||||||
"windows" => rdev::macos_code_to_win_scancode(event.code as _)?,
|
OS_LOWER_WINDOWS => rdev::macos_code_to_win_scancode(event.code as _)?,
|
||||||
"macos" => event.code as _,
|
OS_LOWER_MACOS => event.code as _,
|
||||||
_ => rdev::macos_code_to_linux_code(event.code as _)?,
|
_ => rdev::macos_code_to_linux_code(event.code as _)?,
|
||||||
};
|
};
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
let keycode = match peer.as_str() {
|
let keycode = match peer {
|
||||||
"windows" => rdev::linux_code_to_win_scancode(event.code as _)?,
|
OS_LOWER_WINDOWS => rdev::linux_code_to_win_scancode(event.code as _)?,
|
||||||
"macos" => {
|
OS_LOWER_MACOS => {
|
||||||
if hbb_common::config::LocalConfig::get_kb_layout_type() == "ISO" {
|
if hbb_common::config::LocalConfig::get_kb_layout_type() == "ISO" {
|
||||||
rdev::linux_code_to_macos_iso_code(event.code as _)?
|
rdev::linux_code_to_macos_iso_code(event.code as _)?
|
||||||
} else {
|
} else {
|
||||||
@ -759,10 +778,12 @@ pub fn map_keyboard_mode(event: &Event, mut key_event: KeyEvent) -> Option<KeyEv
|
|||||||
fn try_fill_unicode(event: &Event, key_event: &KeyEvent, events: &mut Vec<KeyEvent>) {
|
fn try_fill_unicode(event: &Event, key_event: &KeyEvent, events: &mut Vec<KeyEvent>) {
|
||||||
match &event.unicode {
|
match &event.unicode {
|
||||||
Some(unicode_info) => {
|
Some(unicode_info) => {
|
||||||
for code in &unicode_info.unicode {
|
if let Some(name) = &unicode_info.name {
|
||||||
let mut evt = key_event.clone();
|
if name.len() > 0 {
|
||||||
evt.set_unicode(*code as _);
|
let mut evt = key_event.clone();
|
||||||
events.push(evt);
|
evt.set_seq(name.to_string());
|
||||||
|
events.push(evt);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None => {}
|
None => {}
|
||||||
@ -783,45 +804,42 @@ fn is_hot_key_modifiers_down() -> bool {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn translate_virtual_keycode(event: &Event, mut key_event: KeyEvent) -> Option<KeyEvent> {
|
#[inline]
|
||||||
match event.event_type {
|
#[cfg(target_os = "windows")]
|
||||||
EventType::KeyPress(..) => {
|
pub fn translate_key_code(peer: &str, event: &Event, key_event: KeyEvent) -> Option<KeyEvent> {
|
||||||
key_event.down = true;
|
let mut key_event = map_keyboard_mode(peer, event, key_event)?;
|
||||||
}
|
key_event.set_chr((key_event.chr() & 0x0000FFFF) | ((event.code as u32) << 16));
|
||||||
EventType::KeyRelease(..) => {
|
|
||||||
key_event.down = false;
|
|
||||||
}
|
|
||||||
_ => return None,
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut peer = get_peer_platform().to_lowercase();
|
|
||||||
peer.retain(|c| !c.is_whitespace());
|
|
||||||
|
|
||||||
// #[cfg(target_os = "windows")]
|
|
||||||
// let keycode = match peer.as_str() {
|
|
||||||
// "windows" => event.code,
|
|
||||||
// "macos" => {
|
|
||||||
// if hbb_common::config::LocalConfig::get_kb_layout_type() == "ISO" {
|
|
||||||
// rdev::win_scancode_to_macos_iso_code(event.scan_code)?
|
|
||||||
// } else {
|
|
||||||
// rdev::win_scancode_to_macos_code(event.scan_code)?
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// _ => rdev::win_scancode_to_linux_code(event.scan_code)?,
|
|
||||||
// };
|
|
||||||
|
|
||||||
key_event.set_chr(event.code as _);
|
|
||||||
Some(key_event)
|
Some(key_event)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn translate_keyboard_mode(event: &Event, key_event: KeyEvent) -> Vec<KeyEvent> {
|
#[inline]
|
||||||
|
#[cfg(not(target_os = "windows"))]
|
||||||
|
pub fn translate_key_code(peer: &str, event: &Event, key_event: KeyEvent) -> Option<KeyEvent> {
|
||||||
|
map_keyboard_mode(peer, event, key_event)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn translate_keyboard_mode(peer: &str, event: &Event, key_event: KeyEvent) -> Vec<KeyEvent> {
|
||||||
let mut events: Vec<KeyEvent> = Vec::new();
|
let mut events: Vec<KeyEvent> = Vec::new();
|
||||||
if let Some(unicode_info) = &event.unicode {
|
if let Some(unicode_info) = &event.unicode {
|
||||||
if unicode_info.is_dead {
|
if unicode_info.is_dead {
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
if peer != OS_LOWER_MACOS && unsafe { IS_LEFT_OPTION_DOWN } {
|
||||||
|
// try clear dead key state
|
||||||
|
// rdev::clear_dead_key_state();
|
||||||
|
} else {
|
||||||
|
return events;
|
||||||
|
}
|
||||||
|
#[cfg(not(target_os = "macos"))]
|
||||||
return events;
|
return events;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
// ignore right option key
|
||||||
|
if event.code as u32 == rdev::kVK_RightOption {
|
||||||
|
return events;
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
unsafe {
|
unsafe {
|
||||||
if event.scan_code == 0x021D {
|
if event.scan_code == 0x021D {
|
||||||
@ -847,11 +865,16 @@ pub fn translate_keyboard_mode(event: &Event, key_event: KeyEvent) -> Vec<KeyEve
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(target_os = "windows"))]
|
#[cfg(target_os = "linux")]
|
||||||
try_fill_unicode(event, &key_event, &mut events);
|
try_fill_unicode(event, &key_event, &mut events);
|
||||||
|
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
if !unsafe { IS_LEFT_OPTION_DOWN } {
|
||||||
|
try_fill_unicode(event, &key_event, &mut events);
|
||||||
|
}
|
||||||
|
|
||||||
if events.is_empty() {
|
if events.is_empty() {
|
||||||
if let Some(evt) = translate_virtual_keycode(event, key_event) {
|
if let Some(evt) = translate_key_code(peer, event, key_event) {
|
||||||
events.push(evt);
|
events.push(evt);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -719,7 +719,7 @@ fn reset_input() {
|
|||||||
let _lock = VIRTUAL_INPUT_MTX.lock();
|
let _lock = VIRTUAL_INPUT_MTX.lock();
|
||||||
VIRTUAL_INPUT = VirtualInput::new(
|
VIRTUAL_INPUT = VirtualInput::new(
|
||||||
CGEventSourceStateID::Private,
|
CGEventSourceStateID::Private,
|
||||||
CGEventTapLocation::AnnotatedSession,
|
CGEventTapLocation::Session,
|
||||||
)
|
)
|
||||||
.ok();
|
.ok();
|
||||||
}
|
}
|
||||||
@ -1082,21 +1082,28 @@ fn legacy_keyboard_mode(evt: &KeyEvent) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
fn translate_process_virtual_keycode(vk: u32, down: bool) {
|
fn translate_process_code(code: u32, down: bool) {
|
||||||
crate::platform::windows::try_change_desktop();
|
crate::platform::windows::try_change_desktop();
|
||||||
sim_rdev_rawkey_virtual(vk, down);
|
match code >> 16 {
|
||||||
|
0 => sim_rdev_rawkey_position(code, down),
|
||||||
|
vk_code => sim_rdev_rawkey_virtual(vk_code, down),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn translate_keyboard_mode(evt: &KeyEvent) {
|
fn translate_keyboard_mode(evt: &KeyEvent) {
|
||||||
match evt.union {
|
match &evt.union {
|
||||||
Some(key_event::Union::Unicode(_unicode)) => {
|
Some(key_event::Union::Seq(seq)) => {
|
||||||
#[cfg(target_os = "windows")]
|
ENIGO.lock().unwrap().key_sequence(seq);
|
||||||
allow_err!(rdev::simulate_unicode(_unicode as _));
|
|
||||||
}
|
}
|
||||||
Some(key_event::Union::Chr(..)) =>
|
Some(key_event::Union::Chr(..)) =>
|
||||||
{
|
{
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
translate_process_virtual_keycode(evt.chr(), evt.down)
|
translate_process_code(evt.chr(), evt.down);
|
||||||
|
#[cfg(not(target_os = "windows"))]
|
||||||
|
sim_rdev_rawkey_position(evt.chr(), evt.down);
|
||||||
|
}
|
||||||
|
Some(key_event::Union::Unicode(..)) => {
|
||||||
|
// Do not handle unicode for now.
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
log::debug!("Unreachable. Unexpected key event {:?}", &evt);
|
log::debug!("Unreachable. Unexpected key event {:?}", &evt);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user