Files
octal_cookie/src/simulator/gui.odin
T

243 lines
5.8 KiB
Odin

package simulator
import emu "../machine"
import rl "vendor:raylib"
// Window
WINDOW_WIDTH :: 1920
WINDOW_HEIGHT :: 1080
// Layout proportions
// Sidebar takes 20% of screen width on each side; display takes the rest.
// The center column splits vertically: 40% display, 60% debug panel.
SIDEBAR_PERCENT :: f32(0.20)
DISPLAY_V_RATIO :: f32(0.40)
// Fixed heights
CONTROL_BAR_H :: f32(50)
STATUS_BAR_H :: f32(30)
// Layout constants
PADDING_X :: f32(10)
PADDING_Y :: f32(8)
PANEL_HEADER :: f32(24)
// Buttons
BUTTON_HEIGHT :: 30
BUTTON_WIDTH :: 120
// Fonts
BIG_FONT_SIZE :: 20
KEYPAD_FONT_SIZE :: 18
LINE_HEIGHT :: 22
Layout :: struct {
control_bar : rl.Rectangle,
file_loader : rl.Rectangle,
keypad : rl.Rectangle,
display : rl.Rectangle,
bottom_panel : rl.Rectangle,
cpu : rl.Rectangle,
status_bar : rl.Rectangle,
info_box : rl.Rectangle,
}
init :: proc(sim: ^Simulator) {
_sim = sim
// desktop
when ODIN_OS != .JS {
rl.SetConfigFlags({.WINDOW_RESIZABLE})
rl.SetTargetFPS(60)
}
// web
when ODIN_OS == .JS {
rl.SetConfigFlags({.VSYNC_HINT})
}
rl.InitWindow(WINDOW_WIDTH, WINDOW_HEIGHT, "Octal Cookie - Chip 8 Simulator")
rl.InitAudioDevice()
// Load sound
beep := rl.LoadSound("./assets/sounds/beep.wav")
_sim.sound = beep
// Load fonts
font := rl.LoadFontEx("./assets/fonts/Inter_18pt-Regular.ttf", 18, nil, 0)
rl.SetTextureFilter(font.texture, .BILINEAR)
_sim.font = font
rl.GuiLoadStyleDefault()
rl.GuiLoadStyle("./assets/raygui_styles/genesis.rgs")
rl.GuiSetFont(font)
rl.GuiSetStyle(.DEFAULT, i32(rl.GuiDefaultProperty.TEXT_SIZE), 18)
}
update :: proc() {
sim := _sim
// Recalculate layout each frame based on current window size
// Pass these down to gui functions so they can setup their sizes?
screen_width := f32(rl.GetScreenWidth())
screen_height := f32(rl.GetScreenHeight())
sidebar_width := screen_width * 0.20
// set all the layout structs dynamically with the screen size
layout := calc_layout(screen_width, screen_height)
rl.BeginDrawing()
rl.ClearBackground(rl.Color{0x18, 0x18, 0x18, 0xFF})
cycles := int(sim.cpu_hz / SIM_FPS)
if (!sim.paused) {
// Cycle the machine to update memory etc
emu.run_machine(sim.machine, cycles)
tick_timers(sim)
}
if(sim.paused && sim.step) {
// Cycle the machine to update memory etc
emu.run_machine(sim.machine, 1)
tick_timers(sim)
sim.step = false
}
// Top
// ------------------------------------------
gui_control_bar(layout.control_bar, sim)
// Left
// ------------------------------------------
when ODIN_OS != .JS { gui_file_loader(layout.file_loader, sim)}
gui_key_pad(layout.keypad, sim.machine.keypad, sim.font)
// Center
// ------------------------------------------
gui_screen(layout.display, sim)
// Right
// ------------------------------------------
gui_cpu(layout.cpu, sim)
// Bottom
// ------------------------------------------
gui_bottom_panel(layout.bottom_panel, sim)
gui_status_bar(layout.status_bar, sim)
// Info Box
// ------------------------------------------
gui_info_box(layout.info_box, sim, screen_width, screen_height)
rl.EndDrawing()
free_all(context.temp_allocator)
}
shutdown :: proc() {
sim := _sim
rl.UnloadFont(sim.font)
rl.UnloadSound(sim.sound)
rl.CloseAudioDevice()
rl.CloseWindow()
}
should_run :: proc() -> bool {
when ODIN_OS != .JS {
return !rl.WindowShouldClose()
}
return true // web loop is controlled by JS requestAnimationFrame
}
tick_timers :: proc(sim: ^Simulator) {
beep := sim.sound
if sim.machine.delay_timer > 0 do sim.machine.delay_timer -= 1
if sim.machine.sound_timer > 0 {
sim.machine.sound_timer -= 1
if !rl.IsSoundPlaying(beep) do rl.PlaySound(beep)
} else {
// only stop if actually playing
if rl.IsSoundPlaying(beep) do rl.StopSound(beep)
}
}
calc_layout :: proc(screen_width: f32, screen_height: f32) -> Layout {
// Control bar is a fixed height frozen at top of gui, all items start below it.
y_pos := CONTROL_BAR_H
x_pos := f32(0)
// Usable gui vertical space
visible_height := screen_height - CONTROL_BAR_H - STATUS_BAR_H
sidebar_width := screen_width * SIDEBAR_PERCENT
display_width := screen_width - (sidebar_width * 2) - sidebar_width
display_height := visible_height * DISPLAY_V_RATIO
keypad_height := (y_pos + visible_height - visible_height / 2)
x_center := sidebar_width
y_center := screen_width - sidebar_width * 2
return Layout {
// Left Area
control_bar = rl.Rectangle{
x = 0,
y = 0,
width = screen_width,
height = CONTROL_BAR_H,
},
file_loader = rl.Rectangle{
x = 0,
y = y_pos,
width = sidebar_width,
height = keypad_height - y_pos
},
keypad = rl.Rectangle{
x = 0,
y = keypad_height,
width = sidebar_width,
height = visible_height / 2
},
// ------------------------------------------
// Center Area
display = rl.Rectangle{
x = sidebar_width,
y = y_pos,
width = display_width,
height = display_height,
},
// ------------------------------------------
// Bottom Area
bottom_panel = rl.Rectangle{
x = sidebar_width,
y = y_pos + display_height,
width = screen_width - sidebar_width,
height = visible_height - display_height,
},
// ------------------------------------------
// Right Area
cpu = rl.Rectangle {
x = sidebar_width + display_width,
y = y_pos,
width = sidebar_width * 2,
height = display_height
},
// ------------------------------------------
// Bottom Area
status_bar = rl.Rectangle{
x = x_pos,
y = y_pos + visible_height,
width = screen_width,
height = STATUS_BAR_H,
},
// ------------------------------------------
// Info Box
info_box = rl.Rectangle{
x = (screen_width / 2 - 200),
y = (screen_height / 2 - 200),
width = 400,
height = 140
}
}
}