xwayland: allow windows to scale themselves
This commit is contained in:
parent
cc8db84289
commit
19b07fa7dc
40 changed files with 800 additions and 80 deletions
|
|
@ -27,6 +27,7 @@ use {
|
||||||
connector_type::{ConnectorType, CON_UNKNOWN},
|
connector_type::{ConnectorType, CON_UNKNOWN},
|
||||||
Connector, DrmDevice, Format, GfxApi, Mode, TearingMode, Transform, VrrMode,
|
Connector, DrmDevice, Format, GfxApi, Mode, TearingMode, Transform, VrrMode,
|
||||||
},
|
},
|
||||||
|
xwayland::XScalingMode,
|
||||||
Axis, Direction, ModifiedKeySym, PciId, Workspace,
|
Axis, Direction, ModifiedKeySym, PciId, Workspace,
|
||||||
},
|
},
|
||||||
bincode::Options,
|
bincode::Options,
|
||||||
|
|
@ -816,6 +817,10 @@ impl Client {
|
||||||
(width, height)
|
(width, height)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_x_scaling_mode(&self, mode: XScalingMode) {
|
||||||
|
self.send(&ClientMessage::SetXScalingMode { mode })
|
||||||
|
}
|
||||||
|
|
||||||
pub fn set_vrr_mode(&self, connector: Option<Connector>, mode: VrrMode) {
|
pub fn set_vrr_mode(&self, connector: Option<Connector>, mode: VrrMode) {
|
||||||
self.send(&ClientMessage::SetVrrMode { connector, mode })
|
self.send(&ClientMessage::SetVrrMode { connector, mode })
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ use {
|
||||||
},
|
},
|
||||||
Axis, Direction, PciId, Workspace,
|
Axis, Direction, PciId, Workspace,
|
||||||
_private::{PollableId, WireMode},
|
_private::{PollableId, WireMode},
|
||||||
|
xwayland::XScalingMode,
|
||||||
},
|
},
|
||||||
serde::{Deserialize, Serialize},
|
serde::{Deserialize, Serialize},
|
||||||
std::time::Duration,
|
std::time::Duration,
|
||||||
|
|
@ -523,6 +524,9 @@ pub enum ClientMessage<'a> {
|
||||||
SetUiDragThreshold {
|
SetUiDragThreshold {
|
||||||
threshold: i32,
|
threshold: i32,
|
||||||
},
|
},
|
||||||
|
SetXScalingMode {
|
||||||
|
mode: XScalingMode,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
|
|
||||||
|
|
@ -66,6 +66,7 @@ pub mod tasks;
|
||||||
pub mod theme;
|
pub mod theme;
|
||||||
pub mod timer;
|
pub mod timer;
|
||||||
pub mod video;
|
pub mod video;
|
||||||
|
pub mod xwayland;
|
||||||
|
|
||||||
/// A planar direction.
|
/// A planar direction.
|
||||||
#[derive(Serialize, Deserialize, Copy, Clone, Debug, Eq, PartialEq)]
|
#[derive(Serialize, Deserialize, Copy, Clone, Debug, Eq, PartialEq)]
|
||||||
|
|
|
||||||
33
jay-config/src/xwayland.rs
Normal file
33
jay-config/src/xwayland.rs
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
//! Tools for configuring Xwayland.
|
||||||
|
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
/// The scaling mode of X windows.
|
||||||
|
#[derive(Serialize, Deserialize, Copy, Clone, Debug, Eq, PartialEq, Hash, Default)]
|
||||||
|
pub struct XScalingMode(pub u32);
|
||||||
|
|
||||||
|
impl XScalingMode {
|
||||||
|
/// The default mode.
|
||||||
|
///
|
||||||
|
/// Currently this means that windows are rendered at the lowest scale and then
|
||||||
|
/// upscaled if necessary.
|
||||||
|
pub const DEFAULT: Self = Self(0);
|
||||||
|
/// Windows are rendered at the highest integer scale and then downscaled.
|
||||||
|
///
|
||||||
|
/// This has significant performance implications unless the window is running on the
|
||||||
|
/// output with the highest scale and that scale is an integer scale.
|
||||||
|
///
|
||||||
|
/// For example, on a 3840x2160 output with a 1.5 scale, a fullscreen window will be
|
||||||
|
/// rendered at 3840x2160 * 2 / 1.5 = 5120x2880 pixels and then downscaled to
|
||||||
|
/// 3840x2160. This overhead gets worse the lower the scale of the output is.
|
||||||
|
///
|
||||||
|
/// Additionally, this mode requires the X window to scale its contents itself. In the
|
||||||
|
/// example above, you might achieve this by setting the environment variable
|
||||||
|
/// `GDK_SCALE=2`.
|
||||||
|
pub const DOWNSCALED: Self = Self(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the scaling mode for X windows.
|
||||||
|
pub fn set_x_scaling_mode(mode: XScalingMode) {
|
||||||
|
get!().set_x_scaling_mode(mode)
|
||||||
|
}
|
||||||
|
|
@ -4,6 +4,7 @@
|
||||||
- Tiles and workspaces can now be dragged with the mouse.
|
- Tiles and workspaces can now be dragged with the mouse.
|
||||||
- Vulkan is now the default renderer.
|
- Vulkan is now the default renderer.
|
||||||
- Emulate vblank events on the nvidia driver.
|
- Emulate vblank events on the nvidia driver.
|
||||||
|
- Allow X windows to scale themselves.
|
||||||
|
|
||||||
# 1.6.0 (2024-09-25)
|
# 1.6.0 (2024-09-25)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,10 +12,14 @@ pub mod screenshot;
|
||||||
mod seat_test;
|
mod seat_test;
|
||||||
mod set_log_level;
|
mod set_log_level;
|
||||||
mod unlock;
|
mod unlock;
|
||||||
|
mod xwayland;
|
||||||
|
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{
|
||||||
cli::{damage_tracking::DamageTrackingArgs, input::InputArgs, randr::RandrArgs},
|
cli::{
|
||||||
|
damage_tracking::DamageTrackingArgs, input::InputArgs, randr::RandrArgs,
|
||||||
|
xwayland::XwaylandArgs,
|
||||||
|
},
|
||||||
compositor::start_compositor,
|
compositor::start_compositor,
|
||||||
format::{ref_formats, Format},
|
format::{ref_formats, Format},
|
||||||
portal,
|
portal,
|
||||||
|
|
@ -72,6 +76,8 @@ pub enum Cmd {
|
||||||
/// Modify damage tracking settings. (Only for debugging.)
|
/// Modify damage tracking settings. (Only for debugging.)
|
||||||
#[clap(hide = true)]
|
#[clap(hide = true)]
|
||||||
DamageTracking(DamageTrackingArgs),
|
DamageTracking(DamageTrackingArgs),
|
||||||
|
/// Inspect/modify xwayland settings.
|
||||||
|
Xwayland(XwaylandArgs),
|
||||||
#[cfg(feature = "it")]
|
#[cfg(feature = "it")]
|
||||||
RunTests,
|
RunTests,
|
||||||
}
|
}
|
||||||
|
|
@ -259,6 +265,7 @@ pub fn main() {
|
||||||
Cmd::Randr(a) => randr::main(cli.global, a),
|
Cmd::Randr(a) => randr::main(cli.global, a),
|
||||||
Cmd::Input(a) => input::main(cli.global, a),
|
Cmd::Input(a) => input::main(cli.global, a),
|
||||||
Cmd::DamageTracking(a) => damage_tracking::main(cli.global, a),
|
Cmd::DamageTracking(a) => damage_tracking::main(cli.global, a),
|
||||||
|
Cmd::Xwayland(a) => xwayland::main(cli.global, a),
|
||||||
#[cfg(feature = "it")]
|
#[cfg(feature = "it")]
|
||||||
Cmd::RunTests => crate::it::run_tests(),
|
Cmd::RunTests => crate::it::run_tests(),
|
||||||
}
|
}
|
||||||
|
|
|
||||||
106
src/cli/xwayland.rs
Normal file
106
src/cli/xwayland.rs
Normal file
|
|
@ -0,0 +1,106 @@
|
||||||
|
use {
|
||||||
|
crate::{
|
||||||
|
cli::GlobalArgs,
|
||||||
|
tools::tool_client::{with_tool_client, Handle, ToolClient},
|
||||||
|
wire::{jay_compositor, jay_xwayland, JayXwaylandId},
|
||||||
|
},
|
||||||
|
clap::{Args, Subcommand, ValueEnum},
|
||||||
|
jay_config::xwayland::XScalingMode,
|
||||||
|
std::{cell::Cell, rc::Rc},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Args, Debug)]
|
||||||
|
pub struct XwaylandArgs {
|
||||||
|
#[clap(subcommand)]
|
||||||
|
pub command: Option<XwaylandCmd>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Subcommand, Debug, Default)]
|
||||||
|
pub enum XwaylandCmd {
|
||||||
|
/// Print the Xwayland status.
|
||||||
|
#[default]
|
||||||
|
Status,
|
||||||
|
/// Set the Xwayland scaling mode.
|
||||||
|
SetScalingMode(SetScalingModeArgs),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Args, Debug)]
|
||||||
|
pub struct SetScalingModeArgs {
|
||||||
|
#[clap(value_enum)]
|
||||||
|
pub mode: CliScalingMode,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(ValueEnum, Debug, Copy, Clone, Hash, PartialEq)]
|
||||||
|
pub enum CliScalingMode {
|
||||||
|
/// The default mode.
|
||||||
|
Default,
|
||||||
|
/// Windows are rendered at the highest integer scale and then downscaled.
|
||||||
|
Downscaled,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn main(global: GlobalArgs, args: XwaylandArgs) {
|
||||||
|
with_tool_client(global.log_level.into(), |tc| async move {
|
||||||
|
let xwayland = Xwayland { tc: tc.clone() };
|
||||||
|
xwayland.run(args).await;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Xwayland {
|
||||||
|
tc: Rc<ToolClient>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Xwayland {
|
||||||
|
async fn run(self, args: XwaylandArgs) {
|
||||||
|
let tc = &self.tc;
|
||||||
|
let comp = tc.jay_compositor().await;
|
||||||
|
let xwayland = tc.id();
|
||||||
|
tc.send(jay_compositor::GetXwayland {
|
||||||
|
self_id: comp,
|
||||||
|
id: xwayland,
|
||||||
|
});
|
||||||
|
match args.command.unwrap_or_default() {
|
||||||
|
XwaylandCmd::Status => self.status(xwayland).await,
|
||||||
|
XwaylandCmd::SetScalingMode(args) => self.set_scaling_mode(xwayland, args).await,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn status(self, xwayland: JayXwaylandId) {
|
||||||
|
let tc = &self.tc;
|
||||||
|
tc.send(jay_xwayland::GetScaling { self_id: xwayland });
|
||||||
|
let mode = Rc::new(Cell::new(0));
|
||||||
|
let scale = Rc::new(Cell::new(None));
|
||||||
|
jay_xwayland::ScalingMode::handle(tc, xwayland, mode.clone(), |iv, msg| {
|
||||||
|
iv.set(msg.mode);
|
||||||
|
});
|
||||||
|
jay_xwayland::ImpliedScale::handle(tc, xwayland, scale.clone(), |iv, msg| {
|
||||||
|
iv.set(Some(msg.scale));
|
||||||
|
});
|
||||||
|
tc.round_trip().await;
|
||||||
|
let mode_str;
|
||||||
|
let mode = match XScalingMode(mode.get()) {
|
||||||
|
XScalingMode::DEFAULT => "default",
|
||||||
|
XScalingMode::DOWNSCALED => "downscaled",
|
||||||
|
o => {
|
||||||
|
mode_str = format!("unknown ({})", o.0);
|
||||||
|
&mode_str
|
||||||
|
}
|
||||||
|
};
|
||||||
|
println!("scaling mode: {}", mode);
|
||||||
|
if let Some(scale) = scale.get() {
|
||||||
|
println!("implied scale: {}", scale);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn set_scaling_mode(self, xwayland: JayXwaylandId, args: SetScalingModeArgs) {
|
||||||
|
let tc = &self.tc;
|
||||||
|
let mode = match args.mode {
|
||||||
|
CliScalingMode::Default => XScalingMode::DEFAULT,
|
||||||
|
CliScalingMode::Downscaled => XScalingMode::DOWNSCALED,
|
||||||
|
};
|
||||||
|
tc.send(jay_xwayland::SetScalingMode {
|
||||||
|
self_id: xwayland,
|
||||||
|
mode: mode.0,
|
||||||
|
});
|
||||||
|
tc.round_trip().await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -172,6 +172,7 @@ impl Clients {
|
||||||
&global.wait_for_sync_obj,
|
&global.wait_for_sync_obj,
|
||||||
&global.ring,
|
&global.ring,
|
||||||
)),
|
)),
|
||||||
|
wire_scale: Default::default(),
|
||||||
});
|
});
|
||||||
track!(data, data);
|
track!(data, data);
|
||||||
let display = Rc::new(WlDisplay::new(&data));
|
let display = Rc::new(WlDisplay::new(&data));
|
||||||
|
|
@ -282,6 +283,7 @@ pub struct Client {
|
||||||
pub surfaces_by_xwayland_serial: CopyHashMap<u64, Rc<WlSurface>>,
|
pub surfaces_by_xwayland_serial: CopyHashMap<u64, Rc<WlSurface>>,
|
||||||
pub activation_tokens: RefCell<VecDeque<ActivationToken>>,
|
pub activation_tokens: RefCell<VecDeque<ActivationToken>>,
|
||||||
pub commit_timelines: Rc<CommitTimelines>,
|
pub commit_timelines: Rc<CommitTimelines>,
|
||||||
|
pub wire_scale: Cell<Option<i32>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const NUM_CACHED_SERIAL_RANGES: usize = 64;
|
pub const NUM_CACHED_SERIAL_RANGES: usize = 64;
|
||||||
|
|
|
||||||
|
|
@ -211,6 +211,8 @@ fn start_compositor2(
|
||||||
handler: Default::default(),
|
handler: Default::default(),
|
||||||
queue: Default::default(),
|
queue: Default::default(),
|
||||||
ipc_device_ids: Default::default(),
|
ipc_device_ids: Default::default(),
|
||||||
|
use_wire_scale: Default::default(),
|
||||||
|
wire_scale: Default::default(),
|
||||||
},
|
},
|
||||||
acceptor: Default::default(),
|
acceptor: Default::default(),
|
||||||
serial: Default::default(),
|
serial: Default::default(),
|
||||||
|
|
|
||||||
|
|
@ -53,6 +53,7 @@ use {
|
||||||
Connector, DrmDevice, Format as ConfigFormat, GfxApi, TearingMode as ConfigTearingMode,
|
Connector, DrmDevice, Format as ConfigFormat, GfxApi, TearingMode as ConfigTearingMode,
|
||||||
Transform, VrrMode as ConfigVrrMode,
|
Transform, VrrMode as ConfigVrrMode,
|
||||||
},
|
},
|
||||||
|
xwayland::XScalingMode,
|
||||||
Axis, Direction, Workspace,
|
Axis, Direction, Workspace,
|
||||||
},
|
},
|
||||||
libloading::Library,
|
libloading::Library,
|
||||||
|
|
@ -759,6 +760,17 @@ impl ConfigProxyHandler {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn handle_set_x_scaling_mode(&self, mode: XScalingMode) -> Result<(), CphError> {
|
||||||
|
let use_wire_scale = match mode {
|
||||||
|
XScalingMode::DEFAULT => false,
|
||||||
|
XScalingMode::DOWNSCALED => true,
|
||||||
|
_ => return Err(CphError::UnknownXScalingMode(mode)),
|
||||||
|
};
|
||||||
|
self.state.xwayland.use_wire_scale.set(use_wire_scale);
|
||||||
|
self.state.update_xwayland_wire_scale();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
fn handle_set_ui_drag_enabled(&self, enabled: bool) {
|
fn handle_set_ui_drag_enabled(&self, enabled: bool) {
|
||||||
self.state.ui_drag_enabled.set(enabled);
|
self.state.ui_drag_enabled.set(enabled);
|
||||||
}
|
}
|
||||||
|
|
@ -1965,6 +1977,9 @@ impl ConfigProxyHandler {
|
||||||
ClientMessage::SetUiDragThreshold { threshold } => {
|
ClientMessage::SetUiDragThreshold { threshold } => {
|
||||||
self.handle_set_ui_drag_threshold(threshold)
|
self.handle_set_ui_drag_threshold(threshold)
|
||||||
}
|
}
|
||||||
|
ClientMessage::SetXScalingMode { mode } => self
|
||||||
|
.handle_set_x_scaling_mode(mode)
|
||||||
|
.wrn("set_x_scaling_mode")?,
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
@ -2034,6 +2049,8 @@ enum CphError {
|
||||||
UnknownTearingMode(ConfigTearingMode),
|
UnknownTearingMode(ConfigTearingMode),
|
||||||
#[error("The format {0:?} is unknown")]
|
#[error("The format {0:?} is unknown")]
|
||||||
UnknownFormat(ConfigFormat),
|
UnknownFormat(ConfigFormat),
|
||||||
|
#[error("Unknown x scaling mode {0:?}")]
|
||||||
|
UnknownXScalingMode(XScalingMode),
|
||||||
}
|
}
|
||||||
|
|
||||||
trait WithRequestName {
|
trait WithRequestName {
|
||||||
|
|
|
||||||
18
src/fixed.rs
18
src/fixed.rs
|
|
@ -1,7 +1,7 @@
|
||||||
use std::{
|
use std::{
|
||||||
cmp::Ordering,
|
cmp::Ordering,
|
||||||
fmt::{Debug, Display, Formatter},
|
fmt::{Debug, Display, Formatter},
|
||||||
ops::{Add, AddAssign, Sub, SubAssign},
|
ops::{Add, AddAssign, Div, Mul, Sub, SubAssign},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Default)]
|
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Default)]
|
||||||
|
|
@ -108,6 +108,22 @@ impl Add<i32> for Fixed {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Mul<i32> for Fixed {
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
fn mul(self, rhs: i32) -> Self::Output {
|
||||||
|
Self(self.0 * rhs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Div<i32> for Fixed {
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
fn div(self, rhs: i32) -> Self::Output {
|
||||||
|
Self(self.0 / rhs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl AddAssign for Fixed {
|
impl AddAssign for Fixed {
|
||||||
fn add_assign(&mut self, rhs: Self) {
|
fn add_assign(&mut self, rhs: Self) {
|
||||||
self.0 += rhs.0;
|
self.0 += rhs.0;
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ pub mod jay_select_workspace;
|
||||||
pub mod jay_toplevel;
|
pub mod jay_toplevel;
|
||||||
pub mod jay_workspace;
|
pub mod jay_workspace;
|
||||||
pub mod jay_workspace_watcher;
|
pub mod jay_workspace_watcher;
|
||||||
|
pub mod jay_xwayland;
|
||||||
pub mod org_kde_kwin_server_decoration;
|
pub mod org_kde_kwin_server_decoration;
|
||||||
pub mod org_kde_kwin_server_decoration_manager;
|
pub mod org_kde_kwin_server_decoration_manager;
|
||||||
pub mod wl_buffer;
|
pub mod wl_buffer;
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ use {
|
||||||
jay_select_toplevel::{JaySelectToplevel, JayToplevelSelector},
|
jay_select_toplevel::{JaySelectToplevel, JayToplevelSelector},
|
||||||
jay_select_workspace::{JaySelectWorkspace, JayWorkspaceSelector},
|
jay_select_workspace::{JaySelectWorkspace, JayWorkspaceSelector},
|
||||||
jay_workspace_watcher::JayWorkspaceWatcher,
|
jay_workspace_watcher::JayWorkspaceWatcher,
|
||||||
|
jay_xwayland::JayXwayland,
|
||||||
},
|
},
|
||||||
leaks::Tracker,
|
leaks::Tracker,
|
||||||
object::{Object, Version},
|
object::{Object, Version},
|
||||||
|
|
@ -70,7 +71,7 @@ impl Global for JayCompositorGlobal {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn version(&self) -> u32 {
|
fn version(&self) -> u32 {
|
||||||
10
|
11
|
||||||
}
|
}
|
||||||
|
|
||||||
fn required_caps(&self) -> ClientCaps {
|
fn required_caps(&self) -> ClientCaps {
|
||||||
|
|
@ -409,6 +410,18 @@ impl JayCompositorRequestHandler for JayCompositor {
|
||||||
self.client.add_client_obj(&obj)?;
|
self.client.add_client_obj(&obj)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_xwayland(&self, req: GetXwayland, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||||
|
let obj = Rc::new(JayXwayland {
|
||||||
|
id: req.id,
|
||||||
|
client: self.client.clone(),
|
||||||
|
tracker: Default::default(),
|
||||||
|
version: self.version,
|
||||||
|
});
|
||||||
|
track!(self.client, obj);
|
||||||
|
self.client.add_client_obj(&obj)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
object_base! {
|
object_base! {
|
||||||
|
|
|
||||||
84
src/ifs/jay_xwayland.rs
Normal file
84
src/ifs/jay_xwayland.rs
Normal file
|
|
@ -0,0 +1,84 @@
|
||||||
|
use {
|
||||||
|
crate::{
|
||||||
|
client::{Client, ClientError},
|
||||||
|
leaks::Tracker,
|
||||||
|
object::{Object, Version},
|
||||||
|
wire::{jay_xwayland::*, JayXwaylandId},
|
||||||
|
},
|
||||||
|
jay_config::xwayland::XScalingMode,
|
||||||
|
std::rc::Rc,
|
||||||
|
thiserror::Error,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct JayXwayland {
|
||||||
|
pub id: JayXwaylandId,
|
||||||
|
pub client: Rc<Client>,
|
||||||
|
pub tracker: Tracker<Self>,
|
||||||
|
pub version: Version,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl JayXwayland {
|
||||||
|
pub fn send_scaling_mode(&self) {
|
||||||
|
let xw = &self.client.state.xwayland;
|
||||||
|
self.client.event(ScalingMode {
|
||||||
|
self_id: self.id,
|
||||||
|
mode: match xw.use_wire_scale.get() {
|
||||||
|
false => XScalingMode::DEFAULT.0,
|
||||||
|
true => XScalingMode::DOWNSCALED.0,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn send_implied_scale(&self) {
|
||||||
|
let xw = &self.client.state.xwayland;
|
||||||
|
if let Some(scale) = xw.wire_scale.get() {
|
||||||
|
self.client.event(ImpliedScale {
|
||||||
|
self_id: self.id,
|
||||||
|
scale,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl JayXwaylandRequestHandler for JayXwayland {
|
||||||
|
type Error = JayXwaylandError;
|
||||||
|
|
||||||
|
fn get_scaling(&self, _req: GetScaling, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||||
|
self.send_scaling_mode();
|
||||||
|
self.send_implied_scale();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_scaling_mode(&self, req: SetScalingMode, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||||
|
let use_wire_scale = match XScalingMode(req.mode) {
|
||||||
|
XScalingMode::DEFAULT => false,
|
||||||
|
XScalingMode::DOWNSCALED => true,
|
||||||
|
_ => return Err(JayXwaylandError::UnknownMode(req.mode)),
|
||||||
|
};
|
||||||
|
self.client
|
||||||
|
.state
|
||||||
|
.xwayland
|
||||||
|
.use_wire_scale
|
||||||
|
.set(use_wire_scale);
|
||||||
|
self.client.state.update_xwayland_wire_scale();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object_base! {
|
||||||
|
self = JayXwayland;
|
||||||
|
version = self.version;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Object for JayXwayland {}
|
||||||
|
|
||||||
|
simple_add_obj!(JayXwayland);
|
||||||
|
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
pub enum JayXwaylandError {
|
||||||
|
#[error(transparent)]
|
||||||
|
ClientError(Box<ClientError>),
|
||||||
|
#[error("Unknown scaling mode {}", .0)]
|
||||||
|
UnknownMode(u32),
|
||||||
|
}
|
||||||
|
efrom!(JayXwaylandError, ClientError);
|
||||||
|
|
@ -12,7 +12,10 @@ use {
|
||||||
rect::Rect,
|
rect::Rect,
|
||||||
state::{ConnectorData, State},
|
state::{ConnectorData, State},
|
||||||
tree::{calculate_logical_size, OutputNode, TearingMode, VrrMode},
|
tree::{calculate_logical_size, OutputNode, TearingMode, VrrMode},
|
||||||
utils::{clonecell::CloneCell, copyhashmap::CopyHashMap, transform_ext::TransformExt},
|
utils::{
|
||||||
|
cell_ext::CellExt, clonecell::CloneCell, copyhashmap::CopyHashMap,
|
||||||
|
transform_ext::TransformExt,
|
||||||
|
},
|
||||||
wire::{wl_output::*, WlOutputId, ZxdgOutputV1Id},
|
wire::{wl_output::*, WlOutputId, ZxdgOutputV1Id},
|
||||||
},
|
},
|
||||||
ahash::AHashMap,
|
ahash::AHashMap,
|
||||||
|
|
@ -196,15 +199,7 @@ impl WlOutputGlobal {
|
||||||
let bindings = self.bindings.borrow_mut();
|
let bindings = self.bindings.borrow_mut();
|
||||||
for binding in bindings.values() {
|
for binding in bindings.values() {
|
||||||
for binding in binding.values() {
|
for binding in binding.values() {
|
||||||
binding.send_geometry();
|
binding.send_updates();
|
||||||
binding.send_mode();
|
|
||||||
binding.send_scale();
|
|
||||||
binding.send_done();
|
|
||||||
let xdg = binding.xdg_outputs.lock();
|
|
||||||
for xdg in xdg.values() {
|
|
||||||
xdg.send_updates();
|
|
||||||
}
|
|
||||||
// binding.client.flush();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -283,15 +278,33 @@ pub const SEND_SCALE_SINCE: Version = Version(2);
|
||||||
pub const SEND_NAME_SINCE: Version = Version(4);
|
pub const SEND_NAME_SINCE: Version = Version(4);
|
||||||
|
|
||||||
impl WlOutput {
|
impl WlOutput {
|
||||||
|
pub fn send_updates(&self) {
|
||||||
|
self.send_geometry();
|
||||||
|
self.send_mode();
|
||||||
|
if self.version >= SEND_SCALE_SINCE {
|
||||||
|
self.send_scale();
|
||||||
|
}
|
||||||
|
if self.version >= SEND_DONE_SINCE {
|
||||||
|
self.send_done();
|
||||||
|
}
|
||||||
|
let xdg = self.xdg_outputs.lock();
|
||||||
|
for xdg in xdg.values() {
|
||||||
|
xdg.send_updates();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn send_geometry(&self) {
|
fn send_geometry(&self) {
|
||||||
let Some(global) = self.global.get() else {
|
let Some(global) = self.global.get() else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let pos = global.pos.get();
|
let pos = global.pos.get();
|
||||||
|
let mut x = pos.x1();
|
||||||
|
let mut y = pos.y1();
|
||||||
|
logical_to_client_wire_scale!(self.client, x, y);
|
||||||
let event = Geometry {
|
let event = Geometry {
|
||||||
self_id: self.id,
|
self_id: self.id,
|
||||||
x: pos.x1(),
|
x,
|
||||||
y: pos.y1(),
|
y,
|
||||||
physical_width: global.width_mm,
|
physical_width: global.width_mm,
|
||||||
physical_height: global.height_mm,
|
physical_height: global.height_mm,
|
||||||
subpixel: SP_UNKNOWN,
|
subpixel: SP_UNKNOWN,
|
||||||
|
|
@ -306,7 +319,8 @@ impl WlOutput {
|
||||||
let Some(global) = self.global.get() else {
|
let Some(global) = self.global.get() else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let mode = global.mode.get();
|
let mut mode = global.mode.get();
|
||||||
|
logical_to_client_wire_scale!(self.client, mode.width, mode.height);
|
||||||
let event = Mode {
|
let event = Mode {
|
||||||
self_id: self.id,
|
self_id: self.id,
|
||||||
flags: MODE_CURRENT,
|
flags: MODE_CURRENT,
|
||||||
|
|
@ -317,13 +331,17 @@ impl WlOutput {
|
||||||
self.client.event(event);
|
self.client.event(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn send_scale(self: &Rc<Self>) {
|
fn send_scale(&self) {
|
||||||
let Some(global) = self.global.get() else {
|
let Some(global) = self.global.get() else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
let factor = match self.client.wire_scale.is_some() {
|
||||||
|
true => 1,
|
||||||
|
false => global.legacy_scale.get() as _,
|
||||||
|
};
|
||||||
let event = Scale {
|
let event = Scale {
|
||||||
self_id: self.id,
|
self_id: self.id,
|
||||||
factor: global.legacy_scale.get() as _,
|
factor,
|
||||||
};
|
};
|
||||||
self.client.event(event);
|
self.client.event(event);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -124,7 +124,8 @@ impl ZwpTabletToolV2 {
|
||||||
self.client.event(Up { self_id: self.id });
|
self.client.event(Up { self_id: self.id });
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn send_motion(&self, x: Fixed, y: Fixed) {
|
pub fn send_motion(&self, mut x: Fixed, mut y: Fixed) {
|
||||||
|
logical_to_client_wire_scale!(self.client, x, y);
|
||||||
self.client.event(Motion {
|
self.client.event(Motion {
|
||||||
self_id: self.id,
|
self_id: self.id,
|
||||||
x,
|
x,
|
||||||
|
|
@ -199,7 +200,7 @@ impl ZwpTabletToolV2 {
|
||||||
impl ZwpTabletToolV2RequestHandler for ZwpTabletToolV2 {
|
impl ZwpTabletToolV2RequestHandler for ZwpTabletToolV2 {
|
||||||
type Error = ZwpTabletToolV2Error;
|
type Error = ZwpTabletToolV2Error;
|
||||||
|
|
||||||
fn set_cursor(&self, req: SetCursor, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
fn set_cursor(&self, mut req: SetCursor, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||||
let Some(tool) = self.tool.get() else {
|
let Some(tool) = self.tool.get() else {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
};
|
};
|
||||||
|
|
@ -209,6 +210,7 @@ impl ZwpTabletToolV2RequestHandler for ZwpTabletToolV2 {
|
||||||
}
|
}
|
||||||
let mut cursor_opt = None;
|
let mut cursor_opt = None;
|
||||||
if req.surface.is_some() {
|
if req.surface.is_some() {
|
||||||
|
client_wire_scale_to_logical!(self.client, req.hotspot_x, req.hotspot_y);
|
||||||
let surface = self.seat.client.lookup(req.surface)?;
|
let surface = self.seat.client.lookup(req.surface)?;
|
||||||
let cursor = surface.get_cursor(&tool.cursor)?;
|
let cursor = surface.get_cursor(&tool.cursor)?;
|
||||||
cursor.set_hotspot(req.hotspot_x, req.hotspot_y);
|
cursor.set_hotspot(req.hotspot_x, req.hotspot_y);
|
||||||
|
|
|
||||||
|
|
@ -86,8 +86,9 @@ impl WlPointer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn send_enter(&self, serial: u32, surface: WlSurfaceId, x: Fixed, y: Fixed) {
|
pub fn send_enter(&self, serial: u32, surface: WlSurfaceId, mut x: Fixed, mut y: Fixed) {
|
||||||
self.last_motion.set((x, y));
|
self.last_motion.set((x, y));
|
||||||
|
logical_to_client_wire_scale!(self.seat.client, x, y);
|
||||||
self.seat.client.event(Enter {
|
self.seat.client.event(Enter {
|
||||||
self_id: self.id,
|
self_id: self.id,
|
||||||
serial,
|
serial,
|
||||||
|
|
@ -105,10 +106,11 @@ impl WlPointer {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn send_motion(&self, time: u32, x: Fixed, y: Fixed) {
|
pub fn send_motion(&self, time: u32, mut x: Fixed, mut y: Fixed) {
|
||||||
if self.last_motion.replace((x, y)) == (x, y) {
|
if self.last_motion.replace((x, y)) == (x, y) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
logical_to_client_wire_scale!(self.seat.client, x, y);
|
||||||
self.seat.client.event(Motion {
|
self.seat.client.event(Motion {
|
||||||
self_id: self.id,
|
self_id: self.id,
|
||||||
time,
|
time,
|
||||||
|
|
@ -135,7 +137,8 @@ impl WlPointer {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn send_axis(&self, time: u32, axis: u32, value: Fixed) {
|
pub fn send_axis(&self, time: u32, axis: u32, mut value: Fixed) {
|
||||||
|
logical_to_client_wire_scale!(self.seat.client, value);
|
||||||
self.seat.client.event(Axis {
|
self.seat.client.event(Axis {
|
||||||
self_id: self.id,
|
self_id: self.id,
|
||||||
time,
|
time,
|
||||||
|
|
@ -183,13 +186,14 @@ impl WlPointer {
|
||||||
impl WlPointerRequestHandler for WlPointer {
|
impl WlPointerRequestHandler for WlPointer {
|
||||||
type Error = WlPointerError;
|
type Error = WlPointerError;
|
||||||
|
|
||||||
fn set_cursor(&self, req: SetCursor, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
fn set_cursor(&self, mut req: SetCursor, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||||
if !self.seat.client.valid_serial(req.serial) {
|
if !self.seat.client.valid_serial(req.serial) {
|
||||||
log::warn!("Client tried to set_cursor with an invalid serial");
|
log::warn!("Client tried to set_cursor with an invalid serial");
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
let mut cursor_opt = None;
|
let mut cursor_opt = None;
|
||||||
if req.surface.is_some() {
|
if req.surface.is_some() {
|
||||||
|
client_wire_scale_to_logical!(self.seat.client, req.hotspot_x, req.hotspot_y);
|
||||||
let surface = self.seat.client.lookup(req.surface)?;
|
let surface = self.seat.client.lookup(req.surface)?;
|
||||||
let cursor = surface.get_cursor(&self.seat.global.pointer_cursor)?;
|
let cursor = surface.get_cursor(&self.seat.global.pointer_cursor)?;
|
||||||
cursor.set_hotspot(req.hotspot_x, req.hotspot_y);
|
cursor.set_hotspot(req.hotspot_x, req.hotspot_y);
|
||||||
|
|
|
||||||
|
|
@ -37,9 +37,10 @@ impl WlTouch {
|
||||||
time: u32,
|
time: u32,
|
||||||
surface: WlSurfaceId,
|
surface: WlSurfaceId,
|
||||||
id: i32,
|
id: i32,
|
||||||
x: Fixed,
|
mut x: Fixed,
|
||||||
y: Fixed,
|
mut y: Fixed,
|
||||||
) {
|
) {
|
||||||
|
logical_to_client_wire_scale!(self.seat.client, x, y);
|
||||||
self.seat.client.event(Down {
|
self.seat.client.event(Down {
|
||||||
self_id: self.id,
|
self_id: self.id,
|
||||||
serial,
|
serial,
|
||||||
|
|
@ -60,7 +61,8 @@ impl WlTouch {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn send_motion(&self, time: u32, id: i32, x: Fixed, y: Fixed) {
|
pub fn send_motion(&self, time: u32, id: i32, mut x: Fixed, mut y: Fixed) {
|
||||||
|
logical_to_client_wire_scale!(self.seat.client, x, y);
|
||||||
self.seat.client.event(Motion {
|
self.seat.client.event(Motion {
|
||||||
self_id: self.id,
|
self_id: self.id,
|
||||||
time,
|
time,
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ use {
|
||||||
},
|
},
|
||||||
leaks::Tracker,
|
leaks::Tracker,
|
||||||
object::{Object, Version},
|
object::{Object, Version},
|
||||||
rect::Region,
|
rect::{Rect, Region},
|
||||||
utils::clonecell::CloneCell,
|
utils::clonecell::CloneCell,
|
||||||
wire::{
|
wire::{
|
||||||
zwp_pointer_constraints_v1::*, WlPointerId, WlRegionId, WlSurfaceId,
|
zwp_pointer_constraints_v1::*, WlPointerId, WlRegionId, WlSurfaceId,
|
||||||
|
|
@ -125,11 +125,7 @@ impl SeatConstraint {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_region(&self, region: WlRegionId) -> Result<(), ZwpPointerConstraintsV1Error> {
|
fn set_region(&self, region: WlRegionId) -> Result<(), ZwpPointerConstraintsV1Error> {
|
||||||
let region = if region.is_some() {
|
let region = get_region(&self.client, region)?;
|
||||||
Some(self.client.lookup(region)?.region())
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
self.region.set(region);
|
self.region.set(region);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
@ -166,6 +162,35 @@ impl ZwpPointerConstraintsV1Global {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_region(
|
||||||
|
client: &Client,
|
||||||
|
region: WlRegionId,
|
||||||
|
) -> Result<Option<Rc<Region>>, ZwpPointerConstraintsV1Error> {
|
||||||
|
let region = if region.is_some() {
|
||||||
|
let mut region = client.lookup(region)?.region();
|
||||||
|
if let Some(scale) = client.wire_scale.get() {
|
||||||
|
let rects: Vec<_> = region
|
||||||
|
.rects()
|
||||||
|
.iter()
|
||||||
|
.map(|r| {
|
||||||
|
Rect::new_sized(
|
||||||
|
r.x1() / scale,
|
||||||
|
r.y1() / scale,
|
||||||
|
r.width() / scale,
|
||||||
|
r.height() / scale,
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
region = Region::from_rects(&rects);
|
||||||
|
}
|
||||||
|
Some(region)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
Ok(region)
|
||||||
|
}
|
||||||
|
|
||||||
impl ZwpPointerConstraintsV1 {
|
impl ZwpPointerConstraintsV1 {
|
||||||
fn create_constraint(
|
fn create_constraint(
|
||||||
&self,
|
&self,
|
||||||
|
|
@ -181,11 +206,7 @@ impl ZwpPointerConstraintsV1 {
|
||||||
if surface.constraints.contains(&seat.id) {
|
if surface.constraints.contains(&seat.id) {
|
||||||
return Err(ZwpPointerConstraintsV1Error::AlreadyConstrained);
|
return Err(ZwpPointerConstraintsV1Error::AlreadyConstrained);
|
||||||
}
|
}
|
||||||
let region = if region.is_some() {
|
let region = get_region(&self.client, region)?;
|
||||||
Some(self.client.lookup(region)?.region())
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
let one_shot = match lifetime {
|
let one_shot = match lifetime {
|
||||||
LT_ONESHOT => true,
|
LT_ONESHOT => true,
|
||||||
LT_PERSISTENT => false,
|
LT_PERSISTENT => false,
|
||||||
|
|
|
||||||
|
|
@ -23,11 +23,12 @@ impl ZwpRelativePointerV1 {
|
||||||
pub fn send_relative_motion(
|
pub fn send_relative_motion(
|
||||||
&self,
|
&self,
|
||||||
time_usec: u64,
|
time_usec: u64,
|
||||||
dx: Fixed,
|
mut dx: Fixed,
|
||||||
dy: Fixed,
|
mut dy: Fixed,
|
||||||
dx_unaccelerated: Fixed,
|
dx_unaccelerated: Fixed,
|
||||||
dy_unaccelerated: Fixed,
|
dy_unaccelerated: Fixed,
|
||||||
) {
|
) {
|
||||||
|
logical_to_client_wire_scale!(self.client, dx, dy);
|
||||||
self.client.event(RelativeMotion {
|
self.client.event(RelativeMotion {
|
||||||
self_id: self.id,
|
self_id: self.id,
|
||||||
utime_hi: (time_usec >> 32) as u32,
|
utime_hi: (time_usec >> 32) as u32,
|
||||||
|
|
|
||||||
|
|
@ -769,9 +769,13 @@ impl WlSurface {
|
||||||
|
|
||||||
pub fn send_preferred_buffer_scale(&self) {
|
pub fn send_preferred_buffer_scale(&self) {
|
||||||
if self.version >= BUFFER_SCALE_SINCE {
|
if self.version >= BUFFER_SCALE_SINCE {
|
||||||
|
let factor = match self.client.wire_scale.is_some() {
|
||||||
|
true => 1,
|
||||||
|
false => self.output.get().global.legacy_scale.get() as _,
|
||||||
|
};
|
||||||
self.client.event(PreferredBufferScale {
|
self.client.event(PreferredBufferScale {
|
||||||
self_id: self.id,
|
self_id: self.id,
|
||||||
factor: self.output.get().global.legacy_scale.get() as _,
|
factor,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -909,6 +913,22 @@ impl WlSurface {
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn handle_xwayland_wire_scale_change(&self) {
|
||||||
|
self.send_preferred_buffer_scale();
|
||||||
|
if let Some(fs) = self.fractional_scale.get() {
|
||||||
|
fs.send_preferred_scale();
|
||||||
|
}
|
||||||
|
if let Some(xsurface) = self.ext.get().into_xsurface() {
|
||||||
|
if let Some(window) = xsurface.xwindow.get() {
|
||||||
|
self.client
|
||||||
|
.state
|
||||||
|
.xwayland
|
||||||
|
.queue
|
||||||
|
.push(XWaylandEvent::Configure(window));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const MAX_DAMAGE: usize = 32;
|
const MAX_DAMAGE: usize = 32;
|
||||||
|
|
@ -1093,7 +1113,7 @@ impl WlSurface {
|
||||||
scale_changed || buffer_transform_changed || viewport_changed || alpha_changed;
|
scale_changed || buffer_transform_changed || viewport_changed || alpha_changed;
|
||||||
let mut buffer_changed = false;
|
let mut buffer_changed = false;
|
||||||
let mut old_raw_size = None;
|
let mut old_raw_size = None;
|
||||||
let (dx, dy) = mem::take(&mut pending.offset);
|
let (mut dx, mut dy) = mem::take(&mut pending.offset);
|
||||||
if let Some(buffer_change) = pending.buffer.take() {
|
if let Some(buffer_change) = pending.buffer.take() {
|
||||||
buffer_changed = true;
|
buffer_changed = true;
|
||||||
if let Some(buffer) = self.buffer.take() {
|
if let Some(buffer) = self.buffer.take() {
|
||||||
|
|
@ -1132,6 +1152,8 @@ impl WlSurface {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if self.buffer.is_some() && (dx, dy) != (0, 0) {
|
if self.buffer.is_some() && (dx, dy) != (0, 0) {
|
||||||
|
// This is somewhat problematic since we don't accumulate small changes.
|
||||||
|
client_wire_scale_to_logical!(self.client, dx, dy);
|
||||||
self.buf_x.fetch_add(dx);
|
self.buf_x.fetch_add(dx);
|
||||||
self.buf_y.fetch_add(dy);
|
self.buf_y.fetch_add(dy);
|
||||||
self.need_extents_update.set(true);
|
self.need_extents_update.set(true);
|
||||||
|
|
@ -1210,7 +1232,8 @@ impl WlSurface {
|
||||||
buffer_transform: self.buffer_transform.get(),
|
buffer_transform: self.buffer_transform.get(),
|
||||||
};
|
};
|
||||||
let (buffer_width, buffer_height) = buffer.buffer.rect.size();
|
let (buffer_width, buffer_height) = buffer.buffer.rect.size();
|
||||||
let (dst_width, dst_height) = new_size.unwrap_or_default();
|
let (mut dst_width, mut dst_height) = new_size.unwrap_or_default();
|
||||||
|
client_wire_scale_to_logical!(self.client, dst_width, dst_height);
|
||||||
let damage_matrix = DamageMatrix::new(
|
let damage_matrix = DamageMatrix::new(
|
||||||
self.buffer_transform.get(),
|
self.buffer_transform.get(),
|
||||||
self.buffer_scale.get(),
|
self.buffer_scale.get(),
|
||||||
|
|
@ -1223,7 +1246,8 @@ impl WlSurface {
|
||||||
self.damage_matrix.set(damage_matrix);
|
self.damage_matrix.set(damage_matrix);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let (width, height) = new_size.unwrap_or_default();
|
let (mut width, mut height) = new_size.unwrap_or_default();
|
||||||
|
client_wire_scale_to_logical!(self.client, width, height);
|
||||||
let (old_width, old_height) = buffer_abs_pos.size();
|
let (old_width, old_height) = buffer_abs_pos.size();
|
||||||
if (width, height) != (old_width, old_height) {
|
if (width, height) != (old_width, old_height) {
|
||||||
self.need_extents_update.set(true);
|
self.need_extents_update.set(true);
|
||||||
|
|
@ -1360,6 +1384,13 @@ impl WlSurface {
|
||||||
}
|
}
|
||||||
for damage in &pending.surface_damage {
|
for damage in &pending.surface_damage {
|
||||||
let mut damage = damage.move_(pos.x1(), pos.y1());
|
let mut damage = damage.move_(pos.x1(), pos.y1());
|
||||||
|
if let Some(scale) = self.client.wire_scale.get() {
|
||||||
|
let x1 = damage.x1() / scale;
|
||||||
|
let y1 = damage.y1() / scale;
|
||||||
|
let x2 = (damage.x2() + scale - 1) / scale;
|
||||||
|
let y2 = (damage.y2() + scale - 1) / scale;
|
||||||
|
damage = Rect::new(x1, y1, x2, y2).unwrap();
|
||||||
|
}
|
||||||
damage = damage.intersect(bounds.unwrap_or(pos));
|
damage = damage.intersect(bounds.unwrap_or(pos));
|
||||||
self.client.state.damage(damage);
|
self.client.state.damage(damage);
|
||||||
}
|
}
|
||||||
|
|
@ -1406,12 +1437,13 @@ impl WlSurface {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn accepts_input_at(&self, x: i32, y: i32) -> bool {
|
fn accepts_input_at(&self, mut x: i32, mut y: i32) -> bool {
|
||||||
let rect = self.buffer_abs_pos.get().at_point(0, 0);
|
let rect = self.buffer_abs_pos.get().at_point(0, 0);
|
||||||
if !rect.contains(x, y) {
|
if !rect.contains(x, y) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if let Some(ir) = self.input_region.get() {
|
if let Some(ir) = self.input_region.get() {
|
||||||
|
logical_to_client_wire_scale!(self.client, x, y);
|
||||||
if !ir.contains(x, y) {
|
if !ir.contains(x, y) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -135,7 +135,8 @@ impl WlSubsurface {
|
||||||
v.pending.set(false);
|
v.pending.set(false);
|
||||||
self.node.borrow_mut().replace(v);
|
self.node.borrow_mut().replace(v);
|
||||||
}
|
}
|
||||||
if let Some((x, y)) = pending.position.take() {
|
if let Some((mut x, mut y)) = pending.position.take() {
|
||||||
|
client_wire_scale_to_logical!(self.surface.client, x, y);
|
||||||
self.position
|
self.position
|
||||||
.set(self.surface.buffer_abs_pos.get().at_point(x, y));
|
.set(self.surface.buffer_abs_pos.get().at_point(x, y));
|
||||||
let (parent_x, parent_y) = self.parent.buffer_abs_pos.get().position();
|
let (parent_x, parent_y) = self.parent.buffer_abs_pos.get().position();
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,8 @@ use {
|
||||||
ifs::wl_surface::WlSurface,
|
ifs::wl_surface::WlSurface,
|
||||||
leaks::Tracker,
|
leaks::Tracker,
|
||||||
object::{Object, Version},
|
object::{Object, Version},
|
||||||
|
scale::Scale,
|
||||||
|
utils::cell_ext::CellExt,
|
||||||
wire::{wp_fractional_scale_v1::*, WpFractionalScaleV1Id},
|
wire::{wp_fractional_scale_v1::*, WpFractionalScaleV1Id},
|
||||||
},
|
},
|
||||||
std::rc::Rc,
|
std::rc::Rc,
|
||||||
|
|
@ -38,17 +40,13 @@ impl WpFractionalScaleV1 {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn send_preferred_scale(&self) {
|
pub fn send_preferred_scale(&self) {
|
||||||
|
let scale = match self.client.wire_scale.is_some() {
|
||||||
|
true => Scale::from_int(1),
|
||||||
|
false => self.surface.output.get().global.persistent.scale.get(),
|
||||||
|
};
|
||||||
self.client.event(PreferredScale {
|
self.client.event(PreferredScale {
|
||||||
self_id: self.id,
|
self_id: self.id,
|
||||||
scale: self
|
scale: scale.to_wl(),
|
||||||
.surface
|
|
||||||
.output
|
|
||||||
.get()
|
|
||||||
.global
|
|
||||||
.persistent
|
|
||||||
.scale
|
|
||||||
.get()
|
|
||||||
.to_wl(),
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -140,13 +140,12 @@ pub struct Xwindow {
|
||||||
|
|
||||||
impl XwindowData {
|
impl XwindowData {
|
||||||
pub fn new(state: &Rc<State>, event: &CreateNotify, client: &Rc<Client>) -> Self {
|
pub fn new(state: &Rc<State>, event: &CreateNotify, client: &Rc<Client>) -> Self {
|
||||||
let extents = Rect::new_sized(
|
let mut x = event.x as i32;
|
||||||
event.x as _,
|
let mut y = event.y as i32;
|
||||||
event.y as _,
|
let mut width = event.width as i32;
|
||||||
event.width as _,
|
let mut height = event.height as i32;
|
||||||
event.height as _,
|
client_wire_scale_to_logical!(client, x, y, width, height);
|
||||||
)
|
let extents = Rect::new_sized(x, y, width, height).unwrap();
|
||||||
.unwrap();
|
|
||||||
// log::info!("xwin {} new {:?} or {}", event.window, extents, event.override_redirect);
|
// log::info!("xwin {} new {:?} or {}", event.window, extents, event.override_redirect);
|
||||||
Self {
|
Self {
|
||||||
state: state.clone(),
|
state: state.clone(),
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,8 @@ pub struct ZxdgOutputV1 {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ZxdgOutputV1 {
|
impl ZxdgOutputV1 {
|
||||||
pub fn send_logical_position(&self, x: i32, y: i32) {
|
pub fn send_logical_position(&self, mut x: i32, mut y: i32) {
|
||||||
|
logical_to_client_wire_scale!(self.client, x, y);
|
||||||
self.client.event(LogicalPosition {
|
self.client.event(LogicalPosition {
|
||||||
self_id: self.id,
|
self_id: self.id,
|
||||||
x,
|
x,
|
||||||
|
|
@ -32,7 +33,8 @@ impl ZxdgOutputV1 {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn send_logical_size(&self, width: i32, height: i32) {
|
pub fn send_logical_size(&self, mut width: i32, mut height: i32) {
|
||||||
|
logical_to_client_wire_scale!(self.client, width, height);
|
||||||
self.client.event(LogicalSize {
|
self.client.event(LogicalSize {
|
||||||
self_id: self.id,
|
self_id: self.id,
|
||||||
width,
|
width,
|
||||||
|
|
|
||||||
|
|
@ -737,3 +737,23 @@ macro_rules! ei_object_base {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
macro_rules! logical_to_client_wire_scale {
|
||||||
|
($client:expr, $($field:expr),+ $(,)?) => {
|
||||||
|
if let Some(scale) = $client.wire_scale.get() {
|
||||||
|
$(
|
||||||
|
$field = $field * scale;
|
||||||
|
)+
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! client_wire_scale_to_logical {
|
||||||
|
($client:expr, $($field:expr),+ $(,)?) => {
|
||||||
|
if let Some(scale) = $client.wire_scale.get() {
|
||||||
|
$(
|
||||||
|
$field = $field / scale;
|
||||||
|
)+
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
|
||||||
33
src/state.rs
33
src/state.rs
|
|
@ -244,6 +244,8 @@ pub struct XWaylandState {
|
||||||
pub handler: RefCell<Option<SpawnedFuture<()>>>,
|
pub handler: RefCell<Option<SpawnedFuture<()>>>,
|
||||||
pub queue: Rc<AsyncQueue<XWaylandEvent>>,
|
pub queue: Rc<AsyncQueue<XWaylandEvent>>,
|
||||||
pub ipc_device_ids: XIpcDeviceIds,
|
pub ipc_device_ids: XIpcDeviceIds,
|
||||||
|
pub use_wire_scale: Cell<bool>,
|
||||||
|
pub wire_scale: Cell<Option<i32>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct IdleState {
|
pub struct IdleState {
|
||||||
|
|
@ -415,6 +417,7 @@ impl State {
|
||||||
fn output_scales_changed(&self) {
|
fn output_scales_changed(&self) {
|
||||||
UpdateTextTexturesVisitor.visit_display(&self.root);
|
UpdateTextTexturesVisitor.visit_display(&self.root);
|
||||||
self.reload_cursors();
|
self.reload_cursors();
|
||||||
|
self.update_xwayland_wire_scale();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cursor_sizes_changed(&self) {
|
fn cursor_sizes_changed(&self) {
|
||||||
|
|
@ -1215,6 +1218,36 @@ impl State {
|
||||||
let dy = y1 - y2;
|
let dy = y1 - y2;
|
||||||
dx * dx + dy * dy > self.ui_drag_threshold_squared.get()
|
dx * dx + dy * dy > self.ui_drag_threshold_squared.get()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn update_xwayland_wire_scale(&self) {
|
||||||
|
let scale = self
|
||||||
|
.scales
|
||||||
|
.lock()
|
||||||
|
.iter()
|
||||||
|
.map(|v| v.0.round_up())
|
||||||
|
.max()
|
||||||
|
.unwrap_or(1);
|
||||||
|
let wire_scale = match self.xwayland.use_wire_scale.get() {
|
||||||
|
true => Some(scale as i32),
|
||||||
|
false => None,
|
||||||
|
};
|
||||||
|
self.xwayland.wire_scale.set(wire_scale);
|
||||||
|
for client in self.clients.clients.borrow().values() {
|
||||||
|
let client = &client.data;
|
||||||
|
if !client.is_xwayland {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if client.wire_scale.replace(wire_scale) == wire_scale {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for output in client.objects.outputs.lock().values() {
|
||||||
|
output.send_updates();
|
||||||
|
}
|
||||||
|
for surface in client.objects.surfaces.lock().values() {
|
||||||
|
surface.handle_xwayland_wire_scale_change();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
|
|
|
||||||
|
|
@ -332,7 +332,7 @@ impl ToolClient {
|
||||||
self_id: s.registry,
|
self_id: s.registry,
|
||||||
name: s.jay_compositor.0,
|
name: s.jay_compositor.0,
|
||||||
interface: JayCompositor.name(),
|
interface: JayCompositor.name(),
|
||||||
version: s.jay_compositor.1.min(10),
|
version: s.jay_compositor.1.min(11),
|
||||||
id: id.into(),
|
id: id.into(),
|
||||||
});
|
});
|
||||||
self.jay_compositor.set(Some(id));
|
self.jay_compositor.set(Some(id));
|
||||||
|
|
|
||||||
|
|
@ -182,6 +182,7 @@ async fn run(
|
||||||
Ok(c) => c,
|
Ok(c) => c,
|
||||||
Err(e) => return Err(XWaylandError::SpawnClient(e)),
|
Err(e) => return Err(XWaylandError::SpawnClient(e)),
|
||||||
};
|
};
|
||||||
|
state.update_xwayland_wire_scale();
|
||||||
state.ring.readable(&Rc::new(dfdread)).await?;
|
state.ring.readable(&Rc::new(dfdread)).await?;
|
||||||
state.xwayland.queue.clear();
|
state.xwayland.queue.clear();
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -913,13 +913,18 @@ impl Wm {
|
||||||
async fn send_configure(&mut self, window: Rc<Xwindow>) {
|
async fn send_configure(&mut self, window: Rc<Xwindow>) {
|
||||||
let extents = window.data.info.extents.get();
|
let extents = window.data.info.extents.get();
|
||||||
// log::info!("xwin {} send_configure {:?}", window.data.window_id, extents);
|
// log::info!("xwin {} send_configure {:?}", window.data.window_id, extents);
|
||||||
|
let mut x = extents.x1();
|
||||||
|
let mut y = extents.y1();
|
||||||
|
let mut width = extents.width();
|
||||||
|
let mut height = extents.height();
|
||||||
|
logical_to_client_wire_scale!(self.client, x, y, width, height);
|
||||||
let cw = ConfigureWindow {
|
let cw = ConfigureWindow {
|
||||||
window: window.data.window_id,
|
window: window.data.window_id,
|
||||||
values: ConfigureWindowValues {
|
values: ConfigureWindowValues {
|
||||||
x: Some(extents.x1()),
|
x: Some(x),
|
||||||
y: Some(extents.y1()),
|
y: Some(y),
|
||||||
width: Some(extents.width() as u32),
|
width: Some(width as u32),
|
||||||
height: Some(extents.height() as u32),
|
height: Some(height as u32),
|
||||||
border_width: Some(0),
|
border_width: Some(0),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
|
|
@ -2134,13 +2139,18 @@ impl Wm {
|
||||||
if pending.width() > 0 && pending.height() > 0 {
|
if pending.width() > 0 && pending.height() > 0 {
|
||||||
let dummy = Rect::new_sized(0, 0, 1, 1).unwrap();
|
let dummy = Rect::new_sized(0, 0, 1, 1).unwrap();
|
||||||
for rect in [dummy, pending] {
|
for rect in [dummy, pending] {
|
||||||
|
let mut x = rect.x1();
|
||||||
|
let mut y = rect.y1();
|
||||||
|
let mut width = rect.width();
|
||||||
|
let mut height = rect.height();
|
||||||
|
logical_to_client_wire_scale!(self.client, x, y, width, height);
|
||||||
let cw = ConfigureWindow {
|
let cw = ConfigureWindow {
|
||||||
window: data.window_id,
|
window: data.window_id,
|
||||||
values: ConfigureWindowValues {
|
values: ConfigureWindowValues {
|
||||||
x: Some(rect.x1()),
|
x: Some(x),
|
||||||
y: Some(rect.y1()),
|
y: Some(y),
|
||||||
width: Some(rect.width() as _),
|
width: Some(width as _),
|
||||||
height: Some(rect.height() as _),
|
height: Some(height as _),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
@ -2213,13 +2223,12 @@ impl Wm {
|
||||||
};
|
};
|
||||||
self.update_override_redirect(data, event.override_redirect);
|
self.update_override_redirect(data, event.override_redirect);
|
||||||
if data.info.override_redirect.get() {
|
if data.info.override_redirect.get() {
|
||||||
let extents = Rect::new_sized(
|
let mut x = event.x as i32;
|
||||||
event.x as _,
|
let mut y = event.y as i32;
|
||||||
event.y as _,
|
let mut width = event.width as i32;
|
||||||
event.width as _,
|
let mut height = event.height as i32;
|
||||||
event.height as _,
|
client_wire_scale_to_logical!(self.client, x, y, width, height);
|
||||||
)
|
let extents = Rect::new_sized(x, y, width, height).unwrap();
|
||||||
.unwrap();
|
|
||||||
if let Some(window) = data.window.get() {
|
if let Some(window) = data.window.get() {
|
||||||
window.tl_change_extents(&extents);
|
window.tl_change_extents(&extents);
|
||||||
self.state.tree_changed();
|
self.state.tree_changed();
|
||||||
|
|
@ -2248,15 +2257,19 @@ impl Wm {
|
||||||
let mut height = de.height();
|
let mut height = de.height();
|
||||||
if event.value_mask.contains(CONFIG_WINDOW_X) {
|
if event.value_mask.contains(CONFIG_WINDOW_X) {
|
||||||
x1 = event.x as _;
|
x1 = event.x as _;
|
||||||
|
client_wire_scale_to_logical!(self.client, x1);
|
||||||
}
|
}
|
||||||
if event.value_mask.contains(CONFIG_WINDOW_Y) {
|
if event.value_mask.contains(CONFIG_WINDOW_Y) {
|
||||||
y1 = event.y as _;
|
y1 = event.y as _;
|
||||||
|
client_wire_scale_to_logical!(self.client, y1);
|
||||||
}
|
}
|
||||||
if event.value_mask.contains(CONFIG_WINDOW_WIDTH) {
|
if event.value_mask.contains(CONFIG_WINDOW_WIDTH) {
|
||||||
width = event.width as _;
|
width = event.width as _;
|
||||||
|
client_wire_scale_to_logical!(self.client, width);
|
||||||
}
|
}
|
||||||
if event.value_mask.contains(CONFIG_WINDOW_HEIGHT) {
|
if event.value_mask.contains(CONFIG_WINDOW_HEIGHT) {
|
||||||
height = event.height as _;
|
height = event.height as _;
|
||||||
|
client_wire_scale_to_logical!(self.client, height);
|
||||||
}
|
}
|
||||||
data.info
|
data.info
|
||||||
.pending_extents
|
.pending_extents
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ use {
|
||||||
status::MessageFormat,
|
status::MessageFormat,
|
||||||
theme::Color,
|
theme::Color,
|
||||||
video::{Format, GfxApi, TearingMode, Transform, VrrMode},
|
video::{Format, GfxApi, TearingMode, Transform, VrrMode},
|
||||||
|
xwayland::XScalingMode,
|
||||||
Axis, Direction, Workspace,
|
Axis, Direction, Workspace,
|
||||||
},
|
},
|
||||||
std::{
|
std::{
|
||||||
|
|
@ -302,6 +303,11 @@ pub struct Vrr {
|
||||||
pub cursor_hz: Option<f64>,
|
pub cursor_hz: Option<f64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Xwayland {
|
||||||
|
pub scaling_mode: Option<XScalingMode>,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Tearing {
|
pub struct Tearing {
|
||||||
pub mode: Option<TearingMode>,
|
pub mode: Option<TearingMode>,
|
||||||
|
|
@ -349,6 +355,7 @@ pub struct Config {
|
||||||
pub tearing: Option<Tearing>,
|
pub tearing: Option<Tearing>,
|
||||||
pub libei: Libei,
|
pub libei: Libei,
|
||||||
pub ui_drag: UiDrag,
|
pub ui_drag: UiDrag,
|
||||||
|
pub xwayland: Option<Xwayland>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,7 @@ mod tearing;
|
||||||
mod theme;
|
mod theme;
|
||||||
mod ui_drag;
|
mod ui_drag;
|
||||||
mod vrr;
|
mod vrr;
|
||||||
|
mod xwayland;
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
pub enum StringParserError {
|
pub enum StringParserError {
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,7 @@ use {
|
||||||
theme::ThemeParser,
|
theme::ThemeParser,
|
||||||
ui_drag::UiDragParser,
|
ui_drag::UiDragParser,
|
||||||
vrr::VrrParser,
|
vrr::VrrParser,
|
||||||
|
xwayland::XwaylandParser,
|
||||||
},
|
},
|
||||||
spanned::SpannedErrorExt,
|
spanned::SpannedErrorExt,
|
||||||
Action, Config, Libei, Theme, UiDrag,
|
Action, Config, Libei, Theme, UiDrag,
|
||||||
|
|
@ -114,6 +115,7 @@ impl Parser for ConfigParser<'_> {
|
||||||
tearing_val,
|
tearing_val,
|
||||||
libei_val,
|
libei_val,
|
||||||
ui_drag_val,
|
ui_drag_val,
|
||||||
|
xwayland_val,
|
||||||
),
|
),
|
||||||
) = ext.extract((
|
) = ext.extract((
|
||||||
(
|
(
|
||||||
|
|
@ -150,6 +152,7 @@ impl Parser for ConfigParser<'_> {
|
||||||
opt(val("tearing")),
|
opt(val("tearing")),
|
||||||
opt(val("libei")),
|
opt(val("libei")),
|
||||||
opt(val("ui-drag")),
|
opt(val("ui-drag")),
|
||||||
|
opt(val("xwayland")),
|
||||||
),
|
),
|
||||||
))?;
|
))?;
|
||||||
let mut keymap = None;
|
let mut keymap = None;
|
||||||
|
|
@ -350,6 +353,15 @@ impl Parser for ConfigParser<'_> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
let mut xwayland = None;
|
||||||
|
if let Some(value) = xwayland_val {
|
||||||
|
match value.parse(&mut XwaylandParser(self.0)) {
|
||||||
|
Ok(v) => xwayland = Some(v),
|
||||||
|
Err(e) => {
|
||||||
|
log::warn!("Could not parse Xwayland setting: {}", self.0.error(e));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Ok(Config {
|
Ok(Config {
|
||||||
keymap,
|
keymap,
|
||||||
repeat_rate,
|
repeat_rate,
|
||||||
|
|
@ -378,6 +390,7 @@ impl Parser for ConfigParser<'_> {
|
||||||
tearing,
|
tearing,
|
||||||
libei,
|
libei,
|
||||||
ui_drag,
|
ui_drag,
|
||||||
|
xwayland,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
75
toml-config/src/config/parsers/xwayland.rs
Normal file
75
toml-config/src/config/parsers/xwayland.rs
Normal file
|
|
@ -0,0 +1,75 @@
|
||||||
|
use {
|
||||||
|
crate::{
|
||||||
|
config::{
|
||||||
|
context::Context,
|
||||||
|
extractor::{opt, val, Extractor, ExtractorError},
|
||||||
|
parser::{DataType, ParseResult, Parser, UnexpectedDataType},
|
||||||
|
Xwayland,
|
||||||
|
},
|
||||||
|
toml::{
|
||||||
|
toml_span::{Span, Spanned, SpannedExt},
|
||||||
|
toml_value::Value,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
indexmap::IndexMap,
|
||||||
|
jay_config::xwayland::XScalingMode,
|
||||||
|
thiserror::Error,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
pub enum XwaylandParserError {
|
||||||
|
#[error(transparent)]
|
||||||
|
Expected(#[from] UnexpectedDataType),
|
||||||
|
#[error(transparent)]
|
||||||
|
Extract(#[from] ExtractorError),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct XwaylandParser<'a>(pub &'a Context<'a>);
|
||||||
|
|
||||||
|
impl Parser for XwaylandParser<'_> {
|
||||||
|
type Value = Xwayland;
|
||||||
|
type Error = XwaylandParserError;
|
||||||
|
const EXPECTED: &'static [DataType] = &[DataType::Table];
|
||||||
|
|
||||||
|
fn parse_table(
|
||||||
|
&mut self,
|
||||||
|
span: Span,
|
||||||
|
table: &IndexMap<Spanned<String>, Spanned<Value>>,
|
||||||
|
) -> ParseResult<Self> {
|
||||||
|
let mut ext = Extractor::new(self.0, span, table);
|
||||||
|
let scaling_mode = ext.extract(opt(val("scaling-mode")))?;
|
||||||
|
let scaling_mode = scaling_mode.and_then(|m| match m.parse(&mut XScalingModeParser) {
|
||||||
|
Ok(m) => Some(m),
|
||||||
|
Err(e) => {
|
||||||
|
log::error!("Could not parse scaling mode: {}", self.0.error(e));
|
||||||
|
None
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Ok(Xwayland { scaling_mode })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
pub enum XScalingModeParserError {
|
||||||
|
#[error(transparent)]
|
||||||
|
Expected(#[from] UnexpectedDataType),
|
||||||
|
#[error("Unknown mode {0}")]
|
||||||
|
UnknownMode(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
struct XScalingModeParser;
|
||||||
|
|
||||||
|
impl Parser for XScalingModeParser {
|
||||||
|
type Value = XScalingMode;
|
||||||
|
type Error = XScalingModeParserError;
|
||||||
|
const EXPECTED: &'static [DataType] = &[DataType::String];
|
||||||
|
|
||||||
|
fn parse_string(&mut self, span: Span, string: &str) -> ParseResult<Self> {
|
||||||
|
let mode = match string {
|
||||||
|
"default" => XScalingMode::DEFAULT,
|
||||||
|
"downscaled" => XScalingMode::DOWNSCALED,
|
||||||
|
_ => return Err(XScalingModeParserError::UnknownMode(string.to_string()).spanned(span)),
|
||||||
|
};
|
||||||
|
Ok(mode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -34,6 +34,7 @@ use {
|
||||||
set_direct_scanout_enabled, set_gfx_api, set_tearing_mode, set_vrr_cursor_hz,
|
set_direct_scanout_enabled, set_gfx_api, set_tearing_mode, set_vrr_cursor_hz,
|
||||||
set_vrr_mode, Connector, DrmDevice,
|
set_vrr_mode, Connector, DrmDevice,
|
||||||
},
|
},
|
||||||
|
xwayland::set_x_scaling_mode,
|
||||||
},
|
},
|
||||||
std::{cell::RefCell, io::ErrorKind, path::PathBuf, rc::Rc, time::Duration},
|
std::{cell::RefCell, io::ErrorKind, path::PathBuf, rc::Rc, time::Duration},
|
||||||
};
|
};
|
||||||
|
|
@ -1061,6 +1062,11 @@ fn load_config(initial_load: bool, persistent: &Rc<PersistentState>) {
|
||||||
if let Some(threshold) = config.ui_drag.threshold {
|
if let Some(threshold) = config.ui_drag.threshold {
|
||||||
set_ui_drag_threshold(threshold);
|
set_ui_drag_threshold(threshold);
|
||||||
}
|
}
|
||||||
|
if let Some(xwayland) = config.xwayland {
|
||||||
|
if let Some(mode) = xwayland.scaling_mode {
|
||||||
|
set_x_scaling_mode(mode);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_command(exec: &Exec) -> Command {
|
fn create_command(exec: &Exec) -> Command {
|
||||||
|
|
|
||||||
|
|
@ -593,6 +593,10 @@
|
||||||
"ui-drag": {
|
"ui-drag": {
|
||||||
"description": "Configures the ui-drag settings.\n\n- Example:\n\n ```toml\n ui-drag = { enabled = false, threshold = 20 }\n ```\n",
|
"description": "Configures the ui-drag settings.\n\n- Example:\n\n ```toml\n ui-drag = { enabled = false, threshold = 20 }\n ```\n",
|
||||||
"$ref": "#/$defs/UiDrag"
|
"$ref": "#/$defs/UiDrag"
|
||||||
|
},
|
||||||
|
"xwayland": {
|
||||||
|
"description": "Configures the Xwayland settings.\n\n- Example:\n\n ```toml\n xwayland = { scaling-mode = \"downscaled\" }\n ```\n",
|
||||||
|
"$ref": "#/$defs/Xwayland"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required": []
|
"required": []
|
||||||
|
|
@ -1399,6 +1403,25 @@
|
||||||
"variant2",
|
"variant2",
|
||||||
"variant3"
|
"variant3"
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
"XScalingMode": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The scaling mode of X windows.\n\n- Example:\n\n ```toml\n xwayland = { scaling-mode = \"downscaled\" }\n ```\n",
|
||||||
|
"enum": [
|
||||||
|
"default",
|
||||||
|
"downscaled"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Xwayland": {
|
||||||
|
"description": "Describes Xwayland settings.\n\n- Example:\n\n ```toml\n xwayland = { scaling-mode = \"downscaled\" }\n ```\n",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"scaling-mode": {
|
||||||
|
"description": "The scaling mode of X windows.",
|
||||||
|
"$ref": "#/$defs/XScalingMode"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1166,6 +1166,18 @@ The table has the following fields:
|
||||||
|
|
||||||
The value of this field should be a [UiDrag](#types-UiDrag).
|
The value of this field should be a [UiDrag](#types-UiDrag).
|
||||||
|
|
||||||
|
- `xwayland` (optional):
|
||||||
|
|
||||||
|
Configures the Xwayland settings.
|
||||||
|
|
||||||
|
- Example:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
xwayland = { scaling-mode = "downscaled" }
|
||||||
|
```
|
||||||
|
|
||||||
|
The value of this field should be a [Xwayland](#types-Xwayland).
|
||||||
|
|
||||||
|
|
||||||
<a name="types-Connector"></a>
|
<a name="types-Connector"></a>
|
||||||
### `Connector`
|
### `Connector`
|
||||||
|
|
@ -3122,3 +3134,64 @@ The string should have one of the following values:
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<a name="types-XScalingMode"></a>
|
||||||
|
### `XScalingMode`
|
||||||
|
|
||||||
|
The scaling mode of X windows.
|
||||||
|
|
||||||
|
- Example:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
xwayland = { scaling-mode = "downscaled" }
|
||||||
|
```
|
||||||
|
|
||||||
|
Values of this type should be strings.
|
||||||
|
|
||||||
|
The string should have one of the following values:
|
||||||
|
|
||||||
|
- `default`:
|
||||||
|
|
||||||
|
The default mode.
|
||||||
|
|
||||||
|
Currently this means that windows are rendered at the lowest scale and then upscaled
|
||||||
|
if necessary.
|
||||||
|
|
||||||
|
- `downscaled`:
|
||||||
|
|
||||||
|
Windows are rendered at the highest integer scale and then downscaled.
|
||||||
|
|
||||||
|
This has significant performance implications unless the window is running on the
|
||||||
|
output with the highest scale and that scale is an integer scale.
|
||||||
|
|
||||||
|
For example, on a 3840x2160 output with a 1.5 scale, a fullscreen window will be
|
||||||
|
rendered at 3840x2160 * 2 / 1.5 = 5120x2880 pixels and then downscaled to
|
||||||
|
3840x2160. This overhead gets worse the lower the scale of the output is.
|
||||||
|
|
||||||
|
Additionally, this mode requires the X window to scale its contents itself. In the
|
||||||
|
example above, you might achieve this by setting the environment variable
|
||||||
|
`GDK_SCALE=2`.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<a name="types-Xwayland"></a>
|
||||||
|
### `Xwayland`
|
||||||
|
|
||||||
|
Describes Xwayland settings.
|
||||||
|
|
||||||
|
- Example:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
xwayland = { scaling-mode = "downscaled" }
|
||||||
|
```
|
||||||
|
|
||||||
|
Values of this type should be tables.
|
||||||
|
|
||||||
|
The table has the following fields:
|
||||||
|
|
||||||
|
- `scaling-mode` (optional):
|
||||||
|
|
||||||
|
The scaling mode of X windows.
|
||||||
|
|
||||||
|
The value of this field should be a [XScalingMode](#types-XScalingMode).
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2275,6 +2275,17 @@ Config:
|
||||||
```toml
|
```toml
|
||||||
ui-drag = { enabled = false, threshold = 20 }
|
ui-drag = { enabled = false, threshold = 20 }
|
||||||
```
|
```
|
||||||
|
xwayland:
|
||||||
|
ref: Xwayland
|
||||||
|
required: false
|
||||||
|
description: |
|
||||||
|
Configures the Xwayland settings.
|
||||||
|
|
||||||
|
- Example:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
xwayland = { scaling-mode = "downscaled" }
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
Idle:
|
Idle:
|
||||||
|
|
@ -2627,3 +2638,53 @@ UiDrag:
|
||||||
Sets the distance at which ui dragging starts.
|
Sets the distance at which ui dragging starts.
|
||||||
|
|
||||||
The default is `10`.
|
The default is `10`.
|
||||||
|
|
||||||
|
|
||||||
|
Xwayland:
|
||||||
|
kind: table
|
||||||
|
description: |
|
||||||
|
Describes Xwayland settings.
|
||||||
|
|
||||||
|
- Example:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
xwayland = { scaling-mode = "downscaled" }
|
||||||
|
```
|
||||||
|
fields:
|
||||||
|
scaling-mode:
|
||||||
|
ref: XScalingMode
|
||||||
|
required: false
|
||||||
|
description: The scaling mode of X windows.
|
||||||
|
|
||||||
|
|
||||||
|
XScalingMode:
|
||||||
|
description: |
|
||||||
|
The scaling mode of X windows.
|
||||||
|
|
||||||
|
- Example:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
xwayland = { scaling-mode = "downscaled" }
|
||||||
|
```
|
||||||
|
kind: string
|
||||||
|
values:
|
||||||
|
- value: default
|
||||||
|
description: |
|
||||||
|
The default mode.
|
||||||
|
|
||||||
|
Currently this means that windows are rendered at the lowest scale and then upscaled
|
||||||
|
if necessary.
|
||||||
|
- value: downscaled
|
||||||
|
description: |
|
||||||
|
Windows are rendered at the highest integer scale and then downscaled.
|
||||||
|
|
||||||
|
This has significant performance implications unless the window is running on the
|
||||||
|
output with the highest scale and that scale is an integer scale.
|
||||||
|
|
||||||
|
For example, on a 3840x2160 output with a 1.5 scale, a fullscreen window will be
|
||||||
|
rendered at 3840x2160 * 2 / 1.5 = 5120x2880 pixels and then downscaled to
|
||||||
|
3840x2160. This overhead gets worse the lower the scale of the output is.
|
||||||
|
|
||||||
|
Additionally, this mode requires the X window to scale its contents itself. In the
|
||||||
|
example above, you might achieve this by setting the environment variable
|
||||||
|
`GDK_SCALE=2`.
|
||||||
|
|
|
||||||
|
|
@ -92,6 +92,10 @@ request create_ei_session (since = 5) {
|
||||||
id: id(jay_ei_session_builder),
|
id: id(jay_ei_session_builder),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
request get_xwayland (since = 11) {
|
||||||
|
id: id(jay_xwayland),
|
||||||
|
}
|
||||||
|
|
||||||
# events
|
# events
|
||||||
|
|
||||||
event client_id {
|
event client_id {
|
||||||
|
|
|
||||||
18
wire/jay_xwayland.txt
Normal file
18
wire/jay_xwayland.txt
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
# requests
|
||||||
|
|
||||||
|
request get_scaling {
|
||||||
|
}
|
||||||
|
|
||||||
|
request set_scaling_mode {
|
||||||
|
mode: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
# events
|
||||||
|
|
||||||
|
event scaling_mode {
|
||||||
|
mode: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
event implied_scale {
|
||||||
|
scale: i32,
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue