mirror of
https://github.com/FliegendeWurst/cursive.git
synced 2024-11-09 19:00:46 +00:00
Fix resize event
This commit is contained in:
parent
14fe6f3b36
commit
804e41ec43
11
Cargo.toml
11
Cargo.toml
@ -25,6 +25,10 @@ xi-unicode = "0.1.0"
|
|||||||
libc = "0.2"
|
libc = "0.2"
|
||||||
chan = "0.1"
|
chan = "0.1"
|
||||||
|
|
||||||
|
[dependencies.ioctl-rs]
|
||||||
|
optional = true
|
||||||
|
version = "0.2"
|
||||||
|
|
||||||
[dependencies.num]
|
[dependencies.num]
|
||||||
default-features = false
|
default-features = false
|
||||||
version = "0.1"
|
version = "0.1"
|
||||||
@ -38,7 +42,6 @@ optional = true
|
|||||||
version = "1.3.1"
|
version = "1.3.1"
|
||||||
|
|
||||||
[dependencies.chan-signal]
|
[dependencies.chan-signal]
|
||||||
optional = true
|
|
||||||
version = "0.3"
|
version = "0.3"
|
||||||
|
|
||||||
[dependencies.ncurses]
|
[dependencies.ncurses]
|
||||||
@ -68,9 +71,9 @@ pretty-bytes = "0.2.2"
|
|||||||
blt-backend = ["bear-lib-terminal"]
|
blt-backend = ["bear-lib-terminal"]
|
||||||
default = ["ncurses-backend"]
|
default = ["ncurses-backend"]
|
||||||
markdown = ["pulldown-cmark"]
|
markdown = ["pulldown-cmark"]
|
||||||
ncurses-backend = ["ncurses", "maplit"]
|
ncurses-backend = ["ncurses", "maplit", "ioctl-rs"]
|
||||||
pancurses-backend = ["pancurses", "maplit"]
|
pancurses-backend = ["pancurses", "maplit", "ioctl-rs"]
|
||||||
termion-backend = ["termion", "chan-signal"]
|
termion-backend = ["termion"]
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
name = "cursive"
|
name = "cursive"
|
||||||
|
@ -7,6 +7,8 @@ use event::{Event, Key};
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use theme::{BaseColor, Color, ColorPair};
|
use theme::{BaseColor, Color, ColorPair};
|
||||||
|
|
||||||
|
mod sizes;
|
||||||
|
|
||||||
#[cfg(feature = "ncurses")]
|
#[cfg(feature = "ncurses")]
|
||||||
pub mod n;
|
pub mod n;
|
||||||
|
|
||||||
|
@ -1,29 +1,37 @@
|
|||||||
extern crate ncurses;
|
extern crate ncurses;
|
||||||
|
|
||||||
use self::super::split_i32;
|
|
||||||
use self::ncurses::mmask_t;
|
|
||||||
use backend;
|
|
||||||
use event::{Event, Key, MouseButton, MouseEvent};
|
|
||||||
use theme::{Color, ColorPair, Effect};
|
|
||||||
use utf8;
|
|
||||||
use vec::Vec2;
|
|
||||||
|
|
||||||
use std::cell::{Cell, RefCell};
|
use std::cell::{Cell, RefCell};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::ffi::CString;
|
use std::ffi::CString;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
|
use std::sync::Arc;
|
||||||
use std::thread;
|
use std::thread;
|
||||||
|
|
||||||
use chan;
|
use chan;
|
||||||
|
use chan_signal;
|
||||||
use libc;
|
use libc;
|
||||||
|
|
||||||
|
use backend;
|
||||||
|
use event::{Event, Key, MouseButton, MouseEvent};
|
||||||
|
use theme::{Color, ColorPair, Effect};
|
||||||
|
use utf8;
|
||||||
|
use vec::Vec2;
|
||||||
|
|
||||||
|
use self::super::split_i32;
|
||||||
|
use self::ncurses::mmask_t;
|
||||||
|
|
||||||
pub struct Backend {
|
pub struct Backend {
|
||||||
current_style: Cell<ColorPair>,
|
current_style: Cell<ColorPair>,
|
||||||
|
|
||||||
// Maps (front, back) ncurses colors to ncurses pairs
|
// Maps (front, back) ncurses colors to ncurses pairs
|
||||||
pairs: RefCell<HashMap<(i16, i16), i16>>,
|
pairs: RefCell<HashMap<(i16, i16), i16>>,
|
||||||
|
|
||||||
|
// This is set by the SIGWINCH-triggered thread.
|
||||||
|
// When TRUE, we should tell ncurses about the new terminal size.
|
||||||
|
needs_resize: Arc<AtomicBool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct InputParser {
|
struct InputParser {
|
||||||
@ -202,6 +210,7 @@ impl Backend {
|
|||||||
let c = Backend {
|
let c = Backend {
|
||||||
current_style: Cell::new(ColorPair::from_256colors(0, 0)),
|
current_style: Cell::new(ColorPair::from_256colors(0, 0)),
|
||||||
pairs: RefCell::new(HashMap::new()),
|
pairs: RefCell::new(HashMap::new()),
|
||||||
|
needs_resize: Arc::new(AtomicBool::new(false)),
|
||||||
};
|
};
|
||||||
|
|
||||||
Box::new(c)
|
Box::new(c)
|
||||||
@ -251,6 +260,17 @@ impl Backend {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Called when a resize event is detected.
|
||||||
|
///
|
||||||
|
/// We need to have ncurses update its representation of the screen.
|
||||||
|
fn on_resize() {
|
||||||
|
// Get size using ioctl
|
||||||
|
let size = super::sizes::terminal_size();
|
||||||
|
|
||||||
|
// Send the size to ncurses
|
||||||
|
ncurses::resize_term(size.y as i32, size.x as i32);
|
||||||
|
}
|
||||||
|
|
||||||
impl backend::Backend for Backend {
|
impl backend::Backend for Backend {
|
||||||
fn screen_size(&self) -> Vec2 {
|
fn screen_size(&self) -> Vec2 {
|
||||||
let mut x: i32 = 0;
|
let mut x: i32 = 0;
|
||||||
@ -267,9 +287,35 @@ impl backend::Backend for Backend {
|
|||||||
&mut self, event_sink: chan::Sender<Event>,
|
&mut self, event_sink: chan::Sender<Event>,
|
||||||
stops: chan::Receiver<bool>,
|
stops: chan::Receiver<bool>,
|
||||||
) {
|
) {
|
||||||
let mut parser = InputParser::new(event_sink);
|
let resize = chan_signal::notify(&[chan_signal::Signal::WINCH]);
|
||||||
|
let (sender, receiver) = chan::async();
|
||||||
|
|
||||||
// Start an input thread
|
let needs_resize = Arc::clone(&self.needs_resize);
|
||||||
|
|
||||||
|
// This thread will merge resize and input into event_sink
|
||||||
|
thread::spawn(move || {
|
||||||
|
loop {
|
||||||
|
chan_select! {
|
||||||
|
resize.recv() => {
|
||||||
|
// Tell ncurses about the new terminal size.
|
||||||
|
// Well, do the actual resizing later on, in the main thread.
|
||||||
|
// Ncurses isn't really thread-safe so calling resize_term() can crash
|
||||||
|
// other calls like clear() or refresh().
|
||||||
|
needs_resize.store(true, Ordering::Relaxed);
|
||||||
|
event_sink.send(Event::WindowResize);
|
||||||
|
},
|
||||||
|
receiver.recv() -> event => {
|
||||||
|
match event {
|
||||||
|
Some(event) => event_sink.send(event),
|
||||||
|
None => return,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let mut parser = InputParser::new(sender);
|
||||||
|
// This thread will just fill the input channel
|
||||||
thread::spawn(move || {
|
thread::spawn(move || {
|
||||||
loop {
|
loop {
|
||||||
// This sends events to the event sender.
|
// This sends events to the event sender.
|
||||||
@ -321,6 +367,10 @@ impl backend::Backend for Backend {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn clear(&self, color: Color) {
|
fn clear(&self, color: Color) {
|
||||||
|
if self.needs_resize.swap(false, Ordering::Relaxed) {
|
||||||
|
on_resize();
|
||||||
|
}
|
||||||
|
|
||||||
let id = self.get_or_create(ColorPair {
|
let id = self.get_or_create(ColorPair {
|
||||||
front: color,
|
front: color,
|
||||||
back: color,
|
back: color,
|
||||||
|
@ -1,18 +1,23 @@
|
|||||||
extern crate pancurses;
|
extern crate pancurses;
|
||||||
|
|
||||||
use self::super::split_i32;
|
|
||||||
use self::pancurses::mmask_t;
|
|
||||||
use backend;
|
|
||||||
use chan;
|
|
||||||
use event::{Event, Key, MouseButton, MouseEvent};
|
|
||||||
use std::cell::{Cell, RefCell};
|
use std::cell::{Cell, RefCell};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::io::{stdout, Write};
|
use std::io::{stdout, Write};
|
||||||
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::thread;
|
use std::thread;
|
||||||
|
|
||||||
|
use chan;
|
||||||
|
use chan_signal;
|
||||||
|
|
||||||
|
use backend;
|
||||||
|
use event::{Event, Key, MouseButton, MouseEvent};
|
||||||
use theme::{Color, ColorPair, Effect};
|
use theme::{Color, ColorPair, Effect};
|
||||||
use vec::Vec2;
|
use vec::Vec2;
|
||||||
|
|
||||||
|
use self::super::split_i32;
|
||||||
|
use self::pancurses::mmask_t;
|
||||||
|
|
||||||
pub struct Backend {
|
pub struct Backend {
|
||||||
// Used
|
// Used
|
||||||
current_style: Cell<ColorPair>,
|
current_style: Cell<ColorPair>,
|
||||||
@ -20,6 +25,10 @@ pub struct Backend {
|
|||||||
|
|
||||||
// pancurses needs a handle to the current window.
|
// pancurses needs a handle to the current window.
|
||||||
window: Arc<pancurses::Window>,
|
window: Arc<pancurses::Window>,
|
||||||
|
|
||||||
|
// This is set by the SIGWINCH-triggered thread.
|
||||||
|
// When TRUE, we should tell ncurses about the new terminal size.
|
||||||
|
needs_resize: Arc<AtomicBool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct InputParser {
|
struct InputParser {
|
||||||
@ -267,7 +276,7 @@ fn find_closest_pair(pair: &ColorPair) -> (i16, i16) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Backend {
|
impl Backend {
|
||||||
pub fn init() -> Box<Self> {
|
pub fn init() -> Box<backend::Backend> {
|
||||||
::std::env::set_var("ESCDELAY", "25");
|
::std::env::set_var("ESCDELAY", "25");
|
||||||
|
|
||||||
let window = pancurses::initscr();
|
let window = pancurses::initscr();
|
||||||
@ -293,6 +302,7 @@ impl Backend {
|
|||||||
current_style: Cell::new(ColorPair::from_256colors(0, 0)),
|
current_style: Cell::new(ColorPair::from_256colors(0, 0)),
|
||||||
pairs: RefCell::new(HashMap::new()),
|
pairs: RefCell::new(HashMap::new()),
|
||||||
window: Arc::new(window),
|
window: Arc::new(window),
|
||||||
|
needs_resize: Arc::new(AtomicBool::new(false)),
|
||||||
};
|
};
|
||||||
|
|
||||||
Box::new(c)
|
Box::new(c)
|
||||||
@ -343,6 +353,17 @@ impl Backend {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Called when a resize event is detected.
|
||||||
|
///
|
||||||
|
/// We need to have ncurses update its representation of the screen.
|
||||||
|
fn on_resize() {
|
||||||
|
// Get size using ioctl
|
||||||
|
let size = super::sizes::terminal_size();
|
||||||
|
|
||||||
|
// Send the size to ncurses
|
||||||
|
pancurses::resize_term(size.y as i32, size.x as i32);
|
||||||
|
}
|
||||||
|
|
||||||
impl backend::Backend for Backend {
|
impl backend::Backend for Backend {
|
||||||
fn screen_size(&self) -> Vec2 {
|
fn screen_size(&self) -> Vec2 {
|
||||||
// Coordinates are reversed here
|
// Coordinates are reversed here
|
||||||
@ -393,6 +414,10 @@ impl backend::Backend for Backend {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn clear(&self, color: Color) {
|
fn clear(&self, color: Color) {
|
||||||
|
if self.needs_resize.swap(false, Ordering::Relaxed) {
|
||||||
|
on_resize();
|
||||||
|
}
|
||||||
|
|
||||||
let id = self.get_or_create(ColorPair {
|
let id = self.get_or_create(ColorPair {
|
||||||
front: color,
|
front: color,
|
||||||
back: color,
|
back: color,
|
||||||
@ -413,8 +438,34 @@ impl backend::Backend for Backend {
|
|||||||
&mut self, event_sink: chan::Sender<Event>,
|
&mut self, event_sink: chan::Sender<Event>,
|
||||||
stops: chan::Receiver<bool>,
|
stops: chan::Receiver<bool>,
|
||||||
) {
|
) {
|
||||||
|
let resize = chan_signal::notify(&[chan_signal::Signal::WINCH]);
|
||||||
|
let (sender, receiver) = chan::async();
|
||||||
|
let needs_resize = Arc::clone(&self.needs_resize);
|
||||||
|
|
||||||
|
// This thread will merge resize and input into event_sink
|
||||||
|
thread::spawn(move || {
|
||||||
|
loop {
|
||||||
|
chan_select! {
|
||||||
|
resize.recv() => {
|
||||||
|
// Tell ncurses about the new terminal size.
|
||||||
|
// Well, do the actual resizing later on, in the main thread.
|
||||||
|
// Ncurses isn't really thread-safe so calling resize_term() can crash
|
||||||
|
// other calls like clear() or refresh().
|
||||||
|
needs_resize.store(true, Ordering::Relaxed);
|
||||||
|
event_sink.send(Event::WindowResize);
|
||||||
|
},
|
||||||
|
receiver.recv() -> event => {
|
||||||
|
match event {
|
||||||
|
Some(event) => event_sink.send(event),
|
||||||
|
None => return,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
let mut input_parser =
|
let mut input_parser =
|
||||||
InputParser::new(event_sink, Arc::clone(&self.window));
|
InputParser::new(sender, Arc::clone(&self.window));
|
||||||
|
|
||||||
thread::spawn(move || {
|
thread::spawn(move || {
|
||||||
loop {
|
loop {
|
||||||
|
23
src/backend/curses/sizes.rs
Normal file
23
src/backend/curses/sizes.rs
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
extern crate ioctl_rs as ioctl;
|
||||||
|
|
||||||
|
use libc::{c_ushort, STDOUT_FILENO};
|
||||||
|
use std::mem;
|
||||||
|
|
||||||
|
use vec::Vec2;
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
struct TermSize {
|
||||||
|
row: c_ushort,
|
||||||
|
col: c_ushort,
|
||||||
|
_x: c_ushort,
|
||||||
|
_y: c_ushort,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the size of the terminal.
|
||||||
|
pub fn terminal_size() -> Vec2 {
|
||||||
|
unsafe {
|
||||||
|
let mut size: TermSize = mem::zeroed();
|
||||||
|
ioctl::ioctl(STDOUT_FILENO, ioctl::TIOCGWINSZ, &mut size as *mut _);
|
||||||
|
Vec2::new(size.col as usize, size.row as usize)
|
||||||
|
}
|
||||||
|
}
|
@ -41,6 +41,8 @@ impl InputParser {
|
|||||||
fn new(event_sink: chan::Sender<Event>) -> Self {
|
fn new(event_sink: chan::Sender<Event>) -> Self {
|
||||||
let (sender, receiver) = chan::async();
|
let (sender, receiver) = chan::async();
|
||||||
|
|
||||||
|
let resize = chan_signal::notify(&[chan_signal::Signal::WINCH]);
|
||||||
|
|
||||||
// Fill the input channel
|
// Fill the input channel
|
||||||
thread::spawn(move || {
|
thread::spawn(move || {
|
||||||
for key in ::std::io::stdin().events() {
|
for key in ::std::io::stdin().events() {
|
||||||
@ -51,7 +53,7 @@ impl InputParser {
|
|||||||
});
|
});
|
||||||
|
|
||||||
InputParser {
|
InputParser {
|
||||||
resize: chan_signal::notify(&[chan_signal::Signal::WINCH]),
|
resize,
|
||||||
event_sink,
|
event_sink,
|
||||||
last_button: None,
|
last_button: None,
|
||||||
input: receiver,
|
input: receiver,
|
||||||
@ -181,7 +183,7 @@ impl Effectable for theme::Effect {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Backend {
|
impl Backend {
|
||||||
pub fn init() -> Box<Self> {
|
pub fn init() -> Box<backend::Backend> {
|
||||||
print!("{}", termion::cursor::Hide);
|
print!("{}", termion::cursor::Hide);
|
||||||
|
|
||||||
// TODO: lock stdout
|
// TODO: lock stdout
|
||||||
|
@ -1,12 +1,14 @@
|
|||||||
use backend;
|
|
||||||
use chan;
|
|
||||||
use direction;
|
|
||||||
use event::{Callback, Event, EventResult};
|
|
||||||
use printer::Printer;
|
|
||||||
use std::any::Any;
|
use std::any::Any;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
|
use chan;
|
||||||
|
|
||||||
|
use backend;
|
||||||
|
use direction;
|
||||||
|
use event::{Callback, Event, EventResult};
|
||||||
|
use printer::Printer;
|
||||||
use theme;
|
use theme;
|
||||||
use vec::Vec2;
|
use vec::Vec2;
|
||||||
use view::{self, Finder, IntoBoxedView, Position, View};
|
use view::{self, Finder, IntoBoxedView, Position, View};
|
||||||
@ -121,7 +123,10 @@ impl Default for Cursive {
|
|||||||
|
|
||||||
impl Cursive {
|
impl Cursive {
|
||||||
/// Creates a new Cursive root, and initialize the back-end.
|
/// Creates a new Cursive root, and initialize the back-end.
|
||||||
pub fn new(mut backend: Box<backend::Backend>) -> Self {
|
pub fn new<F>(backend_init: F) -> Self
|
||||||
|
where
|
||||||
|
F: FnOnce() -> Box<backend::Backend>,
|
||||||
|
{
|
||||||
let theme = theme::load_default();
|
let theme = theme::load_default();
|
||||||
|
|
||||||
let (cb_sink, cb_source) = chan::async();
|
let (cb_sink, cb_source) = chan::async();
|
||||||
@ -129,6 +134,7 @@ impl Cursive {
|
|||||||
|
|
||||||
let (stop_sink, stop_source) = chan::async();
|
let (stop_sink, stop_source) = chan::async();
|
||||||
|
|
||||||
|
let mut backend = backend_init();
|
||||||
backend.start_input_thread(event_sink.clone(), stop_source);
|
backend.start_input_thread(event_sink.clone(), stop_source);
|
||||||
|
|
||||||
Cursive {
|
Cursive {
|
||||||
@ -152,32 +158,32 @@ impl Cursive {
|
|||||||
/// Creates a new Cursive root using a ncurses backend.
|
/// Creates a new Cursive root using a ncurses backend.
|
||||||
#[cfg(feature = "ncurses")]
|
#[cfg(feature = "ncurses")]
|
||||||
pub fn ncurses() -> Self {
|
pub fn ncurses() -> Self {
|
||||||
Self::new(backend::curses::n::Backend::init())
|
Self::new(backend::curses::n::Backend::init)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a new Cursive root using a pancurses backend.
|
/// Creates a new Cursive root using a pancurses backend.
|
||||||
#[cfg(feature = "pancurses")]
|
#[cfg(feature = "pancurses")]
|
||||||
pub fn pancurses() -> Self {
|
pub fn pancurses() -> Self {
|
||||||
Self::new(backend::curses::pan::Backend::init())
|
Self::new(backend::curses::pan::Backend::init)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a new Cursive root using a termion backend.
|
/// Creates a new Cursive root using a termion backend.
|
||||||
#[cfg(feature = "termion")]
|
#[cfg(feature = "termion")]
|
||||||
pub fn termion() -> Self {
|
pub fn termion() -> Self {
|
||||||
Self::new(backend::termion::Backend::init())
|
Self::new(backend::termion::Backend::init)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a new Cursive root using a bear-lib-terminal backend.
|
/// Creates a new Cursive root using a bear-lib-terminal backend.
|
||||||
#[cfg(feature = "bear-lib-terminal")]
|
#[cfg(feature = "bear-lib-terminal")]
|
||||||
pub fn blt() -> Self {
|
pub fn blt() -> Self {
|
||||||
Self::new(backend::blt::Backend::init())
|
Self::new(backend::blt::Backend::init)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a new Cursive root using a dummy backend.
|
/// Creates a new Cursive root using a dummy backend.
|
||||||
///
|
///
|
||||||
/// Nothing will be output. This is mostly here for tests.
|
/// Nothing will be output. This is mostly here for tests.
|
||||||
pub fn dummy() -> Self {
|
pub fn dummy() -> Self {
|
||||||
Self::new(backend::dummy::Backend::init())
|
Self::new(backend::dummy::Backend::init)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a sink for asynchronous callbacks.
|
/// Returns a sink for asynchronous callbacks.
|
||||||
@ -715,6 +721,7 @@ impl Cursive {
|
|||||||
// Wait for next event.
|
// Wait for next event.
|
||||||
match self.poll() {
|
match self.poll() {
|
||||||
Interruption::Event(event) => {
|
Interruption::Event(event) => {
|
||||||
|
// eprintln!("{:?}, {:?}", event, self.screen_size());
|
||||||
if event == Event::Exit {
|
if event == Event::Exit {
|
||||||
self.quit();
|
self.quit();
|
||||||
}
|
}
|
||||||
|
@ -74,6 +74,7 @@ extern crate chan;
|
|||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate maplit;
|
extern crate maplit;
|
||||||
|
|
||||||
|
extern crate chan_signal;
|
||||||
extern crate libc;
|
extern crate libc;
|
||||||
extern crate num;
|
extern crate num;
|
||||||
extern crate owning_ref;
|
extern crate owning_ref;
|
||||||
|
Loading…
Reference in New Issue
Block a user