diff --git a/crates/term/src/backends/process.rs b/crates/term/src/backends/process.rs
index 8ccb092c..648e47d7 100644
--- a/crates/term/src/backends/process.rs
+++ b/crates/term/src/backends/process.rs
@@ -113,6 +113,7 @@ impl super::Backend for Process {
}
fn send(&mut self, msg: alacritty_terminal::event_loop::Msg) {
+ println!("{:?}", msg);
let _ = self.event_loop_sender.send(msg);
}
diff --git a/crates/term/src/widget/keys.rs b/crates/term/src/widget/keys.rs
new file mode 100644
index 00000000..d5247668
--- /dev/null
+++ b/crates/term/src/widget/keys.rs
@@ -0,0 +1,234 @@
+// Copyright C 2024 Lily Lyons
+//
+// This file is part of Luminol.
+//
+// Luminol is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// at your option any later version.
+//
+// Luminol is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Luminol. If not, see .
+//
+// Additional permission under GNU GPL version 3 section 7
+//
+// If you modify this Program, or any covered work, by linking or combining
+// it with Steamworks API by Valve Corporation, containing parts covered by
+// terms of the Steamworks API by Valve Corporation, the licensors of this
+// Program grant you additional permission to convey the resulting work.
+use alacritty_terminal::term::TermMode;
+
+macro_rules! key_binding {
+ (
+ $arg_key:ident, $modifiers:ident, $term_mode:ident;
+ $(
+ $key:ident
+ $(,$input_modifiers:expr)?
+ $(,+$terminal_mode_include:expr)?
+ $(,~$terminal_mode_exclude:expr)?
+ ;$action:literal
+ );*
+ $(;)*
+ ) => {{
+ match $arg_key {
+ $(
+ egui::Key::$key if true
+ $( && $input_modifiers == $modifiers )?
+ $( && $term_mode.contains($terminal_mode_include) )?
+ $( && !$term_mode.contains($terminal_mode_exclude))? => Some($action),
+ )*
+ _ => None,
+ }
+ }};
+}
+
+// Adapted from https://github.com/Harzu/iced_term/blob/master/src/bindings.rs
+pub fn key_to_codes(
+ key: egui::Key,
+ modifiers: egui::Modifiers,
+ term_mode: TermMode,
+) -> Option<&'static [u8]> {
+ println!("{key:?} {modifiers:?}");
+ key_binding! {
+ key, modifiers, term_mode;
+ // ANY
+ Space; b" ";
+ Enter; b"\x0d";
+ Backspace; b"\x7f";
+ Escape; b"\x1b";
+ Tab; b"\x09";
+ Insert; b"\x1b[2~";
+ Delete; b"\x1b[3~";
+ PageUp; b"\x1b[5~";
+ PageDown; b"\x1b[6~";
+ F1; b"\x1bOP";
+ F2; b"\x1bOQ";
+ F3; b"\x1bOR";
+ F4; b"\x1bOS";
+ F5; b"\x1b[15~";
+ F6; b"\x1b[17~";
+ F7; b"\x1b[18~";
+ F8; b"\x1b[19~";
+ F9; b"\x1b[20~";
+ F10; b"\x1b[21~";
+ F11; b"\x1b[23~";
+ F12; b"\x1b[24~";
+ F13; b"\x1b[25~";
+ F14; b"\x1b[26~";
+ F15; b"\x1b[28~";
+ F16; b"\x1b[29~";
+ F17; b"\x1b[31~";
+ F18; b"\x1b[32~";
+ F19; b"\x1b[33~";
+ F20; b"\x1b[34~";
+ // APP_CURSOR Excluding
+ End, ~TermMode::APP_CURSOR; b"\x1b[F";
+ Home, ~TermMode::APP_CURSOR; b"\x1b[H";
+ ArrowUp, ~TermMode::APP_CURSOR; b"\x1b[A";
+ ArrowDown, ~TermMode::APP_CURSOR; b"\x1b[B";
+ ArrowLeft, ~TermMode::APP_CURSOR; b"\x1b[D";
+ ArrowRight, ~TermMode::APP_CURSOR; b"\x1b[C";
+ // APP_CURSOR Including
+ End, +TermMode::APP_CURSOR; b"\x1BOF";
+ Home, +TermMode::APP_CURSOR; b"\x1BOH";
+ ArrowUp, +TermMode::APP_CURSOR; b"\x1bOA";
+ ArrowDown, +TermMode::APP_CURSOR; b"\x1bOB";
+ ArrowLeft, +TermMode::APP_CURSOR; b"\x1bOD";
+ ArrowRight, +TermMode::APP_CURSOR; b"\x1bOC";
+ // CTRL
+ ArrowUp, egui::Modifiers::COMMAND; b"\x1b[1;5A";
+ ArrowDown, egui::Modifiers::COMMAND; b"\x1b[1;5B";
+ ArrowLeft, egui::Modifiers::COMMAND; b"\x1b[1;5D";
+ ArrowRight, egui::Modifiers::COMMAND; b"\x1b[1;5C";
+ End, egui::Modifiers::CTRL; b"\x1b[1;5F";
+ Home, egui::Modifiers::CTRL; b"\x1b[1;5H";
+ Delete, egui::Modifiers::CTRL; b"\x1b[3;5~";
+ PageUp, egui::Modifiers::CTRL; b"\x1b[5;5~";
+ PageDown, egui::Modifiers::CTRL; b"\x1b[6;5~";
+ F1, egui::Modifiers::CTRL; b"\x1bO;5P";
+ F2, egui::Modifiers::CTRL; b"\x1bO;5Q";
+ F3, egui::Modifiers::CTRL; b"\x1bO;5R";
+ F4, egui::Modifiers::CTRL; b"\x1bO;5S";
+ F5, egui::Modifiers::CTRL; b"\x1b[15;5~";
+ F6, egui::Modifiers::CTRL; b"\x1b[17;5~";
+ F7, egui::Modifiers::CTRL; b"\x1b[18;5~";
+ F8, egui::Modifiers::CTRL; b"\x1b[19;5~";
+ F9, egui::Modifiers::CTRL; b"\x1b[20;5~";
+ F10, egui::Modifiers::CTRL; b"\x1b[21;5~";
+ F11, egui::Modifiers::CTRL; b"\x1b[23;5~";
+ F12, egui::Modifiers::CTRL; b"\x1b[24;5~";
+ A, egui::Modifiers::CTRL; b"\x01";
+ B, egui::Modifiers::CTRL; b"\x02";
+ C, egui::Modifiers::CTRL; b"\x03";
+ D, egui::Modifiers::CTRL; b"\x04";
+ E, egui::Modifiers::CTRL; b"\x05"; // ENQ vt100
+ F, egui::Modifiers::CTRL; b"\x06";
+ G, egui::Modifiers::CTRL; b"\x07"; // Bell vt100
+ H, egui::Modifiers::CTRL; b"\x08"; // Backspace vt100
+ I, egui::Modifiers::CTRL; b"\x09"; // Tab vt100
+ J, egui::Modifiers::CTRL; b"\x0a"; // LF new line vt100
+ K, egui::Modifiers::CTRL; b"\x0b"; // VT vertical tab vt100
+ L, egui::Modifiers::CTRL; b"\x0c"; // FF new page vt100
+ M, egui::Modifiers::CTRL; b"\x0d"; // CR vt100
+ N, egui::Modifiers::CTRL; b"\x0e"; // SO shift out vt100
+ O, egui::Modifiers::CTRL; b"\x0f"; // SI shift in vt100
+ P, egui::Modifiers::CTRL; b"\x10";
+ Q, egui::Modifiers::CTRL; b"\x11";
+ R, egui::Modifiers::CTRL; b"\x12";
+ S, egui::Modifiers::CTRL; b"\x13";
+ T, egui::Modifiers::CTRL; b"\x14";
+ U, egui::Modifiers::CTRL; b"\x51";
+ V, egui::Modifiers::CTRL; b"\x16";
+ W, egui::Modifiers::CTRL; b"\x17";
+ X, egui::Modifiers::CTRL; b"\x18";
+ W, egui::Modifiers::CTRL; b"\x19";
+ Z, egui::Modifiers::CTRL; b"\x1a";
+ // SHIFT
+ Enter, egui::Modifiers::SHIFT; b"\x0d";
+ Backspace, egui::Modifiers::SHIFT; b"\x7f";
+ Tab, egui::Modifiers::SHIFT; b"\x1b[Z";
+ End, egui::Modifiers::SHIFT, +TermMode::ALT_SCREEN; b"\x1b[1;2F";
+ Home, egui::Modifiers::SHIFT, +TermMode::ALT_SCREEN; b"\x1b[1;2H";
+ PageUp, egui::Modifiers::SHIFT, +TermMode::ALT_SCREEN; b"\x1b[5;2~";
+ PageDown, egui::Modifiers::SHIFT, +TermMode::ALT_SCREEN; b"\x1b[6;2~";
+ ArrowUp, egui::Modifiers::SHIFT; b"\x1b[1;2A";
+ ArrowDown, egui::Modifiers::SHIFT; b"\x1b[1;2B";
+ ArrowLeft, egui::Modifiers::SHIFT; b"\x1b[1;2D";
+ ArrowRight, egui::Modifiers::SHIFT; b"\x1b[1;2C";
+ // ALT
+ Backspace, egui::Modifiers::ALT; b"\x1b\x7f";
+ End, egui::Modifiers::ALT; b"\x1b[1;3F";
+ Home, egui::Modifiers::ALT; b"\x1b[1;3H";
+ Insert, egui::Modifiers::ALT; b"\x1b[3;2~";
+ Delete, egui::Modifiers::ALT; b"\x1b[3;3~";
+ PageUp, egui::Modifiers::ALT; b"\x1b[5;3~";
+ PageDown, egui::Modifiers::ALT; b"\x1b[6;3~";
+ ArrowUp, egui::Modifiers::ALT; b"\x1b[1;3A";
+ ArrowDown, egui::Modifiers::ALT; b"\x1b[1;3B";
+ ArrowLeft, egui::Modifiers::ALT; b"\x1b[1;3D";
+ ArrowRight, egui::Modifiers::ALT; b"\x1b[1;3C";
+ // SHIFT + ALT
+ End, egui::Modifiers::SHIFT | egui::Modifiers::ALT; b"\x1b[1;4F";
+ Home, egui::Modifiers::SHIFT | egui::Modifiers::ALT; b"\x1b[1;4H";
+ ArrowUp, egui::Modifiers::SHIFT | egui::Modifiers::ALT; b"\x1b[1;4A";
+ ArrowDown, egui::Modifiers::SHIFT | egui::Modifiers::ALT; b"\x1b[1;4B";
+ ArrowLeft, egui::Modifiers::SHIFT | egui::Modifiers::ALT; b"\x1b[1;4D";
+ ArrowRight, egui::Modifiers::SHIFT | egui::Modifiers::ALT; b"\x1b[1;4C";
+ // SHIFT + CTRL
+ End, egui::Modifiers::SHIFT | egui::Modifiers::CTRL; b"\x1b[1;6F";
+ Home, egui::Modifiers::SHIFT | egui::Modifiers::CTRL; b"\x1b[1;6H";
+ ArrowUp, egui::Modifiers::SHIFT | egui::Modifiers::CTRL; b"\x1b[1;6A";
+ ArrowDown, egui::Modifiers::SHIFT | egui::Modifiers::CTRL; b"\x1b[1;6B";
+ ArrowLeft, egui::Modifiers::SHIFT | egui::Modifiers::CTRL; b"\x1b[1;6D";
+ ArrowRight, egui::Modifiers::SHIFT | egui::Modifiers::CTRL; b"\x1b[1;6C";
+ A, egui::Modifiers::SHIFT | egui::Modifiers::CTRL; b"\x01";
+ B, egui::Modifiers::SHIFT | egui::Modifiers::CTRL; b"\x02";
+ C, egui::Modifiers::SHIFT | egui::Modifiers::CTRL; b"\x03";
+ D, egui::Modifiers::SHIFT | egui::Modifiers::CTRL; b"\x04";
+ E, egui::Modifiers::SHIFT | egui::Modifiers::CTRL; b"\x05";
+ F, egui::Modifiers::SHIFT | egui::Modifiers::CTRL; b"\x06";
+ G, egui::Modifiers::SHIFT | egui::Modifiers::CTRL; b"\x07";
+ H, egui::Modifiers::SHIFT | egui::Modifiers::CTRL; b"\x08";
+ I, egui::Modifiers::SHIFT | egui::Modifiers::CTRL; b"\x09";
+ J, egui::Modifiers::SHIFT | egui::Modifiers::CTRL; b"\x0a";
+ K, egui::Modifiers::SHIFT | egui::Modifiers::CTRL; b"\x0b";
+ L, egui::Modifiers::SHIFT | egui::Modifiers::CTRL; b"\x0c";
+ M, egui::Modifiers::SHIFT | egui::Modifiers::CTRL; b"\x0d";
+ N, egui::Modifiers::SHIFT | egui::Modifiers::CTRL; b"\x0e";
+ O, egui::Modifiers::SHIFT | egui::Modifiers::CTRL; b"\x0f";
+ P, egui::Modifiers::SHIFT | egui::Modifiers::CTRL; b"\x10";
+ Q, egui::Modifiers::SHIFT | egui::Modifiers::CTRL; b"\x11";
+ R, egui::Modifiers::SHIFT | egui::Modifiers::CTRL; b"\x12";
+ S, egui::Modifiers::SHIFT | egui::Modifiers::CTRL; b"\x13";
+ T, egui::Modifiers::SHIFT | egui::Modifiers::CTRL; b"\x14";
+ U, egui::Modifiers::SHIFT | egui::Modifiers::CTRL; b"\x51";
+ V, egui::Modifiers::SHIFT | egui::Modifiers::CTRL; b"\x16";
+ W, egui::Modifiers::SHIFT | egui::Modifiers::CTRL; b"\x17";
+ X, egui::Modifiers::SHIFT | egui::Modifiers::CTRL; b"\x18";
+ W, egui::Modifiers::SHIFT | egui::Modifiers::CTRL; b"\x19";
+ Z, egui::Modifiers::SHIFT | egui::Modifiers::CTRL; b"\x1a";
+ Num2, egui::Modifiers::SHIFT | egui::Modifiers::CTRL; b"\x00"; // Null vt100
+ Num6, egui::Modifiers::SHIFT | egui::Modifiers::CTRL; b"\x1e";
+ // CTRL + ALT
+ End, egui::Modifiers::CTRL | egui::Modifiers::ALT; b"\x1b[1;7F";
+ Home, egui::Modifiers::CTRL | egui::Modifiers::ALT; b"\x1b[1;7H";
+ PageUp, egui::Modifiers::CTRL | egui::Modifiers::ALT; b"\x1b[5;7~";
+ PageDown, egui::Modifiers::CTRL | egui::Modifiers::ALT; b"\x1b[6;7~";
+ ArrowUp, egui::Modifiers::CTRL | egui::Modifiers::ALT; b"\x1b[1;7A";
+ ArrowDown, egui::Modifiers::CTRL | egui::Modifiers::ALT; b"\x1b[1;7B";
+ ArrowLeft, egui::Modifiers::CTRL | egui::Modifiers::ALT; b"\x1b[1;7D";
+ ArrowRight, egui::Modifiers::CTRL | egui::Modifiers::ALT; b"\x1b[1;7C";
+ // SHIFT + CTRL + ALT
+ End, egui::Modifiers::SHIFT | egui::Modifiers::CTRL | egui::Modifiers::ALT; b"\x1b[1;8F";
+ Home, egui::Modifiers::SHIFT | egui::Modifiers::CTRL | egui::Modifiers::ALT; b"\x1b[1;8H";
+ ArrowUp, egui::Modifiers::SHIFT | egui::Modifiers::CTRL | egui::Modifiers::ALT; b"\x1b[1;8A";
+ ArrowDown, egui::Modifiers::SHIFT | egui::Modifiers::CTRL | egui::Modifiers::ALT; b"\x1b[1;8B";
+ ArrowLeft, egui::Modifiers::SHIFT | egui::Modifiers::CTRL | egui::Modifiers::ALT; b"\x1b[1;8D";
+ ArrowRight, egui::Modifiers::SHIFT | egui::Modifiers::CTRL | egui::Modifiers::ALT; b"\x1b[1;8C";
+ }
+}
diff --git a/crates/term/src/widget/mod.rs b/crates/term/src/widget/mod.rs
index ecd0f2d9..2c93b34d 100644
--- a/crates/term/src/widget/mod.rs
+++ b/crates/term/src/widget/mod.rs
@@ -22,17 +22,22 @@
// terms of the Steamworks API by Valve Corporation, the licensors of this
// Program grant you additional permission to convey the resulting work.
-use alacritty_terminal::grid::Dimensions;
+use alacritty_terminal::event::Event;
+use alacritty_terminal::term::TermMode;
+use alacritty_terminal::{event_loop::Msg, grid::Dimensions};
use crate::backends::Backend;
+mod keys;
+
mod theme;
pub use theme::Theme;
pub struct Terminal {
backend: T,
theme: Theme, // TODO convert into shared config (possibly do this in luminol-preferences)
- title: String,
+ pub id: egui::Id,
+ pub title: String,
}
pub type ProcessTerminal = Terminal;
@@ -42,6 +47,7 @@ impl Terminal {
fn new(backend: T) -> Self {
Self {
backend,
+ id: egui::Id::new("luminol_term_terminal"),
theme: Theme::default(),
title: "Luminol Terminal".to_string(),
}
@@ -65,14 +71,6 @@ impl Terminal
where
T: Backend,
{
- pub fn title(&self) -> String {
- self.title.to_string()
- }
-
- pub fn id(&self) -> egui::Id {
- egui::Id::new("luminol_term_terminal").with(&self.title)
- }
-
pub fn set_size(&mut self, cols: usize, lines: usize) {
self.backend.resize(lines, cols)
}
@@ -116,8 +114,20 @@ where
pub fn ui(&mut self, ui: &mut egui::Ui) -> color_eyre::Result<()> {
self.backend.update();
+ self.backend.with_event_recv(|recv| {
+ //
+ for event in recv.try_iter() {
+ match event {
+ Event::Title(title) => self.title = title,
+ Event::ResetTitle => "Luminol Terminal".clone_into(&mut self.title),
+
+ _ => {}
+ }
+ }
+ });
+
// TODO cache render jobs
- let job = self.backend.with_term(|term| {
+ let (job, term_mode) = self.backend.with_term(|term| {
let content = term.renderable_content();
let mut job = egui::text::LayoutJob::default();
@@ -125,10 +135,19 @@ where
let mut buf = [0; 4];
let text = cell.c.encode_utf8(&mut buf);
+ let (color, background) = if cell.point == term.grid().cursor.point {
+ (egui::Color32::BLACK, egui::Color32::WHITE)
+ } else {
+ (
+ self.theme.get_ansi_color(cell.fg),
+ self.theme.get_ansi_color(cell.bg),
+ )
+ };
+
let format = egui::TextFormat {
font_id: egui::FontId::monospace(12.),
- color: self.theme.get_ansi_color(cell.fg),
- background: self.theme.get_ansi_color(cell.bg),
+ color,
+ background,
..Default::default()
};
@@ -139,7 +158,7 @@ where
}
}
- job
+ (job, *term.mode())
});
let galley = ui.fonts(|f| f.layout_job(job));
@@ -152,13 +171,71 @@ where
egui::Color32::from_rgb(40, 39, 39),
);
- painter.galley(response.rect.min, galley, egui::Color32::WHITE);
+ painter.galley(response.rect.min, galley.clone(), egui::Color32::WHITE);
+
+ let id = response.id;
+ let event_filter = egui::EventFilter {
+ tab: true,
+ horizontal_arrows: true,
+ vertical_arrows: true,
+ escape: true,
+ };
if response.hovered() {
ui.output_mut(|o| o.mutable_text_under_cursor = true);
ui.ctx().set_cursor_icon(egui::CursorIcon::Text);
}
+ if response.clicked() && !response.lost_focus() {
+ ui.memory_mut(|mem| mem.request_focus(id))
+ }
+
+ if ui.memory(|mem| mem.has_focus(id)) {
+ ui.memory_mut(|mem| mem.set_focus_lock_filter(id, event_filter));
+
+ let (events, modifiers) = ui.input(|i| (i.filtered_events(&event_filter), i.modifiers));
+
+ for event in events {
+ match event {
+ egui::Event::Paste(text) | egui::Event::Text(text) => {
+ let bytes = text.into_bytes();
+ let cow = std::borrow::Cow::Owned(bytes);
+ self.backend.send(Msg::Input(cow));
+ }
+ egui::Event::Key {
+ key, pressed: true, ..
+ } => {
+ if let Some(bytes) = keys::key_to_codes(key, modifiers, term_mode) {
+ let cow = std::borrow::Cow::Borrowed(bytes);
+ self.backend.send(Msg::Input(cow));
+ }
+ }
+ egui::Event::Scroll(scroll_delta) => {
+ let delta = scroll_delta.y as i32;
+ if term_mode.contains(TermMode::ALT_SCREEN | TermMode::ALTERNATE_SCROLL) {
+ let line_cmd = if delta.is_positive() { b'A' } else { b'B' };
+ let mut bytes = vec![];
+
+ for _ in 0..delta.abs() {
+ bytes.push(0x1b);
+ bytes.push(b'O');
+ bytes.push(line_cmd);
+ }
+
+ let cow = std::borrow::Cow::Owned(bytes);
+ self.backend.send(Msg::Input(cow));
+ } else {
+ self.backend.with_term(|term| {
+ term.grid_mut()
+ .scroll_display(alacritty_terminal::grid::Scroll::Delta(delta));
+ });
+ }
+ }
+ _ => {}
+ }
+ }
+ }
+
Ok(())
}
diff --git a/crates/ui/src/windows/console.rs b/crates/ui/src/windows/console.rs
index 6c44bebe..5e090169 100644
--- a/crates/ui/src/windows/console.rs
+++ b/crates/ui/src/windows/console.rs
@@ -37,11 +37,11 @@ impl Window {
impl luminol_core::Window for Window {
fn name(&self) -> String {
- self.term.title()
+ self.term.title.clone()
}
fn id(&self) -> egui::Id {
- self.term.id()
+ self.term.id
}
fn requires_filesystem(&self) -> bool {
@@ -55,7 +55,7 @@ impl luminol_core::Window for Window {
update_state: &mut luminol_core::UpdateState<'_>,
) {
egui::Window::new(self.name())
- .id(self.term.id())
+ .id(self.term.id)
.open(open)
.resizable(false)
.show(ctx, |ui| {
diff --git a/src/app/log_window.rs b/src/app/log_window.rs
index 8029d035..d69df25d 100644
--- a/src/app/log_window.rs
+++ b/src/app/log_window.rs
@@ -43,7 +43,7 @@ impl LogWindow {
self.term.update();
egui::Window::new("Log")
- .id(self.term.id())
+ .id(self.term.id)
.open(&mut self.term_shown)
.resizable(false)
.show(ui.ctx(), |ui| {