Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 19 additions & 8 deletions frontends/rioterm/src/application.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ use cpal::traits::{DeviceTrait, HostTrait, StreamTrait};
use raw_window_handle::HasDisplayHandle;
use rio_backend::clipboard::{Clipboard, ClipboardType};
use rio_backend::config::colors::{ColorRgb, NamedColor};
use rio_backend::config::theme::AppearanceTheme;
use rio_window::application::ApplicationHandler;
use rio_window::event::{
ElementState, Ime, MouseButton, MouseScrollDelta, StartCause, TouchPhase, WindowEvent,
Expand Down Expand Up @@ -612,17 +613,19 @@ impl ApplicationHandler<EventPayload> for Application<'_> {
self.setup_quake_hotkey();
}

let color_scheme = self.config.force_theme.or_else(|| {
event_loop
.system_theme()
.map(AppearanceTheme::from_window_theme)
});
let mut has_checked_adaptive_colors = false;
for (_id, route) in self.router.routes.iter_mut() {
// Apply system theme to ensure colors are consistent
if !has_checked_adaptive_colors {
let system_theme = event_loop.system_theme();
let theme = self
.config
.force_theme
.map(|t| t.to_window_theme())
.or(system_theme);
update_colors_based_on_theme(&mut self.config, theme);
update_colors_based_on_theme(
&mut self.config,
color_scheme.map(AppearanceTheme::to_window_theme),
);
has_checked_adaptive_colors = true;
}

Expand All @@ -642,6 +645,7 @@ impl ApplicationHandler<EventPayload> for Application<'_> {
&self.config,
&self.router.font_library,
has_font_updates,
color_scheme,
);
if !self.config.bell.tab_indicator {
route.window.screen.context_manager.clear_all_bells();
Expand Down Expand Up @@ -1105,7 +1109,6 @@ impl ApplicationHandler<EventPayload> for Application<'_> {
}
RioEventType::Rio(RioEvent::ToggleAppearanceTheme) => {
if let Some(route) = self.router.routes.get_mut(&window_id) {
use rio_backend::config::theme::AppearanceTheme;
let current = self
.config
.force_theme
Expand All @@ -1127,6 +1130,7 @@ impl ApplicationHandler<EventPayload> for Application<'_> {
&self.config,
&self.router.font_library,
false,
Some(toggled),
);
route.window.configure_window(&self.config);
}
Expand Down Expand Up @@ -1483,7 +1487,13 @@ impl ApplicationHandler<EventPayload> for Application<'_> {
}

if route.window.screen.select_current_based_on_mouse() {
// Focus-switch click: clear the now-focused pane's
// stale selection and reset click_state so the
// in-flight press can't drag-extend the old anchor.
route.window.screen.clear_selection();
route.window.screen.mouse.click_state = ClickState::Click;
route.request_redraw();
return;
} else if !route.window.screen.modifiers.state().shift_key()
&& !hint_click
&& route.window.screen.mouse_mode()
Expand Down Expand Up @@ -2196,6 +2206,7 @@ impl ApplicationHandler<EventPayload> for Application<'_> {
&self.config,
&self.router.font_library,
false,
Some(AppearanceTheme::from_window_theme(new_theme)),
);
route.window.configure_window(&self.config);
route.request_redraw();
Expand Down
5 changes: 5 additions & 0 deletions frontends/rioterm/src/context/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,9 @@ pub struct ContextManagerConfig {
pub keyboard: rio_backend::config::keyboard::Keyboard,
pub scrollback_history_limit: usize,
pub grapheme_clustering: bool,
/// Initial color-scheme polarity for new panes, so the
/// `CSI ? 996 n` query answers correctly before any theme change.
pub color_scheme_is_dark: bool,
}

const DEFAULT_CONTEXT_CAPACITY: usize = 28;
Expand Down Expand Up @@ -281,6 +284,7 @@ impl<T: EventListener + Clone + std::marker::Send + 'static> ContextManager<T> {
);
terminal.set_grapheme_clustering(config.grapheme_clustering);
terminal.blinking_cursor = cursor_state.1;
terminal.set_color_scheme(config.color_scheme_is_dark);
let terminal: Arc<FairMutex<Crosswords<T>>> = Arc::new(FairMutex::new(terminal));

let integration = if config.shell_integration {
Expand Down Expand Up @@ -1306,6 +1310,7 @@ impl<T: EventListener + Clone + std::marker::Send + 'static> ContextManager<T> {
keyboard: config.keyboard,
scrollback_history_limit: config.scrollback_history_limit,
grapheme_clustering: config.grapheme_clustering,
color_scheme_is_dark: self.config.color_scheme_is_dark,
};

let current = self.current();
Expand Down
19 changes: 15 additions & 4 deletions frontends/rioterm/src/router/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use crate::screen::{Screen, ScreenWindowProperties};
use assistant::Assistant;
use raw_window_handle::{HasDisplayHandle, HasWindowHandle};
use rio_backend::clipboard::Clipboard;
use rio_backend::config::theme::AppearanceTheme;
use rio_backend::config::Config as RioConfig;
use rio_backend::error::{RioError, RioErrorLevel, RioErrorType};

