aboutsummaryrefslogtreecommitdiff
path: root/src/event.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/event.rs')
-rw-r--r--src/event.rs595
1 files changed, 0 insertions, 595 deletions
diff --git a/src/event.rs b/src/event.rs
deleted file mode 100644
index 1f3e9ca5..00000000
--- a/src/event.rs
+++ /dev/null
@@ -1,595 +0,0 @@
-//! Process window events
-use std::borrow::Cow;
-use std::env;
-#[cfg(unix)]
-use std::fs;
-use std::fs::File;
-use std::io::Write;
-use std::sync::mpsc;
-use std::time::Instant;
-
-use copypasta::{Buffer as ClipboardBuffer, Clipboard, Load, Store};
-use glutin::dpi::PhysicalSize;
-use glutin::{self, ElementState, Event, ModifiersState, MouseButton};
-use parking_lot::MutexGuard;
-use serde_json as json;
-
-use crate::cli::Options;
-use crate::config::{self, Config};
-use crate::display::OnResize;
-use crate::grid::Scroll;
-use crate::index::{Column, Line, Point, Side};
-use crate::input::{self, KeyBinding, MouseBinding};
-use crate::selection::Selection;
-use crate::sync::FairMutex;
-use crate::term::cell::Cell;
-use crate::term::{SizeInfo, Term};
-#[cfg(unix)]
-use crate::tty;
-use crate::util::fmt::Red;
-use crate::util::{limit, start_daemon};
-use crate::window::Window;
-
-/// Byte sequences are sent to a `Notify` in response to some events
-pub trait Notify {
- /// Notify that an escape sequence should be written to the pty
- ///
- /// TODO this needs to be able to error somehow
- 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 last_modifiers: &'a mut ModifiersState,
- pub window_changes: &'a mut WindowChanges,
-}
-
-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(&self, buffer: ClipboardBuffer) {
- if let Some(selected) = self.terminal.selection_to_string() {
- if !selected.is_empty() {
- Clipboard::new()
- .and_then(|mut clipboard| clipboard.store(selected, buffer))
- .unwrap_or_else(|err| {
- warn!("Error storing selection to clipboard. {}", Red(err));
- });
- }
- }
- }
-
- 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 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 last_modifiers(&mut self) -> &mut ModifiersState {
- &mut self.last_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>,
- ref_test: bool,
- size_info: SizeInfo,
- hide_mouse_when_typing: bool,
- hide_mouse: bool,
- received_count: usize,
- suppress_chars: bool,
- last_modifiers: ModifiersState,
- 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>,
- options: &Options,
- config: &Config,
- ref_test: bool,
- 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: options.print_events,
- wait_for_event: true,
- notifier,
- resize_tx,
- ref_test,
- mouse: Default::default(),
- size_info,
- hide_mouse_when_typing: config.hide_mouse_when_typing(),
- hide_mouse: false,
- received_count: 0,
- suppress_chars: false,
- last_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: false,
- 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,
- ref_test: bool,
- 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 => {
- if ref_test {
- // dump grid state
- let mut grid = processor.ctx.terminal.grid().clone();
- grid.initialize_all(&Cell::default());
- grid.truncate();
-
- let serialized_grid = json::to_string(&grid).expect("serialize grid");
-
- let serialized_size =
- json::to_string(processor.ctx.terminal.size_info())
- .expect("serialize size");
-
- let serialized_config =
- format!("{{\"history_size\":{}}}", grid.history_size());
-
- File::create("./grid.json")
- .and_then(|mut f| f.write_all(serialized_grid.as_bytes()))
- .expect("write grid.json");
-
- File::create("./size.json")
- .and_then(|mut f| f.write_all(serialized_size.as_bytes()))
- .expect("write size.json");
-
- File::create("./config.json")
- .and_then(|mut f| f.write_all(serialized_config.as_bytes()))
- .expect("write config.json");
- }
-
- 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.dirty = true;
- processor.ctx.terminal.next_is_urgent = Some(false);
- } else {
- processor.ctx.terminal.reset_url_highlight();
- 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;
- },
- _ => (),
- }
- },
- 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 ref_test = self.ref_test;
- 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,
- last_modifiers: &mut self.last_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,
- ref_test,
- 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();
- }
-}