Merge pull request #3184 from 21pages/resolution

Change resolution
This commit is contained in:
RustDesk
2023-02-24 10:44:55 +08:00
committed by GitHub
54 changed files with 2222 additions and 1337 deletions

View File

@@ -480,6 +480,7 @@ impl InvokeUiSession for FlutterHandler {
features.insert("privacy_mode", 0);
}
let features = serde_json::ser::to_string(&features).unwrap_or("".to_owned());
let resolutions = serialize_resolutions(&pi.resolutions.resolutions);
*self.peer_info.write().unwrap() = pi.clone();
self.push_event(
"peer_info",
@@ -492,6 +493,7 @@ impl InvokeUiSession for FlutterHandler {
("version", &pi.version),
("features", &features),
("current_display", &pi.current_display.to_string()),
("resolutions", &resolutions),
],
);
}
@@ -529,6 +531,7 @@ impl InvokeUiSession for FlutterHandler {
}
fn switch_display(&self, display: &SwitchDisplay) {
let resolutions = serialize_resolutions(&display.resolutions.resolutions);
self.push_event(
"switch_display",
vec![
@@ -548,6 +551,7 @@ impl InvokeUiSession for FlutterHandler {
}
.to_string(),
),
("resolutions", &resolutions),
],
);
}
@@ -861,6 +865,27 @@ pub fn set_cur_session_id(id: String) {
}
}
#[inline]
fn serialize_resolutions(resolutions: &Vec<Resolution>) -> String {
#[derive(Debug, serde::Serialize)]
struct ResolutionSerde {
width: i32,
height: i32,
}
let mut v = vec![];
resolutions
.iter()
.map(|r| {
v.push(ResolutionSerde {
width: r.width,
height: r.height,
})
})
.count();
serde_json::ser::to_string(&v).unwrap_or("".to_string())
}
#[no_mangle]
#[cfg(not(feature = "flutter_texture_render"))]
pub fn session_get_rgba_size(id: *const char) -> usize {

View File

@@ -529,7 +529,13 @@ pub fn session_switch_sides(id: String) {
}
}
pub fn session_set_size(_id: String, _width: i32, _height: i32) {
pub fn session_change_resolution(id: String, width: i32, height: i32) {
if let Some(session) = SESSIONS.read().unwrap().get(&id) {
session.change_resolution(width, height);
}
}
pub fn session_set_size(_id: String, _width: i32, _height: i32) {
#[cfg(feature = "flutter_texture_render")]
if let Some(session) = SESSIONS.write().unwrap().get_mut(&_id) {
session.set_size(_width, _height);

View File

@@ -454,5 +454,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Stop voice call", ""),
("relay_hint_tip", ""),
("Reconnect", ""),
("Codec", ""),
("Resolution", ""),
].iter().cloned().collect();
}

View File

@@ -454,5 +454,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Stop voice call", "停止语音通话"),
("relay_hint_tip", "可能无法直连,可以尝试中继连接。\n另外,如果想直接使用中继连接,可以在 ID 后面添加/r或者在卡片选项里选择强制走中继连接。"),
("Reconnect", "重连"),
("Codec", "编解码"),
("Resolution", "分辨率"),
].iter().cloned().collect();
}

View File

@@ -454,5 +454,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Stop voice call", ""),
("relay_hint_tip", ""),
("Reconnect", ""),
("Codec", ""),
("Resolution", ""),
].iter().cloned().collect();
}

View File

@@ -454,5 +454,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Stop voice call", ""),
("relay_hint_tip", ""),
("Reconnect", ""),
("Codec", ""),
("Resolution", ""),
].iter().cloned().collect();
}

View File

@@ -454,5 +454,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Stop voice call", "Sprachanruf beenden"),
("relay_hint_tip", "Wenn eine direkte Verbindung nicht möglich ist, können Sie versuchen, eine Verbindung über einen Relay-Server herzustellen. \nWenn Sie eine Relay-Verbindung beim ersten Versuch herstellen möchten, können Sie das Suffix \"/r\" an die ID anhängen oder die Option \"Immer über Relay-Server verbinden\" auf der Gegenstelle auswählen."),
("Reconnect", "Erneut verbinden"),
("Codec", ""),
("Resolution", ""),
].iter().cloned().collect();
}

View File

@@ -454,5 +454,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Stop voice call", ""),
("relay_hint_tip", ""),
("Reconnect", ""),
("Codec", ""),
("Resolution", ""),
].iter().cloned().collect();
}

