libnotcurses_sys/notcurses/methods.rs
1//! `Nc*` methods and associated functions.
2
3use core::ptr::{null, null_mut};
4
5use crate::{
6 c_api::{self, notcurses_init},
7 cstring, error, error_ref_mut, rstring, rstring_free, Nc, NcAlign, NcBlitter, NcCapabilities,
8 NcChannels, NcError, NcFd, NcFlag, NcInput, NcLogLevel, NcMiceEvents, NcOptions, NcPixelImpl,
9 NcPlane, NcReceived, NcResult, NcRgb, NcScale, NcStats, NcStyle, NcTime, NcVisual,
10 NcVisualGeometry, NcVisualOptions,
11};
12
13#[cfg(not(feature = "std"))]
14use alloc::{
15 format,
16 string::{String, ToString},
17 vec::Vec,
18};
19
20#[cfg(feature = "std")]
21use crate::NcFile;
22
23/// # `Nc` Constructors and destructors
24impl Nc {
25 /// New notcurses context.
26 ///
27 /// Has the [`SuppressBanners`] flag enabled.
28 ///
29 /// # Safety
30 /// You can't have multiple simultaneous `Nc` instances in the same thread.
31 ///
32 /// [`SuppressBanners`]: NcFlag#associatedconstant.SuppressBanners
33 pub unsafe fn new<'a>() -> NcResult<&'a mut Nc> {
34 Self::with_flags(NcFlag::SuppressBanners)
35 }
36
37 /// New notcurses context in CLI mode.
38 ///
39 /// Has the [`CliMode`] and [`SuppressBanners`] flags enabled.
40 ///
41 /// [`CliMode`]: NcFlag#associatedconstant.CliMode
42 /// [`SuppressBanners`]: NcFlag#associatedconstant.SuppressBanners
43 ///
44 /// # Safety
45 /// You can't have multiple simultaneous `Nc` instances in the same thread.
46 pub unsafe fn new_cli<'a>() -> NcResult<&'a mut Nc> {
47 Self::with_flags(NcFlag::CliMode | NcFlag::SuppressBanners)
48 }
49
50 /// New notcurses context, with banners.
51 ///
52 /// It prints the version information banner at initialization
53 /// and the performance information banner at finalization.
54 ///
55 /// # Safety
56 /// You can't have multiple simultaneous `Nc` instances in the same thread.
57 pub unsafe fn with_banners<'a>() -> NcResult<&'a mut Nc> {
58 Self::with_flags(NcFlag::None)
59 }
60
61 /// New notcurses context in CLI mode, with banners.
62 ///
63 /// It prints the version information banner at initialization
64 /// and the performance information banner at finalization.
65 ///
66 /// It has the [`CliMode`] flag enabled.
67 ///
68 /// [`CliMode`]: NcFlag#associatedconstant.CliMode
69 ///
70 /// # Safety
71 /// You can't have multiple simultaneous `Nc` instances in the same thread.
72 pub unsafe fn with_banners_cli<'a>() -> NcResult<&'a mut Nc> {
73 Self::with_flags(NcFlag::CliMode)
74 }
75
76 /// New notcurses context, expecting `flags`.
77 ///
78 /// # Safety
79 /// You can't have multiple simultaneous `Nc` instances in the same thread.
80 pub unsafe fn with_flags<'a>(flags: impl Into<NcFlag>) -> NcResult<&'a mut Nc> {
81 Self::with_options(NcOptions::with_flags(flags.into()))
82 }
83
84 /// New notcurses context, expects `flags` and `log_level`.
85 ///
86 /// # Safety
87 /// You can't have multiple simultaneous `Nc` instances in the same thread.
88 pub unsafe fn with_flags_log<'a>(
89 flags: impl Into<NcFlag>,
90 log_level: impl Into<NcLogLevel>,
91 ) -> NcResult<&'a mut Nc> {
92 Self::with_options(NcOptions::with_all_options(
93 log_level.into(),
94 Some((0, 0, 0, 0)),
95 flags.into(),
96 ))
97 }
98
99 /// New notcurses context, expects [`NcOptions`].
100 ///
101 /// # Safety
102 /// You can't have multiple simultaneous `Nc` instances in the same thread.
103 pub unsafe fn with_options<'a>(options: NcOptions) -> NcResult<&'a mut Nc> {
104 let res = notcurses_init(&options, null_mut());
105 error_ref_mut![res, &format!["Nc.with_options({:?})", options]]
106 }
107
108 /// Destroys the notcurses context.
109 ///
110 /// # Safety
111 /// You must not call this method repeatedly on the same `Nc` instance.
112 ///
113 /// *C style function: [notcurses_stop()][c_api::notcurses_stop].*
114 pub unsafe fn stop(&mut self) -> NcResult<()> {
115 error![c_api::notcurses_stop(self)]
116 }
117
118 /// Destroys all [`NcPlane`]s other than the stdplane.
119 ///
120 /// # Safety
121 /// Must not use any pre-existing references to `NcPlane`s.
122 ///
123 /// *C style function: [notcurses_drop_planes()][c_api::notcurses_drop_planes].*
124 pub unsafe fn drop_planes(&mut self) {
125 c_api::notcurses_drop_planes(self);
126 }
127}
128
129/// # `Nc` methods
130impl Nc {
131 /// Returns the offset into `availcols` at which `cols` ought be output given
132 /// the requirements of `align`.
133 ///
134 /// Returns `-`[NCRESULT_MAX][c_api::NCRESULT_MAX] if
135 /// [`NcAlign::Unaligned`].
136 ///
137 /// *C style function: [notcurses_align()][c_api::notcurses_align].*
138 pub fn align(availcols: u32, align: impl Into<NcAlign>, cols: u32) -> NcResult<u32> {
139 let align = align.into();
140 let res = c_api::notcurses_align(availcols, align, cols);
141 error![res, &format!("Nc.align({:?}, {})", align, cols), res as u32]
142 }
143
144 /// Retrieves the current contents of the specified [`NcCell`][crate::NcCell]
145 /// as last rendered, returning the `EGC` (or None on error) and writing
146 /// out the [`NcStyle`] and the [`NcChannels`].
147 ///
148 /// *C style function: [notcurses_at_yx()][c_api::notcurses_at_yx].*
149 pub fn at_yx(
150 &mut self,
151 y: u32,
152 x: u32,
153 stylemask: &mut NcStyle,
154 channels: &mut NcChannels,
155 ) -> Option<String> {
156 let egc = unsafe { c_api::notcurses_at_yx(self, y, x, stylemask.into(), &mut channels.0) };
157 if egc.is_null() {
158 return None;
159 }
160 Some(rstring_free![egc])
161 }
162
163 /// Returns the detected capabilities of the current terminal.
164 ///
165 /// *C style function: [notcurses_capabilities()][c_api::notcurses_capabilities].*
166 pub fn capabilities(&self) -> NcCapabilities {
167 unsafe { *c_api::notcurses_capabilities(self) }
168 }
169
170 /// Returns true if we can reliably use Unicode Braille.
171 ///
172 /// See also [`NcBlitter::Braille`].
173 ///
174 /// *C style function: [notcurses_canbraille()][c_api::notcurses_canbraille].*
175 pub fn canbraille(&self) -> bool {
176 c_api::notcurses_canbraille(self)
177 }
178
179 /// Returns true if it's possible to set the "hardware" palette.
180 ///
181 /// Requires the "ccc" terminfo capability.
182 ///
183 /// *C style function: [notcurses_canchangecolor()][c_api::notcurses_canchangecolor].*
184 pub fn canchangecolor(&self) -> bool {
185 c_api::notcurses_canchangecolor(self)
186 }
187
188 /// Returns true if fading is possible.
189 ///
190 /// Fading requires either the "rgb" or "ccc" terminfo capability.
191 ///
192 /// *C style function: [notcurses_canfade()][c_api::notcurses_canfade].*
193 pub fn canfade(&self) -> bool {
194 c_api::notcurses_canfade(self)
195 }
196
197 /// Returns true if we can reliably use Unicode half blocks.
198 ///
199 /// See also [`NcBlitter::Half`].
200 ///
201 /// *C style function: [notcurses_canhalfblock()][c_api::notcurses_canhalfblock].*
202 pub fn canhalfblock(&self) -> bool {
203 c_api::notcurses_canhalfblock(self)
204 }
205
206 /// Returns true if loading images is possible.
207 ///
208 /// This requires being built against FFmpeg/OIIO.
209 ///
210 /// *C style function: [notcurses_canopen_images()][c_api::notcurses_canopen_images].*
211 pub fn canopen_images(&self) -> bool {
212 unsafe { c_api::notcurses_canopen_images(self) }
213 }
214
215 /// Returns true if loading videos is possible.
216 ///
217 /// This requires being built against FFmpeg.
218 ///
219 /// *C style function: [notcurses_canopen_videos()][c_api::notcurses_canopen_videos].*
220 pub fn canopen_videos(&self) -> bool {
221 unsafe { c_api::notcurses_canopen_videos(self) }
222 }
223
224 /// Returns true if we can blit pixel-accurate bitmaps.
225 ///
226 /// See also [`check_pixel_support`][Nc#method.check_pixel_support].
227 ///
228 /// *C style function: [notcurses_canpixel()][c_api::notcurses_canpixel].*
229 pub fn canpixel(&self) -> bool {
230 c_api::notcurses_canpixel(self)
231 }
232
233 /// Returns true if we can reliably use Unicode quadrant blocks.
234 ///
235 /// See also [`NcBlitter::Quadrant`].
236 ///
237 /// *C style function: [notcurses_canquadrant()][c_api::notcurses_canquadrant].*
238 pub fn canquadrant(&self) -> bool {
239 c_api::notcurses_canquadrant(self)
240 }
241
242 /// Returns true if we can reliably use Unicode 13 sextants.
243 ///
244 /// See also [`NcBlitter::Sextant`].
245 ///
246 /// *C style function: [notcurses_cansextant()][c_api::notcurses_cansextant].*
247 pub fn cansextant(&self) -> bool {
248 c_api::notcurses_cansextant(self)
249 }
250
251 /// Returns true if it's possible to directly specify RGB values per cell,
252 /// or false if it's only possible to use palettes.
253 ///
254 /// *C style function: [notcurses_cantruecolor()][c_api::notcurses_cantruecolor].*
255 pub fn cantruecolor(&self) -> bool {
256 c_api::notcurses_cantruecolor(self)
257 }
258
259 /// Returns true if the encoding is UTF-8.
260 ///
261 /// Requires `LANG` being set to a UTF-8 locale.
262 ///
263 /// *C style function: [notcurses_canutf8()][c_api::notcurses_canutf8].*
264 pub fn canutf8(&self) -> bool {
265 c_api::notcurses_canutf8(self)
266 }
267
268 /// Checks for pixel support.
269 ///
270 /// Returns [`NcPixelImpl`] with a non-zero constant corresponding to some
271 /// pixel-blitting mechanism if bitmap support (via any mechanism) has been
272 /// detected, or else 0 (NCPIXEL_NONE).
273 ///
274 /// *C style function: [notcurses_check_pixel_support()][c_api::notcurses_check_pixel_support].*
275 #[allow(clippy::wildcard_in_or_patterns)]
276 pub fn check_pixel_support(&self) -> NcPixelImpl {
277 unsafe { c_api::notcurses_check_pixel_support(self) }.into()
278 }
279
280 /// Returns the default foreground color, if it is known.
281 pub fn default_foreground(&self) -> Option<NcRgb> {
282 let mut fg = 0;
283 let res = unsafe { c_api::notcurses_default_foreground(self, &mut fg) };
284 if res == c_api::NCRESULT_ERR {
285 None
286 } else {
287 Some(fg.into())
288 }
289 }
290
291 /// Returns the default background color, if it is known.
292 pub fn default_background(&self) -> Option<NcRgb> {
293 let mut bg = 0;
294 let res = unsafe { c_api::notcurses_default_background(self, &mut bg) };
295 if res == c_api::NCRESULT_ERR {
296 None
297 } else {
298 Some(bg.into())
299 }
300 }
301
302 /// Disables the terminal's cursor, if supported.
303 ///
304 /// Immediate effect (no need for a call to notcurses_render()).
305 ///
306 /// *C style function: [notcurses_cursor_disable()][c_api::notcurses_cursor_disable].*
307 pub fn cursor_disable(&mut self) -> NcResult<()> {
308 error![unsafe { c_api::notcurses_cursor_disable(self) }]
309 }
310
311 /// Enables the terminal's cursor, if supported, placing it at `y`, `x`.
312 ///
313 /// Immediate effect (no need for a call to notcurses_render()).
314 /// It is an error if `y`, `x` lies outside the standard plane.
315 ///
316 /// *C style function: [notcurses_cursor_enable()][c_api::notcurses_cursor_enable].*
317 pub fn cursor_enable(&mut self, y: u32, x: u32) -> NcResult<()> {
318 error![unsafe { c_api::notcurses_cursor_enable(self, y as i32, x as i32) }]
319 }
320
321 /// Shifts to the alternate screen, if available.
322 ///
323 /// If already using the alternate screen, this returns Ok(()) immediately.
324 ///
325 /// If the alternate screen is not available, returns an Error immediately.
326 ///
327 /// Entering the alternate screen turns off scrolling for the standard plane.
328 ///
329 /// *C style function:
330 /// [notcurses_enter_alternate_screen()][c_api::notcurses_enter_alternate_screen].*
331 pub fn enter_alternate_screen(&mut self) -> NcResult<()> {
332 error![unsafe { c_api::notcurses_enter_alternate_screen(self) }]
333 }
334
335 /// Exits the alternate screen.
336 ///
337 /// Immediately returns Ok(()) if not currently using the alternate screen.
338 ///
339 /// *C style function:
340 /// [notcurses_leave_alternate_screen()][c_api::notcurses_leave_alternate_screen].*
341 pub fn leave_alternate_screen(&mut self) -> NcResult<()> {
342 error![unsafe { c_api::notcurses_leave_alternate_screen(self) }]
343 }
344
345 /// Dumps notcurses state to the supplied `debugfp`.
346 ///
347 /// Output is freeform, and subject to change. It includes geometry of all
348 /// planes, from all piles.
349 ///
350 /// *C style function: [notcurses_debug()][c_api::notcurses_debug].*
351 #[cfg(feature = "std")]
352 #[cfg_attr(feature = "nightly", doc(cfg(feature = "std")))]
353 pub fn debug(&mut self, debugfp: &mut NcFile) {
354 unsafe {
355 c_api::notcurses_debug(self, debugfp.as_nc_ptr());
356 }
357 }
358
359 /// Returns the name of the user under which we are running.
360 ///
361 /// *C style function: [notcurses_accountname()][c_api::notcurses_accountname].*
362 pub fn accountname() -> String {
363 rstring_free![c_api::notcurses_accountname()]
364 }
365
366 /// Returns the name of the local hostname.
367 ///
368 /// *C style function: [notcurses_hostname()][c_api::notcurses_hostname].*
369 pub fn hostname() -> String {
370 rstring_free![c_api::notcurses_hostname()]
371 }
372
373 /// Returns the name of the detected OS version.
374 ///
375 /// *C style function: [notcurses_osversion()][c_api::notcurses_osversion].*
376 pub fn osversion() -> String {
377 rstring_free![c_api::notcurses_osversion()]
378 }
379
380 /// Returns the name of the detected terminal.
381 ///
382 /// *C style function: [notcurses_detected_terminal()][c_api::notcurses_detected_terminal].*
383 pub fn detected_terminal(&self) -> String {
384 rstring_free![c_api::notcurses_detected_terminal(self)]
385 }
386
387 /// Reads input.
388 ///
389 /// Provide `None` in `time` to block at length, and otherwise
390 /// `Some(`[`NcTime`]`)` to bound blocking.
391 ///
392 /// `time` is an a delay bound against `CLOCK_MONOTONIC`
393 /// (see [*pthread_cond_clockwait(3)*](https://linux.die.net/man/3/pthread_cond_wait)).
394 ///
395 /// *C style function: [notcurses_get()][c_api::notcurses_get].*
396 pub fn get(
397 &mut self,
398 time: Option<NcTime>,
399 input: Option<&mut NcInput>,
400 ) -> NcResult<NcReceived> {
401 let ntime = if let Some(time) = time { &time as *const _ } else { null() };
402 let ninput = if let Some(input) = input { input as *mut _ } else { null_mut() };
403
404 let res = unsafe { c_api::notcurses_get(self, ntime, ninput) };
405 if res == c_api::NCRESULT_ERR as u32 {
406 Err(NcError::new_msg(&format!["Nc.get({:?})", time]))
407 } else {
408 Ok(NcReceived::from(res))
409 }
410 }
411
412 /// Reads input blocking until an event is processed or a signal is received.
413 ///
414 /// Will optionally write the event details in `input`.
415 ///
416 /// *C style function: [notcurses_get_blocking()][c_api::notcurses_get_blocking].*
417 pub fn get_blocking(&mut self, input: Option<&mut NcInput>) -> NcResult<NcReceived> {
418 let res = c_api::notcurses_get_blocking(self, input);
419 if res == c_api::NCRESULT_ERR {
420 Err(NcError::new_msg("Nc.get_blocking()"))
421 } else {
422 Ok(NcReceived::from(res as u32))
423 }
424 }
425
426 /// Reads input without blocking.
427 ///
428 /// *C style function: [notcurses_get_nblock()][c_api::notcurses_get_nblock].*
429 pub fn get_nblock(&mut self, input: Option<&mut NcInput>) -> NcResult<NcReceived> {
430 let res = c_api::notcurses_get_nblock(self, input);
431 if res == c_api::NCRESULT_ERR {
432 Err(NcError::new_msg("Nc.get_nblock()"))
433 } else {
434 Ok(NcReceived::from(res as u32))
435 }
436 }
437
438 /// Acquire up to 'vcount' [`NcInput`]s at the vector 'ni'.
439 ///
440 /// The number read will be returned, or 0 on timeout.
441 ///
442 /// *C style function: [notcurses_getvec()][c_api::notcurses_getvec].*
443 pub fn getvec(
444 &mut self,
445 time: Option<NcTime>,
446 ni: &mut Vec<NcInput>,
447 vcount: u32,
448 ) -> NcResult<u32> {
449 let ntime = if let Some(time) = time { &time as *const _ } else { null() };
450 let nivec = ni.as_mut_ptr() as *mut NcInput;
451
452 let res = unsafe { c_api::notcurses_getvec(self, ntime, nivec, vcount as i32) };
453 error![res, "", res as u32]
454 }
455
456 /// Gets a file descriptor suitable for input event poll()ing.
457 ///
458 /// When this descriptor becomes available, you can call
459 /// [get_nblock()][Nc#method.get_nblock], and input ought be ready.
460 ///
461 /// This file descriptor is not necessarily the file descriptor associated
462 /// with stdin (but it might be!).
463 ///
464 /// *C style function: [notcurses_inputready_fd()][c_api::notcurses_inputready_fd].*
465 pub fn inputready_fd(&mut self) -> NcResult<NcFd> {
466 let res = unsafe { c_api::notcurses_inputready_fd(self) };
467 error![res, "", res]
468 }
469
470 /// Returns an [`NcBlitter`] from a string representation.
471 ///
472 /// *C style function: [notcurses_lex_blitter()][c_api::notcurses_lex_blitter].*
473 pub fn lex_blitter(blitter_str: &str) -> NcResult<NcBlitter> {
474 let mut blitter = 0;
475 let cs = cstring![blitter_str];
476 error![
477 unsafe { c_api::notcurses_lex_blitter(cs.as_ptr(), &mut blitter) },
478 "Invalid blitter name",
479 blitter.into()
480 ]
481 }
482
483 /// Lexes a margin argument according to the standard notcurses definition.
484 ///
485 /// There can be either a single number, which will define all margins equally,
486 /// or there can be four numbers separated by commas.
487 ///
488 /// *C style function: [notcurses_lex_margins()][c_api::notcurses_lex_margins].*
489 pub fn lex_margins(margins_str: &str, options: &mut NcOptions) -> NcResult<()> {
490 let cs = cstring![margins_str];
491 error![unsafe { c_api::notcurses_lex_margins(cs.as_ptr(), options) }]
492 }
493
494 /// Returns an [`NcScale`] from a string representation.
495 ///
496 /// *C style function: [notcurses_lex_scalemode()][c_api::notcurses_lex_scalemode].*
497 pub fn lex_scalemode(scale_str: &str) -> NcResult<NcScale> {
498 let mut scale = 0;
499 let cs = cstring![scale_str];
500 error![
501 unsafe { c_api::notcurses_lex_scalemode(cs.as_ptr(), &mut scale) },
502 "",
503 scale.into()
504 ]
505 }
506
507 /// Returns an [`NcStyle`] from a string representation.
508 ///
509 /// It is case-insensitive, and supports multiple styles separated by
510 /// spaces.
511 ///
512 /// The supported styles are: `italic`, `underline`, `undercurl`,
513 /// `struck`, `bold`, and `none`.
514 ///
515 /// If a style is are not recognized returns an error.
516 ///
517 /// *(No equivalent C style function)*
518 pub fn lex_styles(styles_str: &str) -> NcResult<NcStyle> {
519 let mut style = NcStyle::None;
520 let mut errstr = String::new();
521
522 for s in styles_str.split(' ') {
523 match s.to_lowercase().as_str() {
524 "italic" => style.set(NcStyle::Italic),
525 "underline" => style.set(NcStyle::Underline),
526 "undercurl" => style.set(NcStyle::Undercurl),
527 "struck" => style.set(NcStyle::Struck),
528 "bold" => style.set(NcStyle::Bold),
529 "none" => (),
530 _ => {
531 errstr.push_str(s);
532 errstr.push(' ');
533 }
534 }
535 }
536 if errstr.is_empty() {
537 Ok(style)
538 } else {
539 let _ = errstr.pop();
540 Err(NcError::new_msg(&format![
541 "the following styles are not recognized: '{}'",
542 errstr
543 ]))
544 }
545 }
546
547 /// Disables signals originating from the terminal's line discipline, i.e.
548 /// SIGINT (^C), SIGQUIT (^), and SIGTSTP (^Z). They are enabled by default.
549 ///
550 /// *C style function: [notcurses_linesigs_disable()][c_api::notcurses_linesigs_disable].*
551 pub fn linesigs_disable(&mut self) -> NcResult<()> {
552 error![unsafe { c_api::notcurses_linesigs_disable(self) }]
553 }
554
555 /// Restores signals originating from the terminal's line discipline, i.e.
556 /// SIGINT (^C), SIGQUIT (^), and SIGTSTP (^Z), if disabled.
557 ///
558 /// *C style function: [notcurses_linesigs_enable()][c_api::notcurses_linesigs_enable].*
559 pub fn linesigs_enable(&mut self) -> NcResult<()> {
560 error![unsafe { c_api::notcurses_linesigs_enable(self) }]
561 }
562
563 /// Disables mice events.
564 ///
565 /// *C style function: [notcurses_mice_disable()][c_api::notcurses_mice_disable].*
566 pub fn mice_disable(&mut self) -> NcResult<()> {
567 self.mice_enable(NcMiceEvents::None)
568 }
569
570 /// Enables mice events according to `eventmask`.
571 ///
572 /// An eventmask of 0 will disable all mice tracking.
573 ///
574 /// On success mouse events will be published to `notcurses_get`.
575 ///
576 /// *C style function: [notcurses_mice_enable()][c_api::notcurses_mice_enable].*
577 pub fn mice_enable(&mut self, eventmask: NcMiceEvents) -> NcResult<()> {
578 error![
579 unsafe { c_api::notcurses_mice_enable(self, eventmask.into()) },
580 "Nc.mice_enable()"
581 ]
582 }
583
584 /// Returns the number of simultaneous colors claimed to be supported,
585 /// if there is color support.
586 ///
587 /// Note that several terminal emulators advertise more colors than they
588 /// actually support, downsampling internally.
589 ///
590 /// *C style function: [notcurses_palette_size()][c_api::notcurses_palette_size].*
591 pub fn palette_size(&self) -> NcResult<u32> {
592 let res = unsafe { c_api::notcurses_palette_size(self) };
593 if res == 1 {
594 return Err(NcError::with_msg(1, "No color support ← Nc.palette_size()"));
595 }
596 Ok(res)
597 }
598
599 /// Refreshes the physical screen to match what was last rendered (i.e.,
600 /// without reflecting any changes since the last call to
601 /// [`render`][crate::Nc#method.render]).
602 ///
603 /// Returns the current screen geometry (`y`, `x`).
604 ///
605 /// This is primarily useful if the screen is externally corrupted, or if an
606 /// [NcKey::Resize][crate::NcKey#associatedconstant.Resize] event
607 /// has been read and you're not yet ready to render.
608 ///
609 /// *C style function: [notcurses_refresh()][c_api::notcurses_refresh].*
610 pub fn refresh(&mut self) -> NcResult<(u32, u32)> {
611 let (mut y, mut x) = (0, 0);
612 error![
613 unsafe { c_api::notcurses_refresh(self, &mut y, &mut x) },
614 "",
615 (y, x)
616 ]
617 }
618
619 /// Renders and rasterizes the standard pile in one shot. Blocking call.
620 ///
621 /// *C style function: [notcurses_render()][c_api::notcurses_render].*
622 pub fn render(&mut self) -> NcResult<()> {
623 error![c_api::notcurses_render(self), "Nc.render()"]
624 }
625
626 /// Acquires an atomic snapshot of the notcurses object's stats.
627 ///
628 /// *C style function: [notcurses_stats()][c_api::notcurses_stats].*
629 pub fn stats(&mut self, stats: &mut NcStats) {
630 unsafe {
631 c_api::notcurses_stats(self, stats);
632 }
633 }
634
635 /// Allocates an [`NcStats`] object.
636 ///
637 /// Use this rather than allocating your own, since future versions of
638 /// notcurses might enlarge this structure.
639 ///
640 /// *C style function: [notcurses_stats_alloc()][c_api::notcurses_stats_alloc].*
641 pub fn stats_alloc(&mut self) -> &mut NcStats {
642 unsafe { &mut *c_api::notcurses_stats_alloc(self) }
643 }
644
645 /// Resets all cumulative stats (immediate ones, such as fbbytes, are not reset).
646 ///
647 /// *C style function: [notcurses_stats_reset()][c_api::notcurses_stats_reset].*
648 pub fn stats_reset(&mut self, stats: &mut NcStats) {
649 unsafe {
650 c_api::notcurses_stats_reset(self, stats);
651 }
652 }
653
654 // TODO: decide what to do with these two:
655 //
656 // /// [notcurses_stdplane()][c_api::notcurses_stdplane], plus free bonus
657 // /// dimensions written to non-NULL y/x!
658 // ///
659 // /// *C style function: [notcurses_stddim_yx()][c_api::notcurses_stddim_yx].*
660 // #[inline]
661 // pub fn stddim_yx<'a>(
662 // &'a mut self,
663 // y: &mut u32,
664 // x: &mut u32,
665 // ) -> NcResult<&'a mut NcPlane> {
666 // c_api::notcurses_stddim_yx(self, y, x)
667 // }
668
669 // /// [stdplane_const()][Nc#method.stdplane_const], plus free
670 // /// bonus dimensions written to non-NULL y/x!
671 // ///
672 // /// *C style function: [notcurses_stddim_yx()][c_api::notcurses_stddim_yx].*
673 // #[inline]
674 // pub fn stddim_yx_const<'a>(
675 // &'a self,
676 // y: &mut u32,
677 // x: &mut u32,
678 // ) -> NcResult<&'a NcPlane> {
679 // c_api::notcurses_stddim_yx_const(self, y, x)
680 // }
681
682 /// Returns a mutable reference to the standard [`NcPlane`] for this terminal.
683 ///
684 /// The standard plane always exists, and its origin is always at the
685 /// uppermost, leftmost cell.
686 ///
687 /// # Safety
688 /// You must be careful not to end up with multiple exclusive references
689 /// to the standard plane, or with one exclusive reference and one or more
690 /// shared references.
691 ///
692 /// *C style function: [notcurses_stdplane()][c_api::notcurses_stdplane].*
693 pub unsafe fn stdplane<'a>(&mut self) -> &'a mut NcPlane {
694 &mut *c_api::notcurses_stdplane(self)
695 }
696
697 /// Returns a reference to the standard [`NcPlane`] for this terminal.
698 ///
699 /// The standard plane always exists, and its origin is always at the
700 /// uppermost, leftmost cell.
701 ///
702 /// # Safety
703 /// You must be careful not to end up with a mix of exclusive references
704 /// and shared references to the standard plane.
705 ///
706 /// *C style function: [notcurses_stdplane_const()][c_api::notcurses_stdplane_const].*
707 pub unsafe fn stdplane_const<'a>(&self) -> &'a NcPlane {
708 &*c_api::notcurses_stdplane_const(self)
709 }
710
711 /// Gets the name of an [`NcBlitter`] blitter.
712 ///
713 /// *C style function: [notcurses_str_blitter()][c_api::notcurses_str_blitter].*
714 pub fn str_blitter(blitter: impl Into<NcBlitter>) -> String {
715 rstring![c_api::notcurses_str_blitter(blitter.into().into())].to_string()
716 }
717
718 /// Gets the name of an [`NcScale`] scaling mode.
719 ///
720 /// *C style function: [notcurses_str_scalemode()][c_api::notcurses_str_scalemode].*
721 pub fn str_scalemode(scale: impl Into<NcScale>) -> String {
722 rstring![c_api::notcurses_str_scalemode(scale.into().into())].to_string()
723 }
724
725 /// Gets the lowercase name (or names) of the styles included in an [`NcStyle`].
726 ///
727 /// *(No equivalent C style function)*
728 pub fn str_styles(style: impl Into<NcStyle>) -> String {
729 let mut string = String::new();
730
731 let styles = style.into();
732 for s in styles.to_vec() {
733 string.push_str(match s {
734 NcStyle::Italic => "Italic ",
735 NcStyle::Underline => "Underline ",
736 NcStyle::Undercurl => "Undercurl ",
737 NcStyle::Struck => "Struck ",
738 NcStyle::Bold => "Bold ",
739 NcStyle::None => "None ",
740 _ => "",
741 });
742 }
743 let _ = string.pop();
744 string
745 }
746
747 /// Returns an [`NcStyle`] with the supported curses-style attributes.
748 ///
749 /// The attribute is only indicated as supported if the terminal can support
750 /// it together with color.
751 ///
752 /// For more information, see the "ncv" capability in terminfo(5).
753 ///
754 /// *C style function: [notcurses_supported_styles()][c_api::notcurses_supported_styles].*
755 pub fn supported_styles(&self) -> NcStyle {
756 unsafe { c_api::notcurses_supported_styles(self).into() }
757 }
758
759 /// Returns our current idea of the terminal dimensions in rows and cols.
760 ///
761 /// *C style function: [notcurses_term_dim_yx()][c_api::notcurses_term_dim_yx].*
762 pub fn term_dim_yx(&self) -> (u32, u32) {
763 c_api::notcurses_term_dim_yx(self)
764 }
765
766 /// Returns the bottommost [`NcPlane`] on the standard pile,
767 /// of which there is always at least one.
768 ///
769 /// *C style function: [notcurses_bottom()][c_api::notcurses_bottom].*
770 pub fn bottom(&mut self) -> &mut NcPlane {
771 c_api::notcurses_bottom(self)
772 }
773
774 /// Returns the topmost [`NcPlane`], of which there is always at least one.
775 ///
776 /// *C style function: [notcurses_top()][c_api::notcurses_top].*
777 pub fn top(&mut self) -> &mut NcPlane {
778 c_api::notcurses_top(self)
779 }
780
781 /// Returns a human-readable string describing the running notcurses version.
782 ///
783 /// *C style function: [notcurses_version()][c_api::notcurses_version].*
784 pub fn version() -> String {
785 rstring![c_api::notcurses_version()].to_string()
786 }
787
788 /// Returns the running notcurses version components
789 /// (major, minor, patch, tweak).
790 ///
791 /// *C style function: [notcurses_version_components()][c_api::notcurses_version_components].*
792 pub fn version_components() -> (u32, u32, u32, u32) {
793 let (mut major, mut minor, mut patch, mut tweak) = (0, 0, 0, 0);
794 unsafe {
795 c_api::notcurses_version_components(&mut major, &mut minor, &mut patch, &mut tweak);
796 }
797 (major as u32, minor as u32, patch as u32, tweak as u32)
798 }
799
800 /// Returns [`NcVisualGeometry`].
801 ///
802 /// If an [`NcVisual`] is not provided, only the [`cdim_yx`], [`blitter`],
803 /// [`scale_yx`], and [`maxpixel_yx`] fields will be filled in.
804 ///
805 /// If an [`NcVisualOptions`] is not provided, a default one will be used.
806 ///
807 /// Additionally `cdim_yx` and `maxpixel_yx` are only ever filled in if we
808 /// know them, and `maxpixel_yx` is only defined for [`NcBlitter::Pixel`].
809 ///
810 /// # See also
811 /// - [`NcVisual.geom`][NcVisual#method.geom]
812 ///
813 /// [`cdim_yx`]: NcVisualGeometry#structfield.cdim_yx
814 /// [`blitter`]: NcVisualGeometry#structfield.blitter
815 /// [`scale_yx`]: NcVisualGeometry#structfield.scale_yx
816 /// [`maxpixel_yx`]: NcVisualGeometry#structfield.maxpixel_yx
817 ///
818 /// *C style function: [ncvisual_geom()][c_api::ncvisual_geom].*
819 pub fn visual_geom(
820 &self,
821 visual: Option<&NcVisual>,
822 vopts: Option<&NcVisualOptions>,
823 ) -> NcResult<NcVisualGeometry> {
824 let mut vg = c_api::NcVGeom::new();
825
826 let v_ptr: *const NcVisual = if let Some(v) = visual { v } else { null() };
827 let vo_ptr: *const NcVisualOptions =
828 if let Some(o) = vopts { o } else { &NcVisualOptions::default() };
829
830 let res = unsafe { crate::c_api::ncvisual_geom(self, v_ptr, vo_ptr, &mut vg) };
831 if res <= c_api::NCRESULT_ERR {
832 return Err(NcError::with_msg(
833 res,
834 &format!["Nc.visual_geom({:?}, {:?})", visual, vopts],
835 ));
836 }
837
838 let (pix_yx, cdim_yx, rpix_yx, rcell_yx, scale_yx, maxpixel_yx, beg_yx, len_yx);
839
840 // if an `NcVisual` is not provided, only `maxpixel_yx`, `cdim_yx` and
841 // `scale_yx` can be non-zero.
842 if visual.is_none() {
843 cdim_yx = Some((vg.cdimy, vg.cdimx));
844 scale_yx = Some((vg.scaley, vg.scalex));
845
846 // pixel blitter only is defined for Ncblitter::PIXEL
847 if vg.blitter == NcBlitter::Pixel.into() {
848 maxpixel_yx = Some((vg.maxpixely, vg.maxpixelx));
849 } else {
850 maxpixel_yx = None;
851 }
852
853 pix_yx = None;
854 rpix_yx = None;
855 rcell_yx = None;
856 beg_yx = None;
857 len_yx = None;
858 } else {
859 // `maxpixel_yx` only is defined for `Ncblitter`::PIXEL.
860 if vg.blitter == NcBlitter::Pixel.into() {
861 maxpixel_yx = Some((vg.maxpixely, vg.maxpixelx));
862 } else {
863 maxpixel_yx = None;
864 }
865
866 // `beg_yx` & `len_yx` can be safely ignored if they're all 0.
867 if vg.begy | vg.begx | vg.leny | vg.lenx == 0 {
868 beg_yx = None;
869 len_yx = None;
870 } else {
871 beg_yx = Some((vg.begy, vg.begx));
872 len_yx = Some((vg.leny, vg.lenx));
873 }
874
875 // valid values for the following fields can't be 0 either:
876 if vg.pixy | vg.pixx == 0 {
877 pix_yx = None;
878 } else {
879 pix_yx = Some((vg.pixy, vg.pixx));
880 }
881 if vg.cdimy | vg.cdimx == 0 {
882 cdim_yx = None;
883 } else {
884 cdim_yx = Some((vg.cdimy, vg.cdimx));
885 }
886 if vg.scaley | vg.scalex == 0 {
887 scale_yx = None;
888 } else {
889 scale_yx = Some((vg.scaley, vg.scalex));
890 }
891 if vg.rpixy | vg.rpixx == 0 {
892 rpix_yx = None;
893 } else {
894 rpix_yx = Some((vg.rpixy, vg.rpixx));
895 }
896 if vg.rcelly | vg.rcellx == 0 {
897 rcell_yx = None;
898 } else {
899 rcell_yx = Some((vg.rcelly, vg.rcellx));
900 }
901 }
902
903 let vgeometry = NcVisualGeometry {
904 pix_yx,
905 cdim_yx,
906 rpix_yx,
907 rcell_yx,
908 scale_yx,
909 maxpixel_yx,
910 beg_yx,
911 len_yx,
912 blitter: (vg.blitter as crate::c_api::NcBlitter_u32).into(),
913 };
914 Ok(vgeometry)
915 }
916
917 /// Like [`visual_geom`] but auto-fills the `NcVisualOptions` with
918 /// `NcBlitter::Pixel` in order to get the maximum available resolution
919 /// for `scale_yx`, which determines the minimum dot-size for an `NcVisual`.
920 ///
921 /// [`visual_geom`]: Nc#method.visual_geom
922 pub fn visual_geom_with_pixel(&self, visual: Option<&NcVisual>) -> NcResult<NcVisualGeometry> {
923 Self::visual_geom(
924 self,
925 visual,
926 Some(&NcVisualOptions::builder().pixel().build()),
927 )
928 }
929}