1
1
mirror of https://github.com/wez/wezterm.git synced 2024-11-23 23:21:08 +03:00

move Tabs -> Mux windows hash

This commit is contained in:
Wez Furlong 2019-03-10 10:55:33 +00:00
parent a1bdddd8e5
commit e69290715b
6 changed files with 237 additions and 144 deletions

View File

@ -4,11 +4,12 @@ use crate::config::Config;
use crate::failure::Error;
use crate::font::FontConfiguration;
use crate::guicommon::host::{HostHelper, HostImpl, TabHost};
use crate::guicommon::tabs::Tabs;
use crate::guicommon::window::{Dimensions, TerminalWindow};
use crate::guiloop::glutinloop::GuiEventLoop;
use crate::guiloop::SessionTerminated;
use crate::mux::tab::{Tab, TabId};
use crate::mux::window::WindowId;
use crate::mux::Mux;
use crate::opengl::render::Renderer;
use glium;
use glium::glutin::dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize};
@ -82,17 +83,15 @@ pub struct GliumTerminalWindow {
last_mouse_coords: PhysicalPosition,
last_modifiers: KeyModifiers,
allow_received_character: bool,
tabs: Tabs,
mux_window_id: WindowId,
have_pending_resize_check: bool,
}
impl TerminalWindow for GliumTerminalWindow {
fn get_tabs(&self) -> &Tabs {
&self.tabs
}
fn get_tabs_mut(&mut self) -> &mut Tabs {
&mut self.tabs
fn get_mux_window_id(&self) -> WindowId {
self.mux_window_id
}
fn config(&self) -> &Arc<Config> {
&self.config
}
@ -115,9 +114,6 @@ impl TerminalWindow for GliumTerminalWindow {
fn recreate_texture_atlas(&mut self, size: u32) -> Result<(), Error> {
self.renderer.recreate_atlas(&self.host.display, size)
}
fn renderer_and_tab(&mut self) -> (&mut Renderer, &Rc<Tab>) {
(&mut self.renderer, self.tabs.get_active().unwrap())
}
fn tab_was_created(&mut self, tab: &Rc<Tab>) -> Result<(), Error> {
self.event_loop.register_tab(tab)
@ -242,6 +238,9 @@ impl GliumTerminalWindow {
let height = height as u16;
let renderer = Renderer::new(&host.display, width, height, fonts, palette)?;
let mux = Mux::get().unwrap();
let mux_window_id = mux.add_new_window_with_tab(tab)?;
Ok(GliumTerminalWindow {
host,
event_loop: Rc::clone(event_loop),
@ -255,7 +254,7 @@ impl GliumTerminalWindow {
last_mouse_coords: PhysicalPosition::new(0.0, 0.0),
last_modifiers: Default::default(),
allow_received_character: false,
tabs: Tabs::new(tab),
mux_window_id,
have_pending_resize_check: false,
})
}
@ -286,7 +285,8 @@ impl GliumTerminalWindow {
position: PhysicalPosition,
modifiers: glium::glutin::ModifiersState,
) -> Result<(), Error> {
let tab = match self.tabs.get_active() {
let mux = Mux::get().unwrap();
let tab = match mux.get_active_tab_for_window(self.get_mux_window_id()) {
Some(tab) => tab,
None => return Ok(()),
};
@ -326,7 +326,8 @@ impl GliumTerminalWindow {
button: glutin::MouseButton,
modifiers: glium::glutin::ModifiersState,
) -> Result<(), Error> {
let tab = match self.tabs.get_active() {
let mux = Mux::get().unwrap();
let tab = match mux.get_active_tab_for_window(self.get_mux_window_id()) {
Some(tab) => tab,
None => return Ok(()),
};
@ -391,7 +392,8 @@ impl GliumTerminalWindow {
_ => return Ok(()),
};
let tab = match self.tabs.get_active() {
let mux = Mux::get().unwrap();
let tab = match mux.get_active_tab_for_window(self.get_mux_window_id()) {
Some(tab) => tab,
None => return Ok(()),
};
@ -610,7 +612,8 @@ impl GliumTerminalWindow {
}
fn key_event(&mut self, event: glium::glutin::KeyboardInput) -> Result<(), Error> {
let tab = match self.tabs.get_active() {
let mux = Mux::get().unwrap();
let tab = match mux.get_active_tab_for_window(self.get_mux_window_id()) {
Some(tab) => tab,
None => return Ok(()),
};
@ -626,7 +629,7 @@ impl GliumTerminalWindow {
return Ok(());
}
if self.host.process_gui_shortcuts(&**tab, mods, key)? {
if self.host.process_gui_shortcuts(&*tab, mods, key)? {
return Ok(());
}
@ -678,7 +681,8 @@ impl GliumTerminalWindow {
// eprintln!("ReceivedCharacter {} {:?}", c as u32, c);
if self.allow_received_character {
self.allow_received_character = false;
let tab = match self.tabs.get_active() {
let mux = Mux::get().unwrap();
let tab = match mux.get_active_tab_for_window(self.get_mux_window_id()) {
Some(tab) => tab,
None => return Ok(()),
};

View File

@ -3,7 +3,6 @@ use crate::mux::tab::{alloc_tab_id, Tab, TabId};
use crate::{Child, MasterPty};
use failure::Error;
use std::cell::{RefCell, RefMut};
use std::rc::Rc;
use term::{KeyCode, KeyModifiers, MouseEvent, Terminal, TerminalHost};
pub struct LocalTab {
@ -99,74 +98,3 @@ impl Drop for LocalTab {
self.process.borrow_mut().wait().ok();
}
}
pub struct Tabs {
tabs: Vec<Rc<Tab>>,
active: usize,
}
impl Tabs {
pub fn new(tab: &Rc<Tab>) -> Self {
Self {
tabs: vec![Rc::clone(tab)],
active: 0,
}
}
pub fn push(&mut self, tab: &Rc<Tab>) {
self.tabs.push(Rc::clone(tab))
}
pub fn is_empty(&self) -> bool {
self.tabs.is_empty()
}
pub fn len(&self) -> usize {
self.tabs.len()
}
pub fn get_by_idx(&self, idx: usize) -> Option<&Rc<Tab>> {
self.tabs.get(idx)
}
pub fn idx_by_id(&self, id: TabId) -> Option<usize> {
for (idx, t) in self.tabs.iter().enumerate() {
if t.tab_id() == id {
return Some(idx);
}
}
None
}
pub fn remove_by_id(&mut self, id: TabId) {
if let Some(idx) = self.idx_by_id(id) {
self.tabs.remove(idx);
let len = self.tabs.len();
if len > 0 && self.active == idx && idx >= len {
self.set_active(len - 1);
}
}
}
pub fn get_active(&self) -> Option<&Rc<Tab>> {
self.get_by_idx(self.active)
}
#[inline]
pub fn get_active_idx(&self) -> usize {
self.active
}
pub fn set_active(&mut self, idx: usize) {
assert!(idx < self.tabs.len());
self.active = idx;
self.get_by_idx(idx)
.unwrap()
.renderer()
.make_all_lines_dirty();
}
pub fn iter(&self) -> impl Iterator<Item = &Rc<Tab>> {
self.tabs.iter()
}
}

View File

@ -1,7 +1,9 @@
use crate::config::Config;
use crate::font::FontConfiguration;
use crate::guicommon::tabs::{LocalTab, Tabs};
use crate::guicommon::tabs::LocalTab;
use crate::mux::tab::{Tab, TabId};
use crate::mux::window::WindowId;
use crate::mux::Mux;
use crate::opengl::render::Renderer;
use crate::opengl::textureatlas::OutOfTextureSpace;
use crate::openpty;
@ -25,12 +27,10 @@ pub struct Dimensions {
/// A number of methods need to be provided by the window in order to
/// unlock the use of the provided methods towards the bottom of the trait.
pub trait TerminalWindow {
fn get_tabs_mut(&mut self) -> &mut Tabs;
fn get_tabs(&self) -> &Tabs;
fn set_window_title(&mut self, title: &str) -> Result<(), Error>;
fn get_mux_window_id(&self) -> WindowId;
fn frame(&self) -> glium::Frame;
fn renderer(&mut self) -> &mut Renderer;
fn renderer_and_tab(&mut self) -> (&mut Renderer, &Rc<Tab>);
fn recreate_texture_atlas(&mut self, size: u32) -> Result<(), Error>;
fn advise_renderer_that_scaling_has_changed(
&mut self,
@ -49,31 +49,51 @@ pub trait TerminalWindow {
}
fn activate_tab(&mut self, tab_idx: usize) -> Result<(), Error> {
let max = self.get_tabs().len();
let mux = Mux::get().unwrap();
let mut window = mux
.get_window_mut(self.get_mux_window_id())
.ok_or_else(|| format_err!("no such window"))?;
let max = window.len();
if tab_idx < max {
self.get_tabs_mut().set_active(tab_idx);
window.set_active(tab_idx);
drop(window);
self.update_title();
}
Ok(())
}
fn activate_tab_relative(&mut self, delta: isize) -> Result<(), Error> {
let max = self.get_tabs().len();
let active = self.get_tabs().get_active_idx() as isize;
let mux = Mux::get().unwrap();
let window = mux
.get_window(self.get_mux_window_id())
.ok_or_else(|| format_err!("no such window"))?;
let max = window.len();
let active = window.get_active_idx() as isize;
let tab = active + delta;
let tab = if tab < 0 { max as isize + tab } else { tab };
drop(window);
self.activate_tab(tab as usize % max)
}
fn update_title(&mut self) {
let num_tabs = self.get_tabs().len();
let mux = Mux::get().unwrap();
let window = match mux.get_window(self.get_mux_window_id()) {
Some(window) => window,
_ => return,
};
let num_tabs = window.len();
if num_tabs == 0 {
return;
}
let tab_no = self.get_tabs().get_active_idx();
let tab_no = window.get_active_idx();
let title = self.get_tabs().get_active().unwrap().get_title();
let title = window.get_active().unwrap().get_title();
drop(window);
if num_tabs == 1 {
self.set_window_title(&title).ok();
@ -84,7 +104,8 @@ pub trait TerminalWindow {
}
fn paint_if_needed(&mut self) -> Result<(), Error> {
let tab = match self.get_tabs().get_active() {
let mux = Mux::get().unwrap();
let tab = match mux.get_active_tab_for_window(self.get_mux_window_id()) {
Some(tab) => tab,
None => return Ok(()),
};
@ -98,8 +119,14 @@ pub trait TerminalWindow {
fn paint(&mut self) -> Result<(), Error> {
let mut target = self.frame();
let mux = Mux::get().unwrap();
let tab = match mux.get_active_tab_for_window(self.get_mux_window_id()) {
Some(tab) => tab,
None => return Ok(()),
};
let res = {
let (renderer, tab) = self.renderer_and_tab();
let renderer = self.renderer();
renderer.paint(&mut target, &mut *tab.renderer())
};
@ -117,11 +144,7 @@ pub trait TerminalWindow {
if let Some(&OutOfTextureSpace { size }) = err.downcast_ref::<OutOfTextureSpace>() {
eprintln!("out of texture space, allocating {}", size);
self.recreate_texture_atlas(size)?;
self.get_tabs_mut()
.get_active()
.unwrap()
.renderer()
.make_all_lines_dirty();
tab.renderer().make_all_lines_dirty();
// Recursively initiate a new paint
return self.paint();
}
@ -145,6 +168,8 @@ pub trait TerminalWindow {
let process = slave.spawn_command(cmd)?;
eprintln!("spawned: {:?}", process);
let mux = Mux::get().unwrap();
let terminal = term::Terminal::new(
rows,
cols,
@ -155,8 +180,13 @@ pub trait TerminalWindow {
let tab: Rc<Tab> = Rc::new(LocalTab::new(terminal, process, pty));
let tab_id = tab.tab_id();
self.get_tabs_mut().push(&tab);
let len = self.get_tabs().len();
let len = {
let mut window = mux
.get_window_mut(self.get_mux_window_id())
.ok_or_else(|| format_err!("no such window!?"))?;
window.push(&tab);
window.len()
};
self.activate_tab(len - 1)?;
self.tab_was_created(&tab)?;
@ -179,8 +209,11 @@ pub trait TerminalWindow {
let rows = ((height as usize + 1) / dims.cell_height) as u16;
let cols = ((width as usize + 1) / dims.cell_width) as u16;
let tabs = self.get_tabs();
for tab in tabs.iter() {
let mux = Mux::get().unwrap();
let window = mux
.get_window(self.get_mux_window_id())
.ok_or_else(|| format_err!("no such window!?"))?;
for tab in window.iter() {
tab.resize(rows, cols, width as u16, height as u16)?;
}
@ -205,9 +238,12 @@ pub trait TerminalWindow {
"TerminalWindow::scaling_changed dpi_scale={} font_scale={}",
dpi_scale, font_scale
);
if let Some(tab) = self.get_tabs().get_active() {
tab.renderer().make_all_lines_dirty();
}
let mux = Mux::get().unwrap();
let tab = match mux.get_active_tab_for_window(self.get_mux_window_id()) {
Some(tab) => tab,
None => return Ok(()),
};
tab.renderer().make_all_lines_dirty();
fonts.change_scaling(font_scale, dpi_scale);
let metrics = fonts.default_font_metrics()?;
@ -217,13 +253,7 @@ pub trait TerminalWindow {
// so we query for that information here.
// If the backend supports `resize_if_not_full_screen` then we'll try
// to resize the window to match the new cell metrics.
let (rows, cols) = {
self.get_tabs()
.get_active()
.unwrap()
.renderer()
.physical_dimensions()
};
let (rows, cols) = { tab.renderer().physical_dimensions() };
self.advise_renderer_that_scaling_has_changed(
cell_width.ceil() as usize,
@ -239,16 +269,28 @@ pub trait TerminalWindow {
}
fn tab_did_terminate(&mut self, tab_id: TabId) {
self.get_tabs_mut().remove_by_id(tab_id);
if let Some(active) = self.get_tabs().get_active() {
let mux = Mux::get().unwrap();
let mut window = match mux.get_window_mut(self.get_mux_window_id()) {
Some(window) => window,
None => return,
};
window.remove_by_id(tab_id);
if let Some(active) = window.get_active() {
active.renderer().make_all_lines_dirty();
self.update_title();
}
drop(window);
self.update_title();
self.deregister_tab(tab_id).ok();
}
fn test_for_child_exit(&mut self) -> bool {
let tabs = self.get_tabs();
let dead_tabs: Vec<Rc<Tab>> = tabs
let mux = Mux::get().unwrap();
let window = match mux.get_window(self.get_mux_window_id()) {
Some(window) => window,
None => return true,
};
let dead_tabs: Vec<Rc<Tab>> = window
.iter()
.filter_map(|tab| {
if tab.is_dead() {
@ -258,9 +300,14 @@ pub trait TerminalWindow {
}
})
.collect();
drop(window);
for tab in dead_tabs {
self.tab_did_terminate(tab.tab_id());
}
self.get_tabs().is_empty()
let empty = match mux.get_window(self.get_mux_window_id()) {
Some(window) => window.is_empty(),
None => true,
};
empty
}
}

View File

@ -1,6 +1,6 @@
use failure::Error;
use promise::{Executor, Future};
use std::cell::RefCell;
use std::cell::{Ref, RefCell, RefMut};
use std::collections::HashMap;
use std::io::Read;
use std::rc::Rc;
@ -10,12 +10,15 @@ use termwiz::hyperlink::Hyperlink;
pub mod renderable;
pub mod tab;
pub mod window;
use crate::mux::tab::{Tab, TabId};
use crate::mux::window::{Window, WindowId};
#[derive(Default)]
pub struct Mux {
tabs: RefCell<HashMap<TabId, Rc<Tab>>>,
windows: RefCell<HashMap<WindowId, Window>>,
}
fn read_from_tab_pty(executor: Box<Executor>, tab_id: TabId, mut reader: Box<std::io::Read>) {
@ -127,6 +130,36 @@ impl Mux {
self.tabs.borrow_mut().remove(&tab_id);
}
pub fn get_window(&self, window_id: WindowId) -> Option<Ref<Window>> {
if !self.windows.borrow().contains_key(&window_id) {
return None;
}
Some(Ref::map(self.windows.borrow(), |windows| {
windows.get(&window_id).unwrap()
}))
}
pub fn get_window_mut(&self, window_id: WindowId) -> Option<RefMut<Window>> {
if !self.windows.borrow().contains_key(&window_id) {
return None;
}
Some(RefMut::map(self.windows.borrow_mut(), |windows| {
windows.get_mut(&window_id).unwrap()
}))
}
pub fn get_active_tab_for_window(&self, window_id: WindowId) -> Option<Rc<Tab>> {
let window = self.get_window(window_id)?;
window.get_active().map(Rc::clone)
}
pub fn add_new_window_with_tab(&self, tab: &Rc<Tab>) -> Result<WindowId, Error> {
let window = Window::new(tab);
let window_id = window.window_id();
self.windows.borrow_mut().insert(window_id, window);
Ok(window_id)
}
#[allow(dead_code)]
pub fn is_empty(&self) -> bool {
self.tabs.borrow().is_empty()

82
src/mux/window.rs Normal file
View File

@ -0,0 +1,82 @@
use crate::mux::{Tab, TabId};
use std::rc::Rc;
static WIN_ID: ::std::sync::atomic::AtomicUsize = ::std::sync::atomic::ATOMIC_USIZE_INIT;
pub type WindowId = usize;
pub struct Window {
id: WindowId,
tabs: Vec<Rc<Tab>>,
active: usize,
}
impl Window {
pub fn new(tab: &Rc<Tab>) -> Self {
Self {
id: WIN_ID.fetch_add(1, ::std::sync::atomic::Ordering::Relaxed),
tabs: vec![Rc::clone(tab)],
active: 0,
}
}
pub fn window_id(&self) -> WindowId {
self.id
}
pub fn push(&mut self, tab: &Rc<Tab>) {
self.tabs.push(Rc::clone(tab))
}
pub fn is_empty(&self) -> bool {
self.tabs.is_empty()
}
pub fn len(&self) -> usize {
self.tabs.len()
}
pub fn get_by_idx(&self, idx: usize) -> Option<&Rc<Tab>> {
self.tabs.get(idx)
}
pub fn idx_by_id(&self, id: TabId) -> Option<usize> {
for (idx, t) in self.tabs.iter().enumerate() {
if t.tab_id() == id {
return Some(idx);
}
}
None
}
pub fn remove_by_id(&mut self, id: TabId) {
if let Some(idx) = self.idx_by_id(id) {
self.tabs.remove(idx);
let len = self.tabs.len();
if len > 0 && self.active == idx && idx >= len {
self.set_active(len - 1);
}
}
}
pub fn get_active(&self) -> Option<&Rc<Tab>> {
self.get_by_idx(self.active)
}
#[inline]
pub fn get_active_idx(&self) -> usize {
self.active
}
pub fn set_active(&mut self, idx: usize) {
assert!(idx < self.tabs.len());
self.active = idx;
self.get_by_idx(idx)
.unwrap()
.renderer()
.make_all_lines_dirty();
}
pub fn iter(&self) -> impl Iterator<Item = &Rc<Tab>> {
self.tabs.iter()
}
}

View File

@ -4,11 +4,13 @@ use super::{Connection, Window};
use crate::config::Config;
use crate::font::FontConfiguration;
use crate::guicommon::host::{HostHelper, HostImpl, TabHost};
use crate::guicommon::tabs::{Tab, TabId, Tabs};
use crate::guicommon::window::{Dimensions, TerminalWindow};
use crate::guiloop::x11::{GuiEventLoop, WindowId};
use crate::guiloop::x11::{GuiEventLoop, WindowId as X11WindowId};
use crate::guiloop::SessionTerminated;
use crate::mux::renderable::Renderable;
use crate::mux::tab::{Tab, TabId};
use crate::mux::window::WindowId;
use crate::mux::Mux;
use failure::Error;
use std::rc::Rc;
use std::sync::Arc;
@ -43,16 +45,13 @@ pub struct X11TerminalWindow {
height: u16,
cell_height: usize,
cell_width: usize,
tabs: Tabs,
have_pending_resize: Option<(u16, u16)>,
mux_window_id: WindowId,
}
impl TerminalWindow for X11TerminalWindow {
fn get_tabs(&self) -> &Tabs {
&self.tabs
}
fn get_tabs_mut(&mut self) -> &mut Tabs {
&mut self.tabs
fn get_mux_window_id(&self) -> WindowId {
self.mux_window_id
}
fn config(&self) -> &Arc<Config> {
&self.host.config
@ -75,9 +74,6 @@ impl TerminalWindow for X11TerminalWindow {
fn recreate_texture_atlas(&mut self, size: u32) -> Result<(), Error> {
self.renderer.recreate_atlas(&self.host.window, size)
}
fn renderer_and_tab(&mut self) -> (&mut Renderer, &Tab) {
(&mut self.renderer, self.tabs.get_active().unwrap())
}
fn tab_was_created(&mut self, tab: &Rc<Tab>) -> Result<(), Error> {
self.host.event_loop.register_tab(tab)
}
@ -158,7 +154,8 @@ impl X11TerminalWindow {
});
let renderer = Renderer::new(&host.window, width, height, fonts, palette)?;
let mux = Mux::get().unwrap();
let mux_window_id = mux.add_new_window_with_tab(tab)?;
host.window.show();
Ok(X11TerminalWindow {
@ -169,12 +166,12 @@ impl X11TerminalWindow {
height,
cell_height,
cell_width,
tabs: Tabs::new(tab),
have_pending_resize: None,
mux_window_id,
})
}
pub fn window_id(&self) -> WindowId {
pub fn window_id(&self) -> X11WindowId {
self.host.window.window.window_id
}
@ -187,7 +184,8 @@ impl X11TerminalWindow {
}
fn mouse_event(&mut self, event: MouseEvent) -> Result<(), Error> {
let tab = match self.tabs.get_active() {
let mux = Mux::get().unwrap();
let tab = match mux.get_active_tab_for_window(self.get_mux_window_id()) {
Some(tab) => tab,
None => return Ok(()),
};
@ -212,7 +210,8 @@ impl X11TerminalWindow {
}
xcb::KEY_PRESS => {
let key_press: &xcb::KeyPressEvent = unsafe { xcb::cast_event(event) };
let tab = match self.tabs.get_active() {
let mux = Mux::get().unwrap();
let tab = match mux.get_active_tab_for_window(self.get_mux_window_id()) {
Some(tab) => tab,
None => return Ok(()),
};