Harbor

branch main
showing the latest snapshot on main
plan.md 7.4 KB · Markdown
plan.md 0644 Raw

Odin Windowing Library Implementation Plan

Overview

A cross-platform windowing library for Odin with:

  • Event-driven architecture (inspired by winit)
  • Minimal API surface (inspired by GLFW)
  • No rendering management - users bring their own Vulkan/OpenGL
  • Single-threaded event loop on main thread

Initial scope: Linux (X11 + Wayland), windows, input, monitors, fullscreen


Architecture

File Structure

window/
├── window.odin           # Main package, re-exports public API
├── types.odin            # Event union, Control_Flow, handles
├── event_loop.odin       # Event_Loop and run procedures
├── window_api.odin       # Window struct and operations
├── monitor.odin          # Monitor enumeration and info
├── input.odin            # Key codes, mouse buttons, modifiers
├── error.odin            # Error types
│
├── platform/
│   ├── platform.odin     # Platform vtable and dispatch
│   ├── backend.odin      # Backend selection (X11 vs Wayland)
│   │
│   ├── x11/
│   │   ├── x11.odin
│   │   ├── x11_window.odin
│   │   ├── x11_event_loop.odin
│   │   └── x11_monitor.odin
│   │
│   └── wayland/
│       ├── wayland.odin
│       ├── wayland_window.odin
│       ├── wayland_event_loop.odin
│       └── wayland_monitor.odin
│
└── examples/
    ├── basic.odin
    └── fullscreen.odin

Core Types

Event System (Discriminated Unions)

// Top-level event
Event :: union {
    Window_Event_Data,  // Events for a specific window
    Device_Event_Data,  // Raw input events
    New_Events,         // Start of event batch
    About_To_Wait,      // Before blocking
    Loop_Exiting,       // Event loop shutting down
}

// Window events
Window_Event :: union {
    Close_Requested, Destroyed, Resized, Moved,
    Focused, Unfocused, Key_Input, Char_Input,
    Pointer_Moved, Pointer_Entered, Pointer_Left,
    Mouse_Button_Input, Mouse_Wheel,
    Scale_Factor_Changed, Redraw_Requested,
}

// Control flow (how event loop behaves after processing)
Control_Flow :: union {
    Poll,       // Continuous polling (games)
    Wait,       // Block until events (UI apps)
    Wait_Until, // Block with timeout
}

Handles

Window_Id :: distinct u64
Monitor_Handle :: distinct rawptr

Physical_Size :: struct { width, height: u32 }
Physical_Position :: struct { x, y: i32 }

Window Attributes

Window_Attributes :: struct {
    title:       string,
    size:        Physical_Size,
    min_size:    Maybe(Physical_Size),
    max_size:    Maybe(Physical_Size),
    position:    Maybe(Physical_Position),
    resizable:   bool,
    visible:     bool,
    decorated:   bool,
    maximized:   bool,
    fullscreen:  Maybe(Fullscreen_Mode),
}

API Design

Event Loop

// Creation
create_event_loop :: proc(allocator := context.allocator) -> (^Event_Loop, Error)
destroy_event_loop :: proc(loop: ^Event_Loop)

// Running
run :: proc(loop: ^Event_Loop, handler: Event_Handler) -> Error
poll :: proc(loop: ^Event_Loop, handler: Event_Handler) -> bool
exit :: proc(loop: ^Event_Loop)

// User callback type
Event_Handler :: #type proc(event: Event, loop: ^Event_Loop) -> Control_Flow

Window

// Lifecycle
create_window :: proc(loop: ^Event_Loop, attrs: Window_Attributes) -> (^Window, Error)
destroy_window :: proc(win: ^Window)

// Size and position
surface_size :: proc(win: ^Window) -> Physical_Size
request_surface_size :: proc(win: ^Window, size: Physical_Size)
outer_position :: proc(win: ^Window) -> Physical_Position
set_outer_position :: proc(win: ^Window, pos: Physical_Position)

