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 } } }