fixes all around gfx and prometeu render
Some checks failed
Intrepid/Prometeu/Runtime/pipeline/head There was a failure building this commit
Some checks failed
Intrepid/Prometeu/Runtime/pipeline/head There was a failure building this commit
This commit is contained in:
parent
74ef393294
commit
eb398846a0
@ -1,4 +1,3 @@
|
||||
use crate::gfx_overlay::{DeferredGfxOverlay, OverlayCommand};
|
||||
use crate::memory_banks::GlyphBankPoolAccess;
|
||||
use prometeu_hal::GfxBridge;
|
||||
use prometeu_hal::color::Color;
|
||||
@ -34,9 +33,7 @@ pub enum BlendMode {
|
||||
///
|
||||
/// `Gfx` owns the framebuffer backend and the canonical game-frame raster path
|
||||
/// consumed by `FrameComposer`. That canonical path covers scene composition,
|
||||
/// sprite composition, and fades. Public `gfx.*` primitives remain valid, but
|
||||
/// they do not define the canonical game composition contract; they belong to a
|
||||
/// separate final overlay/debug stage.
|
||||
/// sprite composition, and fades. Public `gfx.*` primitives remain valid.
|
||||
pub struct Gfx {
|
||||
/// Width of the internal framebuffer in pixels.
|
||||
w: usize,
|
||||
@ -45,15 +42,10 @@ pub struct Gfx {
|
||||
/// Front buffer: the "VRAM" currently being displayed by the Host window.
|
||||
front: Vec<u16>,
|
||||
/// Back buffer: the working buffer where canonical game frames are composed
|
||||
/// before any final overlay/debug drain.
|
||||
back: Vec<u16>,
|
||||
|
||||
/// Shared access to graphical memory banks (tiles and palettes).
|
||||
pub glyph_banks: Arc<dyn GlyphBankPoolAccess>,
|
||||
/// Deferred overlay/debug capture kept separate from canonical game composition.
|
||||
overlay: DeferredGfxOverlay,
|
||||
/// Internal guard to replay deferred overlay commands without re-enqueueing them.
|
||||
is_draining_overlay: bool,
|
||||
/// Hardware sprite list (512 slots). Equivalent to OAM (Object Attribute Memory).
|
||||
pub sprites: [Sprite; 512],
|
||||
|
||||
@ -298,8 +290,6 @@ impl Gfx {
|
||||
front: vec![0; len],
|
||||
back: vec![0; len],
|
||||
glyph_banks,
|
||||
overlay: DeferredGfxOverlay::default(),
|
||||
is_draining_overlay: false,
|
||||
sprites: [EMPTY_SPRITE; 512],
|
||||
sprite_count: 0,
|
||||
scene_fade_level: 31,
|
||||
@ -319,42 +309,6 @@ impl Gfx {
|
||||
(self.w, self.h)
|
||||
}
|
||||
|
||||
pub fn begin_overlay_frame(&mut self) {
|
||||
self.overlay.begin_frame();
|
||||
}
|
||||
|
||||
pub fn overlay(&self) -> &DeferredGfxOverlay {
|
||||
&self.overlay
|
||||
}
|
||||
|
||||
pub fn drain_overlay_debug(&mut self) {
|
||||
let commands = self.overlay.take_commands();
|
||||
self.is_draining_overlay = true;
|
||||
|
||||
for command in commands {
|
||||
match command {
|
||||
OverlayCommand::FillRectBlend { x, y, w, h, color, mode } => {
|
||||
self.fill_rect_blend(x, y, w, h, color, mode)
|
||||
}
|
||||
OverlayCommand::DrawLine { x0, y0, x1, y1, color } => {
|
||||
self.draw_line(x0, y0, x1, y1, color)
|
||||
}
|
||||
OverlayCommand::DrawCircle { x, y, r, color } => self.draw_circle(x, y, r, color),
|
||||
OverlayCommand::DrawDisc { x, y, r, border_color, fill_color } => {
|
||||
self.draw_disc(x, y, r, border_color, fill_color)
|
||||
}
|
||||
OverlayCommand::DrawSquare { x, y, w, h, border_color, fill_color } => {
|
||||
self.draw_square(x, y, w, h, border_color, fill_color)
|
||||
}
|
||||
OverlayCommand::DrawText { x, y, text, color } => {
|
||||
self.draw_text(x, y, &text, color)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.is_draining_overlay = false;
|
||||
}
|
||||
|
||||
/// The buffer that the host should display (RGB565).
|
||||
pub fn front_buffer(&self) -> &[u16] {
|
||||
&self.front
|
||||
@ -374,10 +328,6 @@ impl Gfx {
|
||||
color: Color,
|
||||
mode: BlendMode,
|
||||
) {
|
||||
if !self.is_draining_overlay {
|
||||
self.overlay.push(OverlayCommand::FillRectBlend { x, y, w, h, color, mode });
|
||||
return;
|
||||
}
|
||||
if color == Color::COLOR_KEY {
|
||||
return;
|
||||
}
|
||||
@ -419,10 +369,6 @@ impl Gfx {
|
||||
|
||||
/// Draws a line between two points using Bresenham's algorithm.
|
||||
pub fn draw_line(&mut self, x0: i32, y0: i32, x1: i32, y1: i32, color: Color) {
|
||||
if !self.is_draining_overlay {
|
||||
self.overlay.push(OverlayCommand::DrawLine { x0, y0, x1, y1, color });
|
||||
return;
|
||||
}
|
||||
if color == Color::COLOR_KEY {
|
||||
return;
|
||||
}
|
||||
@ -455,10 +401,6 @@ impl Gfx {
|
||||
|
||||
/// Draws a circle outline using Midpoint Circle Algorithm.
|
||||
pub fn draw_circle(&mut self, xc: i32, yc: i32, r: i32, color: Color) {
|
||||
if !self.is_draining_overlay {
|
||||
self.overlay.push(OverlayCommand::DrawCircle { x: xc, y: yc, r, color });
|
||||
return;
|
||||
}
|
||||
if color == Color::COLOR_KEY {
|
||||
return;
|
||||
}
|
||||
@ -527,10 +469,6 @@ impl Gfx {
|
||||
|
||||
/// Draws a disc (filled circle with border).
|
||||
pub fn draw_disc(&mut self, x: i32, y: i32, r: i32, border_color: Color, fill_color: Color) {
|
||||
if !self.is_draining_overlay {
|
||||
self.overlay.push(OverlayCommand::DrawDisc { x, y, r, border_color, fill_color });
|
||||
return;
|
||||
}
|
||||
self.fill_circle(x, y, r, fill_color);
|
||||
self.draw_circle(x, y, r, border_color);
|
||||
}
|
||||
@ -560,10 +498,6 @@ impl Gfx {
|
||||
border_color: Color,
|
||||
fill_color: Color,
|
||||
) {
|
||||
if !self.is_draining_overlay {
|
||||
self.overlay.push(OverlayCommand::DrawSquare { x, y, w, h, border_color, fill_color });
|
||||
return;
|
||||
}
|
||||
self.fill_rect(x, y, w, h, fill_color);
|
||||
self.draw_rect(x, y, w, h, border_color);
|
||||
}
|
||||
@ -863,10 +797,6 @@ impl Gfx {
|
||||
}
|
||||
|
||||
pub fn draw_text(&mut self, x: i32, y: i32, text: &str, color: Color) {
|
||||
if !self.is_draining_overlay {
|
||||
self.overlay.push(OverlayCommand::DrawText { x, y, text: text.to_string(), color });
|
||||
return;
|
||||
}
|
||||
let mut cx = x;
|
||||
for c in text.chars() {
|
||||
self.draw_char(cx, y, c, color);
|
||||
@ -961,9 +891,7 @@ fn blend_rgb565(dst: u16, src: u16, mode: BlendMode) -> u16 {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::FrameComposer;
|
||||
use crate::asset::GlyphAssetSlotIndex;
|
||||
use crate::memory_banks::{GlyphBankPoolInstaller, MemoryBanks, SceneBankPoolAccess};
|
||||
use crate::memory_banks::{GlyphBankPoolInstaller, MemoryBanks};
|
||||
use prometeu_hal::asset::AssetId;
|
||||
use prometeu_hal::glyph_bank::TileSize;
|
||||
use prometeu_hal::scene_bank::SceneBank;
|
||||
@ -1047,16 +975,6 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
fn make_glyph_slot_index(bindings: &[(AssetId, usize)]) -> GlyphAssetSlotIndex {
|
||||
let index = GlyphAssetSlotIndex::new();
|
||||
let mut slots = [None; 16];
|
||||
for (asset_id, slot) in bindings {
|
||||
slots[*slot] = Some(*asset_id);
|
||||
}
|
||||
index.rebuild_from_slots(&slots);
|
||||
index
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_draw_pixel() {
|
||||
let banks = Arc::new(MemoryBanks::new());
|
||||
@ -1073,12 +991,9 @@ mod tests {
|
||||
fn test_draw_line() {
|
||||
let banks = Arc::new(MemoryBanks::new());
|
||||
let mut gfx = Gfx::new(10, 10, banks);
|
||||
gfx.begin_overlay_frame();
|
||||
gfx.draw_line(0, 0, 9, 9, Color::WHITE);
|
||||
gfx.drain_overlay_debug();
|
||||
assert_eq!(gfx.back[0], Color::WHITE.0);
|
||||
assert_eq!(gfx.back[9 * 10 + 9], Color::WHITE.0);
|
||||
assert_eq!(gfx.overlay().command_count(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -1104,56 +1019,9 @@ mod tests {
|
||||
fn test_draw_square() {
|
||||
let banks = Arc::new(MemoryBanks::new());
|
||||
let mut gfx = Gfx::new(10, 10, banks);
|
||||
gfx.begin_overlay_frame();
|
||||
gfx.draw_square(2, 2, 6, 6, Color::WHITE, Color::BLACK);
|
||||
gfx.drain_overlay_debug();
|
||||
assert_eq!(gfx.back[2 * 10 + 2], Color::WHITE.0);
|
||||
assert_eq!(gfx.back[3 * 10 + 3], Color::BLACK.0);
|
||||
assert_eq!(gfx.overlay().command_count(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn draw_text_captures_overlay_command() {
|
||||
let banks = Arc::new(MemoryBanks::new());
|
||||
let mut gfx = Gfx::new(32, 18, banks);
|
||||
gfx.begin_overlay_frame();
|
||||
|
||||
gfx.draw_text(4, 5, "HUD", Color::WHITE);
|
||||
|
||||
assert_eq!(
|
||||
gfx.overlay().commands(),
|
||||
&[OverlayCommand::DrawText { x: 4, y: 5, text: "HUD".into(), color: Color::WHITE }]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn overlay_state_is_separate_from_frame_composer_sprite_state() {
|
||||
let banks = Arc::new(MemoryBanks::new());
|
||||
let mut gfx = Gfx::new(32, 18, Arc::clone(&banks) as Arc<dyn GlyphBankPoolAccess>);
|
||||
let mut frame_composer = FrameComposer::new(
|
||||
32,
|
||||
18,
|
||||
Arc::clone(&banks) as Arc<dyn SceneBankPoolAccess>,
|
||||
make_glyph_slot_index(&[]),
|
||||
);
|
||||
|
||||
gfx.begin_overlay_frame();
|
||||
frame_composer.begin_frame();
|
||||
frame_composer.emit_sprite(Sprite {
|
||||
glyph: Glyph { glyph_id: 0, palette_id: 0 },
|
||||
x: 1,
|
||||
y: 2,
|
||||
layer: 0,
|
||||
bank_id: 0,
|
||||
active: true,
|
||||
flip_x: false,
|
||||
flip_y: false,
|
||||
priority: 0,
|
||||
});
|
||||
gfx.draw_text(1, 1, "X", Color::WHITE);
|
||||
|
||||
assert_eq!(frame_composer.sprite_controller().sprite_count(), 1);
|
||||
assert_eq!(gfx.overlay().command_count(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@ -1,39 +0,0 @@
|
||||
use crate::gfx::BlendMode;
|
||||
use prometeu_hal::color::Color;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum OverlayCommand {
|
||||
FillRectBlend { x: i32, y: i32, w: i32, h: i32, color: Color, mode: BlendMode },
|
||||
DrawLine { x0: i32, y0: i32, x1: i32, y1: i32, color: Color },
|
||||
DrawCircle { x: i32, y: i32, r: i32, color: Color },
|
||||
DrawDisc { x: i32, y: i32, r: i32, border_color: Color, fill_color: Color },
|
||||
DrawSquare { x: i32, y: i32, w: i32, h: i32, border_color: Color, fill_color: Color },
|
||||
DrawText { x: i32, y: i32, text: String, color: Color },
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct DeferredGfxOverlay {
|
||||
commands: Vec<OverlayCommand>,
|
||||
}
|
||||
|
||||
impl DeferredGfxOverlay {
|
||||
pub fn begin_frame(&mut self) {
|
||||
self.commands.clear();
|
||||
}
|
||||
|
||||
pub fn push(&mut self, command: OverlayCommand) {
|
||||
self.commands.push(command);
|
||||
}
|
||||
|
||||
pub fn commands(&self) -> &[OverlayCommand] {
|
||||
&self.commands
|
||||
}
|
||||
|
||||
pub fn command_count(&self) -> usize {
|
||||
self.commands.len()
|
||||
}
|
||||
|
||||
pub fn take_commands(&mut self) -> Vec<OverlayCommand> {
|
||||
std::mem::take(&mut self.commands)
|
||||
}
|
||||
}
|
||||
@ -48,7 +48,6 @@ impl Default for Hardware {
|
||||
|
||||
impl HardwareBridge for Hardware {
|
||||
fn begin_frame(&mut self) {
|
||||
self.gfx.begin_overlay_frame();
|
||||
self.frame_composer.begin_frame();
|
||||
}
|
||||
|
||||
@ -70,7 +69,6 @@ impl HardwareBridge for Hardware {
|
||||
|
||||
fn render_frame(&mut self) {
|
||||
self.frame_composer.render_frame(&mut self.gfx);
|
||||
self.gfx.drain_overlay_debug();
|
||||
}
|
||||
|
||||
fn has_glyph_bank(&self, bank_id: usize) -> bool {
|
||||
|
||||
@ -2,7 +2,6 @@ mod asset;
|
||||
mod audio;
|
||||
mod frame_composer;
|
||||
mod gfx;
|
||||
mod gfx_overlay;
|
||||
pub mod hardware;
|
||||
mod memory_banks;
|
||||
mod pad;
|
||||
@ -12,7 +11,6 @@ pub use crate::asset::AssetManager;
|
||||
pub use crate::audio::{Audio, AudioCommand, Channel, MAX_CHANNELS, OUTPUT_SAMPLE_RATE};
|
||||
pub use crate::frame_composer::{FrameComposer, SceneStatus, SpriteController};
|
||||
pub use crate::gfx::Gfx;
|
||||
pub use crate::gfx_overlay::{DeferredGfxOverlay, OverlayCommand};
|
||||
pub use crate::memory_banks::{
|
||||
GlyphBankPoolAccess, GlyphBankPoolInstaller, MemoryBanks, SceneBankPoolAccess,
|
||||
SceneBankPoolInstaller, SoundBankPoolAccess, SoundBankPoolInstaller,
|
||||
|
||||
@ -11,6 +11,7 @@ pub struct HubHomeStep;
|
||||
impl HubHomeStep {
|
||||
pub fn on_enter(&mut self, ctx: &mut PrometeuContext) {
|
||||
ctx.os.log(LogLevel::Info, LogSource::Hub, 0, "Entering HubHome".to_string());
|
||||
ctx.os.windows().remove_all_windows();
|
||||
ctx.hub.init();
|
||||
}
|
||||
|
||||
|
||||
@ -67,12 +67,7 @@ impl ShellRunningStep {
|
||||
}
|
||||
|
||||
if outcome.action == Some(SystemProfileAction::CloseShell) {
|
||||
if let Err(error) = ctx.os.lifecycle().close_task(self.task_id) {
|
||||
ctx.os.error(
|
||||
LogSource::Hub,
|
||||
format!("Failed to close shell task {:?}: {:?}", self.task_id, error),
|
||||
);
|
||||
}
|
||||
self.close_current_shell(ctx);
|
||||
return Some(FirmwareState::HubHome(HubHomeStep));
|
||||
}
|
||||
|
||||
@ -84,18 +79,22 @@ impl ShellRunningStep {
|
||||
self.task_id,
|
||||
),
|
||||
);
|
||||
if let Err(error) = ctx.os.lifecycle().close_task(self.task_id) {
|
||||
ctx.os.error(
|
||||
LogSource::Hub,
|
||||
format!("Failed to close shell task {:?}: {:?}", self.task_id, error),
|
||||
);
|
||||
}
|
||||
self.close_current_shell(ctx);
|
||||
return Some(FirmwareState::HubHome(HubHomeStep));
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
fn close_current_shell(&mut self, ctx: &mut PrometeuContext) {
|
||||
if let Err(error) = ctx.os.lifecycle().close_task(self.task_id) {
|
||||
ctx.os.error(
|
||||
LogSource::Hub,
|
||||
format!("Failed to close shell task {:?}: {:?}", self.task_id, error),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn on_exit(&mut self, ctx: &mut PrometeuContext) {
|
||||
ctx.os.info(LogSource::Hub, "Exiting ShellRunning".to_string());
|
||||
}
|
||||
|
||||
@ -101,7 +101,7 @@ impl PrometeuHub {
|
||||
os: &mut SystemOS,
|
||||
hw: &mut dyn HardwareBridge,
|
||||
) -> Option<SystemProfileAction> {
|
||||
hw.gfx_mut().clear(Color::BLACK);
|
||||
hw.gfx_mut().clear(Color::INDIGO);
|
||||
|
||||
let in_shell = os.windows().focused_window().is_some();
|
||||
|
||||
|
||||
@ -636,82 +636,6 @@ fn tick_renders_scene_through_public_composer_syscalls() {
|
||||
assert_eq!(hardware.gfx.front_buffer()[0], Color::BLUE.raw());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tick_draw_text_survives_scene_backed_frame_composition() {
|
||||
let mut runtime = VirtualMachineRuntime::new(None);
|
||||
let mut log_service = LogService::new(4096);
|
||||
let mut fs = VirtualFS::new();
|
||||
let mut fs_state = FsState::Unmounted;
|
||||
let mut memcard = MemcardService::new();
|
||||
let mut open_files: HashMap<u32, String> = HashMap::new();
|
||||
let mut next_handle = 1;
|
||||
let mut vm = VirtualMachine::default();
|
||||
let signals = InputSignals::default();
|
||||
let code = assemble(
|
||||
"PUSH_I32 0\nHOSTCALL 0\nPOP_N 1\n\
|
||||
PUSH_I32 0\nPUSH_I32 0\nHOSTCALL 1\n\
|
||||
PUSH_I32 0\nPUSH_I32 0\nPUSH_CONST 0\nPUSH_I32 65535\nHOSTCALL 2\n\
|
||||
FRAME_SYNC\nHALT",
|
||||
)
|
||||
.expect("assemble");
|
||||
let program = serialized_single_function_module_with_consts(
|
||||
code,
|
||||
vec![ConstantPoolEntry::String("I".into())],
|
||||
vec![
|
||||
SyscallDecl {
|
||||
module: "composer".into(),
|
||||
name: "bind_scene".into(),
|
||||
version: 1,
|
||||
arg_slots: 1,
|
||||
ret_slots: 1,
|
||||
},
|
||||
SyscallDecl {
|
||||
module: "composer".into(),
|
||||
name: "set_camera".into(),
|
||||
version: 1,
|
||||
arg_slots: 2,
|
||||
ret_slots: 0,
|
||||
},
|
||||
SyscallDecl {
|
||||
module: "gfx".into(),
|
||||
name: "draw_text".into(),
|
||||
version: 1,
|
||||
arg_slots: 4,
|
||||
ret_slots: 0,
|
||||
},
|
||||
],
|
||||
);
|
||||
let cartridge = cartridge_with_program(program, caps::GFX);
|
||||
|
||||
let banks = Arc::new(MemoryBanks::new());
|
||||
banks.install_glyph_bank(0, Arc::new(runtime_test_glyph_bank(TileSize::Size8, 2, Color::BLUE)));
|
||||
banks.install_scene_bank(0, Arc::new(runtime_test_scene(0, 2, TileSize::Size8)));
|
||||
let mut hardware = Hardware::new_with_memory_banks(Arc::clone(&banks));
|
||||
let glyph_slot_index = hardware.assets.glyph_asset_slot_index();
|
||||
let mut slots = [None; 16];
|
||||
slots[0] = Some(0);
|
||||
glyph_slot_index.rebuild_from_slots(&slots);
|
||||
hardware.gfx.scene_fade_level = 31;
|
||||
hardware.gfx.hud_fade_level = 31;
|
||||
|
||||
runtime.initialize_vm(&mut log_service, &mut vm, &cartridge).expect("runtime must initialize");
|
||||
let report = runtime.tick(
|
||||
&mut log_service,
|
||||
&mut fs,
|
||||
&mut fs_state,
|
||||
&mut memcard,
|
||||
&mut open_files,
|
||||
&mut next_handle,
|
||||
&mut vm,
|
||||
&signals,
|
||||
&mut hardware,
|
||||
);
|
||||
|
||||
assert!(report.is_none(), "scene-backed overlay text must not crash");
|
||||
hardware.gfx.present();
|
||||
assert_eq!(hardware.gfx.front_buffer()[0], Color::WHITE.raw());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tick_draw_text_survives_no_scene_frame_path() {
|
||||
let mut runtime = VirtualMachineRuntime::new(None);
|
||||
|
||||
@ -4,6 +4,50 @@ use winit::event::{ElementState, MouseButton, WindowEvent};
|
||||
use winit::keyboard::{KeyCode, PhysicalKey};
|
||||
use winit::window::Window;
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
struct FbViewport {
|
||||
pub x: f32,
|
||||
pub y: f32,
|
||||
pub w: f32,
|
||||
pub h: f32,
|
||||
pub scale: f32,
|
||||
}
|
||||
|
||||
impl FbViewport {
|
||||
fn framebuffer_viewport(window: &Window) -> Option<FbViewport> {
|
||||
let size = window.inner_size();
|
||||
|
||||
let win_w = size.width as f32;
|
||||
let win_h = size.height as f32;
|
||||
|
||||
if win_w <= 0.0 || win_h <= 0.0 {
|
||||
return None;
|
||||
}
|
||||
|
||||
let fb_w = Hardware::W as f32;
|
||||
let fb_h = Hardware::H as f32;
|
||||
|
||||
let scale_x = (win_w / fb_w).floor();
|
||||
let scale_y = (win_h / fb_h).floor();
|
||||
|
||||
let scale = scale_x.min(scale_y).max(1.0);
|
||||
|
||||
let viewport_w = fb_w * scale;
|
||||
let viewport_h = fb_h * scale;
|
||||
|
||||
let viewport_x = ((win_w - viewport_w) * 0.5).floor();
|
||||
let viewport_y = ((win_h - viewport_h) * 0.5).floor();
|
||||
|
||||
Some(FbViewport {
|
||||
x: viewport_x,
|
||||
y: viewport_y,
|
||||
w: viewport_w,
|
||||
h: viewport_h,
|
||||
scale,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub struct HostInputHandler {
|
||||
pub signals: InputSignals,
|
||||
}
|
||||
@ -49,9 +93,10 @@ impl HostInputHandler {
|
||||
}
|
||||
|
||||
WindowEvent::CursorMoved { position, .. } => {
|
||||
let v = window_to_fb(position.x as f32, position.y as f32, window);
|
||||
self.signals.x_pos = v.0;
|
||||
self.signals.y_pos = v.1;
|
||||
if let Some((x, y)) = window_to_fb(position.x as f32, position.y as f32, window) {
|
||||
self.signals.x_pos = x;
|
||||
self.signals.y_pos = y;
|
||||
}
|
||||
}
|
||||
|
||||
WindowEvent::MouseInput { state, button, .. } if *button == MouseButton::Left => {
|
||||
@ -67,16 +112,24 @@ impl HostInputHandler {
|
||||
|
||||
_ => {}
|
||||
}
|
||||
|
||||
fn window_to_fb(wx: f32, wy: f32, window: &Window) -> Option<(i32, i32)> {
|
||||
let viewport = FbViewport::framebuffer_viewport(window)?;
|
||||
|
||||
let local_x = wx - viewport.x;
|
||||
let local_y = wy - viewport.y;
|
||||
|
||||
if local_x < 0.0 || local_y < 0.0 || local_x >= viewport.w || local_y >= viewport.h {
|
||||
return None;
|
||||
}
|
||||
|
||||
let fb_x = (local_x / viewport.scale).floor() as i32;
|
||||
let fb_y = (local_y / viewport.scale).floor() as i32;
|
||||
|
||||
Some((
|
||||
fb_x.clamp(0, Hardware::W as i32 - 1),
|
||||
fb_y.clamp(0, Hardware::H as i32 - 1),
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn window_to_fb(wx: f32, wy: f32, window: &Window) -> (i32, i32) {
|
||||
let size = window.inner_size();
|
||||
let fb_w = Hardware::W as f32;
|
||||
let fb_h = Hardware::H as f32;
|
||||
|
||||
let x = (wx * fb_w / size.width as f32).floor() as i32;
|
||||
let y = (wy * fb_h / size.height as f32).floor() as i32;
|
||||
|
||||
(x.clamp(0, Hardware::W as i32 - 1), y.clamp(0, Hardware::H as i32 - 1))
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user