Harbor

Changelog 8a10473e27c2

import window library

@sky · 1 month ago
32 added 0 modified 0 deleted
.gitignore +2 -0 added
1 + /.claude/
2 + *.bin
.harbor +2 -0 added
1 + [workspace]
2 + name = "window"
CLAUDE.md +8 -0 added
1 + # Project Rules
2 + - When committing, NO AI ATTRIBUTION
3 + - Ensure we stick to phases and all code is tested
4 + - GLFW and winit are included for reference.
5 + - Minimize API service a la GLFW
6 + - Follow winit's lead in architecture
7 + - Use Odin thought patterns for the actual implementation
8 + - Use "odin run examples/<filename>.odin -file" for testing
examples/basic.odin +33 -0 added
1 + package basic_example
2 +
3 + import "core:fmt"
4 + import win "../window"
5 +
6 + main :: proc() {
7 + loop, err := win.create_event_loop()
8 + if err != .None {
9 + fmt.eprintln("Failed to create event loop:", err)
10 + return
11 + }
12 + defer win.destroy_event_loop(loop)
13 +
14 + window, win_err := win.create_window(loop, {
15 + title = "Window Library Test",
16 + size = {800, 600},
17 + resizable = true,
18 + visible = true,
19 + decorated = true,
20 + })
21 + if win_err != .None {
22 + fmt.eprintln("Failed to create window:", win_err)
23 + return
24 + }
25 +
26 + fmt.println("Window created! Close it to exit.")
27 +
28 + for !win.should_close(window) {
29 + win.poll_events(loop)
30 + }
31 +
32 + fmt.println("Goodbye!")
33 + }
examples/features_test.odin +133 -0 added
1 + package features_test
2 +
3 + import "core:fmt"
4 + import win "../window"
5 +
6 + // 4x4 red RGBA icon for testing (tiny but valid)
7 + make_test_icon :: proc(r, g, b: u8, size: u32) -> win.Icon {
8 + n := int(size * size * 4)
9 + rgba := make([]u8, n)
10 + for i := 0; i < n; i += 4 {
11 + rgba[i + 0] = r
12 + rgba[i + 1] = g
13 + rgba[i + 2] = b
14 + rgba[i + 3] = 255
15 + }
16 + return win.Icon{rgba = rgba, width = size, height = size}
17 + }
18 +
19 + // 16x16 green crosshair cursor
20 + make_test_cursor :: proc() -> win.Cursor_Image {
21 + size : u32 = 16
22 + n := int(size * size * 4)
23 + rgba := make([]u8, n)
24 + for y in 0..<size {
25 + for x in 0..<size {
26 + i := int((y * size + x) * 4)
27 + is_cross := (x == size/2 || y == size/2)
28 + rgba[i + 0] = is_cross ? u8(0) : u8(0)
29 + rgba[i + 1] = is_cross ? u8(200) : u8(0)
30 + rgba[i + 2] = is_cross ? u8(0) : u8(0)
31 + rgba[i + 3] = is_cross ? u8(255) : u8(0)
32 + }
33 + }
34 + return win.Cursor_Image{rgba = rgba, width = size, height = size, hotspot = {size/2, size/2}}
35 + }
36 +
37 + main :: proc() {
38 + loop, err := win.create_event_loop()
39 + if err != .None {
40 + fmt.eprintln("Failed to create event loop:", err)
41 + return
42 + }
43 + defer win.destroy_event_loop(loop)
44 +
45 + window, win_err := win.create_window(loop, {
46 + title = "Features Test",
47 + size = {600, 400},
48 + resizable = true,
49 + visible = true,
50 + decorated = true,
51 + })
52 + if win_err != .None {
53 + fmt.eprintln("Failed to create window:", win_err)
54 + return
55 + }
56 +
57 + // --- Tier 1: state tracking ---
58 + fmt.println("is_maximized:", win.is_maximized(window)) // should be false
59 + fmt.println("is_minimized:", win.is_minimized(window)) // should be false
60 + fmt.println("is_decorated:", win.is_decorated(window)) // should be true
61 + fmt.println("is_resizable:", win.is_resizable(window)) // should be true
62 +
63 + // --- Tier 2: icon ---
64 + icon := make_test_icon(255, 0, 0, 32)
65 + defer delete(icon.rgba)
66 + win.set_icon(window, icon)
67 + fmt.println("Set window icon (32x32 red)")
68 +
69 + // --- Tier 2: opacity ---
70 + win.set_opacity(window, 0.9)
71 + fmt.println("Set opacity to 0.9")
72 +
73 + // --- Tier 2: window level ---
74 + win.set_window_level(window, .Always_On_Top)
75 + fmt.println("Set window level: always-on-top")
76 + win.set_window_level(window, .Normal)
77 + fmt.println("Set window level: normal")
78 +
79 + // --- Tier 2: aspect ratio ---
80 + win.set_aspect_ratio(window, [2]u32{16, 9})
81 + fmt.println("Set aspect ratio 16:9 (try resizing the window)")
82 +
83 + // --- Tier 2: custom cursor ---
84 + cursor := make_test_cursor()
85 + defer delete(cursor.rgba)
86 + win.set_custom_cursor(window, cursor)
87 + fmt.println("Set custom 16x16 green cursor")
88 +
89 + // --- Tier 2: request attention ---
90 + win.request_attention(window)
91 + fmt.println("Requested attention (taskbar flash)")
92 +
93 + step := 0
94 +
95 + win.run(loop, proc(event: win.Event, loop: ^win.Event_Loop) -> win.Control_Flow {
96 + #partial switch e in event {
97 + case win.Window_Event_Data:
98 + #partial switch we in e.event {
99 + case win.Close_Requested:
100 + win.exit(loop)
101 +
102 + case win.Key_Input:
103 + if we.state == .Pressed {
104 + fmt.printfln("Key: %v repeat=%v mods=%v", we.key, we.repeat, we.modifiers)
105 + if we.key == .Escape {
106 + win.exit(loop)
107 + }
108 + }
109 +
110 + case win.Resized:
111 + fmt.printfln("Resized: %dx%d", we.size.width, we.size.height)
112 +
113 + case win.Scale_Factor_Changed:
114 + fmt.printfln("Scale factor changed: %.2f new_size: %dx%d",
115 + we.scale_factor, we.new_size.width, we.new_size.height)
116 +
117 + case win.Focused:
118 + fmt.println("Focused")
119 +
120 + case win.Unfocused:
121 + fmt.println("Unfocused")
122 + }
123 +
124 + case win.New_Events:
125 + if e.cause == .Init {
126 + fmt.println("Event loop initialized — close window or press Escape to exit")
127 + }
128 + }
129 + return win.Wait{}
130 + })
131 +
132 + fmt.println("Done.")
133 + }
examples/input_test.odin +139 -0 added
1 + package main
2 +
3 + import "core:fmt"
4 + import win "../window"
5 +
6 + // Store window pointer globally for the callback
7 + g_window: ^win.Window
8 +
9 + main :: proc() {
10 + loop, err := win.create_event_loop()
11 + if err != .None {
12 + fmt.println("Failed to create event loop:", err)
13 + return
14 + }
15 + defer win.destroy_event_loop(loop)
16 +
17 + window, err2 := win.create_window(loop, {
18 + title = "Input Test - Press keys to test features",
19 + size = {800, 600},
20 + resizable = true,
21 + visible = true,
22 + decorated = true,
23 + })
24 + if err2 != .None {
25 + fmt.println("Failed to create window:", err2)
26 + return
27 + }
28 + defer win.destroy_window(window)
29 +
30 + g_window = window
31 +
32 + fmt.println("=== Input Feature Test ===")
33 + fmt.println("Keys:")
34 + fmt.println(" 1-9,0: Change cursor shape")
35 + fmt.println(" H: Toggle hidden cursor")
36 + fmt.println(" C: Toggle confined cursor")
37 + fmt.println(" G: Toggle grabbed cursor")
38 + fmt.println(" R: Toggle raw mouse motion")
39 + fmt.println(" SPACE: Query pressed keys")
40 + fmt.println(" ESC: Exit")
41 + fmt.println()
42 + fmt.printfln("Raw motion supported: %v", win.supports_raw_mouse_motion(loop))
43 + fmt.println()
44 +
45 + win.run(loop, proc(event: win.Event, loop: ^win.Event_Loop) -> win.Control_Flow {
46 + window := g_window
47 +
48 + #partial switch e in event {
49 + case win.Window_Event_Data:
50 + #partial switch we in e.event {
51 + case win.Close_Requested:
52 + return nil // Exit
53 +
54 + case win.Key_Input:
55 + if we.state == .Pressed {
56 + #partial switch we.key {
57 + case .Key_1: win.set_cursor_shape(window, .Arrow); fmt.println("Cursor: Arrow")
58 + case .Key_2: win.set_cursor_shape(window, .IBeam); fmt.println("Cursor: IBeam")
59 + case .Key_3: win.set_cursor_shape(window, .Crosshair); fmt.println("Cursor: Crosshair")
60 + case .Key_4: win.set_cursor_shape(window, .Hand); fmt.println("Cursor: Hand")
61 + case .Key_5: win.set_cursor_shape(window, .Resize_EW); fmt.println("Cursor: Resize_EW")
62 + case .Key_6: win.set_cursor_shape(window, .Resize_NS); fmt.println("Cursor: Resize_NS")
63 + case .Key_7: win.set_cursor_shape(window, .Resize_NWSE); fmt.println("Cursor: Resize_NWSE")
64 + case .Key_8: win.set_cursor_shape(window, .Resize_NESW); fmt.println("Cursor: Resize_NESW")
65 + case .Key_9: win.set_cursor_shape(window, .Resize_All); fmt.println("Cursor: Resize_All")
66 + case .Key_0: win.set_cursor_shape(window, .Not_Allowed); fmt.println("Cursor: Not_Allowed")
67 +
68 + case .H:
69 + if win.get_cursor_mode(window) == .Hidden {
70 + win.set_cursor_mode(window, .Normal)
71 + fmt.println("Cursor mode: Normal")
72 + } else {
73 + win.set_cursor_mode(window, .Hidden)
74 + fmt.println("Cursor mode: Hidden")
75 + }
76 +
77 + case .C:
78 + if win.get_cursor_mode(window) == .Confined {
79 + win.set_cursor_mode(window, .Normal)
80 + fmt.println("Cursor mode: Normal")
81 + } else {
82 + win.set_cursor_mode(window, .Confined)
83 + fmt.println("Cursor mode: Confined")
84 + }
85 +
86 + case .G:
87 + if win.get_cursor_mode(window) == .Grabbed {
88 + win.set_cursor_mode(window, .Normal)
89 + fmt.println("Cursor mode: Normal")
90 + } else {
91 + win.set_cursor_mode(window, .Grabbed)
92 + fmt.println("Cursor mode: Grabbed (press G again to release)")
93 + }
94 +
95 + case .R:
96 + raw_enabled := win.is_raw_mouse_motion_enabled(window)
97 + win.set_raw_mouse_motion(window, !raw_enabled)
98 + if !raw_enabled {
99 + fmt.println("Raw mouse motion: ENABLED")
100 + } else {
101 + fmt.println("Raw mouse motion: DISABLED")
102 + }
103 +
104 + case .Space:
105 + fmt.println("--- Key State Polling ---")
106 + mods := win.get_modifiers(loop)
107 + fmt.printfln("Modifiers: Shift=%v Ctrl=%v Alt=%v Super=%v",
108 + mods.shift, mods.ctrl, mods.alt, mods.super)
109 +
110 + // Check some common keys
111 + if win.is_key_pressed(loop, .W) do fmt.println(" W is pressed")
112 + if win.is_key_pressed(loop, .A) do fmt.println(" A is pressed")
113 + if win.is_key_pressed(loop, .S) do fmt.println(" S is pressed")
114 + if win.is_key_pressed(loop, .D) do fmt.println(" D is pressed")
115 +
116 + // Check mouse buttons
117 + if win.is_mouse_button_pressed(loop, .Left) do fmt.println(" Left mouse button pressed")
118 + if win.is_mouse_button_pressed(loop, .Right) do fmt.println(" Right mouse button pressed")
119 + if win.is_mouse_button_pressed(loop, .Middle) do fmt.println(" Middle mouse button pressed")
120 +
121 + case .Escape:
122 + return nil // Exit
123 + }
124 + }
125 + }
126 +
127 + case win.Device_Event_Data:
128 + #partial switch de in e.event {
129 + case win.Pointer_Motion:
130 + fmt.printfln("Raw motion: dx=%.2f dy=%.2f", de.delta_x, de.delta_y)
131 + }
132 +
133 + case win.Loop_Exiting:
134 + fmt.println("Exiting...")
135 + }
136 +
137 + return win.Wait{}
138 + })
139 + }
examples/monitors.odin +68 -0 added
1 + package monitors_example
2 +
3 + import "core:fmt"
4 + import win "../window"
5 +
6 + main :: proc() {
7 + fmt.println("Monitor Information Demo")
8 + fmt.println("========================")
9 +
10 + loop, err := win.create_event_loop()
11 + if err != .None {
12 + fmt.eprintln("Failed to create event loop:", err)
13 + return
14 + }
15 + defer win.destroy_event_loop(loop)
16 +
17 + // Get all monitors
18 + monitors := win.get_monitors(loop)
19 + fmt.printfln("\nFound %d monitor(s):\n", len(monitors))
20 +
21 + for monitor, i in monitors {
22 + info := win.get_monitor_info(loop, monitor)
23 +
24 + primary_str := info.is_primary ? " (Primary)" : ""
25 + fmt.printfln("Monitor %d: %s%s", i + 1, info.name, primary_str)
26 + fmt.printfln(" Position: (%d, %d)", info.position.x, info.position.y)
27 + fmt.printfln(" Physical size: %d x %d mm", info.size_mm.width, info.size_mm.height)
28 + fmt.printfln(" Scale factor: %.2f", info.scale_factor)
29 + fmt.printfln(" Current mode: %dx%d @ %dHz (%d-bit)",
30 + info.current_mode.size.width,
31 + info.current_mode.size.height,
32 + info.current_mode.refresh_rate,
33 + info.current_mode.bit_depth)
34 +
35 + // Get workarea
36 + workarea := win.get_monitor_workarea(loop, monitor)
37 + fmt.printfln(" Workarea: (%d, %d) %dx%d",
38 + workarea.position.x, workarea.position.y,
39 + workarea.size.width, workarea.size.height)
40 +
41 + // Get all video modes
42 + modes := win.get_video_modes(loop, monitor)
43 + defer delete(modes)
44 + fmt.printfln(" Supported modes: %d", len(modes))
45 +
46 + // Print first few modes
47 + for mode, j in modes {
48 + if j >= 5 {
49 + fmt.printfln(" ... and %d more", len(modes) - 5)
50 + break
51 + }
52 + fmt.printfln(" %dx%d @ %dHz",
53 + mode.size.width, mode.size.height, mode.refresh_rate)
54 + }
55 +
56 + fmt.println()
57 + }
58 +
59 + // Get primary monitor specifically
60 + primary := win.get_primary_monitor(loop)
61 + if primary != nil {
62 + name := win.get_monitor_name(primary)
63 + fmt.printfln("Primary monitor: %s", name)
64 +
65 + scale_x, scale_y := win.get_monitor_content_scale(loop, primary)
66 + fmt.printfln("Content scale: %.2f x %.2f", scale_x, scale_y)
67 + }
68 + }
examples/test_alloc.odin +45 -0 added
1 + package test_alloc
2 +
3 + import "core:fmt"
4 + import win "../window"
5 +
6 + main :: proc() {
7 + // Set up tracking allocator
8 + tracking: win.Tracking_Allocator
9 + win.tracking_allocator_init(&tracking, context.allocator)
10 + tracked := win.tracking_allocator_make(&tracking)
11 +
12 + fmt.println("Testing allocation tracking...")
13 +
14 + // Create and destroy event loop + window
15 + loop, err := win.create_event_loop(tracked)
16 + if err != .None {
17 + fmt.eprintln("Failed to create event loop:", err)
18 + win.tracking_allocator_print_stats(&tracking)
19 + return
20 + }
21 +
22 + window, win_err := win.create_window(loop, {
23 + title = "Allocation Test",
24 + size = {800, 600},
25 + resizable = true,
26 + visible = true,
27 + decorated = true,
28 + })
29 + if win_err != .None {
30 + fmt.eprintln("Failed to create window:", win_err)
31 + win.destroy_event_loop(loop)
32 + win.tracking_allocator_print_stats(&tracking)
33 + return
34 + }
35 +
36 + fmt.println("Window created successfully")
37 + fmt.println("Destroying window and event loop...")
38 +
39 + // Clean up
40 + win.destroy_event_loop(loop)
41 +
42 + // Print allocation statistics
43 + win.tracking_allocator_print_stats(&tracking)
44 + win.tracking_allocator_destroy(&tracking)
45 + }
examples/window_ops.odin +178 -0 added
1 + package window_ops_example
2 +
3 + import "core:fmt"
4 + import win "../window"
5 +
6 + decorated := true
7 + maximized := false
8 + fullscreen := false
9 + resizable := true
10 + visible := true
11 +
12 + main :: proc() {
13 + fmt.println("Window Operations Demo")
14 + fmt.println("======================")
15 +
16 + loop, err := win.create_event_loop()
17 + if err != .None {
18 + fmt.eprintln("Failed to create event loop:", err)
19 + return
20 + }
21 + defer win.destroy_event_loop(loop)
22 +
23 + _, win_err := win.create_window(loop, {
24 + title = "Window Operations Demo",
25 + size = {800, 600},
26 + resizable = true,
27 + visible = true,
28 + decorated = true,
29 + })
30 + if win_err != .None {
31 + fmt.eprintln("Failed to create window:", win_err)
32 + return
33 + }
34 +
35 + fmt.println("\nControls:")
36 + fmt.println(" D - Toggle decorations")
37 + fmt.println(" M - Toggle maximize")
38 + fmt.println(" F - Toggle fullscreen")
39 + fmt.println(" R - Toggle resizable")
40 + fmt.println(" H - Hide/show window")
41 + fmt.println(" 1 - Set size to 400x300")
42 + fmt.println(" 2 - Set size to 800x600")
43 + fmt.println(" 3 - Set size to 1200x800")
44 + fmt.println(" Arrow Keys - Move window")
45 + fmt.println(" Space - Focus window")
46 + fmt.println(" I - Minimize window")
47 + fmt.println(" Escape - Exit")
48 + fmt.println("")
49 +
50 + win.run(loop, handle_event)
51 +
52 + fmt.println("Goodbye!")
53 + }
54 +
55 + handle_event :: proc(event: win.Event, loop: ^win.Event_Loop) -> win.Control_Flow {
56 + #partial switch e in event {
57 + case win.Window_Event_Data:
58 + // Get the first (and only) window
59 + window: ^win.Window = nil
60 + if len(loop.windows) > 0 {
61 + window = loop.windows[0]
62 + }
63 +
64 + #partial switch we in e.event {
65 + case win.Close_Requested:
66 + fmt.println("Exiting...")
67 + win.exit(loop)
68 +
69 + case win.Key_Input:
70 + if we.state == .Pressed && !we.repeat && window != nil {
71 + #partial switch we.key {
72 + case .D:
73 + decorated = !decorated
74 + win.set_decorations(window, decorated)
75 + fmt.printfln("Decorations: %v", decorated)
76 +
77 + case .M:
78 + maximized = !maximized
79 + win.set_maximized(window, maximized)
80 + fmt.printfln("Maximized: %v", maximized)
81 +
82 + case .F:
83 + fullscreen = !fullscreen
84 + if fullscreen {
85 + win.set_fullscreen(window, win.Fullscreen_Mode{})
86 + } else {
87 + win.set_fullscreen(window, nil)
88 + }
89 + fmt.printfln("Fullscreen: %v", fullscreen)
90 +
91 + case .R:
92 + resizable = !resizable
93 + win.set_resizable(window, resizable)
94 + fmt.printfln("Resizable: %v", resizable)
95 +
96 + case .H:
97 + visible = !visible
98 + win.set_visible(window, visible)
99 + fmt.printfln("Visible: %v", visible)
100 +
101 + case .Key_1:
102 + win.set_size(window, {400, 300})
103 + fmt.println("Set size to 400x300")
104 +
105 + case .Key_2:
106 + win.set_size(window, {800, 600})
107 + fmt.println("Set size to 800x600")
108 +
109 + case .Key_3:
110 + win.set_size(window, {1200, 800})
111 + fmt.println("Set size to 1200x800")
112 +
113 + case .Arrow_Up:
114 + pos := win.get_position(window)
115 + pos.y -= 50
116 + win.set_position(window, pos)
117 + fmt.printfln("Moved to (%d, %d)", pos.x, pos.y)
118 +
119 + case .Arrow_Down:
120 + pos := win.get_position(window)
121 + pos.y += 50
122 + win.set_position(window, pos)
123 + fmt.printfln("Moved to (%d, %d)", pos.x, pos.y)
124 +
125 + case .Arrow_Left:
126 + pos := win.get_position(window)
127 + pos.x -= 50
128 + win.set_position(window, pos)
129 + fmt.printfln("Moved to (%d, %d)", pos.x, pos.y)
130 +
131 + case .Arrow_Right:
132 + pos := win.get_position(window)
133 + pos.x += 50
134 + win.set_position(window, pos)
135 + fmt.printfln("Moved to (%d, %d)", pos.x, pos.y)
136 +
137 + case .Space:
138 + win.focus(window)
139 + fmt.println("Focusing window")
140 +
141 + case .I:
142 + win.set_minimized(window)
143 + fmt.println("Minimizing window")
144 +
145 + case .Escape:
146 + fmt.println("Exiting...")
147 + win.exit(loop)
148 +
149 + case:
150 + // Ignore other keys
151 + }
152 + }
153 +
154 + case win.Resized:
155 + fmt.printfln("Resized to %dx%d", we.size.width, we.size.height)
156 +
157 + case win.Moved:
158 + fmt.printfln("Moved to (%d, %d)", we.position.x, we.position.y)
159 +
160 + case win.Focused:
161 + fmt.println("Window focused")
162 +
163 + case win.Unfocused:
164 + fmt.println("Window unfocused")
165 +
166 + case:
167 + // Ignore other window events
168 + }
169 +
170 + case win.Loop_Exiting:
171 + fmt.println("Event loop exiting")
172 +
173 + case:
174 + // Ignore other events
175 + }
176 +
177 + return win.Wait{}
178 + }
impl.md +299 -0 added
1 + # Implementation Reference
2 +
3 + Implementation details extracted from GLFW and winit for our Odin window library.
4 +
5 + ---
6 +
7 + ## GLFW Implementation Details
8 +
9 + ### Architecture Patterns
10 + - **Global state struct** containing all library state (initialized flag, platform vtable, error handling, window/cursor/monitor linked lists)
11 + - **Window struct** with linked list node (`next` pointer), settings/state flags, input state arrays, constraints, callbacks, and platform-specific state via macro embedding
12 + - **Monitor struct** with name, physical dimensions, video modes array, gamma ramp state
13 + - **Opaque handle pattern** - public API only sees pointers, never internal struct details
14 + - **Intrusive linked lists** for windows, cursors, errors - `next` pointer embedded in each structure
15 +
16 + ### Platform Abstraction
17 + - **Virtual function table** - `_GLFWplatform` struct contains ~60 function pointers for all platform operations
18 + - **Platform state macros** - `GLFW_X11_WINDOW_STATE`, `GLFW_WIN32_WINDOW_STATE` etc. embedded in window struct
19 + - **Conditional compilation** - empty stub macros when platform not compiled, same binary types across platforms
20 + - **Platform ID** stored in global state for runtime platform identification
21 +
22 + ### Window Management
23 + - **Hints system** - stateful configuration before creation (`glfwWindowHint()`, `glfwInitHint()`)
24 + - **Hints persist** until explicitly reset or new window created - allows adding new hints without breaking API
25 + - **Simple creation** - `glfwCreateWindow(width, height, title, monitor, share)`
26 + - **Monitor parameter** - NULL for windowed, valid monitor for fullscreen
27 + - **Share parameter** - NULL or reference to existing window for OpenGL context sharing
28 + - **Constraints** - min/max width/height, aspect ratio numerator/denominator
29 +
30 + ### Input Handling
31 + - **Key state array** - `keys[GLFW_KEY_LAST + 1]` stores PRESS/RELEASE/REPEAT
32 + - **Mouse state array** - `mouseButtons[GLFW_MOUSE_BUTTON_LAST + 1]`
33 + - **Sticky input modes** - persistent state capture even when callback missed
34 + - **Virtual cursor position** - software-tracked cursor for disabled cursor mode
35 + - **Raw mouse motion** - platform-specific unfiltered motion data
36 + - **Callback pointers** - 17 callbacks stored directly in window struct (null = no-op)
37 +
38 + ### Event Loop
39 + - `pollEvents()` - non-blocking, processes queue
40 + - `waitEvents()` - blocks until event
41 + - `waitEventsTimeout(t)` - blocks with timeout
42 + - `postEmptyEvent()` - force wake from wait
43 + - **Internal event functions** - `_glfwInputKey()`, `_glfwInputMouseClick()` etc. called by platform code
44 +
45 + ### API Design
46 + - **Error handling** - thread-local storage via TLS slots, last error only (not queued)
47 + - **Input mode configuration** - single function with mode enum (`GLFW_CURSOR`, `GLFW_STICKY_KEYS`, etc.)
48 + - **String hints** for platform extensions (`GLFW_X11_CLASS_NAME`, etc.)
49 + - **Callback registration** returns previous callback pointer
50 + - **User pointer** on windows and monitors for application data
51 +
52 + ### Platform Functions Required
53 + - init/terminate
54 + - Cursor: getCursorPos, setCursorPos, setCursorMode, createCursor, createStandardCursor, destroyCursor, setCursor
55 + - Input: getScancodeName, getKeyScancode, setClipboardString, getClipboardString, rawMouseMotionSupported
56 + - Monitor: freeMonitor, getMonitorPos, getMonitorContentScale, getMonitorWorkarea, getVideoModes, getVideoMode, getGammaRamp, setGammaRamp
57 + - Window: createWindow, destroyWindow, setWindowTitle, setWindowIcon, get/setWindowPos, get/setWindowSize, setWindowSizeLimits, setWindowAspectRatio, getFramebufferSize, getWindowFrameSize, getWindowContentScale, iconifyWindow, restoreWindow, maximizeWindow, showWindow, hideWindow, requestWindowAttention, focusWindow, setWindowMonitor
58 + - Window state: windowFocused, windowIconified, windowVisible, windowMaximized, windowHovered, framebufferTransparent, getWindowOpacity, setWindowResizable, setWindowDecorated, setWindowFloating, setWindowOpacity, setWindowMousePassthrough
59 + - Events: pollEvents, waitEvents, waitEventsTimeout, postEmptyEvent
60 +
61 + ---
62 +
63 + ## Winit Implementation Details
64 +
65 + ### Architecture Patterns
66 + - **Trait-based abstraction** - `ActiveEventLoop`, `Window`, `ApplicationHandler` traits
67 + - **Enum-based event system** - discriminated unions for events (40+ WindowEvent variants)
68 + - **Separate crates per platform** - winit-core (shared), winit-x11, winit-wayland, winit-win32, etc.
69 + - **Runtime backend selection** on Linux - check `WAYLAND_DISPLAY` then `DISPLAY` env vars
70 + - **Window IDs** - distinct u64 identifiers for multi-window handling
71 +
72 + ### ApplicationHandler Callbacks
73 + - `can_create_surfaces()` - lifecycle point for surface creation (critical for Android)
74 + - `window_event(event_loop, window_id, event)` - per-window events
75 + - `device_event(event_loop, device_id, event)` - raw input events
76 + - `about_to_wait()` - pre-blocking hook
77 + - `new_events(cause)` - start of event batch
78 + - `proxy_wake_up()` - inter-thread signaling
79 +
80 + ### Window Attributes (Builder Pattern)
81 + - surface_size, min_surface_size, max_surface_size, surface_resize_increments
82 + - position
83 + - resizable
84 + - enabled_buttons (bitflags for min/max/close)
85 + - title
86 + - maximized, visible, transparent, blur, decorations
87 + - window_icon
88 + - preferred_theme (Light/Dark)
89 + - content_protected
90 + - window_level (Normal, AlwaysOnTop, AlwaysOnBottom)
91 + - active
92 + - cursor
93 + - parent_window
94 + - fullscreen (Borderless or Exclusive with video mode)
95 + - platform (boxed trait object for platform-specific extensions)
96 +
97 + ### Keyboard Input Hierarchy
98 + 1. Physical scancode (XKB keycode, Windows scancode)
99 + 2. Platform-native NativeKeyCode: `Xkb(u32)`, `Windows(u16)`, `MacOS(u16)`
100 + 3. Logical key via NativeKey (keysym, vkcode)
101 + 4. Named key enum: Enter, Escape, ArrowUp, F1-F24, etc.
102 +
103 + ### Key Modifiers
104 + - Shift, Control, Alt, Super (meta/win/cmd)
105 + - Separate left/right tracking
106 + - CapsLock, NumLock state
107 +
108 + ### Pointer Events
109 + - `PointerMoved { position, primary, source }`
110 + - `PointerEntered { position, primary, kind }`
111 + - `PointerLeft { position, primary, kind }`
112 + - `PointerButton { state, position, button }`
113 + - `MouseWheel { delta: LineDelta | PixelDelta, phase }`
114 + - Primary vs secondary pointer for multi-touch
115 +
116 + ### Event Loop Control Flow
117 + - `Poll` - continuous polling (games)
118 + - `Wait` - block until events (UI apps, most efficient)
119 + - `WaitUntil(instant)` - block with timeout
120 +
121 + ### StartCause (Why event loop woke)
122 + - `Init` - first iteration
123 + - `Poll` - Poll control flow triggered
124 + - `WaitCancelled { start, requested_resume }` - new events interrupted wait
125 + - `ResumeTimeReached { start, requested_resume }` - WaitUntil timeout fired
126 +
127 + ### Window Events (Key Variants)
128 + - SurfaceResized, Moved, CloseRequested, Destroyed
129 + - Focused(bool), Occluded(bool)
130 + - KeyboardInput { device_id, event, is_synthetic }
131 + - ModifiersChanged
132 + - PointerMoved, PointerEntered, PointerLeft, PointerButton
133 + - MouseWheel, AxisMotion
134 + - Touch, TouchpadPressure
135 + - PinchGesture, PanGesture, DoubleTapGesture, RotationGesture
136 + - DragEntered, DragMoved, DragDropped, DragLeft
137 + - ScaleFactorChanged { scale_factor, surface_size_writer }
138 + - ThemeChanged
139 + - Ime(Enabled, Preedit, Commit, Disabled)
140 + - RedrawRequested
141 +
142 + ### Coordinate Systems
143 + - **Surface coordinates** - relative to drawable area (0,0 at content top-left)
144 + - **Window coordinates** - relative to window including decorations
145 + - **Desktop coordinates** - absolute screen position
146 + - Methods: `surface_size()`, `outer_size()`, `surface_position()`, `outer_position()`
147 +
148 + ### DPI/Scaling
149 + - `scale_factor()` on Window
150 + - `ScaleFactorChanged` event when DPI changes
151 + - Physical and Logical types that auto-convert
152 + - Fractional scaling support (Wayland)
153 +
154 + ### Monitor API
155 + - `available_monitors()` - iterator of MonitorHandle
156 + - `primary_monitor()` - Option<MonitorHandle>
157 + - MonitorHandle: id, native_id, name, position, scale_factor, video_modes, current_video_mode
158 +
159 + ### Cross-Thread Communication
160 + - `EventLoopProxy::wake_up()` - coalesced (multiple calls = 1 proxy_wake_up event)
161 + - Used for background threads signaling work completion
162 +
163 + ### Raw Window Handles
164 + - `raw_window_handle()` → RawWindowHandle for Vulkan/OpenGL/Metal/DirectX
165 + - `raw_display_handle()` → DisplayHandle for display connection
166 +
167 + ### X11-Specific
168 + - Window types: Desktop, Dock, Toolbar, Menu, Utility, Splash, Dialog, DropdownMenu, PopupMenu, Tooltip, Notification, Combo, Dnd, Normal
169 + - XAtom handling
170 + - Drag & drop (XDND protocol)
171 + - Input method integration
172 + - XSettings for desktop settings
173 +
174 + ### Wayland-Specific
175 + - Client-side decorations (CSD) option
176 + - xdg_toplevel access
177 + - Activation tokens
178 + - Fractional scaling protocol
179 + - Tablet input v2
180 +
181 + ---
182 +
183 + ## Current Implementation Status
184 +
185 + ### Core Data Structures
186 + - [x] Global library state (`Platform_Event_Loop` in `platform_linux.odin`)
187 + - [x] Window struct (`Platform_Window` with union of X11/Wayland data)
188 + - [x] Monitor struct (`Monitor_Info` with id, name, dimensions, modes, position, scale)
189 + - [x] Event enum (discriminated union: `Window_Event`, `Device_Event`, `Loop_Event`)
190 +
191 + ### Window Management
192 + - [x] Window creation with attributes (`create_window()` + `Window_Attributes`)
193 + - [x] Window destruction (`destroy_window()`)
194 + - [x] Title (`set_title()`)
195 + - [ ] Window icon
196 + - [x] Position (`outer_position()`, `set_outer_position()`, `set_position()`, `get_position()`)
197 + - [x] Size (`surface_size()`, `request_surface_size()`, `outer_size()`, `set_size()`)
198 + - [x] Min/max size constraints (`set_min_surface_size()`, `set_max_surface_size()`)
199 + - [ ] Aspect ratio constraints
200 + - [x] Visible (`set_visible()`, `is_visible()`)
201 + - [x] Focused (`focus()`, `is_focused()`)
202 + - [x] Maximized (`set_maximized()`, `is_maximized()` - state tracking TODO)
203 + - [x] Minimized (`set_minimized()`, `is_minimized()` - state tracking TODO)
204 + - [x] Fullscreen (`set_fullscreen()`, `get_fullscreen()` - borderless + exclusive)
205 + - [x] Decorations (`set_decorations()`, `is_decorated()` - state tracking TODO)
206 + - [x] Resizable (`set_resizable()`, `is_resizable()` - state tracking TODO)
207 + - [ ] Floating/always-on-top
208 + - [ ] Mouse passthrough
209 + - [ ] Opacity
210 + - [~] Content scale / DPI (`scale_factor()` - hardcoded 1.0, detection TODO)
211 +
212 + ### Input Handling
213 + - [x] Keyboard input with scancodes (`Key_Input` event, `Key_Code` enum 126+ keys)
214 + - [ ] Key state array for polling
215 + - [x] Modifier keys tracking (`Modifiers` struct: shift, ctrl, alt, super)
216 + - [x] Mouse button input (`Mouse_Button_Input` event)
217 + - [x] Cursor position (`Pointer_Moved` event with position)
218 + - [x] Scroll wheel (`Mouse_Wheel` event with line + pixel deltas)
219 + - [ ] Cursor modes (normal, hidden, disabled/grabbed)
220 + - [ ] Raw mouse motion (relative pointer protocol)
221 + - [ ] Cursor shapes (standard cursors)
222 + - [~] Key repeat detection (TODO in both X11 and Wayland)
223 +
224 + ### Event System
225 + - [x] Poll events (`poll()` - non-blocking)
226 + - [x] Wait events (`run()` with `Wait` control flow - blocking)
227 + - [x] Wait events with timeout (`Wait_Until` control flow)
228 + - [ ] Post empty event / wake from wait (TODO in code)
229 + - [x] Callback-based design (winit-style `Event_Handler` callback)
230 + - [x] Start cause tracking (`Init`, `Poll`, `Wait_Cancelled`, `Resume_Time_Reached`)
231 + - [x] Loop events (`New_Events`, `About_To_Wait`, `Loop_Exiting`)
232 +
233 + ### Monitor/Display
234 + - [x] Enumerate monitors (`get_monitors()`)
235 + - [x] Primary monitor (`get_primary_monitor()`)
236 + - [x] Monitor name (`get_monitor_name()`)
237 + - [x] Monitor position (`get_monitor_position()`)
238 + - [x] Monitor workarea (`get_monitor_workarea()`)
239 + - [x] Physical size (`get_monitor_physical_size()`)
240 + - [x] Content scale (`get_monitor_content_scale()`)
241 + - [x] Video modes enumeration (`get_video_modes()`)
242 + - [x] Current video mode (`get_current_video_mode()`)
243 + - [x] Combined info (`get_monitor_info()`)
244 +
245 + ### Platform Abstraction
246 + - [x] Platform-specific state embedded in structs (union in `Platform_Window`)
247 + - [x] Runtime platform selection (X11 vs Wayland via `select_backend()`)
248 + - [x] Environment variable checks (`WAYLAND_DISPLAY`, `ODIN_WINDOW_BACKEND`)
249 + - [x] X11 backend (complete)
250 + - [x] Wayland backend (functional - window creation, input, monitors, decorations)
251 +
252 + ### Raw Handle Support
253 + - [x] `raw_window_handle()` - X11 window ID or Wayland wl_surface
254 + - [x] `raw_display_handle()` - X11 display or Wayland wl_display
255 +
256 + ### API Design (Implemented Choices)
257 + - [x] Distinct handle types (`Window_Id`, `Device_Id`, `Monitor_Handle`)
258 + - [x] Attributes struct pattern (not hints)
259 + - [x] Callback-based events (winit-style)
260 + - [ ] User pointer on objects
261 + - [x] Error handling via `Error` enum return values
262 + - [ ] Thread-local error storage
263 +
264 + ---
265 +
266 + ## Not Yet Implemented
267 +
268 + ### Window Features
269 + - Window icon
270 + - Aspect ratio constraints
271 + - Floating/always-on-top window level
272 + - Mouse passthrough
273 + - Window opacity
274 + - Proper DPI detection (currently hardcoded to 1.0)
275 + - State tracking for maximized/minimized/resizable/decorated flags
276 +
277 + ### Input Features
278 + - Key state array for polling (currently event-only)
279 + - Cursor visibility modes (hidden, disabled/grabbed)
280 + - Raw mouse motion (Wayland relative-pointer protocol)
281 + - Standard cursor shapes
282 + - Key repeat detection (marked TODO)
283 + - Event loop wake-up mechanism (marked TODO)
284 +
285 + ### Advanced Features
286 + - Clipboard access (copy/paste)
287 + - Custom cursor images
288 + - Drag & drop
289 + - Input method (IME) integration
290 + - Touch input
291 + - Gamepad/joystick
292 + - Pointer gestures (pinch, pan, rotate)
293 + - Tablet input
294 +
295 + ### Wayland-Specific Incomplete
296 + - Exclusive fullscreen with specific video mode
297 + - Pointer constraints (cursor locking)
298 + - Activation tokens
299 + - Fractional scaling (protocol exists, not wired through)
plan.md +291 -0 added
1 + # Odin Windowing Library Implementation Plan
2 +
3 + ## Overview
4 +
5 + A cross-platform windowing library for Odin with:
6 + - **Event-driven architecture** (inspired by winit)
7 + - **Minimal API surface** (inspired by GLFW)
8 + - **No rendering management** - users bring their own Vulkan/OpenGL
9 + - **Single-threaded** event loop on main thread
10 +
11 + **Initial scope**: Linux (X11 + Wayland), windows, input, monitors, fullscreen
12 +
13 + ---
14 +
15 + ## Architecture
16 +
17 + ### File Structure
18 +
19 + ```
20 + window/
21 + ├── window.odin # Main package, re-exports public API
22 + ├── types.odin # Event union, Control_Flow, handles
23 + ├── event_loop.odin # Event_Loop and run procedures
24 + ├── window_api.odin # Window struct and operations
25 + ├── monitor.odin # Monitor enumeration and info
26 + ├── input.odin # Key codes, mouse buttons, modifiers
27 + ├── error.odin # Error types
28 +
29 + ├── platform/
30 + │ ├── platform.odin # Platform vtable and dispatch
31 + │ ├── backend.odin # Backend selection (X11 vs Wayland)
32 + │ │
33 + │ ├── x11/
34 + │ │ ├── x11.odin
35 + │ │ ├── x11_window.odin
36 + │ │ ├── x11_event_loop.odin
37 + │ │ └── x11_monitor.odin
38 + │ │
39 + │ └── wayland/
40 + │ ├── wayland.odin
41 + │ ├── wayland_window.odin
42 + │ ├── wayland_event_loop.odin
43 + │ └── wayland_monitor.odin
44 +
45 + └── examples/
46 + ├── basic.odin
47 + └── fullscreen.odin
48 + ```
49 +
50 + ---
51 +
52 + ## Core Types
53 +
54 + ### Event System (Discriminated Unions)
55 +
56 + ```odin
57 + // Top-level event
58 + Event :: union {
59 + Window_Event_Data, // Events for a specific window
60 + Device_Event_Data, // Raw input events
61 + New_Events, // Start of event batch
62 + About_To_Wait, // Before blocking
63 + Loop_Exiting, // Event loop shutting down
64 + }
65 +
66 + // Window events
67 + Window_Event :: union {
68 + Close_Requested, Destroyed, Resized, Moved,
69 + Focused, Unfocused, Key_Input, Char_Input,
70 + Pointer_Moved, Pointer_Entered, Pointer_Left,
71 + Mouse_Button_Input, Mouse_Wheel,
72 + Scale_Factor_Changed, Redraw_Requested,
73 + }
74 +
75 + // Control flow (how event loop behaves after processing)
76 + Control_Flow :: union {
77 + Poll, // Continuous polling (games)
78 + Wait, // Block until events (UI apps)
79 + Wait_Until, // Block with timeout
80 + }
81 + ```
82 +
83 + ### Handles
84 +
85 + ```odin
86 + Window_Id :: distinct u64
87 + Monitor_Handle :: distinct rawptr
88 +
89 + Physical_Size :: struct { width, height: u32 }
90 + Physical_Position :: struct { x, y: i32 }
91 + ```
92 +
93 + ### Window Attributes
94 +
95 + ```odin
96 + Window_Attributes :: struct {
97 + title: string,
98 + size: Physical_Size,
99 + min_size: Maybe(Physical_Size),
100 + max_size: Maybe(Physical_Size),
101 + position: Maybe(Physical_Position),
102 + resizable: bool,
103 + visible: bool,
104 + decorated: bool,
105 + maximized: bool,
106 + fullscreen: Maybe(Fullscreen_Mode),
107 + }
108 + ```
109 +
110 + ---
111 +
112 + ## API Design
113 +
114 + ### Event Loop
115 +
116 + ```odin
117 + // Creation
118 + create_event_loop :: proc(allocator := context.allocator) -> (^Event_Loop, Error)
119 + destroy_event_loop :: proc(loop: ^Event_Loop)
120 +
121 + // Running
122 + run :: proc(loop: ^Event_Loop, handler: Event_Handler) -> Error
123 + poll :: proc(loop: ^Event_Loop, handler: Event_Handler) -> bool
124 + exit :: proc(loop: ^Event_Loop)
125 +
126 + // User callback type
127 + Event_Handler :: #type proc(event: Event, loop: ^Event_Loop) -> Control_Flow
128 + ```
129 +
130 + ### Window
131 +
132 + ```odin
133 + // Lifecycle
134 + create_window :: proc(loop: ^Event_Loop, attrs: Window_Attributes) -> (^Window, Error)
135 + destroy_window :: proc(win: ^Window)
136 +
137 + // Size and position
138 + surface_size :: proc(win: ^Window) -> Physical_Size
139 + request_surface_size :: proc(win: ^Window, size: Physical_Size)
140 + outer_position :: proc(win: ^Window) -> Physical_Position
141 + set_outer_position :: proc(win: ^Window, pos: Physical_Position)
142 +
143 + // State
144 + set_title :: proc(win: ^Window, title: string)
145 + set_visible :: proc(win: ^Window, visible: bool)
146 + set_fullscreen :: proc(win: ^Window, mode: Maybe(Fullscreen_Mode))
147 + request_redraw :: proc(win: ^Window)
148 +
149 + // Raw handle for rendering APIs
150 + raw_window_handle :: proc(win: ^Window) -> Raw_Window_Handle
151 + ```
152 +
153 + ### Monitor
154 +
155 + ```odin
156 + available_monitors :: proc(loop: ^Event_Loop) -> []Monitor_Handle
157 + primary_monitor :: proc(loop: ^Event_Loop) -> Maybe(Monitor_Handle)
158 + monitor_info :: proc(handle: Monitor_Handle) -> Monitor_Info
159 + video_modes :: proc(handle: Monitor_Handle) -> []Video_Mode
160 + current_video_mode :: proc(handle: Monitor_Handle) -> Video_Mode
161 + ```
162 +
163 + ---
164 +
165 + ## Platform Abstraction
166 +
167 + ### Vtable Pattern
168 +
169 + ```odin
170 + Platform_Vtable :: struct {
171 + // Event loop operations
172 + init: proc(loop: ^Platform_Event_Loop) -> Error,
173 + terminate: proc(loop: ^Platform_Event_Loop),
174 + pump_events: proc(loop: ^Platform_Event_Loop, ...),
175 + wait_events: proc(loop: ^Platform_Event_Loop),
176 + wake_up: proc(loop: ^Platform_Event_Loop),
177 +
178 + // Window operations
179 + create_window: proc(...) -> Error,
180 + destroy_window: proc(...),
181 + // ... 20+ window operations
182 +
183 + // Monitor operations
184 + get_monitors: proc(...) -> []Monitor_Handle,
185 + // ... monitor operations
186 + }
187 + ```
188 +
189 + ### Backend Selection
190 +
191 + ```odin
192 + select_backend :: proc() -> Backend {
193 + if os.getenv("WAYLAND_DISPLAY") != "" {
194 + return .Wayland
195 + }
196 + return .X11
197 + }
198 + ```
199 +
200 + ---
201 +
202 + ## Example Usage
203 +
204 + ```odin
205 + package main
206 + import "window"
207 +
208 + main :: proc() {
209 + loop, _ := window.create_event_loop()
210 + defer window.destroy_event_loop(loop)
211 +
212 + attrs := window.default_window_attributes()
213 + attrs.title = "My App"
214 + win, _ := window.create_window(loop, attrs)
215 +
216 + window.run(loop, proc(event: window.Event, loop: ^window.Event_Loop) -> window.Control_Flow {
217 + switch e in event {
218 + case window.Window_Event_Data:
219 + switch we in e.event {
220 + case window.Close_Requested:
221 + window.exit(loop)
222 + case window.Redraw_Requested:
223 + // Render here
224 + }
225 + }
226 + return window.Wait{}
227 + })
228 + }
229 + ```
230 +
231 + ---
232 +
233 + ## Implementation Phases
234 +
235 + ### Phase 1: Core Foundation
236 + - Package structure with stub files
237 + - Core types (Event, Control_Flow, Window_Id)
238 + - Basic Event_Loop with X11 backend
239 + - Create window, handle close/resize events
240 + - **Test**: Window that responds to close button
241 +
242 + ### Phase 2: Input Events
243 + - Key events with scancode mapping
244 + - Mouse button/motion/wheel events
245 + - Modifier key tracking
246 + - **Test**: Print all input events
247 +
248 + ### Phase 3: Window Operations
249 + - Resize, move, size constraints
250 + - Visibility, decorations, resizable toggle
251 + - Maximize/minimize, focus
252 + - **Test**: Interactive window management demo
253 +
254 + ### Phase 4: Monitor API
255 + - Monitor enumeration and properties
256 + - Video modes
257 + - Borderless and exclusive fullscreen
258 + - **Test**: Fullscreen toggle demo
259 +
260 + ### Phase 5: Event Loop Polish
261 + - Wait and WaitUntil control flow
262 + - wake_up() for cross-thread signaling
263 + - Redraw request batching
264 + - **Test**: Benchmark control flow modes
265 +
266 + ### Phase 6: Wayland Backend
267 + - Wayland connection/display
268 + - xdg_surface window creation
269 + - Port all operations to Wayland
270 + - Handle CSD and Wayland quirks
271 + - **Test**: All tests pass on Wayland
272 +
273 + ### Phase 7: DPI/Scale Factor
274 + - Scale factor detection
275 + - Scale_Factor_Changed events
276 + - **Test**: Correct rendering at different DPIs
277 +
278 + ### Phase 8: Documentation & Examples
279 + - API documentation
280 + - Examples: basic, input, fullscreen, multi-window, Vulkan
281 + - Performance testing
282 +
283 + ---
284 +
285 + ## Verification
286 +
287 + After each phase:
288 + 1. Build with `odin build .`
289 + 2. Run example programs
290 + 3. Test on both X11 and Wayland (after Phase 6)
291 + 4. Check for memory leaks with allocator tracking
readme.md +44 -0 added
1 + # Window
2 +
window/error.odin added

