summaryrefslogtreecommitdiff
path: root/alacritty_terminal/src/event.rs
diff options
context:
space:
mode:
authorChristian Duerr <contact@christianduerr.com>2019-10-05 02:29:26 +0200
committerGitHub <noreply@github.com>2019-10-05 02:29:26 +0200
commit729eef0c933831bccfeac6a355bdb410787fbe5f (patch)
tree35cdf2e6427ad18bc53efbab4cab34a0af2054d7 /alacritty_terminal/src/event.rs
parentb0c6fdff763f7271506d26d7e768e6377fdc691b (diff)
downloadalacritty-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.rs561
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);
}