mirror of
https://github.com/wez/wezterm.git
synced 2024-12-23 13:21:38 +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.
|
* 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!
|
* 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)
|
* [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
|
#### 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)
|
* [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
|
* `"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
|
* `"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
|
* `"LightHighContrast"` - light mode but with high contrast colors (not reported on all systems)
|
||||||
* `"DarkHighContrast"` - dark mode but with high contrast colors
|
* `"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
|
wezterm is able to detect when the appearance has changed and will generate a
|
||||||
systems, and will always report `"Light"`.
|
[window-config-reloaded](../window-events/window-config-reloaded.md) event for
|
||||||
|
each window.
|
||||||
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.
|
|
||||||
|
|
||||||
This example configuration shows how you can have your color scheme
|
This example configuration shows how you can have your color scheme
|
||||||
automatically adjust to the current appearance:
|
automatically adjust to the current appearance:
|
||||||
@ -48,6 +44,16 @@ return {
|
|||||||
|
|
||||||
### Wayland GNOME Appearance
|
### 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
|
The GNOME desktop environment provides the `gsettings` tool that can
|
||||||
inform us of the selected appearance even in a Wayland session. We 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
|
substitute the call to `window:get_appearance` above with a call to the
|
||||||
@ -77,10 +83,10 @@ function query_appearance_gnome()
|
|||||||
end
|
end
|
||||||
```
|
```
|
||||||
|
|
||||||
Since WezTerm will not fire a `window-config-reloaded`
|
Since WezTerm will not fire a `window-config-reloaded` event on Wayland for
|
||||||
event on Wayland, you will instead need to listen on the
|
older versions of wezterm, you will instead need to listen on the
|
||||||
[update-right-status](../window-events/update-right-status.md) event,
|
[update-right-status](../window-events/update-right-status.md) event, which
|
||||||
which will essentially poll for the appearance periodically:
|
will essentially poll for the appearance periodically:
|
||||||
|
|
||||||
```lua
|
```lua
|
||||||
local wezterm = require 'wezterm'
|
local wezterm = require 'wezterm'
|
||||||
|
@ -412,6 +412,12 @@ impl WaylandConnection {
|
|||||||
}
|
}
|
||||||
Ok(())
|
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 {
|
impl ConnectionOps for WaylandConnection {
|
||||||
|
@ -6,9 +6,9 @@ use crate::os::wayland::connection::WaylandConnection;
|
|||||||
use crate::os::wayland::wl_id;
|
use crate::os::wayland::wl_id;
|
||||||
use crate::os::x11::keyboard::Keyboard;
|
use crate::os::x11::keyboard::Keyboard;
|
||||||
use crate::{
|
use crate::{
|
||||||
Clipboard, Connection, Dimensions, MouseCursor, Point, Rect, RequestedWindowGeometry,
|
Appearance, Clipboard, Connection, Dimensions, MouseCursor, Point, Rect,
|
||||||
ResolvedGeometry, ScreenPoint, Window, WindowEvent, WindowEventSender, WindowKeyEvent,
|
RequestedWindowGeometry, ResolvedGeometry, ScreenPoint, Window, WindowEvent, WindowEventSender,
|
||||||
WindowOps, WindowState,
|
WindowKeyEvent, WindowOps, WindowState,
|
||||||
};
|
};
|
||||||
use anyhow::{anyhow, bail, Context};
|
use anyhow::{anyhow, bail, Context};
|
||||||
use async_io::Timer;
|
use async_io::Timer;
|
||||||
@ -146,6 +146,7 @@ pub struct WaylandWindowInner {
|
|||||||
invalidated: bool,
|
invalidated: bool,
|
||||||
font_config: Rc<FontConfiguration>,
|
font_config: Rc<FontConfiguration>,
|
||||||
text_cursor: Option<Rect>,
|
text_cursor: Option<Rect>,
|
||||||
|
appearance: Appearance,
|
||||||
config: Option<ConfigHandle>,
|
config: Option<ConfigHandle>,
|
||||||
// cache the title for comparison to avoid spamming
|
// cache the title for comparison to avoid spamming
|
||||||
// the compositor with updates that don't actually change it
|
// the compositor with updates that don't actually change it
|
||||||
@ -372,6 +373,7 @@ impl WaylandWindow {
|
|||||||
gl_state: None,
|
gl_state: None,
|
||||||
wegl_surface: None,
|
wegl_surface: None,
|
||||||
text_cursor: None,
|
text_cursor: None,
|
||||||
|
appearance: Appearance::Light,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
let window_handle = Window::Wayland(WaylandWindow(window_id));
|
let window_handle = Window::Wayland(WaylandWindow(window_id));
|
||||||
@ -401,6 +403,14 @@ unsafe impl HasRawWindowHandle for WaylandWindowInner {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl 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) {
|
pub(crate) fn keyboard_event(&mut self, event: WlKeyboardEvent) {
|
||||||
let conn = WaylandConnection::get().unwrap().wayland();
|
let conn = WaylandConnection::get().unwrap().wayland();
|
||||||
let mut mapper = conn.keyboard_mapper.borrow_mut();
|
let mut mapper = conn.keyboard_mapper.borrow_mut();
|
||||||
|
@ -313,6 +313,12 @@ impl XConnection {
|
|||||||
*self.default_dpi.borrow_mut() = dpi;
|
*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<()> {
|
fn process_queued_xcb(&self) -> anyhow::Result<()> {
|
||||||
if let Some(event) = self
|
if let Some(event) = self
|
||||||
.conn
|
.conn
|
||||||
|
@ -523,11 +523,7 @@ impl XWindowInner {
|
|||||||
conn.update_xrm();
|
conn.update_xrm();
|
||||||
self.check_dpi_and_synthesize_resize();
|
self.check_dpi_and_synthesize_resize();
|
||||||
let appearance = conn.get_appearance();
|
let appearance = conn.get_appearance();
|
||||||
if appearance != self.appearance {
|
self.appearance_changed(appearance);
|
||||||
self.appearance = appearance;
|
|
||||||
self.events
|
|
||||||
.dispatch(WindowEvent::AppearanceChanged(appearance));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if msg.atom() == conn.atom_net_wm_state {
|
if msg.atom() == conn.atom_net_wm_state {
|
||||||
@ -555,6 +551,14 @@ impl XWindowInner {
|
|||||||
Ok(())
|
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) {
|
fn focus_changed(&mut self, focused: bool) {
|
||||||
log::trace!("focus_changed {focused}, flagging geometry as unsure");
|
log::trace!("focus_changed {focused}, flagging geometry as unsure");
|
||||||
self.sure_about_geometry = false;
|
self.sure_about_geometry = false;
|
||||||
|
@ -104,6 +104,15 @@ impl Connection {
|
|||||||
_ => panic!("attempted to get wayland reference on non-wayland 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 {
|
impl ConnectionOps for Connection {
|
||||||
@ -124,6 +133,7 @@ impl ConnectionOps for Connection {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn run_message_loop(&self) -> anyhow::Result<()> {
|
fn run_message_loop(&self) -> anyhow::Result<()> {
|
||||||
|
crate::os::xdg_desktop_portal::subscribe();
|
||||||
match self {
|
match self {
|
||||||
Self::X11(x) => x.run_message_loop(),
|
Self::X11(x) => x.run_message_loop(),
|
||||||
#[cfg(feature = "wayland")]
|
#[cfg(feature = "wayland")]
|
||||||
@ -132,17 +142,8 @@ impl ConnectionOps for Connection {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn get_appearance(&self) -> Appearance {
|
fn get_appearance(&self) -> Appearance {
|
||||||
match promise::spawn::block_on(crate::os::xdg_desktop_portal::read_setting(
|
match promise::spawn::block_on(crate::os::xdg_desktop_portal::get_appearance()) {
|
||||||
"org.freedesktop.appearance",
|
Ok(appearance) => return 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:#?}");
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
log::debug!("Unable to resolve appearance using xdg-desktop-portal: {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>
|
//! <https://github.com/flatpak/xdg-desktop-portal/blob/main/data/org.freedesktop.portal.Settings.xml>
|
||||||
|
|
||||||
|
use crate::{Appearance, Connection, ConnectionOps};
|
||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
|
use futures_util::stream::StreamExt;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use zbus::dbus_proxy;
|
use zbus::dbus_proxy;
|
||||||
use zvariant::OwnedValue;
|
use zvariant::OwnedValue;
|
||||||
@ -31,3 +33,43 @@ pub async fn read_setting(namespace: &str, key: &str) -> anyhow::Result<OwnedVal
|
|||||||
.context("make proxy")?;
|
.context("make proxy")?;
|
||||||
proxy.Read(namespace, key).await.context("Read")
|
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