View File

@@ -454,5 +454,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Stop voice call", "Detener llamada de voz"),
("relay_hint_tip", "Puede que no sea posible conectar directamente. Puedes tratar de conectar a través de relay. \nAdicionalmente, si quieres usar relay en el primer intento, puedes añadir el sufijo \"/r\" a la ID o seleccionar la opción \"Conectar siempre a través de relay\" en la tarjeta del par."),
("Reconnect", "Reconectar"),
("Codec", ""),
("Resolution", ""),
].iter().cloned().collect();
}

View File

@@ -454,5 +454,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Stop voice call", "توقف تماس صوتی"),
("relay_hint_tip", " را به شناسه اضافه کنید یا گزینه \"همیشه از طریق رله متصل شوید\" را در کارت همتا انتخاب کنید. همچنین، اگر می‌خواهید فوراً از سرور رله استفاده کنید، می‌توانید پسوند \"/r\".\n اتصال مستقیم ممکن است امکان پذیر نباشد. در این صورت می توانید سعی کنید از طریق سرور رله متصل شوید"),
("Reconnect", "اتصال مجدد"),
("Codec", ""),
("Resolution", ""),
].iter().cloned().collect();
}

View File

@@ -454,5 +454,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Stop voice call", ""),
("relay_hint_tip", ""),
("Reconnect", ""),
("Codec", ""),
("Resolution", ""),
].iter().cloned().collect();
}

View File

@@ -454,5 +454,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Stop voice call", ""),
("relay_hint_tip", ""),
("Reconnect", ""),
("Codec", ""),
("Resolution", ""),
].iter().cloned().collect();
}

View File

@@ -454,5 +454,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Stop voice call", ""),
("relay_hint_tip", ""),
("Reconnect", ""),
("Codec", ""),
("Resolution", ""),
].iter().cloned().collect();
}

View File

@@ -454,5 +454,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Stop voice call", ""),
("relay_hint_tip", ""),
("Reconnect", ""),
("Codec", ""),
("Resolution", ""),
].iter().cloned().collect();
}

View File

@@ -454,5 +454,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Stop voice call", "Interrompi la chiamata vocale"),
("relay_hint_tip", "Se non è possibile connettersi direttamente, si può provare a farlo tramite relay.\nInoltre, se si desidera utilizzare il relay al primo tentativo, è possibile aggiungere il suffisso \"/r\" all'ID o selezionare l'opzione \"Collegati sempre tramite relay\" nella scheda peer."),
("Reconnect", "Riconnetti"),
("Codec", ""),
("Resolution", ""),
].iter().cloned().collect();
}

View File

@@ -454,5 +454,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Stop voice call", ""),
("relay_hint_tip", ""),
("Reconnect", ""),
("Codec", ""),
("Resolution", ""),
].iter().cloned().collect();
}

View File

@@ -454,5 +454,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Stop voice call", ""),
("relay_hint_tip", ""),
("Reconnect", ""),
("Codec", ""),
("Resolution", ""),
].iter().cloned().collect();
}

View File

@@ -454,5 +454,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Stop voice call", ""),
("relay_hint_tip", ""),
("Reconnect", ""),
("Codec", ""),
("Resolution", ""),
].iter().cloned().collect();
}

View File

@@ -454,5 +454,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Stop voice call", "Stop spraakoproep"),
("relay_hint_tip", ""),
("Reconnect", ""),
("Codec", ""),
("Resolution", ""),
].iter().cloned().collect();
}

View File

@@ -454,5 +454,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Stop voice call", ""),
("relay_hint_tip", ""),
("Reconnect", ""),
("Codec", ""),
("Resolution", ""),
].iter().cloned().collect();
}

View File

@@ -454,5 +454,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Stop voice call", ""),
("relay_hint_tip", ""),
("Reconnect", ""),
("Codec", ""),
("Resolution", ""),
].iter().cloned().collect();
}

View File

@@ -454,5 +454,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Stop voice call", ""),
("relay_hint_tip", ""),
("Reconnect", ""),
("Codec", ""),
("Resolution", ""),
].iter().cloned().collect();
}

View File

@@ -454,5 +454,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Stop voice call", ""),
("relay_hint_tip", ""),
("Reconnect", ""),
("Codec", ""),
("Resolution", ""),
].iter().cloned().collect();
}

View File

