diff --git a/.github/workflows/flutter-build.yml b/.github/workflows/flutter-build.yml index 56f7030b8..27b6118b9 100644 --- a/.github/workflows/flutter-build.yml +++ b/.github/workflows/flutter-build.yml @@ -38,7 +38,7 @@ env: # https://github.com/rustdesk/rustdesk/actions/runs/14414119794/job/40427970174 # 2. Update the `VCPKG_COMMIT_ID` in `ci.yml` and `playground.yml`. VCPKG_COMMIT_ID: "6f29f12e82a8293156836ad81cc9bf5af41fe836" - VERSION: "1.3.9" + VERSION: "1.4.0" NDK_VERSION: "r27c" #signing keys env variable checks ANDROID_SIGNING_KEY: "${{ secrets.ANDROID_SIGNING_KEY }}" diff --git a/.github/workflows/playground.yml b/.github/workflows/playground.yml index 3370e9921..962df73f1 100644 --- a/.github/workflows/playground.yml +++ b/.github/workflows/playground.yml @@ -17,7 +17,7 @@ env: TAG_NAME: "nightly" VCPKG_BINARY_SOURCES: "clear;x-gha,readwrite" VCPKG_COMMIT_ID: "6f29f12e82a8293156836ad81cc9bf5af41fe836" - VERSION: "1.3.9" + VERSION: "1.4.0" NDK_VERSION: "r26d" #signing keys env variable checks ANDROID_SIGNING_KEY: "${{ secrets.ANDROID_SIGNING_KEY }}" diff --git a/Cargo.lock b/Cargo.lock index 1aa7e4040..1afa949f1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5857,7 +5857,7 @@ dependencies = [ [[package]] name = "rustdesk" -version = "1.3.9" +version = "1.4.0" dependencies = [ "android-wakelock", "android_logger", @@ -5960,7 +5960,7 @@ dependencies = [ [[package]] name = "rustdesk-portable-packer" -version = "1.3.9" +version = "1.4.0" dependencies = [ "brotli", "dirs 5.0.1", diff --git a/Cargo.toml b/Cargo.toml index 2fb22dc29..ff0e8a870 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rustdesk" -version = "1.3.9" +version = "1.4.0" authors = ["rustdesk "] edition = "2021" build= "build.rs" diff --git a/appimage/AppImageBuilder-aarch64.yml b/appimage/AppImageBuilder-aarch64.yml index 8e0f25906..36297f0e1 100644 --- a/appimage/AppImageBuilder-aarch64.yml +++ b/appimage/AppImageBuilder-aarch64.yml @@ -18,7 +18,7 @@ AppDir: id: rustdesk name: rustdesk icon: rustdesk - version: 1.3.9 + version: 1.4.0 exec: usr/share/rustdesk/rustdesk exec_args: $@ apt: diff --git a/appimage/AppImageBuilder-x86_64.yml b/appimage/AppImageBuilder-x86_64.yml index d117d1717..59bcca92b 100644 --- a/appimage/AppImageBuilder-x86_64.yml +++ b/appimage/AppImageBuilder-x86_64.yml @@ -18,7 +18,7 @@ AppDir: id: rustdesk name: rustdesk icon: rustdesk - version: 1.3.9 + version: 1.4.0 exec: usr/share/rustdesk/rustdesk exec_args: $@ apt: diff --git a/flutter/lib/common/widgets/toolbar.dart b/flutter/lib/common/widgets/toolbar.dart index 4011110dd..c861a0977 100644 --- a/flutter/lib/common/widgets/toolbar.dart +++ b/flutter/lib/common/widgets/toolbar.dart @@ -1,3 +1,4 @@ +import 'dart:async'; import 'dart:convert'; import 'package:flutter/material.dart'; @@ -15,7 +16,7 @@ bool isEditOsPassword = false; class TTextMenu { final Widget child; - final VoidCallback onPressed; + final VoidCallback? onPressed; Widget? trailingIcon; bool divider; TTextMenu( @@ -294,6 +295,41 @@ List toolbarControls(BuildContext context, String id, FFI ffi) { ), onPressed: () => ffi.recordingModel.toggle())); } + + // to-do: + // 1. Web desktop + // 2. Mobile, copy the image to the clipboard + if (isDesktop) { + final isScreenshotSupported = bind.sessionGetCommonSync( + sessionId: sessionId, key: 'is_screenshot_supported', param: ''); + if ('true' == isScreenshotSupported) { + v.add(TTextMenu( + child: Text(ffi.ffiModel.timerScreenshot != null + ? '${translate('Taking screenshot')} ...' + : translate('Take screenshot')), + onPressed: ffi.ffiModel.timerScreenshot != null + ? null + : () { + if (pi.currentDisplay == kAllDisplayValue) { + msgBox( + sessionId, + 'custom-nook-nocancel-hasclose-info', + 'Take screenshot', + 'screenshot-merged-screen-not-supported-tip', + '', + ffi.dialogManager); + } else { + bind.sessionTakeScreenshot( + sessionId: sessionId, display: pi.currentDisplay); + ffi.ffiModel.timerScreenshot = + Timer(Duration(seconds: 30), () { + ffi.ffiModel.timerScreenshot = null; + }); + } + }, + )); + } + } // fingerprint if (!(isDesktop || isWebDesktop)) { v.add(TTextMenu( diff --git a/flutter/lib/consts.dart b/flutter/lib/consts.dart index 63023443e..a4d60b0a6 100644 --- a/flutter/lib/consts.dart +++ b/flutter/lib/consts.dart @@ -220,7 +220,8 @@ const double kDefaultQuality = 50; const double kMaxQuality = 100; const double kMaxMoreQuality = 2000; -const String kKeyPrinterIncommingJobAction = 'printer-incomming-job-action'; +// incomming (should be incoming) is kept, because change it will break the previous setting. +const String kKeyPrinterIncomingJobAction = 'printer-incomming-job-action'; const String kValuePrinterIncomingJobDismiss = 'dismiss'; const String kValuePrinterIncomingJobDefault = ''; const String kValuePrinterIncomingJobSelected = 'selected'; diff --git a/flutter/lib/desktop/pages/desktop_setting_page.dart b/flutter/lib/desktop/pages/desktop_setting_page.dart index 9870437e4..5ac53775c 100644 --- a/flutter/lib/desktop/pages/desktop_setting_page.dart +++ b/flutter/lib/desktop/pages/desktop_setting_page.dart @@ -1908,7 +1908,7 @@ class __PrinterState extends State<_Printer> { final scrollController = ScrollController(); return ListView(controller: scrollController, children: [ outgoing(context), - incomming(context), + incoming(context), ]).marginOnly(bottom: _kListViewBottomMargin); } @@ -1995,15 +1995,15 @@ class __PrinterState extends State<_Printer> { return _Card(title: 'Outgoing Print Jobs', children: children); } - Widget incomming(BuildContext context) { + Widget incoming(BuildContext context) { onRadioChanged(String value) async { await bind.mainSetLocalOption( - key: kKeyPrinterIncommingJobAction, value: value); + key: kKeyPrinterIncomingJobAction, value: value); setState(() {}); } PrinterOptions printerOptions = PrinterOptions.load(); - return _Card(title: 'Incomming Print Jobs', children: [ + return _Card(title: 'Incoming Print Jobs', children: [ _Radio(context, value: kValuePrinterIncomingJobDismiss, groupValue: printerOptions.action, diff --git a/flutter/lib/mobile/pages/remote_page.dart b/flutter/lib/mobile/pages/remote_page.dart index 27ce22713..93f49f585 100644 --- a/flutter/lib/mobile/pages/remote_page.dart +++ b/flutter/lib/mobile/pages/remote_page.dart @@ -695,9 +695,9 @@ class _RemotePageState extends State with WidgetsBindingObserver { ); if (index != null) { if (index < mobileActionMenus.length) { - mobileActionMenus[index].onPressed.call(); + mobileActionMenus[index].onPressed?.call(); } else if (index < mobileActionMenus.length + more.length) { - menus[index - mobileActionMenus.length].onPressed.call(); + menus[index - mobileActionMenus.length].onPressed?.call(); } } }(); @@ -770,7 +770,7 @@ class _RemotePageState extends State with WidgetsBindingObserver { elevation: 8, ); if (index != null && index < menus.length) { - menus[index].onPressed.call(); + menus[index].onPressed?.call(); } }); } @@ -1267,7 +1267,7 @@ void showOptions( title: resolution.child, onTap: () { close(); - resolution.onPressed(); + resolution.onPressed?.call(); }, )); } @@ -1279,7 +1279,7 @@ void showOptions( title: virtualDisplayMenu.child, onTap: () { close(); - virtualDisplayMenu.onPressed(); + virtualDisplayMenu.onPressed?.call(); }, )); } diff --git a/flutter/lib/mobile/pages/settings_page.dart b/flutter/lib/mobile/pages/settings_page.dart index 83cfa2fb2..600f807e8 100644 --- a/flutter/lib/mobile/pages/settings_page.dart +++ b/flutter/lib/mobile/pages/settings_page.dart @@ -243,7 +243,7 @@ class _SettingsState extends State with WidgetsBindingObserver { Widget build(BuildContext context) { Provider.of(context); final outgoingOnly = bind.isOutgoingOnly(); - final incommingOnly = bind.isIncomingOnly(); + final incomingOnly = bind.isIncomingOnly(); final customClientSection = CustomSettingsSection( child: Column( children: [ @@ -728,7 +728,7 @@ class _SettingsState extends State with WidgetsBindingObserver { }); }, ), - if (!incommingOnly) + if (!incomingOnly) SettingsTile.switchTile( title: Text(translate('Automatically record outgoing sessions')), diff --git a/flutter/lib/mobile/pages/view_camera_page.dart b/flutter/lib/mobile/pages/view_camera_page.dart index afd24dc7e..ac70a2dab 100644 --- a/flutter/lib/mobile/pages/view_camera_page.dart +++ b/flutter/lib/mobile/pages/view_camera_page.dart @@ -478,9 +478,9 @@ class _ViewCameraPageState extends State ); if (index != null) { if (index < mobileActionMenus.length) { - mobileActionMenus[index].onPressed.call(); + mobileActionMenus[index].onPressed?.call(); } else if (index < mobileActionMenus.length + more.length) { - menus[index - mobileActionMenus.length].onPressed.call(); + menus[index - mobileActionMenus.length].onPressed?.call(); } } }(); @@ -553,7 +553,7 @@ class _ViewCameraPageState extends State elevation: 8, ); if (index != null && index < menus.length) { - menus[index].onPressed.call(); + menus[index].onPressed?.call(); } }); } diff --git a/flutter/lib/models/model.dart b/flutter/lib/models/model.dart index 8091846c6..52782fc95 100644 --- a/flutter/lib/models/model.dart +++ b/flutter/lib/models/model.dart @@ -34,6 +34,7 @@ import 'package:flutter_svg/flutter_svg.dart'; import 'package:get/get.dart'; import 'package:uuid/uuid.dart'; import 'package:window_manager/window_manager.dart'; +import 'package:file_picker/file_picker.dart'; import '../common.dart'; import '../utils/image.dart' as img; @@ -119,6 +120,8 @@ class FfiModel with ChangeNotifier { RxBool waitForFirstImage = true.obs; bool isRefreshing = false; + Timer? timerScreenshot; + Rect? get rect => _rect; bool get isOriginalResolutionSet => _pi.tryGetDisplayIfNotAllDisplay()?.isOriginalResolutionSet ?? false; @@ -216,6 +219,7 @@ class FfiModel with ChangeNotifier { _timer = null; clearPermissions(); waitForImageTimer?.cancel(); + timerScreenshot?.cancel(); } setConnectionType(String peerId, bool secure, bool direct) { @@ -414,12 +418,82 @@ class FfiModel with ChangeNotifier { } } else if (name == "printer_request") { _handlePrinterRequest(evt, sessionId, peerId); + } else if (name == 'screenshot') { + _handleScreenshot(evt, sessionId, peerId); } else { debugPrint('Event is not handled in the fixed branch: $name'); } }; } + _handleScreenshot( + Map evt, SessionID sessionId, String peerId) { + timerScreenshot?.cancel(); + timerScreenshot = null; + final msg = evt['msg'] ?? ''; + final msgBoxType = 'custom-nook-nocancel-hasclose'; + final msgBoxTitle = 'Take screenshot'; + final dialogManager = parent.target!.dialogManager; + if (msg.isNotEmpty) { + msgBox(sessionId, msgBoxType, msgBoxTitle, msg, '', dialogManager); + } else { + final msgBoxText = 'screenshot-action-tip'; + + close() { + dialogManager.dismissAll(); + } + + saveAs() { + close(); + Future.delayed(Duration.zero, () async { + final ts = DateTime.now().millisecondsSinceEpoch ~/ 1000; + String? outputFile = await FilePicker.platform.saveFile( + dialogTitle: '${translate('Save as')}...', + fileName: 'screenshot_$ts.png', + allowedExtensions: ['png'], + type: FileType.custom, + ); + if (outputFile == null) { + bind.sessionHandleScreenshot(sessionId: sessionId, action: '2'); + } else { + final res = await bind.sessionHandleScreenshot( + sessionId: sessionId, action: '0:$outputFile'); + if (res.isNotEmpty) { + msgBox(sessionId, 'custom-nook-nocancel-hasclose-error', + 'Take screenshot', res, '', dialogManager); + } + } + }); + } + + copyToClipboard() { + bind.sessionHandleScreenshot(sessionId: sessionId, action: '1'); + close(); + } + + cancel() { + bind.sessionHandleScreenshot(sessionId: sessionId, action: '2'); + close(); + } + + final List buttons = [ + dialogButton('${translate('Save as')}...', onPressed: saveAs), + dialogButton('Copy to clipboard', onPressed: copyToClipboard), + dialogButton('Cancel', onPressed: cancel), + ]; + dialogManager.dismissAll(); + dialogManager.show( + (setState, close, context) => CustomAlertDialog( + title: null, + content: SelectionArea( + child: msgboxContent(msgBoxType, msgBoxTitle, msgBoxText)), + actions: buttons, + ), + tag: '$msgBoxType-$msgBoxTitle-$msgBoxTitle', + ); + } + } + _handlePrinterRequest( Map evt, SessionID sessionId, String peerId) { final id = evt['id']; @@ -451,7 +525,7 @@ class FfiModel with ChangeNotifier { if (saveSettings.value || dontShowAgain.value) { bind.mainSetLocalOption(key: kKeyPrinterSelected, value: printerName); bind.mainSetLocalOption( - key: kKeyPrinterIncommingJobAction, + key: kKeyPrinterIncomingJobAction, value: defaultOrSelectedGroupValue.value); } if (dontShowAgain.value) { @@ -463,7 +537,7 @@ class FfiModel with ChangeNotifier { onCancel() { if (dontShowAgain.value) { bind.mainSetLocalOption( - key: kKeyPrinterIncommingJobAction, + key: kKeyPrinterIncomingJobAction, value: kValuePrinterIncomingJobDismiss); } close(); diff --git a/flutter/lib/models/printer_model.dart b/flutter/lib/models/printer_model.dart index df8f67cff..8d0b37932 100644 --- a/flutter/lib/models/printer_model.dart +++ b/flutter/lib/models/printer_model.dart @@ -13,7 +13,7 @@ class PrinterOptions { required this.printerName}); static PrinterOptions load() { - var action = bind.mainGetLocalOption(key: kKeyPrinterIncommingJobAction); + var action = bind.mainGetLocalOption(key: kKeyPrinterIncomingJobAction); if (![ kValuePrinterIncomingJobDismiss, kValuePrinterIncomingJobDefault, @@ -28,7 +28,7 @@ class PrinterOptions { if (action == kValuePrinterIncomingJobSelected) { action = kValuePrinterIncomingJobDefault; bind.mainSetLocalOption( - key: kKeyPrinterIncommingJobAction, + key: kKeyPrinterIncomingJobAction, value: kValuePrinterIncomingJobDefault); if (printerNames.isEmpty) { selectedPrinterName = ''; diff --git a/flutter/pubspec.yaml b/flutter/pubspec.yaml index d16365fa5..ac9b753fc 100644 --- a/flutter/pubspec.yaml +++ b/flutter/pubspec.yaml @@ -16,7 +16,7 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # 1.1.9-1 works for android, but for ios it becomes 1.1.91, need to set it to 1.1.9-a.1 for iOS, will get 1.1.9.1, but iOS store not allow 4 numbers -version: 1.3.9+57 +version: 1.4.0+58 environment: sdk: '^3.1.0' diff --git a/libs/hbb_common b/libs/hbb_common index 3afaf6494..42aad01a5 160000 --- a/libs/hbb_common +++ b/libs/hbb_common @@ -1 +1 @@ -Subproject commit 3afaf6494475ef58dcaaae6b4e6d2303cc3d632b +Subproject commit 42aad01a517d4de7420ddcb7e99c29bd9f6d2b5a diff --git a/libs/portable/Cargo.toml b/libs/portable/Cargo.toml index 418e122f7..2855b3cb6 100644 --- a/libs/portable/Cargo.toml +++ b/libs/portable/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rustdesk-portable-packer" -version = "1.3.9" +version = "1.4.0" edition = "2021" description = "RustDesk Remote Desktop" diff --git a/libs/scrap/src/common/convert.rs b/libs/scrap/src/common/convert.rs index d38831928..40c17e5ba 100644 --- a/libs/scrap/src/common/convert.rs +++ b/libs/scrap/src/common/convert.rs @@ -197,3 +197,40 @@ pub fn convert_to_yuv( } Ok(()) } + +#[cfg(not(target_os = "ios"))] +pub fn convert(captured: &PixelBuffer, pixfmt: crate::Pixfmt, dst: &mut Vec) -> ResultType<()> { + if captured.pixfmt() == pixfmt { + dst.extend_from_slice(captured.data()); + return Ok(()); + } + + let src = captured.data(); + let src_stride = captured.stride(); + let src_pixfmt = captured.pixfmt(); + let src_width = captured.width(); + let src_height = captured.height(); + + let unsupported = format!( + "unsupported pixfmt conversion: {src_pixfmt:?} -> {:?}", + pixfmt + ); + + match (src_pixfmt, pixfmt) { + (crate::Pixfmt::BGRA, crate::Pixfmt::RGBA) | (crate::Pixfmt::RGBA, crate::Pixfmt::BGRA) => { + dst.resize(src.len(), 0); + call_yuv!(ABGRToARGB( + src.as_ptr(), + src_stride[0] as _, + dst.as_mut_ptr(), + src_stride[0] as _, + src_width as _, + src_height as _, + )); + } + _ => { + bail!(unsupported); + } + } + Ok(()) +} diff --git a/libs/scrap/src/common/mod.rs b/libs/scrap/src/common/mod.rs index af5421973..2d74caa0d 100644 --- a/libs/scrap/src/common/mod.rs +++ b/libs/scrap/src/common/mod.rs @@ -63,6 +63,7 @@ pub enum ImageFormat { } #[repr(C)] +#[derive(Clone)] pub struct ImageRgb { pub raw: Vec, pub w: usize, diff --git a/res/PKGBUILD b/res/PKGBUILD index b5ed11370..a5601bf31 100644 --- a/res/PKGBUILD +++ b/res/PKGBUILD @@ -1,5 +1,5 @@ pkgname=rustdesk -pkgver=1.3.9 +pkgver=1.4.0 pkgrel=0 epoch= pkgdesc="" diff --git a/res/rpm-flutter-suse.spec b/res/rpm-flutter-suse.spec index 421bcb25a..1f566c6ec 100644 --- a/res/rpm-flutter-suse.spec +++ b/res/rpm-flutter-suse.spec @@ -1,5 +1,5 @@ Name: rustdesk -Version: 1.3.9 +Version: 1.4.0 Release: 0 Summary: RPM package License: GPL-3.0 diff --git a/res/rpm-flutter.spec b/res/rpm-flutter.spec index 508b8050f..7323c92f2 100644 --- a/res/rpm-flutter.spec +++ b/res/rpm-flutter.spec @@ -1,5 +1,5 @@ Name: rustdesk -Version: 1.3.9 +Version: 1.4.0 Release: 0 Summary: RPM package License: GPL-3.0 diff --git a/res/rpm.spec b/res/rpm.spec index ebfefb8a0..0a64cbb3c 100644 --- a/res/rpm.spec +++ b/res/rpm.spec @@ -1,5 +1,5 @@ Name: rustdesk -Version: 1.3.9 +Version: 1.4.0 Release: 0 Summary: RPM package License: GPL-3.0 diff --git a/src/client.rs b/src/client.rs index 0019bb975..dbad64675 100644 --- a/src/client.rs +++ b/src/client.rs @@ -85,6 +85,7 @@ pub use super::lang::*; pub mod file_trait; pub mod helper; pub mod io_loop; +pub mod screenshot; pub const MILLI1: Duration = Duration::from_millis(1); pub const SEC30: Duration = Duration::from_secs(30); @@ -3336,6 +3337,7 @@ pub enum Data { CloseVoiceCall, ResetDecoder(Option), RenameFile((i32, String, String, bool)), + TakeScreenshot((i32, String)), } /// Keycode for key events. diff --git a/src/client/io_loop.rs b/src/client/io_loop.rs index 69ab0fb04..687c942d9 100644 --- a/src/client/io_loop.rs +++ b/src/client/io_loop.rs @@ -962,6 +962,15 @@ impl Remote { } } }, + Data::TakeScreenshot((display, sid)) => { + let mut msg = Message::new(); + msg.set_screenshot_request(ScreenshotRequest { + display, + sid, + ..Default::default() + }); + allow_err!(peer.send(&msg).await); + } _ => {} } true @@ -1909,6 +1918,11 @@ impl Remote { self.handler.set_displays(&pi.displays); self.handler.set_platform_additions(&pi.platform_additions); } + Some(message::Union::ScreenshotResponse(response)) => { + crate::client::screenshot::set_screenshot(response.data); + self.handler + .handle_screenshot_resp(response.sid, response.msg); + } _ => {} } } diff --git a/src/client/screenshot.rs b/src/client/screenshot.rs new file mode 100644 index 000000000..82a95bee9 --- /dev/null +++ b/src/client/screenshot.rs @@ -0,0 +1,99 @@ +#[cfg(not(any(target_os = "android", target_os = "ios")))] +use crate::clipboard::{update_clipboard, ClipboardSide}; +use hbb_common::{message_proto::*, ResultType}; +use std::sync::Mutex; + +lazy_static::lazy_static! { + static ref SCREENSHOT: Mutex = Default::default(); +} + +pub enum ScreenshotAction { + SaveAs(String), + CopyToClipboard, + Discard, +} + +impl Default for ScreenshotAction { + fn default() -> Self { + Self::Discard + } +} + +impl From<&str> for ScreenshotAction { + fn from(value: &str) -> Self { + match value.chars().next() { + Some('0') => { + if let Some((pos, _)) = value.char_indices().nth(2) { + let substring = &value[pos..]; + Self::SaveAs(substring.to_string()) + } else { + Self::default() + } + } + Some('1') => Self::CopyToClipboard, + Some('2') => Self::default(), + _ => Self::default(), + } + } +} + +impl Into for ScreenshotAction { + fn into(self) -> String { + match self { + Self::SaveAs(p) => format!("0:{p}"), + Self::CopyToClipboard => "1".to_owned(), + Self::Discard => "2".to_owned(), + } + } +} + +#[derive(Default)] +pub struct Screenshot { + data: Option, +} + +impl Screenshot { + fn set_screenshot(&mut self, data: bytes::Bytes) { + self.data.replace(data); + } + + fn handle_screenshot(&mut self, action: String) -> String { + let Some(data) = self.data.take() else { + return "No cached screenshot".to_owned(); + }; + match Self::handle_screenshot_(data, action) { + Ok(()) => "".to_owned(), + Err(e) => e.to_string(), + } + } + + fn handle_screenshot_(data: bytes::Bytes, action: String) -> ResultType<()> { + match ScreenshotAction::from(&action as &str) { + ScreenshotAction::SaveAs(p) => { + std::fs::write(p, data)?; + } + ScreenshotAction::CopyToClipboard => { + #[cfg(not(any(target_os = "android", target_os = "ios")))] + { + let clips = vec![Clipboard { + compress: false, + content: data, + format: ClipboardFormat::ImagePng.into(), + ..Default::default() + }]; + update_clipboard(clips, ClipboardSide::Client); + } + } + ScreenshotAction::Discard => {} + } + Ok(()) + } +} + +pub fn set_screenshot(data: bytes::Bytes) { + SCREENSHOT.lock().unwrap().set_screenshot(data); +} + +pub fn handle_screenshot(action: String) -> String { + SCREENSHOT.lock().unwrap().handle_screenshot(action) +} diff --git a/src/common.rs b/src/common.rs index 5f9bd71fe..f8d5571b2 100644 --- a/src/common.rs +++ b/src/common.rs @@ -147,6 +147,16 @@ pub fn is_support_file_paste_if_macos(ver: &str) -> bool { hbb_common::get_version_number(ver) >= hbb_common::get_version_number("1.3.9") } +#[inline] +pub fn is_support_screenshot(ver: &str) -> bool { + is_support_multi_ui_session_num(hbb_common::get_version_number(ver)) +} + +#[inline] +pub fn is_support_screenshot_num(ver: i64) -> bool { + ver >= hbb_common::get_version_number("1.4.0") +} + // is server process, with "--server" args #[inline] pub fn is_server() -> bool { diff --git a/src/flutter.rs b/src/flutter.rs index 13c7bdd15..ff9a787e3 100644 --- a/src/flutter.rs +++ b/src/flutter.rs @@ -543,6 +543,25 @@ impl FlutterHandler { pub fn push_event(&self, name: &str, event: &[(&str, V)], excludes: &[&SessionID]) where V: Sized + Serialize + Clone, + { + self.push_event_(name, event, &[], excludes); + } + + pub fn push_event_to(&self, name: &str, event: &[(&str, V)], include: &[&SessionID]) + where + V: Sized + Serialize + Clone, + { + self.push_event_(name, event, include, &[]); + } + + pub fn push_event_( + &self, + name: &str, + event: &[(&str, V)], + includes: &[&SessionID], + excludes: &[&SessionID], + ) where + V: Sized + Serialize + Clone, { let mut h: HashMap<&str, serde_json::Value> = event.iter().map(|(k, v)| (*k, json!(*v))).collect(); @@ -550,11 +569,20 @@ impl FlutterHandler { h.insert("name", json!(name)); let out = serde_json::ser::to_string(&h).unwrap_or("".to_owned()); for (sid, session) in self.session_handlers.read().unwrap().iter() { - if excludes.contains(&sid) { - continue; + let mut push = false; + if includes.is_empty() { + if !excludes.contains(&sid) { + push = true; + } + } else { + if includes.contains(&sid) { + push = true; + } } - if let Some(stream) = &session.event_stream { - stream.add(EventToUI::Event(out.clone())); + if push { + if let Some(stream) = &session.event_stream { + stream.add(EventToUI::Event(out.clone())); + } } } } @@ -1067,6 +1095,16 @@ impl InvokeUiSession for FlutterHandler { &[], ); } + + fn handle_screenshot_resp(&self, sid: String, msg: String) { + match SessionID::from_str(&sid) { + Ok(sid) => self.push_event_to("screenshot", &[("msg", json!(msg))], &[&sid]), + Err(e) => { + // Unreachable! + log::error!("Failed to parse sid \"{}\", {}", sid, e); + } + } + } } impl FlutterHandler { diff --git a/src/flutter_ffi.rs b/src/flutter_ffi.rs index c446e6a83..6e3932bcb 100644 --- a/src/flutter_ffi.rs +++ b/src/flutter_ffi.rs @@ -250,6 +250,16 @@ pub fn session_refresh(session_id: SessionID, display: usize) { } } +pub fn session_take_screenshot(session_id: SessionID, display: usize) { + if let Some(s) = sessions::get_session_by_session_id(&session_id) { + s.take_screenshot(display as _, session_id.to_string()); + } +} + +pub fn session_handle_screenshot(session_id: SessionID, action: String) -> String { + crate::client::screenshot::handle_screenshot(action) +} + pub fn session_is_multi_ui_session(session_id: SessionID) -> SyncReturn { if let Some(session) = sessions::get_session_by_session_id(&session_id) { SyncReturn(session.is_multi_ui_session()) @@ -2461,6 +2471,27 @@ pub fn main_set_common(_key: String, _value: String) { } } +pub fn session_get_common_sync( + session_id: SessionID, + key: String, + param: String, +) -> SyncReturn> { + SyncReturn(session_get_common(session_id, key, param)) +} + +pub fn session_get_common(session_id: SessionID, key: String, param: String) -> Option { + if let Some(s) = sessions::get_session_by_session_id(&session_id) { + let v = if key == "is_screenshot_supported" { + s.is_screenshot_supported().to_string() + } else { + "".to_owned() + }; + Some(v) + } else { + None + } +} + #[cfg(target_os = "android")] pub mod server_side { use hbb_common::{config, log}; diff --git a/src/lang/ar.rs b/src/lang/ar.rs index 1112bea77..c3823b150 100644 --- a/src/lang/ar.rs +++ b/src/lang/ar.rs @@ -671,7 +671,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("printer-{}-ready-tip", "الطابعة {} جاهزة"), ("Install {} Printer", "تثبيت طابعة {}"), ("Outgoing Print Jobs", "وظائف الطباعة الصادرة"), - ("Incomming Print Jobs", "وظائف الطباعة الواردة"), + ("Incoming Print Jobs", "وظائف الطباعة الواردة"), ("Incoming Print Job", "وظيفة طباعة واردة"), ("use-the-default-printer-tip", "استخدم الطابعة الافتراضية"), ("use-the-selected-printer-tip", "استخدم الطابعة المحددة"), @@ -681,5 +681,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remote-printing-disallowed-text-tip", "الطباعة عن بُعد غير مسموح بها على هذا الجهاز"), ("save-settings-tip", "حفظ الإعدادات"), ("dont-show-again-tip", "لا تظهر هذا مرة أخرى"), + ("Take screenshot", ""), + ("Taking screenshot", ""), + ("screenshot-merged-screen-not-supported-tip", ""), + ("screenshot-action-tip", ""), + ("Save as", ""), + ("Copy to clipboard", ""), ].iter().cloned().collect(); } diff --git a/src/lang/be.rs b/src/lang/be.rs index b618d21f8..64e13ca8d 100644 --- a/src/lang/be.rs +++ b/src/lang/be.rs @@ -671,7 +671,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("printer-{}-ready-tip", ""), ("Install {} Printer", ""), ("Outgoing Print Jobs", ""), - ("Incomming Print Jobs", ""), + ("Incoming Print Jobs", ""), ("Incoming Print Job", ""), ("use-the-default-printer-tip", ""), ("use-the-selected-printer-tip", ""), @@ -681,5 +681,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remote-printing-disallowed-text-tip", ""), ("save-settings-tip", ""), ("dont-show-again-tip", ""), + ("Take screenshot", ""), + ("Taking screenshot", ""), + ("screenshot-merged-screen-not-supported-tip", ""), + ("screenshot-action-tip", ""), + ("Save as", ""), + ("Copy to clipboard", ""), ].iter().cloned().collect(); } diff --git a/src/lang/bg.rs b/src/lang/bg.rs index e11e7db4a..e14c5415d 100644 --- a/src/lang/bg.rs +++ b/src/lang/bg.rs @@ -671,7 +671,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("printer-{}-ready-tip", ""), ("Install {} Printer", ""), ("Outgoing Print Jobs", ""), - ("Incomming Print Jobs", ""), + ("Incoming Print Jobs", ""), ("Incoming Print Job", ""), ("use-the-default-printer-tip", ""), ("use-the-selected-printer-tip", ""), @@ -681,5 +681,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remote-printing-disallowed-text-tip", ""), ("save-settings-tip", ""), ("dont-show-again-tip", ""), + ("Take screenshot", ""), + ("Taking screenshot", ""), + ("screenshot-merged-screen-not-supported-tip", ""), + ("screenshot-action-tip", ""), + ("Save as", ""), + ("Copy to clipboard", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ca.rs b/src/lang/ca.rs index 8120ace84..74c1d696e 100644 --- a/src/lang/ca.rs +++ b/src/lang/ca.rs @@ -671,7 +671,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("printer-{}-ready-tip", ""), ("Install {} Printer", ""), ("Outgoing Print Jobs", ""), - ("Incomming Print Jobs", ""), + ("Incoming Print Jobs", ""), ("Incoming Print Job", ""), ("use-the-default-printer-tip", ""), ("use-the-selected-printer-tip", ""), @@ -681,5 +681,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remote-printing-disallowed-text-tip", ""), ("save-settings-tip", ""), ("dont-show-again-tip", ""), + ("Take screenshot", ""), + ("Taking screenshot", ""), + ("screenshot-merged-screen-not-supported-tip", ""), + ("screenshot-action-tip", ""), + ("Save as", ""), + ("Copy to clipboard", ""), ].iter().cloned().collect(); } diff --git a/src/lang/cn.rs b/src/lang/cn.rs index 0297af774..3cbea8a1e 100644 --- a/src/lang/cn.rs +++ b/src/lang/cn.rs @@ -653,7 +653,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Upload folder", "上传文件夹"), ("Upload files", "上传文件"), ("Clipboard is synchronized", "剪贴板已同步"), - ("Update client clipboard", "更新客户端的粘贴板"), + ("Update client clipboard", "更新客户端的剪贴板"), ("Untagged", "无标签"), ("new-version-of-{}-tip", "{} 版本更新"), ("Accessible devices", "可访问的设备"), @@ -671,7 +671,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("printer-{}-ready-tip", "{} 打印机已安装,您可以使用打印功能了。"), ("Install {} Printer", "安装 {} 打印机"), ("Outgoing Print Jobs", "传出的打印任务"), - ("Incomming Print Jobs", "传入的打印任务"), + ("Incoming Print Jobs", "传入的打印任务"), ("Incoming Print Job", "传入的打印任务"), ("use-the-default-printer-tip", "使用默认的打印机执行"), ("use-the-selected-printer-tip", "使用选择的打印机执行"), @@ -681,5 +681,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remote-printing-disallowed-text-tip", "被控端的权限设置拒绝了远程打印。"), ("save-settings-tip", "保存设置"), ("dont-show-again-tip", "不再显示此信息"), + ("Take screenshot", "截屏"), + ("Taking screenshot", "正在截屏"), + ("screenshot-merged-screen-not-supported-tip", "当前不支持多个屏幕的合并截屏,请切换到单个屏幕重试。"), + ("screenshot-action-tip", "请选择如何继续截屏。"), + ("Save as", "另存为"), + ("Copy to clipboard", "复制到剪贴板"), ].iter().cloned().collect(); } diff --git a/src/lang/cs.rs b/src/lang/cs.rs index 6f287a03c..f5f9acb68 100644 --- a/src/lang/cs.rs +++ b/src/lang/cs.rs @@ -671,7 +671,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("printer-{}-ready-tip", ""), ("Install {} Printer", ""), ("Outgoing Print Jobs", ""), - ("Incomming Print Jobs", ""), + ("Incoming Print Jobs", ""), ("Incoming Print Job", ""), ("use-the-default-printer-tip", ""), ("use-the-selected-printer-tip", ""), @@ -681,5 +681,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remote-printing-disallowed-text-tip", ""), ("save-settings-tip", ""), ("dont-show-again-tip", ""), + ("Take screenshot", ""), + ("Taking screenshot", ""), + ("screenshot-merged-screen-not-supported-tip", ""), + ("screenshot-action-tip", ""), + ("Save as", ""), + ("Copy to clipboard", ""), ].iter().cloned().collect(); } diff --git a/src/lang/da.rs b/src/lang/da.rs index 32bd52881..c8ea45327 100644 --- a/src/lang/da.rs +++ b/src/lang/da.rs @@ -671,7 +671,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("printer-{}-ready-tip", ""), ("Install {} Printer", ""), ("Outgoing Print Jobs", ""), - ("Incomming Print Jobs", ""), + ("Incoming Print Jobs", ""), ("Incoming Print Job", ""), ("use-the-default-printer-tip", ""), ("use-the-selected-printer-tip", ""), @@ -681,5 +681,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remote-printing-disallowed-text-tip", ""), ("save-settings-tip", ""), ("dont-show-again-tip", ""), + ("Take screenshot", ""), + ("Taking screenshot", ""), + ("screenshot-merged-screen-not-supported-tip", ""), + ("screenshot-action-tip", ""), + ("Save as", ""), + ("Copy to clipboard", ""), ].iter().cloned().collect(); } diff --git a/src/lang/de.rs b/src/lang/de.rs index aaee6afec..221b786dd 100644 --- a/src/lang/de.rs +++ b/src/lang/de.rs @@ -671,7 +671,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("printer-{}-ready-tip", "Der Drucker {} ist installiert und einsatzbereit."), ("Install {} Printer", "Drucker {} installieren"), ("Outgoing Print Jobs", "Ausgehende Druckaufträge"), - ("Incomming Print Jobs", "Eingehende Druckaufträge"), + ("Incoming Print Jobs", "Eingehende Druckaufträge"), ("Incoming Print Job", "Eingehender Druckauftrag"), ("use-the-default-printer-tip", "Standarddrucker verwenden"), ("use-the-selected-printer-tip", "Ausgewählten Drucker verwenden"), @@ -681,5 +681,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remote-printing-disallowed-text-tip", "Die Berechtigungseinstellungen der kontrollierten Seite verweigern den entfernten Druck."), ("save-settings-tip", "Einstellungen speichern"), ("dont-show-again-tip", "Nicht mehr anzeigen"), + ("Take screenshot", ""), + ("Taking screenshot", ""), + ("screenshot-merged-screen-not-supported-tip", ""), + ("screenshot-action-tip", ""), + ("Save as", ""), + ("Copy to clipboard", ""), ].iter().cloned().collect(); } diff --git a/src/lang/el.rs b/src/lang/el.rs index dc23cd03d..0688020ed 100644 --- a/src/lang/el.rs +++ b/src/lang/el.rs @@ -671,7 +671,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("printer-{}-ready-tip", ""), ("Install {} Printer", ""), ("Outgoing Print Jobs", ""), - ("Incomming Print Jobs", ""), + ("Incoming Print Jobs", ""), ("Incoming Print Job", ""), ("use-the-default-printer-tip", ""), ("use-the-selected-printer-tip", ""), @@ -681,5 +681,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remote-printing-disallowed-text-tip", ""), ("save-settings-tip", ""), ("dont-show-again-tip", ""), + ("Take screenshot", ""), + ("Taking screenshot", ""), + ("screenshot-merged-screen-not-supported-tip", ""), + ("screenshot-action-tip", ""), + ("Save as", ""), + ("Copy to clipboard", ""), ].iter().cloned().collect(); } diff --git a/src/lang/en.rs b/src/lang/en.rs index 04d661ec9..ec7c745b1 100644 --- a/src/lang/en.rs +++ b/src/lang/en.rs @@ -253,5 +253,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remote-printing-disallowed-text-tip", "The permission settings of the controlled side deny Remote Printing."), ("save-settings-tip", "Save settings"), ("dont-show-again-tip", "Don't show this again"), + ("screenshot-merged-screen-not-supported-tip", "Merging screenshots of multiple displays is currently not supported. Please switch to a single display and try again."), + ("screenshot-action-tip", "Please select how to continue with the screenshot."), ].iter().cloned().collect(); } diff --git a/src/lang/eo.rs b/src/lang/eo.rs index a92c36449..2cf311265 100644 --- a/src/lang/eo.rs +++ b/src/lang/eo.rs @@ -671,7 +671,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("printer-{}-ready-tip", ""), ("Install {} Printer", ""), ("Outgoing Print Jobs", ""), - ("Incomming Print Jobs", ""), + ("Incoming Print Jobs", ""), ("Incoming Print Job", ""), ("use-the-default-printer-tip", ""), ("use-the-selected-printer-tip", ""), @@ -681,5 +681,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remote-printing-disallowed-text-tip", ""), ("save-settings-tip", ""), ("dont-show-again-tip", ""), + ("Take screenshot", ""), + ("Taking screenshot", ""), + ("screenshot-merged-screen-not-supported-tip", ""), + ("screenshot-action-tip", ""), + ("Save as", ""), + ("Copy to clipboard", ""), ].iter().cloned().collect(); } diff --git a/src/lang/es.rs b/src/lang/es.rs index 9c7528f02..2e7a1d823 100644 --- a/src/lang/es.rs +++ b/src/lang/es.rs @@ -671,7 +671,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("printer-{}-ready-tip", "La impresora {} está instalada y lista para usar."), ("Install {} Printer", "Instalar la impresora {}"), ("Outgoing Print Jobs", "Tareas salientes de impresión"), - ("Incomming Print Jobs", "Tareas entrantes de impresión"), + ("Incoming Print Jobs", "Tareas entrantes de impresión"), ("Incoming Print Job", "Trabajo entrante de impresión"), ("use-the-default-printer-tip", "Usar la impresora predeterminada"), ("use-the-selected-printer-tip", "Usar la impresora seleccionada"), @@ -681,5 +681,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remote-printing-disallowed-text-tip", "Los ajustes de permisos del lado controlado no permiten la impresión remota."), ("save-settings-tip", "Guardar ajustes"), ("dont-show-again-tip", "No volver a mostrar"), + ("Take screenshot", ""), + ("Taking screenshot", ""), + ("screenshot-merged-screen-not-supported-tip", ""), + ("screenshot-action-tip", ""), + ("Save as", ""), + ("Copy to clipboard", ""), ].iter().cloned().collect(); } diff --git a/src/lang/et.rs b/src/lang/et.rs index 292271185..501927ad8 100644 --- a/src/lang/et.rs +++ b/src/lang/et.rs @@ -671,7 +671,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("printer-{}-ready-tip", ""), ("Install {} Printer", ""), ("Outgoing Print Jobs", ""), - ("Incomming Print Jobs", ""), + ("Incoming Print Jobs", ""), ("Incoming Print Job", ""), ("use-the-default-printer-tip", ""), ("use-the-selected-printer-tip", ""), @@ -681,5 +681,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remote-printing-disallowed-text-tip", ""), ("save-settings-tip", ""), ("dont-show-again-tip", ""), + ("Take screenshot", ""), + ("Taking screenshot", ""), + ("screenshot-merged-screen-not-supported-tip", ""), + ("screenshot-action-tip", ""), + ("Save as", ""), + ("Copy to clipboard", ""), ].iter().cloned().collect(); } diff --git a/src/lang/eu.rs b/src/lang/eu.rs index 851aa25fd..fd7f7407f 100644 --- a/src/lang/eu.rs +++ b/src/lang/eu.rs @@ -671,7 +671,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("printer-{}-ready-tip", ""), ("Install {} Printer", ""), ("Outgoing Print Jobs", ""), - ("Incomming Print Jobs", ""), + ("Incoming Print Jobs", ""), ("Incoming Print Job", ""), ("use-the-default-printer-tip", ""), ("use-the-selected-printer-tip", ""), @@ -681,5 +681,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remote-printing-disallowed-text-tip", ""), ("save-settings-tip", ""), ("dont-show-again-tip", ""), + ("Take screenshot", ""), + ("Taking screenshot", ""), + ("screenshot-merged-screen-not-supported-tip", ""), + ("screenshot-action-tip", ""), + ("Save as", ""), + ("Copy to clipboard", ""), ].iter().cloned().collect(); } diff --git a/src/lang/fa.rs b/src/lang/fa.rs index 51bc3f4ef..44bfc6754 100644 --- a/src/lang/fa.rs +++ b/src/lang/fa.rs @@ -671,7 +671,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("printer-{}-ready-tip", "چاپگر {} آماده است"), ("Install {} Printer", "نصب چاپگر {}"), ("Outgoing Print Jobs", "وظایف چاپ خروجی"), - ("Incomming Print Jobs", "وظایف چاپ ورودی"), + ("Incoming Print Jobs", "وظایف چاپ ورودی"), ("Incoming Print Job", "وظیفه چاپ ورودی"), ("use-the-default-printer-tip", "از چاپگر پیش‌فرض استفاده کنید"), ("use-the-selected-printer-tip", "از چاپگر انتخاب‌شده استفاده کنید"), @@ -681,5 +681,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remote-printing-disallowed-text-tip", "شما مجوز لازم برای چاپ از راه دور را ندارید"), ("save-settings-tip", "تنظیمات را ذخیره کنید"), ("dont-show-again-tip", "دیگر نمایش داده نشود"), + ("Take screenshot", ""), + ("Taking screenshot", ""), + ("screenshot-merged-screen-not-supported-tip", ""), + ("screenshot-action-tip", ""), + ("Save as", ""), + ("Copy to clipboard", ""), ].iter().cloned().collect(); } diff --git a/src/lang/fr.rs b/src/lang/fr.rs index d599e6535..f15a762d4 100644 --- a/src/lang/fr.rs +++ b/src/lang/fr.rs @@ -671,7 +671,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("printer-{}-ready-tip", "L’imprimante {} est installée et opérationnelle."), ("Install {} Printer", "Installer l’imprimante {}"), ("Outgoing Print Jobs", "Impressions sortantes"), - ("Incomming Print Jobs", "Impressions entrantes"), + ("Incoming Print Jobs", "Impressions entrantes"), ("Incoming Print Job", "Impression entrante"), ("use-the-default-printer-tip", "Utiliser l’imprimante par défaut"), ("use-the-selected-printer-tip", "Utiliser l’imprimante sélectionnée"), @@ -681,5 +681,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remote-printing-disallowed-text-tip", "Les paramètres de l’appareil contrôlé n’autorisent pas l’impression à distance."), ("save-settings-tip", "Enregistrer les paramètres"), ("dont-show-again-tip", "Ne plus afficher"), + ("Take screenshot", ""), + ("Taking screenshot", ""), + ("screenshot-merged-screen-not-supported-tip", ""), + ("screenshot-action-tip", ""), + ("Save as", ""), + ("Copy to clipboard", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ge.rs b/src/lang/ge.rs index 64863f28a..18d58a8c5 100644 --- a/src/lang/ge.rs +++ b/src/lang/ge.rs @@ -239,7 +239,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Empty", "ცარიელი"), ("Invalid folder name", "არასწორი საქაღალდის სახელი"), ("Socks5 Proxy", "SOCKS5-პროქსი"), - ("Proxy", "პროქსი"), + ("Socks5/Http(s) Proxy", ""), ("Discovered", "ნაპოვნია"), ("install_daemon_tip", "ჩატვირთვისას გასაშვებად საჭიროა სისტემური სერვისის დაყენება"), ("Remote ID", "დაშორებული ID"), @@ -567,7 +567,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("id_input_tip", "შეგიძლიათ შეიყვანოთ იდენტიფიკატორი, პირდაპირი IP მისამართი ან დომენი პორტით (<დომენი>:<პორტი>).\nთუ გჭირდებათ წვდომა მოწყობილობაზე სხვა სერვერზე, დაამატეთ სერვერის მისამართი (@<სერვერის_მისამართი>?key=<გასაღების_მნიშვნელობა>), მაგალითად:\n9123456234@192.168.16.1:21117?key=5Qbwsde3unUcJBtrx9ZkvUmwFNoExHzpryHuPUdqlWM=.\nთუ გჭირდებათ წვდომა მოწყობილობაზე საჯარო სერვერზე, შეიყვანეთ \"@public\", გასაღები საჯარო სერვერისთვის არ არის საჭირო."), ("privacy_mode_impl_mag_tip", "რეჟიმი 1"), ("privacy_mode_impl_virtual_display_tip", "რეჟიმი 2"), - ("privacy_mode_impl_virtual_display_tip", "რეჟიმი 2"), ("Enter privacy mode", "კონფიდენციალურობის რეჟიმის ჩართვა"), ("Exit privacy mode", "კონფიდენციალურობის რეჟიმის გამორთვა"), ("idd_not_support_under_win10_2004_tip", "არაპირდაპირი ჩვენების დრაივერი არ არის მხარდაჭერილი. საჭიროა Windows 10 ვერსია 2004 ან უფრო ახალი."), @@ -659,7 +658,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("new-version-of-{}-tip", "ხელმისაწვდომია ახალი ვერსია {}"), ("Accessible devices", "ხელმისაწვდომი მოწყობილობები"), ("View camera", "კამერის ნახვა"), - ("upgrade_remote_rustdesk_client_to{}_tip", "განაახლეთ RustDesk კლიენტი ვერსიამდე {} ან უფრო ახალი დისტანციურ მხარეზე!"), + ("upgrade_remote_rustdesk_client_to_{}_tip", ""), ("view_camera_unsupported_tip", "დისტანციური მოწყობილობა არ უჭერს მხარს კამერის ნახვას."), ("Enable camera", "კამერის ჩართვა"), ("No cameras", "კამერა არ არის"), @@ -672,7 +671,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("printer-{}-ready-tip", "პრინტერი {} დაინსტალირებულია და მზად არის გამოსაყენებლად."), ("Install {} Printer", "დააინსტალირეთ პრინტერი {}"), ("Outgoing Print Jobs", "გამავალი ბეჭდვის დავალება"), - ("Incomming Print Jobs", "შემომავალი ბეჭდვის დავალება"), + ("Incoming Print Jobs", "შემომავალი ბეჭდვის დავალება"), ("Incoming Print Job", "შემომავალი ბეჭდვის დავალება"), ("use-the-default-printer-tip", "ნაგულისხმევი პრინტერის გამოყენება"), ("use-the-selected-printer-tip", "არჩეული პრინტერის გამოყენება"), @@ -682,5 +681,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remote-printing-disallowed-text-tip", "მართულ მხარეზე უფლებების პარამეტრები კრძალავს დისტანციურ ბეჭდვას."), ("save-settings-tip", "პარამეტრების შენახვა"), ("dont-show-again-tip", "აღარ აჩვენოთ"), - ].iter().cloned().collect(); + ("Take screenshot", ""), + ("Taking screenshot", ""), + ("screenshot-merged-screen-not-supported-tip", ""), + ("screenshot-action-tip", ""), + ("Save as", ""), + ("Copy to clipboard", ""), + ].iter().cloned().collect(); } diff --git a/src/lang/he.rs b/src/lang/he.rs index bdbeee146..0d9e6d40c 100644 --- a/src/lang/he.rs +++ b/src/lang/he.rs @@ -671,7 +671,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("printer-{}-ready-tip", ""), ("Install {} Printer", ""), ("Outgoing Print Jobs", ""), - ("Incomming Print Jobs", ""), + ("Incoming Print Jobs", ""), ("Incoming Print Job", ""), ("use-the-default-printer-tip", ""), ("use-the-selected-printer-tip", ""), @@ -681,5 +681,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remote-printing-disallowed-text-tip", ""), ("save-settings-tip", ""), ("dont-show-again-tip", ""), + ("Take screenshot", ""), + ("Taking screenshot", ""), + ("screenshot-merged-screen-not-supported-tip", ""), + ("screenshot-action-tip", ""), + ("Save as", ""), + ("Copy to clipboard", ""), ].iter().cloned().collect(); } diff --git a/src/lang/hr.rs b/src/lang/hr.rs index 46c9795b4..e76cb732e 100644 --- a/src/lang/hr.rs +++ b/src/lang/hr.rs @@ -671,7 +671,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("printer-{}-ready-tip", ""), ("Install {} Printer", ""), ("Outgoing Print Jobs", ""), - ("Incomming Print Jobs", ""), + ("Incoming Print Jobs", ""), ("Incoming Print Job", ""), ("use-the-default-printer-tip", ""), ("use-the-selected-printer-tip", ""), @@ -681,5 +681,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remote-printing-disallowed-text-tip", ""), ("save-settings-tip", ""), ("dont-show-again-tip", ""), + ("Take screenshot", ""), + ("Taking screenshot", ""), + ("screenshot-merged-screen-not-supported-tip", ""), + ("screenshot-action-tip", ""), + ("Save as", ""), + ("Copy to clipboard", ""), ].iter().cloned().collect(); } diff --git a/src/lang/hu.rs b/src/lang/hu.rs index d297c9ed0..2db839057 100644 --- a/src/lang/hu.rs +++ b/src/lang/hu.rs @@ -671,7 +671,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("printer-{}-ready-tip", "A(z) {} nyomtató készen áll"), ("Install {} Printer", "A(z) {} nyomtató nyomtató telepítése"), ("Outgoing Print Jobs", "Kimenő nyomtatási feladatok"), - ("Incomming Print Jobs", "Bejövő nyomtatási feladatok"), + ("Incoming Print Jobs", "Bejövő nyomtatási feladatok"), ("Incoming Print Job", "Bejövő nyomtatási feladat"), ("use-the-default-printer-tip", "Alapértelmezett nyomtató használata"), ("use-the-selected-printer-tip", "Kiválasztott nyomtató használata"), @@ -681,5 +681,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remote-printing-disallowed-text-tip", "A távoli nyomtatás nincs engedélyezve"), ("save-settings-tip", "Beállítások mentése"), ("dont-show-again-tip", "Ne jelenítse meg újra"), + ("Take screenshot", ""), + ("Taking screenshot", ""), + ("screenshot-merged-screen-not-supported-tip", ""), + ("screenshot-action-tip", ""), + ("Save as", ""), + ("Copy to clipboard", ""), ].iter().cloned().collect(); } diff --git a/src/lang/id.rs b/src/lang/id.rs index f397442af..06ca80024 100644 --- a/src/lang/id.rs +++ b/src/lang/id.rs @@ -671,7 +671,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("printer-{}-ready-tip", ""), ("Install {} Printer", ""), ("Outgoing Print Jobs", ""), - ("Incomming Print Jobs", ""), + ("Incoming Print Jobs", ""), ("Incoming Print Job", ""), ("use-the-default-printer-tip", ""), ("use-the-selected-printer-tip", ""), @@ -681,5 +681,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remote-printing-disallowed-text-tip", ""), ("save-settings-tip", ""), ("dont-show-again-tip", ""), + ("Take screenshot", ""), + ("Taking screenshot", ""), + ("screenshot-merged-screen-not-supported-tip", ""), + ("screenshot-action-tip", ""), + ("Save as", ""), + ("Copy to clipboard", ""), ].iter().cloned().collect(); } diff --git a/src/lang/it.rs b/src/lang/it.rs index 524c86255..2f029ab5a 100644 --- a/src/lang/it.rs +++ b/src/lang/it.rs @@ -671,7 +671,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("printer-{}-ready-tip", "La stampante {} è installata e pronta all'uso."), ("Install {} Printer", "Installa la stampante {}"), ("Outgoing Print Jobs", "Lavori di stampa in uscita"), - ("Incomming Print Jobs", "Lavori di stampa in entrata"), + ("Incoming Print Jobs", "Lavori di stampa in entrata"), ("Incoming Print Job", "Lavoro di stampa in entrata"), ("use-the-default-printer-tip", "Usa la stampante predefinita"), ("use-the-selected-printer-tip", "Usa la stampante selezionata"), @@ -681,5 +681,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remote-printing-disallowed-text-tip", "Le impostazioni di autorizzazione del lato controllato negano la stampa remota."), ("save-settings-tip", "Salva impostazioni"), ("dont-show-again-tip", "Non visualizzare più questo messaggio"), + ("Take screenshot", ""), + ("Taking screenshot", ""), + ("screenshot-merged-screen-not-supported-tip", ""), + ("screenshot-action-tip", ""), + ("Save as", ""), + ("Copy to clipboard", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ja.rs b/src/lang/ja.rs index a18d6d8e6..bbfe4bb99 100644 --- a/src/lang/ja.rs +++ b/src/lang/ja.rs @@ -671,7 +671,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("printer-{}-ready-tip", ""), ("Install {} Printer", ""), ("Outgoing Print Jobs", ""), - ("Incomming Print Jobs", ""), + ("Incoming Print Jobs", ""), ("Incoming Print Job", ""), ("use-the-default-printer-tip", ""), ("use-the-selected-printer-tip", ""), @@ -681,5 +681,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remote-printing-disallowed-text-tip", ""), ("save-settings-tip", ""), ("dont-show-again-tip", ""), + ("Take screenshot", ""), + ("Taking screenshot", ""), + ("screenshot-merged-screen-not-supported-tip", ""), + ("screenshot-action-tip", ""), + ("Save as", ""), + ("Copy to clipboard", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ko.rs b/src/lang/ko.rs index d104dd452..741864abd 100644 --- a/src/lang/ko.rs +++ b/src/lang/ko.rs @@ -671,7 +671,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("printer-{}-ready-tip", ""), ("Install {} Printer", ""), ("Outgoing Print Jobs", ""), - ("Incomming Print Jobs", ""), + ("Incoming Print Jobs", ""), ("Incoming Print Job", ""), ("use-the-default-printer-tip", ""), ("use-the-selected-printer-tip", ""), @@ -681,5 +681,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remote-printing-disallowed-text-tip", ""), ("save-settings-tip", ""), ("dont-show-again-tip", ""), + ("Take screenshot", ""), + ("Taking screenshot", ""), + ("screenshot-merged-screen-not-supported-tip", ""), + ("screenshot-action-tip", ""), + ("Save as", ""), + ("Copy to clipboard", ""), ].iter().cloned().collect(); } diff --git a/src/lang/kz.rs b/src/lang/kz.rs index 82b095e50..a755e77e0 100644 --- a/src/lang/kz.rs +++ b/src/lang/kz.rs @@ -671,7 +671,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("printer-{}-ready-tip", ""), ("Install {} Printer", ""), ("Outgoing Print Jobs", ""), - ("Incomming Print Jobs", ""), + ("Incoming Print Jobs", ""), ("Incoming Print Job", ""), ("use-the-default-printer-tip", ""), ("use-the-selected-printer-tip", ""), @@ -681,5 +681,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remote-printing-disallowed-text-tip", ""), ("save-settings-tip", ""), ("dont-show-again-tip", ""), + ("Take screenshot", ""), + ("Taking screenshot", ""), + ("screenshot-merged-screen-not-supported-tip", ""), + ("screenshot-action-tip", ""), + ("Save as", ""), + ("Copy to clipboard", ""), ].iter().cloned().collect(); } diff --git a/src/lang/lt.rs b/src/lang/lt.rs index 07bbf2ac9..acaccd777 100644 --- a/src/lang/lt.rs +++ b/src/lang/lt.rs @@ -671,7 +671,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("printer-{}-ready-tip", ""), ("Install {} Printer", ""), ("Outgoing Print Jobs", ""), - ("Incomming Print Jobs", ""), + ("Incoming Print Jobs", ""), ("Incoming Print Job", ""), ("use-the-default-printer-tip", ""), ("use-the-selected-printer-tip", ""), @@ -681,5 +681,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remote-printing-disallowed-text-tip", ""), ("save-settings-tip", ""), ("dont-show-again-tip", ""), + ("Take screenshot", ""), + ("Taking screenshot", ""), + ("screenshot-merged-screen-not-supported-tip", ""), + ("screenshot-action-tip", ""), + ("Save as", ""), + ("Copy to clipboard", ""), ].iter().cloned().collect(); } diff --git a/src/lang/lv.rs b/src/lang/lv.rs index 8601f48fd..23f307958 100644 --- a/src/lang/lv.rs +++ b/src/lang/lv.rs @@ -671,7 +671,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("printer-{}-ready-tip", "Printeris {} ir instalēts un gatavs lietošanai."), ("Install {} Printer", "Instalēt {} printeri"), ("Outgoing Print Jobs", "Izejošie drukas darbi"), - ("Incomming Print Jobs", "Ienākošie drukas darbi"), + ("Incoming Print Jobs", "Ienākošie drukas darbi"), ("Incoming Print Job", "Ienākošais drukas darbs"), ("use-the-default-printer-tip", "Izmantot noklusējuma printeri"), ("use-the-selected-printer-tip", "Izmantot atlasīto printeri"), @@ -681,5 +681,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remote-printing-disallowed-text-tip", "Kontrolētās puses atļauju iestatījumi liedz attālo drukāšanu."), ("save-settings-tip", "Saglabāt iestatījumus"), ("dont-show-again-tip", "Nerādīt šo vēlreiz"), + ("Take screenshot", ""), + ("Taking screenshot", ""), + ("screenshot-merged-screen-not-supported-tip", ""), + ("screenshot-action-tip", ""), + ("Save as", ""), + ("Copy to clipboard", ""), ].iter().cloned().collect(); } diff --git a/src/lang/nb.rs b/src/lang/nb.rs index a6d909a9b..96cd0682d 100644 --- a/src/lang/nb.rs +++ b/src/lang/nb.rs @@ -671,7 +671,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("printer-{}-ready-tip", ""), ("Install {} Printer", ""), ("Outgoing Print Jobs", ""), - ("Incomming Print Jobs", ""), + ("Incoming Print Jobs", ""), ("Incoming Print Job", ""), ("use-the-default-printer-tip", ""), ("use-the-selected-printer-tip", ""), @@ -681,5 +681,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remote-printing-disallowed-text-tip", ""), ("save-settings-tip", ""), ("dont-show-again-tip", ""), + ("Take screenshot", ""), + ("Taking screenshot", ""), + ("screenshot-merged-screen-not-supported-tip", ""), + ("screenshot-action-tip", ""), + ("Save as", ""), + ("Copy to clipboard", ""), ].iter().cloned().collect(); } diff --git a/src/lang/nl.rs b/src/lang/nl.rs index 2c906ea9e..d9b14aff7 100644 --- a/src/lang/nl.rs +++ b/src/lang/nl.rs @@ -671,7 +671,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("printer-{}-ready-tip", "De printer {} is geïnstalleerd en klaar voor gebruik."), ("Install {} Printer", "Installeer {} Printer"), ("Outgoing Print Jobs", "Uitgaande Afdruktaken"), - ("Incomming Print Jobs", "Inkomende Afdruktaken"), + ("Incoming Print Jobs", "Inkomende Afdruktaken"), ("Incoming Print Job", "Inkomende Afdruktaak"), ("use-the-default-printer-tip", "Gebruik de standaard printer"), ("use-the-selected-printer-tip", "Gebruik de geselecteerde printer"), @@ -681,5 +681,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remote-printing-disallowed-text-tip", "Machtigingsinstellingen aan beheerde zijde verhinderen afdrukken op afstand."), ("save-settings-tip", "Instellingen opslaan"), ("dont-show-again-tip", "Dit bericht wordt niet meer weergegeven"), + ("Take screenshot", ""), + ("Taking screenshot", ""), + ("screenshot-merged-screen-not-supported-tip", ""), + ("screenshot-action-tip", ""), + ("Save as", ""), + ("Copy to clipboard", ""), ].iter().cloned().collect(); } diff --git a/src/lang/pl.rs b/src/lang/pl.rs index 70a3e25a9..016fbc1a3 100644 --- a/src/lang/pl.rs +++ b/src/lang/pl.rs @@ -671,7 +671,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("printer-{}-ready-tip", "Drukarka {} jest zainstalowana i gotowa do użycia."), ("Install {} Printer", "Zainstaluj drukarkę {}"), ("Outgoing Print Jobs", "Wychodzące zadania drukowania"), - ("Incomming Print Jobs", "Przychodzące zadania drukowania"), + ("Incoming Print Jobs", "Przychodzące zadania drukowania"), ("Incoming Print Job", "Przychodzące zadanie drukowania"), ("use-the-default-printer-tip", "Użyj domyślnej drukarki"), ("use-the-selected-printer-tip", "Użyj wybranej drukarki"), @@ -681,5 +681,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remote-printing-disallowed-text-tip", "Ustawienia uprawnień po zdalnej stronie uniemożliwiają zdalne drukowanie."), ("save-settings-tip", "Zapisz ustawienia"), ("dont-show-again-tip", "Nie pokazuj więcej"), + ("Take screenshot", ""), + ("Taking screenshot", ""), + ("screenshot-merged-screen-not-supported-tip", ""), + ("screenshot-action-tip", ""), + ("Save as", ""), + ("Copy to clipboard", ""), ].iter().cloned().collect(); } diff --git a/src/lang/pt_PT.rs b/src/lang/pt_PT.rs index 155c8e161..949771657 100644 --- a/src/lang/pt_PT.rs +++ b/src/lang/pt_PT.rs @@ -671,7 +671,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("printer-{}-ready-tip", ""), ("Install {} Printer", ""), ("Outgoing Print Jobs", ""), - ("Incomming Print Jobs", ""), + ("Incoming Print Jobs", ""), ("Incoming Print Job", ""), ("use-the-default-printer-tip", ""), ("use-the-selected-printer-tip", ""), @@ -681,5 +681,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remote-printing-disallowed-text-tip", ""), ("save-settings-tip", ""), ("dont-show-again-tip", ""), + ("Take screenshot", ""), + ("Taking screenshot", ""), + ("screenshot-merged-screen-not-supported-tip", ""), + ("screenshot-action-tip", ""), + ("Save as", ""), + ("Copy to clipboard", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ptbr.rs b/src/lang/ptbr.rs index dc8faa18a..f239d72a8 100644 --- a/src/lang/ptbr.rs +++ b/src/lang/ptbr.rs @@ -671,7 +671,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("printer-{}-ready-tip", "A impressora {} está instalada e operacional."), ("Install {} Printer", "Instalar impressora {}"), ("Outgoing Print Jobs", "Trabalhos de impressão enviados"), - ("Incomming Print Jobs", "Trabalhos de impressão recebidos"), + ("Incoming Print Jobs", "Trabalhos de impressão recebidos"), ("Incoming Print Job", "Impressão recebida"), ("use-the-default-printer-tip", "Usar impressora padrão"), ("use-the-selected-printer-tip", "Usar impressora selecionada"), @@ -681,5 +681,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remote-printing-disallowed-text-tip", "As configurações do dispositivo controlado não permitem impressão remota."), ("save-settings-tip", "Salvar configurações"), ("dont-show-again-tip", "Não mostrar novamente"), + ("Take screenshot", ""), + ("Taking screenshot", ""), + ("screenshot-merged-screen-not-supported-tip", ""), + ("screenshot-action-tip", ""), + ("Save as", ""), + ("Copy to clipboard", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ro.rs b/src/lang/ro.rs index 9703a4d15..5d074a77a 100644 --- a/src/lang/ro.rs +++ b/src/lang/ro.rs @@ -671,7 +671,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("printer-{}-ready-tip", ""), ("Install {} Printer", ""), ("Outgoing Print Jobs", ""), - ("Incomming Print Jobs", ""), + ("Incoming Print Jobs", ""), ("Incoming Print Job", ""), ("use-the-default-printer-tip", ""), ("use-the-selected-printer-tip", ""), @@ -681,5 +681,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remote-printing-disallowed-text-tip", ""), ("save-settings-tip", ""), ("dont-show-again-tip", ""), + ("Take screenshot", ""), + ("Taking screenshot", ""), + ("screenshot-merged-screen-not-supported-tip", ""), + ("screenshot-action-tip", ""), + ("Save as", ""), + ("Copy to clipboard", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ru.rs b/src/lang/ru.rs index d9322bfe1..f970cc8cf 100644 --- a/src/lang/ru.rs +++ b/src/lang/ru.rs @@ -671,7 +671,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("printer-{}-ready-tip", "Принтер {} установлен и готов к использованию."), ("Install {} Printer", "Установить принтер {}"), ("Outgoing Print Jobs", "Исходящее задание печати"), - ("Incomming Print Jobs", "Входящее задание печати"), + ("Incoming Print Jobs", "Входящее задание печати"), ("Incoming Print Job", "Входящее задание печати"), ("use-the-default-printer-tip", "Использовать принтер по умолчанию"), ("use-the-selected-printer-tip", "Использовать выбранный принтер"), @@ -681,5 +681,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remote-printing-disallowed-text-tip", "Настройки разрешений на управляемой стороне запрещают удалённую печать."), ("save-settings-tip", "Сохранить настройки"), ("dont-show-again-tip", "Больше не показывать"), + ("Take screenshot", ""), + ("Taking screenshot", ""), + ("screenshot-merged-screen-not-supported-tip", ""), + ("screenshot-action-tip", ""), + ("Save as", ""), + ("Copy to clipboard", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sc.rs b/src/lang/sc.rs index 71e31e298..ede1a4bf9 100644 --- a/src/lang/sc.rs +++ b/src/lang/sc.rs @@ -671,7 +671,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("printer-{}-ready-tip", "S'imprentadora {} est installada e pronta pro s'impreu."), ("Install {} Printer", "Installa s'imprentadora {}"), ("Outgoing Print Jobs", "Traballos de imprenta in essida"), - ("Incomming Print Jobs", "Traballos de imprenta in intrada"), + ("Incoming Print Jobs", "Traballos de imprenta in intrada"), ("Incoming Print Job", "Traballu de imprenta in intrada"), ("use-the-default-printer-tip", "Imprea s'imprentadora predefinida"), ("use-the-selected-printer-tip", "Imprea s'imprentadora seletzionada"), @@ -681,5 +681,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remote-printing-disallowed-text-tip", "Sas impostatziones de sos permissos de s'ala controllada negant s'imprenta remota."), ("save-settings-tip", "Sarva sas impostatziones"), ("dont-show-again-tip", "Non mustres prus custu messàgiu"), + ("Take screenshot", ""), + ("Taking screenshot", ""), + ("screenshot-merged-screen-not-supported-tip", ""), + ("screenshot-action-tip", ""), + ("Save as", ""), + ("Copy to clipboard", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sk.rs b/src/lang/sk.rs index 9f96cb60e..95f336f1c 100644 --- a/src/lang/sk.rs +++ b/src/lang/sk.rs @@ -671,7 +671,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("printer-{}-ready-tip", ""), ("Install {} Printer", ""), ("Outgoing Print Jobs", ""), - ("Incomming Print Jobs", ""), + ("Incoming Print Jobs", ""), ("Incoming Print Job", ""), ("use-the-default-printer-tip", ""), ("use-the-selected-printer-tip", ""), @@ -681,5 +681,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remote-printing-disallowed-text-tip", ""), ("save-settings-tip", ""), ("dont-show-again-tip", ""), + ("Take screenshot", ""), + ("Taking screenshot", ""), + ("screenshot-merged-screen-not-supported-tip", ""), + ("screenshot-action-tip", ""), + ("Save as", ""), + ("Copy to clipboard", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sl.rs b/src/lang/sl.rs index 405208f52..6156d3eb9 100755 --- a/src/lang/sl.rs +++ b/src/lang/sl.rs @@ -671,7 +671,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("printer-{}-ready-tip", ""), ("Install {} Printer", ""), ("Outgoing Print Jobs", ""), - ("Incomming Print Jobs", ""), + ("Incoming Print Jobs", ""), ("Incoming Print Job", ""), ("use-the-default-printer-tip", ""), ("use-the-selected-printer-tip", ""), @@ -681,5 +681,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remote-printing-disallowed-text-tip", ""), ("save-settings-tip", ""), ("dont-show-again-tip", ""), + ("Take screenshot", ""), + ("Taking screenshot", ""), + ("screenshot-merged-screen-not-supported-tip", ""), + ("screenshot-action-tip", ""), + ("Save as", ""), + ("Copy to clipboard", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sq.rs b/src/lang/sq.rs index b1467f661..4564f1757 100644 --- a/src/lang/sq.rs +++ b/src/lang/sq.rs @@ -671,7 +671,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("printer-{}-ready-tip", ""), ("Install {} Printer", ""), ("Outgoing Print Jobs", ""), - ("Incomming Print Jobs", ""), + ("Incoming Print Jobs", ""), ("Incoming Print Job", ""), ("use-the-default-printer-tip", ""), ("use-the-selected-printer-tip", ""), @@ -681,5 +681,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remote-printing-disallowed-text-tip", ""), ("save-settings-tip", ""), ("dont-show-again-tip", ""), + ("Take screenshot", ""), + ("Taking screenshot", ""), + ("screenshot-merged-screen-not-supported-tip", ""), + ("screenshot-action-tip", ""), + ("Save as", ""), + ("Copy to clipboard", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sr.rs b/src/lang/sr.rs index 8e469d9be..b482c154e 100644 --- a/src/lang/sr.rs +++ b/src/lang/sr.rs @@ -671,7 +671,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("printer-{}-ready-tip", ""), ("Install {} Printer", ""), ("Outgoing Print Jobs", ""), - ("Incomming Print Jobs", ""), + ("Incoming Print Jobs", ""), ("Incoming Print Job", ""), ("use-the-default-printer-tip", ""), ("use-the-selected-printer-tip", ""), @@ -681,5 +681,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remote-printing-disallowed-text-tip", ""), ("save-settings-tip", ""), ("dont-show-again-tip", ""), + ("Take screenshot", ""), + ("Taking screenshot", ""), + ("screenshot-merged-screen-not-supported-tip", ""), + ("screenshot-action-tip", ""), + ("Save as", ""), + ("Copy to clipboard", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sv.rs b/src/lang/sv.rs index 5d3135719..8c3e28e5d 100644 --- a/src/lang/sv.rs +++ b/src/lang/sv.rs @@ -671,7 +671,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("printer-{}-ready-tip", ""), ("Install {} Printer", ""), ("Outgoing Print Jobs", ""), - ("Incomming Print Jobs", ""), + ("Incoming Print Jobs", ""), ("Incoming Print Job", ""), ("use-the-default-printer-tip", ""), ("use-the-selected-printer-tip", ""), @@ -681,5 +681,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remote-printing-disallowed-text-tip", ""), ("save-settings-tip", ""), ("dont-show-again-tip", ""), + ("Take screenshot", ""), + ("Taking screenshot", ""), + ("screenshot-merged-screen-not-supported-tip", ""), + ("screenshot-action-tip", ""), + ("Save as", ""), + ("Copy to clipboard", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ta.rs b/src/lang/ta.rs index d4d176090..53a648504 100644 --- a/src/lang/ta.rs +++ b/src/lang/ta.rs @@ -671,7 +671,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("printer-{}-ready-tip", ""), ("Install {} Printer", ""), ("Outgoing Print Jobs", ""), - ("Incomming Print Jobs", ""), + ("Incoming Print Jobs", ""), ("Incoming Print Job", ""), ("use-the-default-printer-tip", ""), ("use-the-selected-printer-tip", ""), @@ -681,5 +681,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remote-printing-disallowed-text-tip", ""), ("save-settings-tip", ""), ("dont-show-again-tip", ""), + ("Take screenshot", ""), + ("Taking screenshot", ""), + ("screenshot-merged-screen-not-supported-tip", ""), + ("screenshot-action-tip", ""), + ("Save as", ""), + ("Copy to clipboard", ""), ].iter().cloned().collect(); } diff --git a/src/lang/template.rs b/src/lang/template.rs index 064a2f657..7df9025ed 100644 --- a/src/lang/template.rs +++ b/src/lang/template.rs @@ -671,7 +671,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("printer-{}-ready-tip", ""), ("Install {} Printer", ""), ("Outgoing Print Jobs", ""), - ("Incomming Print Jobs", ""), + ("Incoming Print Jobs", ""), ("Incoming Print Job", ""), ("use-the-default-printer-tip", ""), ("use-the-selected-printer-tip", ""), @@ -681,5 +681,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remote-printing-disallowed-text-tip", ""), ("save-settings-tip", ""), ("dont-show-again-tip", ""), + ("Take screenshot", ""), + ("Taking screenshot", ""), + ("screenshot-merged-screen-not-supported-tip", ""), + ("screenshot-action-tip", ""), + ("Save as", ""), + ("Copy to clipboard", ""), ].iter().cloned().collect(); } diff --git a/src/lang/th.rs b/src/lang/th.rs index 010bd3c2e..f46237fd2 100644 --- a/src/lang/th.rs +++ b/src/lang/th.rs @@ -671,7 +671,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("printer-{}-ready-tip", ""), ("Install {} Printer", ""), ("Outgoing Print Jobs", ""), - ("Incomming Print Jobs", ""), + ("Incoming Print Jobs", ""), ("Incoming Print Job", ""), ("use-the-default-printer-tip", ""), ("use-the-selected-printer-tip", ""), @@ -681,5 +681,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remote-printing-disallowed-text-tip", ""), ("save-settings-tip", ""), ("dont-show-again-tip", ""), + ("Take screenshot", ""), + ("Taking screenshot", ""), + ("screenshot-merged-screen-not-supported-tip", ""), + ("screenshot-action-tip", ""), + ("Save as", ""), + ("Copy to clipboard", ""), ].iter().cloned().collect(); } diff --git a/src/lang/tr.rs b/src/lang/tr.rs index 1e36b85fd..7645a9798 100644 --- a/src/lang/tr.rs +++ b/src/lang/tr.rs @@ -671,7 +671,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("printer-{}-ready-tip", ""), ("Install {} Printer", ""), ("Outgoing Print Jobs", ""), - ("Incomming Print Jobs", ""), + ("Incoming Print Jobs", ""), ("Incoming Print Job", ""), ("use-the-default-printer-tip", ""), ("use-the-selected-printer-tip", ""), @@ -681,5 +681,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remote-printing-disallowed-text-tip", ""), ("save-settings-tip", ""), ("dont-show-again-tip", ""), + ("Take screenshot", ""), + ("Taking screenshot", ""), + ("screenshot-merged-screen-not-supported-tip", ""), + ("screenshot-action-tip", ""), + ("Save as", ""), + ("Copy to clipboard", ""), ].iter().cloned().collect(); } diff --git a/src/lang/tw.rs b/src/lang/tw.rs index cecdf1d05..0d863fd8d 100644 --- a/src/lang/tw.rs +++ b/src/lang/tw.rs @@ -671,7 +671,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("printer-{}-ready-tip", "{} 印表機已安裝,您可以使用列印功能了。"), ("Install {} Printer", "安裝 {} 印表機"), ("Outgoing Print Jobs", "傳出的列印任務"), - ("Incomming Print Jobs", "傳入的列印任務"), + ("Incoming Print Jobs", "傳入的列印任務"), ("Incoming Print Job", "傳入的列印任務"), ("use-the-default-printer-tip", "使用預設的印表機"), ("use-the-selected-printer-tip", "使用選取的印表機"), @@ -681,5 +681,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remote-printing-disallowed-text-tip", "被控端的權限設置拒絕了遠端列印。"), ("save-settings-tip", "儲存設定"), ("dont-show-again-tip", "不再顯示此訊息"), + ("Take screenshot", ""), + ("Taking screenshot", ""), + ("screenshot-merged-screen-not-supported-tip", ""), + ("screenshot-action-tip", ""), + ("Save as", ""), + ("Copy to clipboard", ""), ].iter().cloned().collect(); } diff --git a/src/lang/uk.rs b/src/lang/uk.rs index 475dd071c..8c4f9b6a5 100644 --- a/src/lang/uk.rs +++ b/src/lang/uk.rs @@ -671,7 +671,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("printer-{}-ready-tip", ""), ("Install {} Printer", ""), ("Outgoing Print Jobs", ""), - ("Incomming Print Jobs", ""), + ("Incoming Print Jobs", ""), ("Incoming Print Job", ""), ("use-the-default-printer-tip", ""), ("use-the-selected-printer-tip", ""), @@ -681,5 +681,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remote-printing-disallowed-text-tip", ""), ("save-settings-tip", ""), ("dont-show-again-tip", ""), + ("Take screenshot", ""), + ("Taking screenshot", ""), + ("screenshot-merged-screen-not-supported-tip", ""), + ("screenshot-action-tip", ""), + ("Save as", ""), + ("Copy to clipboard", ""), ].iter().cloned().collect(); } diff --git a/src/lang/vn.rs b/src/lang/vn.rs index e44627495..defc5c030 100644 --- a/src/lang/vn.rs +++ b/src/lang/vn.rs @@ -671,7 +671,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("printer-{}-ready-tip", ""), ("Install {} Printer", ""), ("Outgoing Print Jobs", ""), - ("Incomming Print Jobs", ""), + ("Incoming Print Jobs", ""), ("Incoming Print Job", ""), ("use-the-default-printer-tip", ""), ("use-the-selected-printer-tip", ""), @@ -681,5 +681,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remote-printing-disallowed-text-tip", ""), ("save-settings-tip", ""), ("dont-show-again-tip", ""), + ("Take screenshot", ""), + ("Taking screenshot", ""), + ("screenshot-merged-screen-not-supported-tip", ""), + ("screenshot-action-tip", ""), + ("Save as", ""), + ("Copy to clipboard", ""), ].iter().cloned().collect(); } diff --git a/src/server/connection.rs b/src/server/connection.rs index 46e19315d..b7d5be863 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -2819,6 +2819,16 @@ impl Connection { Some(message::Union::VoiceCallResponse(_response)) => { // TODO: Maybe we can do a voice call from cm directly. } + Some(message::Union::ScreenshotRequest(request)) => { + if let Some(tx) = self.inner.tx.clone() { + crate::video_service::set_take_screenshot( + request.display as _, + request.sid.clone(), + tx, + ); + self.refresh_video_display(Some(request.display as usize)); + } + } _ => {} } } diff --git a/src/server/video_service.rs b/src/server/video_service.rs index 508316535..a9474db74 100644 --- a/src/server/video_service.rs +++ b/src/server/video_service.rs @@ -49,7 +49,7 @@ use scrap::{ codec::{Encoder, EncoderCfg}, record::{Recorder, RecorderContext}, vpxcodec::{VpxEncoderConfig, VpxVideoCodecId}, - CodecFormat, Display, EncodeInput, TraitCapturer, + CodecFormat, Display, EncodeInput, TraitCapturer, TraitPixelBuffer, }; #[cfg(windows)] use std::sync::Once; @@ -70,6 +70,13 @@ lazy_static::lazy_static! { pub static ref VIDEO_QOS: Arc> = Default::default(); pub static ref IS_UAC_RUNNING: Arc> = Default::default(); pub static ref IS_FOREGROUND_WINDOW_ELEVATED: Arc> = Default::default(); + static ref SCREENSHOTS: Mutex> = Default::default(); +} + +struct Screenshot { + sid: String, + tx: Sender, + restore_vram: bool, } #[inline] @@ -457,7 +464,7 @@ fn get_capturer( } fn run(vs: VideoService) -> ResultType<()> { - let _raii = Raii::new(vs.sp.name()); + let mut _raii = Raii::new(vs.sp.name()); // Wayland only support one video capturer for now. It is ok to call ensure_inited() here. // // ensure_inited() is needed because clear() may be called. @@ -639,6 +646,49 @@ fn run(vs: VideoService) -> ResultType<()> { Ok(frame) => { repeat_encode_counter = 0; if frame.valid() { + let screenshot = SCREENSHOTS.lock().unwrap().remove(&display_idx); + if let Some(mut screenshot) = screenshot { + let restore_vram = screenshot.restore_vram; + let (msg, w, h, data) = match &frame { + scrap::Frame::PixelBuffer(f) => match get_rgba_from_pixelbuf(f) { + Ok(rgba) => ("".to_owned(), f.width(), f.height(), rgba), + Err(e) => { + let serr = e.to_string(); + log::error!( + "Failed to convert the pix format into rgba, {}", + &serr + ); + (format!("Convert pixfmt: {}", serr), 0, 0, vec![]) + } + }, + scrap::Frame::Texture(_) => { + if restore_vram { + // Already set one time, just ignore to break infinite loop. + // Though it's unreachable, this branch is kept to avoid infinite loop. + ( + "Please change codec and try again.".to_owned(), + 0, + 0, + vec![], + ) + } else { + #[cfg(all(windows, feature = "vram"))] + VRamEncoder::set_not_use(sp.name(), true); + screenshot.restore_vram = true; + SCREENSHOTS.lock().unwrap().insert(display_idx, screenshot); + _raii.try_vram = false; + bail!("SWITCH"); + } + } + }; + std::thread::spawn(move || { + handle_screenshot(screenshot, msg, w, h, data); + }); + if restore_vram { + bail!("SWITCH"); + } + } + let frame = frame.to(encoder.yuvfmt(), &mut yuv, &mut mid_data)?; let send_conn_ids = handle_one_frame( display_idx, @@ -764,24 +814,32 @@ fn run(vs: VideoService) -> ResultType<()> { Ok(()) } -struct Raii(String); +struct Raii { + name: String, + try_vram: bool, +} impl Raii { fn new(name: String) -> Self { log::info!("new video service: {}", name); VIDEO_QOS.lock().unwrap().new_display(name.clone()); - Raii(name) + Raii { + name, + try_vram: true, + } } } impl Drop for Raii { fn drop(&mut self) { - log::info!("stop video service: {}", self.0); + log::info!("stop video service: {}", self.name); #[cfg(feature = "vram")] - VRamEncoder::set_not_use(self.0.clone(), false); + if self.try_vram { + VRamEncoder::set_not_use(self.name.clone(), false); + } #[cfg(feature = "vram")] Encoder::update(scrap::codec::EncodingUpdate::Check); - VIDEO_QOS.lock().unwrap().remove_display(&self.0); + VIDEO_QOS.lock().unwrap().remove_display(&self.name); } } @@ -1206,3 +1264,77 @@ fn check_qos( drop(video_qos); Ok(()) } + +pub fn set_take_screenshot(display_idx: usize, sid: String, tx: Sender) { + SCREENSHOTS.lock().unwrap().insert( + display_idx, + Screenshot { + sid, + tx, + restore_vram: false, + }, + ); +} + +// We need to this function, because the `stride` may be larger than `width * 4`. +fn get_rgba_from_pixelbuf<'a>(pixbuf: &scrap::PixelBuffer<'a>) -> ResultType> { + let w = pixbuf.width(); + let h = pixbuf.height(); + let stride = pixbuf.stride(); + let Some(s) = stride.get(0) else { + bail!("Invalid pixel buf stride.") + }; + + if *s == w * 4 { + let mut rgba = vec![]; + scrap::convert(pixbuf, scrap::Pixfmt::RGBA, &mut rgba)?; + Ok(rgba) + } else { + let bgra = pixbuf.data(); + let mut bit_flipped = Vec::with_capacity(w * h * 4); + for y in 0..h { + for x in 0..w { + let i = s * y + 4 * x; + bit_flipped.extend_from_slice(&[bgra[i + 2], bgra[i + 1], bgra[i], bgra[i + 3]]); + } + } + Ok(bit_flipped) + } +} + +fn handle_screenshot(screenshot: Screenshot, msg: String, w: usize, h: usize, data: Vec) { + let mut response = ScreenshotResponse::new(); + response.sid = screenshot.sid; + if msg.is_empty() { + if data.is_empty() { + response.msg = "Failed to take screenshot, please try again later.".to_owned(); + } else { + fn encode_png(width: usize, height: usize, rgba: Vec) -> ResultType> { + let mut png = Vec::new(); + let mut encoder = + repng::Options::smallest(width as _, height as _).build(&mut png)?; + encoder.write(&rgba)?; + encoder.finish()?; + Ok(png) + } + match encode_png(w as _, h as _, data) { + Ok(png) => { + response.data = png.into(); + } + Err(e) => { + response.msg = format!("Error encoding png: {}", e); + } + } + } + } else { + response.msg = msg; + } + let mut msg_out = Message::new(); + msg_out.set_screenshot_response(response); + if let Err(e) = screenshot + .tx + .send((hbb_common::tokio::time::Instant::now(), Arc::new(msg_out))) + { + log::error!("Failed to send screenshot, {}", e); + } +} diff --git a/src/ui/header.tis b/src/ui/header.tis index 241cab0f9..13bd98de5 100644 --- a/src/ui/header.tis +++ b/src/ui/header.tis @@ -230,6 +230,7 @@ class Header: Reactor.Component { {restart_enabled && (pi.platform == "Linux" || pi.platform == "Windows" || pi.platform == "Mac OS") ?
  • {translate('Restart remote device')}
  • : ""} {keyboard_enabled ?
  • {translate('Insert Lock')}
  • : ""} {keyboard_enabled && pi.platform == "Windows" && pi.sas_enabled ?
  • {translate("Block user input")}
  • : ""} + {handler.is_screenshot_supported() ?
  • {translate('Take screenshot')}
  • : "" }
  • {translate('Refresh')}
  • ; @@ -376,6 +377,10 @@ class Header: Reactor.Component { event click $(#lock-screen) { handler.lock_screen(); } + + event click $(#take-screenshot) { + handler.take_screenshot(pi.current_display, ""); + } event click $(#refresh) { // 0 is just a dummy value. It will be ignored by the handler. @@ -546,6 +551,26 @@ handler.setCurrentDisplay = function(v) { } } +handler.screenshot = function(msg) { + if (msg) { + msgbox( + "custom-nocancel-nook-hasclose-error", + translate("Take screenshot"), + msg, + "", + function() {} + ); + } else { + msgbox( + "custom-take-screenshot-nocancel-nook", + translate("Take screenshot"), + translate("screenshot-action-tip"), + "", + function() {} + ); + } +} + function updatePrivacyMode() { var el = $(li#privacy-mode); if (el) { diff --git a/src/ui/msgbox.tis b/src/ui/msgbox.tis index da557e312..542691f5f 100644 --- a/src/ui/msgbox.tis +++ b/src/ui/msgbox.tis @@ -132,6 +132,17 @@ class MsgboxComponent: Reactor.Component { return this.type.indexOf("skip") >= 0; } + function getScreenshotButtons() { + var isScreenshot = this.type.indexOf("take-screenshot") >= 0; + return isScreenshot + ?
    + + + +
    + : ""; + } + function render() { this.set_outline_focus(); var color = this.getColor(); @@ -170,6 +181,7 @@ class MsgboxComponent: Reactor.Component { {hasOk || this.hasRetry ? : ""} {hasLink ? : ""} {hasClose ? : ""} + {this.getScreenshotButtons()} @@ -245,6 +257,39 @@ class MsgboxComponent: Reactor.Component { this.close(); } } + + event click $(button#screenshotSaveAs) { + this.close(); + + handler.leave(handler.get_keyboard_mode()); + const filter = "Png file (*.png)"; + const defaultExt = "png"; + const initialPath = System.path(#USER_DOCUMENTS, "screenshot"); + const caption = "Save as"; + var url = view.selectFile(#save, filter, defaultExt, initialPath, caption); + handler.enter(handler.get_keyboard_mode()); + if(url) { + var res = handler.handle_screenshot("0:" + URL.toPath(url)); + if (res) { + msgbox("custom-error-nocancel-nook-hasclose", "Take screenshot", res, "", function() {}); + } + } else { + handler.handle_screenshot("2"); + } + } + + event click $(button#screenshotCopyToClip) { + this.close(); + var res = handler.handle_screenshot("1"); + if (res) { + msgbox("custom-error-nocancel-nook-hasclose", "Take screenshot", res, "", function() {}); + } + } + + event click $(button#screenshotCancel) { + this.close(); + handler.handle_screenshot("2"); + } event keydown (evt) { if (!evt.shortcutKey) { diff --git a/src/ui/remote.rs b/src/ui/remote.rs index fffc51b86..82cac19b1 100644 --- a/src/ui/remote.rs +++ b/src/ui/remote.rs @@ -383,6 +383,10 @@ impl InvokeUiSession for SciterHandler { fn printer_request(&self, id: i32, path: String) { self.call("printerRequest", &make_args!(id, path)); } + + fn handle_screenshot_resp(&self, _sid: String, msg: String) { + self.call("screenshot", &make_args!(msg)); + } } pub struct SciterSession(Session); @@ -529,6 +533,9 @@ impl sciter::EventHandler for SciterSession { fn save_custom_image_quality(i32); fn refresh_video(i32); fn record_screen(bool); + fn is_screenshot_supported(); + fn take_screenshot(i32, String); + fn handle_screenshot(String); fn get_toggle_option(String); fn is_privacy_mode_supported(); fn toggle_option(String); @@ -866,6 +873,10 @@ impl SciterSession { fn on_printer_selected(&self, id: i32, path: String, printer_name: String) { self.printer_response(id, path, printer_name); } + + fn handle_screenshot(&self, action: String) -> String { + crate::client::screenshot::handle_screenshot(action) + } } pub fn make_fd(id: i32, entries: &Vec, only_count: bool) -> Value { diff --git a/src/ui_session_interface.rs b/src/ui_session_interface.rs index fb06493fb..b3809466d 100644 --- a/src/ui_session_interface.rs +++ b/src/ui_session_interface.rs @@ -412,6 +412,14 @@ impl Session { self.send(Data::RecordScreen(start)); } + pub fn is_screenshot_supported(&self) -> bool { + crate::common::is_support_screenshot_num(self.lc.read().unwrap().version) + } + + pub fn take_screenshot(&self, display: i32, sid: String) { + self.send(Data::TakeScreenshot((display, sid))); + } + pub fn is_recording(&self) -> bool { self.lc.read().unwrap().record_state } @@ -1586,6 +1594,7 @@ pub trait InvokeUiSession: Send + Sync + Clone + 'static + Sized + Default { fn update_record_status(&self, start: bool); fn update_empty_dirs(&self, _res: ReadEmptyDirsResponse) {} fn printer_request(&self, id: i32, path: String); + fn handle_screenshot_resp(&self, sid: String, msg: String); } impl Deref for Session {