1 // Copyright 2013 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
14 use std::rt::rtio::RtioTTY;
16 use homing::{HomingIO, HomeHandle};
17 use stream::StreamWatcher;
18 use super::{UvError, UvHandle, uv_error_to_io_error};
19 use uvio::UvIoFactory;
22 pub struct TtyWatcher{
24 stream: StreamWatcher,
30 pub fn new(io: &mut UvIoFactory, fd: libc::c_int, readable: bool)
31 -> Result<TtyWatcher, UvError>
33 // libuv may succeed in giving us a handle (via uv_tty_init), but if the
34 // handle isn't actually connected to a terminal there are frequently
35 // many problems in using it with libuv. To get around this, always
36 // return a failure if the specified file descriptor isn't actually a
40 // - https://github.com/joyent/libuv/issues/982
41 // - https://github.com/joyent/libuv/issues/988
42 let guess = unsafe { uvll::guess_handle(fd) };
43 if guess != uvll::UV_TTY as libc::c_int {
44 return Err(UvError(uvll::EBADF));
47 // libuv was recently changed to not close the stdio file descriptors,
48 // but it did not change the behavior for windows. Until this issue is
49 // fixed, we need to dup the stdio file descriptors because otherwise
50 // uv_close will close them
51 let fd = if cfg!(windows) && fd <= libc::STDERR_FILENO {
52 unsafe { libc::dup(fd) }
55 // If this file descriptor is indeed guessed to be a tty, then go ahead
56 // with attempting to open it as a tty.
57 let handle = UvHandle::alloc(None::<TtyWatcher>, uvll::UV_TTY);
58 let mut watcher = TtyWatcher {
60 stream: StreamWatcher::new(handle),
61 home: io.make_handle(),
65 uvll::uv_tty_init(io.uv_loop(), handle, fd as libc::c_int,
66 readable as libc::c_int)
70 // On windows, libuv returns errors before initializing the
71 // handle, so our only cleanup is to free the handle itself
73 unsafe { uvll::free_handle(handle); }
74 watcher.tty = ptr::null();
82 impl RtioTTY for TtyWatcher {
83 fn read(&mut self, buf: &mut [u8]) -> Result<uint, IoError> {
84 let _m = self.fire_homing_missile();
85 self.stream.read(buf).map_err(uv_error_to_io_error)
88 fn write(&mut self, buf: &[u8]) -> Result<(), IoError> {
89 let _m = self.fire_homing_missile();
90 self.stream.write(buf).map_err(uv_error_to_io_error)
93 fn set_raw(&mut self, raw: bool) -> Result<(), IoError> {
94 let raw = raw as libc::c_int;
95 let _m = self.fire_homing_missile();
96 match unsafe { uvll::uv_tty_set_mode(self.tty, raw) } {
98 n => Err(uv_error_to_io_error(UvError(n)))
103 fn get_winsize(&mut self) -> Result<(int, int), IoError> {
104 let mut width: libc::c_int = 0;
105 let mut height: libc::c_int = 0;
106 let widthptr: *libc::c_int = &width;
107 let heightptr: *libc::c_int = &width;
109 let _m = self.fire_homing_missile();
110 match unsafe { uvll::uv_tty_get_winsize(self.tty,
111 widthptr, heightptr) } {
112 0 => Ok((width as int, height as int)),
113 n => Err(uv_error_to_io_error(UvError(n)))
117 fn isatty(&self) -> bool {
118 unsafe { uvll::guess_handle(self.fd) == uvll::UV_TTY as libc::c_int }
122 impl UvHandle<uvll::uv_tty_t> for TtyWatcher {
123 fn uv_handle(&self) -> *uvll::uv_tty_t { self.tty }
126 impl HomingIO for TtyWatcher {
127 fn home<'a>(&'a mut self) -> &'a mut HomeHandle { &mut self.home }
130 impl Drop for TtyWatcher {
132 if !self.tty.is_null() {
133 let _m = self.fire_homing_missile();