Add 4 window pixels of padding around the screen contents (#210)

* Add 4 window pixels of padding around the screen contents

Move the canvases from emulated-pixel to window-pixel coordinates and
draw the screen texture at an explicit 3x rect inset by PAD, so the
contents no longer touch the window border. The dst rect stays an exact
integer 3x of the texture, keeping the nearest-neighbor crispness.

In the ][+ this also stops the status-bar mode from stretching the
screen over the bar strip before overdrawing it: the screen rect is now
mode-independent and the bar sits below it inside the side padding.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* Make the window padding configurable via EWM_WINDOW_PADDING

Defaults to 4; missing or unparsable values fall back to the default.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Stefan ArentzandClaude Fable 5 authored and GitHub committed 2026-07-06 21:04:38 -04:00
1 parent f638c535a2
commit fec25cfec8
4 files changed
+65 -27

No files matched your search

+19 -4
View File
@@ -4,6 +4,7 @@
use sdl3::event::Event;
use sdl3::keyboard::Keycode;
use sdl3::pixels::{Color, PixelFormat};
use sdl3::rect::Rect;
use sdl3::render::ScaleMode;
use sdl3::sys::render::SDL_RendererLogicalPresentation;
@@ -48,6 +49,8 @@ static MENU: [&str; TTY_ROWS] = [
];
pub fn main(_args: &[String]) -> BooChoice {
let pad = sdl::window_padding();
// Setup SDL
let context = match sdl3::init() {
@@ -60,7 +63,11 @@ pub fn main(_args: &[String]) -> BooChoice {
let video = context.video().expect("Failed to initialize SDL video");
let window = video
.window("EWM v0.1 - Bootloader", 280 * 3, 192 * 3)
.window(
"EWM v0.1 - Bootloader",
280 * 3 + 2 * pad,
192 * 3 + 2 * pad,
)
.position_centered()
.build()
.expect("Failed create window");
@@ -72,10 +79,12 @@ pub fn main(_args: &[String]) -> BooChoice {
return BooChoice::Quit;
}
// Logical units are window pixels: the tty texture is drawn at 3x into
// an explicit rect, leaving pad window pixels around it.
canvas
.set_logical_size(
TTY_PIXEL_WIDTH as u32,
TTY_PIXEL_HEIGHT as u32,
TTY_PIXEL_WIDTH as u32 * 3 + 2 * pad,
TTY_PIXEL_HEIGHT as u32 * 3 + 2 * pad,
SDL_RendererLogicalPresentation::LETTERBOX,
)
.expect("Failed to set logical size");
@@ -140,8 +149,14 @@ pub fn main(_args: &[String]) -> BooChoice {
texture
.update(None, &bytes, TTY_PIXEL_WIDTH * 4)
.expect("Failed to update texture");
let dst = Rect::new(
pad as i32,
pad as i32,
TTY_PIXEL_WIDTH as u32 * 3,
TTY_PIXEL_HEIGHT as u32 * 3,
);
canvas
.copy(&texture, None, None)
.copy(&texture, None, dst)
.expect("Failed to copy texture");
canvas.present();
+15 -4
View File
@@ -12,6 +12,7 @@ use ewm_core::mem::{DeviceHandle, Memory};
use sdl3::event::Event;
use sdl3::keyboard::{Keycode, Mod};
use sdl3::pixels::PixelFormat;
use sdl3::rect::Rect;
use sdl3::render::ScaleMode;
use sdl3::sys::render::SDL_RendererLogicalPresentation;
use sdl3::video::FullscreenType;
@@ -197,6 +198,8 @@ fn step_cpu(one: &mut One, cycles: u32) {
}
pub fn main(args: &[String]) -> i32 {
let pad = sdl::window_padding();
// Parse Apple 1 specific options. The C default model is the Replica 1.
let mut model = OneModel::Replica1;
let mut memory: Vec<MemoryOption> = Vec::new();
@@ -252,7 +255,7 @@ pub fn main(args: &[String]) -> i32 {
let video = context.video().expect("Failed to initialize SDL video");
let window = video
.window("EWM v0.1 - Apple 1", 280 * 3, 192 * 3)
.window("EWM v0.1 - Apple 1", 280 * 3 + 2 * pad, 192 * 3 + 2 * pad)
.position_centered()
.build();
let window = match window {
@@ -270,10 +273,12 @@ pub fn main(args: &[String]) -> i32 {
return 1;
}
// Logical units are window pixels: the tty texture is drawn at 3x into
// an explicit rect, leaving pad window pixels around it.
canvas
.set_logical_size(
TTY_PIXEL_WIDTH as u32,
TTY_PIXEL_HEIGHT as u32,
TTY_PIXEL_WIDTH as u32 * 3 + 2 * pad,
TTY_PIXEL_HEIGHT as u32 * 3 + 2 * pad,
SDL_RendererLogicalPresentation::LETTERBOX,
)
.expect("Failed to set logical size");
@@ -375,8 +380,14 @@ pub fn main(args: &[String]) -> i32 {
texture
.update(None, &bytes, TTY_PIXEL_WIDTH * 4)
.expect("Failed to update texture");
let dst = Rect::new(
pad as i32,
pad as i32,
TTY_PIXEL_WIDTH as u32 * 3,
TTY_PIXEL_HEIGHT as u32 * 3,
);
canvas
.copy(&texture, None, None)
.copy(&texture, None, dst)
.expect("Failed to copy texture");
canvas.present();
+9
View File
@@ -53,6 +53,15 @@ pub fn check_renderer(canvas: &WindowCanvas) -> Result<(), String> {
Ok(())
}
/// Window pixels between the screen contents and the window border,
/// from `EWM_WINDOW_PADDING` (default 4).
pub fn window_padding() -> u32 {
std::env::var("EWM_WINDOW_PADDING")
.ok()
.and_then(|s| s.parse().ok())
.unwrap_or(4)
}
/// Port of `ewm_sdl_green`: full green packed for the renderer's format.
pub fn green(canvas: &WindowCanvas) -> u32 {
match pixel_format(canvas) {
+22 -19
View File
@@ -620,6 +620,7 @@ pub fn main(args: &[String]) -> i32 {
Err(code) => return code,
};
let fps = options.fps;
let pad = sdl::window_padding();
// Initialize SDL
@@ -635,7 +636,7 @@ pub fn main(args: &[String]) -> i32 {
let controller_subsystem = context.gamepad().ok();
let window = video
.window("EWM v0.1 / Apple ][+", 280 * 3, 192 * 3)
.window("EWM v0.1 / Apple ][+", 280 * 3 + 2 * pad, 192 * 3 + 2 * pad)
.position(400, 60)
.build();
let window = match window {
@@ -653,10 +654,12 @@ pub fn main(args: &[String]) -> i32 {
return 1;
}
// Logical units are window pixels: the screen texture is drawn at 3x
// into an explicit rect, leaving pad window pixels around it.
canvas
.set_logical_size(
SCR_WIDTH as u32,
SCR_HEIGHT as u32,
SCR_WIDTH as u32 * 3 + 2 * pad,
SCR_HEIGHT as u32 * 3 + 2 * pad,
SDL_RendererLogicalPresentation::LETTERBOX,
)
.expect("Failed to set logical size");
@@ -855,21 +858,15 @@ pub fn main(args: &[String]) -> i32 {
} else {
0
};
let _ = canvas
.window_mut()
.set_size(SCR_WIDTH as u32 * 3, SCR_HEIGHT as u32 * 3 + extra);
let _ = canvas.window_mut().set_size(
SCR_WIDTH as u32 * 3 + 2 * pad,
SCR_HEIGHT as u32 * 3 + 2 * pad + extra,
);
let _ = canvas.set_logical_size(
SCR_WIDTH as u32 * 3,
SCR_HEIGHT as u32 * 3 + extra,
SCR_WIDTH as u32 * 3 + 2 * pad,
SCR_HEIGHT as u32 * 3 + 2 * pad + extra,
SDL_RendererLogicalPresentation::LETTERBOX,
);
if !status_bar_visible {
let _ = canvas.set_logical_size(
SCR_WIDTH as u32,
SCR_HEIGHT as u32,
SDL_RendererLogicalPresentation::LETTERBOX,
);
}
}
Keycode::P => paused = !paused,
_ => {}
@@ -952,8 +949,14 @@ pub fn main(args: &[String]) -> i32 {
texture
.update(None, &pixels_to_bytes(&scr.pixels), SCR_WIDTH * 4)
.expect("Failed to update texture");
let screen_dst = Rect::new(
pad as i32,
pad as i32,
SCR_WIDTH as u32 * 3,
SCR_HEIGHT as u32 * 3,
);
canvas
.copy(&texture, None, None)
.copy(&texture, None, screen_dst)
.expect("Failed to copy texture");
if status_bar_visible {
@@ -962,8 +965,8 @@ pub fn main(args: &[String]) -> i32 {
.update(None, &pixels_to_bytes(&bar), TTY_PIXEL_WIDTH * 4)
.expect("Failed to update bar texture");
let dst = Rect::new(
0,
SCR_HEIGHT as i32 * 3,
pad as i32,
pad as i32 + SCR_HEIGHT as i32 * 3,
SCR_WIDTH as u32 * 3,
STATUS_BAR_HEIGHT * 3,
);
@@ -989,7 +992,7 @@ pub fn main(args: &[String]) -> i32 {
TTY_PIXEL_WIDTH * 4,
)
.expect("Failed to update tty texture");
let _ = canvas.copy(&tty_texture, None, None);
let _ = canvas.copy(&tty_texture, None, screen_dst);
}
canvas.present();