@@ -454,5 +454,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Stop voice call", "Завершить голосовой вызов"),
("relay_hint_tip", "Прямое подключение может оказаться невозможным. В этом случае можно попытаться подключиться через сервер ретрансляции. \nКроме того, если вы хотите сразу использовать сервер ретрансляции, можно добавить к ID суффикс \"/r\" или включить \"Всегда подключаться через ретранслятор\" в настройках удалённого узла."),
("Reconnect", "Переподключить"),
("Codec", ""),
("Resolution", ""),
].iter().cloned().collect();
}

View File

@@ -454,5 +454,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Stop voice call", ""),
("relay_hint_tip", ""),
("Reconnect", ""),
("Codec", ""),
("Resolution", ""),
].iter().cloned().collect();
}

View File

@@ -454,5 +454,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Stop voice call", ""),
("relay_hint_tip", ""),
("Reconnect", ""),
("Codec", ""),
("Resolution", ""),
].iter().cloned().collect();
}

View File

@@ -454,5 +454,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Stop voice call", ""),
("relay_hint_tip", ""),
("Reconnect", ""),
("Codec", ""),
("Resolution", ""),
].iter().cloned().collect();
}

View File

@@ -454,5 +454,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Stop voice call", ""),
("relay_hint_tip", ""),
("Reconnect", ""),
("Codec", ""),
("Resolution", ""),
].iter().cloned().collect();
}

View File

@@ -454,5 +454,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Stop voice call", ""),
("relay_hint_tip", ""),
("Reconnect", ""),
("Codec", ""),
("Resolution", ""),
].iter().cloned().collect();
}

View File

@@ -454,5 +454,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Stop voice call", ""),
("relay_hint_tip", ""),
("Reconnect", ""),
("Codec", ""),
("Resolution", ""),
].iter().cloned().collect();
}

View File

@@ -454,5 +454,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Stop voice call", ""),
("relay_hint_tip", ""),
("Reconnect", ""),
("Codec", ""),
("Resolution", ""),
].iter().cloned().collect();
}

View File

@@ -454,5 +454,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Stop voice call", ""),
("relay_hint_tip", ""),
("Reconnect", ""),
("Codec", ""),
("Resolution", ""),
].iter().cloned().collect();
}

View File

@@ -454,5 +454,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Stop voice call", "停止語音聊天"),
("relay_hint_tip", "可能無法直連,可以嘗試中繼連接。 \n另外如果想直接使用中繼連接可以在ID後面添加/r或者在卡片選項裡選擇強制走中繼連接。"),
("Reconnect", "重連"),
("Codec", "編解碼"),
("Resolution", "分辨率"),
].iter().cloned().collect();
}

View File

@@ -454,5 +454,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Stop voice call", ""),
("relay_hint_tip", ""),
("Reconnect", ""),
("Codec", ""),
("Resolution", ""),
].iter().cloned().collect();
}

View File

@@ -454,5 +454,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Stop voice call", ""),
("relay_hint_tip", ""),
("Reconnect", ""),
("Codec", ""),
("Resolution", ""),
].iter().cloned().collect();
}

View File

@@ -1,7 +1,7 @@
use super::{CursorData, ResultType};
use hbb_common::libc::{c_char, c_int, c_long, c_void};
pub use hbb_common::platform::linux::*;
use hbb_common::{allow_err, bail, log};
use hbb_common::{allow_err, anyhow::anyhow, bail, log, message_proto::Resolution};
use std::{
cell::RefCell,
path::PathBuf,
@@ -10,6 +10,7 @@ use std::{
Arc,
},
};
use xrandr_parser::Parser;
type Xdo = *const c_void;
@@ -641,3 +642,55 @@ pub fn get_double_click_time() -> u32 {
double_click_time
}
}
pub fn resolutions(name: &str) -> Vec<Resolution> {
let mut v = vec![];
let mut parser = Parser::new();
if parser.parse().is_ok() {
if let Ok(connector) = parser.get_connector(name) {
if let Ok(resolutions) = &connector.available_resolutions() {
for r in resolutions {
if let Ok(width) = r.horizontal.parse::<i32>() {
if let Ok(height) = r.vertical.parse::<i32>() {
let resolution = Resolution {
width,
height,
..Default::default()
};
if !v.contains(&resolution) {
v.push(resolution);
}
}
}
}
}
}
}
v
}
pub fn current_resolution(name: &str) -> ResultType<Resolution> {
let mut parser = Parser::new();
parser.parse().map_err(|e| anyhow!(e))?;
let connector = parser.get_connector(name).map_err(|e| anyhow!(e))?;
let r = connector.current_resolution();
let width = r.horizontal.parse::<i32>()?;
let height = r.vertical.parse::<i32>()?;
Ok(Resolution {
width,
height,
..Default::default()
})
}
pub fn change_resolution(name: &str, width: usize, height: usize) -> ResultType<()> {
std::process::Command::new("xrandr")
.args(vec![
"--output",
name,
"--mode",
&format!("{}x{}", width, height),
])
.spawn()?;
Ok(())
}