Inline diff hidden to keep this page fast.

window/event_loop.odin added

Inline diff hidden to keep this page fast.

window/input.odin added

Inline diff hidden to keep this page fast.

window/linux/wayland/bindings.odin added

Inline diff hidden to keep this page fast.

window/linux/wayland/interfaces.odin added

Inline diff hidden to keep this page fast.

window/linux/wayland/wayland.odin added

Inline diff hidden to keep this page fast.

window/linux/wayland/xdg.odin added

Inline diff hidden to keep this page fast.

window/linux/wayland/xkb.odin added

Inline diff hidden to keep this page fast.

window/linux/x11/x11.odin added

Inline diff hidden to keep this page fast.

window/platform_linux.odin added

Inline diff hidden to keep this page fast.

window/platform_windows.odin added

Inline diff hidden to keep this page fast.

window/tracking_allocator.odin added

Inline diff hidden to keep this page fast.

window/types.odin added

Inline diff hidden to keep this page fast.

window/win32/win32.odin added

Inline diff hidden to keep this page fast.

window/win32/win32_cursor.odin added

Inline diff hidden to keep this page fast.

window/win32/win32_events.odin added

Inline diff hidden to keep this page fast.

window/win32/win32_keys.odin added

Inline diff hidden to keep this page fast.

window/win32/win32_window.odin added

Inline diff hidden to keep this page fast.

window/window.odin added

Inline diff hidden to keep this page fast.

window/window_api.odin added

Inline diff hidden to keep this page fast.