A cross-platform windowing library for Odin with:
Initial scope: Linux (X11 + Wayland), windows, input, monitors, fullscreen
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
// 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
}
Window_Id :: distinct u64
Monitor_Handle :: distinct rawptr
Physical_Size :: struct { width, height: u32 }
Physical_Position :: struct { x, y: i32 }
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),
}
// 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
// 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
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_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
}
select_backend :: proc() -> Backend {
if os.getenv("WAYLAND_DISPLAY") != "" {
return .Wayland
}
return .X11
}
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{}
})
}
After each phase:
odin build .