1use crate::prelude::{BTerm, FontCharType, TextAlign};
6use crate::BResult;
7use bracket_color::prelude::{ColorPair, RGBA};
8use bracket_geometry::prelude::{Point, PointF, Radians, Rect};
9use object_pool::{Pool, Reusable};
10use parking_lot::Mutex;
11use std::convert::TryInto;
12use std::sync::Arc;
13
14lazy_static! {
15 static ref COMMAND_BUFFER: Mutex<Vec<(usize, Vec<DrawCommand>)>> =
16 Mutex::new(Vec::with_capacity(100));
17}
18
19lazy_static! {
20 static ref BUFFER_POOL: Arc<Pool<DrawBatch>> = Arc::new(Pool::new(128, || DrawBatch {
21 batch: Vec::with_capacity(5000),
22 z_count: 0,
23 needs_sort: false
24 }));
25}
26
27pub fn clear_command_buffer() -> BResult<()> {
30 COMMAND_BUFFER.lock().clear();
31 Ok(())
32}
33
34#[derive(Clone)]
37pub enum DrawCommand {
38 ClearScreen,
39 ClearToColor {
40 color: RGBA,
41 },
42 SetTarget {
43 console: usize,
44 },
45 Set {
46 pos: Point,
47 color: ColorPair,
48 glyph: FontCharType,
49 },
50 SetBackground {
51 pos: Point,
52 bg: RGBA,
53 },
54 Print {
55 pos: Point,
56 text: String,
57 },
58 PrintColor {
59 pos: Point,
60 text: String,
61 color: ColorPair,
62 },
63 PrintRight {
64 pos: Point,
65 text: String,
66 },
67 PrintColorRight {
68 pos: Point,
69 text: String,
70 color: ColorPair,
71 },
72 PrintCentered {
73 y: i32,
74 text: String,
75 },
76 PrintColorCentered {
77 y: i32,
78 text: String,
79 color: ColorPair,
80 },
81 PrintCenteredAt {
82 pos: Point,
83 text: String,
84 },
85 PrintColorCenteredAt {
86 pos: Point,
87 text: String,
88 color: ColorPair,
89 },
90 Printer {
91 pos: Point,
92 text: String,
93 align: TextAlign,
94 background: Option<RGBA>,
95 },
96 Box {
97 pos: Rect,
98 color: ColorPair,
99 },
100 HollowBox {
101 pos: Rect,
102 color: ColorPair,
103 },
104 DoubleBox {
105 pos: Rect,
106 color: ColorPair,
107 },
108 HollowDoubleBox {
109 pos: Rect,
110 color: ColorPair,
111 },
112 FillRegion {
113 pos: Rect,
114 color: ColorPair,
115 glyph: FontCharType,
116 },
117 BarHorizontal {
118 pos: Point,
119 width: i32,
120 n: i32,
121 max: i32,
122 color: ColorPair,
123 },
124 BarVertical {
125 pos: Point,
126 height: i32,
127 n: i32,
128 max: i32,
129 color: ColorPair,
130 },
131 SetClipping {
132 clip: Option<Rect>,
133 },
134 SetFgAlpha {
135 alpha: f32,
136 },
137 SetBgAlpha {
138 alpha: f32,
139 },
140 SetAllAlpha {
141 fg: f32,
142 bg: f32,
143 },
144 SetFancy {
145 position: PointF,
146 z_order: i32,
147 rotation: Radians,
148 color: ColorPair,
149 glyph: FontCharType,
150 scale: PointF,
151 },
152}
153
154pub struct DrawBatch {
156 batch: Vec<(u32, DrawCommand)>,
157 z_count: u32,
158 needs_sort: bool,
159}
160
161impl DrawBatch {
162 pub fn new() -> Reusable<'static, DrawBatch> {
164 let mut result = BUFFER_POOL.pull(|| panic!("No pooling!"));
165 result.z_count = 0;
166 result.needs_sort = false;
167 result
168 }
169
170 fn next_z(&mut self) -> u32 {
171 let result = self.z_count;
172 self.z_count += 1;
173 result
174 }
175
176 pub fn submit(&mut self, z_order: usize) -> BResult<()> {
178 if self.needs_sort {
179 self.batch.sort_by(|a, b| a.0.cmp(&b.0));
180 }
181 let mut new_batch = Vec::with_capacity(self.batch.len());
182 self.batch.drain(0..).for_each(|(_, b)| new_batch.push(b));
183 COMMAND_BUFFER.lock().push((z_order, new_batch));
184 Ok(())
185 }
186
187 pub fn cls(&mut self) -> &mut Self {
189 let z = self.next_z();
190 self.batch.push((z, DrawCommand::ClearScreen));
191 self
192 }
193
194 pub fn cls_color<COLOR>(&mut self, color: COLOR) -> &mut Self
196 where
197 COLOR: Into<RGBA>,
198 {
199 let z = self.next_z();
200 self.batch.push((
201 z,
202 DrawCommand::ClearToColor {
203 color: color.into(),
204 },
205 ));
206 self
207 }
208
209 pub fn target(&mut self, console: usize) -> &mut Self {
211 let z = self.next_z();
212 self.batch.push((z, DrawCommand::SetTarget { console }));
213 self
214 }
215
216 pub fn set<G: TryInto<FontCharType>>(
218 &mut self,
219 pos: Point,
220 color: ColorPair,
221 glyph: G,
222 ) -> &mut Self {
223 let z = self.next_z();
224 self.batch.push((
225 z,
226 DrawCommand::Set {
227 pos,
228 color,
229 glyph: glyph.try_into().ok().expect("Must be u16 convertible"),
230 },
231 ));
232 self
233 }
234
235 pub fn set_with_z<G: TryInto<FontCharType>>(
237 &mut self,
238 pos: Point,
239 color: ColorPair,
240 glyph: G,
241 z: u32,
242 ) -> &mut Self {
243 self.batch.push((
244 z,
245 DrawCommand::Set {
246 pos,
247 color,
248 glyph: glyph.try_into().ok().expect("Must be u16 convertible"),
249 },
250 ));
251 self.needs_sort = true;
252 self
253 }
254
255 pub fn set_fancy<ANGLE: Into<Radians>, Z: TryInto<i32>, G: TryInto<FontCharType>>(
257 &mut self,
258 position: PointF,
259 z_order: Z,
260 rotation: ANGLE,
261 scale: PointF,
262 color: ColorPair,
263 glyph: G,
264 ) -> &mut Self {
265 let z = self.next_z();
266 self.batch.push((
267 z,
268 DrawCommand::SetFancy {
269 position,
270 z_order: z_order.try_into().ok().expect("Must be i32 convertible"),
271 rotation: rotation.into(),
272 color,
273 glyph: glyph.try_into().ok().expect("Must be u16 convertible"),
274 scale,
275 },
276 ));
277 self
278 }
279
280 pub fn set_bg<COLOR>(&mut self, pos: Point, bg: COLOR) -> &mut Self
282 where
283 COLOR: Into<RGBA>,
284 {
285 let z = self.next_z();
286 self.batch
287 .push((z, DrawCommand::SetBackground { pos, bg: bg.into() }));
288 self
289 }
290
291 pub fn set_bg_with_z<COLOR>(&mut self, pos: Point, bg: COLOR, z: u32) -> &mut Self
293 where
294 COLOR: Into<RGBA>,
295 {
296 self.batch
297 .push((z, DrawCommand::SetBackground { pos, bg: bg.into() }));
298 self.needs_sort = true;
299 self
300 }
301
302 pub fn printer<S: ToString>(
305 &mut self,
306 pos: Point,
307 text: S,
308 align: TextAlign,
309 background: Option<RGBA>,
310 ) -> &mut Self {
311 let z = self.next_z();
312 self.batch.push((
313 z,
314 DrawCommand::Printer {
315 pos,
316 text: text.to_string(),
317 align,
318 background,
319 },
320 ));
321 self
322 }
323
324 pub fn printer_with_z<S: ToString>(
328 &mut self,
329 pos: Point,
330 text: S,
331 align: TextAlign,
332 background: Option<RGBA>,
333 z: u32,
334 ) -> &mut Self {
335 self.batch.push((
336 z,
337 DrawCommand::Printer {
338 pos,
339 text: text.to_string(),
340 align,
341 background,
342 },
343 ));
344 self.needs_sort = true;
345 self
346 }
347
348 pub fn print<S: ToString>(&mut self, pos: Point, text: S) -> &mut Self {
350 let z = self.next_z();
351 self.batch.push((
352 z,
353 DrawCommand::Print {
354 pos,
355 text: text.to_string(),
356 },
357 ));
358 self
359 }
360
361 pub fn print_with_z<S: ToString>(&mut self, pos: Point, text: S, z: u32) -> &mut Self {
363 self.batch.push((
364 z,
365 DrawCommand::Print {
366 pos,
367 text: text.to_string(),
368 },
369 ));
370 self.needs_sort = true;
371 self
372 }
373
374 pub fn print_color<S: ToString>(&mut self, pos: Point, text: S, color: ColorPair) -> &mut Self {
376 let z = self.next_z();
377 self.batch.push((
378 z,
379 DrawCommand::PrintColor {
380 pos,
381 text: text.to_string(),
382 color,
383 },
384 ));
385 self
386 }
387
388 pub fn print_color_with_z<S: ToString>(
390 &mut self,
391 pos: Point,
392 text: S,
393 color: ColorPair,
394 z: u32,
395 ) -> &mut Self {
396 self.batch.push((
397 z,
398 DrawCommand::PrintColor {
399 pos,
400 text: text.to_string(),
401 color,
402 },
403 ));
404 self.needs_sort = true;
405 self
406 }
407
408 pub fn print_centered<S: ToString, Y: TryInto<i32>>(&mut self, y: Y, text: S) -> &mut Self {
410 let z = self.next_z();
411 self.batch.push((
412 z,
413 DrawCommand::PrintCentered {
414 y: y.try_into().ok().expect("Must be i32 convertible"),
415 text: text.to_string(),
416 },
417 ));
418 self
419 }
420
421 pub fn print_centered_with_z<S: ToString, Y: TryInto<i32>>(
423 &mut self,
424 y: Y,
425 text: S,
426 z: u32,
427 ) -> &mut Self {
428 self.batch.push((
429 z,
430 DrawCommand::PrintCentered {
431 y: y.try_into().ok().expect("Must be i32 convertible"),
432 text: text.to_string(),
433 },
434 ));
435 self.needs_sort = true;
436 self
437 }
438
439 pub fn print_color_centered<S: ToString, Y: TryInto<i32>>(
441 &mut self,
442 y: Y,
443 text: S,
444 color: ColorPair,
445 ) -> &mut Self {
446 let z = self.next_z();
447 self.batch.push((
448 z,
449 DrawCommand::PrintColorCentered {
450 y: y.try_into().ok().expect("Must be i32 convertible"),
451 text: text.to_string(),
452 color,
453 },
454 ));
455 self
456 }
457
458 pub fn print_color_centered_with_z<S: ToString, Y: TryInto<i32>>(
460 &mut self,
461 y: Y,
462 text: S,
463 color: ColorPair,
464 z: u32,
465 ) -> &mut Self {
466 self.batch.push((
467 z,
468 DrawCommand::PrintColorCentered {
469 y: y.try_into().ok().expect("Must be i32 convertible"),
470 text: text.to_string(),
471 color,
472 },
473 ));
474 self.needs_sort = true;
475 self
476 }
477
478 pub fn print_centered_at<S: ToString>(&mut self, pos: Point, text: S) -> &mut Self {
480 let z = self.next_z();
481 self.batch.push((
482 z,
483 DrawCommand::PrintCenteredAt {
484 pos,
485 text: text.to_string(),
486 },
487 ));
488 self
489 }
490
491 pub fn print_centered_at_with_z<S: ToString>(
493 &mut self,
494 pos: Point,
495 text: S,
496 z: u32,
497 ) -> &mut Self {
498 self.batch.push((
499 z,
500 DrawCommand::PrintCenteredAt {
501 pos,
502 text: text.to_string(),
503 },
504 ));
505 self.needs_sort = true;
506 self
507 }
508
509 pub fn print_color_centered_at<S: ToString>(
511 &mut self,
512 pos: Point,
513 text: S,
514 color: ColorPair,
515 ) -> &mut Self {
516 let z = self.next_z();
517 self.batch.push((
518 z,
519 DrawCommand::PrintColorCenteredAt {
520 pos,
521 text: text.to_string(),
522 color,
523 },
524 ));
525 self
526 }
527
528 pub fn print_color_centered_at_with_z<S: ToString>(
530 &mut self,
531 pos: Point,
532 text: S,
533 color: ColorPair,
534 z: u32,
535 ) -> &mut Self {
536 self.batch.push((
537 z,
538 DrawCommand::PrintColorCenteredAt {
539 pos,
540 text: text.to_string(),
541 color,
542 },
543 ));
544 self.needs_sort = true;
545 self
546 }
547
548 pub fn print_right<S: ToString>(&mut self, pos: Point, text: S) -> &mut Self {
550 let z = self.next_z();
551 self.batch.push((
552 z,
553 DrawCommand::PrintRight {
554 pos,
555 text: text.to_string(),
556 },
557 ));
558 self
559 }
560
561 pub fn print_right_z<S: ToString>(&mut self, pos: Point, text: S, z: u32) -> &mut Self {
563 self.batch.push((
564 z,
565 DrawCommand::PrintRight {
566 pos,
567 text: text.to_string(),
568 },
569 ));
570 self.needs_sort = true;
571 self
572 }
573
574 pub fn print_color_right<S: ToString>(
576 &mut self,
577 pos: Point,
578 text: S,
579 color: ColorPair,
580 ) -> &mut Self {
581 let z = self.next_z();
582 self.batch.push((
583 z,
584 DrawCommand::PrintColorRight {
585 pos,
586 text: text.to_string(),
587 color,
588 },
589 ));
590 self
591 }
592
593 pub fn print_color_right_with_z<S: ToString>(
595 &mut self,
596 pos: Point,
597 text: S,
598 color: ColorPair,
599 z: u32,
600 ) -> &mut Self {
601 self.batch.push((
602 z,
603 DrawCommand::PrintColorRight {
604 pos,
605 text: text.to_string(),
606 color,
607 },
608 ));
609 self.needs_sort = true;
610 self
611 }
612
613 pub fn draw_box(&mut self, pos: Rect, color: ColorPair) -> &mut Self {
615 let z = self.next_z();
616 self.batch.push((z, DrawCommand::Box { pos, color }));
617 self
618 }
619
620 pub fn draw_box_with_z(&mut self, pos: Rect, color: ColorPair, z: u32) -> &mut Self {
622 self.batch.push((z, DrawCommand::Box { pos, color }));
623 self.needs_sort = true;
624 self
625 }
626
627 pub fn draw_hollow_box(&mut self, pos: Rect, color: ColorPair) -> &mut Self {
629 let z = self.next_z();
630 self.batch.push((z, DrawCommand::HollowBox { pos, color }));
631 self
632 }
633
634 pub fn draw_hollow_box_with_z(&mut self, pos: Rect, color: ColorPair, z: u32) -> &mut Self {
636 self.batch.push((z, DrawCommand::HollowBox { pos, color }));
637 self.needs_sort = true;
638 self
639 }
640
641 pub fn draw_double_box(&mut self, pos: Rect, color: ColorPair) -> &mut Self {
643 let z = self.next_z();
644 self.batch.push((z, DrawCommand::DoubleBox { pos, color }));
645 self
646 }
647
648 pub fn draw_double_box_with_z(&mut self, pos: Rect, color: ColorPair, z: u32) -> &mut Self {
650 self.batch.push((z, DrawCommand::DoubleBox { pos, color }));
651 self.needs_sort = true;
652 self
653 }
654
655 pub fn draw_hollow_double_box(&mut self, pos: Rect, color: ColorPair) -> &mut Self {
657 let z = self.next_z();
658 self.batch
659 .push((z, DrawCommand::HollowDoubleBox { pos, color }));
660 self
661 }
662
663 pub fn draw_hollow_double_box_with_z(
665 &mut self,
666 pos: Rect,
667 color: ColorPair,
668 z: u32,
669 ) -> &mut Self {
670 self.batch
671 .push((z, DrawCommand::HollowDoubleBox { pos, color }));
672 self.needs_sort = true;
673 self
674 }
675
676 pub fn fill_region<G: TryInto<FontCharType>>(
678 &mut self,
679 pos: Rect,
680 color: ColorPair,
681 glyph: G,
682 ) -> &mut Self {
683 let z = self.next_z();
684 self.batch.push((
685 z,
686 DrawCommand::FillRegion {
687 pos,
688 color,
689 glyph: glyph.try_into().ok().expect("Must be u16 convertible"),
690 },
691 ));
692 self
693 }
694
695 pub fn fill_region_with_z<G: TryInto<FontCharType>>(
697 &mut self,
698 pos: Rect,
699 color: ColorPair,
700 glyph: G,
701 z: u32,
702 ) -> &mut Self {
703 self.batch.push((
704 z,
705 DrawCommand::FillRegion {
706 pos,
707 color,
708 glyph: glyph.try_into().ok().expect("Must be u16 convertible"),
709 },
710 ));
711 self.needs_sort = true;
712 self
713 }
714
715 pub fn bar_horizontal<W, N, MAX>(
717 &mut self,
718 pos: Point,
719 width: W,
720 n: N,
721 max: MAX,
722 color: ColorPair,
723 ) -> &mut Self
724 where
725 W: TryInto<i32>,
726 N: TryInto<i32>,
727 MAX: TryInto<i32>,
728 {
729 let z = self.next_z();
730 self.batch.push((
731 z,
732 DrawCommand::BarHorizontal {
733 pos,
734 width: width.try_into().ok().expect("Must be i32 convertible"),
735 n: n.try_into().ok().expect("Must be i32 convertible"),
736 max: max.try_into().ok().expect("Must be i32 convertible"),
737 color,
738 },
739 ));
740 self
741 }
742
743 pub fn bar_horizontal_with_z<W, N, MAX>(
745 &mut self,
746 pos: Point,
747 width: W,
748 n: N,
749 max: MAX,
750 color: ColorPair,
751 z: u32,
752 ) -> &mut Self
753 where
754 W: TryInto<i32>,
755 N: TryInto<i32>,
756 MAX: TryInto<i32>,
757 {
758 self.batch.push((
759 z,
760 DrawCommand::BarHorizontal {
761 pos,
762 width: width.try_into().ok().expect("Must be i32 convertible"),
763 n: n.try_into().ok().expect("Must be i32 convertible"),
764 max: max.try_into().ok().expect("Must be i32 convertible"),
765 color,
766 },
767 ));
768 self.needs_sort = true;
769 self
770 }
771
772 pub fn bar_vertical<H, N, MAX>(
774 &mut self,
775 pos: Point,
776 height: H,
777 n: N,
778 max: MAX,
779 color: ColorPair,
780 ) -> &mut Self
781 where
782 H: TryInto<i32>,
783 N: TryInto<i32>,
784 MAX: TryInto<i32>,
785 {
786 let z = self.next_z();
787 self.batch.push((
788 z,
789 DrawCommand::BarVertical {
790 pos,
791 height: height.try_into().ok().expect("Must be i32 convertible"),
792 n: n.try_into().ok().expect("Must be i32 convertible"),
793 max: max.try_into().ok().expect("Must be i32 convertible"),
794 color,
795 },
796 ));
797 self
798 }
799
800 pub fn bar_vertical_with_z<H, N, MAX>(
802 &mut self,
803 pos: Point,
804 height: H,
805 n: N,
806 max: MAX,
807 color: ColorPair,
808 z: u32,
809 ) -> &mut Self
810 where
811 H: TryInto<i32>,
812 N: TryInto<i32>,
813 MAX: TryInto<i32>,
814 {
815 self.batch.push((
816 z,
817 DrawCommand::BarVertical {
818 pos,
819 height: height.try_into().ok().expect("Must be i32 convertible"),
820 n: n.try_into().ok().expect("Must be i32 convertible"),
821 max: max.try_into().ok().expect("Must be i32 convertible"),
822 color,
823 },
824 ));
825 self.needs_sort = true;
826 self
827 }
828
829 pub fn set_clipping(&mut self, clip: Option<Rect>) -> &mut Self {
831 let z = self.next_z();
832 self.batch.push((z, DrawCommand::SetClipping { clip }));
833 self
834 }
835
836 pub fn set_all_fg_alpha(&mut self, alpha: f32) -> &mut Self {
838 let z = self.next_z();
839 self.batch.push((z, DrawCommand::SetFgAlpha { alpha }));
840 self
841 }
842
843 pub fn set_all_bg_alpha(&mut self, alpha: f32) -> &mut Self {
845 let z = self.next_z();
846 self.batch.push((z, DrawCommand::SetBgAlpha { alpha }));
847 self
848 }
849
850 pub fn set_all_alpha(&mut self, fg: f32, bg: f32) -> &mut Self {
852 let z = self.next_z();
853 self.batch.push((z, DrawCommand::SetAllAlpha { fg, bg }));
854 self
855 }
856}
857
858pub fn render_draw_buffer(bterm: &mut BTerm) -> BResult<()> {
860 let mut buffer = COMMAND_BUFFER.lock();
861 buffer.sort_unstable_by(|a, b| a.0.cmp(&b.0));
862 buffer.iter().for_each(|(_, batch)| {
863 batch.iter().for_each(|cmd| match cmd {
864 DrawCommand::ClearScreen => bterm.cls(),
865 DrawCommand::ClearToColor { color } => bterm.cls_bg(*color),
866 DrawCommand::SetTarget { console } => bterm.set_active_console(*console),
867 DrawCommand::Set { pos, color, glyph } => {
868 bterm.set(pos.x, pos.y, color.fg, color.bg, *glyph)
869 }
870 DrawCommand::SetBackground { pos, bg } => bterm.set_bg(pos.x, pos.y, *bg),
871 DrawCommand::Print { pos, text } => bterm.print(pos.x, pos.y, &text),
872 DrawCommand::PrintColor { pos, text, color } => {
873 bterm.print_color(pos.x, pos.y, color.fg, color.bg, &text)
874 }
875 DrawCommand::PrintCentered { y, text } => bterm.print_centered(*y, &text),
876 DrawCommand::PrintColorCentered { y, text, color } => {
877 bterm.print_color_centered(*y, color.fg, color.bg, &text)
878 }
879 DrawCommand::PrintCenteredAt { pos, text } => {
880 bterm.print_centered_at(pos.x, pos.y, &text)
881 }
882 DrawCommand::PrintColorCenteredAt { pos, text, color } => {
883 bterm.print_color_centered_at(pos.x, pos.y, color.fg, color.bg, &text)
884 }
885 DrawCommand::PrintRight { pos, text } => bterm.print_right(pos.x, pos.y, text),
886 DrawCommand::PrintColorRight { pos, text, color } => {
887 bterm.print_color_right(pos.x, pos.y, color.fg, color.bg, text)
888 }
889 DrawCommand::Printer {
890 pos,
891 text,
892 align,
893 background,
894 } => bterm.printer(pos.x, pos.y, text, *align, *background),
895 DrawCommand::Box { pos, color } => bterm.draw_box(
896 pos.x1,
897 pos.y1,
898 pos.width(),
899 pos.height(),
900 color.fg,
901 color.bg,
902 ),
903 DrawCommand::HollowBox { pos, color } => bterm.draw_hollow_box(
904 pos.x1,
905 pos.y1,
906 pos.width(),
907 pos.height(),
908 color.fg,
909 color.bg,
910 ),
911 DrawCommand::DoubleBox { pos, color } => bterm.draw_box_double(
912 pos.x1,
913 pos.y1,
914 pos.width(),
915 pos.height(),
916 color.fg,
917 color.bg,
918 ),
919 DrawCommand::HollowDoubleBox { pos, color } => bterm.draw_hollow_box_double(
920 pos.x1,
921 pos.y1,
922 pos.width(),
923 pos.height(),
924 color.fg,
925 color.bg,
926 ),
927 DrawCommand::FillRegion { pos, color, glyph } => {
928 bterm.fill_region::<RGBA, RGBA, FontCharType>(*pos, *glyph, color.fg, color.bg)
929 }
930 DrawCommand::BarHorizontal {
931 pos,
932 width,
933 n,
934 max,
935 color,
936 } => bterm.draw_bar_horizontal(pos.x, pos.y, *width, *n, *max, color.fg, color.bg),
937 DrawCommand::BarVertical {
938 pos,
939 height,
940 n,
941 max,
942 color,
943 } => bterm.draw_bar_vertical(pos.x, pos.y, *height, *n, *max, color.fg, color.bg),
944 DrawCommand::SetClipping { clip } => bterm.set_clipping(*clip),
945 DrawCommand::SetFgAlpha { alpha } => bterm.set_all_fg_alpha(*alpha),
946 DrawCommand::SetBgAlpha { alpha } => bterm.set_all_fg_alpha(*alpha),
947 DrawCommand::SetAllAlpha { fg, bg } => bterm.set_all_alpha(*fg, *bg),
948 DrawCommand::SetFancy {
949 position,
950 z_order,
951 color,
952 glyph,
953 rotation,
954 scale,
955 } => {
956 bterm.set_fancy(
957 *position, *z_order, *rotation, *scale, color.fg, color.bg, *glyph,
958 );
959 }
960 })
961 });
962 buffer.clear();
963 Ok(())
964}