1#![allow(dead_code)]
2#[allow(unused_imports)]
3use crate::{
4 prelude::{
5 init_raw, BEvent, CharacterTranslationMode, Console, FlexiConsole, Font, FontCharType,
6 GameState, InitHints, Radians, RenderSprite, Shader, SimpleConsole, SpriteConsole,
7 SpriteSheet, TextAlign, VirtualKeyCode, XpFile, XpLayer, BACKEND, INPUT,
8 },
9 BResult,
10};
11use bracket_color::prelude::RGBA;
12use bracket_geometry::prelude::{Point, PointF, Rect};
13use parking_lot::Mutex;
14use std::convert::*;
15
16pub struct DisplayConsole {
19 pub console: Box<dyn Console>,
20 pub shader_index: usize,
21 pub font_index: usize,
22}
23
24pub struct BTermInternal {
25 pub fonts: Vec<Font>,
26 pub shaders: Vec<Shader>,
27 pub consoles: Vec<DisplayConsole>,
28 pub sprite_sheets: Vec<SpriteSheet>,
29}
30
31impl BTermInternal {
32 pub fn new() -> Self {
33 Self {
34 fonts: Vec::new(),
35 shaders: Vec::new(),
36 consoles: Vec::new(),
37 sprite_sheets: Vec::new(),
38 }
39 }
40}
41
42impl Default for BTermInternal {
43 fn default() -> Self {
44 Self {
45 fonts: Vec::new(),
46 shaders: Vec::new(),
47 consoles: Vec::new(),
48 sprite_sheets: Vec::new(),
49 }
50 }
51}
52
53unsafe impl Send for BTermInternal {}
54unsafe impl Sync for BTermInternal {}
55
56lazy_static! {
57 pub static ref BACKEND_INTERNAL: Mutex<BTermInternal> = Mutex::new(BTermInternal::new());
58}
59
60#[derive(Clone, Debug)]
62pub struct BTerm {
63 pub width_pixels: u32,
64 pub height_pixels: u32,
65 pub original_height_pixels: u32,
66 pub original_width_pixels: u32,
67 pub fps: f32,
68 pub frame_time_ms: f32,
69 pub active_console: usize,
70 pub key: Option<VirtualKeyCode>,
71 pub mouse_pos: (i32, i32),
72 pub left_click: bool,
73 pub shift: bool,
74 pub control: bool,
75 pub alt: bool,
76 pub web_button: Option<String>,
77 pub quitting: bool,
78 pub post_scanlines: bool,
79 pub post_screenburn: bool,
80 pub screen_burn_color: bracket_color::prelude::RGB,
81 pub mouse_visible: bool,
82}
83
84impl BTerm {
85 pub fn init_raw<S: ToString, T>(
87 width_pixels: T,
88 height_pixels: T,
89 window_title: S,
90 platform_hints: InitHints,
91 ) -> BResult<BTerm>
92 where
93 T: TryInto<u32>,
94 {
95 let w = width_pixels.try_into();
96 let h = height_pixels.try_into();
97 let (w, h) = if let (Ok(w), Ok(h)) = (w, h) {
98 (w, h)
99 } else {
100 return Err("Couldn't convert to u32".into());
101 };
102 init_raw(w, h, window_title, platform_hints)
103 }
104
105 #[deprecated(
107 since = "0.6.2",
108 note = "Please migrate to the BTermBuilder system instead."
109 )]
110 pub fn init_simple8x8<S: ToString, T>(
111 width_chars: T,
112 height_chars: T,
113 window_title: S,
114 path_to_shaders: S,
115 ) -> BTerm
116 where
117 T: TryInto<u32>,
118 {
119 let w: u32 = width_chars.try_into().ok().unwrap();
120 let h: u32 = height_chars.try_into().ok().unwrap();
121 let font_path = format!("{}/terminal8x8.png", &path_to_shaders.to_string());
122 let mut context = BTerm::init_raw(w * 8, h * 8, window_title, InitHints::new()).unwrap();
123 let font = context.register_font(Font::load(font_path, (8, 8), None));
124 context.register_console(SimpleConsole::init(w, h), font.unwrap());
125 context
126 }
127
128 #[deprecated(
130 since = "0.6.2",
131 note = "Please migrate to the BTermBuilder system instead."
132 )]
133 pub fn init_simple8x16<S: ToString, T>(
134 width_chars: T,
135 height_chars: T,
136 window_title: S,
137 path_to_shaders: S,
138 ) -> BTerm
139 where
140 T: TryInto<u32>,
141 {
142 let w: u32 = width_chars.try_into().ok().unwrap();
143 let h: u32 = height_chars.try_into().ok().unwrap();
144 let font_path = format!("{}/vga8x16.png", &path_to_shaders.to_string());
145 let mut context = BTerm::init_raw(w * 8, h * 16, window_title, InitHints::new()).unwrap();
146 let font = context.register_font(Font::load(font_path, (8, 16), None));
147 context.register_console(SimpleConsole::init(w, h), font.unwrap());
148 context
149 }
150
151 pub(crate) fn register_font(&mut self, font: Font) -> BResult<usize> {
153 let mut bi = BACKEND_INTERNAL.lock();
154 bi.fonts.push(font);
155 Ok(bi.fonts.len() - 1)
156 }
157
158 pub fn register_console(&mut self, new_console: Box<dyn Console>, font_index: usize) -> usize {
160 let mut bi = BACKEND_INTERNAL.lock();
161 bi.consoles.push(DisplayConsole {
162 console: new_console,
163 font_index,
164 shader_index: 0,
165 });
166 bi.consoles.len() - 1
167 }
168
169 pub fn register_console_no_bg(
172 &mut self,
173 new_console: Box<dyn Console>,
174 font_index: usize,
175 ) -> usize {
176 let mut bi = BACKEND_INTERNAL.lock();
177 bi.consoles.push(DisplayConsole {
178 console: new_console,
179 font_index,
180 shader_index: 1,
181 });
182 bi.consoles.len() - 1
183 }
184
185 pub fn register_fancy_console(
188 &mut self,
189 new_console: Box<dyn Console>,
190 font_index: usize,
191 ) -> usize {
192 let mut bi = BACKEND_INTERNAL.lock();
193 bi.consoles.push(DisplayConsole {
194 console: new_console,
195 font_index,
196 shader_index: 4,
197 });
198 bi.consoles.len() - 1
199 }
200
201 pub fn register_sprite_console(&mut self, new_console: Box<dyn Console>) -> usize {
203 let mut bi = BACKEND_INTERNAL.lock();
204 bi.consoles.push(DisplayConsole {
205 console: new_console,
206 font_index: 0,
207 shader_index: 5,
208 });
209 bi.consoles.len() - 1
210 }
211
212 pub fn set_active_console(&mut self, id: usize) {
214 let length = BACKEND_INTERNAL.lock().consoles.len();
215 if id < length {
216 self.active_console = id;
217 } else {
218 panic!(
219 "Invalid console id: {}. Valid consoles are 0..{}",
220 id, length
221 );
222 }
223 }
224
225 #[cfg(any(feature = "curses", feature = "cross_term"))]
227 pub fn mouse_pos(&self) -> (i32, i32) {
228 (self.mouse_pos.0, self.mouse_pos.1)
229 }
230
231 #[cfg(any(feature = "curses", feature = "cross_term"))]
233 fn pixel_to_char_pos(&self, pos: (i32, i32), _console: &Box<dyn Console>) -> (i32, i32) {
234 pos
235 }
236
237 #[cfg(not(any(feature = "curses", feature = "cross_term")))]
238 fn pixel_to_char_pos(&self, pos: (i32, i32), console: &Box<dyn Console>) -> (i32, i32) {
239 let max_sizes = console.get_char_size();
240 let (scale, center_x, center_y) = console.get_scale();
241
242 let font_size = (
245 self.width_pixels as f32 / max_sizes.0 as f32,
246 self.height_pixels as f32 / max_sizes.1 as f32,
247 );
248 let mut offsets = (
249 center_x as f32 * font_size.0 * (scale - 1.0),
250 center_y as f32 * font_size.1 * (scale - 1.0),
251 );
252
253 let w: f32;
254 let h: f32;
255
256 {
257 let be = crate::hal::BACKEND.lock();
258 w = be.screen_scaler.available_width as f32;
259 h = be.screen_scaler.available_height as f32;
260 offsets.0 -= be.screen_scaler.gutter_left as f32;
261 offsets.1 -= be.screen_scaler.gutter_top as f32;
262 }
263
264 let extent_x = (pos.0 as f32 + offsets.0) / w;
265 let extent_y = (pos.1 as f32 + offsets.1) / h;
266 let mouse_x = f32::min(extent_x * max_sizes.0 as f32, max_sizes.0 as f32 - 1.0);
267 let mouse_y = f32::min(extent_y * max_sizes.1 as f32, max_sizes.1 as f32 - 1.0);
268
269 (i32::max(0, mouse_x as i32), i32::max(0, mouse_y as i32))
270 }
271
272 #[cfg(not(any(feature = "curses", feature = "cross_term")))]
274 pub fn mouse_pos(&self) -> (i32, i32) {
275 let bi = BACKEND_INTERNAL.lock();
276 let active_console = &bi.consoles[self.active_console].console;
277
278 self.pixel_to_char_pos(self.mouse_pos, active_console)
279 }
280
281 pub fn mouse_point(&self) -> Point {
283 let bi = BACKEND_INTERNAL.lock();
284 let active_console = &bi.consoles[self.active_console].console;
285 let char_pos = self.pixel_to_char_pos(self.mouse_pos, active_console);
286
287 Point::new(char_pos.0, char_pos.1)
288 }
289
290 pub fn quit(&mut self) {
292 self.quitting = true;
293 }
294
295 pub fn render_xp_sprite(&mut self, xp: &super::rex::XpFile, x: i32, y: i32) {
299 let mut bi = BACKEND_INTERNAL.lock();
300 super::rex::xp_to_console(xp, &mut bi.consoles[self.active_console].console, x, y);
301 }
302
303 pub fn to_xp_file(&self, width: usize, height: usize) -> XpFile {
307 let bi = BACKEND_INTERNAL.lock();
308 let mut xp = XpFile::new(width, height);
309
310 xp.layers
311 .push(bi.consoles[self.active_console].console.to_xp_layer());
312
313 if bi.consoles.len() > 1 {
314 for layer in bi.consoles.iter().skip(1) {
315 xp.layers.push(layer.console.to_xp_layer());
316 }
317 }
318
319 xp
320 }
321
322 pub fn with_post_scanlines(&mut self, with_burn: bool) {
324 self.post_scanlines = true;
325 self.post_screenburn = with_burn;
326 }
327
328 pub fn screen_burn_color(&mut self, color: bracket_color::prelude::RGB) {
330 self.screen_burn_color = color;
331 }
332
333 pub fn with_mouse_visibility(&mut self, with_visibility: bool) {
335 self.mouse_visible = with_visibility;
336 }
337
338 pub(crate) fn on_key(&mut self, key: VirtualKeyCode, scan_code: u32, pressed: bool) {
340 let mut input = INPUT.lock();
341 if pressed {
342 self.key = Some(key);
343 input.on_key_down(key, scan_code);
344 } else {
345 self.key = None;
346 input.on_key_up(key, scan_code);
347 }
348 input.push_event(BEvent::KeyboardInput {
349 key,
350 scan_code,
351 pressed,
352 });
353 }
354
355 pub(crate) fn on_mouse_button(&mut self, button_num: usize, pressed: bool) {
357 if button_num == 0 {
358 self.left_click = true;
359 }
360 let mut input = INPUT.lock();
361 if pressed {
362 input.on_mouse_button_down(button_num);
363 } else {
364 input.on_mouse_button_up(button_num);
365 }
366 input.push_event(BEvent::MouseClick {
367 button: button_num,
368 pressed,
369 });
370 }
371
372 pub(crate) fn on_mouse_position(&mut self, x: f64, y: f64) {
374 let bi = BACKEND_INTERNAL.lock();
375 self.mouse_pos = (x as i32, y as i32);
376 let mut input = INPUT.lock();
377 input.on_mouse_pixel_position(x, y);
378 for (i, cons) in bi.consoles.iter().enumerate() {
380 let char_pos = self.pixel_to_char_pos(self.mouse_pos, &cons.console);
381
382 input.on_mouse_tile_position(i, char_pos.0, char_pos.1);
383 }
384 }
385
386 #[allow(dead_code)]
388 pub(crate) fn on_event(&mut self, event: BEvent) {
389 INPUT.lock().push_event(event);
390 }
391}
392
393impl BTerm {
396 pub fn get_char_size(&self) -> (u32, u32) {
398 let bi = BACKEND_INTERNAL.lock();
399 bi.consoles[self.active_console].console.get_char_size()
400 }
401
402 pub(crate) fn resize_pixels<T>(&mut self, width: T, height: T, scaling_enabled: bool)
405 where
406 T: Into<u32>,
407 {
408 self.width_pixels = width.into();
409 self.height_pixels = height.into();
410
411 if scaling_enabled {
412 self.original_width_pixels = self.width_pixels;
413 self.original_height_pixels = self.height_pixels;
414 }
415
416 let mut bi = BACKEND_INTERNAL.lock();
417 for c in bi.consoles.iter_mut() {
418 c.console
419 .resize_pixels(self.width_pixels, self.height_pixels);
420 }
421 }
422
423 pub fn cls(&mut self) {
425 BACKEND_INTERNAL.lock().consoles[self.active_console]
426 .console
427 .cls();
428 }
429
430 pub fn cls_bg<COLOR>(&mut self, background: COLOR)
433 where
434 COLOR: Into<RGBA>,
435 {
436 BACKEND_INTERNAL.lock().consoles[self.active_console]
437 .console
438 .cls_bg(background.into());
439 }
440
441 pub fn print<S, X, Y>(&mut self, x: X, y: Y, output: S)
443 where
444 S: ToString,
445 X: TryInto<i32>,
446 Y: TryInto<i32>,
447 {
448 BACKEND_INTERNAL.lock().consoles[self.active_console]
449 .console
450 .print(
451 x.try_into().ok().expect("Must be i32 convertible"),
452 y.try_into().ok().expect("Must be i32 convertible"),
453 &output.to_string(),
454 );
455 }
456
457 pub fn print_color<S, COLOR, COLOR2, X, Y>(
459 &mut self,
460 x: X,
461 y: Y,
462 fg: COLOR,
463 bg: COLOR2,
464 output: S,
465 ) where
466 S: ToString,
467 COLOR: Into<RGBA>,
468 COLOR2: Into<RGBA>,
469 X: TryInto<i32>,
470 Y: TryInto<i32>,
471 {
472 BACKEND_INTERNAL.lock().consoles[self.active_console]
473 .console
474 .print_color(
475 x.try_into().ok().expect("Must be i32 convertible"),
476 y.try_into().ok().expect("Must be i32 convertible"),
477 fg.into(),
478 bg.into(),
479 &output.to_string(),
480 );
481 }
482
483 pub fn set<COLOR, COLOR2, GLYPH, X, Y>(
485 &mut self,
486 x: X,
487 y: Y,
488 fg: COLOR,
489 bg: COLOR2,
490 glyph: GLYPH,
491 ) where
492 COLOR: Into<RGBA>,
493 COLOR2: Into<RGBA>,
494 GLYPH: TryInto<FontCharType>,
495 X: TryInto<i32>,
496 Y: TryInto<i32>,
497 {
498 BACKEND_INTERNAL.lock().consoles[self.active_console]
499 .console
500 .set(
501 x.try_into().ok().expect("Must be i32 convertible"),
502 y.try_into().ok().expect("Must be i32 convertible"),
503 fg.into(),
504 bg.into(),
505 glyph.try_into().ok().expect("Must be u16 convertible"),
506 );
507 }
508
509 #[cfg(any(feature = "opengl", feature = "webgpu"))]
511 #[allow(clippy::too_many_arguments)]
512 pub fn set_fancy<COLOR, COLOR2, GLYPH, ANGLE>(
513 &mut self,
514 position: PointF,
515 z_order: i32,
516 rotation: ANGLE,
517 scale: PointF,
518 fg: COLOR,
519 bg: COLOR2,
520 glyph: GLYPH,
521 ) where
522 COLOR: Into<RGBA>,
523 COLOR2: Into<RGBA>,
524 GLYPH: TryInto<FontCharType>,
525 ANGLE: Into<Radians>,
526 {
527 let mut be = BACKEND_INTERNAL.lock();
528 let cons_any = be.consoles[self.active_console].console.as_any_mut();
529 if let Some(fc) = cons_any.downcast_mut::<FlexiConsole>() {
530 fc.set_fancy(
531 position,
532 z_order,
533 rotation.into().0,
534 scale,
535 fg.into(),
536 bg.into(),
537 glyph.try_into().ok().expect("Must be u16 convertible"),
538 );
539 }
540 }
541
542 #[cfg(not(any(feature = "opengl", feature = "webgpu")))]
544 pub fn set_fancy<COLOR, COLOR2, GLYPH, ANGLE>(
545 &mut self,
546 _position: PointF,
547 _z_order: i32,
548 _rotation: ANGLE,
549 _scale: PointF,
550 _fg: COLOR,
551 _bg: COLOR2,
552 _glyph: GLYPH,
553 ) where
554 COLOR: Into<RGBA>,
555 COLOR2: Into<RGBA>,
556 GLYPH: TryInto<FontCharType>,
557 ANGLE: Into<Radians>,
558 {
559 }
561
562 pub fn set_bg<COLOR, X, Y>(&mut self, x: X, y: Y, bg: COLOR)
564 where
565 COLOR: Into<RGBA>,
566 X: TryInto<i32>,
567 Y: TryInto<i32>,
568 {
569 BACKEND_INTERNAL.lock().consoles[self.active_console]
570 .console
571 .set_bg(
572 x.try_into().ok().expect("Must be i32 convertible"),
573 y.try_into().ok().expect("Must be i32 convertible"),
574 bg.into(),
575 );
576 }
577
578 pub fn draw_box<COLOR, COLOR2, X, Y, W, H>(
580 &mut self,
581 x: X,
582 y: Y,
583 width: W,
584 height: H,
585 fg: COLOR,
586 bg: COLOR2,
587 ) where
588 COLOR: Into<RGBA>,
589 COLOR2: Into<RGBA>,
590 X: TryInto<i32>,
591 Y: TryInto<i32>,
592 W: TryInto<i32>,
593 H: TryInto<i32>,
594 {
595 BACKEND_INTERNAL.lock().consoles[self.active_console]
596 .console
597 .draw_box(
598 x.try_into().ok().expect("Must be i32 convertible"),
599 y.try_into().ok().expect("Must be i32 convertible"),
600 width.try_into().ok().expect("Must be i32 convertible"),
601 height.try_into().ok().expect("Must be i32 convertible"),
602 fg.into(),
603 bg.into(),
604 );
605 }
606
607 pub fn draw_box_double<COLOR, COLOR2, X, Y, W, H>(
609 &mut self,
610 x: X,
611 y: Y,
612 width: W,
613 height: H,
614 fg: COLOR,
615 bg: COLOR2,
616 ) where
617 COLOR: Into<RGBA>,
618 COLOR2: Into<RGBA>,
619 X: TryInto<i32>,
620 Y: TryInto<i32>,
621 W: TryInto<i32>,
622 H: TryInto<i32>,
623 {
624 BACKEND_INTERNAL.lock().consoles[self.active_console]
625 .console
626 .draw_box_double(
627 x.try_into().ok().expect("Must be i32 convertible"),
628 y.try_into().ok().expect("Must be i32 convertible"),
629 width.try_into().ok().expect("Must be i32 convertible"),
630 height.try_into().ok().expect("Must be i32 convertible"),
631 fg.into(),
632 bg.into(),
633 );
634 }
635
636 pub fn draw_hollow_box<COLOR, COLOR2, X, Y, W, H>(
638 &mut self,
639 x: X,
640 y: Y,
641 width: W,
642 height: H,
643 fg: COLOR,
644 bg: COLOR2,
645 ) where
646 COLOR: Into<RGBA>,
647 COLOR2: Into<RGBA>,
648 X: TryInto<i32>,
649 Y: TryInto<i32>,
650 W: TryInto<i32>,
651 H: TryInto<i32>,
652 {
653 BACKEND_INTERNAL.lock().consoles[self.active_console]
654 .console
655 .draw_hollow_box(
656 x.try_into().ok().expect("Must be i32 convertible"),
657 y.try_into().ok().expect("Must be i32 convertible"),
658 width.try_into().ok().expect("Must be i32 convertible"),
659 height.try_into().ok().expect("Must be i32 convertible"),
660 fg.into(),
661 bg.into(),
662 );
663 }
664
665 pub fn draw_hollow_box_double<COLOR, COLOR2, X, Y, W, H>(
667 &mut self,
668 x: X,
669 y: Y,
670 width: W,
671 height: H,
672 fg: COLOR,
673 bg: COLOR2,
674 ) where
675 COLOR: Into<RGBA>,
676 COLOR2: Into<RGBA>,
677 X: TryInto<i32>,
678 Y: TryInto<i32>,
679 W: TryInto<i32>,
680 H: TryInto<i32>,
681 {
682 BACKEND_INTERNAL.lock().consoles[self.active_console]
683 .console
684 .draw_hollow_box_double(
685 x.try_into().ok().expect("Must be i32 convertible"),
686 y.try_into().ok().expect("Must be i32 convertible"),
687 width.try_into().ok().expect("Must be i32 convertible"),
688 height.try_into().ok().expect("Must be i32 convertible"),
689 fg.into(),
690 bg.into(),
691 );
692 }
693
694 #[allow(clippy::too_many_arguments)]
696 pub fn draw_bar_horizontal<COLOR, COLOR2, X, Y, W, N, MAX>(
697 &mut self,
698 x: X,
699 y: Y,
700 width: W,
701 n: N,
702 max: MAX,
703 fg: COLOR,
704 bg: COLOR2,
705 ) where
706 COLOR: Into<RGBA>,
707 COLOR2: Into<RGBA>,
708 X: TryInto<i32>,
709 Y: TryInto<i32>,
710 W: TryInto<i32>,
711 N: TryInto<i32>,
712 MAX: TryInto<i32>,
713 {
714 BACKEND_INTERNAL.lock().consoles[self.active_console]
715 .console
716 .draw_bar_horizontal(
717 x.try_into().ok().expect("Must be i32 convertible"),
718 y.try_into().ok().expect("Must be i32 convertible"),
719 width.try_into().ok().expect("Must be i32 convertible"),
720 n.try_into().ok().expect("Must be i32 convertible"),
721 max.try_into().ok().expect("Must be i32 convertible"),
722 fg.into(),
723 bg.into(),
724 );
725 }
726
727 #[allow(clippy::too_many_arguments)]
729 pub fn draw_bar_vertical<COLOR, COLOR2, X, Y, H, N, MAX>(
730 &mut self,
731 x: X,
732 y: Y,
733 height: H,
734 n: N,
735 max: MAX,
736 fg: COLOR,
737 bg: COLOR2,
738 ) where
739 COLOR: Into<RGBA>,
740 COLOR2: Into<RGBA>,
741 X: TryInto<i32>,
742 Y: TryInto<i32>,
743 H: TryInto<i32>,
744 N: TryInto<i32>,
745 MAX: TryInto<i32>,
746 {
747 BACKEND_INTERNAL.lock().consoles[self.active_console]
748 .console
749 .draw_bar_vertical(
750 x.try_into().ok().expect("Must be i32 convertible"),
751 y.try_into().ok().expect("Must be i32 convertible"),
752 height.try_into().ok().expect("Must be i32 convertible"),
753 n.try_into().ok().expect("Must be i32 convertible"),
754 max.try_into().ok().expect("Must be i32 convertible"),
755 fg.into(),
756 bg.into(),
757 );
758 }
759
760 pub fn fill_region<COLOR, COLOR2, GLYPH>(
762 &mut self,
763 target: Rect,
764 glyph: GLYPH,
765 fg: COLOR,
766 bg: COLOR2,
767 ) where
768 COLOR: Into<RGBA>,
769 COLOR2: Into<RGBA>,
770 GLYPH: TryInto<FontCharType>,
771 {
772 BACKEND_INTERNAL.lock().consoles[self.active_console]
773 .console
774 .fill_region(target, glyph.try_into().ok().unwrap(), fg.into(), bg.into());
775 }
776
777 pub fn print_centered<S, Y>(&mut self, y: Y, text: S)
779 where
780 S: ToString,
781 Y: TryInto<i32>,
782 {
783 BACKEND_INTERNAL.lock().consoles[self.active_console]
784 .console
785 .print_centered(
786 y.try_into().ok().expect("Must be i32 convertible"),
787 &text.to_string(),
788 );
789 }
790
791 pub fn print_color_centered<S, COLOR, COLOR2, Y>(
793 &mut self,
794 y: Y,
795 fg: COLOR,
796 bg: COLOR2,
797 text: S,
798 ) where
799 S: ToString,
800 COLOR: Into<RGBA>,
801 COLOR2: Into<RGBA>,
802 Y: TryInto<i32>,
803 {
804 BACKEND_INTERNAL.lock().consoles[self.active_console]
805 .console
806 .print_color_centered(
807 y.try_into().ok().expect("Must be i32 convertible"),
808 fg.into(),
809 bg.into(),
810 &text.to_string(),
811 );
812 }
813
814 pub fn print_centered_at<S, X, Y>(&mut self, x: X, y: Y, text: S)
816 where
817 S: ToString,
818 X: TryInto<i32>,
819 Y: TryInto<i32>,
820 {
821 BACKEND_INTERNAL.lock().consoles[self.active_console]
822 .console
823 .print_centered_at(
824 x.try_into().ok().expect("Must be i32 convertible"),
825 y.try_into().ok().expect("Must be i32 convertible"),
826 &text.to_string(),
827 );
828 }
829
830 pub fn print_color_centered_at<S, COLOR, COLOR2, X, Y>(
832 &mut self,
833 x: X,
834 y: Y,
835 fg: COLOR,
836 bg: COLOR2,
837 text: S,
838 ) where
839 S: ToString,
840 COLOR: Into<RGBA>,
841 COLOR2: Into<RGBA>,
842 X: TryInto<i32>,
843 Y: TryInto<i32>,
844 {
845 BACKEND_INTERNAL.lock().consoles[self.active_console]
846 .console
847 .print_color_centered_at(
848 x.try_into().ok().expect("Must be i32 convertible"),
849 y.try_into().ok().expect("Must be i32 convertible"),
850 fg.into(),
851 bg.into(),
852 &text.to_string(),
853 );
854 }
855
856 pub fn print_right<S, X, Y>(&mut self, x: X, y: Y, text: S)
858 where
859 S: ToString,
860 X: TryInto<i32>,
861 Y: TryInto<i32>,
862 {
863 BACKEND_INTERNAL.lock().consoles[self.active_console]
864 .console
865 .print_right(
866 x.try_into().ok().expect("Must be i32 convertible"),
867 y.try_into().ok().expect("Must be i32 convertible"),
868 &text.to_string(),
869 );
870 }
871
872 pub fn print_color_right<S, COLOR, COLOR2, X, Y>(
874 &mut self,
875 x: X,
876 y: Y,
877 fg: COLOR,
878 bg: COLOR2,
879 text: S,
880 ) where
881 S: ToString,
882 COLOR: Into<RGBA>,
883 COLOR2: Into<RGBA>,
884 X: TryInto<i32>,
885 Y: TryInto<i32>,
886 {
887 BACKEND_INTERNAL.lock().consoles[self.active_console]
888 .console
889 .print_color_right(
890 x.try_into().ok().expect("Must be i32 convertible"),
891 y.try_into().ok().expect("Must be i32 convertible"),
892 fg.into(),
893 bg.into(),
894 &text.to_string(),
895 );
896 }
897
898 pub fn printer<S, X, Y>(
903 &mut self,
904 x: X,
905 y: Y,
906 output: S,
907 align: TextAlign,
908 background: Option<RGBA>,
909 ) where
910 S: ToString,
911 X: TryInto<i32>,
912 Y: TryInto<i32>,
913 {
914 BACKEND_INTERNAL.lock().consoles[self.active_console]
915 .console
916 .printer(
917 x.try_into().ok().expect("Must be i32 convertible"),
918 y.try_into().ok().expect("Must be i32 convertible"),
919 &output.to_string(),
920 align,
921 background,
922 );
923 }
924
925 pub fn to_xp_layer(&self) -> XpLayer {
927 BACKEND_INTERNAL.lock().consoles[self.active_console]
928 .console
929 .to_xp_layer()
930 }
931
932 pub fn set_offset(&mut self, x: f32, y: f32) {
934 BACKEND_INTERNAL.lock().consoles[self.active_console]
935 .console
936 .set_offset(x, y);
937 }
938
939 pub fn set_scale(&mut self, scale: f32, center_x: i32, center_y: i32) {
941 BACKEND_INTERNAL.lock().consoles[self.active_console]
942 .console
943 .set_scale(scale, center_x, center_y);
944 }
945
946 pub fn get_scale(&self) -> (f32, i32, i32) {
948 BACKEND_INTERNAL.lock().consoles[self.active_console]
949 .console
950 .get_scale()
951 }
952
953 pub fn set_clipping(&mut self, clipping: Option<Rect>) {
956 BACKEND_INTERNAL.lock().consoles[self.active_console]
957 .console
958 .set_clipping(clipping);
959 }
960
961 pub fn get_clipping(&self) -> Option<Rect> {
963 BACKEND_INTERNAL.lock().consoles[self.active_console]
964 .console
965 .get_clipping()
966 }
967
968 pub fn set_all_fg_alpha(&mut self, alpha: f32) {
970 BACKEND_INTERNAL.lock().consoles[self.active_console]
971 .console
972 .set_all_fg_alpha(alpha);
973 }
974
975 pub fn set_all_bg_alpha(&mut self, alpha: f32) {
977 BACKEND_INTERNAL.lock().consoles[self.active_console]
978 .console
979 .set_all_bg_alpha(alpha);
980 }
981
982 pub fn set_all_alpha(&mut self, fg: f32, bg: f32) {
984 BACKEND_INTERNAL.lock().consoles[self.active_console]
985 .console
986 .set_all_alpha(fg, bg);
987 }
988
989 pub fn set_translation_mode(&mut self, console: usize, translation: CharacterTranslationMode) {
991 BACKEND_INTERNAL.lock().consoles[console]
992 .console
993 .set_translation_mode(translation)
994 }
995
996 #[cfg(any(feature = "opengl", feature = "webgpu"))]
997 pub fn set_active_font(&mut self, font_index: usize, resize_to_natural_dimensions: bool) {
999 let mut be = BACKEND_INTERNAL.lock();
1000 if font_index > be.fonts.len() {
1001 panic!("Font index out of bounds.");
1002 }
1003 let old_font_size = be.fonts[be.consoles[self.active_console].font_index].tile_size;
1004 let new_font_size = be.fonts[font_index].tile_size;
1005 be.consoles[self.active_console].font_index = font_index;
1006
1007 if old_font_size != new_font_size && resize_to_natural_dimensions {
1008 let x_size = self.original_width_pixels / new_font_size.0;
1009 let y_size = self.original_height_pixels / new_font_size.1;
1010
1011 be.consoles[self.active_console]
1012 .console
1013 .set_char_size(x_size, y_size);
1014 }
1015 }
1016
1017 #[cfg(all(
1018 any(feature = "opengl", feature = "webgpu"),
1019 not(target_arch = "wasm32")
1020 ))]
1021 pub fn set_char_size(&mut self, width: u32, height: u32) {
1023 BACKEND_INTERNAL.lock().consoles[self.active_console]
1024 .console
1025 .set_char_size(width, height);
1026 }
1027
1028 #[cfg(all(
1029 any(feature = "opengl", feature = "webgpu"),
1030 not(target_arch = "wasm32")
1031 ))]
1032 pub fn set_char_size_and_resize_window(&mut self, _width: u32, _height: u32) {
1034 }
1043
1044 #[cfg(all(
1046 any(feature = "opengl", feature = "webgpu"),
1047 not(target_arch = "wasm32")
1048 ))]
1049 pub fn screenshot<S: ToString>(&mut self, filename: S) {
1050 BACKEND.lock().request_screenshot = Some(filename.to_string());
1051 }
1052
1053 #[cfg(not(all(
1055 any(feature = "opengl", feature = "webgpu"),
1056 not(target_arch = "wasm32")
1057 )))]
1058 pub fn screenshot<S: ToString>(&mut self, _filename: S) {
1059 }
1061
1062 #[cfg(any(feature = "opengl", feature = "webgpu"))]
1064 pub fn register_spritesheet(&mut self, ss: SpriteSheet) -> usize {
1065 let mut bi = BACKEND_INTERNAL.lock();
1066 let id = bi.sprite_sheets.len();
1067 bi.sprite_sheets.push(ss);
1068 id
1069 }
1070
1071 #[cfg(any(feature = "opengl", feature = "webgpu"))]
1073 pub fn add_sprite(&mut self, destination: Rect, z_order: i32, tint: RGBA, index: usize) {
1074 let mut bi = BACKEND_INTERNAL.lock();
1075 let as_any = bi.consoles[self.active_console].console.as_any_mut();
1076 if let Some(cons) = as_any.downcast_mut::<SpriteConsole>() {
1077 cons.render_sprite(RenderSprite {
1078 destination,
1079 z_order,
1080 tint,
1081 index,
1082 });
1083 }
1084 }
1085}
1086
1087pub fn main_loop<GS: GameState>(bterm: BTerm, gamestate: GS) -> BResult<()> {
1089 super::hal::main_loop(bterm, gamestate)?;
1090 Ok(())
1091}
1092
1093pub fn letter_to_option(key: VirtualKeyCode) -> i32 {
1095 match key {
1096 VirtualKeyCode::A => 0,
1097 VirtualKeyCode::B => 1,
1098 VirtualKeyCode::C => 2,
1099 VirtualKeyCode::D => 3,
1100 VirtualKeyCode::E => 4,
1101 VirtualKeyCode::F => 5,
1102 VirtualKeyCode::G => 6,
1103 VirtualKeyCode::H => 7,
1104 VirtualKeyCode::I => 8,
1105 VirtualKeyCode::J => 9,
1106 VirtualKeyCode::K => 10,
1107 VirtualKeyCode::L => 11,
1108 VirtualKeyCode::M => 12,
1109 VirtualKeyCode::N => 13,
1110 VirtualKeyCode::O => 14,
1111 VirtualKeyCode::P => 15,
1112 VirtualKeyCode::Q => 16,
1113 VirtualKeyCode::R => 17,
1114 VirtualKeyCode::S => 18,
1115 VirtualKeyCode::T => 19,
1116 VirtualKeyCode::U => 20,
1117 VirtualKeyCode::V => 21,
1118 VirtualKeyCode::W => 22,
1119 VirtualKeyCode::X => 23,
1120 VirtualKeyCode::Y => 24,
1121 VirtualKeyCode::Z => 25,
1122 _ => -1,
1123 }
1124}
1125
1126fn iclamp(val: i32, min: i32, max: i32) -> i32 {
1128 i32::max(min, i32::min(val, max))
1129}
1130
1131#[cfg(test)]
1132mod tests {
1133 use super::iclamp;
1134
1135 #[test]
1136 fn test_iclamp() {
1138 assert!(iclamp(1, 0, 2) == 1);
1139 assert!(iclamp(5, 0, 2) == 2);
1140 assert!(iclamp(-5, 0, 2) == 0);
1141 }
1142}