View File

@@ -40,3 +40,114 @@ extern "C" float BackingScaleFactor() {
if (s) return [s backingScaleFactor];
return 1;
}
// https://github.com/jhford/screenresolution/blob/master/cg_utils.c
// https://github.com/jdoupe/screenres/blob/master/setgetscreen.m
extern "C" bool MacGetModeNum(CGDirectDisplayID display, uint32_t *numModes) {
CFArrayRef allModes = CGDisplayCopyAllDisplayModes(display, NULL);
if (allModes == NULL) {
return false;
}
*numModes = CFArrayGetCount(allModes);
CFRelease(allModes);
return true;
}
extern "C" bool MacGetModes(CGDirectDisplayID display, uint32_t *widths, uint32_t *heights, uint32_t max, uint32_t *numModes) {
CFArrayRef allModes = CGDisplayCopyAllDisplayModes(display, NULL);
if (allModes == NULL) {
return false;
}
*numModes = CFArrayGetCount(allModes);
for (int i = 0; i < *numModes && i < max; i++) {
CGDisplayModeRef mode = (CGDisplayModeRef)CFArrayGetValueAtIndex(allModes, i);
widths[i] = (uint32_t)CGDisplayModeGetWidth(mode);
heights[i] = (uint32_t)CGDisplayModeGetHeight(mode);
}
CFRelease(allModes);
return true;
}
extern "C" bool MacGetMode(CGDirectDisplayID display, uint32_t *width, uint32_t *height) {
CGDisplayModeRef mode = CGDisplayCopyDisplayMode(display);
if (mode == NULL) {
return false;
}
*width = (uint32_t)CGDisplayModeGetWidth(mode);
*height = (uint32_t)CGDisplayModeGetHeight(mode);
CGDisplayModeRelease(mode);
return true;
}
size_t bitDepth(CGDisplayModeRef mode) {
size_t depth = 0;
CFStringRef pixelEncoding = CGDisplayModeCopyPixelEncoding(mode);
// my numerical representation for kIO16BitFloatPixels and kIO32bitFloatPixels
// are made up and possibly non-sensical
if (kCFCompareEqualTo == CFStringCompare(pixelEncoding, CFSTR(kIO32BitFloatPixels), kCFCompareCaseInsensitive)) {
depth = 96;
} else if (kCFCompareEqualTo == CFStringCompare(pixelEncoding, CFSTR(kIO64BitDirectPixels), kCFCompareCaseInsensitive)) {
depth = 64;
} else if (kCFCompareEqualTo == CFStringCompare(pixelEncoding, CFSTR(kIO16BitFloatPixels), kCFCompareCaseInsensitive)) {
depth = 48;
} else if (kCFCompareEqualTo == CFStringCompare(pixelEncoding, CFSTR(IO32BitDirectPixels), kCFCompareCaseInsensitive)) {
depth = 32;
} else if (kCFCompareEqualTo == CFStringCompare(pixelEncoding, CFSTR(kIO30BitDirectPixels), kCFCompareCaseInsensitive)) {
depth = 30;
} else if (kCFCompareEqualTo == CFStringCompare(pixelEncoding, CFSTR(IO16BitDirectPixels), kCFCompareCaseInsensitive)) {
depth = 16;
} else if (kCFCompareEqualTo == CFStringCompare(pixelEncoding, CFSTR(IO8BitIndexedPixels), kCFCompareCaseInsensitive)) {
depth = 8;
}
CFRelease(pixelEncoding);
return depth;
}
bool setDisplayToMode(CGDirectDisplayID display, CGDisplayModeRef mode) {
CGError rc;
CGDisplayConfigRef config;
rc = CGBeginDisplayConfiguration(&config);
if (rc != kCGErrorSuccess) {
return false;
}
rc = CGConfigureDisplayWithDisplayMode(config, display, mode, NULL);
if (rc != kCGErrorSuccess) {
return false;
}
rc = CGCompleteDisplayConfiguration(config, kCGConfigureForSession);
if (rc != kCGErrorSuccess) {
return false;
}
return true;
}
extern "C" bool MacSetMode(CGDirectDisplayID display, uint32_t width, uint32_t height)
{
bool ret = false;
CGDisplayModeRef currentMode = CGDisplayCopyDisplayMode(display);
if (currentMode == NULL) {
return ret;
}
CFArrayRef allModes = CGDisplayCopyAllDisplayModes(display, NULL);
if (allModes == NULL) {
CGDisplayModeRelease(currentMode);
return ret;
}
int numModes = CFArrayGetCount(allModes);
CGDisplayModeRef bestMode = NULL;
for (int i = 0; i < numModes; i++) {
CGDisplayModeRef mode = (CGDisplayModeRef)CFArrayGetValueAtIndex(allModes, i);
if (width == CGDisplayModeGetWidth(mode) &&
height == CGDisplayModeGetHeight(mode) &&
bitDepth(currentMode) == bitDepth(mode) &&
CGDisplayModeGetRefreshRate(currentMode) == CGDisplayModeGetRefreshRate(mode)) {
ret = setDisplayToMode(display, mode);
break;
}
}
CGDisplayModeRelease(currentMode);
CFRelease(allModes);
return ret;
}

View File

@@ -17,7 +17,7 @@ use core_graphics::{
display::{kCGNullWindowID, kCGWindowListOptionOnScreenOnly, CGWindowListCopyWindowInfo},
window::{kCGWindowName, kCGWindowOwnerPID},
};
use hbb_common::{allow_err, bail, log};
use hbb_common::{allow_err, anyhow::anyhow, bail, log, message_proto::Resolution};
use include_dir::{include_dir, Dir};
use objc::{class, msg_send, sel, sel_impl};
use scrap::{libc::c_void, quartz::ffi::*};
@@ -34,6 +34,16 @@ extern "C" {
static kAXTrustedCheckOptionPrompt: CFStringRef;
fn AXIsProcessTrustedWithOptions(options: CFDictionaryRef) -> BOOL;
fn InputMonitoringAuthStatus(_: BOOL) -> BOOL;
fn MacGetModeNum(display: u32, numModes: *mut u32) -> BOOL;
fn MacGetModes(
display: u32,
widths: *mut u32,
heights: *mut u32,
max: u32,
numModes: *mut u32,
) -> BOOL;
fn MacGetMode(display: u32, width: *mut u32, height: *mut u32) -> BOOL;
fn MacSetMode(display: u32, width: u32, height: u32) -> BOOL;
}
pub fn is_process_trusted(prompt: bool) -> bool {
@@ -594,3 +604,64 @@ pub fn handle_application_should_open_untitled_file() {
}
}
}
pub fn resolutions(name: &str) -> Vec<Resolution> {
let mut v = vec![];
if let Ok(display) = name.parse::<u32>() {
let mut num = 0;
unsafe {
if YES == MacGetModeNum(display, &mut num) {
let (mut widths, mut heights) = (vec![0; num as _], vec![0; num as _]);
let mut realNum = 0;
if YES
== MacGetModes(
display,
widths.as_mut_ptr(),
heights.as_mut_ptr(),
num,
&mut realNum,
)
{
if realNum <= num {
for i in 0..realNum {
let resolution = Resolution {
width: widths[i as usize] as _,
height: heights[i as usize] as _,
..Default::default()
};
if !v.contains(&resolution) {
v.push(resolution);
}
}
}
}
}
}
}
v
}
pub fn current_resolution(name: &str) -> ResultType<Resolution> {
let display = name.parse::<u32>().map_err(|e| anyhow!(e))?;
unsafe {
let (mut width, mut height) = (0, 0);
if NO == MacGetMode(display, &mut width, &mut height) {
bail!("MacGetMode failed");
}
Ok(Resolution {
width: width as _,
height: height as _,
..Default::default()
})
}
}
pub fn change_resolution(name: &str, width: usize, height: usize) -> ResultType<()> {
let display = name.parse::<u32>().map_err(|e| anyhow!(e))?;
unsafe {
if NO == MacSetMode(display, width as _, height as _) {
bail!("MacSetMode failed");
}
}
Ok(())
}

