aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/main.rs16
-rw-r--r--src/tty.rs246
2 files changed, 261 insertions, 1 deletions
diff --git a/src/main.rs b/src/main.rs
index 3b1d7335..e95908d3 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,3 +1,6 @@
+//! Alacritty - The GPU Enhanced Terminal
+#![feature(question_mark)]
+
extern crate fontconfig;
extern crate freetype;
extern crate libc;
@@ -7,11 +10,14 @@ extern crate euclid;
use std::collections::HashMap;
+use std::io::{BufReader, Read, BufRead};
+
mod list_fonts;
mod text;
mod renderer;
mod grid;
mod meter;
+mod tty;
use renderer::{Glyph, QuadRenderer};
use text::FontDesc;
@@ -76,6 +82,14 @@ fn main() {
let num_cols = grid::num_cells_axis(cell_width, sep_x, width);
let num_rows = grid::num_cells_axis(cell_height, sep_y, height);
+ let mut cmd = tty::new(num_rows as u8, num_cols as u8);
+
+ ::std::thread::spawn(move || {
+ for byte in cmd.bytes() {
+ println!("{:?}", byte);
+ }
+ });
+
println!("num_cols, num_rows = {}, {}", num_cols, num_rows);
let mut grid = Grid::new(num_rows as usize, num_cols as usize);
@@ -130,7 +144,7 @@ fn main() {
for event in window.poll_events() {
match event {
glutin::Event::Closed => break 'main_loop,
- _ => println!("event: {:?}", event)
+ _ => ()
}
}
diff --git a/src/tty.rs b/src/tty.rs
new file mode 100644
index 00000000..7fd27b69
--- /dev/null
+++ b/src/tty.rs
@@ -0,0 +1,246 @@
+//! tty related functionality
+//!
+use std::env;
+use std::ffi::CStr;
+use std::fs::File;
+use std::mem;
+use std::os::unix::io::FromRawFd;
+use std::ptr;
+
+use libc::{self, winsize, c_int, c_char};
+
+macro_rules! die {
+ ($($arg:tt)*) => {
+ println!($($arg)*);
+ ::std::process::exit(1);
+ }
+}
+
+pub enum Error {
+ /// TODO
+ Unknown,
+}
+
+impl Error {
+ /// Build an Error from the current value of errno.
+ fn from_errno() -> Error {
+ let err = errno();
+ match err {
+ _ => Error::Unknown
+ }
+ }
+}
+
+pub type Result<T> = ::std::result::Result<T, Error>;
+
+/// Get the current value of errno
+fn errno() -> c_int {
+ unsafe {
+ ptr::read(libc::__errno_location() as *const _)
+ }
+}
+
+enum Relation {
+ Child,
+ Parent
+}
+
+fn fork() -> Relation {
+ let res = unsafe {
+ libc::fork()
+ };
+
+ if res < 0 {
+ die!("fork failed");
+ }
+
+ if res == 0 {
+ Relation::Child
+ } else {
+ Relation::Parent
+ }
+}
+
+/// Get raw fds for master/slave ends of a new pty
+fn openpty(rows: u8, cols: u8) -> (c_int, c_int) {
+ let mut master: c_int = 0;
+ let mut slave: c_int = 0;
+
+ let win = winsize {
+ ws_row: rows as libc::c_ushort,
+ ws_col: cols as libc::c_ushort,
+ ws_xpixel: 0,
+ ws_ypixel: 0,
+ };
+
+ let res = unsafe {
+ libc::openpty(&mut master, &mut slave, ptr::null_mut(), ptr::null(), &win)
+ };
+
+ if res < 0 {
+ die!("openpty failed");
+ }
+
+ (master, slave)
+}
+
+/// Really only needed on BSD, but should be fine elsewhere
+fn set_controlling_terminal(fd: c_int) {
+ let res = unsafe {
+ libc::ioctl(fd, libc::TIOCSCTTY, 0)
+ };
+
+ if res < 0 {
+ die!("ioctl TIOCSCTTY failed: {}", errno());
+ }
+}
+
+#[derive(Debug)]
+struct Passwd<'a> {
+ name: &'a str,
+ passwd: &'a str,
+ uid: libc::uid_t,
+ gid: libc::gid_t,
+ gecos: &'a str,
+ dir: &'a str,
+ shell: &'a str,
+}
+
+/// Return a Passwd struct with pointers into the provided buf
+///
+/// # Unsafety
+///
+/// If `buf` is changed while `Passwd` is alive, bad thing will almost certainly happen.
+fn get_pw_entry<'a>(buf: &'a mut [i8; 1024]) -> Passwd<'a> {
+ // Create zeroed passwd struct
+ let mut entry = libc::passwd {
+ pw_name: ptr::null_mut(),
+ pw_passwd: ptr::null_mut(),
+ pw_uid: 0,
+ pw_gid: 0,
+ pw_gecos: ptr::null_mut(),
+ pw_dir: ptr::null_mut(),
+ pw_shell: ptr::null_mut(),
+ };
+
+ let mut res: *mut libc::passwd = ptr::null_mut();
+
+ // Try and read the pw file.
+ let uid = unsafe { libc::getuid() };
+ let status = unsafe {
+ libc::getpwuid_r(uid, &mut entry, buf.as_mut_ptr(), buf.len(), &mut res)
+ };
+
+ if status < 0 {
+ die!("getpwuid_r failed");
+ }
+
+ if res.is_null() {
+ die!("pw not found");
+ }
+
+
+ // sanity check
+ assert_eq!(entry.pw_uid, uid);
+
+ // Build a borrowed Passwd struct
+ //
+ // Transmute is used here to conveniently cast from the raw CStr to a &str with the appropriate
+ // lifetime.
+ Passwd {
+ name: unsafe { mem::transmute(CStr::from_ptr(entry.pw_name).to_str().unwrap()) },
+ passwd: unsafe { mem::transmute(CStr::from_ptr(entry.pw_passwd).to_str().unwrap()) },
+ uid: entry.pw_uid,
+ gid: entry.pw_gid,
+ gecos: unsafe { mem::transmute(CStr::from_ptr(entry.pw_gecos).to_str().unwrap()) },
+ dir: unsafe { mem::transmute(CStr::from_ptr(entry.pw_dir).to_str().unwrap()) },
+ shell: unsafe { mem::transmute(CStr::from_ptr(entry.pw_shell).to_str().unwrap()) },
+ }
+}
+
+/// Exec a shell
+fn execsh() -> ! {
+ let mut buf = [0; 1024];
+ let pw = get_pw_entry(&mut buf);
+
+ // setup environment
+ env::set_var("LOGNAME", pw.name);
+ env::set_var("USER", pw.name);
+ env::set_var("SHELL", pw.shell);
+ env::set_var("HOME", pw.dir);
+ env::set_var("TERM", "xterm-256color"); // sigh
+
+ unsafe {
+ libc::signal(libc::SIGCHLD, libc::SIG_DFL);
+ libc::signal(libc::SIGHUP, libc::SIG_DFL);
+ libc::signal(libc::SIGINT, libc::SIG_DFL);
+ libc::signal(libc::SIGQUIT, libc::SIG_DFL);
+ libc::signal(libc::SIGTERM, libc::SIG_DFL);
+ libc::signal(libc::SIGALRM, libc::SIG_DFL);
+ }
+
+ // pw.shell is null terminated
+ let shell = unsafe { CStr::from_ptr(pw.shell.as_ptr() as *const _) };
+
+ let argv = [shell.as_ptr(), ptr::null()];
+
+ let res = unsafe {
+ libc::execvp(shell.as_ptr(), argv.as_ptr())
+ };
+
+ if res < 0 {
+ die!("execvp failed: {}", errno());
+ }
+
+ ::std::process::exit(1);
+}
+
+/// Create a new tty and return a handle to interact with it.
+pub fn new(rows: u8, cols: u8) -> File {
+ let (master, slave) = openpty(rows, cols);
+
+ match fork() {
+ Relation::Child => {
+ unsafe {
+ // Create a new process group
+ libc::setsid();
+
+ // Duplicate pty slave to be child stdin, stdoud, and stderr
+ libc::dup2(slave, 0);
+ libc::dup2(slave, 1);
+ libc::dup2(slave, 2);
+ }
+
+ set_controlling_terminal(slave);
+
+ // No longer need slave/master fds
+ unsafe {
+ libc::close(slave);
+ libc::close(master);
+ }
+
+ // Exec a shell!
+ execsh();
+ },
+ Relation::Parent => {
+ // Parent doesn't need slave fd
+ unsafe {
+ libc::close(slave);
+ }
+
+ // XXX should this really return a file?
+ // How should this be done? Could build a File::from_raw_fd, or maybe implement a custom
+ // type that can be used in a mio event loop? For now, just do the file option.
+ unsafe {
+ File::from_raw_fd(master)
+ }
+ }
+ }
+}
+
+#[test]
+fn test_get_pw_entry() {
+ let mut buf: [i8; 1024] = [0; 1024];
+ let pw = get_pw_entry(&mut buf);
+ println!("{:?}", pw);
+}