// State
set_title :: proc(win: ^Window, title: string)
set_visible :: proc(win: ^Window, visible: bool)
set_fullscreen :: proc(win: ^Window, mode: Maybe(Fullscreen_Mode))
request_redraw :: proc(win: ^Window)

// Raw handle for rendering APIs
raw_window_handle :: proc(win: ^Window) -> Raw_Window_Handle

Monitor

available_monitors :: proc(loop: ^Event_Loop) -> []Monitor_Handle
primary_monitor :: proc(loop: ^Event_Loop) -> Maybe(Monitor_Handle)
monitor_info :: proc(handle: Monitor_Handle) -> Monitor_Info
video_modes :: proc(handle: Monitor_Handle) -> []Video_Mode
current_video_mode :: proc(handle: Monitor_Handle) -> Video_Mode

Platform Abstraction

Vtable Pattern

Platform_Vtable :: struct {
    // Event loop operations
    init:        proc(loop: ^Platform_Event_Loop) -> Error,
    terminate:   proc(loop: ^Platform_Event_Loop),
    pump_events: proc(loop: ^Platform_Event_Loop, ...),
    wait_events: proc(loop: ^Platform_Event_Loop),
    wake_up:     proc(loop: ^Platform_Event_Loop),

    // Window operations
    create_window:  proc(...) -> Error,
    destroy_window: proc(...),
    // ... 20+ window operations

    // Monitor operations
    get_monitors:   proc(...) -> []Monitor_Handle,
    // ... monitor operations
}

Backend Selection

select_backend :: proc() -> Backend {
    if os.getenv("WAYLAND_DISPLAY") != "" {
        return .Wayland
    }
    return .X11
}

Example Usage

package main
import "window"

main :: proc() {
    loop, _ := window.create_event_loop()
    defer window.destroy_event_loop(loop)

    attrs := window.default_window_attributes()
    attrs.title = "My App"
    win, _ := window.create_window(loop, attrs)

    window.run(loop, proc(event: window.Event, loop: ^window.Event_Loop) -> window.Control_Flow {
        switch e in event {
        case window.Window_Event_Data:
            switch we in e.event {
            case window.Close_Requested:
                window.exit(loop)
            case window.Redraw_Requested:
                // Render here
            }
        }
        return window.Wait{}
    })
}

Implementation Phases

Phase 1: Core Foundation

  • Package structure with stub files
  • Core types (Event, Control_Flow, Window_Id)
  • Basic Event_Loop with X11 backend
  • Create window, handle close/resize events
  • Test: Window that responds to close button

Phase 2: Input Events

  • Key events with scancode mapping
  • Mouse button/motion/wheel events
  • Modifier key tracking
  • Test: Print all input events

Phase 3: Window Operations

  • Resize, move, size constraints
  • Visibility, decorations, resizable toggle
  • Maximize/minimize, focus
  • Test: Interactive window management demo

Phase 4: Monitor API

  • Monitor enumeration and properties
  • Video modes
  • Borderless and exclusive fullscreen
  • Test: Fullscreen toggle demo

Phase 5: Event Loop Polish

  • Wait and WaitUntil control flow
  • wake_up() for cross-thread signaling
  • Redraw request batching
  • Test: Benchmark control flow modes

Phase 6: Wayland Backend

  • Wayland connection/display
  • xdg_surface window creation
  • Port all operations to Wayland
  • Handle CSD and Wayland quirks
  • Test: All tests pass on Wayland

Phase 7: DPI/Scale Factor

  • Scale factor detection
  • Scale_Factor_Changed events
  • Test: Correct rendering at different DPIs

Phase 8: Documentation & Examples

  • API documentation
  • Examples: basic, input, fullscreen, multi-window, Vulkan
  • Performance testing

Verification

After each phase:

  1. Build with odin build .
  2. Run example programs
  3. Test on both X11 and Wayland (after Phase 6)
  4. Check for memory leaks with allocator tracking