summaryrefslogtreecommitdiff
path: root/alacritty
diff options
context:
space:
mode:
Diffstat (limited to 'alacritty')
-rw-r--r--alacritty/src/config/monitor.rs3
-rw-r--r--alacritty/src/display.rs4
-rw-r--r--alacritty/src/event.rs132
-rw-r--r--alacritty/src/input.rs140
-rw-r--r--alacritty/src/logging.rs2
-rw-r--r--alacritty/src/main.rs4
-rw-r--r--alacritty/src/scheduler.rs102
-rw-r--r--alacritty/src/window.rs9
8 files changed, 294 insertions, 102 deletions
diff --git a/alacritty/src/config/monitor.rs b/alacritty/src/config/monitor.rs
index d91b2e4b..42603c7e 100644
--- a/alacritty/src/config/monitor.rs
+++ b/alacritty/src/config/monitor.rs
@@ -4,10 +4,9 @@ use std::time::Duration;
use notify::{watcher, DebouncedEvent, RecursiveMode, Watcher};
-use alacritty_terminal::event::{Event, EventListener};
use alacritty_terminal::util;
-use crate::event::EventProxy;
+use crate::event::{Event, EventProxy};
pub struct Monitor {
_thread: ::std::thread::JoinHandle<()>,
diff --git a/alacritty/src/display.rs b/alacritty/src/display.rs
index 71c98559..d61a5bbd 100644
--- a/alacritty/src/display.rs
+++ b/alacritty/src/display.rs
@@ -23,7 +23,7 @@ use font::set_font_smoothing;
use font::{self, Rasterize};
use alacritty_terminal::config::{Font, StartupMode};
-use alacritty_terminal::event::{Event, OnResize};
+use alacritty_terminal::event::OnResize;
use alacritty_terminal::index::Line;
use alacritty_terminal::message_bar::MessageBuffer;
use alacritty_terminal::meter::Meter;
@@ -119,7 +119,7 @@ pub struct Display {
}
impl Display {
- pub fn new(config: &Config, event_loop: &EventLoop<Event>) -> Result<Display, Error> {
+ pub fn new<E>(config: &Config, event_loop: &EventLoop<E>) -> Result<Display, Error> {
// Guess DPR based on first monitor.
let estimated_dpr =
event_loop.available_monitors().next().map(|m| m.scale_factor()).unwrap_or(1.);
diff --git a/alacritty/src/event.rs b/alacritty/src/event.rs
index 4e2a0578..084ebe1e 100644
--- a/alacritty/src/event.rs
+++ b/alacritty/src/event.rs
@@ -30,7 +30,7 @@ use font::{self, Size};
use alacritty_terminal::config::Font;
use alacritty_terminal::config::LOG_TARGET_CONFIG;
use alacritty_terminal::event::OnResize;
-use alacritty_terminal::event::{Event, EventListener, Notify};
+use alacritty_terminal::event::{Event as TerminalEvent, EventListener, Notify};
use alacritty_terminal::grid::Scroll;
use alacritty_terminal::index::{Column, Line, Point, Side};
use alacritty_terminal::message_bar::{Message, MessageBuffer};
@@ -40,7 +40,7 @@ use alacritty_terminal::term::cell::Cell;
use alacritty_terminal::term::{ClipboardType, SizeInfo, Term, TermMode};
#[cfg(not(windows))]
use alacritty_terminal::tty;
-use alacritty_terminal::util::{limit, start_daemon};
+use alacritty_terminal::util::start_daemon;
use crate::cli::Options;
use crate::clipboard::Clipboard;
@@ -48,9 +48,32 @@ use crate::config;
use crate::config::Config;
use crate::display::Display;
use crate::input::{self, ActionContext as _, FONT_SIZE_STEP};
+use crate::scheduler::Scheduler;
use crate::url::{Url, Urls};
use crate::window::Window;
+/// Events dispatched through the UI event loop.
+#[derive(Debug, Clone)]
+pub enum Event {
+ TerminalEvent(TerminalEvent),
+ DPRChanged(f64, (u32, u32)),
+ Scroll(Scroll),
+ ConfigReload(PathBuf),
+ Message(Message),
+}
+
+impl From<Event> for GlutinEvent<'_, Event> {
+ fn from(event: Event) -> Self {
+ GlutinEvent::UserEvent(event)
+ }
+}
+
+impl From<TerminalEvent> for Event {
+ fn from(event: TerminalEvent) -> Self {
+ Event::TerminalEvent(event)
+ }
+}
+
#[derive(Default, Clone, Debug, PartialEq)]
pub struct DisplayUpdate {
pub dimensions: Option<PhysicalSize<u32>>,
@@ -80,6 +103,7 @@ pub struct ActionContext<'a, N, T> {
pub config: &'a mut Config,
pub event_loop: &'a EventLoopWindowTarget<Event>,
pub urls: &'a Urls,
+ pub scheduler: &'a mut Scheduler,
font_size: &'a mut Size,
}
@@ -248,6 +272,25 @@ impl<'a, N: Notify + 'a, T: EventListener> input::ActionContext<T> for ActionCon
}
}
+ /// Spawn URL launcher when clicking on URLs.
+ fn launch_url(&self, url: Url) {
+ if self.mouse.block_url_launcher {
+ return;
+ }
+
+ if let Some(ref launcher) = self.config.ui_config.mouse.url.launcher {
+ let mut args = launcher.args().to_vec();
+ let start = self.terminal.visible_to_buffer(url.start());
+ let end = self.terminal.visible_to_buffer(url.end());
+ args.push(self.terminal.bounds_to_string(start, end));
+
+ match start_daemon(launcher.program(), &args) {
+ Ok(_) => debug!("Launched {} with args {:?}", launcher.program(), args),
+ Err(_) => warn!("Unable to launch {} with args {:?}", launcher.program(), args),
+ }
+ }
+ }
+
fn change_font_size(&mut self, delta: f32) {
*self.font_size = max(*self.font_size + delta, Size::new(FONT_SIZE_STEP));
let font = self.config.font.clone().with_size(*self.font_size);
@@ -282,27 +325,12 @@ impl<'a, N: Notify + 'a, T: EventListener> input::ActionContext<T> for ActionCon
self.urls
}
- fn clipboard(&mut self) -> &mut Clipboard {
+ fn clipboard_mut(&mut self) -> &mut Clipboard {
self.clipboard
}
- /// Spawn URL launcher when clicking on URLs.
- fn launch_url(&self, url: Url) {
- if self.mouse.block_url_launcher {
- return;
- }
-
- if let Some(ref launcher) = self.config.ui_config.mouse.url.launcher {
- let mut args = launcher.args().to_vec();
- let start = self.terminal.visible_to_buffer(url.start());
- let end = self.terminal.visible_to_buffer(url.end());
- args.push(self.terminal.bounds_to_string(start, end));
-
- match start_daemon(launcher.program(), &args) {
- Ok(_) => debug!("Launched {} with args {:?}", launcher.program(), args),
- Err(_) => warn!("Unable to launch {} with args {:?}", launcher.program(), args),
- }
- }
+ fn scheduler_mut(&mut self) -> &mut Scheduler {
+ self.scheduler
}
}
@@ -369,7 +397,7 @@ pub struct Processor<N> {
message_buffer: MessageBuffer,
display: Display,
font_size: Size,
- event_queue: Vec<GlutinEvent<'static, alacritty_terminal::event::Event>>,
+ event_queue: Vec<GlutinEvent<'static, Event>>,
}
impl<N: Notify + OnResize> Processor<N> {
@@ -432,6 +460,8 @@ impl<N: Notify + OnResize> Processor<N> {
where
T: EventListener,
{
+ let mut scheduler = Scheduler::new();
+
event_loop.run_return(|event, event_loop, control_flow| {
if self.config.debug.print_events {
info!("glutin event: {:?}", event);
@@ -444,13 +474,16 @@ impl<N: Notify + OnResize> Processor<N> {
match event {
// Check for shutdown.
- GlutinEvent::UserEvent(Event::Exit) => {
+ GlutinEvent::UserEvent(Event::TerminalEvent(TerminalEvent::Exit)) => {
*control_flow = ControlFlow::Exit;
return;
},
// Process events.
GlutinEvent::RedrawEventsCleared => {
- *control_flow = ControlFlow::Wait;
+ *control_flow = match scheduler.update(&mut self.event_queue) {
+ Some(instant) => ControlFlow::WaitUntil(instant),
+ None => ControlFlow::Wait,
+ };
if self.event_queue_empty() {
return;
@@ -463,8 +496,7 @@ impl<N: Notify + OnResize> Processor<N> {
} => {
*control_flow = ControlFlow::Poll;
let size = (new_inner_size.width, new_inner_size.height);
- let event = GlutinEvent::UserEvent(Event::DPRChanged(scale_factor, size));
- self.event_queue.push(event);
+ self.event_queue.push(Event::DPRChanged(scale_factor, size).into());
return;
},
// Transmute to extend lifetime, which exists only for `ScaleFactorChanged` event.
@@ -495,6 +527,7 @@ impl<N: Notify + OnResize> Processor<N> {
font_size: &mut self.font_size,
config: &mut self.config,
urls: &self.display.urls,
+ scheduler: &mut scheduler,
event_loop,
};
let mut processor = input::Processor::new(context, &self.display.highlighted_url);
@@ -529,7 +562,8 @@ impl<N: Notify + OnResize> Processor<N> {
// Request immediate re-draw if visual bell animation is not finished yet.
if !terminal.visual_bell.completed() {
- self.event_queue.push(GlutinEvent::UserEvent(Event::Wakeup));
+ let event: Event = TerminalEvent::Wakeup.into();
+ self.event_queue.push(event.into());
}
// Redraw screen.
@@ -571,26 +605,29 @@ impl<N: Notify + OnResize> Processor<N> {
processor.ctx.size_info.dpr = scale_factor;
processor.ctx.terminal.dirty = true;
},
- Event::Title(title) => processor.ctx.window.set_title(&title),
- Event::Wakeup => processor.ctx.terminal.dirty = true,
- Event::Urgent => {
- processor.ctx.window.set_urgent(!processor.ctx.terminal.is_focused)
- },
- Event::ConfigReload(path) => Self::reload_config(&path, processor),
Event::Message(message) => {
processor.ctx.message_buffer.push(message);
processor.ctx.display_update_pending.message_buffer = true;
processor.ctx.terminal.dirty = true;
},
- Event::ClipboardStore(clipboard_type, content) => {
- processor.ctx.clipboard.store(clipboard_type, content);
- },
- Event::ClipboardLoad(clipboard_type, format) => {
- let text = format(processor.ctx.clipboard.load(clipboard_type).as_str());
- processor.ctx.write_to_pty(text.into_bytes());
+ Event::ConfigReload(path) => Self::reload_config(&path, processor),
+ Event::Scroll(scroll) => processor.ctx.scroll(scroll),
+ Event::TerminalEvent(event) => match event {
+ TerminalEvent::Title(title) => processor.ctx.window.set_title(&title),
+ TerminalEvent::Wakeup => processor.ctx.terminal.dirty = true,
+ TerminalEvent::Urgent => {
+ processor.ctx.window.set_urgent(!processor.ctx.terminal.is_focused)
+ },
+ TerminalEvent::ClipboardStore(clipboard_type, content) => {
+ processor.ctx.clipboard.store(clipboard_type, content);
+ },
+ TerminalEvent::ClipboardLoad(clipboard_type, format) => {
+ let text = format(processor.ctx.clipboard.load(clipboard_type).as_str());
+ processor.ctx.write_to_pty(text.into_bytes());
+ },
+ TerminalEvent::MouseCursorDirty => processor.reset_mouse_cursor(),
+ TerminalEvent::Exit => (),
},
- Event::MouseCursorDirty => processor.reset_mouse_cursor(),
- Event::Exit => (),
},
GlutinEvent::RedrawRequested(_) => processor.ctx.terminal.dirty = true,
GlutinEvent::WindowEvent { event, window_id, .. } => {
@@ -624,12 +661,8 @@ impl<N: Notify + OnResize> Processor<N> {
processor.modifiers_input(modifiers)
},
WindowEvent::CursorMoved { position, .. } => {
- let (x, y) = position.into();
- let x = limit(x, 0, processor.ctx.size_info.width as i32);
- let y = limit(y, 0, processor.ctx.size_info.height as i32);
-
processor.ctx.window.set_mouse_visible(true);
- processor.mouse_moved(x as usize, y as usize);
+ processor.mouse_moved(position);
},
WindowEvent::MouseWheel { delta, phase, .. } => {
processor.ctx.window.set_mouse_visible(true);
@@ -796,10 +829,15 @@ impl EventProxy {
pub fn new(proxy: EventLoopProxy<Event>) -> Self {
EventProxy(proxy)
}
+
+ /// Send an event to the event loop.
+ pub fn send_event(&self, event: Event) {
+ let _ = self.0.send_event(event);
+ }
}
impl EventListener for EventProxy {
- fn send_event(&self, event: Event) {
- let _ = self.0.send_event(event);
+ fn send_event(&self, event: TerminalEvent) {
+ let _ = self.0.send_event(Event::TerminalEvent(event));
}
}
diff --git a/alacritty/src/input.rs b/alacritty/src/input.rs
index 2025f108..d43bb26f 100644
--- a/alacritty/src/input.rs
+++ b/alacritty/src/input.rs
@@ -6,12 +6,13 @@
//! determine what to do when a non-modifier key is pressed.
use std::borrow::Cow;
-use std::cmp::{min, Ordering};
+use std::cmp::{max, min, Ordering};
use std::marker::PhantomData;
-use std::time::Instant;
+use std::time::{Duration, Instant};
use log::{debug, trace, warn};
+use glutin::dpi::PhysicalPosition;
use glutin::event::{
ElementState, KeyboardInput, ModifiersState, MouseButton, MouseScrollDelta, TouchPhase,
};
@@ -21,7 +22,7 @@ use glutin::platform::macos::EventLoopWindowTargetExtMacOS;
use glutin::window::CursorIcon;
use alacritty_terminal::ansi::{ClearMode, Handler};
-use alacritty_terminal::event::{Event, EventListener};
+use alacritty_terminal::event::EventListener;
use alacritty_terminal::grid::Scroll;
use alacritty_terminal::index::{Column, Line, Point, Side};
use alacritty_terminal::message_bar::{self, Message};
@@ -33,13 +34,23 @@ use alacritty_terminal::vi_mode::ViMotion;
use crate::clipboard::Clipboard;
use crate::config::{Action, Binding, Config, Key, ViAction};
-use crate::event::{ClickState, Mouse};
+use crate::event::{ClickState, Event, Mouse};
+use crate::scheduler::{Scheduler, TimerId};
use crate::url::{Url, Urls};
use crate::window::Window;
/// Font size change interval.
pub const FONT_SIZE_STEP: f32 = 0.5;
+/// Interval for mouse scrolling during selection outside of the boundaries.
+const SELECTION_SCROLLING_INTERVAL: Duration = Duration::from_millis(15);
+
+/// Minimum number of pixels at the bottom/top where selection scrolling is performed.
+const MIN_SELECTION_SCROLLING_HEIGHT: f64 = 5.;
+
+/// Number of pixels for increasing the selection scrolling speed factor by one.
+const SELECTION_SCROLLING_STEP: f64 = 20.;
+
/// Processes input from glutin.
///
/// An escape sequence may be emitted in case specific keys or key combinations
@@ -77,10 +88,11 @@ pub trait ActionContext<T: EventListener> {
fn message(&self) -> Option<&Message>;
fn config(&self) -> &Config;
fn event_loop(&self) -> &EventLoopWindowTarget<Event>;
- fn clipboard(&mut self) -> &mut Clipboard;
fn urls(&self) -> &Urls;
fn launch_url(&self, url: Url);
fn mouse_mode(&self) -> bool;
+ fn clipboard_mut(&mut self) -> &mut Clipboard;
+ fn scheduler_mut(&mut self) -> &mut Scheduler;
}
trait Execute<T: EventListener> {
@@ -128,11 +140,11 @@ impl<T: EventListener> Execute<T> for Action {
#[cfg(not(any(target_os = "macos", windows)))]
Action::CopySelection => ctx.copy_selection(ClipboardType::Selection),
Action::Paste => {
- let text = ctx.clipboard().load(ClipboardType::Clipboard);
+ let text = ctx.clipboard_mut().load(ClipboardType::Clipboard);
paste(ctx, &text);
},
Action::PasteSelection => {
- let text = ctx.clipboard().load(ClipboardType::Selection);
+ let text = ctx.clipboard_mut().load(ClipboardType::Selection);
paste(ctx, &text);
},
Action::Command(ref program) => {
@@ -304,9 +316,19 @@ impl<'a, T: EventListener, A: ActionContext<T>> Processor<'a, T, A> {
}
#[inline]
- pub fn mouse_moved(&mut self, x: usize, y: usize) {
+ pub fn mouse_moved(&mut self, position: PhysicalPosition<f64>) {
let size_info = self.ctx.size_info();
+ let (x, y) = position.into();
+
+ let lmb_pressed = self.ctx.mouse().left_button_state == ElementState::Pressed;
+ if !self.ctx.selection_is_empty() && lmb_pressed {
+ self.update_selection_scrolling(y);
+ }
+
+ let x = min(max(x, 0), size_info.width as i32 - 1) as usize;
+ let y = min(max(y, 0), size_info.height as i32 - 1) as usize;
+
self.ctx.mouse_mut().x = x;
self.ctx.mouse_mut().y = y;
@@ -339,9 +361,7 @@ impl<'a, T: EventListener, A: ActionContext<T>> Processor<'a, T, A> {
self.ctx.window_mut().set_mouse_cursor(mouse_state.into());
let last_term_line = self.ctx.terminal().grid().num_lines() - 1;
- if self.ctx.mouse().left_button_state == ElementState::Pressed
- && (self.ctx.modifiers().shift() || !self.ctx.mouse_mode())
- {
+ if lmb_pressed && (self.ctx.modifiers().shift() || !self.ctx.mouse_mode()) {
// Treat motion over message bar like motion over the last line.
let line = min(point.line, last_term_line);
@@ -356,7 +376,7 @@ impl<'a, T: EventListener, A: ActionContext<T>> Processor<'a, T, A> {
&& point.line <= last_term_line
&& self.ctx.terminal().mode().intersects(TermMode::MOUSE_MOTION | TermMode::MOUSE_DRAG)
{
- if self.ctx.mouse().left_button_state == ElementState::Pressed {
+ if lmb_pressed {
self.mouse_report(32, ElementState::Pressed);
} else if self.ctx.mouse().middle_button_state == ElementState::Pressed {
self.mouse_report(33, ElementState::Pressed);
@@ -548,6 +568,7 @@ impl<'a, T: EventListener, A: ActionContext<T>> Processor<'a, T, A> {
self.ctx.launch_url(url);
}
+ self.ctx.scheduler_mut().unschedule(TimerId::SelectionScrolling);
self.copy_selection();
}
@@ -870,32 +891,61 @@ impl<'a, T: EventListener, A: ActionContext<T>> Processor<'a, T, A> {
MouseState::Text
}
}
+
+ /// Handle automatic scrolling when selecting above/below the window.
+ fn update_selection_scrolling(&mut self, mouse_y: i32) {
+ let size_info = self.ctx.size_info();
+ let scheduler = self.ctx.scheduler_mut();
+
+ // Scale constants by DPI.
+ let min_height = (MIN_SELECTION_SCROLLING_HEIGHT * size_info.dpr) as i32;
+ let step = (SELECTION_SCROLLING_STEP * size_info.dpr) as i32;
+
+ // Compute the height of the scrolling areas.
+ let end_top = max(min_height, size_info.padding_y as i32);
+ let height_bottom = max(min_height, size_info.padding_bottom() as i32);
+ let start_bottom = size_info.height as i32 - height_bottom;
+
+ // Get distance from closest window boundary.
+ let delta = if mouse_y < end_top {
+ end_top - mouse_y + step
+ } else if mouse_y >= start_bottom {
+ start_bottom - mouse_y - step
+ } else {
+ scheduler.unschedule(TimerId::SelectionScrolling);
+ return;
+ };
+
+ // Scale number of lines scrolled based on distance to boundary.
+ let delta = delta as isize / step as isize;
+ let event = Event::Scroll(Scroll::Lines(delta));
+
+ // Schedule event.
+ match scheduler.get_mut(TimerId::SelectionScrolling) {
+ Some(timer) => timer.event = event.into(),
+ None => {
+ scheduler.schedule(
+ event.into(),
+ SELECTION_SCROLLING_INTERVAL,
+ true,
+ TimerId::SelectionScrolling,
+ );
+ },
+ }
+ }
}
#[cfg(test)]
mod tests {
- use std::borrow::Cow;
- use std::time::Duration;
-
- use glutin::event::{
- ElementState, Event, ModifiersState, MouseButton, VirtualKeyCode, WindowEvent,
- };
- use glutin::event_loop::EventLoopWindowTarget;
+ use super::*;
- use alacritty_terminal::event::{Event as TerminalEvent, EventListener};
- use alacritty_terminal::grid::Scroll;
- use alacritty_terminal::index::{Point, Side};
- use alacritty_terminal::message_bar::{Message, MessageBuffer};
- use alacritty_terminal::selection::{Selection, SelectionType};
- use alacritty_terminal::term::{ClipboardType, SizeInfo, Term, TermMode};
+ use glutin::event::{Event as GlutinEvent, VirtualKeyCode, WindowEvent};
- use crate::clipboard::Clipboard;
- use crate::config::{ClickHandler, Config};
- use crate::event::{ClickState, Mouse};
- use crate::url::{Url, Urls};
- use crate::window::Window;
+ use alacritty_terminal::event::Event as TerminalEvent;
+ use alacritty_terminal::message_bar::MessageBuffer;
+ use alacritty_terminal::selection::Selection;
- use super::{Action, Binding, Processor};
+ use crate::config::ClickHandler;
const KEY: VirtualKeyCode = VirtualKeyCode::Key0;
@@ -1014,7 +1064,11 @@ mod tests {
self.config
}
- fn event_loop(&self) -> &EventLoopWindowTarget<TerminalEvent> {
+ fn clipboard_mut(&mut self) -> &mut Clipboard {
+ self.clipboard
+ }
+
+ fn event_loop(&self) -> &EventLoopWindowTarget<Event> {
unimplemented!();
}
@@ -1022,11 +1076,11 @@ mod tests {
unimplemented!();
}
- fn clipboard(&mut self) -> &mut Clipboard {
- self.clipboard
+ fn launch_url(&self, _: Url) {
+ unimplemented!();
}
- fn launch_url(&self, _: Url) {
+ fn scheduler_mut (&mut self) -> &mut Scheduler {
unimplemented!();
}
}
@@ -1089,8 +1143,8 @@ mod tests {
let mut processor = Processor::new(context, &None);
- let event: Event::<'_, TerminalEvent> = $input;
- if let Event::WindowEvent {
+ let event: GlutinEvent::<'_, TerminalEvent> = $input;
+ if let GlutinEvent::WindowEvent {
event: WindowEvent::MouseInput {
state,
button,
@@ -1130,7 +1184,7 @@ mod tests {
name: single_click,
initial_state: ClickState::None,
initial_button: MouseButton::Other(0),
- input: Event::WindowEvent {
+ input: GlutinEvent::WindowEvent {
event: WindowEvent::MouseInput {
state: ElementState::Pressed,
button: MouseButton::Left,
@@ -1146,7 +1200,7 @@ mod tests {
name: single_right_click,
initial_state: ClickState::None,
initial_button: MouseButton::Other(0),
- input: Event::WindowEvent {
+ input: GlutinEvent::WindowEvent {
event: WindowEvent::MouseInput {
state: ElementState::Pressed,
button: MouseButton::Right,
@@ -1162,7 +1216,7 @@ mod tests {
name: single_middle_click,
initial_state: ClickState::None,
initial_button: MouseButton::Other(0),
- input: Event::WindowEvent {
+ input: GlutinEvent::WindowEvent {
event: WindowEvent::MouseInput {
state: ElementState::Pressed,
button: MouseButton::Middle,
@@ -1178,7 +1232,7 @@ mod tests {
name: double_click,
initial_state: ClickState::Click,
initial_button: MouseButton::Left,
- input: Event::WindowEvent {
+ input: GlutinEvent::WindowEvent {
event: WindowEvent::MouseInput {
state: ElementState::Pressed,
button: MouseButton::Left,
@@ -1194,7 +1248,7 @@ mod tests {
name: triple_click,
initial_state: ClickState::DoubleClick,
initial_button: MouseButton::Left,
- input: Event::WindowEvent {
+ input: GlutinEvent::WindowEvent {
event: WindowEvent::MouseInput {
state: ElementState::Pressed,
button: MouseButton::Left,
@@ -1210,7 +1264,7 @@ mod tests {
name: multi_click_separate_buttons,
initial_state: ClickState::DoubleClick,
initial_button: MouseButton::Left,
- input: Event::WindowEvent {
+ input: GlutinEvent::WindowEvent {
event: WindowEvent::MouseInput {
state: ElementState::Pressed,
button: MouseButton::Right,
diff --git a/alacritty/src/logging.rs b/alacritty/src/logging.rs
index 1e56f3cb..a6fcf3fb 100644
--- a/alacritty/src/logging.rs
+++ b/alacritty/src/logging.rs
@@ -15,11 +15,11 @@ use std::sync::{Arc, Mutex};
use glutin::event_loop::EventLoopProxy;
use log::{self, Level};
-use alacritty_terminal::event::Event;
use alacritty_terminal::message_bar::Message;
use alacritty_terminal::term::color;
use crate::cli::Options;
+use crate::event::Event;
const ALACRITTY_LOG_ENV: &str = "ALACRITTY_LOG";
diff --git a/alacritty/src/main.rs b/alacritty/src/main.rs
index 9587aec3..ab4acaa7 100644
--- a/alacritty/src/main.rs
+++ b/alacritty/src/main.rs
@@ -23,7 +23,6 @@ use log::{error, info};
#[cfg(windows)]
use winapi::um::wincon::{AttachConsole, FreeConsole, ATTACH_PARENT_PROCESS};
-use alacritty_terminal::event::Event;
use alacritty_terminal::event_loop::{self, EventLoop, Msg};
#[cfg(target_os = "macos")]
use alacritty_terminal::locale;
@@ -42,6 +41,7 @@ mod event;
mod input;
mod logging;
mod renderer;
+mod scheduler;
mod url;
mod window;
@@ -57,7 +57,7 @@ use crate::cli::Options;
use crate::config::monitor::Monitor;
use crate::config::Config;
use crate::display::Display;
-use crate::event::{EventProxy, Processor};
+use crate::event::{Event, EventProxy, Processor};
fn main() {
panic::attach_handler();
diff --git a/alacritty/src/scheduler.rs b/alacritty/src/scheduler.rs
new file mode 100644
index 00000000..a6559acc
--- /dev/null
+++ b/alacritty/src/scheduler.rs
@@ -0,0 +1,102 @@
+//! Scheduler for emitting events at a specific time in the future.
+
+use std::collections::VecDeque;
+use std::time::{Duration, Instant};
+
+use glutin::event::Event as GlutinEvent;
+
+use crate::event::Event as AlacrittyEvent;
+
+type Event = GlutinEvent<'static, AlacrittyEvent>;
+
+/// Scheduler tracking all pending timers.
+pub struct Scheduler {
+ timers: VecDeque<Timer>,
+}
+
+impl Default for Scheduler {
+ fn default() -> Self {
+ Self { timers: VecDeque::new() }
+ }
+}
+
+impl Scheduler {
+ pub fn new() -> Self {
+ Self::default()
+ }
+
+ /// Process all pending timers.
+ ///
+ /// If there are still timers pending after all ready events have been processed, the closest
+ /// pending deadline will be returned.
+ pub fn update(&mut self, event_queue: &mut Vec<Event>) -> Option<Instant> {
+ let now = Instant::now();
+ while !self.timers.is_empty() && self.timers[0].deadline <= now {
+ if let Some(timer) = self.timers.pop_front() {
+ // Automatically repeat the event.
+ if let Some(interval) = timer.interval {
+ self.schedule(timer.event.clone(), interval, true, timer.id);
+ }
+
+ event_queue.push(timer.event);
+ }
+ }
+
+ self.timers.get(0).map(|timer| timer.deadline)
+ }
+
+ /// Schedule a new event.
+ pub fn schedule(
+ &mut self,
+ event: Event,
+ interval: Duration,
+ repeat: bool,
+ timer_id: TimerId,
+ ) {
+ let deadline = Instant::now() + interval;
+
+ // Get insert position in the schedule.
+ let mut index = self.timers.len();
+ loop {
+ if index == 0 {
+ break;
+ }
+ index -= 1;
+
+ if self.timers[index].deadline < deadline {
+ break;
+ }
+ }
+
+ // Set the automatic event repeat rate.
+ let interval = if repeat { Some(interval) } else { None };
+
+ self.timers.insert(index, Timer { interval, deadline, event, id: timer_id });
+ }
+
+ /// Cancel a scheduled event.
+ pub fn unschedule(&mut self, id: TimerId) -> Option<Event> {
+ let index = self.timers.iter().position(|timer| timer.id == id)?;
+ self.timers.remove(index).map(|timer| timer.event)
+ }
+
+ /// Access a staged event by ID.
+ pub fn get_mut(&mut self, id: TimerId) -> Option<&mut Timer> {
+ self.timers.iter_mut().find(|timer| timer.id == id)
+ }
+}
+
+/// ID uniquely identifying a timer.
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+pub enum TimerId {
+ SelectionScrolling,
+}
+
+/// Event scheduled to be emitted at a specific time.
+pub struct Timer {
+ pub deadline: Instant,
+ pub event: Event,
+
+ interval: Option<Duration>,
+ id: TimerId,
+}
diff --git a/alacritty/src/window.rs b/alacritty/src/window.rs
index 22e84780..4275f859 100644
--- a/alacritty/src/window.rs
+++ b/alacritty/src/window.rs
@@ -34,7 +34,6 @@ use glutin::{self, ContextBuilder, PossiblyCurrent, WindowedContext};
use winapi::shared::minwindef::WORD;
use alacritty_terminal::config::{Decorations, StartupMode, WindowConfig};
-use alacritty_terminal::event::Event;
#[cfg(not(windows))]
use alacritty_terminal::term::{SizeInfo, Term};
@@ -103,9 +102,9 @@ impl From<font::Error> for Error {
}
}
-fn create_gl_window(
+fn create_gl_window<E>(
mut window: WindowBuilder,
- event_loop: &EventLoop<Event>,
+ event_loop: &EventLoop<E>,
srgb: bool,
vsync: bool,
dimensions: Option<PhysicalSize<u32>>,
@@ -147,8 +146,8 @@ impl Window {
/// Create a new window.
///
/// This creates a window and fully initializes a window.
- pub fn new(
- event_loop: &EventLoop<Event>,
+ pub fn new<E>(
+ event_loop: &EventLoop<E>,
config: &Config,
size: Option<PhysicalSize<u32>>,
#[cfg(not(any(target_os = "macos", windows)))] wayland_event_queue: Option<&EventQueue>,