Add copy/paste
This commit is contained in:
parent
c2fb3573d5
commit
4ffad110b6
3 changed files with 111 additions and 26 deletions
97
src/main.rs
97
src/main.rs
|
|
@ -5,13 +5,15 @@ use alacritty_terminal::{
|
||||||
config::Config as TermConfig, event::Event as TermEvent, term::color::Colors as TermColors, tty,
|
config::Config as TermConfig, event::Event as TermEvent, term::color::Colors as TermColors, tty,
|
||||||
};
|
};
|
||||||
use cosmic::{
|
use cosmic::{
|
||||||
app::{Command, Core, Settings},
|
app::{message, Command, Core, Settings},
|
||||||
cosmic_theme, executor,
|
cosmic_theme, executor,
|
||||||
iced::{
|
iced::{
|
||||||
|
clipboard, event,
|
||||||
futures::SinkExt,
|
futures::SinkExt,
|
||||||
|
keyboard::{Event as KeyEvent, KeyCode, Modifiers},
|
||||||
subscription::{self, Subscription},
|
subscription::{self, Subscription},
|
||||||
widget::row,
|
widget::row,
|
||||||
window, Alignment, Length,
|
window, Alignment, Event, Length,
|
||||||
},
|
},
|
||||||
iced_core::Size,
|
iced_core::Size,
|
||||||
style,
|
style,
|
||||||
|
|
@ -58,6 +60,9 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
/// Messages that are used specifically by our [`App`].
|
/// Messages that are used specifically by our [`App`].
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum Message {
|
pub enum Message {
|
||||||
|
Copy,
|
||||||
|
Paste,
|
||||||
|
PasteValue(String),
|
||||||
TabActivate(segmented_button::Entity),
|
TabActivate(segmented_button::Entity),
|
||||||
TabClose(segmented_button::Entity),
|
TabClose(segmented_button::Entity),
|
||||||
TabNew,
|
TabNew,
|
||||||
|
|
@ -118,6 +123,33 @@ impl cosmic::Application for App {
|
||||||
/// Handle application events here.
|
/// Handle application events here.
|
||||||
fn update(&mut self, message: Self::Message) -> Command<Self::Message> {
|
fn update(&mut self, message: Self::Message) -> Command<Self::Message> {
|
||||||
match message {
|
match message {
|
||||||
|
Message::Copy => {
|
||||||
|
if let Some(terminal) = self
|
||||||
|
.tab_model
|
||||||
|
.data::<Mutex<Terminal>>(self.tab_model.active())
|
||||||
|
{
|
||||||
|
let terminal = terminal.lock().unwrap();
|
||||||
|
let term = terminal.term.lock();
|
||||||
|
if let Some(text) = term.selection_to_string() {
|
||||||
|
return clipboard::write(text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Message::Paste => {
|
||||||
|
return clipboard::read(|value_opt| match value_opt {
|
||||||
|
Some(value) => message::app(Message::PasteValue(value)),
|
||||||
|
None => message::none(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Message::PasteValue(value) => {
|
||||||
|
if let Some(terminal) = self
|
||||||
|
.tab_model
|
||||||
|
.data::<Mutex<Terminal>>(self.tab_model.active())
|
||||||
|
{
|
||||||
|
let terminal = terminal.lock().unwrap();
|
||||||
|
terminal.paste(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
Message::TabActivate(entity) => {
|
Message::TabActivate(entity) => {
|
||||||
self.tab_model.activate(entity);
|
self.tab_model.activate(entity);
|
||||||
return self.update_title();
|
return self.update_title();
|
||||||
|
|
@ -279,26 +311,51 @@ impl cosmic::Application for App {
|
||||||
|
|
||||||
fn subscription(&self) -> Subscription<Self::Message> {
|
fn subscription(&self) -> Subscription<Self::Message> {
|
||||||
struct TerminalEventWorker;
|
struct TerminalEventWorker;
|
||||||
subscription::channel(
|
Subscription::batch([
|
||||||
TypeId::of::<TerminalEventWorker>(),
|
event::listen_with(|event, _status| match event {
|
||||||
100,
|
Event::Keyboard(KeyEvent::KeyPressed {
|
||||||
|mut output| async move {
|
key_code: KeyCode::C,
|
||||||
let (event_tx, mut event_rx) = mpsc::channel(100);
|
modifiers,
|
||||||
output.send(Message::TermEventTx(event_tx)).await.unwrap();
|
}) => {
|
||||||
|
if modifiers == Modifiers::CTRL | Modifiers::SHIFT {
|
||||||
// Create first terminal tab
|
Some(Message::Copy)
|
||||||
output.send(Message::TabNew).await.unwrap();
|
} else {
|
||||||
|
None
|
||||||
while let Some((entity, event)) = event_rx.recv().await {
|
}
|
||||||
output
|
|
||||||
.send(Message::TermEvent(entity, event))
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
}
|
}
|
||||||
|
Event::Keyboard(KeyEvent::KeyPressed {
|
||||||
|
key_code: KeyCode::V,
|
||||||
|
modifiers,
|
||||||
|
}) => {
|
||||||
|
if modifiers == Modifiers::CTRL | Modifiers::SHIFT {
|
||||||
|
Some(Message::Paste)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
}),
|
||||||
|
subscription::channel(
|
||||||
|
TypeId::of::<TerminalEventWorker>(),
|
||||||
|
100,
|
||||||
|
|mut output| async move {
|
||||||
|
let (event_tx, mut event_rx) = mpsc::channel(100);
|
||||||
|
output.send(Message::TermEventTx(event_tx)).await.unwrap();
|
||||||
|
|
||||||
panic!("terminal event channel closed");
|
// Create first terminal tab
|
||||||
},
|
output.send(Message::TabNew).await.unwrap();
|
||||||
)
|
|
||||||
|
while let Some((entity, event)) = event_rx.recv().await {
|
||||||
|
output
|
||||||
|
.send(Message::TermEvent(entity, event))
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
panic!("terminal event channel closed");
|
||||||
|
},
|
||||||
|
),
|
||||||
|
])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ use alacritty_terminal::{
|
||||||
term::{
|
term::{
|
||||||
cell::Flags,
|
cell::Flags,
|
||||||
color::{Colors, Rgb},
|
color::{Colors, Rgb},
|
||||||
|
TermMode,
|
||||||
},
|
},
|
||||||
tty, Term,
|
tty, Term,
|
||||||
};
|
};
|
||||||
|
|
@ -207,6 +208,27 @@ impl Terminal {
|
||||||
self.scroll(TerminalScroll::Bottom);
|
self.scroll(TerminalScroll::Bottom);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn paste(&self, value: String) {
|
||||||
|
// This code is ported from alacritty
|
||||||
|
let bracketed_paste = {
|
||||||
|
let term = self.term.lock();
|
||||||
|
term.mode().contains(TermMode::BRACKETED_PASTE)
|
||||||
|
};
|
||||||
|
if bracketed_paste {
|
||||||
|
self.input_no_scroll(&b"\x1b[200~"[..]);
|
||||||
|
self.input_no_scroll(value.replace('\x1b', "").into_bytes());
|
||||||
|
self.input_scroll(&b"\x1b[201~"[..]);
|
||||||
|
} else {
|
||||||
|
// In non-bracketed (ie: normal) mode, terminal applications cannot distinguish
|
||||||
|
// pasted data from keystrokes.
|
||||||
|
// In theory, we should construct the keystrokes needed to produce the data we are
|
||||||
|
// pasting... since that's neither practical nor sensible (and probably an impossible
|
||||||
|
// task to solve in a general way), we'll just replace line breaks (windows and unix
|
||||||
|
// style) with a single carriage return (\r, which is what the Enter key produces).
|
||||||
|
self.input_scroll(value.replace("\r\n", "\r").replace('\n', "\r").into_bytes());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn resize(&mut self, width: u32, height: u32) {
|
pub fn resize(&mut self, width: u32, height: u32) {
|
||||||
if width != self.size.width || height != self.size.height {
|
if width != self.size.width || height != self.size.height {
|
||||||
let instant = Instant::now();
|
let instant = Instant::now();
|
||||||
|
|
|
||||||
|
|
@ -224,7 +224,7 @@ where
|
||||||
background_color.r() as f32 / 255.0,
|
background_color.r() as f32 / 255.0,
|
||||||
background_color.g() as f32 / 255.0,
|
background_color.g() as f32 / 255.0,
|
||||||
background_color.b() as f32 / 255.0,
|
background_color.b() as f32 / 255.0,
|
||||||
background_color.a() as f32 / 255.0
|
background_color.a() as f32 / 255.0,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -405,36 +405,42 @@ where
|
||||||
state.modifiers.logo(),
|
state.modifiers.logo(),
|
||||||
state.modifiers.control(),
|
state.modifiers.control(),
|
||||||
state.modifiers.alt(),
|
state.modifiers.alt(),
|
||||||
|
state.modifiers.shift(),
|
||||||
) {
|
) {
|
||||||
(true, _, _) => {
|
(true, _, _, _) => {
|
||||||
// Ignore super
|
// Ignore super
|
||||||
}
|
}
|
||||||
(false, true, _) => {
|
(false, true, _, false) => {
|
||||||
// Handle ctrl for control characters (Ctrl-A to Ctrl-Z)
|
// Handle ctrl for control characters (Ctrl-A to Ctrl-Z)
|
||||||
if character.is_control() {
|
if character.is_control() {
|
||||||
let mut buf = [0, 0, 0, 0];
|
let mut buf = [0, 0, 0, 0];
|
||||||
let str = character.encode_utf8(&mut buf);
|
let str = character.encode_utf8(&mut buf);
|
||||||
terminal.input_scroll(str.as_bytes().to_vec());
|
terminal.input_scroll(str.as_bytes().to_vec());
|
||||||
|
status = Status::Captured;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(false, false, true) => {
|
(false, true, _, true) => {
|
||||||
|
// Ignore ctrl+shift
|
||||||
|
}
|
||||||
|
(false, false, true, _) => {
|
||||||
if !character.is_control() {
|
if !character.is_control() {
|
||||||
// Handle alt for non-control characters
|
// Handle alt for non-control characters
|
||||||
let mut buf = [0x1B, 0, 0, 0, 0];
|
let mut buf = [0x1B, 0, 0, 0, 0];
|
||||||
let str = character.encode_utf8(&mut buf[1..]);
|
let str = character.encode_utf8(&mut buf[1..]);
|
||||||
terminal.input_scroll(str.as_bytes().to_vec());
|
terminal.input_scroll(str.as_bytes().to_vec());
|
||||||
|
status = Status::Captured;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(false, false, false) => {
|
(false, false, false, _) => {
|
||||||
// Handle no modifiers for non-control characters
|
// Handle no modifiers for non-control characters
|
||||||
if !character.is_control() {
|
if !character.is_control() {
|
||||||
let mut buf = [0, 0, 0, 0];
|
let mut buf = [0, 0, 0, 0];
|
||||||
let str = character.encode_utf8(&mut buf);
|
let str = character.encode_utf8(&mut buf);
|
||||||
terminal.input_scroll(str.as_bytes().to_vec());
|
terminal.input_scroll(str.as_bytes().to_vec());
|
||||||
|
status = Status::Captured;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
status = Status::Captured;
|
|
||||||
}
|
}
|
||||||
Event::Mouse(MouseEvent::ButtonPressed(button)) => {
|
Event::Mouse(MouseEvent::ButtonPressed(button)) => {
|
||||||
if let Some(p) = cursor_position.position_in(layout.bounds()) {
|
if let Some(p) = cursor_position.position_in(layout.bounds()) {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue