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.
13 use std::rt::rtio::{RtioTTY, IoResult};
15 use homing::{HomingIO, HomeHandle};
16 use stream::StreamWatcher;
17 use super::{UvError, UvHandle, uv_error_to_io_error};
18 use uvio::UvIoFactory;
21 pub struct TtyWatcher{
22 tty: *mut uvll::uv_tty_t,
23 stream: StreamWatcher,
29 pub fn new(io: &mut UvIoFactory, fd: libc::c_int, readable: bool)
30 -> Result<TtyWatcher, UvError>
32 // libuv may succeed in giving us a handle (via uv_tty_init), but if the
33 // handle isn't actually connected to a terminal there are frequently
34 // many problems in using it with libuv. To get around this, always
35 // return a failure if the specified file descriptor isn't actually a
39 // - https://github.com/joyent/libuv/issues/982
40 // - https://github.com/joyent/libuv/issues/988
41 let guess = unsafe { uvll::guess_handle(fd) };
42 if guess != uvll::UV_TTY as libc::c_int {
43 return Err(UvError(uvll::EBADF));
46 // libuv was recently changed to not close the stdio file descriptors,
47 // but it did not change the behavior for windows. Until this issue is
48 // fixed, we need to dup the stdio file descriptors because otherwise
49 // uv_close will close them
50 let fd = if cfg!(windows) && fd <= libc::STDERR_FILENO {
51 unsafe { libc::dup(fd) }
54 // If this file descriptor is indeed guessed to be a tty, then go ahead
55 // with attempting to open it as a tty.
56 let handle = UvHandle::alloc(None::<TtyWatcher>, uvll::UV_TTY);
57 let mut watcher = TtyWatcher {
59 stream: StreamWatcher::new(handle),
60 home: io.make_handle(),
64 uvll::uv_tty_init(io.uv_loop(), handle, fd as libc::c_int,
65 readable as libc::c_int)
69 // On windows, libuv returns errors before initializing the
70 // handle, so our only cleanup is to free the handle itself
72 unsafe { uvll::free_handle(handle); }
73 watcher.tty = ptr::mut_null();
81 impl RtioTTY for TtyWatcher {
82 fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> {
83 let _m = self.fire_homing_missile();
84 self.stream.read(buf).map_err(uv_error_to_io_error)
87 fn write(&mut self, buf: &[u8]) -> IoResult<()> {
88 let _m = self.fire_homing_missile();
89 self.stream.write(buf, false).map_err(uv_error_to_io_error)
92 fn set_raw(&mut self, raw: bool) -> IoResult<()> {
93 let raw = raw as libc::c_int;
94 let _m = self.fire_homing_missile();
95 match unsafe { uvll::uv_tty_set_mode(self.tty, raw) } {
97 n => Err(uv_error_to_io_error(UvError(n)))
102 fn get_winsize(&mut self) -> IoResult<(int, int)> {
103 let mut width: libc::c_int = 0;
104 let mut height: libc::c_int = 0;
105 let widthptr: *mut libc::c_int = &mut width;
106 let heightptr: *mut libc::c_int = &mut width;
108 let _m = self.fire_homing_missile();
109 match unsafe { uvll::uv_tty_get_winsize(self.tty,
110 widthptr, heightptr) } {
111 0 => Ok((width as int, height as int)),
112 n => Err(uv_error_to_io_error(UvError(n)))
116 fn isatty(&self) -> bool {
117 unsafe { uvll::guess_handle(self.fd) == uvll::UV_TTY as libc::c_int }
121 impl UvHandle<uvll::uv_tty_t> for TtyWatcher {
122 fn uv_handle(&self) -> *mut uvll::uv_tty_t { self.tty }
125 impl HomingIO for TtyWatcher {
126 fn home<'a>(&'a mut self) -> &'a mut HomeHandle { &mut self.home }
129 impl Drop for TtyWatcher {
131 if !self.tty.is_null() {
132 let _m = self.fire_homing_missile();