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}