mirror of
https://github.com/wez/wezterm.git
synced 2024-12-23 05:12:40 +03:00
x11/wayland: subscribe to xdg desktop portal for settings changes
We use this to detect changes in dark mode refs: https://github.com/wez/wezterm/issues/2258
This commit is contained in:
parent
5f8ed00faf
commit
a1c8b4a9b3
@ -27,7 +27,7 @@ As features stabilize some brief notes about them will accumulate here.
|
||||
* New [wezterm.json_parse()](config/lua/wezterm/json_parse.md) and [wezterm.json_encode()](config/lua/wezterm/json_encode.md) functions for working with JSON.
|
||||
* Hundreds of new color schemes have been imported from [base16](https://github.com/chriskempson/base16-schemes-source), [Gogh](https://gogh-co.github.io/Gogh/) and [terminal.sexy](https://terminal.sexy/). [Browse the schemes](colorschemes/index.md) and look for themes with `(base16)`, `(Gogh)` and `(terminal.sexy)` in the name to discover them!
|
||||
* [pane:is_alt_screen_active()](config/lua/pane/is_alt_screen_active.md) for testing whether the alt screen is active. Thanks to [@Funami580](https://github.com/Funami580)! [#2234](https://github.com/wez/wezterm/issues/2234)
|
||||
* X11/Wayland: XDG desktop portal is now used to determine whether dark mode is in use [#2258](https://github.com/wez/wezterm/issues/2258)
|
||||
* X11/Wayland: [XDG desktop portal](https://flatpak.github.io/xdg-desktop-portal/) is now used to determine whether dark mode is in use [#2258](https://github.com/wez/wezterm/issues/2258)
|
||||
|
||||
#### Fixed
|
||||
* [ActivateKeyTable](config/lua/keyassignment/ActivateKeyTable.md)'s `replace_current` field was not actually optional. Made it optional. [#2179](https://github.com/wez/wezterm/issues/2179)
|
||||
|
@ -7,16 +7,12 @@ can be one of the following 4 values:
|
||||
|
||||
* `"Light"` - the normal appearance, with dark text on a light background
|
||||
* `"Dark"` - "dark mode", with predominantly dark colors and probably a lighter, lower contrasting, text color on a dark background
|
||||
* `"LightHighContrast"` - light mode but with high contrast colors
|
||||
* `"DarkHighContrast"` - dark mode but with high contrast colors
|
||||
* `"LightHighContrast"` - light mode but with high contrast colors (not reported on all systems)
|
||||
* `"DarkHighContrast"` - dark mode but with high contrast colors (not reported on all systems)
|
||||
|
||||
wezterm currently doesn't know how to interrogate the appearance on Wayland
|
||||
systems, and will always report `"Light"`.
|
||||
|
||||
On macOS, X11 and Windows systems, wezterm is able to detect when the
|
||||
appearance has changed and will generate a
|
||||
[window-config-reloaded](../window-events/window-config-reloaded.md) event for each
|
||||
window.
|
||||
wezterm is able to detect when the appearance has changed and will generate a
|
||||
[window-config-reloaded](../window-events/window-config-reloaded.md) event for
|
||||
each window.
|
||||
|
||||
This example configuration shows how you can have your color scheme
|
||||
automatically adjust to the current appearance:
|
||||
@ -48,6 +44,16 @@ return {
|
||||
|
||||
### Wayland GNOME Appearance
|
||||
|
||||
*Since: nightly builds only*
|
||||
|
||||
wezterm uses [XDG Desktop
|
||||
Portal](https://flatpak.github.io/xdg-desktop-portal/) to determine the
|
||||
appearance.
|
||||
|
||||
In earlier versions you may wish to use an alternative method to determine the
|
||||
appearance, as wezterm didn't know how to interrogate the appearance on Wayland
|
||||
systems, and would always report `"Light"`.
|
||||
|
||||
The GNOME desktop environment provides the `gsettings` tool that can
|
||||
inform us of the selected appearance even in a Wayland session. We can
|
||||
substitute the call to `window:get_appearance` above with a call to the
|
||||
@ -77,10 +83,10 @@ function query_appearance_gnome()
|
||||
end
|
||||
```
|
||||
|
||||
Since WezTerm will not fire a `window-config-reloaded`
|
||||
event on Wayland, you will instead need to listen on the
|
||||
[update-right-status](../window-events/update-right-status.md) event,
|
||||
which will essentially poll for the appearance periodically:
|
||||
Since WezTerm will not fire a `window-config-reloaded` event on Wayland for
|
||||
older versions of wezterm, you will instead need to listen on the
|
||||
[update-right-status](../window-events/update-right-status.md) event, which
|
||||
will essentially poll for the appearance periodically:
|
||||
|
||||
```lua
|
||||
local wezterm = require 'wezterm'
|
||||
|
@ -412,6 +412,12 @@ impl WaylandConnection {
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn advise_of_appearance_change(&self, appearance: crate::Appearance) {
|
||||
for win in self.windows.borrow().values() {
|
||||
win.borrow_mut().appearance_changed(appearance);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ConnectionOps for WaylandConnection {
|
||||
|
@ -6,9 +6,9 @@ use crate::os::wayland::connection::WaylandConnection;
|
||||
use crate::os::wayland::wl_id;
|
||||
use crate::os::x11::keyboard::Keyboard;
|
||||
use crate::{
|
||||
Clipboard, Connection, Dimensions, MouseCursor, Point, Rect, RequestedWindowGeometry,
|
||||
ResolvedGeometry, ScreenPoint, Window, WindowEvent, WindowEventSender, WindowKeyEvent,
|
||||
WindowOps, WindowState,
|
||||
Appearance, Clipboard, Connection, Dimensions, MouseCursor, Point, Rect,
|
||||
RequestedWindowGeometry, ResolvedGeometry, ScreenPoint, Window, WindowEvent, WindowEventSender,
|
||||
WindowKeyEvent, WindowOps, WindowState,
|
||||
};
|
||||
use anyhow::{anyhow, bail, Context};
|
||||
use async_io::Timer;
|
||||
@ -146,6 +146,7 @@ pub struct WaylandWindowInner {
|
||||
invalidated: bool,
|
||||
font_config: Rc<FontConfiguration>,
|
||||
text_cursor: Option<Rect>,
|
||||
appearance: Appearance,
|
||||
config: Option<ConfigHandle>,
|
||||
// cache the title for comparison to avoid spamming
|
||||
// the compositor with updates that don't actually change it
|
||||
@ -372,6 +373,7 @@ impl WaylandWindow {
|
||||
gl_state: None,
|
||||
wegl_surface: None,
|
||||
text_cursor: None,
|
||||
appearance: Appearance::Light,
|
||||
}));
|
||||
|
||||
let window_handle = Window::Wayland(WaylandWindow(window_id));
|
||||
@ -401,6 +403,14 @@ unsafe impl HasRawWindowHandle for WaylandWindowInner {
|
||||
}
|
||||
|
||||
impl WaylandWindowInner {
|
||||
pub(crate) fn appearance_changed(&mut self, appearance: Appearance) {
|
||||
if appearance != self.appearance {
|
||||
self.appearance = appearance;
|
||||
self.events
|
||||
.dispatch(WindowEvent::AppearanceChanged(appearance));
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn keyboard_event(&mut self, event: WlKeyboardEvent) {
|
||||
let conn = WaylandConnection::get().unwrap().wayland();
|
||||
let mut mapper = conn.keyboard_mapper.borrow_mut();
|
||||
|
@ -313,6 +313,12 @@ impl XConnection {
|
||||
*self.default_dpi.borrow_mut() = dpi;
|
||||
}
|
||||
|
||||
pub(crate) fn advise_of_appearance_change(&self, appearance: crate::Appearance) {
|
||||
for win in self.windows.borrow().values() {
|
||||
win.lock().unwrap().appearance_changed(appearance);
|
||||
}
|
||||
}
|
||||
|
||||
fn process_queued_xcb(&self) -> anyhow::Result<()> {
|
||||
if let Some(event) = self
|
||||
.conn
|
||||
|
@ -523,11 +523,7 @@ impl XWindowInner {
|
||||
conn.update_xrm();
|
||||
self.check_dpi_and_synthesize_resize();
|
||||
let appearance = conn.get_appearance();
|
||||
if appearance != self.appearance {
|
||||
self.appearance = appearance;
|
||||
self.events
|
||||
.dispatch(WindowEvent::AppearanceChanged(appearance));
|
||||
}
|
||||
self.appearance_changed(appearance);
|
||||
}
|
||||
|
||||
if msg.atom() == conn.atom_net_wm_state {
|
||||
@ -555,6 +551,14 @@ impl XWindowInner {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn appearance_changed(&mut self, appearance: Appearance) {
|
||||
if appearance != self.appearance {
|
||||
self.appearance = appearance;
|
||||
self.events
|
||||
.dispatch(WindowEvent::AppearanceChanged(appearance));
|
||||
}
|
||||
}
|
||||
|
||||
fn focus_changed(&mut self, focused: bool) {
|
||||
log::trace!("focus_changed {focused}, flagging geometry as unsure");
|
||||
self.sure_about_geometry = false;
|
||||
|
@ -104,6 +104,15 @@ impl Connection {
|
||||
_ => panic!("attempted to get wayland reference on non-wayland connection"),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn advise_of_appearance_change(&self, appearance: Appearance) {
|
||||
log::trace!("Appearance changed to {appearance:?}");
|
||||
match self {
|
||||
Self::X11(x) => x.advise_of_appearance_change(appearance),
|
||||
#[cfg(feature = "wayland")]
|
||||
Self::Wayland(w) => w.advise_of_appearance_change(appearance),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ConnectionOps for Connection {
|
||||
@ -124,6 +133,7 @@ impl ConnectionOps for Connection {
|
||||
}
|
||||
|
||||
fn run_message_loop(&self) -> anyhow::Result<()> {
|
||||
crate::os::xdg_desktop_portal::subscribe();
|
||||
match self {
|
||||
Self::X11(x) => x.run_message_loop(),
|
||||
#[cfg(feature = "wayland")]
|
||||
@ -132,17 +142,8 @@ impl ConnectionOps for Connection {
|
||||
}
|
||||
|
||||
fn get_appearance(&self) -> Appearance {
|
||||
match promise::spawn::block_on(crate::os::xdg_desktop_portal::read_setting(
|
||||
"org.freedesktop.appearance",
|
||||
"color-scheme",
|
||||
)) {
|
||||
Ok(value) => match value.downcast_ref::<u32>() {
|
||||
Some(1) => return Appearance::Dark,
|
||||
Some(_) => return Appearance::Light,
|
||||
None => {
|
||||
log::debug!("Unable to resolve appearance using xdg-desktop-portal: expected a u32 value but got {value:#?}");
|
||||
}
|
||||
},
|
||||
match promise::spawn::block_on(crate::os::xdg_desktop_portal::get_appearance()) {
|
||||
Ok(appearance) => return appearance,
|
||||
Err(err) => {
|
||||
log::debug!("Unable to resolve appearance using xdg-desktop-portal: {err:#}");
|
||||
}
|
||||
|
@ -2,7 +2,9 @@
|
||||
|
||||
//! <https://github.com/flatpak/xdg-desktop-portal/blob/main/data/org.freedesktop.portal.Settings.xml>
|
||||
|
||||
use crate::{Appearance, Connection, ConnectionOps};
|
||||
use anyhow::Context;
|
||||
use futures_util::stream::StreamExt;
|
||||
use std::collections::HashMap;
|
||||
use zbus::dbus_proxy;
|
||||
use zvariant::OwnedValue;
|
||||
@ -31,3 +33,43 @@ pub async fn read_setting(namespace: &str, key: &str) -> anyhow::Result<OwnedVal
|
||||
.context("make proxy")?;
|
||||
proxy.Read(namespace, key).await.context("Read")
|
||||
}
|
||||
|
||||
fn value_to_appearance(value: OwnedValue) -> anyhow::Result<Appearance> {
|
||||
Ok(match value.downcast_ref::<u32>() {
|
||||
Some(1) => Appearance::Dark,
|
||||
Some(_) => Appearance::Light,
|
||||
None => {
|
||||
anyhow::bail!(
|
||||
"Unable to resolve appearance \
|
||||
using xdg-desktop-portal: expected a u32 value but got {value:#?}"
|
||||
);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn get_appearance() -> anyhow::Result<Appearance> {
|
||||
let value = read_setting("org.freedesktop.appearance", "color-scheme").await?;
|
||||
value_to_appearance(value)
|
||||
}
|
||||
|
||||
pub fn subscribe() {
|
||||
promise::spawn::spawn(async move {
|
||||
let connection = zbus::ConnectionBuilder::session()?.build().await?;
|
||||
let proxy = PortalSettingsProxy::new(&connection)
|
||||
.await
|
||||
.context("make proxy")?;
|
||||
let mut stream = proxy.receive_SettingChanged().await?;
|
||||
while let Some(signal) = stream.next().await {
|
||||
let args = signal.args()?;
|
||||
if args.namespace == "org.freedesktop.appearance" && args.key == "color-scheme" {
|
||||
if let Ok(appearance) = value_to_appearance(args.value) {
|
||||
let conn =
|
||||
Connection::get().ok_or_else(|| anyhow::anyhow!("connection is dead"))?;
|
||||
conn.advise_of_appearance_change(appearance);
|
||||
}
|
||||
}
|
||||
}
|
||||
Result::<(), anyhow::Error>::Ok(())
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user