diff options
author | Christian Duerr <contact@christianduerr.com> | 2019-10-05 02:29:26 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-10-05 02:29:26 +0200 |
commit | 729eef0c933831bccfeac6a355bdb410787fbe5f (patch) | |
tree | 35cdf2e6427ad18bc53efbab4cab34a0af2054d7 /alacritty_terminal/src/event.rs | |
parent | b0c6fdff763f7271506d26d7e768e6377fdc691b (diff) | |
download | alacritty-729eef0c933831bccfeac6a355bdb410787fbe5f.tar.gz alacritty-729eef0c933831bccfeac6a355bdb410787fbe5f.zip |
Update to winit/glutin EventLoop 2.0
This takes the latest glutin master to port Alacritty to the EventLoop
2.0 rework.
This changes a big part of the event loop handling by pushing the event
loop in a separate thread from the renderer and running both in
parallel.
Fixes #2796.
Fixes #2694.
Fixes #2643.
Fixes #2625.
Fixes #2618.
Fixes #2601.
Fixes #2564.
Fixes #2456.
Fixes #2438.
Fixes #2334.
Fixes #2254.
Fixes #2217.
Fixes #1789.
Fixes #1750.
Fixes #1125.
Diffstat (limited to 'alacritty_terminal/src/event.rs')
-rw-r--r-- | alacritty_terminal/src/event.rs | 561 |
1 files changed, 19 insertions, 542 deletions
diff --git a/alacritty_terminal/src/event.rs b/alacritty_terminal/src/event.rs index 9d2aa78c..2d43e9dd 100644 --- a/alacritty_terminal/src/event.rs +++ b/alacritty_terminal/src/event.rs @@ -1,28 +1,19 @@ -//! Process window events use std::borrow::Cow; -use std::env; -#[cfg(unix)] -use std::fs; -use std::sync::mpsc; -use std::time::Instant; +use std::path::PathBuf; -use glutin::dpi::PhysicalSize; -use glutin::{self, ElementState, Event, MouseButton}; -use parking_lot::MutexGuard; +use crate::message_bar::Message; +use crate::term::SizeInfo; -use crate::clipboard::ClipboardType; -use crate::config::{self, Config, StartupMode}; -use crate::display::OnResize; -use crate::grid::Scroll; -use crate::index::{Column, Line, Point, Side}; -use crate::input::{self, KeyBinding, Modifiers, MouseBinding}; -use crate::selection::Selection; -use crate::sync::FairMutex; -use crate::term::{SizeInfo, Term}; -#[cfg(unix)] -use crate::tty; -use crate::util::{limit, start_daemon}; -use crate::window::Window; +#[derive(Clone, Debug, PartialEq)] +pub enum Event { + ConfigReload(PathBuf), + MouseCursorDirty, + Message(Message), + Title(String), + Wakeup, + Urgent, + Exit, +} /// Byte sequences are sent to a `Notify` in response to some events pub trait Notify { @@ -32,526 +23,12 @@ pub trait Notify { fn notify<B: Into<Cow<'static, [u8]>>>(&mut self, _: B); } -pub struct ActionContext<'a, N> { - pub notifier: &'a mut N, - pub terminal: &'a mut Term, - pub size_info: &'a mut SizeInfo, - pub mouse: &'a mut Mouse, - pub received_count: &'a mut usize, - pub suppress_chars: &'a mut bool, - pub modifiers: &'a mut Modifiers, - pub window_changes: &'a mut WindowChanges, +/// Types that are interested in when the display is resized +pub trait OnResize { + fn on_resize(&mut self, size: &SizeInfo); } -impl<'a, N: Notify + 'a> input::ActionContext for ActionContext<'a, N> { - fn write_to_pty<B: Into<Cow<'static, [u8]>>>(&mut self, val: B) { - self.notifier.notify(val); - } - - fn size_info(&self) -> SizeInfo { - *self.size_info - } - - fn scroll(&mut self, scroll: Scroll) { - self.terminal.scroll_display(scroll); - - if let ElementState::Pressed = self.mouse().left_button_state { - let (x, y) = (self.mouse().x, self.mouse().y); - let size_info = self.size_info(); - let point = size_info.pixels_to_coords(x, y); - let cell_side = self.mouse().cell_side; - self.update_selection(Point { line: point.line, col: point.col }, cell_side); - } - } - - fn copy_selection(&mut self, ty: ClipboardType) { - if let Some(selected) = self.terminal.selection_to_string() { - if !selected.is_empty() { - self.terminal.clipboard().store(ty, selected); - } - } - } - - fn selection_is_empty(&self) -> bool { - self.terminal.selection().as_ref().map(Selection::is_empty).unwrap_or(true) - } - - fn clear_selection(&mut self) { - *self.terminal.selection_mut() = None; - self.terminal.dirty = true; - } - - fn update_selection(&mut self, point: Point, side: Side) { - let point = self.terminal.visible_to_buffer(point); - - // Update selection if one exists - if let Some(ref mut selection) = self.terminal.selection_mut() { - selection.update(point, side); - } - - self.terminal.dirty = true; - } - - fn simple_selection(&mut self, point: Point, side: Side) { - let point = self.terminal.visible_to_buffer(point); - *self.terminal.selection_mut() = Some(Selection::simple(point, side)); - self.terminal.dirty = true; - } - - fn block_selection(&mut self, point: Point, side: Side) { - let point = self.terminal.visible_to_buffer(point); - *self.terminal.selection_mut() = Some(Selection::block(point, side)); - self.terminal.dirty = true; - } - - fn semantic_selection(&mut self, point: Point) { - let point = self.terminal.visible_to_buffer(point); - *self.terminal.selection_mut() = Some(Selection::semantic(point)); - self.terminal.dirty = true; - } - - fn line_selection(&mut self, point: Point) { - let point = self.terminal.visible_to_buffer(point); - *self.terminal.selection_mut() = Some(Selection::lines(point)); - self.terminal.dirty = true; - } - - fn mouse_coords(&self) -> Option<Point> { - self.terminal.pixels_to_coords(self.mouse.x as usize, self.mouse.y as usize) - } - - #[inline] - fn mouse_mut(&mut self) -> &mut Mouse { - self.mouse - } - - #[inline] - fn mouse(&self) -> &Mouse { - self.mouse - } - - #[inline] - fn received_count(&mut self) -> &mut usize { - &mut self.received_count - } - - #[inline] - fn suppress_chars(&mut self) -> &mut bool { - &mut self.suppress_chars - } - - #[inline] - fn modifiers(&mut self) -> &mut Modifiers { - &mut self.modifiers - } - - #[inline] - fn hide_window(&mut self) { - self.window_changes.hide = true; - } - - #[inline] - fn terminal(&self) -> &Term { - self.terminal - } - - #[inline] - fn terminal_mut(&mut self) -> &mut Term { - self.terminal - } - - fn spawn_new_instance(&mut self) { - let alacritty = env::args().next().unwrap(); - - #[cfg(unix)] - let args = { - #[cfg(not(target_os = "freebsd"))] - let proc_prefix = ""; - #[cfg(target_os = "freebsd")] - let proc_prefix = "/compat/linux"; - let link_path = format!("{}/proc/{}/cwd", proc_prefix, tty::child_pid()); - if let Ok(path) = fs::read_link(link_path) { - vec!["--working-directory".into(), path] - } else { - Vec::new() - } - }; - #[cfg(not(unix))] - let args: Vec<String> = Vec::new(); - - match start_daemon(&alacritty, &args) { - Ok(_) => debug!("Started new Alacritty process: {} {:?}", alacritty, args), - Err(_) => warn!("Unable to start new Alacritty process: {} {:?}", alacritty, args), - } - } - - fn toggle_fullscreen(&mut self) { - self.window_changes.toggle_fullscreen(); - } - - #[cfg(target_os = "macos")] - fn toggle_simple_fullscreen(&mut self) { - self.window_changes.toggle_simple_fullscreen() - } -} - -/// The ActionContext can't really have direct access to the Window -/// with the current design. Event handlers that want to change the -/// window must set these flags instead. The processor will trigger -/// the actual changes. -#[derive(Default)] -pub struct WindowChanges { - pub hide: bool, - pub toggle_fullscreen: bool, - #[cfg(target_os = "macos")] - pub toggle_simple_fullscreen: bool, -} - -impl WindowChanges { - fn clear(&mut self) { - *self = WindowChanges::default(); - } - - fn toggle_fullscreen(&mut self) { - self.toggle_fullscreen = !self.toggle_fullscreen; - } - - #[cfg(target_os = "macos")] - fn toggle_simple_fullscreen(&mut self) { - self.toggle_simple_fullscreen = !self.toggle_simple_fullscreen; - } -} - -pub enum ClickState { - None, - Click, - DoubleClick, - TripleClick, -} - -/// State of the mouse -pub struct Mouse { - pub x: usize, - pub y: usize, - pub left_button_state: ElementState, - pub middle_button_state: ElementState, - pub right_button_state: ElementState, - pub last_click_timestamp: Instant, - pub click_state: ClickState, - pub scroll_px: i32, - pub line: Line, - pub column: Column, - pub cell_side: Side, - pub lines_scrolled: f32, - pub block_url_launcher: bool, - pub last_button: MouseButton, -} - -impl Default for Mouse { - fn default() -> Mouse { - Mouse { - x: 0, - y: 0, - last_click_timestamp: Instant::now(), - left_button_state: ElementState::Released, - middle_button_state: ElementState::Released, - right_button_state: ElementState::Released, - click_state: ClickState::None, - scroll_px: 0, - line: Line(0), - column: Column(0), - cell_side: Side::Left, - lines_scrolled: 0.0, - block_url_launcher: false, - last_button: MouseButton::Other(0), - } - } -} - -/// The event processor -/// -/// Stores some state from received events and dispatches actions when they are -/// triggered. -pub struct Processor<N> { - key_bindings: Vec<KeyBinding>, - mouse_bindings: Vec<MouseBinding>, - mouse_config: config::Mouse, - scrolling_config: config::Scrolling, - print_events: bool, - wait_for_event: bool, - notifier: N, - mouse: Mouse, - resize_tx: mpsc::Sender<PhysicalSize>, - size_info: SizeInfo, - hide_mouse_when_typing: bool, - hide_mouse: bool, - received_count: usize, - suppress_chars: bool, - modifiers: Modifiers, - pending_events: Vec<Event>, - window_changes: WindowChanges, - save_to_clipboard: bool, - alt_send_esc: bool, - is_fullscreen: bool, - is_simple_fullscreen: bool, -} - -/// Notify that the terminal was resized -/// -/// Currently this just forwards the notice to the input processor. -impl<N> OnResize for Processor<N> { - fn on_resize(&mut self, size: &SizeInfo) { - self.size_info = size.to_owned(); - } -} - -impl<N: Notify> Processor<N> { - /// 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( - notifier: N, - resize_tx: mpsc::Sender<PhysicalSize>, - config: &Config, - size_info: SizeInfo, - ) -> Processor<N> { - Processor { - key_bindings: config.key_bindings.to_vec(), - mouse_bindings: config.mouse_bindings.to_vec(), - mouse_config: config.mouse.to_owned(), - scrolling_config: config.scrolling, - print_events: config.debug.print_events, - wait_for_event: true, - notifier, - resize_tx, - mouse: Default::default(), - size_info, - hide_mouse_when_typing: config.mouse.hide_when_typing, - hide_mouse: false, - received_count: 0, - suppress_chars: false, - modifiers: Default::default(), - pending_events: Vec::with_capacity(4), - window_changes: Default::default(), - save_to_clipboard: config.selection.save_to_clipboard, - alt_send_esc: config.alt_send_esc(), - is_fullscreen: config.window.startup_mode() == StartupMode::Fullscreen, - #[cfg(target_os = "macos")] - is_simple_fullscreen: config.window.startup_mode() == StartupMode::SimpleFullscreen, - #[cfg(not(target_os = "macos"))] - is_simple_fullscreen: false, - } - } - - /// Handle events from glutin - /// - /// Doesn't take self mutably due to borrow checking. Kinda uggo but w/e. - fn handle_event<'a>( - processor: &mut input::Processor<'a, ActionContext<'a, N>>, - event: Event, - resize_tx: &mpsc::Sender<PhysicalSize>, - hide_mouse: &mut bool, - window_is_focused: &mut bool, - ) { - match event { - // Pass on device events - Event::DeviceEvent { .. } | Event::Suspended { .. } => (), - Event::WindowEvent { event, .. } => { - use glutin::WindowEvent::*; - match event { - CloseRequested => processor.ctx.terminal.exit(), - Resized(lsize) => { - // Resize events are emitted via glutin/winit with logical sizes - // However the terminal, window and renderer use physical sizes - // so a conversion must be done here - resize_tx - .send(lsize.to_physical(processor.ctx.size_info.dpr)) - .expect("send new size"); - processor.ctx.terminal.dirty = true; - }, - KeyboardInput { input, .. } => { - processor.process_key(input); - if input.state == ElementState::Pressed { - // Hide cursor while typing - *hide_mouse = true; - } - }, - ReceivedCharacter(c) => { - processor.received_char(c); - }, - MouseInput { state, button, modifiers, .. } => { - if !cfg!(target_os = "macos") || *window_is_focused { - *hide_mouse = false; - processor.mouse_input(state, button, modifiers); - processor.ctx.terminal.dirty = true; - } - }, - CursorMoved { position: lpos, modifiers, .. } => { - let (x, y) = lpos.to_physical(processor.ctx.size_info.dpr).into(); - let x: i32 = limit(x, 0, processor.ctx.size_info.width as i32); - let y: i32 = limit(y, 0, processor.ctx.size_info.height as i32); - - *hide_mouse = false; - processor.mouse_moved(x as usize, y as usize, modifiers); - }, - MouseWheel { delta, phase, modifiers, .. } => { - *hide_mouse = false; - processor.on_mouse_wheel(delta, phase, modifiers); - }, - Refresh => { - processor.ctx.terminal.dirty = true; - }, - Focused(is_focused) => { - *window_is_focused = is_focused; - - if is_focused { - processor.ctx.terminal.next_is_urgent = Some(false); - processor.ctx.terminal.dirty = true; - } else { - processor.ctx.terminal.dirty = true; - *hide_mouse = false; - } - - processor.on_focus_change(is_focused); - }, - DroppedFile(path) => { - use crate::input::ActionContext; - let path: String = path.to_string_lossy().into(); - processor.ctx.write_to_pty(path.into_bytes()); - }, - HiDpiFactorChanged(new_dpr) => { - processor.ctx.size_info.dpr = new_dpr; - processor.ctx.terminal.dirty = true; - }, - CursorLeft { .. } => processor.ctx.terminal.reset_url_highlight(), - _ => (), - } - }, - Event::Awakened => { - processor.ctx.terminal.dirty = true; - }, - } - } - - /// Process events. When `wait_for_event` is set, this method is guaranteed - /// to process at least one event. - pub fn process_events<'a>( - &mut self, - term: &'a FairMutex<Term>, - window: &mut Window, - ) -> MutexGuard<'a, Term> { - // Terminal is lazily initialized the first time an event is returned - // from the blocking WaitEventsIterator. Otherwise, the pty reader would - // be blocked the entire time we wait for input! - let mut terminal; - - self.pending_events.clear(); - - { - // Ditto on lazy initialization for context and processor. - let context; - let mut processor: input::Processor<'_, ActionContext<'_, N>>; - - let print_events = self.print_events; - - let resize_tx = &self.resize_tx; - - if self.wait_for_event { - // A Vec is used here since wait_events can potentially yield - // multiple events before the interrupt is handled. For example, - // Resize and Moved events. - let pending_events = &mut self.pending_events; - window.wait_events(|e| { - pending_events.push(e); - glutin::ControlFlow::Break - }); - } - - terminal = term.lock(); - - context = ActionContext { - terminal: &mut terminal, - notifier: &mut self.notifier, - mouse: &mut self.mouse, - size_info: &mut self.size_info, - received_count: &mut self.received_count, - suppress_chars: &mut self.suppress_chars, - modifiers: &mut self.modifiers, - window_changes: &mut self.window_changes, - }; - - processor = input::Processor { - ctx: context, - scrolling_config: &self.scrolling_config, - mouse_config: &self.mouse_config, - key_bindings: &self.key_bindings[..], - mouse_bindings: &self.mouse_bindings[..], - save_to_clipboard: self.save_to_clipboard, - alt_send_esc: self.alt_send_esc, - }; - - let mut window_is_focused = window.is_focused; - - // Scope needed to that hide_mouse isn't borrowed after the scope - // ends. - { - let hide_mouse = &mut self.hide_mouse; - let mut process = |event| { - if print_events { - info!("glutin event: {:?}", event); - } - Processor::handle_event( - &mut processor, - event, - resize_tx, - hide_mouse, - &mut window_is_focused, - ); - }; - - for event in self.pending_events.drain(..) { - process(event); - } - - window.poll_events(process); - } - - if self.hide_mouse_when_typing { - window.set_mouse_visible(!self.hide_mouse); - } - - window.is_focused = window_is_focused; - } - - if self.window_changes.hide { - window.hide(); - } - - #[cfg(target_os = "macos")] - { - if self.window_changes.toggle_simple_fullscreen && !self.is_fullscreen { - window.set_simple_fullscreen(!self.is_simple_fullscreen); - self.is_simple_fullscreen = !self.is_simple_fullscreen; - } - } - - if self.window_changes.toggle_fullscreen && !self.is_simple_fullscreen { - window.set_fullscreen(!self.is_fullscreen); - self.is_fullscreen = !self.is_fullscreen; - } - - self.window_changes.clear(); - self.wait_for_event = !terminal.dirty; - - terminal - } - - pub fn update_config(&mut self, config: &Config) { - self.key_bindings = config.key_bindings.to_vec(); - self.mouse_bindings = config.mouse_bindings.to_vec(); - self.mouse_config = config.mouse.to_owned(); - self.save_to_clipboard = config.selection.save_to_clipboard; - self.alt_send_esc = config.alt_send_esc(); - } +/// Event Loop for notifying the renderer about terminal events +pub trait EventListener { + fn send_event(&self, event: Event); } |