use super::cli::Cli;
use super::config_manager::AppConfig;
use super::platform::is_console_environment;
#[cfg(all(target_os = "linux", feature = "framebuffer-backend"))]
use crate::framebuffer::fb_config::FramebufferConfig;
#[cfg(all(target_os = "linux", feature = "framebuffer-backend"))]
use crate::framebuffer::text_modes::{TextMode, TextModeKind};
#[cfg(all(target_os = "linux", feature = "framebuffer-backend"))]
use crate::rendering::FramebufferBackend;
use crate::rendering::{Charset, RenderBackend, TerminalBackend, Theme, VideoBuffer};
use crate::term_emu::ShellConfig;
use crate::window::manager::WindowManager;
use crossterm::{cursor, event, execute, queue, style, terminal};
use std::io::{self, Write};
pub fn initialize_backend(
#[cfg_attr(
not(all(target_os = "linux", feature = "framebuffer-backend")),
allow(unused_variables)
)]
cli_args: &Cli,
) -> io::Result<Box<dyn RenderBackend>> {
#[cfg(all(target_os = "linux", feature = "framebuffer-backend"))]
if cli_args.framebuffer {
let fb_config = FramebufferConfig::load();
let mode_str = if cli_args.fb_mode != "80x25" {
cli_args.fb_mode.clone()
} else if FramebufferConfig::exists() {
fb_config.display.mode.clone()
} else {
cli_args.fb_mode.clone()
};
let mode_kind = TextModeKind::from_str(&mode_str).unwrap_or_else(|| {
eprintln!(
"Warning: Invalid framebuffer mode '{}', using default 80x25",
mode_str
);
TextModeKind::Mode80x25
});
let mode = TextMode::new(mode_kind);
let scale_str = cli_args
.fb_scale
.clone()
.unwrap_or_else(|| fb_config.display.scale.clone());
let scale = if scale_str == "auto" {
None } else {
scale_str
.parse::<usize>()
.ok()
.filter(|&n| (1..=8).contains(&n))
};
let font_name = cli_args.fb_font.clone().or_else(|| {
if FramebufferConfig::exists() {
Some(fb_config.font.name.clone())
} else {
None
}
});
let mouse_device = cli_args
.mouse_device
.clone()
.or_else(|| fb_config.mouse.device.clone());
let invert_x = cli_args.invert_mouse_x || fb_config.mouse.invert_x;
let invert_y = cli_args.invert_mouse_y || fb_config.mouse.invert_y;
let sensitivity = cli_args.mouse_sensitivity.or(fb_config.mouse.sensitivity);
match FramebufferBackend::new(
mode,
scale,
font_name.as_deref(),
mouse_device.as_deref(),
invert_x,
invert_y,
sensitivity,
) {
Ok(fb_backend) => {
println!("Framebuffer backend initialized: {}", mode_kind);
return Ok(Box::new(fb_backend));
}
Err(e) => {
eprintln!("Failed to initialize framebuffer: {}", e);
eprintln!("Falling back to terminal backend...");
std::thread::sleep(std::time::Duration::from_secs(2));
}
}
}
Ok(Box::new(TerminalBackend::new()?))
}
pub fn setup_terminal(stdout: &mut io::Stdout) -> io::Result<()> {
terminal::enable_raw_mode()?;
#[cfg(unix)]
{
let _ = stdout.write_all(b"\x1b[?1036l"); let _ = stdout.flush();
use std::os::unix::io::AsRawFd;
unsafe {
let mut termios: libc::termios = std::mem::zeroed();
if libc::tcgetattr(stdout.as_raw_fd(), &mut termios) == 0 {
termios.c_iflag &= !libc::IXON;
let _ = libc::tcsetattr(stdout.as_raw_fd(), libc::TCSANOW, &termios);
}
}
}
let is_console = is_console_environment();
if is_console {
execute!(stdout, terminal::EnterAlternateScreen, cursor::Hide)?;
} else {
execute!(
stdout,
terminal::EnterAlternateScreen,
cursor::Hide,
event::EnableMouseCapture
)?;
}
if terminal::supports_keyboard_enhancement().unwrap_or(false) {
queue!(
stdout,
event::PushKeyboardEnhancementFlags(
event::KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES
)
)?;
stdout.flush()?;
}
execute!(stdout, terminal::Clear(terminal::ClearType::All))?;
Ok(())
}
pub fn initialize_charset(cli_args: &Cli, app_config: &AppConfig) -> Charset {
let mut charset = if cli_args.ascii {
Charset::ascii()
} else if cli_args.single_line {
Charset::unicode_single_line()
} else {
Charset::unicode()
};
charset.set_background(app_config.get_background_char());
charset
}
pub fn initialize_keybinding_profile(
cli_args: &Cli,
app_config: &AppConfig,
) -> crate::input::keybinding_profile::KeybindingProfile {
let profile_name = cli_args
.keybindings
.as_ref()
.unwrap_or(&app_config.keybinding_profile);
crate::input::keybinding_profile::KeybindingProfile::from_name(profile_name)
}
pub fn initialize_theme(cli_args: &Cli, app_config: &AppConfig) -> Theme {
let theme_name = cli_args.theme.as_ref().unwrap_or(&app_config.theme);
Theme::from_name(theme_name)
}
pub fn validate_shell_config(cli_args: &Cli) -> ShellConfig {
if let Some(ref shell_path) = cli_args.shell {
let config = ShellConfig::custom_shell(shell_path.clone());
if let Err(msg) = config.validate() {
eprintln!("Warning: {}, using system default shell", msg);
std::thread::sleep(std::time::Duration::from_secs(2));
ShellConfig::default()
} else {
config
}
} else {
ShellConfig::default()
}
}
pub fn initialize_window_manager(
cli_args: &Cli,
app_config: &mut AppConfig,
shell_config: ShellConfig,
) -> io::Result<WindowManager> {
let window_manager = if !cli_args.no_restore {
let manager = WindowManager::restore_session_from_file(shell_config.clone())
.unwrap_or_else(|_| WindowManager::with_shell_config(shell_config));
if !app_config.auto_save {
let _ = WindowManager::clear_session_file();
}
manager
} else {
WindowManager::with_shell_config(shell_config)
};
Ok(window_manager)
}
pub fn initialize_video_buffer(backend: &dyn RenderBackend) -> VideoBuffer {
let (cols, rows) = backend.dimensions();
VideoBuffer::new(cols, rows)
}
#[cfg(any(
target_os = "linux",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd"
))]
pub fn initialize_mouse_input(
#[cfg_attr(not(target_os = "linux"), allow(unused_variables))] cli_args: &Cli,
cols: u16,
rows: u16,
is_framebuffer_mode: bool,
) -> (
crate::input::mouse::MouseInputManager,
Option<crate::input::gpm_control::GpmConnection>,
) {
use crate::input::mouse::{MouseInputManager, MouseInputMode};
let mode = MouseInputMode::detect(is_framebuffer_mode);
let gpm_connection = if mode.uses_raw_input() {
crate::input::gpm_control::try_disable_gpm()
} else {
None
};
#[cfg(all(target_os = "linux", feature = "framebuffer-backend"))]
let device_path = cli_args.mouse_device.as_deref();
#[cfg(not(all(target_os = "linux", feature = "framebuffer-backend")))]
let device_path: Option<&str> = None;
#[cfg(all(target_os = "linux", feature = "framebuffer-backend"))]
let invert_x = cli_args.invert_mouse_x;
#[cfg(not(all(target_os = "linux", feature = "framebuffer-backend")))]
let invert_x = false;
#[cfg(all(target_os = "linux", feature = "framebuffer-backend"))]
let invert_y = cli_args.invert_mouse_y;
#[cfg(not(all(target_os = "linux", feature = "framebuffer-backend")))]
let invert_y = false;
#[cfg(target_os = "linux")]
let swap_buttons = cli_args.swap_mouse_buttons;
#[cfg(not(target_os = "linux"))]
let swap_buttons = false;
#[cfg(target_os = "linux")]
let sensitivity = cli_args.mouse_sensitivity;
#[cfg(not(target_os = "linux"))]
let sensitivity: Option<f32> = None;
let manager = MouseInputManager::new(
mode,
cols,
rows,
device_path,
invert_x,
invert_y,
swap_buttons,
sensitivity,
)
.unwrap_or_else(|e| {
eprintln!("Warning: Failed to initialize mouse input: {}", e);
MouseInputManager::new(
MouseInputMode::TerminalEmulator,
cols,
rows,
None,
false,
false,
false,
None,
)
.expect("Terminal emulator mode should always succeed")
});
(manager, gpm_connection)
}
#[cfg(not(any(
target_os = "linux",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd"
)))]
pub fn initialize_mouse_input(
_cli_args: &Cli,
cols: u16,
rows: u16,
_is_framebuffer_mode: bool,
) -> (crate::input::mouse::MouseInputManager, Option<()>) {
use crate::input::mouse::{MouseInputManager, MouseInputMode};
let manager = MouseInputManager::new(
MouseInputMode::TerminalEmulator,
cols,
rows,
None,
false,
false,
false,
None,
)
.expect("Terminal emulator mode should always succeed");
(manager, None)
}
pub fn try_cleanup(stdout: &mut io::Stdout) -> io::Result<()> {
if terminal::supports_keyboard_enhancement().unwrap_or(false) {
let _ = execute!(stdout, event::PopKeyboardEnhancementFlags);
}
terminal::disable_raw_mode()?;
execute!(stdout, terminal::LeaveAlternateScreen, cursor::Show)?;
let is_console = is_console_environment();
if !is_console {
execute!(stdout, event::DisableMouseCapture)?;
}
execute!(stdout, style::ResetColor)?;
Ok(())
}
pub fn cleanup(stdout: &mut io::Stdout) {
if let Err(err) = try_cleanup(stdout) {
eprintln!("Failed to restore terminal: {err}");
}
}