Expand Down Expand Up @@ -112,10 +113,11 @@ impl Route<'_> {
config: &RioConfig,
db: &rio_backend::sugarloaf::font::FontLibrary,
should_update_font: bool,
color_scheme: Option<AppearanceTheme>,
) {
self.window
.screen
.update_config(config, db, should_update_font);
.update_config(config, db, should_update_font, color_scheme);
}

#[inline]
Expand Down Expand Up @@ -890,9 +892,18 @@ impl<'a> RouteWindow<'a> {
window_id: winit_window.id(),
};

let mut screen =
Screen::new(properties, config, event_proxy, font_library, open_url)
.expect("Screen not created");
let color_scheme = config
.force_theme
.or_else(|| winit_window.theme().map(AppearanceTheme::from_window_theme));
let mut screen = Screen::new(
properties,
config,
event_proxy,
font_library,
open_url,
color_scheme,
)
.expect("Screen not created");

if config.window.columns.is_some() || config.window.rows.is_some() {
let (physical_width, physical_height) = compute_window_size_from_grid(
Expand Down
20 changes: 20 additions & 0 deletions frontends/rioterm/src/screen/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ use rio_backend::clipboard::Clipboard;
use rio_backend::clipboard::ClipboardType;
use rio_backend::config::layout::Margin;
use rio_backend::config::renderer::Backend;
use rio_backend::config::theme::AppearanceTheme;
use rio_backend::crosswords::pos::{Boundary, CursorState, Direction, Line};
use rio_backend::crosswords::search::RegexSearch;
use rio_backend::error::{RioError, RioErrorLevel, RioErrorType};
Expand Down Expand Up @@ -142,6 +143,7 @@ impl Screen<'_> {
event_proxy: EventProxy,
font_library: &rio_backend::sugarloaf::font::FontLibrary,
open_url: Option<String>,
color_scheme: Option<AppearanceTheme>,
) -> Result<Screen<'screen>, Box<dyn Error>> {
let size = window_properties.size;
let scale = window_properties.scale;
Expand Down Expand Up @@ -264,6 +266,7 @@ impl Screen<'_> {
keyboard: config.keyboard.clone(),
scrollback_history_limit: config.scrollback_history_limit,
grapheme_clustering: config.grapheme_clustering,
color_scheme_is_dark: !matches!(color_scheme, Some(AppearanceTheme::Light)),
};

let rich_text_id = next_rich_text_id();
Expand Down Expand Up @@ -516,6 +519,7 @@ impl Screen<'_> {
config: &rio_backend::config::Config,
font_library: &rio_backend::sugarloaf::font::FontLibrary,
should_update_font_library: bool,
color_scheme: Option<AppearanceTheme>,
) {
let num_tabs = self.ctx().len();
let padding_y_top = padding_top_from_config(
Expand All @@ -526,6 +530,11 @@ impl Screen<'_> {
);
let padding_y_bottom = config.margin.bottom;

// Resolved OS/adaptive scheme drives the DECSET 2031 notify.
let scheme_is_dark = !matches!(color_scheme, Some(AppearanceTheme::Light));
let color_scheme_changed =
self.context_manager.config.color_scheme_is_dark != scheme_is_dark;

if should_update_font_library {
self.sugarloaf.update_font(font_library);
// Caches keyed by font_id would serve the old font's data.
Expand Down Expand Up @@ -591,10 +600,20 @@ impl Screen<'_> {
let mut terminal = current_context.terminal.lock();
current_context.renderable_content =
RenderableContent::from_cursor_config(&config.cursor);
// Idle panes are skipped by the render loop, so force a
// full repaint or they keep the old theme.
current_context
.renderable_content
.pending_update
.set_terminal_damage(rio_backend::event::TerminalDamage::Full);
let shape = config.cursor.shape;
terminal.cursor_shape = shape;
terminal.default_cursor_shape = shape;
terminal.blinking_cursor = config.cursor.blinking;
terminal.set_color_scheme(scheme_is_dark);
if color_scheme_changed {
terminal.report_color_scheme(scheme_is_dark);
}
drop(terminal);
}
}
Expand All @@ -605,6 +624,7 @@ impl Screen<'_> {
// Update keyboard config in context manager
self.context_manager.config.keyboard = config.keyboard.clone();
self.context_manager.config.title = config.title.clone();
self.context_manager.config.color_scheme_is_dark = scheme_is_dark;

// Re-evaluate the opaque flag — toggling `window.opacity` /
// `window.blur` at runtime should flip the compositor mode.
Expand Down
4 changes: 4 additions & 0 deletions rio-vt/src/ansi/mode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ impl PrivateMode {
2004 => Self::Named(NamedPrivateMode::BracketedPaste),
2026 => Self::Named(NamedPrivateMode::SyncUpdate),
2027 => Self::Named(NamedPrivateMode::GraphemeCluster),
2031 => Self::Named(NamedPrivateMode::ColorSchemeUpdates),
_ => Self::Unknown(mode),
}
}
Expand Down Expand Up @@ -136,6 +137,9 @@ pub enum NamedPrivateMode {
/// width-bearing codepoint. Queryable via DECRQM, which is how
/// applications detect support. Default: reset.
GraphemeCluster = 2027,
/// App opts in to unsolicited DSR `CSI ? 997 n` light/dark
/// color-scheme notifications (DECSET 2031).
ColorSchemeUpdates = 2031,
}

/// Mode for clearing line.
Expand Down
Loading
Loading