mirror of
https://github.com/wez/wezterm.git
synced 2024-12-23 05:12:40 +03:00
lua: add wezterm.window.screens()
Currently implemented on X11 only, this function returns information about the geometry of the screen(s). This is taken from the same source of information we use for the `--position` CLI argument to `wezterm start`. ``` > wezterm.window.screens() { "by_name": { "DisplayPort-1": { "height": 2160, "name": "DisplayPort-1", "width": 3840, "x": 0, "y": 0, }, }, "main": { "height": 2160, "name": "DisplayPort-1", "width": 3840, "x": 0, "y": 0, }, "origin_x": 0, "origin_y": 0, "virtual_height": 2160, "virtual_width": 3840, } ```
This commit is contained in:
parent
082c61c2c3
commit
a6cf13e1e2
12
Cargo.lock
generated
12
Cargo.lock
generated
@ -4811,6 +4811,7 @@ dependencies = [
|
|||||||
"wezterm-toast-notification",
|
"wezterm-toast-notification",
|
||||||
"winapi",
|
"winapi",
|
||||||
"window",
|
"window",
|
||||||
|
"window-funcs",
|
||||||
"windows",
|
"windows",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -5060,6 +5061,17 @@ dependencies = [
|
|||||||
"xkbcommon",
|
"xkbcommon",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "window-funcs"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"config",
|
||||||
|
"luahelper",
|
||||||
|
"wezterm-dynamic",
|
||||||
|
"window",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows"
|
name = "windows"
|
||||||
version = "0.33.0"
|
version = "0.33.0"
|
||||||
|
13
lua-api-crates/window-funcs/Cargo.toml
Normal file
13
lua-api-crates/window-funcs/Cargo.toml
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
[package]
|
||||||
|
name = "window-funcs"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
anyhow = "1.0"
|
||||||
|
config = { path = "../../config" }
|
||||||
|
luahelper = { path = "../../luahelper" }
|
||||||
|
wezterm-dynamic = { path = "../../wezterm-dynamic" }
|
||||||
|
window = {path="../../window"}
|
87
lua-api-crates/window-funcs/src/lib.rs
Normal file
87
lua-api-crates/window-funcs/src/lib.rs
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
use config::lua::get_or_create_module;
|
||||||
|
use config::lua::mlua::{self, Lua};
|
||||||
|
use luahelper::impl_lua_conversion_dynamic;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::rc::Rc;
|
||||||
|
use wezterm_dynamic::{FromDynamic, ToDynamic};
|
||||||
|
use window::{Connection, ConnectionOps};
|
||||||
|
|
||||||
|
fn get_conn() -> mlua::Result<Rc<Connection>> {
|
||||||
|
Connection::get().ok_or_else(|| {
|
||||||
|
mlua::Error::external("cannot get window Connection: not running on the gui thread?")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, FromDynamic, ToDynamic)]
|
||||||
|
pub struct ScreenInfo {
|
||||||
|
pub name: String,
|
||||||
|
pub x: isize,
|
||||||
|
pub y: isize,
|
||||||
|
pub width: isize,
|
||||||
|
pub height: isize,
|
||||||
|
}
|
||||||
|
impl_lua_conversion_dynamic!(ScreenInfo);
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, FromDynamic, ToDynamic)]
|
||||||
|
pub struct Screens {
|
||||||
|
pub main: ScreenInfo,
|
||||||
|
pub by_name: HashMap<String, ScreenInfo>,
|
||||||
|
pub origin_x: isize,
|
||||||
|
pub origin_y: isize,
|
||||||
|
pub virtual_width: isize,
|
||||||
|
pub virtual_height: isize,
|
||||||
|
}
|
||||||
|
impl_lua_conversion_dynamic!(Screens);
|
||||||
|
|
||||||
|
impl From<window::screen::ScreenInfo> for ScreenInfo {
|
||||||
|
fn from(info: window::screen::ScreenInfo) -> Self {
|
||||||
|
Self {
|
||||||
|
name: info.name,
|
||||||
|
x: info.rect.min_x(),
|
||||||
|
y: info.rect.min_y(),
|
||||||
|
width: info.rect.width(),
|
||||||
|
height: info.rect.height(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<window::screen::Screens> for Screens {
|
||||||
|
fn from(screens: window::screen::Screens) -> Self {
|
||||||
|
let origin_x = screens.virtual_rect.min_x();
|
||||||
|
let origin_y = screens.virtual_rect.min_y();
|
||||||
|
let virtual_width = screens.virtual_rect.width();
|
||||||
|
let virtual_height = screens.virtual_rect.height();
|
||||||
|
Self {
|
||||||
|
main: screens.main.into(),
|
||||||
|
by_name: screens
|
||||||
|
.by_name
|
||||||
|
.into_iter()
|
||||||
|
.map(|(k, info)| (k, info.into()))
|
||||||
|
.collect(),
|
||||||
|
origin_x,
|
||||||
|
origin_y,
|
||||||
|
virtual_width,
|
||||||
|
virtual_height,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn register(lua: &Lua) -> anyhow::Result<()> {
|
||||||
|
let wezterm_mod = get_or_create_module(lua, "wezterm")?;
|
||||||
|
let window_mod = lua.create_table()?;
|
||||||
|
|
||||||
|
window_mod.set(
|
||||||
|
"screens",
|
||||||
|
lua.create_function(|_, _: ()| {
|
||||||
|
let conn = get_conn()?;
|
||||||
|
let screens: Screens = conn
|
||||||
|
.screens()
|
||||||
|
.map_err(|err| mlua::Error::external(format!("{err:#}")))?
|
||||||
|
.into();
|
||||||
|
Ok(screens)
|
||||||
|
})?,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
wezterm_mod.set("window", window_mod)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
@ -82,6 +82,7 @@ wezterm-ssh = { path = "../wezterm-ssh" }
|
|||||||
wezterm-term = { path = "../term", features=["use_serde"] }
|
wezterm-term = { path = "../term", features=["use_serde"] }
|
||||||
wezterm-toast-notification = { path = "../wezterm-toast-notification" }
|
wezterm-toast-notification = { path = "../wezterm-toast-notification" }
|
||||||
window = { path = "../window" }
|
window = { path = "../window" }
|
||||||
|
window-funcs = { path = "../lua-api-crates/window-funcs" }
|
||||||
|
|
||||||
[target."cfg(windows)".dependencies]
|
[target."cfg(windows)".dependencies]
|
||||||
shared_library = "0.1"
|
shared_library = "0.1"
|
||||||
|
@ -982,6 +982,10 @@ fn run() -> anyhow::Result<()> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
env_bootstrap::bootstrap();
|
env_bootstrap::bootstrap();
|
||||||
|
// window_funcs is not set up by env_bootstrap as window_funcs is
|
||||||
|
// GUI environment specific and env_bootstrap is used to setup the
|
||||||
|
// headless mux server.
|
||||||
|
config::lua::add_context_setup_func(window_funcs::register);
|
||||||
|
|
||||||
stats::Stats::init()?;
|
stats::Stats::init()?;
|
||||||
let _saver = umask::UmaskSaver::new();
|
let _saver = umask::UmaskSaver::new();
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
use crate::screen::Screens;
|
||||||
use crate::{Appearance, Connection};
|
use crate::{Appearance, Connection};
|
||||||
use anyhow::Result as Fallible;
|
use anyhow::Result as Fallible;
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
@ -48,4 +49,9 @@ pub trait ConnectionOps {
|
|||||||
|
|
||||||
/// Perform the system beep/notification sound
|
/// Perform the system beep/notification sound
|
||||||
fn beep(&self) {}
|
fn beep(&self) {}
|
||||||
|
|
||||||
|
/// Returns information about the screens
|
||||||
|
fn screens(&self) -> anyhow::Result<Screens> {
|
||||||
|
anyhow::bail!("Unable to query screen information");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,7 @@ pub use wezterm_color_types as color;
|
|||||||
mod configuration;
|
mod configuration;
|
||||||
pub mod connection;
|
pub mod connection;
|
||||||
pub mod os;
|
pub mod os;
|
||||||
|
pub mod screen;
|
||||||
mod spawn;
|
mod spawn;
|
||||||
|
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
@ -57,6 +58,7 @@ pub type Rect = euclid::Rect<isize, PixelUnit>;
|
|||||||
pub type RectF = euclid::Rect<f32, PixelUnit>;
|
pub type RectF = euclid::Rect<f32, PixelUnit>;
|
||||||
pub type Size = euclid::Size2D<isize, PixelUnit>;
|
pub type Size = euclid::Size2D<isize, PixelUnit>;
|
||||||
pub type SizeF = euclid::Size2D<f32, PixelUnit>;
|
pub type SizeF = euclid::Size2D<f32, PixelUnit>;
|
||||||
|
pub type ScreenRect = euclid::Rect<isize, ScreenPixelUnit>;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
pub enum MouseCursor {
|
pub enum MouseCursor {
|
||||||
|
@ -3,8 +3,9 @@ use crate::connection::ConnectionOps;
|
|||||||
use crate::os::x11::window::XWindowInner;
|
use crate::os::x11::window::XWindowInner;
|
||||||
use crate::os::x11::xsettings::*;
|
use crate::os::x11::xsettings::*;
|
||||||
use crate::os::Connection;
|
use crate::os::Connection;
|
||||||
|
use crate::screen::{ScreenInfo, Screens};
|
||||||
use crate::spawn::*;
|
use crate::spawn::*;
|
||||||
use crate::{Appearance, DeadKeyStatus};
|
use crate::{Appearance, DeadKeyStatus, ScreenRect};
|
||||||
use anyhow::{anyhow, bail, Context as _};
|
use anyhow::{anyhow, bail, Context as _};
|
||||||
use mio::event::Source;
|
use mio::event::Source;
|
||||||
use mio::unix::SourceFd;
|
use mio::unix::SourceFd;
|
||||||
@ -144,6 +145,69 @@ impl ConnectionOps for XConnection {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn screens(&self) -> anyhow::Result<Screens> {
|
||||||
|
if !self.has_randr {
|
||||||
|
anyhow::bail!("XRANDR is not available, cannot query screen geometry");
|
||||||
|
}
|
||||||
|
|
||||||
|
let res = self
|
||||||
|
.send_and_wait_request(&xcb::randr::GetScreenResources { window: self.root })
|
||||||
|
.context("get_screen_resources")?;
|
||||||
|
|
||||||
|
let mut virtual_rect: ScreenRect = euclid::rect(0, 0, 0, 0);
|
||||||
|
let mut by_name = HashMap::new();
|
||||||
|
|
||||||
|
for &o in res.outputs() {
|
||||||
|
let info = self
|
||||||
|
.send_and_wait_request(&xcb::randr::GetOutputInfo {
|
||||||
|
output: o,
|
||||||
|
config_timestamp: res.config_timestamp(),
|
||||||
|
})
|
||||||
|
.context("get_output_info")?;
|
||||||
|
let name = String::from_utf8_lossy(info.name()).to_string();
|
||||||
|
let c = info.crtc();
|
||||||
|
if let Ok(cinfo) = self.send_and_wait_request(&xcb::randr::GetCrtcInfo {
|
||||||
|
crtc: c,
|
||||||
|
config_timestamp: res.config_timestamp(),
|
||||||
|
}) {
|
||||||
|
let bounds = euclid::rect(
|
||||||
|
cinfo.x() as isize,
|
||||||
|
cinfo.y() as isize,
|
||||||
|
cinfo.width() as isize,
|
||||||
|
cinfo.height() as isize,
|
||||||
|
);
|
||||||
|
virtual_rect = virtual_rect.union(&bounds);
|
||||||
|
let info = ScreenInfo {
|
||||||
|
name: name.clone(),
|
||||||
|
rect: bounds,
|
||||||
|
};
|
||||||
|
by_name.insert(name, info);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The main screen is the one either at the origin of
|
||||||
|
// the virtual area, or if that doesn't exist for some weird
|
||||||
|
// reason, the screen closest to the origin.
|
||||||
|
let main = by_name
|
||||||
|
.values()
|
||||||
|
.min_by_key(|screen| {
|
||||||
|
screen
|
||||||
|
.rect
|
||||||
|
.origin
|
||||||
|
.to_f32()
|
||||||
|
.distance_to(euclid::Point2D::origin())
|
||||||
|
.abs() as isize
|
||||||
|
})
|
||||||
|
.ok_or_else(|| anyhow::anyhow!("no screens were found"))?
|
||||||
|
.clone();
|
||||||
|
|
||||||
|
Ok(Screens {
|
||||||
|
main,
|
||||||
|
by_name,
|
||||||
|
virtual_rect,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
fn run_message_loop(&self) -> anyhow::Result<()> {
|
fn run_message_loop(&self) -> anyhow::Result<()> {
|
||||||
self.conn.flush()?;
|
self.conn.flush()?;
|
||||||
|
|
||||||
|
@ -14,7 +14,6 @@ use promise::{Future, Promise};
|
|||||||
use raw_window_handle::unix::XcbHandle;
|
use raw_window_handle::unix::XcbHandle;
|
||||||
use raw_window_handle::{HasRawWindowHandle, RawWindowHandle};
|
use raw_window_handle::{HasRawWindowHandle, RawWindowHandle};
|
||||||
use std::any::Any;
|
use std::any::Any;
|
||||||
use std::collections::HashMap;
|
|
||||||
use std::convert::TryInto;
|
use std::convert::TryInto;
|
||||||
use std::rc::{Rc, Weak};
|
use std::rc::{Rc, Weak};
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
@ -1534,69 +1533,34 @@ fn resolve_geometry(
|
|||||||
conn: &XConnection,
|
conn: &XConnection,
|
||||||
geometry: RequestedWindowGeometry,
|
geometry: RequestedWindowGeometry,
|
||||||
) -> anyhow::Result<ResolvedGeometry> {
|
) -> anyhow::Result<ResolvedGeometry> {
|
||||||
let bounds = if conn.has_randr {
|
let bounds = match conn.screens() {
|
||||||
let res = conn
|
Ok(screens) => {
|
||||||
.send_and_wait_request(&xcb::randr::GetScreenResources { window: conn.root })
|
log::trace!("{screens:?}");
|
||||||
.context("get_screen_resources")?;
|
|
||||||
|
|
||||||
let mut virtual_screen: Rect = euclid::rect(0, 0, 0, 0);
|
match geometry.origin {
|
||||||
let mut main_screen: Rect = euclid::rect(0, 0, 0, 0);
|
GeometryOrigin::ScreenCoordinateSystem => screens.virtual_rect,
|
||||||
let mut by_name = HashMap::new();
|
GeometryOrigin::MainScreen => screens.main.rect,
|
||||||
|
GeometryOrigin::ActiveScreen => {
|
||||||
for &o in res.outputs() {
|
// TODO: find focused window and resolve it!
|
||||||
let info = conn
|
// Maybe something like <https://stackoverflow.com/a/43666928/149111>
|
||||||
.send_and_wait_request(&xcb::randr::GetOutputInfo {
|
// but ported to Rust?
|
||||||
output: o,
|
screens.main.rect
|
||||||
config_timestamp: res.config_timestamp(),
|
|
||||||
})
|
|
||||||
.context("get_output_info")?;
|
|
||||||
let name = String::from_utf8_lossy(info.name()).to_string();
|
|
||||||
let c = info.crtc();
|
|
||||||
if let Ok(cinfo) = conn.send_and_wait_request(&xcb::randr::GetCrtcInfo {
|
|
||||||
crtc: c,
|
|
||||||
config_timestamp: res.config_timestamp(),
|
|
||||||
}) {
|
|
||||||
let bounds = euclid::rect(
|
|
||||||
cinfo.x() as isize,
|
|
||||||
cinfo.y() as isize,
|
|
||||||
cinfo.width() as isize,
|
|
||||||
cinfo.height() as isize,
|
|
||||||
);
|
|
||||||
virtual_screen = virtual_screen.union(&bounds);
|
|
||||||
if bounds.origin.x == 0 && bounds.origin.y == 0 {
|
|
||||||
main_screen = bounds;
|
|
||||||
}
|
}
|
||||||
by_name.insert(name, bounds);
|
GeometryOrigin::Named(name) => match screens.by_name.get(&name) {
|
||||||
}
|
Some(info) => info.rect.clone(),
|
||||||
}
|
None => {
|
||||||
log::trace!("{:?}", by_name);
|
log::error!(
|
||||||
log::trace!("virtual: {:?}", virtual_screen);
|
"Requested display {} was not found; available displays are: {:?}. \
|
||||||
log::trace!("main: {:?}", main_screen);
|
|
||||||
|
|
||||||
match geometry.origin {
|
|
||||||
GeometryOrigin::ScreenCoordinateSystem => virtual_screen,
|
|
||||||
GeometryOrigin::MainScreen => main_screen,
|
|
||||||
GeometryOrigin::ActiveScreen => {
|
|
||||||
// TODO: find focused window and resolve it!
|
|
||||||
// Maybe something like <https://stackoverflow.com/a/43666928/149111>
|
|
||||||
// but ported to Rust?
|
|
||||||
main_screen
|
|
||||||
}
|
|
||||||
GeometryOrigin::Named(name) => match by_name.get(&name) {
|
|
||||||
Some(bounds) => bounds.clone(),
|
|
||||||
None => {
|
|
||||||
log::error!(
|
|
||||||
"Requested display {} was not found; available displays are: {:?}. \
|
|
||||||
Using primary display instead",
|
Using primary display instead",
|
||||||
name,
|
name,
|
||||||
by_name,
|
screens.by_name,
|
||||||
);
|
);
|
||||||
main_screen
|
screens.main.rect
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
Err(_) => euclid::rect(0, 0, 65535, 65535),
|
||||||
euclid::rect(0, 0, 65535, 65535)
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let dpi = conn.default_dpi();
|
let dpi = conn.default_dpi();
|
||||||
|
@ -5,6 +5,7 @@ use crate::connection::ConnectionOps;
|
|||||||
use crate::os::wayland::connection::WaylandConnection;
|
use crate::os::wayland::connection::WaylandConnection;
|
||||||
#[cfg(feature = "wayland")]
|
#[cfg(feature = "wayland")]
|
||||||
use crate::os::wayland::window::WaylandWindow;
|
use crate::os::wayland::window::WaylandWindow;
|
||||||
|
use crate::screen::Screens;
|
||||||
use crate::os::x11::connection::XConnection;
|
use crate::os::x11::connection::XConnection;
|
||||||
use crate::os::x11::window::XWindow;
|
use crate::os::x11::window::XWindow;
|
||||||
use crate::{
|
use crate::{
|
||||||
@ -145,6 +146,14 @@ impl ConnectionOps for Connection {
|
|||||||
Self::Wayland(w) => w.beep(),
|
Self::Wayland(w) => w.beep(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn screens(&self) -> anyhow::Result<Screens> {
|
||||||
|
match self {
|
||||||
|
Self::X11(x) => x.screens(),
|
||||||
|
#[cfg(feature = "wayland")]
|
||||||
|
Self::Wayland(w) => w.screens(),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Window {
|
impl Window {
|
||||||
|
15
window/src/screen.rs
Normal file
15
window/src/screen.rs
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
use crate::ScreenRect;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Screens {
|
||||||
|
pub main: ScreenInfo,
|
||||||
|
pub by_name: HashMap<String, ScreenInfo>,
|
||||||
|
pub virtual_rect: ScreenRect,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct ScreenInfo {
|
||||||
|
pub name: String,
|
||||||
|
pub rect: ScreenRect,
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user