Merge branch 'master' of https://github.com/rustdesk/rustdesk into master

This commit is contained in:
rustdesk 2021-05-17 20:46:31 +08:00
commit c05dd0b1b7
23 changed files with 547 additions and 819 deletions

997
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -46,7 +46,7 @@ cpal = { git = "https://github.com/rustaudio/cpal" }
[target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dependencies] [target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dependencies]
machine-uid = "0.2" machine-uid = "0.2"
mac_address = "1.1" mac_address = "1.1"
sciter-rs = { git = "https://github.com/sciter-sdk/rust-sciter" } sciter-rs = { git = "https://github.com/sciter-sdk/rust-sciter", rev = "7702fb17290e76606bdd7010a0c3cbb32e85db76" }
[target.'cfg(target_os = "windows")'.dependencies] [target.'cfg(target_os = "windows")'.dependencies]
systray = { path = "libs/systray-rs" } systray = { path = "libs/systray-rs" }

View File

@ -1,10 +1,10 @@
# RustDesk | Your Remote Desktop Software # RustDesk | Your Remote Desktop Software
The best open-source remote desktop software, written in Rust. Works out of the box, no configuration required. Great alternative to TeamViewer and AnyDesk! You have full control of your data, with no concerns about security. You can use our rendezvous/relay server, [set up your own](https://rustdesk.com/blog/id-relay-set/), or write your own rendezvous/relay server. The best open-source remote desktop client software, written in Rust. Works out of the box, no configuration required. Great alternative to TeamViewer and AnyDesk! You have full control of your data, with no concerns about security. You can use our rendezvous/relay server, [set up your own](https://rustdesk.com/blog/id-relay-set/), or write your own rendezvous/relay server.
[**BINARY DOWNLOAD**](https://github.com/rustdesk/rustdesk/releases) [**BINARY DOWNLOAD**](https://github.com/rustdesk/rustdesk/releases)
## Dependences ## Dependencies
Desktop versions use [sciter](https://sciter.com/) for GUI, please download sciter dynamic library yourself. Desktop versions use [sciter](https://sciter.com/) for GUI, please download sciter dynamic library yourself.
@ -12,8 +12,8 @@ Desktop versions use [sciter](https://sciter.com/) for GUI, please download scit
[Linux](https://github.com/c-smile/sciter-sdk/raw/dc65744b66389cd5a0ff6bdb7c63a8b7b05a708b/bin.lnx/x64/libsciter-gtk.so) [Linux](https://github.com/c-smile/sciter-sdk/raw/dc65744b66389cd5a0ff6bdb7c63a8b7b05a708b/bin.lnx/x64/libsciter-gtk.so)
[Osx](https://github.com/c-smile/sciter-sdk/raw/dc65744b66389cd5a0ff6bdb7c63a8b7b05a708b/bin.osx/sciter-osx-64.dylib) [Osx](https://github.com/c-smile/sciter-sdk/raw/dc65744b66389cd5a0ff6bdb7c63a8b7b05a708b/bin.osx/sciter-osx-64.dylib)
## How To Build
## Raw steps to build
* Prepare your Rust development env and C++ build env * Prepare your Rust development env and C++ build env
* Install [vcpkg](https://github.com/microsoft/vcpkg), and set `VCPKG_ROOT` env variable correctly * Install [vcpkg](https://github.com/microsoft/vcpkg), and set `VCPKG_ROOT` env variable correctly
@ -23,6 +23,58 @@ Desktop versions use [sciter](https://sciter.com/) for GUI, please download scit
* run `cargo run` * run `cargo run`
## How to build on Linux
### Ubuntu 18 (Debian 10)
```
sudo apt install -y g++ gcc git curl wget nasm yasm libgtk-3-dev clang libxcb-randr0-dev libxdo-dev libxfixes-dev libxcb-shape0-dev libxcb-xfixes0-dev libasound2-dev libpulse-dev cmake
```
### Fedora 28 (CentOS 8)
```
sudo yum -y install gcc-c++ git curl wget nasm yasm gcc gtk3-devel clang libxcb-devel libxdo-devel libXfixes-devel pulseaudio-libs-devel cmake alsa-lib-devel
```
### Arch (Manjaro)
```
sudo pacman -Syu unzip git cmake gcc curl wget yasm nasm zip make pkg-config clang gtk3 xdotool libxcb libxfixes alsa-lib pulseaudio
```
### Install vcpkg
```
git clone https://github.com/microsoft/vcpkg --branch 2020.11-1
vcpkg/bootstrap-vcpkg.sh
export VCPKG_ROOT=$HOME/vcpkg
vcpkg/vcpkg install libvpx libyuv opus
```
### Fix libvpx (For Fedora)
```
cd vcpkg/buildtrees/libvpx/src
cd *
./configure
sed -i 's/CFLAGS+=-I/CFLAGS+=-fPIC -I/g' Makefile
sed -i 's/CXXFLAGS+=-I/CXXFLAGS+=-fPIC -I/g' Makefile
make
cp libvpx.a $HOME/vcpkg/installed/x64-linux/lib/
cd
```
### Build
```
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
source $HOME/.cargo/env
git clone https://github.com/rustdesk/rustdesk
cd rustdesk
mkdir -p target/debug
wget https://github.com/c-smile/sciter-sdk/raw/dc65744b66389cd5a0ff6bdb7c63a8b7b05a708b/bin.lnx/x64/libsciter-gtk.so
mv libsciter-gtk.so target/debug
cargo run
```
### Change Wayland to X11 (Xorg)
RustDesk does not support Wayland. Check [this](https://docs.fedoraproject.org/en-US/quick-docs/configuring-xorg-as-default-gnome-session/) to configuring Xorg as the default GNOME session.
## File Structure ## File Structure
- **[libs/hbb_common](https://github.com/rustdesk/rustdesk/tree/master/libs/hbb_common)**: video codec, config, tcp/udp wrapper, protobuf, fs functions for file transfer, and some other utility functions - **[libs/hbb_common](https://github.com/rustdesk/rustdesk/tree/master/libs/hbb_common)**: video codec, config, tcp/udp wrapper, protobuf, fs functions for file transfer, and some other utility functions

View File

@ -109,6 +109,7 @@ message LocalAddr {
bytes socket_addr = 1; bytes socket_addr = 1;
bytes local_addr = 2; bytes local_addr = 2;
string relay_server = 3; string relay_server = 3;
string id = 4;
} }
message RendezvousMessage { message RendezvousMessage {

View File

@ -31,7 +31,7 @@ fn find_package(name: &str) -> Vec<PathBuf> {
if lib == "vpx" && target_os == "windows" { if lib == "vpx" && target_os == "windows" {
lib = format!("{}mt", lib); lib = format!("{}mt", lib);
} }
println!("{}", format!("cargo:rustc-link-lib={}", lib)); println!("{}", format!("cargo:rustc-link-lib=static={}", lib));
println!( println!(
"{}", "{}",
format!( format!(

View File

@ -62,13 +62,23 @@ pub struct Display(dxgi::Display);
impl Display { impl Display {
pub fn primary() -> io::Result<Display> { pub fn primary() -> io::Result<Display> {
match dxgi::Displays::new()?.next() { // not implemented yet
Some(inner) => Ok(Display(inner)), Err(NotFound.into())
None => Err(NotFound.into()),
}
} }
pub fn all() -> io::Result<Vec<Display>> { pub fn all() -> io::Result<Vec<Display>> {
let tmp = Self::all_().unwrap_or(Default::default());
if tmp.is_empty() {
println!("Display got from gdi");
return Ok(dxgi::Displays::get_from_gdi()
.drain(..)
.map(Display)
.collect::<Vec<_>>());
}
Ok(tmp)
}
fn all_() -> io::Result<Vec<Display>> {
Ok(dxgi::Displays::new()?.map(Display).collect::<Vec<_>>()) Ok(dxgi::Displays::new()?.map(Display).collect::<Vec<_>>())
} }
@ -92,13 +102,12 @@ impl Display {
self.0.is_online() self.0.is_online()
} }
pub fn origin(&self) -> (usize, usize) { pub fn origin(&self) -> (i32, i32) {
let o = self.0.origin(); self.0.origin()
(o.0 as usize, o.1 as usize)
} }
// to-do: not found primary display api for dxgi
pub fn is_primary(&self) -> bool { pub fn is_primary(&self) -> bool {
self.name() == Self::primary().unwrap().name() // https://docs.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-devmodea
self.origin() == (0, 0)
} }
} }

View File

@ -114,9 +114,9 @@ impl Display {
self.0.is_online() self.0.is_online()
} }
pub fn origin(&self) -> (usize, usize) { pub fn origin(&self) -> (i32, i32) {
let o = self.0.bounds().origin; let o = self.0.bounds().origin;
(o.x as usize, o.y as usize) (o.x as _, o.y as _)
} }
pub fn is_primary(&self) -> bool { pub fn is_primary(&self) -> bool {

View File

@ -68,7 +68,7 @@ impl Display {
self.0.rect().h as usize self.0.rect().h as usize
} }
pub fn origin(&self) -> (usize, usize) { pub fn origin(&self) -> (i32, i32) {
let r = self.0.rect(); let r = self.0.rect();
(r.x as _, r.y as _) (r.x as _, r.y as _)
} }

View File

@ -13,6 +13,7 @@ use winapi::{
BITMAPINFO, BITMAPINFO,
BITMAPINFOHEADER, BITMAPINFOHEADER,
BI_RGB, BI_RGB,
CAPTUREBLT,
DIB_RGB_COLORS, //CAPTUREBLT, DIB_RGB_COLORS, //CAPTUREBLT,
HGDI_ERROR, HGDI_ERROR,
RGBQUAD, RGBQUAD,
@ -97,7 +98,7 @@ impl CapturerGDI {
self.screen_dc, self.screen_dc,
0, 0,
0, 0,
SRCCOPY, // | CAPTUREBLT, // CAPTUREBLT enable layered window but also make cursor blinking SRCCOPY | CAPTUREBLT, // CAPTUREBLT enable layered window but also make cursor blinking
); );
if res == 0 { if res == 0 {
return Err("Failed to copy screen to Windows buffer".into()); return Err("Failed to copy screen to Windows buffer".into());

View File

@ -12,7 +12,7 @@ use winapi::{
// shared::dxgiformat::{DXGI_FORMAT, DXGI_FORMAT_B8G8R8A8_UNORM, DXGI_FORMAT_420_OPAQUE}, // shared::dxgiformat::{DXGI_FORMAT, DXGI_FORMAT_B8G8R8A8_UNORM, DXGI_FORMAT_420_OPAQUE},
shared::dxgi1_2::{IDXGIOutput1, IID_IDXGIOutput1}, shared::dxgi1_2::{IDXGIOutput1, IID_IDXGIOutput1},
shared::dxgitype::DXGI_MODE_ROTATION, shared::dxgitype::DXGI_MODE_ROTATION,
shared::minwindef::{TRUE, UINT}, shared::minwindef::{DWORD, FALSE, TRUE, UINT},
shared::ntdef::LONG, shared::ntdef::LONG,
shared::windef::HMONITOR, shared::windef::HMONITOR,
shared::winerror::{ shared::winerror::{
@ -25,7 +25,9 @@ use winapi::{
D3D11_CPU_ACCESS_READ, D3D11_SDK_VERSION, D3D11_USAGE_STAGING, D3D11_CPU_ACCESS_READ, D3D11_SDK_VERSION, D3D11_USAGE_STAGING,
}, },
um::d3dcommon::D3D_DRIVER_TYPE_UNKNOWN, um::d3dcommon::D3D_DRIVER_TYPE_UNKNOWN,
um::wingdi::*,
um::winnt::HRESULT, um::winnt::HRESULT,
um::winuser::*,
}; };
//TODO: Split up into files. //TODO: Split up into files.
@ -55,20 +57,24 @@ impl Capturer {
let mut desc = unsafe { mem::MaybeUninit::uninit().assume_init() }; let mut desc = unsafe { mem::MaybeUninit::uninit().assume_init() };
let mut gdi_capturer = None; let mut gdi_capturer = None;
let mut res = wrap_hresult(unsafe { let mut res = if display.gdi {
D3D11CreateDevice( wrap_hresult(1)
display.adapter as *mut _, } else {
D3D_DRIVER_TYPE_UNKNOWN, wrap_hresult(unsafe {
ptr::null_mut(), // No software rasterizer. D3D11CreateDevice(
0, // No device flags. display.adapter as *mut _,
ptr::null_mut(), // Feature levels. D3D_DRIVER_TYPE_UNKNOWN,
0, // Feature levels' length. ptr::null_mut(), // No software rasterizer.
D3D11_SDK_VERSION, 0, // No device flags.
&mut device, ptr::null_mut(), // Feature levels.
ptr::null_mut(), 0, // Feature levels' length.
&mut context, D3D11_SDK_VERSION,
) &mut device,
}); ptr::null_mut(),
&mut context,
)
})
};
if res.is_err() { if res.is_err() {
gdi_capturer = display.create_gdi(); gdi_capturer = display.create_gdi();
@ -346,6 +352,56 @@ impl Displays {
}) })
} }
pub fn get_from_gdi() -> Vec<Display> {
let mut all = Vec::new();
let mut i: DWORD = 0;
loop {
let mut d: DISPLAY_DEVICEW = unsafe { std::mem::MaybeUninit::uninit().assume_init() };
d.cb = std::mem::size_of::<DISPLAY_DEVICEW>() as _;
let ok = unsafe { EnumDisplayDevicesW(std::ptr::null(), i, &mut d as _, 0) };
if ok == FALSE {
break;
}
i += 1;
if 0 == (d.StateFlags & DISPLAY_DEVICE_ACTIVE)
|| (d.StateFlags & DISPLAY_DEVICE_MIRRORING_DRIVER) > 0
{
continue;
}
// let is_primary = (d.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE) > 0;
let mut disp = Display {
inner: std::ptr::null_mut(),
adapter: std::ptr::null_mut(),
desc: unsafe { std::mem::zeroed() },
gdi: true,
};
disp.desc.DeviceName = d.DeviceName;
let mut m: DEVMODEW = unsafe { std::mem::MaybeUninit::uninit().assume_init() };
m.dmSize = std::mem::size_of::<DEVMODEW>() as _;
m.dmDriverExtra = 0;
let ok = unsafe {
EnumDisplaySettingsExW(
disp.desc.DeviceName.as_ptr(),
ENUM_CURRENT_SETTINGS,
&mut m as _,
0,
)
};
if ok == FALSE {
continue;
}
disp.desc.DesktopCoordinates.left = unsafe { m.u1.s2().dmPosition.x };
disp.desc.DesktopCoordinates.top = unsafe { m.u1.s2().dmPosition.y };
disp.desc.DesktopCoordinates.right =
disp.desc.DesktopCoordinates.left + m.dmPelsWidth as i32;
disp.desc.DesktopCoordinates.bottom =
disp.desc.DesktopCoordinates.top + m.dmPelsHeight as i32;
disp.desc.AttachedToDesktop = 1;
all.push(disp);
}
all
}
// No Adapter => Some(None) // No Adapter => Some(None)
// Non-Empty Adapter => Some(Some(OUTPUT)) // Non-Empty Adapter => Some(Some(OUTPUT))
// End of Adapter => None // End of Adapter => None
@ -414,6 +470,7 @@ impl Displays {
inner, inner,
adapter: self.adapter, adapter: self.adapter,
desc, desc,
gdi: false,
})) }))
} }
} }
@ -460,8 +517,11 @@ pub struct Display {
inner: *mut IDXGIOutput1, inner: *mut IDXGIOutput1,
adapter: *mut IDXGIAdapter1, adapter: *mut IDXGIAdapter1,
desc: DXGI_OUTPUT_DESC, desc: DXGI_OUTPUT_DESC,
gdi: bool,
} }
// https://github.com/dchapyshev/aspia/blob/59233c5d01a4d03ed6de19b03ce77d61a6facf79/source/base/desktop/win/screen_capture_utils.cc
impl Display { impl Display {
pub fn width(&self) -> LONG { pub fn width(&self) -> LONG {
self.desc.DesktopCoordinates.right - self.desc.DesktopCoordinates.left self.desc.DesktopCoordinates.right - self.desc.DesktopCoordinates.left
@ -511,6 +571,9 @@ impl Display {
impl Drop for Display { impl Drop for Display {
fn drop(&mut self) { fn drop(&mut self) {
if self.inner.is_null() {
return;
}
unsafe { unsafe {
(*self.inner).Release(); (*self.inner).Release();
(*self.adapter).Release(); (*self.adapter).Release();

View File

@ -1,6 +1,7 @@
use super::{CursorData, ResultType}; use super::{CursorData, ResultType};
use hbb_common::{allow_err, bail, log}; use hbb_common::{allow_err, bail, log};
use libc::{c_char, c_int, c_void}; use libc::{c_char, c_int, c_void};
use std::io::prelude::*;
use std::{ use std::{
cell::RefCell, cell::RefCell,
sync::{ sync::{
@ -30,7 +31,7 @@ extern "C" {
#[link(name = "X11")] #[link(name = "X11")]
extern "C" { extern "C" {
fn XOpenDisplay(display_name: *const c_char) -> *mut c_void; fn XOpenDisplay(display_name: *const c_char) -> *mut c_void;
// fn XCloseDisplay(d: *mut c_void) -> c_int; // fn XCloseDisplay(d: *mut c_void) -> c_int;
} }
#[link(name = "Xfixes")] #[link(name = "Xfixes")]
@ -162,8 +163,36 @@ pub fn start_os_service() {
if tmp != uid && !tmp.is_empty() { if tmp != uid && !tmp.is_empty() {
uid = tmp; uid = tmp;
log::info!("uid of seat0: {}", uid); log::info!("uid of seat0: {}", uid);
std::env::set_var("XAUTHORITY", format!("/run/user/{}/gdm/Xauthority", uid)); let gdm = format!("/run/user/{}/gdm/Xauthority", uid);
std::env::set_var("DISPLAY", get_display()); let mut auth = get_env("XAUTHORITY", &uid);
if auth.is_empty() {
auth = if std::path::Path::new(&gdm).exists() {
gdm
} else {
let username = get_active_username();
if username == "root" {
format!("/{}/.Xauthority", username)
} else {
let tmp = format!("/home/{}/.Xauthority", username);
if std::path::Path::new(&tmp).exists() {
tmp
} else {
format!("/var/lib/{}/.Xauthority", username)
}
}
};
}
let mut d = get_env("DISPLAY", &uid);
if d.is_empty() {
d = get_display();
}
if d.is_empty() {
d = ":0".to_owned()
}
log::info!("DISPLAY: {}", d);
log::info!("XAUTHORITY: {}", auth);
std::env::set_var("XAUTHORITY", auth);
std::env::set_var("DISPLAY", d);
if let Some(ps) = server.as_mut() { if let Some(ps) = server.as_mut() {
allow_err!(ps.kill()); allow_err!(ps.kill());
std::thread::sleep(std::time::Duration::from_millis(30)); std::thread::sleep(std::time::Duration::from_millis(30));
@ -245,26 +274,27 @@ fn get_cm() -> bool {
fn get_display() -> String { fn get_display() -> String {
let user = get_active_username(); let user = get_active_username();
log::debug!("w {}", &user);
if let Ok(output) = std::process::Command::new("w").arg(&user).output() { if let Ok(output) = std::process::Command::new("w").arg(&user).output() {
for line in String::from_utf8_lossy(&output.stdout).lines() { for line in String::from_utf8_lossy(&output.stdout).lines() {
log::debug!(" {}", line);
let mut iter = line.split_whitespace(); let mut iter = line.split_whitespace();
let a = iter.nth(1); let b = iter.nth(2);
let b = iter.next(); if let Some(b) = b {
if a == b { if b.starts_with(":") {
if let Some(b) = b { return b.to_owned();
if b.starts_with(":") {
return b.to_owned();
}
} }
} }
} }
} }
// above not work for gdm user // above not work for gdm user
log::debug!("ls -l /tmp/.X11-unix/");
if let Ok(output) = std::process::Command::new("ls") if let Ok(output) = std::process::Command::new("ls")
.args(vec!["-l", "/tmp/.X11-unix/"]) .args(vec!["-l", "/tmp/.X11-unix/"])
.output() .output()
{ {
for line in String::from_utf8_lossy(&output.stdout).lines() { for line in String::from_utf8_lossy(&output.stdout).lines() {
log::debug!(" {}", line);
let mut iter = line.split_whitespace(); let mut iter = line.split_whitespace();
if iter.nth(2) == Some(&user) { if iter.nth(2) == Some(&user) {
if let Some(x) = iter.last() { if let Some(x) = iter.last() {
@ -444,3 +474,33 @@ pub fn block_input(_v: bool) {
pub fn is_installed() -> bool { pub fn is_installed() -> bool {
true true
} }
fn run_cmds(cmds: String) -> ResultType<Option<String>> {
let mut tmp = std::env::temp_dir();
tmp.push(format!(
"{}_{}",
hbb_common::config::APP_NAME,
crate::get_time()
));
let mut file = std::fs::File::create(&tmp)?;
file.write_all(cmds.as_bytes())?;
file.sync_all()?;
if let Ok(output) = std::process::Command::new("bash")
.arg(tmp.to_str().unwrap_or(""))
.output()
{
Ok(Some(String::from_utf8_lossy(&output.stdout).to_string()))
} else {
Ok(None)
}
}
fn get_env(name: &str, uid: &str) -> String {
let cmd = format!("ps -u {} -o pid= | xargs -I__ cat /proc/__/environ 2>/dev/null | tr '\\0' '\\n' | grep -m1 '^{}=' | sed 's/{}=//g'", uid, name, name);
log::debug!("Run: {}", &cmd);
if let Ok(Some(x)) = run_cmds(cmd) {
x.trim_end().to_string()
} else {
"".to_owned()
}
}

View File

@ -17,6 +17,17 @@ pub mod linux;
use hbb_common::{message_proto::CursorData, ResultType}; use hbb_common::{message_proto::CursorData, ResultType};
const SERVICE_INTERVAL: u64 = 300; const SERVICE_INTERVAL: u64 = 300;
pub fn is_xfce() -> bool {
#[cfg(target_os = "linux")]
{
return std::env::var_os("XDG_CURRENT_DESKTOP") == Some(std::ffi::OsString::from("XFCE"));
}
#[cfg(not(target_os = "linux"))]
{
return false;
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;

View File

@ -105,6 +105,9 @@ pub fn get_cursor_data(hcursor: u64) -> ResultType<CursorData> {
bm_mask.bmHeight / 2 bm_mask.bmHeight / 2
}; };
let cbits_size = width * height * 4; let cbits_size = width * height * 4;
if cbits_size < 16 {
bail!("Invalid icon: too small"); // solve some crash
}
let mut cbits: Vec<u8> = Vec::new(); let mut cbits: Vec<u8> = Vec::new();
cbits.resize(cbits_size as _, 0); cbits.resize(cbits_size as _, 0);
let mut mbits: Vec<u8> = Vec::new(); let mut mbits: Vec<u8> = Vec::new();

View File

@ -302,6 +302,7 @@ impl RendezvousMediator {
relay_server = fla.relay_server; relay_server = fla.relay_server;
} }
msg_out.set_local_addr(LocalAddr { msg_out.set_local_addr(LocalAddr {
id: Config::get_id(),
socket_addr: AddrMangle::encode(peer_addr), socket_addr: AddrMangle::encode(peer_addr),
local_addr: AddrMangle::encode(local_addr), local_addr: AddrMangle::encode(local_addr),
relay_server, relay_server,
@ -309,7 +310,7 @@ impl RendezvousMediator {
}); });
let bytes = msg_out.write_to_bytes()?; let bytes = msg_out.write_to_bytes()?;
socket.send_raw(bytes).await?; socket.send_raw(bytes).await?;
crate::accept_connection(server.clone(), socket, peer_addr, false).await; crate::accept_connection(server.clone(), socket, peer_addr, true).await;
Ok(()) Ok(())
} }

View File

@ -198,6 +198,7 @@ mod cpal_impl {
log::error!("an error occurred on stream: {}", err); log::error!("an error occurred on stream: {}", err);
}; };
// Sample rate must be one of 8000, 12000, 16000, 24000, or 48000. // Sample rate must be one of 8000, 12000, 16000, 24000, or 48000.
// Note: somehow 48000 not work
let sample_rate_0 = config.sample_rate().0; let sample_rate_0 = config.sample_rate().0;
let sample_rate = if sample_rate_0 < 12000 { let sample_rate = if sample_rate_0 < 12000 {
8000 8000
@ -205,10 +206,8 @@ mod cpal_impl {
12000 12000
} else if sample_rate_0 < 24000 { } else if sample_rate_0 < 24000 {
16000 16000
} else if sample_rate_0 < 48000 {
24000
} else { } else {
48000 24000
}; };
let mut encoder = Encoder::new( let mut encoder = Encoder::new(
sample_rate, sample_rate,

View File

@ -437,7 +437,7 @@ impl Connection {
try_activate_screen(); try_activate_screen();
match super::video_service::get_displays() { match super::video_service::get_displays() {
Err(err) => { Err(err) => {
res.set_error(err.to_string()); res.set_error(format!("X11 error: {}", err));
} }
Ok((current, displays)) => { Ok((current, displays)) => {
pi.displays = displays.into(); pi.displays = displays.into();

View File

@ -73,7 +73,11 @@ fn run(sp: GenericService) -> ResultType<()> {
rc_max_quantizer, rc_max_quantizer,
speed, speed,
}; };
let mut vpx = Encoder::new(&cfg, 1).with_context(|| "Failed to create encoder")?; let mut vpx;
match Encoder::new(&cfg, 1) {
Ok(x) => vpx = x,
Err(err) => bail!("Failed to create encoder: {}", err),
}
if *SWITCH.lock().unwrap() { if *SWITCH.lock().unwrap() {
log::debug!("Broadcasting display switch"); log::debug!("Broadcasting display switch");

View File

@ -495,10 +495,15 @@ impl UI {
let p = "xdg-open"; let p = "xdg-open";
allow_err!(std::process::Command::new(p).arg(url).spawn()); allow_err!(std::process::Command::new(p).arg(url).spawn());
} }
fn is_xfce(&self) -> bool {
crate::platform::is_xfce()
}
} }
impl sciter::EventHandler for UI { impl sciter::EventHandler for UI {
sciter::dispatch_script_call! { sciter::dispatch_script_call! {
fn is_xfce();
fn get_id(); fn get_id();
fn get_password(); fn get_password();
fn update_password(String); fn update_password(String);

View File

@ -246,7 +246,11 @@ function update() {
function bring_to_top(idx=-1) { function bring_to_top(idx=-1) {
if (view.windowState == View.WINDOW_HIDDEN || view.windowState == View.WINDOW_MINIMIZED) { if (view.windowState == View.WINDOW_HIDDEN || view.windowState == View.WINDOW_MINIMIZED) {
view.windowState = View.WINDOW_SHOWN; if (is_linux) {
view.focus = self;
} else {
view.windowState = View.WINDOW_SHOWN;
}
if (idx >= 0) body.cur = idx; if (idx >= 0) body.cur = idx;
} else { } else {
view.windowTopmost = true; view.windowTopmost = true;

View File

@ -7,6 +7,8 @@ var is_osx = OS == "OSX";
var is_win = OS == "Windows"; var is_win = OS == "Windows";
var is_linux = OS == "Linux"; var is_linux = OS == "Linux";
var is_file_transfer; var is_file_transfer;
var is_xfce = false;
try { is_xfce = handler.is_xfce(); } catch(e) {}
function hashCode(str) { function hashCode(str) {
var hash = 160 << 16 + 114 << 8 + 91; var hash = 160 << 16 + 114 << 8 + 91;
@ -221,8 +223,8 @@ function msgbox(type, title, text, callback, height, width) {
var dialog = { var dialog = {
client: true, client: true,
parameters: msgbox_params, parameters: msgbox_params,
width: width, width: width + (is_xfce ? 50 : 0),
height: height, height: height + (is_xfce ? 50 : 0),
}; };
var html = handler.get_msgbox(); var html = handler.get_msgbox();
if (html) dialog.html = html; if (html) dialog.html = html;

View File

@ -15,10 +15,28 @@ var svg_insecure = <svg viewBox="0 0 347.97 347.97"><path d="M317.469 61.615c-59
var svg_insecure_relay = <svg viewBox="0 0 347.97 347.97"><path d="M317.469 61.615c-59.442 0-104.976-16.082-143.489-51.539-38.504 35.457-84.04 51.539-143.479 51.539 0 92.337-20.177 224.612 143.479 278.324 163.661-53.717 143.489-185.992 143.489-278.324z" fill="none" stroke="red" stroke-width="14.827"/><g fill="red"><path d="M231.442 247.498l-7.754-10.205c-17.268 12.441-38.391 17.705-59.478 14.822-21.087-2.883-39.613-13.569-52.166-30.088-25.916-34.101-17.997-82.738 17.65-108.42 32.871-23.685 78.02-19.704 105.172 7.802l-32.052 7.987 3.082 12.369 48.722-12.142-11.712-46.998-12.822 3.196 4.496 18.039c-31.933-24.008-78.103-25.342-112.642-.458-31.361 22.596-44.3 60.436-35.754 94.723 2.77 11.115 7.801 21.862 15.192 31.588 30.19 39.727 88.538 47.705 130.066 17.785z"/></g></svg>; var svg_insecure_relay = <svg viewBox="0 0 347.97 347.97"><path d="M317.469 61.615c-59.442 0-104.976-16.082-143.489-51.539-38.504 35.457-84.04 51.539-143.479 51.539 0 92.337-20.177 224.612 143.479 278.324 163.661-53.717 143.489-185.992 143.489-278.324z" fill="none" stroke="red" stroke-width="14.827"/><g fill="red"><path d="M231.442 247.498l-7.754-10.205c-17.268 12.441-38.391 17.705-59.478 14.822-21.087-2.883-39.613-13.569-52.166-30.088-25.916-34.101-17.997-82.738 17.65-108.42 32.871-23.685 78.02-19.704 105.172 7.802l-32.052 7.987 3.082 12.369 48.722-12.142-11.712-46.998-12.822 3.196 4.496 18.039c-31.933-24.008-78.103-25.342-112.642-.458-31.361 22.596-44.3 60.436-35.754 94.723 2.77 11.115 7.801 21.862 15.192 31.588 30.19 39.727 88.538 47.705 130.066 17.785z"/></g></svg>;
var svg_secure_relay = <svg viewBox="0 0 347.97 347.97"><path d="M317.469 61.615c-59.442 0-104.976-16.082-143.489-51.539-38.504 35.457-84.04 51.539-143.479 51.539 0 92.337-20.177 224.612 143.479 278.324 163.661-53.717 143.489-185.992 143.489-278.324z" fill="#3f7d46" stroke="#3f7d46" stroke-width="14.827"/><g fill="red"><path d="M231.442 247.498l-7.754-10.205c-17.268 12.441-38.391 17.705-59.478 14.822-21.087-2.883-39.613-13.569-52.166-30.088-25.916-34.101-17.997-82.738 17.65-108.42 32.871-23.685 78.02-19.704 105.172 7.802l-32.052 7.987 3.082 12.369 48.722-12.142-11.712-46.998-12.822 3.196 4.496 18.039c-31.933-24.008-78.103-25.342-112.642-.458-31.361 22.596-44.3 60.436-35.754 94.723 2.77 11.115 7.801 21.862 15.192 31.588 30.19 39.727 88.538 47.705 130.066 17.785z" fill="#fff"/></g></svg>; var svg_secure_relay = <svg viewBox="0 0 347.97 347.97"><path d="M317.469 61.615c-59.442 0-104.976-16.082-143.489-51.539-38.504 35.457-84.04 51.539-143.479 51.539 0 92.337-20.177 224.612 143.479 278.324 163.661-53.717 143.489-185.992 143.489-278.324z" fill="#3f7d46" stroke="#3f7d46" stroke-width="14.827"/><g fill="red"><path d="M231.442 247.498l-7.754-10.205c-17.268 12.441-38.391 17.705-59.478 14.822-21.087-2.883-39.613-13.569-52.166-30.088-25.916-34.101-17.997-82.738 17.65-108.42 32.871-23.685 78.02-19.704 105.172 7.802l-32.052 7.987 3.082 12.369 48.722-12.142-11.712-46.998-12.822 3.196 4.496 18.039c-31.933-24.008-78.103-25.342-112.642-.458-31.361 22.596-44.3 60.436-35.754 94.723 2.77 11.115 7.801 21.862 15.192 31.588 30.19 39.727 88.538 47.705 130.066 17.785z" fill="#fff"/></g></svg>;
view << event statechange { var cur_window_state = view.windowState;
function check_state_change() {
if (view.windowState != cur_window_state) {
stateChanged();
}
self.timer(30ms, check_state_change);
}
if (is_linux) {
check_state_change();
} else {
view << event statechange {
stateChanged();
}
}
function stateChanged() {
stdout.println('state changed from ' + cur_window_state + ' -> ' + view.windowState);
cur_window_state = view.windowState;
adjustBorder(); adjustBorder();
adaptDisplay(); adaptDisplay();
view.focus = handler; if (!is_linux) view.focus = handler; // this cause windows always topmost on linux
var fs = view.windowState == View.WINDOW_FULL_SCREEN; var fs = view.windowState == View.WINDOW_FULL_SCREEN;
var el = $(#fullscreen); var el = $(#fullscreen);
if (el) el.attributes.toggleClass("active", fs); if (el) el.attributes.toggleClass("active", fs);
@ -26,6 +44,11 @@ view << event statechange {
if (el) { if (el) {
el.state.disabled = fs; el.state.disabled = fs;
} }
if (fs) {
$(header).style.set {
display: "none",
};
}
} }
var header; var header;
@ -56,7 +79,7 @@ class Header: Reactor.Component {
var title = handler.get_id(); var title = handler.get_id();
if (pi.hostname) title += "(" + pi.username + "@" + pi.hostname + ")"; if (pi.hostname) title += "(" + pi.username + "@" + pi.hostname + ")";
if ((pi.displays || []).length == 0) { if ((pi.displays || []).length == 0) {
return <div .ellipsis style={is_osx || is_win ? "size:*;text-align:center;margin:*;" : ""}>{title}</div>; return <div .ellipsis style="size:*;text-align:center;margin:*;">{title}</div>;
} }
var screens = pi.displays.map(function(d, i) { var screens = pi.displays.map(function(d, i) {
return <div #screen class={pi.current_display == i ? "current" : ""}> return <div #screen class={pi.current_display == i ? "current" : ""}>
@ -68,7 +91,7 @@ class Header: Reactor.Component {
if (is_osx) style += "margin: *"; if (is_osx) style += "margin: *";
self.timer(1ms, toggleMenuState); self.timer(1ms, toggleMenuState);
return <div style={style}> return <div style={style}>
{is_osx ? "" : <span #fullscreen>{svg_fullscreen}</span>} {is_osx || is_xfce ? "" : <span #fullscreen>{svg_fullscreen}</span>}
<div #screens> <div #screens>
<span #secure title={title_conn}>{icon_conn}</span> <span #secure title={title_conn}>{icon_conn}</span>
<div .remote-id>{handler.get_id()}</div> <div .remote-id>{handler.get_id()}</div>
@ -164,6 +187,7 @@ class Header: Reactor.Component {
view.windowState = View.WINDOW_SHOWN; view.windowState = View.WINDOW_SHOWN;
} }
view.windowState = View.WINDOW_FULL_SCREEN; view.windowState = View.WINDOW_FULL_SCREEN;
if (is_linux) { self.timer(150ms, function() { view.windowState = View.WINDOW_FULL_SCREEN; }); }
} }
} }

View File

@ -139,6 +139,7 @@ impl sciter::EventHandler for Handler {
} }
sciter::dispatch_script_call! { sciter::dispatch_script_call! {
fn is_xfce();
fn get_id(); fn get_id();
fn get_default_pi(); fn get_default_pi();
fn get_option(String); fn get_option(String);
@ -279,6 +280,10 @@ impl Handler {
self.lc.read().unwrap().remember self.lc.read().unwrap().remember
} }
fn is_xfce(&self) -> bool {
crate::platform::is_xfce()
}
fn save_size(&mut self, x: i32, y: i32, w: i32, h: i32) { fn save_size(&mut self, x: i32, y: i32, w: i32, h: i32) {
let size = (x, y, w, h); let size = (x, y, w, h);
let mut config = self.load_config(); let mut config = self.load_config();

View File

@ -10,6 +10,7 @@ var display_scale = 1;
var keyboard_enabled = true; // server side var keyboard_enabled = true; // server side
var clipboard_enabled = true; // server side var clipboard_enabled = true; // server side
var audio_enabled = true; // server side var audio_enabled = true; // server side
var scroll_body = $(body);
handler.setDisplay = function(x, y, w, h) { handler.setDisplay = function(x, y, w, h) {
display_width = w; display_width = w;
@ -166,7 +167,7 @@ function handler.onMouse(evt)
{ {
if (is_file_transfer || is_port_forward) return false; if (is_file_transfer || is_port_forward) return false;
if (view.windowState == View.WINDOW_FULL_SCREEN && !dragging) { if (view.windowState == View.WINDOW_FULL_SCREEN && !dragging) {
if (evt.y < 10) { if (evt.y - scroll_body.scroll(#top) < 10) {
if (!wait_window_toolbar) { if (!wait_window_toolbar) {
wait_window_toolbar = true; wait_window_toolbar = true;
self.timer(300ms, function() { self.timer(300ms, function() {
@ -392,23 +393,25 @@ handler.adaptSize = function() {
size_adapted = true; size_adapted = true;
var (sx, sy, sw, sh) = view.screenBox(#workarea, #rectw); var (sx, sy, sw, sh) = view.screenBox(#workarea, #rectw);
var (fx, fy, fw, fh) = view.screenBox(#frame, #rectw); var (fx, fy, fw, fh) = view.screenBox(#frame, #rectw);
workarea_offset = sy; if (is_osx) workarea_offset = sy;
var r = handler.get_size(); var r = handler.get_size();
if (r[2] > 0) { if (r[2] > 0) {
if (r[2] >= fw && r[3] >= fh) { if (r[2] >= fw && r[3] >= fh && !is_linux) {
view.windowState = View.WINDOW_FULL_SCREEN; view.windowState = View.WINDOW_FULL_SCREEN;
stdout.println("Initialize to full screen");
} else if (r[2] >= sw && r[3] >= sh) { } else if (r[2] >= sw && r[3] >= sh) {
view.windowState = View.WINDOW_MAXIMIZED; view.windowState = View.WINDOW_MAXIMIZED;
stdout.println("Initialize to full screen");
} else { } else {
view.move(r[0], r[1], r[2], r[3]); view.move(r[0], r[1], r[2], r[3]);
} }
} else { } else {
var w = handler.box(#width, #border) var w = handler.box(#width, #border)
if (sw == w) { var h = handler.box(#height, #border)
if (w >= sw || h >= sh) {
view.windowState = View.WINDOW_MAXIMIZED; view.windowState = View.WINDOW_MAXIMIZED;
return; return;
} }
var h = $(header).box(#height, #border);
// extra for border // extra for border
var extra = 2; var extra = 2;
centerize(w + extra, handler.box(#height, #border) + h + extra); centerize(w + extra, handler.box(#height, #border) + h + extra);