libnotcurses_sys/direct/
methods.rs

1//! `NcDirect` methods and associated functions.
2
3use core::ptr::{null, null_mut};
4
5use crate::{
6    c_api::{self, ffi::wchar_t},
7    cstring, error, error_ref_mut, rstring_free, NcAlign, NcBlitter, NcCapabilities, NcChannels,
8    NcDirect, NcDirectFlag, NcError, NcFd, NcInput, NcPaletteIndex, NcPlane, NcResult, NcRgb,
9    NcScale, NcStyle, NcTime,
10};
11
12#[cfg(not(feature = "std"))]
13use alloc::{format, string::String};
14
15/// # `NcDirect` constructors and destructors
16impl NcDirect {
17    /// New NcDirect with the default options.
18    ///
19    /// Initializes a direct-mode notcurses context on the tty.
20    ///
21    /// Direct mode supports a limited subset of notcurses routines,
22    /// and neither supports nor requires
23    /// [`notcurses_render`][c_api::notcurses_render]. This can be used to add
24    /// color and styling to text in the standard output paradigm.
25    ///
26    /// # Safety
27    /// You must not create multiple `NcDirect` instances at the same time, on
28    /// the same thread. You must [`stop`][NcDirect#method.stop] the current one
29    /// before creating a new one.
30    ///
31    /// *C style function: [ncdirect_init()][c_api::ncdirect_init].*
32    pub unsafe fn new<'a>() -> NcResult<&'a mut NcDirect> {
33        Self::with_flags(NcDirectFlag::None)
34    }
35
36    /// New `NcDirect` with optional flags.
37    ///
38    /// # Safety
39    /// You must not create multiple `NcDirect` instances at the same time, on
40    /// the same thread. You must [`stop`][NcDirect#method.stop] the current one
41    /// before creating a new one.
42    ///
43    /// *C style function: [ncdirect_init()][c_api::ncdirect_init].*
44    pub unsafe fn with_flags<'a>(flags: impl Into<NcDirectFlag>) -> NcResult<&'a mut NcDirect> {
45        let res = c_api::ncdirect_init(null(), null_mut(), flags.into().into());
46        error_ref_mut![res, "Initializing NcDirect"]
47    }
48
49    /// Releases this `NcDirect` and any associated resources.
50    ///
51    /// # Safety
52    /// You must not call this method repeatedly on the same `NcDirect` instance.
53    ///
54    /// *C style function: [ncdirect_stop()][c_api::ncdirect_stop].*
55    pub unsafe fn stop(&mut self) -> NcResult<()> {
56        error![c_api::ncdirect_stop(self), "NcDirect.stop()"]
57    }
58}
59
60/// ## NcDirect methods: clear, flush, render
61impl NcDirect {
62    /// Clears the screen.
63    ///
64    /// *C style function: [ncdirect_clear()][c_api::ncdirect_clear].*
65    pub fn clear(&mut self) -> NcResult<()> {
66        error![unsafe { c_api::ncdirect_clear(self) }, "NcDirect.clear()"]
67    }
68
69    /// Forces a flush.
70    ///
71    /// *C style function: [ncdirect_flush()][c_api::ncdirect_flush].*
72    pub fn flush(&self) -> NcResult<()> {
73        error![unsafe { c_api::ncdirect_flush(self) }, "NcDirect.clear()"]
74    }
75
76    /// Takes the result of [`render_frame`][NcDirect#method.render_frame]
77    /// and writes it to the output.
78    ///
79    /// *C style function: [ncdirect_raster_frame()][c_api::ncdirect_raster_frame].*
80    pub fn raster_frame(&mut self, frame: &mut NcPlane, align: impl Into<NcAlign>) -> NcResult<()> {
81        error![
82            unsafe { c_api::ncdirect_raster_frame(self, frame, align.into().into()) },
83            "NcDirect.raster_frame()"
84        ]
85    }
86
87    /// Renders an image using the specified blitter and scaling,
88    /// but doesn't write the result.
89    ///
90    /// The image may be arbitrarily many rows -- the output will scroll --
91    /// but will only occupy the column of the cursor, and those to the right.
92    ///
93    /// To actually write (and free) this, invoke ncdirect_raster_frame().
94    ///
95    /// `max_y' and 'max_x` (cell geometry, *not* pixel), if greater than 0,
96    /// are used for scaling; the terminal's geometry is otherwise used.
97    ///
98    /// *C style function: [ncdirect_render_frame()][c_api::ncdirect_render_frame].*
99    pub fn render_frame<'a>(
100        &mut self,
101        filename: &str,
102        blitter: impl Into<NcBlitter>,
103        scale: impl Into<NcScale>,
104        max_y: u32,
105        max_x: u32,
106    ) -> NcResult<&'a mut NcPlane> {
107        let (blitter, scale) = (blitter.into(), scale.into());
108        let cs = cstring![filename];
109        let res = unsafe {
110            c_api::ncdirect_render_frame(
111                self,
112                cs.as_ptr(),
113                blitter.into(),
114                scale.into(),
115                max_y as i32,
116                max_x as i32,
117            )
118        };
119        error_ref_mut![
120            res,
121            &format!(
122                "NcDirect.render_frame({:?}, {:?}, {:?})",
123                filename, blitter, scale
124            )
125        ]
126    }
127
128    /// Displays an image using the specified blitter and scaling.
129    ///
130    /// The image may be arbitrarily many rows -- the output will scroll -- but
131    /// will only occupy the column of the cursor, and those to the right.
132    ///
133    /// The render/raster process can be split by using
134    /// [`render_frame`][#method.render_frame] and
135    /// [`raster_frame`][#method.raster_frame].
136    ///
137    /// *C style function: [ncdirect_render_image()][c_api::ncdirect_render_image].*
138    pub fn render_image(
139        &mut self,
140        filename: &str,
141        align: impl Into<NcAlign>,
142        blitter: impl Into<NcBlitter>,
143        scale: impl Into<NcScale>,
144    ) -> NcResult<()> {
145        let (align, blitter, scale) = (align.into(), blitter.into(), scale.into());
146        let cs = cstring![filename];
147        error![
148            unsafe {
149                c_api::ncdirect_render_image(
150                    self,
151                    cs.as_ptr(),
152                    align.into(),
153                    blitter.into(),
154                    scale.into(),
155                )
156            },
157            &format!(
158                "NcDirect.render_image({:?}, {:?}, {:?}, {:?})",
159                filename, align, blitter, scale
160            )
161        ]
162    }
163}
164
165/// ## NcDirect methods: `NcPaletteIndex`, `NcRgb`, `NcStyle` & default color
166impl NcDirect {
167    /// Sets the foreground [`NcPaletteIndex`].
168    ///
169    /// *C style function: [ncdirect_set_fg_palindex()][c_api::ncdirect_set_fg_palindex].*
170    pub fn set_fg_palindex(&mut self, index: impl Into<NcPaletteIndex>) -> NcResult<()> {
171        let index = index.into();
172        error![
173            unsafe { c_api::ncdirect_set_fg_palindex(self, index as i32) },
174            &format!("NcDirect.set_fg_palindex({})", index)
175        ]
176    }
177
178    /// Sets the background [`NcPaletteIndex`].
179    ///
180    /// *C style function: [ncdirect_set_bg_palindex()][c_api::ncdirect_set_bg_palindex].*
181    pub fn set_bg_palindex(&mut self, index: impl Into<NcPaletteIndex>) -> NcResult<()> {
182        let index = index.into();
183        error![
184            unsafe { c_api::ncdirect_set_bg_palindex(self, index as i32) },
185            &format!("NcDirect.set_fg_palindex({})", index)
186        ]
187    }
188
189    /// Returns the number of simultaneous colors claimed to be supported,
190    /// if there is color support.
191    ///
192    /// Note that several terminal emulators advertise more colors than they
193    /// actually support, downsampling internally.
194    ///
195    /// *C style function: [ncdirect_palette_size()][c_api::ncdirect_palette_size].*
196    pub fn palette_size(&self) -> NcResult<u32> {
197        let res = unsafe { c_api::ncdirect_palette_size(self) };
198        if res == 1 {
199            return Err(NcError::with_msg(
200                1,
201                "No color support ← NcDirect.palette_size()",
202            ));
203        }
204        Ok(res)
205    }
206
207    /// Sets the foreground [`NcRgb`].
208    ///
209    /// *C style function: [ncdirect_set_fg_rgb()][c_api::ncdirect_set_fg_rgb].*
210    pub fn set_fg_rgb(&mut self, rgb: impl Into<NcRgb>) -> NcResult<()> {
211        error![
212            unsafe { c_api::ncdirect_set_fg_rgb(self, rgb.into().into()) },
213            "NcDirect.set_fg_rgb()"
214        ]
215    }
216
217    /// Sets the background [`NcRgb`].
218    ///
219    /// *C style function: [ncdirect_set_bg_rgb()][c_api::ncdirect_set_bg_rgb].*
220    pub fn set_bg_rgb(&mut self, rgb: impl Into<NcRgb>) -> NcResult<()> {
221        error![
222            unsafe { c_api::ncdirect_set_bg_rgb(self, rgb.into().into()) },
223            "NcDirect.set_bg_rgb({:?})"
224        ]
225    }
226
227    /// Returns the current styling.
228    ///
229    /// *C style function: [ncdirect_styles()][c_api::ncdirect_styles].*
230    pub fn styles(&self) -> NcStyle {
231        unsafe { c_api::ncdirect_styles(self).into() }
232    }
233
234    /// Removes the specified `styles`.
235    ///
236    /// *C style function: [ncdirect_off_styles()][c_api::ncdirect_off_styles].*
237    pub fn styles_off(&mut self, styles: impl Into<NcStyle>) -> NcResult<()> {
238        let styles = styles.into();
239        error![
240            unsafe { c_api::ncdirect_off_styles(self, styles.into()) },
241            &format!("NcDirect.styles_off({:0X})", styles)
242        ]
243    }
244
245    /// Adds the specified `styles`.
246    ///
247    /// *C style function: [ncdirect_on_styles()][c_api::ncdirect_on_styles].*
248    pub fn styles_on(&mut self, styles: impl Into<NcStyle>) -> NcResult<()> {
249        let styles = styles.into();
250        error![
251            unsafe { c_api::ncdirect_on_styles(self, styles.into()) },
252            &format!("NcDirect.styles_on({:0X})", styles)
253        ]
254    }
255
256    /// Sets just the specified `styles`.
257    ///
258    /// *C style function: [ncdirect_set_styles()][c_api::ncdirect_set_styles].*
259    pub fn styles_set(&mut self, styles: impl Into<NcStyle>) -> NcResult<()> {
260        let styles = styles.into();
261        error![
262            unsafe { c_api::ncdirect_set_styles(self, styles.into()) },
263            &format!("NcDirect.styles_set({:0X})", styles)
264        ]
265    }
266
267    /// Returns an [`NcStyle`] with the supported curses-style attributes.
268    ///
269    /// The attribute is only indicated as supported if the terminal can support
270    /// it together with color.
271    ///
272    /// For more information, see the "ncv" capability in *terminfo(5)*.
273    ///
274    /// *C style function: [ncdirect_supported_styles()][c_api::ncdirect_supported_styles].*
275    pub fn supported_styles(&self) -> NcStyle {
276        unsafe { c_api::ncdirect_supported_styles(self).into() }
277    }
278
279    /// Indicates to use the "default color" for the foreground.
280    ///
281    /// *C style function: [ncdirect_set_fg_default()][c_api::ncdirect_set_fg_default].*
282    pub fn set_fg_default(&mut self) -> NcResult<()> {
283        error![
284            unsafe { c_api::ncdirect_set_fg_default(self) },
285            "NcDirect.set_fg_default()"
286        ]
287    }
288
289    /// Indicates to use the "default color" for the background.
290    ///
291    /// *C style function: [ncdirect_set_bg_default()][c_api::ncdirect_set_bg_default].*
292    pub fn set_bg_default(&mut self) -> NcResult<()> {
293        error![
294            unsafe { c_api::ncdirect_set_bg_default(self) },
295            "NcDirect.set_bg_default()"
296        ]
297    }
298}
299
300/// ## NcDirect methods: capabilities, cursor, dimensions
301impl NcDirect {
302    /// Is there support for acquiring the cursor's current position?
303    ///
304    /// Requires the u7 terminfo capability, and that we are connected to an
305    /// actual terminal.
306    pub fn canget_cursor(&self) -> bool {
307        unsafe { c_api::ncdirect_canget_cursor(self) }
308    }
309
310    /// Can we reliably use Unicode braille?
311    ///
312    /// *C style function: [ncdirect_canbraille()][c_api::ncdirect_canbraille].*
313    pub fn canbraille(&self) -> bool {
314        c_api::ncdirect_canbraille(self)
315    }
316
317    /// Can we set the "hardware" palette?
318    ///
319    /// Requires the "ccc" terminfo capability.
320    ///
321    /// *C style function: [ncdirect_canchangecolor()][c_api::ncdirect_canchangecolor].*
322    pub fn canchangecolor(&self) -> bool {
323        c_api::ncdirect_canchangecolor(self)
324    }
325
326    /// Can we fade?
327    ///
328    /// Requires either the "rgb" or "ccc" terminfo capability.
329    ///
330    /// *C style function: [ncdirect_canfade()][c_api::ncdirect_canfade].*
331    pub fn canfade(&self) -> bool {
332        c_api::ncdirect_canfade(self)
333    }
334
335    /// Can we reliably use Unicode halfblocks?
336    ///
337    /// *C style function: [ncdirect_canhalfblock()][c_api::ncdirect_canhalfblock].*
338    pub fn canhalfblock(&self) -> bool {
339        c_api::ncdirect_canhalfblock(self)
340    }
341
342    /// Can we load images?
343    ///
344    /// Requires being built against FFmpeg/OIIO.
345    ///
346    /// *C style function: [ncdirect_canopen_images()][c_api::ncdirect_canopen_images].*
347    pub fn canopen_images(&self) -> bool {
348        c_api::ncdirect_canopen_images(self)
349    }
350
351    /// Can we load videos?
352    ///
353    /// Requires being built against FFmpeg/OIIO.
354    ///
355    /// *C style function: [ncdirect_canopen_videos()][c_api::ncdirect_canopen_videos].*
356    pub fn canopen_videos(&self) -> bool {
357        c_api::ncdirect_canopen_videos(self)
358    }
359
360    /// Can we reliably use Unicode quadrants?
361    ///
362    /// *C style function: [ncdirect_canquadrant()][c_api::ncdirect_canquadrant].*
363    pub fn canquadrant(&self) -> bool {
364        c_api::ncdirect_canquadrant(self)
365    }
366
367    /// Can we reliably use Unicode sextants?
368    ///
369    /// *C style function: [ncdirect_cansextant()][c_api::ncdirect_cansextant].*
370    pub fn cansextant(&self) -> bool {
371        c_api::ncdirect_cansextant(self)
372    }
373
374    /// Can we directly specify RGB values per cell, or only use palettes?
375    ///
376    /// *C style function: [ncdirect_cantruecolor()][c_api::ncdirect_cantruecolor].*
377    pub fn cantruecolor(&self) -> bool {
378        c_api::ncdirect_cantruecolor(self)
379    }
380
381    /// Is our encoding UTF-8?
382    ///
383    /// Requires LANG being set to a UTF8 locale.
384    ///
385    /// *C style function: [ncdirect_canutf8()][c_api::ncdirect_canutf8].*
386    pub fn canutf8(&self) -> bool {
387        unsafe { c_api::ncdirect_canutf8(self) }
388    }
389
390    /// Returns the [`NcCapabilities`].
391    ///
392    /// *C style function: [ncdirect_capabilities()][c_api::ncdirect_capabilities].*
393    pub fn capabilities(&self) -> NcCapabilities {
394        c_api::ncdirect_capabilities(self)
395    }
396
397    /// Checks for pixel support.
398    ///
399    /// Returns `false` for no support, or `true` if pixel output is supported.
400    ///
401    /// This function must successfully return before NCBLIT_PIXEL is available.
402    ///
403    /// Must not be called concurrently with either input or rasterization.
404    ///
405    /// *C style function: [ncdirect_check_pixel_support()][c_api::ncdirect_check-pixel_support].*
406    #[allow(clippy::wildcard_in_or_patterns)]
407    pub fn check_pixel_support(&self) -> NcResult<bool> {
408        let res = unsafe { c_api::ncdirect_check_pixel_support(self) };
409        match res {
410            0 => Ok(false),
411            1 => Ok(true),
412            c_api::NCRESULT_ERR | _ => {
413                Err(NcError::with_msg(res, "NcDirect.check_pixel_support()"))
414            }
415        }
416    }
417
418    /// Disables the terminal's cursor, if supported.
419    ///
420    /// *C style function: [ncdirect_cursor_disable()][c_api::ncdirect_cursor_disable].*
421    pub fn cursor_disable(&mut self) -> NcResult<()> {
422        error![
423            unsafe { c_api::ncdirect_cursor_disable(self) },
424            "NcDirect.cursor_disable()"
425        ]
426    }
427
428    /// Enables the terminal's cursor, if supported.
429    ///
430    /// *C style function: [ncdirect_cursor_enable()][c_api::ncdirect_cursor_enable].*
431    pub fn cursor_enable(&mut self) -> NcResult<()> {
432        error![
433            unsafe { c_api::ncdirect_cursor_enable(self) },
434            "NcDirect.cursor_enable()"
435        ]
436    }
437
438    /// Moves the cursor down any number of rows.
439    ///
440    /// *C style function: [ncdirect_cursor_down()][c_api::ncdirect_cursor_down].*
441    pub fn cursor_down(&mut self, rows: i32) -> NcResult<()> {
442        error![
443            unsafe { c_api::ncdirect_cursor_down(self, rows) },
444            &format!("NcDirect.cursor_down({})", rows)
445        ]
446    }
447
448    /// Moves the cursor left any number of columns.
449    ///
450    /// *C style function: [ncdirect_cursor_left()][c_api::ncdirect_cursor_left].*
451    pub fn cursor_left(&mut self, cols: i32) -> NcResult<()> {
452        error![
453            unsafe { c_api::ncdirect_cursor_left(self, cols) },
454            &format!("NcDirect.cursor_left({})", cols)
455        ]
456    }
457
458    /// Moves the cursor right any number of columns.
459    ///
460    /// *C style function: [ncdirect_cursor_right()][c_api::ncdirect_cursor_right].*
461    pub fn cursor_right(&mut self, cols: i32) -> NcResult<()> {
462        error![
463            unsafe { c_api::ncdirect_cursor_right(self, cols) },
464            &format!("NcDirect.cursor_right({})", cols)
465        ]
466    }
467
468    /// Moves the cursor up any number of rows.
469    ///
470    /// *C style function: [ncdirect_cursor_up()][c_api::ncdirect_cursor_up].*
471    pub fn cursor_up(&mut self, rows: i32) -> NcResult<()> {
472        error![
473            unsafe { c_api::ncdirect_cursor_up(self, rows) },
474            &format!("NcDirect.cursor_up({})", rows)
475        ]
476    }
477
478    /// Sets the cursor to the specified row `y`, column `x`.
479    ///
480    /// *C style function: [ncdirect_cursor_move_yx()][c_api::ncdirect_cursor_move_yx].*
481    pub fn cursor_set_yx(&mut self, y: u32, x: u32) -> NcResult<()> {
482        error![unsafe { c_api::ncdirect_cursor_move_yx(self, y as i32, x as i32) }]
483    }
484
485    /// Sets the cursor to the specified row `y`.
486    ///
487    /// *(No equivalent C style function)*
488    pub fn cursor_set_y(&mut self, y: u32) -> NcResult<()> {
489        error![unsafe { c_api::ncdirect_cursor_move_yx(self, y as i32, -1) }]
490    }
491
492    /// Sets the cursor to the specified column `x`.
493    ///
494    /// *(No equivalent C style function)*
495    pub fn cursor_set_x(&mut self, x: u32) -> NcResult<()> {
496        error![unsafe { c_api::ncdirect_cursor_move_yx(self, -1, x as i32) }]
497    }
498
499    /// Gets the cursor (y, x) position, when supported.
500    ///
501    /// This requires writing to the terminal, and then reading from it.
502    /// If the terminal doesn't reply, or doesn't reply in a way we understand,
503    /// the results might be detrimental.
504    ///
505    /// *C style function: [ncdirect_cursor_yx()][c_api::ncdirect_cursor_yx].*
506    pub fn cursor_yx(&mut self) -> NcResult<(u32, u32)> {
507        let (mut y, mut x) = (0, 0);
508        error![
509            unsafe { c_api::ncdirect_cursor_yx(self, &mut y, &mut x) },
510            "",
511            (y, x)
512        ]
513    }
514
515    /// Pushes the cursor location to the terminal's stack.
516    ///
517    /// The depth of this stack, and indeed its existence, is terminal-dependent.
518    ///
519    /// *C style function: [ncdirect_cursor_push()][c_api::ncdirect_cursor_push].*
520    pub fn cursor_push(&mut self) -> NcResult<()> {
521        error![unsafe { c_api::ncdirect_cursor_push(self) }]
522    }
523
524    /// Pops the cursor location from the terminal's stack.
525    ///
526    /// The depth of this stack, and indeed its existence, is terminal-dependent.
527    ///
528    /// *C style function: [ncdirect_cursor_pop()][c_api::ncdirect_cursor_pop].*
529    pub fn cursor_pop(&mut self) -> NcResult<()> {
530        error![unsafe { c_api::ncdirect_cursor_pop(self) }]
531    }
532
533    /// Gets the current number of rows.
534    ///
535    /// *C style function: [ncdirect_dim_y()][c_api::ncdirect_dim_y].*
536    pub fn dim_y(&mut self) -> u32 {
537        unsafe { c_api::ncdirect_dim_y(self) }
538    }
539
540    /// Gets the current number of columns.
541    ///
542    /// *C style function: [ncdirect_dim_x()][c_api::ncdirect_dim_x].*
543    pub fn dim_x(&mut self) -> u32 {
544        unsafe { c_api::ncdirect_dim_x(self) }
545    }
546
547    /// Gets the current number of rows and columns.
548    ///
549    /// *C style function: [ncdirect_dim_y()][c_api::ncdirect_dim_y].*
550    pub fn dim_yx(&mut self) -> (u32, u32) {
551        let y = unsafe { c_api::ncdirect_dim_y(self) };
552        let x = unsafe { c_api::ncdirect_dim_x(self) };
553        (y, x)
554    }
555
556    /// Returns the name of the detected terminal.
557    ///
558    /// *C style function: [ncdirect_detected_terminal()][c_api::ncdirect_detected_terminal].*
559    pub fn detected_terminal(&self) -> String {
560        rstring_free![c_api::ncdirect_detected_terminal(self)]
561    }
562}
563
564/// ## NcDirect methods: I/O
565impl NcDirect {
566    /// Returns a [char] representing a single unicode point.
567    ///
568    /// If an event is processed, the return value is the `id` field from that
569    /// event.
570    ///
571    /// Provide a None `time` to block at length, a `time` of 0 for non-blocking
572    /// operation, and otherwise a timespec to bound blocking.
573    ///
574    /// *C style function: [ncdirect_get()][c_api::ncdirect_get].*
575    // CHECK returns 0 on a timeout.
576    pub fn get(&mut self, time: Option<NcTime>, input: Option<&mut NcInput>) -> NcResult<char> {
577        let ntime = if let Some(time) = time { &time as *const _ } else { null() };
578        let ninput = if let Some(input) = input { input as *mut _ } else { null_mut() };
579
580        let res = unsafe { c_api::ncdirect_get(self, ntime, ninput) };
581        core::char::from_u32(res)
582            .ok_or_else(|| NcError::with_msg(res as i32, &format!["Nc.get(time: {:?})", time]))
583    }
584
585    /// Reads input blocking until an event is processed or a signal is received.
586    ///
587    /// Will optionally write the event details in `input`.
588    ///
589    /// In the case of a valid read, a [`char`] is returned.
590    ///
591    /// *C style function: [ncdirect_get_blocking()][c_api::ncdirect_get_blocking].*
592    pub fn get_blocking(&mut self, input: Option<&mut NcInput>) -> NcResult<char> {
593        let res = c_api::ncdirect_get_blocking(self, input);
594        core::char::from_u32(res as u32)
595            .ok_or_else(|| NcError::with_msg(res, "NcDirect.get_blocking()"))
596    }
597
598    /// Reads input without blocking.
599    ///
600    /// In the case of a valid read, a [`char`] is returned.
601    ///
602    /// If no event is ready, returns 0.
603    ///
604    /// *C style function: [ncdirect_get_nblock()][c_api::ncdirect_get_nblock].*
605    pub fn get_nblock(&mut self, input: Option<&mut NcInput>) -> NcResult<char> {
606        let res = c_api::ncdirect_get_nblock(self, input);
607        core::char::from_u32(res as u32)
608            .ok_or_else(|| NcError::with_msg(res, "NcDirect.get_nblock()"))
609    }
610
611    /// Get a file descriptor suitable for input event poll()ing.
612    ///
613    /// When this descriptor becomes available, you can call `NcDirect.`
614    /// [`get_nblock`][NcDirect#method.get_nblock], and input ought be ready.
615    ///
616    /// This file descriptor is not necessarily the file descriptor associated
617    /// with stdin (but it might be!).
618    ///
619    /// *C style function: [ncdirect_inputready_fd()][c_api::ncdirect_inputready_fd].*
620    pub fn inputready_fd(&mut self) -> NcResult<NcFd> {
621        let res = unsafe { c_api::ncdirect_inputready_fd(self) };
622        error![res, "", res]
623    }
624
625    /// Outputs the `string` according to the `channels`, and
626    /// returns the total number of characters written on success.
627    ///
628    /// Note that it does not explicitly flush output buffers, so it will not
629    /// necessarily be immediately visible.
630    ///
631    /// It will fail if the NcDirect context and the foreground channel
632    /// are both marked as using the default color.
633    ///
634    /// *C style function: [ncdirect_putstr()][c_api::ncdirect_putstr].*
635    pub fn putstr(&mut self, channels: impl Into<NcChannels>, string: &str) -> NcResult<()> {
636        let channels = channels.into();
637        let cs = cstring![string];
638        error![
639            unsafe { c_api::ncdirect_putstr(self, channels.into(), cs.as_ptr()) },
640            &format!("NcDirect.putstr({:0X}, {:?})", channels, string)
641        ]
642    }
643
644    /// Reads a (heap-allocated) line of text using the Readline library.
645    ///
646    /// Initializes Readline the first time it's called.
647    ///
648    /// For input to be echoed to the terminal, it is necessary that the flag
649    /// [`NcDirectFlag::INHIBIT_CBREAK`][0] be provided to the constructor.
650    ///
651    /// [0]: NcDirectFlag#associatedconstant.INHIBIT_CBREAK
652    ///
653    /// *C style function: [ncdirect_readline()][c_api::ncdirect_readline].*
654    //
655    // CHECK if memory leak still reported by valgrind
656    pub fn readline(&mut self, prompt: &str) -> NcResult<String> {
657        let cs = cstring![prompt];
658        let res = unsafe { c_api::ncdirect_readline(self, cs.as_ptr()) };
659        if !res.is_null() {
660            Ok(rstring_free![res])
661        } else {
662            Err(NcError::with_msg(
663                c_api::NCRESULT_ERR,
664                &format!["NcDirect.readline({})", prompt],
665            ))
666        }
667    }
668
669    /// Draws a box with its upper-left corner at the current cursor position,
670    /// having dimensions `ylen` * `xlen`.
671    ///
672    /// See `NcPlane.`[`box`][NcPlane#method.box] for more information.
673    ///
674    /// The minimum box size is 2x2, and it cannot be drawn off-screen.
675    ///
676    /// `wchars` is an array of 6 characters: UL, UR, LL, LR, HL, VL.
677    ///
678    /// *C style function: [ncdirect_box()][c_api::ncdirect_box].*
679    //
680    // CHECK, specially wchars.
681    pub fn r#box(
682        &mut self,
683        ul: impl Into<NcChannels>,
684        ur: impl Into<NcChannels>,
685        ll: impl Into<NcChannels>,
686        lr: impl Into<NcChannels>,
687        wchars: &[char; 6],
688        len_y: u32,
689        len_x: u32,
690        ctlword: u32,
691    ) -> NcResult<()> {
692        let (ul, ur, ll, lr) = (ul.into(), ur.into(), ll.into(), lr.into());
693        error![
694            unsafe {
695                let wchars = wchars as *const [char; 6] as *const wchar_t;
696                c_api::ncdirect_box(self, ul.0, ur.0, ll.0, lr.0, wchars, len_y, len_x, ctlword)
697            },
698            &format!(
699                "NcDirect.box({:0X}, {:0X}, {:0X}, {:0X}, {:?}, {}, {}, {})",
700                ul, ur, ll, lr, wchars, len_y, len_x, ctlword
701            )
702        ]
703    }
704
705    /// `NcDirect.`[`box`][NcDirect#method.box] with the double box-drawing characters.
706    ///
707    /// *C style function: [ncdirect_double_box()][c_api::ncdirect_double_box].*
708    pub fn double_box(
709        &mut self,
710        ul: impl Into<NcChannels>,
711        ur: impl Into<NcChannels>,
712        ll: impl Into<NcChannels>,
713        lr: impl Into<NcChannels>,
714        len_y: u32,
715        len_x: u32,
716        ctlword: u32,
717    ) -> NcResult<()> {
718        error![unsafe {
719            c_api::ncdirect_double_box(
720                self,
721                ul.into().0,
722                ur.into().0,
723                ll.into().0,
724                lr.into().0,
725                len_y,
726                len_x,
727                ctlword,
728            )
729        }]
730    }
731
732    /// `NcDirect.`[`box`][NcDirect#method.box] with the rounded box-drawing characters.
733    ///
734    /// *C style function: [ncdirect_rounded_box()][c_api::ncdirect_rounded_box].*
735    pub fn rounded_box(
736        &mut self,
737        ul: impl Into<NcChannels>,
738        ur: impl Into<NcChannels>,
739        ll: impl Into<NcChannels>,
740        lr: impl Into<NcChannels>,
741        len_y: u32,
742        len_x: u32,
743        ctlword: u32,
744    ) -> NcResult<()> {
745        error![unsafe {
746            c_api::ncdirect_rounded_box(
747                self,
748                ul.into().0,
749                ur.into().0,
750                ll.into().0,
751                lr.into().0,
752                len_y,
753                len_x,
754                ctlword,
755            )
756        }]
757    }
758
759    /// Draws horizontal lines using the specified [NcChannels]s, interpolating
760    /// between them as we go.
761    ///
762    /// All lines start at the current cursor position.
763    ///
764    /// The string at `egc` may not use more than one column.
765    ///
766    /// For a horizontal line, `len` cannot exceed the screen width minus the
767    /// cursor's offset.
768    ///
769    /// *C style function: [ncdirect_hline_interp()][c_api::ncdirect_hline_interp].*
770    #[inline]
771    pub fn hline_interp(
772        &mut self,
773        egc: &str,
774        len: u32,
775        h1: impl Into<NcChannels>,
776        h2: impl Into<NcChannels>,
777    ) -> NcResult<()> {
778        error![c_api::ncdirect_hline_interp(
779            self,
780            egc,
781            len,
782            h1.into().0,
783            h2.into().0
784        )]
785    }
786
787    /// Draws horizontal lines using the specified [NcChannels]s, interpolating
788    /// between them as we go.
789    ///
790    /// All lines start at the current cursor position.
791    ///
792    /// The string at `egc` may not use more than one column.
793    ///
794    /// For a vertical line, `len` may be as long as you'd like; the screen
795    /// will scroll as necessary.
796    ///
797    /// *C style function: [ncdirect_vline_interp()][c_api::ncdirect_vline_interp].*
798    #[inline]
799    pub fn vline_interp(
800        &mut self,
801        egc: &str,
802        len: u32,
803        h1: impl Into<NcChannels>,
804        h2: impl Into<NcChannels>,
805    ) -> NcResult<()> {
806        error![c_api::ncdirect_vline_interp(
807            self,
808            egc,
809            len,
810            h1.into().0,
811            h2.into().0
812        )]
813    }
814}