1 use {chtype, curses, platform_specific, ptr, Input, ToChtype, ERR}; 2 use std::ffi::CString; 3 4 #[derive(Debug)] 5 pub struct Window { 6 #[cfg(windows)] 7 _window: *mut curses::WINDOW, 8 #[cfg(unix)] 9 _window: curses::WINDOW, 10 _stdscr: bool, 11 _deleted: bool, 12 } 13 14 #[cfg(windows)] 15 type WindowPointer = *mut curses::WINDOW; 16 #[cfg(unix)] 17 type WindowPointer = curses::WINDOW; 18 19 impl Window { 20 /// Adds the chtype ch to the window at the current cursor position, and advances the cursor. 21 /// 22 /// Note that chtypes can convey both text (a single character) and attributes, including a 23 /// color pair. 24 pub fn addch<T: ToChtype>(&self, ch: T) -> i32 { 25 unsafe { curses::waddch(self._window, ch.to_chtype()) } 26 } 27 28 /// Write all the characters of the string to the given window. 29 /// 30 /// The functionality is similar to calling window.addch() once for each character in the 31 /// string. 32 pub fn addstr<T: AsRef<str>>(&self, string: T) -> i32 { 33 let s = CString::new(string.as_ref()).unwrap(); 34 unsafe { curses::waddstr(self._window, s.as_ptr()) } 35 } 36 37 /// Write at most length characters; if length is negative, then the entire string will be 38 /// added. 39 pub fn addnstr<T: AsRef<str>>(&self, string: T, length: usize) -> i32 { 40 let s = CString::new(string.as_ref()).unwrap(); 41 unsafe { curses::waddnstr(self._window, s.as_ptr(), length as i32) } 42 } 43 44 /// Retrieve attributes for the given window. 45 /// 46 /// ```rust 47 /// use pancurses::{A_BOLD, initscr, endwin}; 48 /// let window = initscr(); 49 /// window.attron(A_BOLD); 50 /// let (active_attributes, color_pair) = window.attrget(); 51 /// assert_eq!(A_BOLD, active_attributes); 52 /// endwin(); 53 /// ``` 54 pub fn attrget(&self) -> (chtype, i16) { 55 let mut attributes: chtype = 0; 56 let mut color_pair: i16 = 0; 57 unsafe { 58 curses::wattr_get( 59 self._window, 60 &mut attributes, 61 &mut color_pair, 62 ptr::null_mut(), 63 ); 64 } 65 (attributes, color_pair) 66 } 67 68 /// Turns off the named attributes without affecting any other attributes. 69 pub fn attroff<T: Into<chtype>>(&self, attributes: T) -> i32 { 70 platform_specific::_attroff(self._window, attributes.into()) 71 } 72 73 /// Turns on the named attributes without affecting any other attributes. 74 pub fn attron<T: Into<chtype>>(&self, attributes: T) -> i32 { 75 platform_specific::_attron(self._window, attributes.into()) 76 } 77 78 /// Sets the current attributes of the given window to attributes. 79 pub fn attrset<T: Into<chtype>>(&self, attributes: T) -> i32 { 80 platform_specific::_attrset(self._window, attributes.into()) 81 } 82 83 /// Not only change the background, but apply it immediately to every cell in the window. 84 pub fn bkgd<T: Into<chtype>>(&self, ch: T) -> i32 { 85 unsafe { curses::wbkgd(self._window, ch.into()) } 86 } 87 88 /// Manipulate the background of a window. The background is a chtype consisting of any 89 /// combination of attributes and a character; it is combined with each chtype added or 90 /// inserted to the window by addch() or insch(). Only the attribute part is used to set 91 /// the background of non-blank characters, while both character and attributes are used 92 /// for blank positions. 93 pub fn bkgdset<T: Into<chtype>>(&self, ch: T) { 94 unsafe { curses::wbkgdset(self._window, ch.into()) } 95 } 96 97 /// Draw a border around the edges of the window. 98 pub fn border<T: ToChtype>( 99 &self, 100 left_side: T, 101 right_side: T, 102 top_side: T, 103 bottom_side: T, 104 top_left_corner: T, 105 top_right_corner: T, 106 bottom_left_corner: T, 107 bottom_right_corner: T, 108 ) -> i32 { 109 unsafe { 110 curses::wborder( 111 self._window, 112 left_side.to_chtype(), 113 right_side.to_chtype(), 114 top_side.to_chtype(), 115 bottom_side.to_chtype(), 116 top_left_corner.to_chtype(), 117 top_right_corner.to_chtype(), 118 bottom_left_corner.to_chtype(), 119 bottom_right_corner.to_chtype(), 120 ) 121 } 122 } 123 124 /// Changes the attributes of a given number of characters starting at the current cursor 125 /// location. It does not update the cursor and does not perform wrapping. A character count 126 /// of -1 or greater than the remaining window width means to change attributes all the way 127 /// to the end of the current line. 128 pub fn chgat(&self, n: i32, attributes: chtype, color_pair: i16) -> i32 { 129 unsafe { curses::wchgat(self._window, n, attributes, color_pair, ptr::null_mut()) } 130 } 131 132 /// Similar to erase(), but also calls clearok() to ensure that the the window is cleared on 133 /// the next refresh(). 134 pub fn clear(&self) -> i32 { 135 unsafe { curses::wclear(self._window) } 136 } 137 138 /// With clearok(), if bf is TRUE, the next call to refresh() with 139 /// this window will clear the screen completely and redraw the 140 /// entire screen. 141 pub fn clearok(&self, bf: bool) -> i32 { 142 unsafe { curses::clearok(self._window, bf as u8) } 143 } 144 145 /// Clear the window from the current cursor position to the end of the window. 146 pub fn clrtobot(&self) -> i32 { 147 unsafe { curses::wclrtobot(self._window) } 148 } 149 150 /// Clear the window from the current cursor position to the end of the current line. 151 pub fn clrtoeol(&self) -> i32 { 152 unsafe { curses::wclrtoeol(self._window) } 153 } 154 155 /// Sets the current color of the given window to the foreground/background combination 156 /// described by the color pair parameter. 157 pub fn color_set(&self, color_pair: i16) -> i32 { 158 unsafe { curses::wcolor_set(self._window, color_pair, ptr::null_mut()) } 159 } 160 161 /// Copy all text from this window to the destination window. The arguments src_tc and 162 /// src_tr specify the top left corner of the region to be copied. dst_tc, dst_tr, dst_br, 163 /// and dst_bc specify the region within the destination window to copy to. The argument 164 /// "overlay", if TRUE, indicates that the copy is done non-destructively (as in overlay()); 165 /// blanks in the source window are not copied to the destination window. When overlay is 166 /// FALSE, blanks are copied. 167 pub fn copywin( 168 &self, 169 destination_window: &Window, 170 src_tr: i32, 171 src_tc: i32, 172 dst_tr: i32, 173 dst_tc: i32, 174 dst_br: i32, 175 dst_bc: i32, 176 overlay: bool, 177 ) -> i32 { 178 unsafe { 179 curses::copywin( 180 self._window, 181 destination_window._window, 182 src_tr, 183 src_tc, 184 dst_tr, 185 dst_tc, 186 dst_br, 187 dst_bc, 188 overlay as i32, 189 ) 190 } 191 } 192 193 /// Delete the character under the cursor. All characters to the right of the cursor 194 /// on the same line are moved to the left one position and hte last character on the 195 /// line is filled with a blank. The cursor position does not change. 196 pub fn delch(&self) -> i32 { 197 unsafe { curses::wdelch(self._window) } 198 } 199 200 /// Delete the line under the cursor. All lines below are moved up one line, and the 201 /// bottom line is cleared. The cursor position does not change. 202 pub fn deleteln(&self) -> i32 { 203 unsafe { curses::wdeleteln(self._window) } 204 } 205 206 /// Deletes the window, freeing all associated memory. In the case of overlapping windows, 207 /// subwindows should be deleted before the main window. 208 pub fn delwin(mut self) -> i32 { 209 self._deleted = true; 210 unsafe { curses::delwin(self._window) } 211 } 212 213 /// The same as subwin(), except that begy and begx are relative to the origin of the window 214 /// rather than the screen. 215 /// 216 /// There is no difference between subwindows and derived windows. 217 pub fn derwin(&self, nlines: i32, ncols: i32, begy: i32, begx: i32) -> Result<Window, i32> { 218 self.subwin( 219 nlines, 220 ncols, 221 begy + self.get_beg_y(), 222 begx + self.get_beg_x(), 223 ) 224 } 225 226 /// Draw a border around the edge of the window. If any argument is zero, an appropriate 227 /// default is used. 228 pub fn draw_box<T: ToChtype>(&self, verch: T, horch: T) -> i32 { 229 platform_specific::_draw_box(self._window, verch.to_chtype(), horch.to_chtype()) 230 } 231 232 /// Creates an exact duplicate of the window. 233 pub fn dupwin(&self) -> Window { 234 let dup_win = unsafe { curses::dupwin(self._window) }; 235 Window { 236 _window: dup_win, 237 _stdscr: false, 238 _deleted: false, 239 } 240 } 241 242 /// Reports whether the given screen-relative y, x coordinates fall within the window. 243 pub fn enclose(&self, y: i32, x: i32) -> bool { 244 unsafe { curses::wenclose(self._window, y, x) > 0 } 245 } 246 247 /// Copies blanks (i.e. the background chtype) to every cell of the window. 248 pub fn erase(&self) -> i32 { 249 unsafe { curses::werase(self._window) } 250 } 251 252 /// Get the upper-left y coordinate of this window 253 pub fn get_beg_y(&self) -> i32 { 254 unsafe { curses::getbegy(self._window) } 255 } 256 257 // Get the upper-left x coordinate of this window 258 pub fn get_beg_x(&self) -> i32 { 259 unsafe { curses::getbegx(self._window) } 260 } 261 262 /// Get the upper-left y and x coordinates of this window 263 pub fn get_beg_yx(&self) -> (i32, i32) { 264 (self.get_beg_y(), self.get_beg_x()) 265 } 266 267 /// Returns the given window's current background character and attributes. 268 pub fn getbkgd(&self) -> chtype { 269 unsafe { curses::getbkgd(self._window) } 270 } 271 272 /// Read a character from the terminal associated with the window. 273 /// 274 /// In nodelay mode, if there is no input waiting, None is returned. In delay mode, 275 /// the program will hang until the system passes text through to the program. Depending on 276 /// the setting of cbreak(), this will be after one character or after the first newline. 277 /// Unless noecho() has been set, the character will also be echoed into the designated window. 278 /// 279 /// If keypad() is TRUE, and a function key is pressed, the token for that function key will be 280 /// returned instead of the raw characters. 281 /// If nodelay(win, TRUE) has been called on the window and no input is waiting, None is 282 /// returned. 283 pub fn getch(&self) -> Option<Input> { 284 platform_specific::_wgetch(self._window) 285 } 286 287 /// Return the current x coordinate of the cursor 288 pub fn get_cur_x(&self) -> i32 { 289 unsafe { curses::getcurx(self._window) } 290 } 291 292 /// Return the current y coordinate of the cursor 293 pub fn get_cur_y(&self) -> i32 { 294 unsafe { curses::getcury(self._window) } 295 } 296 297 /// Return the current y and x coordinates of the cursor 298 pub fn get_cur_yx(&self) -> (i32, i32) { 299 (self.get_cur_y(), self.get_cur_x()) 300 } 301 302 /// Return the maximum x value of this Window, in other words the number of columns. 303 pub fn get_max_x(&self) -> i32 { 304 unsafe { curses::getmaxx(self._window) } 305 } 306 307 /// Return the maximum y value of this Window, in other words the number of rows. 308 pub fn get_max_y(&self) -> i32 { 309 unsafe { curses::getmaxy(self._window) } 310 } 311 312 /// Return the maximum y and x value of this Window 313 pub fn get_max_yx(&self) -> (i32, i32) { 314 (self.get_max_y(), self.get_max_x()) 315 } 316 317 /// Draw a horizontal line using ch from the current cursor position. The line is at most 318 /// n characters long, or as many as fit into the window. 319 pub fn hline<T: ToChtype>(&self, ch: T, n: i32) -> i32 { 320 unsafe { curses::whline(self._window, ch.to_chtype(), n) } 321 } 322 323 /// For positive n, insert n lines into the specified window above the current line. 324 /// The n bottom lines are lost. For negative n, delete n lines (starting with the one under 325 /// the cursor), and move the remaining lines up. The bottom n lines are cleared. 326 /// The current cursor position remains the same. 327 pub fn insdelln(&self, n: i32) -> i32 { 328 unsafe { curses::winsdelln(self._window, n) } 329 } 330 331 /// A blank line is inserted above the current line and the bottom line is lost. 332 pub fn insertln(&self) -> i32 { 333 unsafe { curses::winsertln(self._window) } 334 } 335 336 /// Returns true if the specified line in the specified window has been changed since the last 337 /// call to refresh(). 338 pub fn is_linetouched(&self, line: i32) -> bool { 339 unsafe { curses::is_linetouched(self._window, line) > 0 } 340 } 341 342 /// Returns true if the specified window has been changed since the last call to refresh(). 343 pub fn is_touched(&self) -> bool { 344 unsafe { curses::is_wintouched(self._window) > 0 } 345 } 346 347 /// Controls whether getch() returns function/special keys as single key codes (e.g., the left 348 /// arrow key as KEY_LEFT). 349 /// 350 /// Per X/Open, the default for keypad mode is OFF. You'll probably want it on. With keypad 351 /// mode off, if a special key is pressed, getch() does nothing or returns ERR. 352 pub fn keypad(&self, use_keypad: bool) -> i32 { 353 unsafe { curses::keypad(self._window, use_keypad as u8) } 354 } 355 356 /// Insert the character ch before the character under the cursor. 357 /// 358 /// All characters to the right of the cursor are moved one space to the right, with the 359 /// possibility of the rightmost character on the line being lost. The insertion operation does 360 /// not change the cursor position. 361 pub fn insch<T: ToChtype>(&self, ch: T) -> i32 { 362 unsafe { curses::winsch(self._window, ch.to_chtype()) } 363 } 364 365 /// Converts between screen-relative and window-relative coordinates. 366 /// 367 /// A to_screen parameter of true means to convert from window to screen; 368 /// otherwise the reverse. 369 pub fn mouse_trafo(&self, y: i32, x: i32, to_screen: bool) -> (i32, i32) { 370 let mut mut_y = y; 371 let mut mut_x = x; 372 unsafe { curses::wmouse_trafo(self._window, &mut mut_y, &mut mut_x, to_screen as u8); } 373 (mut_y, mut_x) 374 } 375 376 /// The cursor associated with the window is moved to the given location. 377 /// 378 /// This does not move the physical cursor of the terminal until refresh() is called. The 379 /// position specified is relative to the upper left corner of the window, which is (0,0). 380 pub fn mv(&self, y: i32, x: i32) -> i32 { 381 unsafe { curses::wmove(self._window, y, x) } 382 } 383 384 /// moves the cursor to the specified position and adds ch to the specified window 385 pub fn mvaddch<T: ToChtype>(&self, y: i32, x: i32, ch: T) -> i32 { 386 unsafe { curses::mvwaddch(self._window, y, x, ch.to_chtype()) } 387 } 388 389 /// Write all the characters of the string str to the given window. The functionality is 390 /// similar to calling waddch() once for each character in the string. 391 pub fn mvaddstr<T: AsRef<str>>(&self, y: i32, x: i32, string: T) -> i32 { 392 let s = CString::new(string.as_ref()).unwrap(); 393 unsafe { curses::mvwaddstr(self._window, y, x, s.as_ptr()) } 394 } 395 396 /// Write the first'n' characters of the string str to the given window. 397 pub fn mvaddnstr<T: AsRef<str>>(&self, y: i32, x: i32, string: T, n: i32) -> i32 { 398 let s = CString::new(string.as_ref()).unwrap(); 399 unsafe { curses::mvwaddnstr(self._window, y, x, s.as_ptr(), n) } 400 } 401 402 /// Moves the cursor and changes the attributes of a given number of characters starting at the 403 /// cursor location. It does not update the cursor and does not perform wrapping. A character count 404 /// of -1 or greater than the remaining window width means to change attributes all the way 405 /// to the end of the current line. 406 pub fn mvchgat(&self, y: i32, x: i32, n: i32, attributes: chtype, color_pair: i16) -> i32 { 407 unsafe { 408 curses::mvwchgat( 409 self._window, 410 y, 411 x, 412 n, 413 attributes, 414 color_pair, 415 ptr::null_mut(), 416 ) 417 } 418 } 419 420 /// Moves a derived window (or subwindow) inside its parent window. 421 /// 422 /// The screen-relative parameters of the window are not changed. This routine is used to 423 /// display different parts of the parent window at the same physical position on the screen. 424 pub fn mvderwin(&self, pary: i32, parx: i32) -> i32 { 425 unsafe { curses::mvderwin(self._window, pary, parx) } 426 } 427 428 /// Retrieves the character and attribute from the specified window position, in the form of a 429 /// chtype. 430 pub fn mvinch(&self, y: i32, x: i32) -> chtype { 431 unsafe { curses::mvwinch(self._window, y, x) } 432 } 433 434 /// Move the cursor and then insert the character ch before the character under the cursor. 435 /// 436 /// First performs a cursor movement using wmove, and returns an error if the position is 437 /// outside the window. All characters to the right of the cursor are moved one space to the 438 /// right, with the possibility of the rightmost character on the line being lost. The insertion 439 /// operation does not change the cursor position. 440 pub fn mvinsch<T: ToChtype>(&self, y: i32, x: i32, ch: T) -> i32 { 441 unsafe { curses::mvwinsch(self._window, y, x, ch.to_chtype()) } 442 } 443 444 /// Add a string to the window at the specified cursor position. 445 pub fn mvprintw<T: AsRef<str>>(&self, y: i32, x: i32, string: T) -> i32 { 446 let s = CString::new(string.as_ref()).unwrap(); 447 unsafe { curses::mvwprintw(self._window, y, x, s.as_ptr()) } 448 } 449 450 /// Moves the window so that the upper left-hand corner is at position (y,x). 451 /// 452 /// If the move would cause the window to be off the screen, it is an error and the window is 453 /// not moved. Moving subwindows is allowed. 454 pub fn mvwin(&self, y: i32, x: i32) -> i32 { 455 unsafe { curses::mvwin(self._window, y, x) } 456 } 457 458 /// Controls whether wgetch() is a non-blocking call. If the option is enabled, and 459 /// no input is ready, wgetch() will return ERR. If disabled, wgetch() will hang until input is 460 /// ready. 461 pub fn nodelay(&self, enabled: bool) -> i32 { 462 unsafe { curses::nodelay(self._window, enabled as u8) as i32 } 463 } 464 465 /// Copies the window to the virtual screen. 466 pub fn noutrefresh(&self) -> i32 { 467 unsafe { curses::wnoutrefresh(self._window) } 468 } 469 470 /// Overlays this window on top of destination_window. This window and destination_window are 471 /// not required to be the same size; only text where the two windows overlap is copied. 472 /// overlay() is non-destructive. 473 pub fn overlay(&self, destination_window: &Window) -> i32 { 474 unsafe { curses::overlay(self._window, destination_window._window) } 475 } 476 477 /// Overlays this window on top of destination_window. This window and destination_window are 478 /// not required to be the same size; only text where the two windows overlap is copied. 479 /// overwrite() is destructive. 480 pub fn overwrite(&self, destination_window: &Window) -> i32 { 481 unsafe { curses::overwrite(self._window, destination_window._window) } 482 } 483 484 /// Add a string to the window at the current cursor position. 485 pub fn printw<T: AsRef<str>>(&self, string: T) -> i32 { 486 let s = CString::new(string.as_ref()).unwrap(); 487 unsafe { curses::wprintw(self._window, s.as_ptr()) } 488 } 489 490 /// Copies the named window to the physical terminal screen, taking into account what 491 /// is already there in order to optimize cursor movement. 492 /// 493 /// This function must be called to get any output on the terminal, as other routines only 494 /// manipulate data structures. Unless leaveok() has been enabled, the physical cursor of the 495 /// terminal is left at the location of the window's cursor. 496 pub fn refresh(&self) -> i32 { 497 unsafe { curses::wrefresh(self._window) } 498 } 499 500 /// If enabled and a scrolling region is set with setscrreg(), any attempt to move off 501 /// the bottom margin will cause all lines in the scrolling region to scroll up one line. 502 pub fn scrollok(&self, bf: bool) -> i32 { 503 unsafe { curses::scrollok(self._window, bf as u8) } 504 } 505 506 /// Sets a scrolling region in a window. 507 /// 508 /// "top" and "bot" are the line numbers for the top and bottom margins. 509 pub fn setscrreg(&self, top: i32, bot: i32) -> i32 { 510 unsafe { curses::wsetscrreg(self._window, top, bot) } 511 } 512 513 /// Creates a new subwindow within a window. 514 /// 515 /// The dimensions of the subwindow are nlines lines and ncols columns. The subwindow is at 516 /// position (begy, begx) on the screen. This position is relative to the screen, and not to 517 /// the window orig. Changes made to either window will affect both. When using this routine, 518 /// you will often need to call touchwin() before calling refresh(). 519 pub fn subwin(&self, nlines: i32, ncols: i32, begy: i32, begx: i32) -> Result<Window, i32> { 520 let new_window = unsafe { curses::subwin(self._window, nlines, ncols, begy, begx) }; 521 if new_window.is_null() { 522 Err(ERR) 523 } else { 524 Ok(Window { 525 _window: new_window, 526 _stdscr: false, 527 _deleted: false, 528 }) 529 } 530 } 531 532 /// Set blocking or non-blocking reads for the specified window. 533 /// 534 /// The delay is measured in milliseconds. If it's negative, a blocking read is used; if zero, 535 /// then non-blocking reads are done -- if no input is waiting, ERR is returned immediately. 536 /// If the delay is positive, the read blocks for the delay period; if the period expires, 537 /// ERR is returned. 538 pub fn timeout(&self, milliseconds: i32) { 539 unsafe { curses::wtimeout(self._window, milliseconds) } 540 } 541 542 /// Throws away all information about which parts of the window have been touched, pretending 543 /// that the entire window has been drawn on. 544 /// 545 /// This is sometimes necessary when using overlapping windows, since a change to one window 546 /// will affect the other window, but the records of which lines have been changed in the other 547 /// window will not reflect the change. 548 pub fn touch(&self) -> i32 { 549 unsafe { curses::touchwin(self._window) } 550 } 551 552 /// Throws away all information about which parts of the window have been touched, pretending 553 /// that the entire window has been drawn on. 554 /// 555 /// This is sometimes necessary when using overlapping windows, since a change to one window 556 /// will affect the other window, but the records of which lines have been changed in the other 557 /// window will not reflect the change. 558 pub fn touchline(&self, start: i32, count: i32) -> i32 { 559 unsafe { curses::touchline(self._window, start, count) } 560 } 561 562 /// Makes n lines in the window, starting at line y, look as if they have or have not been 563 /// changed since the last call to refresh(). 564 pub fn touchln(&self, y: i32, n: i32, changed: bool) -> i32 { 565 unsafe { curses::wtouchln(self._window, y, n, if changed { 1 } else { 0 }) } 566 } 567 568 /// Places ch back onto the input queue to be returned by the next call to getch(). 569 pub fn ungetch(&self, input: &Input) -> i32 { 570 platform_specific::_ungetch(input) 571 } 572 573 /// Marks all lines in the window as unchanged since the last call to refresh(). 574 pub fn untouch(&self) -> i32 { 575 unsafe { curses::untouchwin(self._window) } 576 } 577 578 /// Draw a vertical line using ch from the current cursor position. The line is at most 579 /// n characters long, or as many as fit into the window. 580 pub fn vline<T: ToChtype>(&self, ch: T, n: i32) -> i32 { 581 unsafe { curses::wvline(self._window, ch.to_chtype(), n) } 582 } 583 } 584 585 pub fn new_window(window_pointer: WindowPointer, is_stdscr: bool) -> Window { 586 Window { 587 _window: window_pointer, 588 _stdscr: is_stdscr, 589 _deleted: false, 590 } 591 } 592 593 /// Automatically clean up window resources when dropped 594 impl Drop for Window { 595 fn drop(&mut self) { 596 if !self._stdscr && !self._deleted { 597 unsafe { 598 curses::delwin(self._window); 599 } 600 } 601 } 602 } 603