libnotcurses_sys/plane/
reimplemented.rs

1//! `ncplane_*` reimplemented functions.
2
3// TOC:
4// - Alpha
5// - NcChannel_u32
6// - RGB components
7// - NcRgb_u32
8// - Default
9// - put & print
10// - movement, size & alignment
11// - line
12// - perimeter
13// - box
14// - gradient
15// - cursor
16
17use core::{ffi::c_char, ptr::null_mut};
18
19#[cfg(not(feature = "std"))]
20use alloc::{ffi::CString, string::ToString};
21
22#[cfg(feature = "std")]
23use std::ffi::CString;
24
25use crate::{
26    c_api::{
27        self, nccell_release, NcAlign_u32, NcAlpha_u32, NcBoxMask_u32, NcChannel_u32,
28        NcChannels_u64, NcResult_i32, NcRgb_u32, NcStyle_u16, NCRESULT_ERR, NCRESULT_OK,
29    },
30    cstring, NcCell, NcPlane,
31};
32
33// Alpha -----------------------------------------------------------------------
34
35/// Gets the foreground [`NcAlpha_u32`] from the [`NcPlane`], shifted to LSBs.
36///
37/// *Method: NcPlane.[fg_alpha()][NcPlane#method.fg_alpha].*
38#[inline]
39pub fn ncplane_fg_alpha(plane: &NcPlane) -> NcAlpha_u32 {
40    c_api::ncchannels_fg_alpha(ncplane_channels(plane))
41}
42
43/// Gets the background [`NcAlpha_u32`] from the [`NcPlane`], shifted to LSBs.
44///
45/// *Method: NcPlane.[bg_alpha()][NcPlane#method.bg_alpha].*
46#[inline]
47pub fn ncplane_bg_alpha(plane: &NcPlane) -> NcAlpha_u32 {
48    c_api::ncchannels_bg_alpha(ncplane_channels(plane))
49}
50
51// NcChannel_u32 ---------------------------------------------------------------
52
53/// Gets the foreground alpha and coloring bits from an [`NcPlane`] as an [`NcChannel_u32`].
54///
55/// *Method: NcPlane.[fchannel()][NcPlane#method.fchannel].*
56#[inline]
57pub fn ncplane_fchannel(plane: &NcPlane) -> NcChannel_u32 {
58    c_api::ncchannels_fchannel(ncplane_channels(plane))
59}
60
61/// Gets the background alpha and coloring bits from an [`NcPlane`] as an [`NcChannel_u32`].
62///
63/// *Method: NcPlane.[bchannel()][NcPlane#method.bchannel].*
64#[inline]
65pub fn ncplane_bchannel(plane: &NcPlane) -> NcChannel_u32 {
66    c_api::ncchannels_bchannel(ncplane_channels(plane))
67}
68
69/// Sets the background alpha and coloring bits of an [`NcPlane`] from an [`NcChannel_u32`],
70/// and returns the new [`NcChannels_u64`].
71///
72/// *Method: NcPlane.[set_fchannel()][NcPlane#method.set_fchannel].*
73#[inline]
74pub fn ncplane_set_fchannel(
75    plane: &mut NcPlane,
76    channel: impl Into<NcChannel_u32>,
77) -> NcChannels_u64 {
78    unsafe { c_api::ffi::ncplane_set_fchannel(plane, channel.into()) }
79}
80
81/// Sets the background alpha and coloring bits of an [`NcPlane`] from an [`NcChannel_u32`],
82/// and returns the new [`NcChannels_u64`].
83///
84/// *Method: NcPlane.[set_bchannel()][NcPlane#method.set_bchannel].*
85#[inline]
86pub fn ncplane_set_bchannel(
87    plane: &mut NcPlane,
88    channel: impl Into<NcChannel_u32>,
89) -> NcChannels_u64 {
90    unsafe { c_api::ffi::ncplane_set_bchannel(plane, channel.into()) }
91}
92
93/// Gets the [`NcChannels_u64`] of an [`NcPlane`].
94///
95/// *Method: NcPlane.[channels()][NcPlane#method.channels].*
96#[inline]
97pub fn ncplane_channels(plane: &NcPlane) -> NcChannels_u64 {
98    unsafe { c_api::ffi::ncplane_channels(plane) }
99}
100
101/// Sets the [`NcChannels_u64`] of an [`NcPlane`].
102///
103/// *Method: NcPlane.[set_channels()][NcPlane#method.set_channels].*
104#[inline]
105pub fn ncplane_set_channels(plane: &mut NcPlane, channels: impl Into<NcChannels_u64>) {
106    unsafe {
107        c_api::ffi::ncplane_set_channels(plane, channels.into());
108    }
109}
110
111// RGB components --------------------------------------------------------------
112
113/// Gets the foreground RGB components from an [`NcPlane`].
114/// and returns the background [`NcChannel_u32`].
115///
116/// *Method: NcPlane.[fg_rgb()][NcPlane#method.fg_rgb].*
117#[inline]
118pub fn ncplane_fg_rgb8(
119    plane: &NcPlane,
120    red: &mut u8,
121    green: &mut u8,
122    blue: &mut u8,
123) -> NcChannel_u32 {
124    c_api::ncchannels_fg_rgb8(ncplane_channels(plane), red, green, blue)
125}
126
127/// Gets the background RGB componentss from an [`NcPlane`],
128/// and returns the background [`NcChannel_u32`].
129///
130/// *Method: NcPlane.[bg_rgb()][NcPlane#method.bg_rgb].*
131#[inline]
132pub fn ncplane_bg_rgb8(
133    plane: &NcPlane,
134    red: &mut u8,
135    green: &mut u8,
136    blue: &mut u8,
137) -> NcChannel_u32 {
138    c_api::ncchannels_bg_rgb8(ncplane_channels(plane), red, green, blue)
139}
140
141// NcRgb_u32 -----------------------------------------------------------------------
142
143/// Gets the foreground [`NcRgb_u32`] from an [`NcPlane`], shifted to LSBs.
144///
145/// *Method: NcPlane.[fg_rgb()][NcPlane#method.fg_rgb].*
146#[inline]
147pub fn ncplane_fg_rgb(plane: &NcPlane) -> NcRgb_u32 {
148    c_api::ncchannels_fg_rgb(ncplane_channels(plane))
149}
150
151/// Gets the background [`NcRgb_u32`] from an [`NcPlane`], shifted to LSBs.
152///
153/// *Method: NcPlane.[bg_rgb()][NcPlane#method.bg_rgb].*
154#[inline]
155pub fn ncplane_bg_rgb(plane: &NcPlane) -> NcRgb_u32 {
156    c_api::ncchannels_bg_rgb(ncplane_channels(plane))
157}
158
159// Default ---------------------------------------------------------------------
160
161/// Is the plane's foreground using the "default foreground color"?
162///
163/// *Method: NcPlane.[fg_default_p()][NcPlane#method.fg_default_p].*
164#[inline]
165pub fn ncplane_fg_default_p(plane: &NcPlane) -> bool {
166    c_api::ncchannels_fg_default_p(ncplane_channels(plane))
167}
168
169/// Is the plane's background using the "default background color"?
170///
171/// *Method: NcPlane.[bg_default_p()][NcPlane#method.bg_default_p].*
172#[inline]
173pub fn ncplane_bg_default_p(plane: &NcPlane) -> bool {
174    c_api::ncchannels_bg_default_p(ncplane_channels(plane))
175}
176
177/// Marks both the foreground and background as using the "default color",
178/// and returns the new [`NcChannels_u64`].
179///
180/// *Method: NcPlane.[set_default()][NcPlane#method.set_default].*
181//
182// Not in the C API.
183#[inline]
184pub fn ncplane_set_default(plane: &mut NcPlane) -> NcChannels_u64 {
185    let channels = c_api::ncchannels_set_default(&mut ncplane_channels(plane));
186    ncplane_set_channels(plane, channels);
187    channels
188}
189
190/// Marks both the foreground and background as NOT using the "default color",
191/// and returns the new [`NcChannels_u64`].
192///
193/// *Method: NcPlane.[set_not_default()][NcPlane#method.set_not_default].*
194//
195// Not in the C API.
196#[inline]
197pub fn ncplane_set_not_default(plane: &mut NcPlane) -> NcChannels_u64 {
198    let channels = c_api::ncchannels_set_not_default(&mut ncplane_channels(plane));
199    c_api::ncplane_set_channels(plane, channels);
200    channels
201}
202
203/// Marks the foreground as NOT using the "default color",
204/// and returns the new [`NcChannels_u64`].
205///
206/// *Method: NcPlane.[set_fg_not_default()][NcPlane#method.set_fg_not_default].*
207//
208// Not in the C API.
209#[inline]
210pub fn ncplane_set_fg_not_default(plane: &NcPlane) -> NcChannels_u64 {
211    c_api::ncchannels_set_fg_not_default(&mut ncplane_channels(plane))
212}
213
214/// Marks the background as NOT using the "default color",
215/// and returns the new [`NcChannels_u64`].
216///
217/// *Method: NcPlane.[set_bg_not_default()][NcPlane#method.set_bg_not_default].*
218//
219// Not in the C API.
220#[inline]
221pub fn ncplane_set_bg_not_default(plane: &NcPlane) -> NcChannels_u64 {
222    c_api::ncchannels_set_bg_not_default(&mut ncplane_channels(plane))
223}
224
225// put & print -----------------------------------------------------------------
226
227/// Replaces the [`NcCell`] at the current location with the provided `cell`,
228/// advancing the cursor by its width (but not past the end of the plane).
229///
230/// The new `cell` must already be associated with this `NcPlane`.
231///
232/// On success, returns the number of columns the cursor was advanced.
233///
234/// If the glyph can not fit in the current line, it is an error, unless
235/// scrolling is enabled.
236///
237/// *Method: NcPlane.[putc()][NcPlane#method.putc].*
238#[inline]
239pub fn ncplane_putc(plane: &mut NcPlane, cell: &NcCell) -> NcResult_i32 {
240    unsafe { c_api::ncplane_putc_yx(plane, -1, -1, cell) }
241}
242
243/// Replaces the [`NcCell`] at the current location with the provided [`char`],
244/// using the current style.
245///
246/// On success, returns the number of columns the cursor was advanced.
247///
248/// If the glyph can not fit in the current line, it is an error, unless
249/// scrolling is enabled.
250///
251/// *Method: NcPlane.[putchar()][NcPlane#method.putchar].*
252#[inline]
253pub fn ncplane_putchar(plane: &mut NcPlane, ch: char) -> NcResult_i32 {
254    unsafe {
255        let cell = NcCell::from_char(plane, ch);
256        if cell.is_err() {
257            return NCRESULT_ERR;
258        }
259        c_api::ncplane_putc_yx(plane, -1, -1, &cell.unwrap())
260    }
261}
262
263/// Replaces the [`NcCell`] at the specified coordinates with the provided
264/// [`char`], using the current style.
265///
266/// On success, returns the number of columns the cursor was advanced.
267///
268/// If the glyph can not fit in the current line, it is an error, unless
269/// scrolling is enabled.
270///
271/// *Method: NcPlane.[putchar_yx()][NcPlane#method.putchar_yx].*
272#[inline]
273pub fn ncplane_putchar_yx(plane: &mut NcPlane, y: u32, x: u32, ch: char) -> NcResult_i32 {
274    unsafe {
275        let cell = NcCell::from_char(plane, ch);
276        if cell.is_err() {
277            return NCRESULT_ERR;
278        }
279        c_api::ncplane_putc_yx(plane, y as i32, x as i32, &cell.unwrap())
280    }
281}
282
283/// Replaces the [`NcCell`] at the current location with the provided
284/// [`char`], while retaining the previous style.
285///
286/// On success, returns the number of columns the cursor was advanced.
287///
288/// If the glyph can not fit in the current line, it is an error, unless
289/// scrolling is enabled.
290///
291/// *Method: NcPlane.[putchar_stained()][NcPlane#method.putchar_stained].*
292#[inline]
293pub fn ncplane_putchar_stained(plane: &mut NcPlane, ch: char) -> NcResult_i32 {
294    ncplane_putstr_stained(plane, &ch.to_string())
295}
296
297/// Replaces the [`NcCell`] at the current location with the provided `egc`,
298/// using the current style.
299///
300/// Advances the cursor by the width of the cluster (but not past the end of
301/// the the plane), and this number is returned on success.
302///
303/// The number of bytes converted from the `egc` can be optionally written to
304/// `sbytes`.
305///
306/// If the glyph can not fit in the current line, it is an error, unless
307/// scrolling is enabled.
308///
309/// *Method: NcPlane.[putegc()][NcPlane#method.putegc].*
310#[inline]
311pub fn ncplane_putegc(plane: &mut NcPlane, egc: &str, sbytes: Option<&mut usize>) -> NcResult_i32 {
312    let sbytes_ptr = if let Some(sb) = sbytes { sb as *mut _ } else { null_mut() };
313    let cs = cstring![egc];
314    let egc_ptr = cs.as_ptr() as *const c_char; // CHECK why is this needed only here
315
316    unsafe { c_api::ffi::ncplane_putegc_yx(plane, -1, -1, egc_ptr, sbytes_ptr) }
317}
318
319/// Replaces the [`NcCell`] at the specified coordinates with the provided `egc`,
320/// using the current style.
321///
322/// Advances the cursor by the width of the cluster (but not past the end of
323/// the the plane), and this number is returned on success.
324///
325/// The number of bytes converted from the `egc` can be optionally written to
326/// `sbytes`.
327///
328/// If the glyph can not fit in the current line, it is an error, unless
329/// scrolling is enabled.
330///
331/// *Method: NcPlane.[putegc_yx()][NcPlane#method.putegc_yx].*
332#[inline]
333pub fn ncplane_putegc_yx(
334    plane: &mut NcPlane,
335    y: Option<u32>,
336    x: Option<u32>,
337    egc: &str,
338    sbytes: Option<&mut usize>,
339) -> NcResult_i32 {
340    let sbytes_ptr = if let Some(sb) = sbytes { sb as *mut _ } else { null_mut() };
341    let cs = cstring![egc];
342
343    unsafe {
344        c_api::ffi::ncplane_putegc_yx(
345            plane,
346            y.unwrap_or(u32::MAX) as i32,
347            x.unwrap_or(u32::MAX) as i32,
348            cs.as_ptr(),
349            sbytes_ptr,
350        )
351    }
352}
353
354/// Replaces the [`NcCell`] at the current location with the provided `egc`,
355/// while retaining the previous style.
356///
357/// Advances the cursor by the width of the cluster (but not past the end of
358/// the the plane), and this number is returned on success.
359///
360/// The number of bytes converted from the `egc` can be optionally written to
361/// `sbytes`.
362///
363/// If the glyph can not fit in the current line, it is an error, unless
364/// scrolling is enabled.
365///
366/// *Method: NcPlane.[putegc_stained()][NcPlane#method.putegc_stained].*
367#[inline]
368pub fn ncplane_putegc_stained(
369    plane: &mut NcPlane,
370    egc: &str,
371    sbytes: Option<&mut usize>,
372) -> NcResult_i32 {
373    let sbytes_ptr = if let Some(sb) = sbytes { sb as *mut _ } else { null_mut() };
374
375    let cs = cstring![egc];
376    unsafe { c_api::ffi::ncplane_putegc_stained(plane, cs.as_ptr(), sbytes_ptr) }
377}
378
379/// Writes a string to the current location, using the current style.
380///
381/// Advances the cursor by some positive number of columns (though not beyond
382/// the end of the plane), and this number is returned on success.
383///
384/// On error, a non-positive number is returned, indicating the number of
385/// columns which were written before the error.
386///
387/// If a glyph can not fit in the current line, it is an error, unless
388/// scrolling is enabled.
389///
390/// *Method: NcPlane.[putstr()][NcPlane#method.putstr].*
391#[inline]
392pub fn ncplane_putstr(plane: &mut NcPlane, string: &str) -> NcResult_i32 {
393    ncplane_putstr_yx(plane, None, None, string)
394}
395
396/// Writes a string to the provided location, using the current style.
397///
398/// Advances the cursor by some positive number of columns (though not beyond
399/// the end of the plane), and this number is returned on success.
400///
401/// On error, a non-positive number is returned, indicating the number of
402/// columns which were written before the error.
403///
404/// If a glyph can not fit in the current line, it is an error, unless
405/// scrolling is enabled.
406///
407/// *Method: NcPlane.[putstr_yx()][NcPlane#method.putstr_yx].*
408#[inline]
409pub fn ncplane_putstr_yx(
410    plane: &mut NcPlane,
411    y: Option<u32>,
412    x: Option<u32>,
413    string: &str,
414) -> NcResult_i32 {
415    let cs = cstring![string];
416    let mut cs_ptr = cs.as_ptr();
417
418    let (mut y, mut x) = (y, x);
419    let mut ret = 0;
420
421    while unsafe { cs_ptr.read() != 0 } {
422        let mut wcs = 0;
423        let cols = unsafe {
424            c_api::ffi::ncplane_putegc_yx(
425                plane,
426                y.unwrap_or(u32::MAX) as i32,
427                x.unwrap_or(u32::MAX) as i32,
428                cs_ptr,
429                &mut wcs,
430            )
431        };
432        if cols < 0 {
433            return -ret;
434        }
435        if wcs == 0 {
436            break;
437        }
438        // after the first iteration, just let the cursor code control where we
439        // print, so that scrolling is taken into account
440        y = None;
441        x = None;
442
443        cs_ptr = unsafe { cs_ptr.add(wcs) };
444        ret += cols;
445    }
446    ret
447}
448
449/// Writes a string to the provided location, using the current style
450/// and [`NcAlign_u32`]ed on *x*.
451///
452/// Advances the cursor by some positive number of columns (though not
453/// beyond the end of the plane); this number is returned on success.
454///
455/// On error, a non-positive number is returned, indicating the number of
456/// columns which were written before the error.
457///
458/// If a glyph can not fit in the current line, it is an error, unless
459/// scrolling is enabled.
460///
461/// *Method: NcPlane.[putstr_aligned()][NcPlane#method.putstr_aligned].*
462#[inline]
463pub fn ncplane_putstr_aligned(
464    plane: &mut NcPlane,
465    y: Option<u32>,
466    align: impl Into<NcAlign_u32>,
467    string: &str,
468) -> NcResult_i32 {
469    let (mut validbytes, mut validwidth) = (0, 0);
470    let cs = cstring![string];
471
472    // we'll want to do the partial write if there's an error somewhere within
473    unsafe {
474        c_api::ncstrwidth(cs.as_ptr(), &mut validbytes, &mut validwidth);
475    }
476
477    let xpos = ncplane_halign(plane, align.into(), validwidth as u32);
478    if xpos < 0 {
479        NCRESULT_ERR
480    } else {
481        ncplane_putstr_yx(plane, y, Some(xpos as u32), string)
482    }
483}
484
485/// Writes a string to the current location, retaining the previous style.
486///
487/// Advances the cursor by some positive number of columns (though not
488/// beyond the end of the plane); this number is returned on success.
489///
490/// On error, a non-positive number is returned, indicating the number of
491/// columns which were written before the error.
492///
493/// If a glyph can not fit in the current line, it is an error, unless
494/// scrolling is enabled.
495///
496/// *Method: NcPlane.[putstr_stained()][NcPlane#method.putstr_stained].*
497#[inline]
498pub fn ncplane_putstr_stained(plane: &mut NcPlane, string: &str) -> NcResult_i32 {
499    let cstring = CString::new(string).unwrap();
500    let mut cstring_ptr = cstring.as_ptr();
501
502    let mut ret = 0;
503    while unsafe { cstring_ptr.read() != 0 } {
504        let mut wcs = 0;
505        let cols = unsafe { c_api::ffi::ncplane_putegc_stained(plane, cstring_ptr, &mut wcs) };
506
507        if cols < 0 {
508            return -ret;
509        }
510        if wcs == 0 {
511            break;
512        }
513        cstring_ptr = unsafe { cstring_ptr.add(wcs) };
514        ret += cols;
515    }
516    ret
517}
518
519/// Writes a series of EGCs to the provided location, using the current style,
520/// and no more than `num_bytes` bytes will be written.
521///
522/// Advances the cursor by some positive number of columns (though not beyond
523/// the end of the plane); this number is returned on success.
524///
525/// On error, a non-positive number is returned, indicating the number of
526/// columns which were written before the error.
527///
528/// If a glyph can not fit in the current line, it is an error, unless
529/// scrolling is enabled.
530///
531/// *Method: NcPlane.[putnstr()][NcPlane#method.putnstr].*
532#[inline]
533pub fn ncplane_putnstr_yx(
534    plane: &mut NcPlane,
535    y: Option<u32>,
536    x: Option<u32>,
537    num_bytes: usize,
538    string: &str,
539) -> NcResult_i32 {
540    let cstring = CString::new(string).unwrap();
541    let cstring_bytes_len = cstring.as_bytes().len();
542    let cstring_ptr = cstring.as_ptr();
543
544    let (ret, mut offset) = (0, 0);
545    let (mut y, mut x) = (y, x);
546
547    while offset < num_bytes && offset < cstring_bytes_len {
548        let mut wcs = 0;
549        let cols = unsafe {
550            c_api::ffi::ncplane_putegc_yx(
551                plane,
552                y.unwrap_or(u32::MAX) as i32,
553                x.unwrap_or(u32::MAX) as i32,
554                cstring_ptr.add(offset),
555                &mut wcs,
556            )
557        };
558        if cols < 0 {
559            return c_api::NCRESULT_ERR;
560        }
561        if wcs == 0 {
562            break;
563        }
564
565        // after the first iteration, just let the cursor code control where we
566        // print, so that scrolling is taken into account
567        y = None;
568        x = None;
569        offset += wcs;
570    }
571    ret
572}
573
574/// Writes a string to the current location, using the current style,
575/// and no more than `num_bytes` bytes will be written.
576///
577/// Advances the cursor by some positive number of columns (though not beyond
578/// the end of the plane); this number is returned on success.
579///
580/// On error, a non-positive number is returned, indicating the number of
581/// columns which were written before the error.
582///
583/// If a glyph can not fit in the current line, it is an error, unless
584/// scrolling is enabled.
585///
586/// *Method: NcPlane.[putnstr()][NcPlane#method.putnstr].*
587#[inline]
588pub fn ncplane_putnstr(plane: &mut NcPlane, num_bytes: usize, string: &str) -> NcResult_i32 {
589    c_api::ncplane_putnstr_yx(plane, None, None, num_bytes, string)
590}
591
592// movement, size & alignment --------------------------------------------------
593
594/// Moves this `NcPlane` relative to its current location.
595///
596/// Negative values move up and left, respectively.
597/// Pass 0 to hold an axis constant.
598///
599/// It is an error to attempt to move the standard plane.
600///
601/// *Method: NcPlane.[move_rel()][NcPlane#method.move_rel].*
602#[inline]
603pub fn ncplane_moverel(plane: &mut NcPlane, rows: i32, cols: i32) -> NcResult_i32 {
604    let (mut orig_y, mut orig_x) = (0, 0);
605    unsafe {
606        c_api::ncplane_yx(plane, &mut orig_y, &mut orig_x);
607        c_api::ncplane_move_yx(plane, orig_y + rows, orig_x + cols)
608    }
609}
610
611/// Relocates this `NcPlane` at the bottom of the z-buffer.
612///
613/// *Method: NcPlane.[move_bottom()][NcPlane#method.move_bottom].*
614#[inline]
615pub fn ncplane_move_bottom(plane: &mut NcPlane) {
616    unsafe {
617        c_api::ncplane_move_above(plane, null_mut());
618    }
619}
620
621/// Relocates this `NcPlane` at the top of the z-buffer.
622///
623/// *Method: NcPlane.[move_top()][NcPlane#method.move_top].*
624#[inline]
625pub fn ncplane_move_top(plane: &mut NcPlane) {
626    unsafe {
627        c_api::ncplane_move_below(plane, null_mut());
628    }
629}
630
631/// Splices this plane and its bound planes out of the z-buffer,
632/// and reinserts them at the top.
633///
634/// Relative order will be maintained between the reinserted planes.
635///
636/// For a plane E bound to C, with z-ordering A B C D E, moving the C family
637/// to the top results in C E A B D.
638///
639/// *Method: NcPlane.[move_family_top()][NcPlane#method.move_family_top].*
640#[inline]
641pub fn ncplane_move_family_top(plane: &mut NcPlane) {
642    unsafe {
643        c_api::ncplane_move_family_below(plane, null_mut());
644    }
645}
646
647/// Splices this plane and its bound planes out of the z-buffer,
648/// and reinserts them at the bottom.
649///
650/// Relative order will be maintained between the reinserted planes.
651///
652/// For a plane E bound to C, with z-ordering A B C D E, moving the C family
653/// to the bottom results in A B D C E.
654///
655/// *Method: NcPlane.[move_family_bottom()][NcPlane#method.move_family_bottom].*
656#[inline]
657pub fn ncplane_move_family_bottom(plane: &mut NcPlane) {
658    unsafe {
659        c_api::ncplane_move_family_above(plane, null_mut());
660    }
661}
662
663/// Gets the columns of the [`NcPlane`].
664///
665/// *Method: NcPlane.[dim_x()][NcPlane#method.dim_x].*
666#[inline]
667pub fn ncplane_dim_x(plane: &NcPlane) -> u32 {
668    unsafe {
669        let mut x = 0;
670        c_api::ncplane_dim_yx(plane, null_mut(), &mut x);
671        x
672    }
673}
674
675/// Gets the rows of the [`NcPlane`].
676///
677/// *Method: NcPlane.[dim_y()][NcPlane#method.dim_y].*
678#[inline]
679pub fn ncplane_dim_y(plane: &NcPlane) -> u32 {
680    unsafe {
681        let mut y = 0;
682        c_api::ncplane_dim_yx(plane, &mut y, null_mut());
683        y
684    }
685}
686
687/// Resizes the plane, retaining what data we can (everything, unless we're
688/// shrinking in some dimension). Keep the origin where it is.
689///
690/// *Method: NcPlane.[resize_simple()][NcPlane#method.resize_simple].*
691#[inline]
692pub fn ncplane_resize_simple(plane: &mut NcPlane, len_y: u32, len_x: u32) -> NcResult_i32 {
693    let (mut old_y, mut old_x) = (0, 0);
694    unsafe {
695        c_api::ncplane_dim_yx(plane, &mut old_y, &mut old_x);
696    }
697    let keep_len_y = {
698        if old_y > len_y {
699            len_y
700        } else {
701            old_y
702        }
703    };
704    let keep_len_x = {
705        if old_x > len_x {
706            len_x
707        } else {
708            old_x
709        }
710    };
711    unsafe { c_api::ncplane_resize(plane, 0, 0, keep_len_y, keep_len_x, 0, 0, len_y, len_x) }
712}
713
714/// Returns the column at which `numcols` columns ought start in order to be
715/// aligned according to `align` within the `plane`.
716///
717/// Returns `-`[`NCRESULT_MAX`][c_api::NCRESULT_MAX] if
718/// [NCALIGN_UNALIGNED][c_api::NCALIGN_UNALIGNED].
719///
720/// *Method: NcPlane.[halign()][NcPlane#method.halign].*
721#[inline]
722pub fn ncplane_halign(
723    plane: &NcPlane,
724    align: impl Into<NcAlign_u32>,
725    numcols: u32,
726) -> NcResult_i32 {
727    c_api::notcurses_align(ncplane_dim_x(plane), align, numcols)
728}
729
730/// Returns the row at which `numrows` rows ought start in order to be aligned
731/// according to `align` within this NcPlane.
732///
733/// Returns `-`[`NCRESULT_MAX`][c_api::NCRESULT_MAX] if
734/// [NCALIGN_UNALIGNED][c_api::NCALIGN_UNALIGNED].
735///
736/// *Method: NcPlane.[valign()][NcPlane#method.valign].*
737#[inline]
738pub fn ncplane_valign(
739    plane: &NcPlane,
740    align: impl Into<NcAlign_u32>,
741    numrows: u32,
742) -> NcResult_i32 {
743    c_api::notcurses_align(ncplane_dim_y(plane), align, numrows)
744}
745
746// line ------------------------------------------------------------------------
747
748/// Draws horizontal lines using the specified NcCell, starting at the current
749/// cursor position.
750///
751/// The cursor will end at the cell following the last cell output,
752/// just as if [`ncplane_putc`] was called at that spot.
753///
754/// Returns the number of cells drawn on success. On error, returns the negative
755/// number of cells drawn.
756///
757/// *Method: NcPlane.[hline()][NcPlane#method.hline].*
758#[inline]
759pub fn ncplane_hline(plane: &mut NcPlane, cell: &NcCell, len: u32) -> NcResult_i32 {
760    unsafe { c_api::ncplane_hline_interp(plane, cell, len, cell.channels, cell.channels) }
761}
762
763/// Draws vertical lines using the specified `cell`, starting at the current
764/// cursor position.
765///
766/// The cursor will end at the cell following the last cell output,
767/// just as if [`ncplane_putc`] was called at that spot.
768///
769/// Returns the number of cells drawn on success. On error, returns the negative
770/// number of cells drawn.
771///
772/// *Method: NcPlane.[vline()][NcPlane#method.vline].*
773#[inline]
774pub fn ncplane_vline(plane: &mut NcPlane, cell: &NcCell, len: u32) -> NcResult_i32 {
775    unsafe { c_api::ncplane_vline_interp(plane, cell, len, cell.channels, cell.channels) }
776}
777
778// perimeter -------------------------------------------------------------------
779
780/// Draws the perimeter around `plane`.
781///
782/// *Method: NcPlane.[perimeter()][NcPlane#method.perimeter].*
783#[inline]
784pub fn ncplane_perimeter(
785    plane: &mut NcPlane,
786    ul: &NcCell,
787    ur: &NcCell,
788    ll: &NcCell,
789    lr: &NcCell,
790    hline: &NcCell,
791    vline: &NcCell,
792    boxmask: impl Into<NcBoxMask_u32>,
793) -> NcResult_i32 {
794    unsafe {
795        c_api::ncplane_cursor_move_yx(plane, 0, 0);
796        let (mut dimy, mut dimx) = (0, 0);
797        c_api::ncplane_dim_yx(plane, &mut dimy, &mut dimx);
798        ncplane_box_sized(
799            plane,
800            ul,
801            ur,
802            ll,
803            lr,
804            hline,
805            vline,
806            dimy,
807            dimx,
808            boxmask.into(),
809        )
810    }
811}
812
813/// Like [`ncplane_perimeter`] with the double box-drawing characters.
814///
815/// *Method: NcPlane.[perimeter_double()][NcPlane#method.perimeter_double].*
816#[inline]
817pub fn ncplane_perimeter_double(
818    plane: &mut NcPlane,
819    stylemask: impl Into<NcStyle_u16>,
820    channels: impl Into<NcChannels_u64>,
821    boxmask: impl Into<NcBoxMask_u32>,
822) -> NcResult_i32 {
823    if unsafe { c_api::ncplane_cursor_move_yx(plane, 0, 0) } != NCRESULT_OK {
824        return NCRESULT_ERR;
825    }
826    let (mut dimy, mut dimx) = (0, 0);
827    unsafe {
828        c_api::ncplane_dim_yx(plane, &mut dimy, &mut dimx);
829    }
830    let mut ul = NcCell::new();
831    let mut ur = NcCell::new();
832    let mut ll = NcCell::new();
833    let mut lr = NcCell::new();
834    let mut hl = NcCell::new();
835    let mut vl = NcCell::new();
836    if c_api::nccells_double_box(
837        plane, stylemask, channels, &mut ul, &mut ur, &mut ll, &mut lr, &mut hl, &mut vl,
838    ) != NCRESULT_OK
839    {
840        return NCRESULT_ERR;
841    }
842    let ret = ncplane_box_sized(plane, &ul, &ur, &ll, &lr, &hl, &vl, dimy, dimx, boxmask);
843    unsafe {
844        nccell_release(plane, &mut ul);
845        nccell_release(plane, &mut ur);
846        nccell_release(plane, &mut ll);
847        nccell_release(plane, &mut lr);
848        nccell_release(plane, &mut hl);
849        nccell_release(plane, &mut vl);
850    }
851    ret
852}
853
854/// Like [`ncplane_perimeter`] with the rounded box-drawing characters.
855///
856/// *Method: NcPlane.[perimeter_rounded()][NcPlane#method.perimeter_rounded].*
857#[inline]
858pub fn ncplane_perimeter_rounded(
859    plane: &mut NcPlane,
860    stylemask: impl Into<NcStyle_u16>,
861    channels: impl Into<NcChannels_u64>,
862    boxmask: impl Into<NcBoxMask_u32>,
863) -> NcResult_i32 {
864    if unsafe { c_api::ncplane_cursor_move_yx(plane, 0, 0) } != NCRESULT_OK {
865        return NCRESULT_ERR;
866    }
867    let (mut dimy, mut dimx) = (0, 0);
868    unsafe {
869        c_api::ncplane_dim_yx(plane, &mut dimy, &mut dimx);
870    }
871    let mut ul = NcCell::new();
872    let mut ur = NcCell::new();
873    let mut ll = NcCell::new();
874    let mut lr = NcCell::new();
875    let mut hl = NcCell::new();
876    let mut vl = NcCell::new();
877    if c_api::nccells_rounded_box(
878        plane, stylemask, channels, &mut ul, &mut ur, &mut ll, &mut lr, &mut hl, &mut vl,
879    ) != NCRESULT_OK
880    {
881        return NCRESULT_ERR;
882    }
883    let ret = ncplane_box_sized(plane, &ul, &ur, &ll, &lr, &hl, &vl, dimy, dimx, boxmask);
884    unsafe {
885        nccell_release(plane, &mut ul);
886        nccell_release(plane, &mut ur);
887        nccell_release(plane, &mut ll);
888        nccell_release(plane, &mut lr);
889        nccell_release(plane, &mut hl);
890        nccell_release(plane, &mut vl);
891    }
892    ret
893}
894
895// box -------------------------------------------------------------------------
896
897/// Like [`ncplane_box`][c_api::ncplane_box] using only ASCII characters.
898///
899/// *Method: NcPlane.[ascii_box()][NcPlane#method.ascii_box].*
900#[inline]
901pub fn ncplane_ascii_box(
902    plane: &mut NcPlane,
903    stylemask: impl Into<NcStyle_u16>,
904    channels: impl Into<NcChannels_u64>,
905    end_y: u32,
906    end_x: u32,
907    boxmask: impl Into<NcBoxMask_u32>,
908) -> NcResult_i32 {
909    #[allow(unused_assignments)]
910    let mut ret = NCRESULT_OK;
911
912    let mut ul = NcCell::new();
913    let mut ur = NcCell::new();
914    let mut ll = NcCell::new();
915    let mut lr = NcCell::new();
916    let mut hl = NcCell::new();
917    let mut vl = NcCell::new();
918
919    unsafe {
920        ret = c_api::nccells_ascii_box(
921            plane, stylemask, channels, &mut ul, &mut ur, &mut ll, &mut lr, &mut hl, &mut vl,
922        );
923        if ret == NCRESULT_OK {
924            ret = c_api::ncplane_box(
925                plane,
926                &ul,
927                &ur,
928                &ll,
929                &lr,
930                &hl,
931                &vl,
932                end_y,
933                end_x,
934                boxmask.into(),
935            );
936        }
937
938        nccell_release(plane, &mut ul);
939        nccell_release(plane, &mut ur);
940        nccell_release(plane, &mut ll);
941        nccell_release(plane, &mut lr);
942        nccell_release(plane, &mut hl);
943        nccell_release(plane, &mut vl);
944    }
945    ret
946}
947
948/// Draws a box with its upper-left corner at the current cursor position,
949/// having dimensions `len_y` * `len_x`.
950///
951/// The minimum box size is 2x2, and it cannot be drawn off-screen.
952///
953/// See [ncplane_box()](c_api::ncplane_box) for more information.
954///
955/// *Method: NcPlane.[box_sized()][NcPlane#method.box_sized].*
956#[inline]
957pub fn ncplane_box_sized(
958    plane: &mut NcPlane,
959    ul: &NcCell,
960    ur: &NcCell,
961    ll: &NcCell,
962    lr: &NcCell,
963    hline: &NcCell,
964    vline: &NcCell,
965    len_y: u32,
966    len_x: u32,
967    boxmask: impl Into<NcBoxMask_u32>,
968) -> NcResult_i32 {
969    let (mut y, mut x) = (0, 0);
970    unsafe {
971        c_api::ncplane_cursor_yx(plane, &mut y, &mut x);
972        c_api::ncplane_box(
973            plane,
974            ul,
975            ur,
976            ll,
977            lr,
978            hline,
979            vline,
980            y + len_y - 1,
981            x + len_x - 1,
982            boxmask.into(),
983        )
984    }
985}
986
987/// Like [`ncplane_box`][c_api::ncplane_box] with the double box-drawing
988/// characters.
989///
990/// *Method: NcPlane.[double_box()][NcPlane#method.double_box].*
991#[inline]
992pub fn ncplane_double_box(
993    plane: &mut NcPlane,
994    stylemask: impl Into<NcStyle_u16>,
995    channels: impl Into<NcChannels_u64>,
996    end_y: u32,
997    end_x: u32,
998    boxmask: impl Into<NcBoxMask_u32>,
999) -> NcResult_i32 {
1000    #[allow(unused_assignments)]
1001    let mut ret = NCRESULT_OK;
1002
1003    let mut ul = NcCell::new();
1004    let mut ur = NcCell::new();
1005    let mut ll = NcCell::new();
1006    let mut lr = NcCell::new();
1007    let mut hl = NcCell::new();
1008    let mut vl = NcCell::new();
1009
1010    unsafe {
1011        ret = c_api::nccells_double_box(
1012            plane, stylemask, channels, &mut ul, &mut ur, &mut ll, &mut lr, &mut hl, &mut vl,
1013        );
1014        if ret == NCRESULT_OK {
1015            ret = c_api::ncplane_box(
1016                plane,
1017                &ul,
1018                &ur,
1019                &ll,
1020                &lr,
1021                &hl,
1022                &vl,
1023                end_y,
1024                end_x,
1025                boxmask.into(),
1026            );
1027        }
1028
1029        nccell_release(plane, &mut ul);
1030        nccell_release(plane, &mut ur);
1031        nccell_release(plane, &mut ll);
1032        nccell_release(plane, &mut lr);
1033        nccell_release(plane, &mut hl);
1034        nccell_release(plane, &mut vl);
1035    }
1036    ret
1037}
1038
1039/// Like [`ncplane_box_sized`] with the double box-drawing characters.
1040///
1041/// *Method: NcPlane.[double_box_sized()][NcPlane#method.double_box_sized].*
1042#[inline]
1043pub fn ncplane_double_box_sized(
1044    plane: &mut NcPlane,
1045    stylemask: impl Into<NcStyle_u16>,
1046    channels: impl Into<NcChannels_u64>,
1047    len_y: u32,
1048    len_x: u32,
1049    boxmask: impl Into<NcBoxMask_u32>,
1050) -> NcResult_i32 {
1051    let (mut y, mut x) = (0, 0);
1052    unsafe {
1053        c_api::ncplane_cursor_yx(plane, &mut y, &mut x);
1054    }
1055    c_api::ncplane_double_box(
1056        plane,
1057        stylemask,
1058        channels,
1059        y + len_y - 1,
1060        x + len_x - 1,
1061        boxmask,
1062    )
1063}
1064
1065/// Like [`ncplane_box`][c_api::ncplane_box] with the rounded box-drawing
1066/// characters.
1067///
1068/// *Method: NcPlane.[rounded_box()][NcPlane#method.rounded_box].*
1069#[inline]
1070pub fn ncplane_rounded_box(
1071    plane: &mut NcPlane,
1072    stylemask: impl Into<NcStyle_u16>,
1073    channels: impl Into<NcChannels_u64>,
1074    end_y: u32,
1075    end_x: u32,
1076    boxmask: impl Into<NcBoxMask_u32>,
1077) -> NcResult_i32 {
1078    #[allow(unused_assignments)]
1079    let mut ret = NCRESULT_OK;
1080
1081    let mut ul = NcCell::new();
1082    let mut ur = NcCell::new();
1083    let mut ll = NcCell::new();
1084    let mut lr = NcCell::new();
1085    let mut hl = NcCell::new();
1086    let mut vl = NcCell::new();
1087
1088    unsafe {
1089        ret = c_api::nccells_rounded_box(
1090            plane, stylemask, channels, &mut ul, &mut ur, &mut ll, &mut lr, &mut hl, &mut vl,
1091        );
1092        if ret == NCRESULT_OK {
1093            ret = c_api::ncplane_box(
1094                plane,
1095                &ul,
1096                &ur,
1097                &ll,
1098                &lr,
1099                &hl,
1100                &vl,
1101                end_y,
1102                end_x,
1103                boxmask.into(),
1104            );
1105        }
1106        nccell_release(plane, &mut ul);
1107        nccell_release(plane, &mut ur);
1108        nccell_release(plane, &mut ll);
1109        nccell_release(plane, &mut lr);
1110        nccell_release(plane, &mut hl);
1111        nccell_release(plane, &mut vl);
1112    }
1113    ret
1114}
1115
1116/// Like [`ncplane_box_sized`] with the rounded box-drawing characters.
1117///
1118/// *Method: NcPlane.[rounded_box_sized()][NcPlane#method.rounded_box_sized].*
1119#[inline]
1120pub fn ncplane_rounded_box_sized(
1121    plane: &mut NcPlane,
1122    stylemask: impl Into<NcStyle_u16>,
1123    channels: impl Into<NcChannels_u64>,
1124    len_y: u32,
1125    len_x: u32,
1126    boxmask: impl Into<NcBoxMask_u32>,
1127) -> NcResult_i32 {
1128    let (mut y, mut x) = (0, 0);
1129    unsafe {
1130        c_api::ncplane_cursor_yx(plane, &mut y, &mut x);
1131    }
1132    ncplane_rounded_box(
1133        plane,
1134        stylemask,
1135        channels,
1136        y + len_y - 1,
1137        x + len_x - 1,
1138        boxmask,
1139    )
1140}
1141
1142// gradient --------------------------------------------------------------------
1143
1144/// Draws a gradient with its upper-left corner at the current cursor position,
1145/// stopping at `end_y`×`end_x`.
1146///
1147/// Use `None` for either or all of `beg_y` and `beg_x` in order to
1148/// use the current cursor position along that axis.
1149///
1150/// Use `None` for either or both of `len_y` and `len_x` in order to
1151/// go through the boundary of the plane in that axis (same as `0`).
1152///
1153/// The glyph composed of `egc` and `stylemask` is used for all cells. The
1154/// `NcChannels_u64` specified by `ul`, `ur`, `ll`, and `lr` are composed into
1155/// foreground and background gradients.
1156///
1157/// - To do a vertical gradient, `ul` ought equal `ur` and `ll` ought equal `lr`.
1158/// - To do a horizontal gradient, `ul` ought equal `ll` and `ur` ought equal `ul`.
1159/// - To color everything the same, all four channels should be equivalent. The
1160/// resulting alpha values are equal to incoming alpha values. Returns the number
1161/// of cells filled on success, or -1 on failure.
1162///
1163/// Palette-indexed color is not supported.
1164///
1165/// Preconditions for gradient operations (error otherwise):
1166/// - all: only RGB colors, unless all four channels match as default
1167/// - all: all alpha values must be the same
1168/// - 1x1: all four colors must be the same
1169/// - 1xN: both top and both bottom colors must be the same (vertical gradient)
1170/// - Nx1: both left and both right colors must be the same (horizontal gradient)
1171///
1172/// *Method: NcPlane.[gradient()][NcPlane#method.gradient].*
1173#[inline]
1174pub fn ncplane_gradient(
1175    plane: &mut NcPlane,
1176    y: Option<u32>,
1177    x: Option<u32>,
1178    len_y: Option<u32>,
1179    len_x: Option<u32>,
1180    egc: &str,
1181    stylemask: impl Into<NcStyle_u16>,
1182    ul: impl Into<NcChannels_u64>,
1183    ur: impl Into<NcChannels_u64>,
1184    ll: impl Into<NcChannels_u64>,
1185    lr: impl Into<NcChannels_u64>,
1186) -> NcResult_i32 {
1187    let cs = cstring![egc];
1188    let egc_ptr = cs.as_ptr() as *const c_char;
1189
1190    unsafe {
1191        c_api::ffi::ncplane_gradient(
1192            plane,
1193            y.unwrap_or(u32::MAX) as i32,
1194            x.unwrap_or(u32::MAX) as i32,
1195            len_y.unwrap_or(0),
1196            len_x.unwrap_or(0),
1197            egc_ptr,
1198            stylemask.into(),
1199            ul.into(),
1200            ur.into(),
1201            ll.into(),
1202            lr.into(),
1203        )
1204    }
1205}
1206
1207// cursor --------------------------------------------------------------------
1208
1209/// Returns the current row of the cursor within this `NcPlane`.
1210///
1211/// *Method: NcPlane.[gradient()][NcPlane#method.gradient].*
1212#[inline]
1213pub fn ncplane_cursor_y(plane: &NcPlane) -> u32 {
1214    let mut y = 0;
1215    unsafe {
1216        c_api::ncplane_cursor_yx(plane, &mut y, null_mut());
1217    }
1218    y
1219}
1220
1221/// Returns the current row of the cursor within this `NcPlane`.
1222///
1223/// *Method: NcPlane.[gradient()][NcPlane#method.gradient].*
1224#[inline]
1225pub fn ncplane_cursor_x(plane: &NcPlane) -> u32 {
1226    let mut x = 0;
1227    unsafe {
1228        c_api::ncplane_cursor_yx(plane, null_mut(), &mut x);
1229    }
1230    x
1231}