mirror of
https://github.com/rustdesk/rustdesk.git
synced 2024-11-23 19:49:05 +08:00
fix: voice call, select audio input device (#7922)
Signed-off-by: fufesou <shuanglongchen@yeah.net>
This commit is contained in:
parent
f08933f93c
commit
2c1595d0d5
@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="200" height="200" class="icon" viewBox="0 0 1024 1024"><path d="M608 160c141.16 0 256 114.84 256 256 0 17.67 14.33 32 32 32s32-14.33 32-32c0-85.48-33.29-165.83-93.73-226.27C773.83 129.29 693.47 96 608 96c-17.67 0-32 14.33-32 32s14.33 32 32 32zm-24 168c61.76 0 112 50.24 112 112 0 17.67 14.33 32 32 32s32-14.33 32-32c0-97.05-78.95-176-176-176-17.67 0-32 14.33-32 32s14.33 32 32 32z"/><path d="M808.3 561.21c-12.76-3.83-25.7-6.2-38.46-7.03-60.3-4.5-116.45 18.9-146.55 61.08-22.6 31.67-45.66 50.01-68.52 54.5-17.71 3.48-33.12-1.7-45.49-5.85-2.66-.9-5.18-1.74-7.68-2.49-93.84-28.17-156.49-108.42-155.9-199.7.16-24.14 16.38-45.98 42.34-56.99 43.75-18.56 77.35-54 92.17-97.22 7.02-20.48 9.65-41.57 7.8-62.68-2.66-31.78-15.1-61.85-35.96-86.96-21.1-25.39-49.51-44-82.16-53.8-4.07-1.22-8.22-2.31-12.35-3.23-30.63-6.87-62.7-4.49-92.73 6.88-29.24 11.07-54.56 29.86-73.23 54.33a476.073 476.073 0 0 0-36.42 55.34 477.675 477.675 0 0 0-17.24 33.81C109.84 312.17 95.73 376.76 96 443.15c.26 63.78 13.7 126.26 39.95 185.7 27.55 62.39 69.3 119.84 120.74 166.11 54.14 48.71 117.6 84.85 188.63 107.4C499.02 919.41 554.33 928 610.21 928c10.99 0 22.01-.33 33.03-1 17.64-1.07 31.08-16.23 30.01-33.87-1.07-17.64-16.22-31.08-33.87-30.01-59.19 3.57-117.96-3.75-174.69-21.76C342.78 802.66 244.31 715.78 194.5 603c-46.76-105.9-46.21-221.33 1.55-325.03 4.55-9.87 9.57-19.72 14.92-29.26 9.29-16.54 19.89-32.64 31.5-47.86 23.47-30.77 64.09-45.87 101.07-37.58 2.66.6 5.33 1.3 7.95 2.08 40.93 12.29 69.48 45.6 72.75 84.86 0 .05.01.1.01.15 1.07 12.15-.47 24.39-4.58 36.37-8.94 26.06-29.58 47.59-56.63 59.07-23.58 10.01-43.63 25.72-57.99 45.45-15.12 20.78-23.2 45-23.36 70.05-.37 57.15 19 114.29 54.53 160.91 36.46 47.83 87.28 82.58 146.96 100.49 1.5.45 3.44 1.1 5.69 1.86 29.79 10.01 108.9 36.59 186.49-72.13 16.95-23.75 52.2-37.26 89.81-34.42l.36.03c7.97.51 16.17 2.02 24.34 4.47 22.12 6.64 42.04 25.38 56.11 52.77 16.97 33.04 21.71 72.53 12.1 100.56l-.16.47c-5.54 16.05-17.78 29.48-34.47 37.8-15.82 7.89-22.24 27.1-14.36 42.92s27.1 22.24 42.92 14.36c31.78-15.85 55.36-42.19 66.41-74.2l.18-.53c15.23-44.4 9.22-102.11-15.68-150.61-22.07-43.02-55.68-73.15-94.62-84.84z"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="200" height="200" class="icon" viewBox="-186 -186 1365 1365"><path d="M608 160c141.16 0 256 114.84 256 256 0 17.67 14.33 32 32 32s32-14.33 32-32c0-85.48-33.29-165.83-93.73-226.27C773.83 129.29 693.47 96 608 96c-17.67 0-32 14.33-32 32s14.33 32 32 32zm-24 168c61.76 0 112 50.24 112 112 0 17.67 14.33 32 32 32s32-14.33 32-32c0-97.05-78.95-176-176-176-17.67 0-32 14.33-32 32s14.33 32 32 32z"/><path d="M808.3 561.21c-12.76-3.83-25.7-6.2-38.46-7.03-60.3-4.5-116.45 18.9-146.55 61.08-22.6 31.67-45.66 50.01-68.52 54.5-17.71 3.48-33.12-1.7-45.49-5.85-2.66-.9-5.18-1.74-7.68-2.49-93.84-28.17-156.49-108.42-155.9-199.7.16-24.14 16.38-45.98 42.34-56.99 43.75-18.56 77.35-54 92.17-97.22 7.02-20.48 9.65-41.57 7.8-62.68-2.66-31.78-15.1-61.85-35.96-86.96-21.1-25.39-49.51-44-82.16-53.8-4.07-1.22-8.22-2.31-12.35-3.23-30.63-6.87-62.7-4.49-92.73 6.88-29.24 11.07-54.56 29.86-73.23 54.33a476.073 476.073 0 0 0-36.42 55.34 477.675 477.675 0 0 0-17.24 33.81C109.84 312.17 95.73 376.76 96 443.15c.26 63.78 13.7 126.26 39.95 185.7 27.55 62.39 69.3 119.84 120.74 166.11 54.14 48.71 117.6 84.85 188.63 107.4C499.02 919.41 554.33 928 610.21 928c10.99 0 22.01-.33 33.03-1 17.64-1.07 31.08-16.23 30.01-33.87-1.07-17.64-16.22-31.08-33.87-30.01-59.19 3.57-117.96-3.75-174.69-21.76C342.78 802.66 244.31 715.78 194.5 603c-46.76-105.9-46.21-221.33 1.55-325.03 4.55-9.87 9.57-19.72 14.92-29.26 9.29-16.54 19.89-32.64 31.5-47.86 23.47-30.77 64.09-45.87 101.07-37.58 2.66.6 5.33 1.3 7.95 2.08 40.93 12.29 69.48 45.6 72.75 84.86 0 .05.01.1.01.15 1.07 12.15-.47 24.39-4.58 36.37-8.94 26.06-29.58 47.59-56.63 59.07-23.58 10.01-43.63 25.72-57.99 45.45-15.12 20.78-23.2 45-23.36 70.05-.37 57.15 19 114.29 54.53 160.91 36.46 47.83 87.28 82.58 146.96 100.49 1.5.45 3.44 1.1 5.69 1.86 29.79 10.01 108.9 36.59 186.49-72.13 16.95-23.75 52.2-37.26 89.81-34.42l.36.03c7.97.51 16.17 2.02 24.34 4.47 22.12 6.64 42.04 25.38 56.11 52.77 16.97 33.04 21.71 72.53 12.1 100.56l-.16.47c-5.54 16.05-17.78 29.48-34.47 37.8-15.82 7.89-22.24 27.1-14.36 42.92s27.1 22.24 42.92 14.36c31.78-15.85 55.36-42.19 66.41-74.2l.18-.53c15.23-44.4 9.22-102.11-15.68-150.61-22.07-43.02-55.68-73.15-94.62-84.84z"/></svg>
|
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2.1 KiB |
56
flutter/lib/common/widgets/audio_input.dart
Normal file
56
flutter/lib/common/widgets/audio_input.dart
Normal file
@ -0,0 +1,56 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_hbb/common.dart';
|
||||
import 'package:flutter_hbb/models/platform_model.dart';
|
||||
|
||||
typedef AudioINputSetDevice = void Function(String device);
|
||||
typedef AudioInputBuilder = Widget Function(
|
||||
List<String> devices, String currentDevice, AudioINputSetDevice setDevice);
|
||||
|
||||
class AudioInput extends StatelessWidget {
|
||||
final AudioInputBuilder builder;
|
||||
|
||||
const AudioInput({Key? key, required this.builder}) : super(key: key);
|
||||
|
||||
static String getDefault() {
|
||||
if (isWindows) return translate('System Sound');
|
||||
return '';
|
||||
}
|
||||
|
||||
static Future<String> getValue() async {
|
||||
String device = await bind.mainGetOption(key: 'audio-input');
|
||||
if (device.isNotEmpty) {
|
||||
return device;
|
||||
} else {
|
||||
return getDefault();
|
||||
}
|
||||
}
|
||||
|
||||
static Future<void> setDevice(String device) async {
|
||||
if (device == getDefault()) device = '';
|
||||
await bind.mainSetOption(key: 'audio-input', value: device);
|
||||
}
|
||||
|
||||
static Future<Map<String, Object>> getDevicesInfo() async {
|
||||
List<String> devices = (await bind.mainGetSoundInputs()).toList();
|
||||
if (isWindows) {
|
||||
devices.insert(0, translate('System Sound'));
|
||||
}
|
||||
String current = await getValue();
|
||||
return {'devices': devices, 'current': current};
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return futureBuilder(
|
||||
future: getDevicesInfo(),
|
||||
hasData: (data) {
|
||||
String currentDevice = data['current'];
|
||||
List<String> devices = data['devices'] as List<String>;
|
||||
if (devices.isEmpty) {
|
||||
return const Offstage();
|
||||
}
|
||||
return builder(devices, currentDevice, setDevice);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
@ -6,6 +6,7 @@ import 'package:file_picker/file_picker.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_hbb/common.dart';
|
||||
import 'package:flutter_hbb/common/widgets/audio_input.dart';
|
||||
import 'package:flutter_hbb/common/widgets/setting_widgets.dart';
|
||||
import 'package:flutter_hbb/consts.dart';
|
||||
import 'package:flutter_hbb/desktop/pages/desktop_home_page.dart';
|
||||
@ -469,38 +470,7 @@ class _GeneralState extends State<_General> {
|
||||
return const Offstage();
|
||||
}
|
||||
|
||||
String getDefault() {
|
||||
if (isWindows) return translate('System Sound');
|
||||
return '';
|
||||
}
|
||||
|
||||
Future<String> getValue() async {
|
||||
String device = await bind.mainGetOption(key: 'audio-input');
|
||||
if (device.isNotEmpty) {
|
||||
return device;
|
||||
} else {
|
||||
return getDefault();
|
||||
}
|
||||
}
|
||||
|
||||
setDevice(String device) {
|
||||
if (device == getDefault()) device = '';
|
||||
bind.mainSetOption(key: 'audio-input', value: device);
|
||||
}
|
||||
|
||||
return futureBuilder(future: () async {
|
||||
List<String> devices = (await bind.mainGetSoundInputs()).toList();
|
||||
if (isWindows) {
|
||||
devices.insert(0, translate('System Sound'));
|
||||
}
|
||||
String current = await getValue();
|
||||
return {'devices': devices, 'current': current};
|
||||
}(), hasData: (data) {
|
||||
String currentDevice = data['current'];
|
||||
List<String> devices = data['devices'] as List<String>;
|
||||
if (devices.isEmpty) {
|
||||
return const Offstage();
|
||||
}
|
||||
return AudioInput(builder: (devices, currentDevice, setDevice) {
|
||||
return _Card(title: 'Audio Input Device', children: [
|
||||
...devices.map((device) => _Radio<String>(context,
|
||||
value: device,
|
||||
|
@ -4,6 +4,7 @@ import 'dart:async';
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_hbb/common/widgets/audio_input.dart';
|
||||
import 'package:flutter_hbb/consts.dart';
|
||||
import 'package:flutter_hbb/desktop/widgets/tabbar_widget.dart';
|
||||
import 'package:flutter_hbb/models/chat_model.dart';
|
||||
@ -701,17 +702,86 @@ class _CmControlPanel extends StatelessWidget {
|
||||
children: [
|
||||
Offstage(
|
||||
offstage: !client.inVoiceCall,
|
||||
child: buildButton(
|
||||
context,
|
||||
color: Colors.red,
|
||||
onClick: () => closeVoiceCall(),
|
||||
icon: Icon(
|
||||
Icons.call_end_rounded,
|
||||
color: Colors.white,
|
||||
size: 14,
|
||||
),
|
||||
text: "Stop voice call",
|
||||
textColor: Colors.white,
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: buildButton(context,
|
||||
color: MyTheme.accent,
|
||||
onClick: null, onTapDown: (details) async {
|
||||
final devicesInfo = await AudioInput.getDevicesInfo();
|
||||
List<String> devices = devicesInfo['devices'] as List<String>;
|
||||
if (devices.isEmpty) {
|
||||
msgBox(
|
||||
gFFI.sessionId,
|
||||
'custom-nocancel-info',
|
||||
'Prompt',
|
||||
'no_audio_input_device_tip',
|
||||
'',
|
||||
gFFI.dialogManager,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
String currentDevice = devicesInfo['current'] as String;
|
||||
final x = details.globalPosition.dx;
|
||||
final y = details.globalPosition.dy;
|
||||
final position = RelativeRect.fromLTRB(x, y, x, y);
|
||||
showMenu(
|
||||
context: context,
|
||||
position: position,
|
||||
items: devices
|
||||
.map((d) => PopupMenuItem<String>(
|
||||
value: d,
|
||||
height: 18,
|
||||
padding: EdgeInsets.zero,
|
||||
onTap: () => AudioInput.setDevice(d),
|
||||
child: IgnorePointer(
|
||||
child: RadioMenuButton(
|
||||
value: d,
|
||||
groupValue: currentDevice,
|
||||
onChanged: (v) {
|
||||
if (v != null) AudioInput.setDevice(v);
|
||||
},
|
||||
child: Container(
|
||||
child: Text(
|
||||
d,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
maxLines: 1,
|
||||
),
|
||||
constraints: BoxConstraints(
|
||||
maxWidth:
|
||||
kConnectionManagerWindowSizeClosedChat
|
||||
.width -
|
||||
80),
|
||||
),
|
||||
)),
|
||||
))
|
||||
.toList(),
|
||||
);
|
||||
},
|
||||
icon: Icon(
|
||||
Icons.call_rounded,
|
||||
color: Colors.white,
|
||||
size: 14,
|
||||
),
|
||||
text: "Audio input",
|
||||
textColor: Colors.white),
|
||||
),
|
||||
Expanded(
|
||||
child: buildButton(
|
||||
context,
|
||||
color: Colors.red,
|
||||
onClick: () => closeVoiceCall(),
|
||||
icon: Icon(
|
||||
Icons.call_end_rounded,
|
||||
color: Colors.white,
|
||||
size: 14,
|
||||
),
|
||||
text: "Stop voice call",
|
||||
textColor: Colors.white,
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
Offstage(
|
||||
@ -872,12 +942,14 @@ class _CmControlPanel extends StatelessWidget {
|
||||
|
||||
Widget buildButton(BuildContext context,
|
||||
{required Color? color,
|
||||
required Function() onClick,
|
||||
Icon? icon,
|
||||
GestureTapCallback? onClick,
|
||||
Widget? icon,
|
||||
BoxBorder? border,
|
||||
required String text,
|
||||
required Color? textColor,
|
||||
String? tooltip}) {
|
||||
String? tooltip,
|
||||
GestureTapDownCallback? onTapDown}) {
|
||||
assert(!(onClick == null && onTapDown == null));
|
||||
Widget textWidget;
|
||||
if (icon != null) {
|
||||
textWidget = Text(
|
||||
@ -901,7 +973,16 @@ class _CmControlPanel extends StatelessWidget {
|
||||
color: color, borderRadius: borderRadius, border: border),
|
||||
child: InkWell(
|
||||
borderRadius: borderRadius,
|
||||
onTap: () => checkClickTime(client.id, onClick),
|
||||
onTap: () {
|
||||
if (onClick == null) return;
|
||||
checkClickTime(client.id, onClick);
|
||||
},
|
||||
onTapDown: (details) {
|
||||
if (onTapDown == null) return;
|
||||
checkClickTime(client.id, () {
|
||||
onTapDown.call(details);
|
||||
});
|
||||
},
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
|
@ -3,6 +3,7 @@ import 'dart:async';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_hbb/common/widgets/audio_input.dart';
|
||||
import 'package:flutter_hbb/common/widgets/toolbar.dart';
|
||||
import 'package:flutter_hbb/models/chat_model.dart';
|
||||
import 'package:flutter_hbb/models/state_model.dart';
|
||||
@ -1953,34 +1954,71 @@ class _VoiceCallMenu extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
menuChildrenGetter() {
|
||||
final audioInput =
|
||||
AudioInput(builder: (devices, currentDevice, setDevice) {
|
||||
return Column(
|
||||
children: devices
|
||||
.map((d) => RdoMenuButton<String>(
|
||||
child: Container(
|
||||
child: Text(
|
||||
d,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
constraints: BoxConstraints(maxWidth: 250),
|
||||
),
|
||||
value: d,
|
||||
groupValue: currentDevice,
|
||||
onChanged: (v) {
|
||||
if (v != null) setDevice(v);
|
||||
},
|
||||
ffi: ffi,
|
||||
))
|
||||
.toList(),
|
||||
);
|
||||
});
|
||||
return [
|
||||
audioInput,
|
||||
Divider(),
|
||||
MenuButton(
|
||||
child: Text(translate('End call')),
|
||||
onPressed: () => bind.sessionCloseVoiceCall(sessionId: ffi.sessionId),
|
||||
ffi: ffi,
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
return Obx(
|
||||
() {
|
||||
final String tooltip;
|
||||
final String icon;
|
||||
switch (ffi.chatModel.voiceCallStatus.value) {
|
||||
case VoiceCallStatus.waitingForResponse:
|
||||
tooltip = "Waiting";
|
||||
icon = "assets/call_wait.svg";
|
||||
break;
|
||||
return buildCallWaiting(context);
|
||||
case VoiceCallStatus.connected:
|
||||
tooltip = "Disconnect";
|
||||
icon = "assets/call_end.svg";
|
||||
break;
|
||||
return _IconSubmenuButton(
|
||||
tooltip: 'Voice call',
|
||||
svg: 'assets/voice_call.svg',
|
||||
color: _ToolbarTheme.blueColor,
|
||||
hoverColor: _ToolbarTheme.hoverBlueColor,
|
||||
menuChildrenGetter: menuChildrenGetter,
|
||||
ffi: ffi,
|
||||
);
|
||||
default:
|
||||
return Offstage();
|
||||
}
|
||||
return _IconMenuButton(
|
||||
assetName: icon,
|
||||
tooltip: tooltip,
|
||||
onPressed: () =>
|
||||
bind.sessionCloseVoiceCall(sessionId: ffi.sessionId),
|
||||
color: _ToolbarTheme.redColor,
|
||||
hoverColor: _ToolbarTheme.hoverRedColor);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Widget buildCallWaiting(BuildContext context) {
|
||||
return _IconMenuButton(
|
||||
assetName: "assets/call_wait.svg",
|
||||
tooltip: "Waiting",
|
||||
onPressed: () => bind.sessionCloseVoiceCall(sessionId: ffi.sessionId),
|
||||
color: _ToolbarTheme.redColor,
|
||||
hoverColor: _ToolbarTheme.hoverRedColor,
|
||||
);
|
||||
}
|
||||
}
|
||||
class _RecordMenu extends StatelessWidget {
|
||||
const _RecordMenu({Key? key}) : super(key: key);
|
||||
|
||||
@ -2115,7 +2153,7 @@ class _IconSubmenuButton extends StatefulWidget {
|
||||
final Color hoverColor;
|
||||
final List<Widget> Function() menuChildrenGetter;
|
||||
final MenuStyle? menuStyle;
|
||||
final FFI ffi;
|
||||
final FFI? ffi;
|
||||
final double? width;
|
||||
|
||||
_IconSubmenuButton({
|
||||
@ -2126,7 +2164,7 @@ class _IconSubmenuButton extends StatefulWidget {
|
||||
required this.color,
|
||||
required this.hoverColor,
|
||||
required this.menuChildrenGetter,
|
||||
required this.ffi,
|
||||
this.ffi,
|
||||
this.menuStyle,
|
||||
this.width,
|
||||
}) : super(key: key);
|
||||
@ -2208,13 +2246,13 @@ class MenuButton extends StatelessWidget {
|
||||
final VoidCallback? onPressed;
|
||||
final Widget? trailingIcon;
|
||||
final Widget? child;
|
||||
final FFI ffi;
|
||||
final FFI? ffi;
|
||||
MenuButton(
|
||||
{Key? key,
|
||||
this.onPressed,
|
||||
this.trailingIcon,
|
||||
required this.child,
|
||||
required this.ffi})
|
||||
this.ffi})
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
@ -2223,7 +2261,9 @@ class MenuButton extends StatelessWidget {
|
||||
key: key,
|
||||
onPressed: onPressed != null
|
||||
? () {
|
||||
_menuDismissCallback(ffi);
|
||||
if (ffi != null) {
|
||||
_menuDismissCallback(ffi!);
|
||||
}
|
||||
onPressed?.call();
|
||||
}
|
||||
: null,
|
||||
@ -2236,13 +2276,13 @@ class CkbMenuButton extends StatelessWidget {
|
||||
final bool? value;
|
||||
final ValueChanged<bool?>? onChanged;
|
||||
final Widget? child;
|
||||
final FFI ffi;
|
||||
final FFI? ffi;
|
||||
const CkbMenuButton(
|
||||
{Key? key,
|
||||
required this.value,
|
||||
required this.onChanged,
|
||||
required this.child,
|
||||
required this.ffi})
|
||||
this.ffi})
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
@ -2253,7 +2293,9 @@ class CkbMenuButton extends StatelessWidget {
|
||||
child: child,
|
||||
onChanged: onChanged != null
|
||||
? (bool? value) {
|
||||
_menuDismissCallback(ffi);
|
||||
if (ffi != null) {
|
||||
_menuDismissCallback(ffi!);
|
||||
}
|
||||
onChanged?.call(value);
|
||||
}
|
||||
: null,
|
||||
@ -2266,13 +2308,13 @@ class RdoMenuButton<T> extends StatelessWidget {
|
||||
final T? groupValue;
|
||||
final ValueChanged<T?>? onChanged;
|
||||
final Widget? child;
|
||||
final FFI ffi;
|
||||
final FFI? ffi;
|
||||
const RdoMenuButton({
|
||||
Key? key,
|
||||
required this.value,
|
||||
required this.groupValue,
|
||||
required this.child,
|
||||
required this.ffi,
|
||||
this.ffi,
|
||||
this.onChanged,
|
||||
}) : super(key: key);
|
||||
|
||||
@ -2284,7 +2326,9 @@ class RdoMenuButton<T> extends StatelessWidget {
|
||||
child: child,
|
||||
onChanged: onChanged != null
|
||||
? (T? value) {
|
||||
_menuDismissCallback(ffi);
|
||||
if (ffi != null) {
|
||||
_menuDismissCallback(ffi!);
|
||||
}
|
||||
onChanged?.call(value);
|
||||
}
|
||||
: null,
|
||||
@ -2471,10 +2515,11 @@ class InputModeMenu {
|
||||
|
||||
_menuDismissCallback(FFI ffi) => ffi.inputModel.refreshMousePos();
|
||||
|
||||
Widget _buildPointerTrackWidget(Widget child, FFI ffi) {
|
||||
Widget _buildPointerTrackWidget(Widget child, FFI? ffi) {
|
||||
return Listener(
|
||||
onPointerHover: (PointerHoverEvent e) =>
|
||||
ffi.inputModel.lastMousePos = e.position,
|
||||
onPointerHover: (PointerHoverEvent e) => {
|
||||
if (ffi != null) {ffi.inputModel.lastMousePos = e.position}
|
||||
},
|
||||
child: MouseRegion(
|
||||
child: child,
|
||||
),
|
||||
|
@ -454,7 +454,12 @@ async fn handle(data: Data, stream: &mut Connection) {
|
||||
if let Some(v) = value.get("privacy-mode-impl-key") {
|
||||
crate::privacy_mode::switch(v);
|
||||
}
|
||||
let pre_opts = Config::get_options();
|
||||
let new_audio_input = pre_opts.get("audio-input");
|
||||
Config::set_options(value);
|
||||
if new_audio_input != pre_opts.get("audio-input") {
|
||||
crate::audio_service::restart();
|
||||
}
|
||||
allow_err!(stream.send(&Data::Options(None)).await);
|
||||
}
|
||||
},
|
||||
|
@ -606,5 +606,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Follow remote cursor", ""),
|
||||
("Follow remote window focus", ""),
|
||||
("default_proxy_tip", ""),
|
||||
("no_audio_input_device_tip", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -606,5 +606,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Follow remote cursor", ""),
|
||||
("Follow remote window focus", ""),
|
||||
("default_proxy_tip", ""),
|
||||
("no_audio_input_device_tip", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -606,5 +606,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Follow remote cursor", ""),
|
||||
("Follow remote window focus", ""),
|
||||
("default_proxy_tip", ""),
|
||||
("no_audio_input_device_tip", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -606,5 +606,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Follow remote cursor", "跟随远程光标"),
|
||||
("Follow remote window focus", "跟随远程窗口焦点"),
|
||||
("default_proxy_tip", "默认代理协议及端口为 Socks5 和 1080"),
|
||||
("no_audio_input_device_tip", "未找到音频输入设备"),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -606,5 +606,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Follow remote cursor", "Sledovat dálkový kurzor"),
|
||||
("Follow remote window focus", "Sledovat zaměření vzdáleného okna"),
|
||||
("default_proxy_tip", ""),
|
||||
("no_audio_input_device_tip", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -606,5 +606,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Follow remote cursor", ""),
|
||||
("Follow remote window focus", ""),
|
||||
("default_proxy_tip", ""),
|
||||
("no_audio_input_device_tip", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -606,5 +606,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Follow remote cursor", "Dem entfernten Cursor folgen"),
|
||||
("Follow remote window focus", "Dem Fokus des entfernten Fensters folgen"),
|
||||
("default_proxy_tip", ""),
|
||||
("no_audio_input_device_tip", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -606,5 +606,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Follow remote cursor", ""),
|
||||
("Follow remote window focus", ""),
|
||||
("default_proxy_tip", ""),
|
||||
("no_audio_input_device_tip", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -224,5 +224,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Follow remote cursor", ""),
|
||||
("Follow remote window focus", ""),
|
||||
("default_proxy_tip", "Default protocol and port are Socks5 and 1080"),
|
||||
("no_audio_input_device_tip", "No audio input device found."),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -606,5 +606,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Follow remote cursor", ""),
|
||||
("Follow remote window focus", ""),
|
||||
("default_proxy_tip", ""),
|
||||
("no_audio_input_device_tip", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -606,5 +606,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Follow remote cursor", "Seguir cursor remoto"),
|
||||
("Follow remote window focus", "Seguir ventana remota activa"),
|
||||
("default_proxy_tip", ""),
|
||||
("no_audio_input_device_tip", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -606,5 +606,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Follow remote cursor", ""),
|
||||
("Follow remote window focus", ""),
|
||||
("default_proxy_tip", ""),
|
||||
("no_audio_input_device_tip", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -606,5 +606,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Follow remote cursor", ""),
|
||||
("Follow remote window focus", ""),
|
||||
("default_proxy_tip", ""),
|
||||
("no_audio_input_device_tip", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -606,5 +606,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Follow remote cursor", ""),
|
||||
("Follow remote window focus", ""),
|
||||
("default_proxy_tip", ""),
|
||||
("no_audio_input_device_tip", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -606,5 +606,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Follow remote cursor", ""),
|
||||
("Follow remote window focus", ""),
|
||||
("default_proxy_tip", ""),
|
||||
("no_audio_input_device_tip", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -606,5 +606,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Follow remote cursor", ""),
|
||||
("Follow remote window focus", ""),
|
||||
("default_proxy_tip", ""),
|
||||
("no_audio_input_device_tip", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -606,5 +606,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Follow remote cursor", ""),
|
||||
("Follow remote window focus", ""),
|
||||
("default_proxy_tip", ""),
|
||||
("no_audio_input_device_tip", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -606,5 +606,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Follow remote cursor", ""),
|
||||
("Follow remote window focus", ""),
|
||||
("default_proxy_tip", ""),
|
||||
("no_audio_input_device_tip", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -606,5 +606,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Follow remote cursor", "Segui cursore remoto"),
|
||||
("Follow remote window focus", "Segui focus finestra remota"),
|
||||
("default_proxy_tip", ""),
|
||||
("no_audio_input_device_tip", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -606,5 +606,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Follow remote cursor", ""),
|
||||
("Follow remote window focus", ""),
|
||||
("default_proxy_tip", ""),
|
||||
("no_audio_input_device_tip", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -606,5 +606,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Follow remote cursor", ""),
|
||||
("Follow remote window focus", ""),
|
||||
("default_proxy_tip", ""),
|
||||
("no_audio_input_device_tip", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -606,5 +606,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Follow remote cursor", ""),
|
||||
("Follow remote window focus", ""),
|
||||
("default_proxy_tip", ""),
|
||||
("no_audio_input_device_tip", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -606,5 +606,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Follow remote cursor", ""),
|
||||
("Follow remote window focus", ""),
|
||||
("default_proxy_tip", ""),
|
||||
("no_audio_input_device_tip", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -606,5 +606,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Follow remote cursor", "Sekot attālajam kursoram"),
|
||||
("Follow remote window focus", "Sekot attālā loga fokusam"),
|
||||
("default_proxy_tip", ""),
|
||||
("no_audio_input_device_tip", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -606,5 +606,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Follow remote cursor", ""),
|
||||
("Follow remote window focus", ""),
|
||||
("default_proxy_tip", ""),
|
||||
("no_audio_input_device_tip", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -606,5 +606,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Follow remote cursor", "Volg de cursor op afstand"),
|
||||
("Follow remote window focus", "Volg de focus van het venster op afstand"),
|
||||
("default_proxy_tip", ""),
|
||||
("no_audio_input_device_tip", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -606,5 +606,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Follow remote cursor", "Podążaj za zdalnym kursorem"),
|
||||
("Follow remote window focus", "Podążaj za aktywnością zdalnych okien"),
|
||||
("default_proxy_tip", ""),
|
||||
("no_audio_input_device_tip", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -606,5 +606,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Follow remote cursor", ""),
|
||||
("Follow remote window focus", ""),
|
||||
("default_proxy_tip", ""),
|
||||
("no_audio_input_device_tip", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -606,5 +606,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Follow remote cursor", ""),
|
||||
("Follow remote window focus", ""),
|
||||
("default_proxy_tip", ""),
|
||||
("no_audio_input_device_tip", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -606,5 +606,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Follow remote cursor", ""),
|
||||
("Follow remote window focus", ""),
|
||||
("default_proxy_tip", ""),
|
||||
("no_audio_input_device_tip", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -606,5 +606,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Follow remote cursor", "Следовать за удалённым курсором"),
|
||||
("Follow remote window focus", "Следовать за фокусом удалённого окна"),
|
||||
("default_proxy_tip", ""),
|
||||
("no_audio_input_device_tip", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -606,5 +606,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Follow remote cursor", "Nasledovať vzdialený kurzor"),
|
||||
("Follow remote window focus", "Nasledovať vzdialené zameranie okna"),
|
||||
("default_proxy_tip", ""),
|
||||
("no_audio_input_device_tip", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -606,5 +606,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Follow remote cursor", ""),
|
||||
("Follow remote window focus", ""),
|
||||
("default_proxy_tip", ""),
|
||||
("no_audio_input_device_tip", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -606,5 +606,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Follow remote cursor", ""),
|
||||
("Follow remote window focus", ""),
|
||||
("default_proxy_tip", ""),
|
||||
("no_audio_input_device_tip", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -606,5 +606,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Follow remote cursor", ""),
|
||||
("Follow remote window focus", ""),
|
||||
("default_proxy_tip", ""),
|
||||
("no_audio_input_device_tip", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -606,5 +606,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Follow remote cursor", ""),
|
||||
("Follow remote window focus", ""),
|
||||
("default_proxy_tip", ""),
|
||||
("no_audio_input_device_tip", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -606,5 +606,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Follow remote cursor", ""),
|
||||
("Follow remote window focus", ""),
|
||||
("default_proxy_tip", ""),
|
||||
("no_audio_input_device_tip", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -606,5 +606,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Follow remote cursor", ""),
|
||||
("Follow remote window focus", ""),
|
||||
("default_proxy_tip", ""),
|
||||
("no_audio_input_device_tip", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -606,5 +606,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Follow remote cursor", ""),
|
||||
("Follow remote window focus", ""),
|
||||
("default_proxy_tip", ""),
|
||||
("no_audio_input_device_tip", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -606,5 +606,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Follow remote cursor", "跟隨遠端游標"),
|
||||
("Follow remote window focus", "跟隨遠端視窗焦點"),
|
||||
("default_proxy_tip", ""),
|
||||
("no_audio_input_device_tip", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -606,5 +606,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Follow remote cursor", ""),
|
||||
("Follow remote window focus", ""),
|
||||
("default_proxy_tip", ""),
|
||||
("no_audio_input_device_tip", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -606,5 +606,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Follow remote cursor", ""),
|
||||
("Follow remote window focus", ""),
|
||||
("default_proxy_tip", ""),
|
||||
("no_audio_input_device_tip", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -103,6 +103,7 @@ mod pa_impl {
|
||||
|
||||
#[cfg(not(any(target_os = "linux", target_os = "android")))]
|
||||
mod cpal_impl {
|
||||
use self::service::{Reset, ServiceSwap};
|
||||
use super::*;
|
||||
use cpal::{
|
||||
traits::{DeviceTrait, HostTrait, StreamTrait},
|
||||
@ -125,7 +126,23 @@ mod cpal_impl {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run(sp: EmptyExtraFieldService, state: &mut State) -> ResultType<()> {
|
||||
fn run_restart(sp: EmptyExtraFieldService, state: &mut State) -> ResultType<()> {
|
||||
state.reset();
|
||||
sp.snapshot(|_sps: ServiceSwap<_>| Ok(()))?;
|
||||
match &state.stream {
|
||||
None => {
|
||||
state.stream = Some(play(&sp)?);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
if let Some((_, format)) = &state.stream {
|
||||
sp.send_shared(format.clone());
|
||||
}
|
||||
RESTARTING.store(false, Ordering::SeqCst);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn run_serv_snapshot(sp: EmptyExtraFieldService, state: &mut State) -> ResultType<()> {
|
||||
sp.snapshot(|sps| {
|
||||
match &state.stream {
|
||||
None => {
|
||||
@ -141,6 +158,14 @@ mod cpal_impl {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn run(sp: EmptyExtraFieldService, state: &mut State) -> ResultType<()> {
|
||||
if !RESTARTING.load(Ordering::SeqCst) {
|
||||
run_serv_snapshot(sp, state)
|
||||
} else {
|
||||
run_restart(sp, state)
|
||||
}
|
||||
}
|
||||
|
||||
fn send(
|
||||
data: Vec<f32>,
|
||||
sample_rate0: u32,
|
||||
|
@ -369,6 +369,8 @@ pub fn set_option(key: String, value: String) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else if &key == "audio-input" {
|
||||
crate::audio_service::restart();
|
||||
}
|
||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user