View File

@@ -74,5 +74,13 @@ mod tests {
assert!(!get_cursor_pos().is_none());
}
}
}
#[cfg(not(any(target_os = "android", target_os = "ios")))]
#[test]
fn test_resolution() {
let name = r"\\.\DISPLAY1";
println!("current:{:?}", current_resolution(name));
println!("change:{:?}", change_resolution(name, 2880, 1800));
println!("resolutions:{:?}", resolutions(name));
}
}

View File

@@ -5,7 +5,9 @@ use crate::license::*;
use hbb_common::{
allow_err, bail,
config::{self, Config},
log, sleep, timeout, tokio,
log,
message_proto::Resolution,
sleep, timeout, tokio,
};
use std::io::prelude::*;
use std::{
@@ -1784,3 +1786,89 @@ pub fn set_path_permission(dir: &PathBuf, permission: &str) -> ResultType<()> {
.spawn()?;
Ok(())
}
pub fn resolutions(name: &str) -> Vec<Resolution> {
unsafe {
let mut dm: DEVMODEW = std::mem::zeroed();
let wname = wide_string(name);
let len = if wname.len() <= dm.dmDeviceName.len() {
wname.len()
} else {
dm.dmDeviceName.len()
};
std::ptr::copy_nonoverlapping(wname.as_ptr(), dm.dmDeviceName.as_mut_ptr(), len);
dm.dmSize = std::mem::size_of::<DEVMODEW>() as _;
let mut v = vec![];
let mut num = 0;
loop {
if EnumDisplaySettingsW(NULL as _, num, &mut dm) == 0 {
break;
}
let r = Resolution {
width: dm.dmPelsWidth as _,
height: dm.dmPelsHeight as _,
..Default::default()
};
if !v.contains(&r) {
v.push(r);
}
num += 1;
}
v
}
}
pub fn current_resolution(name: &str) -> ResultType<Resolution> {
unsafe {
let mut dm: DEVMODEW = std::mem::zeroed();
dm.dmSize = std::mem::size_of::<DEVMODEW>() as _;
let wname = wide_string(name);
if EnumDisplaySettingsW(wname.as_ptr(), ENUM_CURRENT_SETTINGS, &mut dm) == 0 {
bail!(
"failed to get currrent resolution, errno={}",
GetLastError()
);
}
let r = Resolution {
width: dm.dmPelsWidth as _,
height: dm.dmPelsHeight as _,
..Default::default()
};
Ok(r)
}
}
pub fn change_resolution(name: &str, width: usize, height: usize) -> ResultType<()> {
unsafe {
let mut dm: DEVMODEW = std::mem::zeroed();
if FALSE == EnumDisplaySettingsW(NULL as _, ENUM_CURRENT_SETTINGS, &mut dm) {
bail!("EnumDisplaySettingsW failed, errno={}", GetLastError());
}
let wname = wide_string(name);
let len = if wname.len() <= dm.dmDeviceName.len() {
wname.len()
} else {
dm.dmDeviceName.len()
};
std::ptr::copy_nonoverlapping(wname.as_ptr(), dm.dmDeviceName.as_mut_ptr(), len);
dm.dmSize = std::mem::size_of::<DEVMODEW>() as _;
dm.dmPelsWidth = width as _;
dm.dmPelsHeight = height as _;
dm.dmFields = DM_PELSHEIGHT | DM_PELSWIDTH;
let res = ChangeDisplaySettingsExW(
wname.as_ptr(),
&mut dm,
NULL as _,
CDS_UPDATEREGISTRY | CDS_GLOBAL | CDS_RESET,
NULL,
);
if res != DISP_CHANGE_SUCCESSFUL {
bail!(
"ChangeDisplaySettingsExW failed, res={}, errno={}",
res,
GetLastError()
);
}
Ok(())
}
}

View File

@@ -123,6 +123,7 @@ pub struct Connection {
#[cfg(windows)]
portable: PortableState,
from_switch: bool,
origin_resolution: HashMap<String, Resolution>,
voice_call_request_timestamp: Option<NonZeroI64>,
audio_input_device_before_voice_call: Option<String>,
}
@@ -228,6 +229,7 @@ impl Connection {
#[cfg(windows)]
portable: Default::default(),
from_switch: false,
origin_resolution: Default::default(),
audio_sender: None,
voice_call_request_timestamp: None,
audio_input_device_before_voice_call: None,
@@ -533,6 +535,8 @@ impl Connection {
conn.post_conn_audit(json!({
"action": "close",
}));
#[cfg(not(any(target_os = "android", target_os = "ios")))]
conn.reset_resolution();
ALIVE_CONNS.lock().unwrap().retain(|&c| c != id);
if let Some(s) = conn.server.upgrade() {
s.write().unwrap().remove_connection(&conn.inner);
@@ -881,6 +885,16 @@ impl Connection {
..Default::default()
})
.into();
#[cfg(not(any(target_os = "android", target_os = "ios")))]
{
pi.resolutions = Some(SupportedResolutions {
resolutions: video_service::get_current_display_name()
.map(|name| crate::platform::resolutions(&name))
.unwrap_or(vec![]),
..Default::default()
})
.into();
}
let mut sub_service = false;
if self.file_transfer.is_some() {
@@ -1597,6 +1611,26 @@ impl Connection {
return false;
}
}
#[cfg(not(any(target_os = "android", target_os = "ios")))]
Some(misc::Union::ChangeResolution(r)) => {
if self.keyboard {
if let Ok(name) = video_service::get_current_display_name() {
if let Ok(current) = crate::platform::current_resolution(&name) {
if let Err(e) = crate::platform::change_resolution(
&name,
r.width as _,
r.height as _,
) {
log::error!("change resolution failed:{:?}", e);
} else {
if !self.origin_resolution.contains_key(&name) {
self.origin_resolution.insert(name, current);
}
}
}
}
}
}
_ => {}
},
Some(message::Union::AudioFrame(frame)) => {
@@ -1937,6 +1971,20 @@ impl Connection {
}
}
}
#[cfg(not(any(target_os = "android", target_os = "ios")))]
fn reset_resolution(&self) {
self.origin_resolution
.iter()
.map(|(name, r)| {
if let Err(e) =
crate::platform::change_resolution(&name, r.width as _, r.height as _)
{
log::error!("change resolution failed:{:?}", e);
}
})
.count();
}
}
pub fn insert_switch_sides_uuid(id: String, uuid: uuid::Uuid) {

View File

@@ -356,7 +356,7 @@ fn get_capturer(use_yuv: bool, portable_service_running: bool) -> ResultType<Cap
let (ndisplay, current, display) = get_current_display()?;
let (origin, width, height) = (display.origin(), display.width(), display.height());
log::debug!(
"#displays={}, current={}, origin: {:?}, width={}, height={}, cpus={}/{}",
"#displays={}, current={}, origin: {:?}, width={}, height={}, cpus={}/{}, name:{}",
ndisplay,
current,
&origin,
@@ -364,6 +364,7 @@ fn get_capturer(use_yuv: bool, portable_service_running: bool) -> ResultType<Cap
height,
num_cpus::get_physical(),
num_cpus::get(),
display.name(),
);
let privacy_mode_id = *PRIVACY_MODE_CONN_ID.lock().unwrap();
@@ -501,6 +502,14 @@ fn run(sp: GenericService) -> ResultType<()> {
width: c.width as _,
height: c.height as _,
cursor_embedded: capture_cursor_embedded(),
#[cfg(not(any(target_os = "android", target_os = "ios")))]
resolutions: Some(SupportedResolutions {
resolutions: get_current_display_name()
.map(|name| crate::platform::resolutions(&name))
.unwrap_or(vec![]),
..SupportedResolutions::default()
})
.into(),
..Default::default()
});
let mut msg_out = Message::new();
@@ -992,6 +1001,10 @@ pub fn get_current_display() -> ResultType<(usize, usize, Display)> {
get_current_display_2(try_get_displays()?)
}
pub fn get_current_display_name() -> ResultType<String> {
Ok(get_current_display_2(try_get_displays()?)?.2.name())
}
#[cfg(windows)]
fn start_uac_elevation_check() {
static START: Once = Once::new();

View File

@@ -713,6 +713,18 @@ impl<T: InvokeUiSession> Session<T> {
}
}
pub fn change_resolution(&self, width: i32, height: i32) {
let mut misc = Misc::new();
misc.set_change_resolution(Resolution {
width,
height,
..Default::default()
});
let mut msg = Message::new();
msg.set_misc(misc);
self.send(Data::Message(msg));
}
pub fn request_voice_call(&self) {
self.send(Data::NewVoiceCall);
}