aboutsummaryrefslogtreecommitdiff
path: root/alacritty
diff options
context:
space:
mode:
authorKirill Chibisov <contact@kchibisov.com>2024-05-04 20:51:56 +0400
committerGitHub <noreply@github.com>2024-05-04 20:51:56 +0400
commit48c088a50c977dfaf2a5a7052a5ddd39071e6063 (patch)
tree21e1fb0ac1e7d3427a6c80f92890486f3f205eb5 /alacritty
parenta77f77c48fca298caab3a4834b2d7ab1a98cae88 (diff)
downloadalacritty-48c088a50c977dfaf2a5a7052a5ddd39071e6063.tar.gz
alacritty-48c088a50c977dfaf2a5a7052a5ddd39071e6063.zip
Bump winit to 0.30.0
Diffstat (limited to 'alacritty')
-rw-r--r--alacritty/Cargo.toml6
-rw-r--r--alacritty/src/clipboard.rs6
-rw-r--r--alacritty/src/display/window.rs48
-rw-r--r--alacritty/src/event.rs741
-rw-r--r--alacritty/src/input/mod.rs8
-rw-r--r--alacritty/src/logging.rs4
-rw-r--r--alacritty/src/main.rs29
-rw-r--r--alacritty/src/window_context.rs8
8 files changed, 435 insertions, 415 deletions
diff --git a/alacritty/Cargo.toml b/alacritty/Cargo.toml
index 7fcbb13a..10ee1aee 100644
--- a/alacritty/Cargo.toml
+++ b/alacritty/Cargo.toml
@@ -27,7 +27,7 @@ ahash = { version = "0.8.6", features = ["no-rng"] }
bitflags = "2.2.1"
clap = { version = "4.2.7", features = ["derive", "env"] }
copypasta = { version = "0.10.1", default-features = false }
-crossfont = { version = "0.7.0", features = ["force_system_fontconfig"] }
+crossfont = "0.8.0"
glutin = { version = "0.31.1", default-features = false, features = ["egl", "wgl"] }
home = "0.5.5"
libc = "0.2"
@@ -40,7 +40,7 @@ serde_json = "1"
serde_yaml = "0.9.25"
toml = "0.8.2"
unicode-width = "0.1"
-winit = { version = "0.29.15", default-features = false, features = ["rwh_05", "serde"] }
+winit = { version = "0.30.0", default-features = false, features = ["rwh_05", "serde"] }
[build-dependencies]
gl_generator = "0.14.0"
@@ -60,7 +60,7 @@ objc = "0.2.2"
[target.'cfg(windows)'.dependencies]
dirs = "5.0.1"
-windows-sys = { version = "0.48", features = [
+windows-sys = { version = "0.52", features = [
"Win32_UI_WindowsAndMessaging",
"Win32_System_Threading",
"Win32_System_Console",
diff --git a/alacritty/src/clipboard.rs b/alacritty/src/clipboard.rs
index b3818c75..bb90a13d 100644
--- a/alacritty/src/clipboard.rs
+++ b/alacritty/src/clipboard.rs
@@ -3,7 +3,6 @@ use raw_window_handle::RawDisplayHandle;
use alacritty_terminal::term::ClipboardType;
-#[cfg(any(test, not(any(feature = "x11", target_os = "macos", windows))))]
use copypasta::nop_clipboard::NopClipboardContext;
#[cfg(all(feature = "wayland", not(any(target_os = "macos", windows))))]
use copypasta::wayland_clipboard;
@@ -31,9 +30,8 @@ impl Clipboard {
}
}
- /// Used for tests and to handle missing clipboard provider when built without the `x11`
- /// feature.
- #[cfg(any(test, not(any(feature = "x11", target_os = "macos", windows))))]
+ /// Used for tests, to handle missing clipboard provider when built without the `x11`
+ /// feature, and as default clipboard value.
pub fn new_nop() -> Self {
Self { clipboard: Box::new(NopClipboardContext::new().unwrap()), selection: None }
}
diff --git a/alacritty/src/display/window.rs b/alacritty/src/display/window.rs
index e4bfa2cb..09793fa0 100644
--- a/alacritty/src/display/window.rs
+++ b/alacritty/src/display/window.rs
@@ -1,16 +1,16 @@
#[cfg(not(any(target_os = "macos", windows)))]
use winit::platform::startup_notify::{
- self, EventLoopExtStartupNotify, WindowBuilderExtStartupNotify,
+ self, EventLoopExtStartupNotify, WindowAttributesExtStartupNotify,
};
#[cfg(all(not(feature = "x11"), not(any(target_os = "macos", windows))))]
-use winit::platform::wayland::WindowBuilderExtWayland;
+use winit::platform::wayland::WindowAttributesExtWayland;
#[rustfmt::skip]
#[cfg(all(feature = "x11", not(any(target_os = "macos", windows))))]
use {
std::io::Cursor,
- winit::platform::x11::{WindowBuilderExtX11, EventLoopWindowTargetExtX11},
+ winit::platform::x11::{WindowAttributesExtX11, ActiveEventLoopExtX11},
glutin::platform::x11::X11VisualInfo,
winit::window::Icon,
png::Decoder,
@@ -23,18 +23,18 @@ use {
cocoa::appkit::NSColorSpace,
cocoa::base::{id, nil, NO, YES},
objc::{msg_send, sel, sel_impl},
- winit::platform::macos::{OptionAsAlt, WindowBuilderExtMacOS, WindowExtMacOS},
+ winit::platform::macos::{OptionAsAlt, WindowAttributesExtMacOS, WindowExtMacOS},
};
use raw_window_handle::{HasRawWindowHandle, RawWindowHandle};
use winit::dpi::{PhysicalPosition, PhysicalSize};
-use winit::event_loop::EventLoopWindowTarget;
+use winit::event_loop::ActiveEventLoop;
use winit::monitor::MonitorHandle;
#[cfg(windows)]
use winit::platform::windows::IconExtWindows;
use winit::window::{
CursorIcon, Fullscreen, ImePurpose, Theme, UserAttentionType, Window as WinitWindow,
- WindowBuilder, WindowId,
+ WindowAttributes, WindowId,
};
use alacritty_terminal::index::Point;
@@ -121,8 +121,8 @@ impl Window {
/// Create a new window.
///
/// This creates a window and fully initializes a window.
- pub fn new<E>(
- event_loop: &EventLoopWindowTarget<E>,
+ pub fn new(
+ event_loop: &ActiveEventLoop,
config: &UiConfig,
identity: &Identity,
#[rustfmt::skip]
@@ -133,7 +133,7 @@ impl Window {
x11_visual: Option<X11VisualInfo>,
) -> Result<Window> {
let identity = identity.clone();
- let mut window_builder = Window::get_platform_window(
+ let mut window_attributes = Window::get_platform_window(
&identity,
&config.window,
#[cfg(all(feature = "x11", not(any(target_os = "macos", windows))))]
@@ -143,14 +143,14 @@ impl Window {
);
if let Some(position) = config.window.position {
- window_builder = window_builder
+ window_attributes = window_attributes
.with_position(PhysicalPosition::<i32>::from((position.x, position.y)));
}
#[cfg(not(any(target_os = "macos", windows)))]
if let Some(token) = event_loop.read_token_from_env() {
log::debug!("Activating window with token: {token:?}");
- window_builder = window_builder.with_activation_token(token);
+ window_attributes = window_attributes.with_activation_token(token);
// Remove the token from the env.
startup_notify::reset_activation_token_env();
@@ -160,22 +160,23 @@ impl Window {
#[cfg(all(feature = "x11", not(any(target_os = "macos", windows))))]
if let Some(parent_window_id) = event_loop.is_x11().then_some(config.window.embed).flatten()
{
- window_builder = window_builder.with_embed_parent_window(parent_window_id);
+ window_attributes = window_attributes.with_embed_parent_window(parent_window_id);
}
- let window = window_builder
+ window_attributes = window_attributes
.with_title(&identity.title)
.with_theme(config.window.theme())
.with_visible(false)
.with_transparent(true)
.with_blur(config.window.blur)
.with_maximized(config.window.maximized())
- .with_fullscreen(config.window.fullscreen())
- .build(event_loop)?;
+ .with_fullscreen(config.window.fullscreen());
+
+ let window = event_loop.create_window(window_attributes)?;
// Text cursor.
let current_mouse_cursor = CursorIcon::Text;
- window.set_cursor_icon(current_mouse_cursor);
+ window.set_cursor(current_mouse_cursor);
// Enable IME.
window.set_ime_allowed(true);
@@ -248,7 +249,7 @@ impl Window {
pub fn set_mouse_cursor(&mut self, cursor: CursorIcon) {
if cursor != self.current_mouse_cursor {
self.current_mouse_cursor = cursor;
- self.window.set_cursor_icon(cursor);
+ self.window.set_cursor(cursor);
}
}
@@ -267,7 +268,7 @@ impl Window {
#[cfg(all(feature = "x11", not(any(target_os = "macos", windows))))] x11_visual: Option<
X11VisualInfo,
>,
- ) -> WindowBuilder {
+ ) -> WindowAttributes {
#[cfg(feature = "x11")]
let icon = {
let mut decoder = Decoder::new(Cursor::new(WINDOW_ICON));
@@ -279,7 +280,7 @@ impl Window {
.expect("invalid embedded icon format")
};
- let builder = WindowBuilder::new()
+ let builder = WinitWindow::default_attributes()
.with_name(&identity.class.general, &identity.class.instance)
.with_decorations(window_config.decorations != Decorations::None);
@@ -296,10 +297,10 @@ impl Window {
}
#[cfg(windows)]
- pub fn get_platform_window(_: &Identity, window_config: &WindowConfig) -> WindowBuilder {
+ pub fn get_platform_window(_: &Identity, window_config: &WindowConfig) -> WindowAttributes {
let icon = winit::window::Icon::from_resource(IDI_ICON, None);
- WindowBuilder::new()
+ WinitWindow::default_attributes()
.with_decorations(window_config.decorations != Decorations::None)
.with_window_icon(icon.ok())
}
@@ -309,8 +310,9 @@ impl Window {
_: &Identity,
window_config: &WindowConfig,
tabbing_id: &Option<String>,
- ) -> WindowBuilder {
- let mut window = WindowBuilder::new().with_option_as_alt(window_config.option_as_alt());
+ ) -> WindowAttributes {
+ let mut window =
+ WinitWindow::default_attributes().with_option_as_alt(window_config.option_as_alt());
if let Some(tabbing_id) = tabbing_id {
window = window.with_tabbing_identifier(tabbing_id);
diff --git a/alacritty/src/event.rs b/alacritty/src/event.rs
index 5276776a..ac8474be 100644
--- a/alacritty/src/event.rs
+++ b/alacritty/src/event.rs
@@ -18,13 +18,12 @@ use crossfont::Size as FontSize;
use glutin::display::{Display as GlutinDisplay, GetGlDisplay};
use log::{debug, error, info, warn};
use raw_window_handle::HasRawDisplayHandle;
+use winit::application::ApplicationHandler;
use winit::event::{
ElementState, Event as WinitEvent, Ime, Modifiers, MouseButton, StartCause,
Touch as TouchEvent, WindowEvent,
};
-use winit::event_loop::{
- ControlFlow, DeviceEvents, EventLoop, EventLoopProxy, EventLoopWindowTarget,
-};
+use winit::event_loop::{ActiveEventLoop, ControlFlow, DeviceEvents, EventLoop, EventLoopProxy};
use winit::window::WindowId;
use alacritty_terminal::event::{Event as TerminalEvent, EventListener, Notify};
@@ -49,7 +48,7 @@ use crate::display::hint::HintMatch;
use crate::display::window::Window;
use crate::display::{Display, Preedit, SizeInfo};
use crate::input::{self, ActionContext as _, FONT_SIZE_STEP};
-use crate::logging::LOG_TARGET_CONFIG;
+use crate::logging::{LOG_TARGET_CONFIG, LOG_TARGET_WINIT};
use crate::message_bar::{Message, MessageBuffer};
use crate::scheduler::{Scheduler, TimerId, Topic};
use crate::window_context::WindowContext;
@@ -66,6 +65,372 @@ const MAX_SEARCH_HISTORY_SIZE: usize = 255;
/// Touch zoom speed.
const TOUCH_ZOOM_FACTOR: f32 = 0.01;
+/// The event processor.
+///
+/// Stores some state from received events and dispatches actions when they are
+/// triggered.
+pub struct Processor {
+ clipboard: Clipboard,
+ scheduler: Scheduler,
+ initial_window_options: Option<WindowOptions>,
+ initial_window_error: Option<Box<dyn Error>>,
+ windows: HashMap<WindowId, WindowContext, RandomState>,
+ proxy: EventLoopProxy<Event>,
+ gl_display: Option<GlutinDisplay>,
+ #[cfg(unix)]
+ global_ipc_options: ParsedOptions,
+ cli_options: CliOptions,
+ config: Rc<UiConfig>,
+}
+
+impl Processor {
+ /// Create a new event processor.
+ pub fn new(
+ config: UiConfig,
+ cli_options: CliOptions,
+ event_loop: &EventLoop<Event>,
+ ) -> Processor {
+ let proxy = event_loop.create_proxy();
+ let scheduler = Scheduler::new(proxy.clone());
+ let initial_window_options = Some(cli_options.window_options.clone());
+
+ // Disable all device events, since we don't care about them.
+ event_loop.listen_device_events(DeviceEvents::Never);
+
+ // SAFETY: Since this takes a pointer to the winit event loop, it MUST be dropped first,
+ // which is done in `loop_exiting`.
+ let clipboard = unsafe { Clipboard::new(event_loop.raw_display_handle()) };
+
+ Processor {
+ initial_window_options,
+ initial_window_error: None,
+ cli_options,
+ proxy,
+ scheduler,
+ gl_display: None,
+ config: Rc::new(config),
+ clipboard,
+ windows: Default::default(),
+ #[cfg(unix)]
+ global_ipc_options: Default::default(),
+ }
+ }
+
+ /// Create initial window and load GL platform.
+ ///
+ /// This will initialize the OpenGL Api and pick a config that
+ /// will be used for the rest of the windows.
+ pub fn create_initial_window(
+ &mut self,
+ event_loop: &ActiveEventLoop,
+ options: WindowOptions,
+ ) -> Result<(), Box<dyn Error>> {
+ let window_context =
+ WindowContext::initial(event_loop, self.proxy.clone(), self.config.clone(), options)?;
+
+ self.gl_display = Some(window_context.display.gl_context().display());
+ self.windows.insert(window_context.id(), window_context);
+
+ Ok(())
+ }
+
+ /// Create a new terminal window.
+ pub fn create_window(
+ &mut self,
+ event_loop: &ActiveEventLoop,
+ options: WindowOptions,
+ ) -> Result<(), Box<dyn Error>> {
+ let window = self.windows.iter().next().as_ref().unwrap().1;
+
+ // Overide config with CLI/IPC options.
+ let mut config_overrides = options.config_overrides();
+ #[cfg(unix)]
+ config_overrides.extend_from_slice(&self.global_ipc_options);
+ let mut config = self.config.clone();
+ config = config_overrides.override_config_rc(config);
+
+ #[allow(unused_mut)]
+ let mut window_context =
+ window.additional(event_loop, self.proxy.clone(), config, options, config_overrides)?;
+
+ self.windows.insert(window_context.id(), window_context);
+ Ok(())
+ }
+
+ /// Run the event loop.
+ ///
+ /// The result is exit code generate from the loop.
+ pub fn run(mut self, event_loop: EventLoop<Event>) -> Result<(), Box<dyn Error>> {
+ let result = event_loop.run_app(&mut self);
+ if let Some(initial_window_error) = self.initial_window_error.take() {
+ Err(initial_window_error)
+ } else {
+ result.map_err(Into::into)
+ }
+ }
+
+ /// Check if an event is irrelevant and can be skipped.
+ fn skip_window_event(event: &WindowEvent) -> bool {
+ matches!(
+ event,
+ WindowEvent::KeyboardInput { is_synthetic: true, .. }
+ | WindowEvent::ActivationTokenDone { .. }
+ | WindowEvent::DoubleTapGesture { .. }
+ | WindowEvent::TouchpadPressure { .. }
+ | WindowEvent::RotationGesture { .. }
+ | WindowEvent::CursorEntered { .. }
+ | WindowEvent::PinchGesture { .. }
+ | WindowEvent::AxisMotion { .. }
+ | WindowEvent::PanGesture { .. }
+ | WindowEvent::HoveredFileCancelled
+ | WindowEvent::Destroyed
+ | WindowEvent::ThemeChanged(_)
+ | WindowEvent::HoveredFile(_)
+ | WindowEvent::Moved(_)
+ )
+ }
+}
+
+impl ApplicationHandler<Event> for Processor {
+ fn resumed(&mut self, _event_loop: &ActiveEventLoop) {}
+
+ fn new_events(&mut self, event_loop: &ActiveEventLoop, cause: StartCause) {
+ if cause != StartCause::Init {
+ return;
+ }
+
+ let initial_window_options = match self.initial_window_options.take() {
+ Some(initial_window_options) => initial_window_options,
+ None => return,
+ };
+
+ if let Err(err) = self.create_initial_window(event_loop, initial_window_options) {
+ self.initial_window_error = Some(err);
+ event_loop.exit();
+ return;
+ }
+
+ info!("Initialisation complete");
+ }
+
+ fn window_event(
+ &mut self,
+ _event_loop: &ActiveEventLoop,
+ window_id: WindowId,
+ event: WindowEvent,
+ ) {
+ if self.config.debug.print_events {
+ info!(target: LOG_TARGET_WINIT, "{event:?}");
+ }
+
+ // Ignore all events we do not care about.
+ if Self::skip_window_event(&event) {
+ return;
+ }
+
+ let window_context = match self.windows.get_mut(&window_id) {
+ Some(window_context) => window_context,
+ None => return,
+ };
+
+ let is_redraw = matches!(event, WindowEvent::RedrawRequested);
+
+ window_context.handle_event(
+ #[cfg(target_os = "macos")]
+ _event_loop,
+ &self.proxy,
+ &mut self.clipboard,
+ &mut self.scheduler,
+ WinitEvent::WindowEvent { window_id, event },
+ );
+
+ if is_redraw {
+ window_context.draw(&mut self.scheduler);
+ }
+ }
+
+ fn user_event(&mut self, event_loop: &ActiveEventLoop, event: Event) {
+ if self.config.debug.print_events {
+ info!(target: LOG_TARGET_WINIT, "{event:?}");
+ }
+
+ // Handle events which don't mandate the WindowId.
+ match &event.payload {
+ // Process IPC config update.
+ #[cfg(unix)]
+ EventType::IpcConfig(ipc_config) => {
+ // Try and parse options as toml.
+ let mut options = ParsedOptions::from_options(&ipc_config.options);
+
+ // Override IPC config for each window with matching ID.
+ for (_, window_context) in self
+ .windows
+ .iter_mut()
+ .filter(|(id, _)| event.window_id.is_none() || event.window_id == Some(**id))
+ {
+ if ipc_config.reset {
+ window_context.reset_window_config(self.config.clone());
+ } else {
+ window_context.add_window_config(self.config.clone(), &options);
+ }
+ }
+
+ // Persist global options for future windows.
+ if event.window_id.is_none() {
+ if ipc_config.reset {
+ self.global_ipc_options.clear();
+ } else {
+ self.global_ipc_options.append(&mut options);
+ }
+ }
+ },
+ EventType::ConfigReload(path) => {
+ // Clear config logs from message bar for all terminals.
+ for window_context in self.windows.values_mut() {
+ if !window_context.message_buffer.is_empty() {
+ window_context.message_buffer.remove_target(LOG_TARGET_CONFIG);
+ window_context.display.pending_update.dirty = true;
+ }
+ }
+
+ // Load config and update each terminal.
+ if let Ok(config) = config::reload(path, &mut self.cli_options) {
+ self.config = Rc::new(config);
+
+ for window_context in self.windows.values_mut() {
+ window_context.update_config(self.config.clone());
+ }
+ }
+ },
+ // Create a new terminal window.
+ EventType::CreateWindow(options) => {
+ // XXX Ensure that no context is current when creating a new window,
+ // otherwise it may lock the backing buffer of the
+ // surface of current context when asking
+ // e.g. EGL on Wayland to create a new context.
+ for window_context in self.windows.values_mut() {
+ window_context.display.make_not_current();
+ }
+
+ if let Err(err) = self.create_window(event_loop, options.clone()) {
+ error!("Could not open window: {:?}", err);
+ }
+ },
+ _ => (),
+ };
+
+ let window_id = match event.window_id {
+ Some(window_id) => window_id,
+ None => return,
+ };
+
+ // Handle the rest of events which require WindowId.
+ match event.payload {
+ EventType::Terminal(TerminalEvent::Wakeup) => {
+ if let Some(window_context) = self.windows.get_mut(&window_id) {
+ window_context.dirty = true;
+ if window_context.display.window.has_frame {
+ window_context.display.window.request_redraw();
+ }
+ }
+ },
+ EventType::Terminal(TerminalEvent::Exit) => {
+ // Remove the closed terminal.
+ let window_context = match self.windows.remove(&window_id) {
+ Some(window_context) => window_context,
+ None => return,
+ };
+
+ // Unschedule pending events.
+ self.scheduler.unschedule_window(window_context.id());
+
+ // Shutdown if no more terminals are open.
+ if self.windows.is_empty() {
+ // Write ref tests of last window to disk.
+ if self.config.debug.ref_test {
+ window_context.write_ref_test_results();
+ }
+
+ event_loop.exit();
+ }
+ },
+ // NOTE: This event bypasses batching to minimize input latency.
+ EventType::Frame => {
+ if let Some(window_context) = self.windows.get_mut(&window_id) {
+ window_context.display.window.has_frame = true;
+ if window_context.dirty {
+ window_context.display.window.request_redraw();
+ }
+ }
+ },
+ _ => {
+ if let Some(window_context) = self.windows.get_mut(&window_id) {
+ window_context.handle_event(
+ #[cfg(target_os = "macos")]
+ event_loop,
+ &self.proxy,
+ &mut self.clipboard,
+ &mut self.scheduler,
+ WinitEvent::UserEvent(event),
+ );
+ }
+ },
+ }
+ }
+
+ fn about_to_wait(&mut self, event_loop: &ActiveEventLoop) {
+ if self.config.debug.print_events {
+ info!(target: LOG_TARGET_WINIT, "About to wait");
+ }
+
+ // Dispatch event to all windows.
+ for window_context in self.windows.values_mut() {
+ window_context.handle_event(
+ #[cfg(target_os = "macos")]
+ event_loop,
+ &self.proxy,
+ &mut self.clipboard,
+ &mut self.scheduler,
+ WinitEvent::AboutToWait,
+ );
+ }
+
+ // Update the scheduler after event processing to ensure
+ // the event loop deadline is as accurate as possible.
+ let control_flow = match self.scheduler.update() {
+ Some(instant) => ControlFlow::WaitUntil(instant),
+ None => ControlFlow::Wait,
+ };
+ event_loop.set_control_flow(control_flow);
+ }
+
+ fn exiting(&mut self, _event_loop: &ActiveEventLoop) {
+ if self.config.debug.print_events {
+ info!("Exiting the event loop");
+ }
+
+ match self.gl_display.take() {
+ #[cfg(not(target_os = "macos"))]
+ Some(glutin::display::Display::Egl(display)) => {
+ // Ensure that all the windows are dropped, so the destructors for
+ // Renderer and contexts ran.
+ self.windows.clear();
+
+ // SAFETY: the display is being destroyed after destroying all the
+ // windows, thus no attempt to access the EGL state will be made.
+ unsafe {
+ display.terminate();
+ }
+ },
+ _ => (),
+ }
+
+ // SAFETY: The clipboard must be dropped before the event loop, so use the nop clipboard
+ // as a safe placeholder.
+ mem::swap(&mut self.clipboard, &mut Clipboard::new_nop());
+ }
+}
+
/// Alacritty events.
#[derive(Debug, Clone)]
pub struct Event {
@@ -218,7 +583,7 @@ pub struct ActionContext<'a, N, T> {
pub config: &'a UiConfig,
pub cursor_blink_timed_out: &'a mut bool,
#[cfg(target_os = "macos")]
- pub event_loop: &'a EventLoopWindowTarget<Event>,
+ pub event_loop: &'a ActiveEventLoop,
pub event_proxy: &'a EventLoopProxy<Event>,
pub scheduler: &'a mut Scheduler,
pub search_state: &'a mut SearchState,
@@ -919,7 +1284,7 @@ impl<'a, N: Notify + 'a, T: EventListener> input::ActionContext<T> for ActionCon
}
#[cfg(target_os = "macos")]
- fn event_loop(&self) -> &EventLoopWindowTarget<Event> {
+ fn event_loop(&self) -> &ActiveEventLoop {
self.event_loop
}
@@ -1468,12 +1833,13 @@ impl input::Processor<EventProxy, ActionContext<'_, Notifier, EventProxy>> {
},
WindowEvent::KeyboardInput { is_synthetic: true, .. }
| WindowEvent::ActivationTokenDone { .. }
+ | WindowEvent::DoubleTapGesture { .. }
| WindowEvent::TouchpadPressure { .. }
- | WindowEvent::TouchpadMagnify { .. }
- | WindowEvent::TouchpadRotate { .. }
- | WindowEvent::SmartMagnify { .. }
+ | WindowEvent::RotationGesture { .. }
| WindowEvent::CursorEntered { .. }
+ | WindowEvent::PinchGesture { .. }
| WindowEvent::AxisMotion { .. }
+ | WindowEvent::PanGesture { .. }
| WindowEvent::HoveredFileCancelled
| WindowEvent::Destroyed
| WindowEvent::ThemeChanged(_)
@@ -1493,363 +1859,6 @@ impl input::Processor<EventProxy, ActionContext<'_, Notifier, EventProxy>> {
}
}
-/// The event processor.
-///
-/// Stores some state from received events and dispatches actions when they are
-/// triggered.
-pub struct Processor {
- windows: HashMap<WindowId, WindowContext, RandomState>,
- gl_display: Option<GlutinDisplay>,
- #[cfg(unix)]
- global_ipc_options: ParsedOptions,
- cli_options: CliOptions,
- config: Rc<UiConfig>,
-}
-
-impl Processor {
- /// Create a new event processor.
- ///
- /// Takes a writer which is expected to be hooked up to the write end of a PTY.
- pub fn new(
- config: UiConfig,
- cli_options: CliOptions,
- _event_loop: &EventLoop<Event>,
- ) -> Processor {
- Processor {
- cli_options,
- gl_display: None,
- config: Rc::new(config),
- windows: Default::default(),
- #[cfg(unix)]
- global_ipc_options: Default::default(),
- }
- }
-
- /// Create initial window and load GL platform.
- ///
- /// This will initialize the OpenGL Api and pick a config that
- /// will be used for the rest of the windows.
- pub fn create_initial_window(
- &mut self,
- event_loop: &EventLoopWindowTarget<Event>,
- proxy: EventLoopProxy<Event>,
- options: WindowOptions,
- ) -> Result<(), Box<dyn Error>> {
- let window_context =
- WindowContext::initial(event_loop, proxy, self.config.clone(), options)?;
-
- self.gl_display = Some(window_context.display.gl_context().display());
- self.windows.insert(window_context.id(), window_context);
-
- Ok(())
- }
-
- /// Create a new terminal window.
- pub fn create_window(
- &mut self,
- event_loop: &EventLoopWindowTarget<Event>,
- proxy: EventLoopProxy<Event>,
- options: WindowOptions,
- ) -> Result<(), Box<dyn Error>> {
- let window = self.windows.iter().next().as_ref().unwrap().1;
-
- // Overide config with CLI/IPC options.
- let mut config_overrides = options.config_overrides();
- #[cfg(unix)]
- config_overrides.extend_from_slice(&self.global_ipc_options);
- let mut config = self.config.clone();
- config = config_overrides.override_config_rc(config);
-
- #[allow(unused_mut)]
- let mut window_context =
- window.additional(event_loop, proxy, config, options, config_overrides)?;
-
- self.windows.insert(window_context.id(), window_context);
- Ok(())
- }
-
- /// Run the event loop.
- ///
- /// The result is exit code generate from the loop.
- pub fn run(
- &mut self,
- event_loop: EventLoop<Event>,
- initial_window_options: WindowOptions,
- ) -> Result<(), Box<dyn Error>> {
- let proxy = event_loop.create_proxy();
- let mut scheduler = Scheduler::new(proxy.clone());
- let mut initial_window_options = Some(initial_window_options);
-
- // Disable all device events, since we don't care about them.
- event_loop.listen_device_events(DeviceEvents::Never);
-
- let mut initial_window_error = Ok(());
- let initial_window_error_loop = &mut initial_window_error;
- // SAFETY: Since this takes a pointer to the winit event loop, it MUST be dropped first,
- // which is done by `move` into event loop.
- let mut clipboard = unsafe { Clipboard::new(event_loop.raw_display_handle()) };
- let result = event_loop.run(move |event, event_loop| {
- if self.config.debug.print_events {
- info!("winit event: {:?}", event);
- }
-
- // Ignore all events we do not care about.
- if Self::skip_event(&event) {
- return;
- }
-
- match event {
- // The event loop just got initialized. Create a window.
- WinitEvent::Resumed => {
- // Creating window inside event loop is required for platforms like macOS to
- // properly initialize state, like tab management. Otherwise the first
- // window won't handle tabs.
- let initial_window_options = match initial_window_options.take() {
- Some(initial_window_options) => initial_window_options,
- None => return,
- };
-
- if let Err(err) = self.create_initial_window(
- event_loop,
- proxy.clone(),
- initial_window_options,
- ) {
- *initial_window_error_loop = Err(err);
- event_loop.exit();
- return;
- }
-
- info!("Initialisation complete");
- },
- WinitEvent::LoopExiting => {
- match self.gl_display.take() {
- #[cfg(not(target_os = "macos"))]
- Some(glutin::display::Display::Egl(display)) => {
- // Ensure that all the windows are dropped, so the destructors for
- // Renderer and contexts ran.
- self.windows.clear();
-
- // SAFETY: the display is being destroyed after destroying all the
- // windows, thus no attempt to access the EGL state will be made.
- unsafe {
- display.terminate();
- }
- },
- _ => (),
- }
- },
- // NOTE: This event bypasses batching to minimize input latency.
- WinitEvent::UserEvent(Event {
- window_id: Some(window_id),
- payload: EventType::Terminal(TerminalEvent::Wakeup),
- }) => {
- if let Some(window_context) = self.windows.get_mut(&window_id) {
- window_context.dirty = true;
- if window_context.display.window.has_frame {
- window_context.display.window.request_redraw();
- }
- }
- },
- // NOTE: This event bypasses batching to minimize input latency.
- WinitEvent::UserEvent(Event {
- window_id: Some(window_id),
- payload: EventType::Frame,
- }) => {
- if let Some(window_context) = self.windows.get_mut(&window_id) {
- window_context.display.window.has_frame = true;
- if window_context.dirty {
- window_context.display.window.request_redraw();
- }
- }
- },
- // Check for shutdown.
- WinitEvent::UserEvent(Event {
- window_id: Some(window_id),
- payload: EventType::Terminal(TerminalEvent::Exit),
- }) => {
- // Remove the closed terminal.
- let window_context = match self.windows.remove(&window_id) {
- Some(window_context) => window_context,
- None => return,
- };
-
- // Unschedule pending events.
- scheduler.unschedule_window(window_context.id());
-
- // Shutdown if no more terminals are open.
- if self.windows.is_empty() {
- // Write ref tests of last window to disk.
- if self.config.debug.ref_test {
- window_context.write_ref_test_results();
- }
-
- event_loop.exit();
- }
- },
- WinitEvent::WindowEvent { window_id, event: WindowEvent::RedrawRequested } => {
- let window_context = match self.windows.get_mut(&window_id) {
- Some(window_context) => window_context,
- None => return,
- };
-
- window_context.handle_event(
- #[cfg(target_os = "macos")]
- event_loop,
- &proxy,
- &mut clipboard,
- &mut scheduler,
- event,
- );
-
- window_context.draw(&mut scheduler);
- },
- // Process all pending events.
- WinitEvent::AboutToWait => {
- // Dispatch event to all windows.
- for window_context in self.windows.values_mut() {
- window_context.handle_event(
- #[cfg(target_os = "macos")]
- event_loop,
- &proxy,
- &mut clipboard,
- &mut scheduler,
- WinitEvent::AboutToWait,
- );
- }
-
- // Update the scheduler after event processing to ensure
- // the event loop deadline is as accurate as possible.
- let control_flow = match scheduler.update() {
- Some(instant) => ControlFlow::WaitUntil(instant),
- None => ControlFlow::Wait,
- };
- event_loop.set_control_flow(control_flow);
- },
- // Process config update.
- WinitEvent::UserEvent(Event { payload: EventType::ConfigReload(path), .. }) => {
- // Clear config logs from message bar for all terminals.
- for window_context in self.windows.values_mut() {
- if !window_context.message_buffer.is_empty() {
- window_context.message_buffer.remove_target(LOG_TARGET_CONFIG);
- window_context.display.pending_update.dirty = true;
- }
- }
-
- // Load config and update each terminal.
- if let Ok(config) = config::reload(&path, &mut self.cli_options) {
- self.config = Rc::new(config);
-
- for window_context in self.windows.values_mut() {
- window_context.update_config(self.config.clone());
- }
- }
- },
- // Process IPC config update.
- #[cfg(unix)]
- WinitEvent::UserEvent(Event {
- payload: EventType::IpcConfig(ipc_config),
- window_id,
- }) => {
- // Try and parse options as toml.
- let mut options = ParsedOptions::from_options(&ipc_config.options);
-
- // Override IPC config for each window with matching ID.
- for (_, window_context) in self
- .windows
- .iter_mut()
- .filter(|(id, _)| window_id.is_none() || window_id == Some(**id))
- {
- if ipc_config.reset {
- window_context.reset_window_config(self.config.clone());
- } else {
- window_context.add_window_config(self.config.clone(), &options);
- }
- }
-
- // Persist global options for future windows.
- if window_id.is_none() {
- if ipc_config.reset {
- self.global_ipc_options.clear();
- } else {
- self.global_ipc_options.append(&mut options);
- }
- }
- },
- // Create a new terminal window.
- WinitEvent::UserEvent(Event {
- payload: EventType::CreateWindow(options), ..
- }) => {
- // XXX Ensure that no context is current when creating a new window,
- // otherwise it may lock the backing buffer of the
- // surface of current context when asking
- // e.g. EGL on Wayland to create a new context.
- for window_context in self.windows.values_mut() {
- window_context.display.make_not_current();
- }
-
- if let Err(err) = self.create_window(event_loop, proxy.clone(), options) {
- error!("Could not open window: {:?}", err);
- }
- },
- // Process events affecting all windows.
- WinitEvent::UserEvent(event @ Event { window_id: None, .. }) => {
- for window_context in self.windows.values_mut() {
- window_context.handle_event(
- #[cfg(target_os = "macos")]
- event_loop,
- &proxy,
- &mut clipboard,
- &mut scheduler,
- event.clone().into(),
- );
- }
- },
- // Process window-specific events.
- WinitEvent::WindowEvent { window_id, .. }
- | WinitEvent::UserEvent(Event { window_id: Some(window_id), .. }) => {
- if let Some(window_context) = self.windows.get_mut(&window_id) {
- window_context.handle_event(
- #[cfg(target_os = "macos")]
- event_loop,
- &proxy,
- &mut clipboard,
- &mut scheduler,
- event,
- );
- }
- },
- _ => (),
- }
- });
-
- if initial_window_error.is_err() {
- initial_window_error
- } else {
- result.map_err(Into::into)
- }
- }
-
- /// Check if an event is irrelevant and can be skipped.
- fn skip_event(event: &WinitEvent<Event>) -> bool {
- match event {
- WinitEvent::NewEvents(StartCause::Init) => false,
- WinitEvent::WindowEvent { event, .. } => matches!(
- event,
- WindowEvent::KeyboardInput { is_synthetic: true, .. }
- | WindowEvent::TouchpadPressure { .. }
- | WindowEvent::CursorEntered { .. }
- | WindowEvent::AxisMotion { .. }
- | WindowEvent::HoveredFileCancelled
- | WindowEvent::Destroyed
- | WindowEvent::HoveredFile(_)
- | WindowEvent::Moved(_)
- ),
- WinitEvent::Suspended { .. } | WinitEvent::NewEvents { .. } => true,
- _ => false,
- }
- }
-}
-
#[derive(Debug, Clone)]
pub struct EventProxy {
proxy: EventLoopProxy<Event>,
diff --git a/alacritty/src/input/mod.rs b/alacritty/src/input/mod.rs
index 365717c3..095e8737 100644
--- a/alacritty/src/input/mod.rs
+++ b/alacritty/src/input/mod.rs
@@ -20,10 +20,10 @@ use winit::event::{
ElementState, Modifiers, MouseButton, MouseScrollDelta, Touch as TouchEvent, TouchPhase,
};
#[cfg(target_os = "macos")]
-use winit::event_loop::EventLoopWindowTarget;
+use winit::event_loop::ActiveEventLoop;
use winit::keyboard::ModifiersState;
#[cfg(target_os = "macos")]
-use winit::platform::macos::EventLoopWindowTargetExtMacOS;
+use winit::platform::macos::ActiveEventLoopExtMacOS;
use winit::window::CursorIcon;
use alacritty_terminal::event::EventListener;
@@ -107,7 +107,7 @@ pub trait ActionContext<T: EventListener> {
fn message(&self) -> Option<&Message>;
fn config(&self) -> &UiConfig;
#[cfg(target_os = "macos")]
- fn event_loop(&self) -> &EventLoopWindowTarget<Event>;
+ fn event_loop(&self) -> &ActiveEventLoop;
fn mouse_mode(&self) -> bool;
fn clipboard_mut(&mut self) -> &mut Clipboard;
fn scheduler_mut(&mut self) -> &mut Scheduler;
@@ -1224,7 +1224,7 @@ mod tests {
}
#[cfg(target_os = "macos")]
- fn event_loop(&self) -> &EventLoopWindowTarget<Event> {
+ fn event_loop(&self) -> &ActiveEventLoop {
unimplemented!();
}
diff --git a/alacritty/src/logging.rs b/alacritty/src/logging.rs
index a3833a5b..59303649 100644
--- a/alacritty/src/logging.rs
+++ b/alacritty/src/logging.rs
@@ -28,6 +28,9 @@ const ALACRITTY_LOG_ENV: &str = "ALACRITTY_LOG";
/// Logging target for config error messages.
pub const LOG_TARGET_CONFIG: &str = "alacritty_config_derive";
+/// Logging target for winit events.
+pub const LOG_TARGET_WINIT: &str = "alacritty_winit_event";
+
/// Name for the environment variable containing extra logging targets.
///
/// The targets are semicolon separated.
@@ -47,6 +50,7 @@ fn extra_log_targets() -> &'static [String] {
const ALLOWED_TARGETS: &[&str] = &[
LOG_TARGET_IPC_CONFIG,
LOG_TARGET_CONFIG,
+ LOG_TARGET_WINIT,
"alacritty_config_derive",
"alacritty_terminal",
"alacritty",
diff --git a/alacritty/src/main.rs b/alacritty/src/main.rs
index f9301767..2951c224 100644
--- a/alacritty/src/main.rs
+++ b/alacritty/src/main.rs
@@ -19,11 +19,11 @@ use std::path::PathBuf;
use std::{env, fs};
use log::info;
+#[cfg(all(feature = "x11", not(any(target_os = "macos", windows))))]
+use raw_window_handle::{HasRawDisplayHandle, RawDisplayHandle};
#[cfg(windows)]
use windows_sys::Win32::System::Console::{AttachConsole, FreeConsole, ATTACH_PARENT_PROCESS};
-use winit::event_loop::EventLoopBuilder as WinitEventLoopBuilder;
-#[cfg(all(feature = "x11", not(any(target_os = "macos", windows))))]
-use winit::platform::x11::EventLoopWindowTargetExtX11;
+use winit::event_loop::EventLoop;
use alacritty_terminal::tty;
@@ -125,7 +125,7 @@ impl Drop for TemporaryFiles {
/// config change monitor, and runs the main display loop.
fn alacritty(mut options: Options) -> Result<(), Box<dyn Error>> {
// Setup winit event loop.
- let window_event_loop = WinitEventLoopBuilder::<Event>::with_user_event().build()?;
+ let window_event_loop = EventLoop::<Event>::with_user_event().build()?;
// Initialize the logger as soon as possible as to capture output from other subsystems.
let log_file = logging::initialize(&options, window_event_loop.create_proxy())
@@ -135,7 +135,14 @@ fn alacritty(mut options: Options) -> Result<(), Box<dyn Error>> {
info!("Version {}", env!("VERSION"));
#[cfg(all(feature = "x11", not(any(target_os = "macos", windows))))]
- info!("Running on {}", if window_event_loop.is_x11() { "X11" } else { "Wayland" });
+ info!(
+ "Running on {}",
+ if matches!(window_event_loop.raw_display_handle(), RawDisplayHandle::Wayland(_)) {
+ "Wayland"
+ } else {
+ "X11"
+ }
+ );
#[cfg(not(any(feature = "x11", target_os = "macos", windows)))]
info!("Running on Wayland");
@@ -189,13 +196,14 @@ fn alacritty(mut options: Options) -> Result<(), Box<dyn Error>> {
};
// Event processor.
- let window_options = options.window_options.clone();
- let mut processor = Processor::new(config, options, &window_event_loop);
+ let processor = Processor::new(config, options, &window_event_loop);
// Start event loop and block until shutdown.
- let result = processor.run(window_event_loop, window_options);
+ let result = processor.run(window_event_loop);
- // This explicit drop is needed for Windows, ConPTY backend. Otherwise a deadlock can occur.
+ // `Processor` must be dropped before calling `FreeConsole`.
+ //
+ // This is needed for ConPTY backend. Otherwise a deadlock can occur.
// The cause:
// - Drop for ConPTY will deadlock if the conout pipe has already been dropped
// - ConPTY is dropped when the last of processor and window context are dropped, because both
@@ -206,7 +214,6 @@ fn alacritty(mut options: Options) -> Result<(), Box<dyn Error>> {
// order.
//
// FIXME: Change PTY API to enforce the correct drop order with the typesystem.
- drop(processor);
// Terminate the config monitor.
if let Some(config_monitor) = config_monitor.take() {
@@ -234,5 +241,5 @@ fn log_config_path(config: &UiConfig) {
let _ = write!(msg, "\n {:?}", path.display());
}
- info!("{}", msg);
+ info!("{msg}");
}
diff --git a/alacritty/src/window_context.rs b/alacritty/src/window_context.rs
index f0f24fe8..f5fb5cc5 100644
--- a/alacritty/src/window_context.rs
+++ b/alacritty/src/window_context.rs
@@ -17,7 +17,7 @@ use log::info;
use raw_window_handle::HasRawDisplayHandle;
use serde_json as json;
use winit::event::{Event as WinitEvent, Modifiers, WindowEvent};
-use winit::event_loop::{EventLoopProxy, EventLoopWindowTarget};
+use winit::event_loop::{ActiveEventLoop, EventLoopProxy};
use winit::window::WindowId;
use alacritty_terminal::event::Event as TerminalEvent;
@@ -70,7 +70,7 @@ pub struct WindowContext {
impl WindowContext {
/// Create initial window context that does bootstrapping the graphics API we're going to use.
pub fn initial(
- event_loop: &EventLoopWindowTarget<Event>,
+ event_loop: &ActiveEventLoop,
proxy: EventLoopProxy<Event>,
config: Rc<UiConfig>,
options: WindowOptions,
@@ -120,7 +120,7 @@ impl WindowContext {
/// Create additional context with the graphics platform other windows are using.
pub fn additional(
&self,
- event_loop: &EventLoopWindowTarget<Event>,
+ event_loop: &ActiveEventLoop,
proxy: EventLoopProxy<Event>,
config: Rc<UiConfig>,
options: WindowOptions,
@@ -399,7 +399,7 @@ impl WindowContext {
/// Process events for this terminal window.
pub fn handle_event(
&mut self,
- #[cfg(target_os = "macos")] event_loop: &EventLoopWindowTarget<Event>,
+ #[cfg(target_os = "macos")] event_loop: &ActiveEventLoop,
event_proxy: &EventLoopProxy<Event>,
clipboard: &mut Clipboard,
scheduler: &mut Scheduler,