Compare commits
3
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
be516d39a3 | ||
|
|
e7cc935035 | ||
|
|
416b42c27e |
@@ -7,11 +7,6 @@ $VERSION = "dev-$(Get-Date -Format 'yyyy-MM-dd')-$COMMIT"
|
|||||||
$OPTION = $args[0]
|
$OPTION = $args[0]
|
||||||
if ($args) { $args = $args[1..($args.Length - 1)] } else { $args = @() }
|
if ($args) { $args = $args[1..($args.Length - 1)] } else { $args = @() }
|
||||||
|
|
||||||
# ------------------------------------------
|
|
||||||
|
|
||||||
# Setup compiled wgpu binary
|
|
||||||
& (Join-Path $PSScriptRoot "scripts\wgpu-init.ps1")
|
|
||||||
|
|
||||||
# ------------------------------------------
|
# ------------------------------------------
|
||||||
# Game compile
|
# Game compile
|
||||||
|
|
||||||
@@ -21,7 +16,7 @@ New-Item -ItemType Directory -Force -Path build | Out-Null
|
|||||||
odin build game/ -show-timings `
|
odin build game/ -show-timings `
|
||||||
-collection:sindri=src `
|
-collection:sindri=src `
|
||||||
-build-mode:dynamic `
|
-build-mode:dynamic `
|
||||||
-out:build/game_tmp.dll -microarch:native "-define:VERSION=$VERSION-debug" -debug @args
|
-out:build/game_tmp -microarch:native "-define:VERSION=$VERSION-debug" -debug @args
|
||||||
|
|
||||||
# Need to use a temp file on Windows because it first writes an empty file,
|
# Need to use a temp file on Windows because it first writes an empty file,
|
||||||
# which the engine will load before it is actually fully written.
|
# which the engine will load before it is actually fully written.
|
||||||
@@ -46,7 +41,7 @@ if ($OPTION -eq "debug") {
|
|||||||
-collection:sindri=src `
|
-collection:sindri=src `
|
||||||
-collection:gram=vendor/gram/src `
|
-collection:gram=vendor/gram/src `
|
||||||
-collection:wgpu=vendor `
|
-collection:wgpu=vendor `
|
||||||
-out:build/$PROJECT.exe -microarch:native -use-separate-modules "-define:VERSION=$VERSION-debug" -debug @args
|
-out:build/$PROJECT -microarch:native -use-separate-modules "-define:VERSION=$VERSION-debug" -debug @args
|
||||||
exit 0
|
exit 0
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,4 +49,4 @@ odin build src/ -show-timings `
|
|||||||
-collection:sindri=src `
|
-collection:sindri=src `
|
||||||
-collection:gram=vendor/gram/src `
|
-collection:gram=vendor/gram/src `
|
||||||
-collection:wgpu=vendor `
|
-collection:wgpu=vendor `
|
||||||
-out:build/$PROJECT.exe -microarch:native -o:speed "-define:VERSION=$VERSION" @args
|
-out:build/$PROJECT -microarch:native -o:speed "-define:VERSION=$VERSION" @args
|
||||||
|
|||||||
@@ -21,10 +21,8 @@ mkdir -p build
|
|||||||
# Game.dll
|
# Game.dll
|
||||||
odin build game/ -show-timings \
|
odin build game/ -show-timings \
|
||||||
-collection:sindri=src \
|
-collection:sindri=src \
|
||||||
-collection:gram=vendor/gram/src \
|
|
||||||
-collection:wgpu=vendor \
|
|
||||||
-build-mode:dynamic \
|
-build-mode:dynamic \
|
||||||
-out:build/game_tmp -microarch:native -o:minimal -use-separate-modules -define:VERSION="$VERSION-debug" -debug "$@"
|
-out:build/game_tmp -microarch:native -define:VERSION="$VERSION-debug" -debug "$@"
|
||||||
|
|
||||||
# Need to use a temp file on Linux/macOS because it first writes an empty file,
|
# Need to use a temp file on Linux/macOS because it first writes an empty file,
|
||||||
# which the engine will load before it is actually fully written.
|
# which the engine will load before it is actually fully written.
|
||||||
@@ -49,7 +47,7 @@ if [ "$OPTION" = "debug" ]; then
|
|||||||
-collection:sindri=src \
|
-collection:sindri=src \
|
||||||
-collection:gram=vendor/gram/src \
|
-collection:gram=vendor/gram/src \
|
||||||
-collection:wgpu=vendor \
|
-collection:wgpu=vendor \
|
||||||
-out:build/$PROJECT -microarch:native -o:minimal -use-separate-modules -define:VERSION="$VERSION-debug" -debug "$@"
|
-out:build/$PROJECT -microarch:native -use-separate-modules -define:VERSION="$VERSION-debug" -debug "$@"
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|||||||
+6
-53
@@ -4,66 +4,19 @@ This document contains specifics about the workings of the Odin programming
|
|||||||
language, dynamic linking and state management design patterns. These are
|
language, dynamic linking and state management design patterns. These are
|
||||||
tested and validated findings, for continued reference by me.
|
tested and validated findings, for continued reference by me.
|
||||||
|
|
||||||
## Compiler
|
|
||||||
|
|
||||||
Odin's compiler is demand-driver: a procedure's body is only semantically
|
|
||||||
analyzed when the proc is actually referenced.
|
|
||||||
|
|
||||||
## Dynamic Library Memory
|
## Dynamic Library Memory
|
||||||
|
|
||||||
When a program loads in a dynamic library, what happens with package globals?
|
When a program loads in a dynamic library, what happens with package globals?
|
||||||
|
|
||||||
Findings (macOS):
|
Findings (macOS):
|
||||||
- Package globals are shared between modules, but only indirectly
|
- The package globals are shared between the host and the lib
|
||||||
- This is due to the dynamic linker merging symbols into a (single) flat namespace
|
|
||||||
- Practically this means the lib's unset package globals get shadowed by the hosts'
|
|
||||||
- After hot reloading the lib, the memory of the packge is not refreshed,
|
- After hot reloading the lib, the memory of the packge is not refreshed,
|
||||||
it keeps pointing to the package global of the host
|
it keeps pointing to the package global of the host
|
||||||
|
|
||||||
Findings (Windows):
|
The clanker states this is due to "flat-namespace symbol interposition".
|
||||||
- Package globals are not shared between modules, they have their own copy
|
|
||||||
|
|
||||||
Findings (Linux):
|
Linux "ELF has symbol preemption: when resolving references, ld.so searches
|
||||||
- Package globals are not shared between modules, they have their own copy
|
globals scope in order — executable first, then loaded shared objects".
|
||||||
|
|
||||||
Conclusion: avoid package globals, or pass pointers to the dynamic library so
|
Windows PE has no interposition at all, every module (exe and each DLL) has its
|
||||||
it can match its package globals with the host module.
|
own symbol table.
|
||||||
|
|
||||||
## Dependency Patterns
|
|
||||||
|
|
||||||
1. Strict layering, only call downwards. If two modules need each other, one of
|
|
||||||
the modules is in a wrong layer. Move it, or extract the shared part into a
|
|
||||||
lower layer.
|
|
||||||
2. Dependency inversion: if required, define an interface in the lower layer,
|
|
||||||
upper layer registers itself.
|
|
||||||
3. Dependency Injection: Prevent dependencies via data, over includes. Pass what
|
|
||||||
a function needs as arguments instead of reaching another module's state.
|
|
||||||
4. Events instead of callbacks both ways If A and B would call each other,
|
|
||||||
have A push events into a queue and B consume them. Neither module includes
|
|
||||||
the other; they only share the event type (which lives in a lower layer).
|
|
||||||
5. Merge or split as a last resort, if two modules genuinely can't be untangled.
|
|
||||||
|
|
||||||
Sindri Engine:
|
|
||||||
|
|
||||||
```
|
|
||||||
+---------------------+ Game:
|
|
||||||
| Game | - Game
|
|
||||||
+---------------------+
|
|
||||||
|
|
|
||||||
v
|
|
||||||
+---------------------+ +---------------------+ Core:
|
|
||||||
| Core |<----+----| main | - Input
|
|
||||||
+---------------------+ | +---------------------+
|
|
||||||
| |
|
|
||||||
v |
|
|
||||||
+---------------------+ | Platform:
|
|
||||||
| Platform |<----+ - GLFW
|
|
||||||
+---------------------+ | - wgpu
|
|
||||||
| |
|
|
||||||
v |
|
|
||||||
+---------------------+ | Base:
|
|
||||||
| Base |<----+ - Hot Reload
|
|
||||||
+---------------------+ - Settings
|
|
||||||
- Event
|
|
||||||
- Keycodes
|
|
||||||
```
|
|
||||||
|
|||||||
+17
-8
@@ -1,10 +1,11 @@
|
|||||||
package game
|
package game
|
||||||
|
|
||||||
|
import "sindri:input"
|
||||||
import "core:time"
|
import "core:time"
|
||||||
import "core:fmt"
|
import "core:fmt"
|
||||||
|
|
||||||
import "sindri:base"
|
|
||||||
import "sindri:core"
|
import "sindri:core"
|
||||||
|
import "sindri:test"
|
||||||
|
|
||||||
// -----------------------------------------
|
// -----------------------------------------
|
||||||
|
|
||||||
@@ -40,12 +41,12 @@ memory_set :: proc(mem: rawptr) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@(export)
|
@(export)
|
||||||
settings :: proc() -> base.Settings {
|
settings :: proc() -> core.Settings {
|
||||||
return base.Settings {
|
return core.Settings {
|
||||||
width = 960,
|
width = 960,
|
||||||
height = 540,
|
height = 540,
|
||||||
title = "WGPU Native Triangle",
|
title = "WGPU Native Triangle",
|
||||||
mode = base.WindowMode.Windowed,
|
mode = core.WindowMode.Windowed,
|
||||||
refresh = 60,
|
refresh = 60,
|
||||||
vsync = true,
|
vsync = true,
|
||||||
}
|
}
|
||||||
@@ -54,6 +55,8 @@ settings :: proc() -> base.Settings {
|
|||||||
@(export)
|
@(export)
|
||||||
init_once :: proc() {
|
init_once :: proc() {
|
||||||
fmt.println("init once")
|
fmt.println("init once")
|
||||||
|
|
||||||
|
test.test_proc = proc() {}
|
||||||
}
|
}
|
||||||
|
|
||||||
@(export)
|
@(export)
|
||||||
@@ -62,15 +65,21 @@ init :: proc() {
|
|||||||
|
|
||||||
g = new(Game_Memory)
|
g = new(Game_Memory)
|
||||||
memory_set(g)
|
memory_set(g)
|
||||||
|
|
||||||
|
test.test_proc = proc() { asd := 2 }
|
||||||
}
|
}
|
||||||
|
|
||||||
@(export)
|
@(export)
|
||||||
update :: proc(dt: f32) {
|
update :: proc(dt: f32) {
|
||||||
fmt.println("dt:", dt)
|
fmt.println("dt:", dt)
|
||||||
|
|
||||||
if core.key_state(.Key_Escape) == .Press {
|
if input.key_state(.Key_Escape) == .Press {
|
||||||
g.should_close = true
|
g.should_close = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fmt.println("GAME:", test.test)
|
||||||
|
fmt.printf("pointer: %p | %p\n", &test.test, &test.test_proc)
|
||||||
|
test.test += 1
|
||||||
}
|
}
|
||||||
|
|
||||||
@(export)
|
@(export)
|
||||||
@@ -84,17 +93,17 @@ start := time.tick_now()
|
|||||||
should_close :: proc() -> bool {
|
should_close :: proc() -> bool {
|
||||||
elapsed := time.tick_since(start)
|
elapsed := time.tick_since(start)
|
||||||
seconds := time.duration_seconds(elapsed)
|
seconds := time.duration_seconds(elapsed)
|
||||||
if seconds > 4 do return true
|
// if seconds > 4 do return true
|
||||||
|
|
||||||
return g.should_close
|
return g.should_close
|
||||||
}
|
}
|
||||||
|
|
||||||
@(export)
|
@(export)
|
||||||
force_reload :: proc() -> bool {
|
force_reload :: proc() -> bool {
|
||||||
return core.key_state(.Key_F5) == .Press
|
return input.key_state(.Key_F5) == .Press
|
||||||
}
|
}
|
||||||
|
|
||||||
@(export)
|
@(export)
|
||||||
force_restart :: proc() -> bool {
|
force_restart :: proc() -> bool {
|
||||||
return core.key_state(.Key_F6) == .Press
|
return input.key_state(.Key_F6) == .Press
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,100 +0,0 @@
|
|||||||
$ErrorActionPreference = "Stop"
|
|
||||||
|
|
||||||
$VENDOR_DIR = Join-Path $PSScriptRoot "..\vendor" | Resolve-Path
|
|
||||||
|
|
||||||
# ------------------------------------------
|
|
||||||
# Platform detection (target dir name, only used for the up-to-date check)
|
|
||||||
|
|
||||||
# Windows PowerShell 5.1 doesn't define $IsWindows/$IsMacOS/$IsLinux
|
|
||||||
if (-not (Test-Path variable:IsWindows)) { $IsWindows = $env:OS -eq "Windows_NT" }
|
|
||||||
if (-not (Test-Path variable:IsMacOS)) { $IsMacOS = $false }
|
|
||||||
if (-not (Test-Path variable:IsLinux)) { $IsLinux = $false }
|
|
||||||
|
|
||||||
if ($IsWindows) {
|
|
||||||
$os = "windows"
|
|
||||||
$extra = "-msvc"
|
|
||||||
} elseif ($IsMacOS) {
|
|
||||||
$os = "macos"
|
|
||||||
$extra = ""
|
|
||||||
} elseif ($IsLinux) {
|
|
||||||
$os = "linux"
|
|
||||||
$extra = ""
|
|
||||||
} else {
|
|
||||||
Write-Error "wgpu setup: unsupported OS"
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
|
|
||||||
$procArch = [System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture
|
|
||||||
$arch = if ($procArch -eq [System.Runtime.InteropServices.Architecture]::Arm64) { "aarch64" } else { "x86_64" }
|
|
||||||
|
|
||||||
$odinRoot = (odin root).Trim()
|
|
||||||
$name = "wgpu-$os-$arch$extra-release"
|
|
||||||
$target = Join-Path $VENDOR_DIR "wgpu\lib\$name"
|
|
||||||
|
|
||||||
# ------------------------------------------
|
|
||||||
# WGPU version detection
|
|
||||||
|
|
||||||
if ($args.Count -gt 0 -and $args[0]) {
|
|
||||||
$WGPU_VERSION = $args[0]
|
|
||||||
} else {
|
|
||||||
$odinTypes = Join-Path $odinRoot "vendor\wgpu\wgpu_native_types.odin"
|
|
||||||
$typesContent = Get-Content $odinTypes -Raw
|
|
||||||
if ($typesContent -match 'BINDINGS_VERSION_STRING :: "([^"]*)"') {
|
|
||||||
$WGPU_VERSION = "v" + $Matches[1]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (-not $WGPU_VERSION -or $WGPU_VERSION -eq "v") {
|
|
||||||
Write-Error "wgpu setup: could not detect the wgpu-native version"
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
|
|
||||||
# Detect the version of the vendored wgpu bindings
|
|
||||||
$currentTypes = Join-Path $VENDOR_DIR "wgpu\wgpu_native_types.odin"
|
|
||||||
$current = $null
|
|
||||||
if (Test-Path $currentTypes) {
|
|
||||||
$cur = Get-Content $currentTypes -Raw
|
|
||||||
if ($cur -match 'BINDINGS_VERSION_STRING :: "([^"]*)"') {
|
|
||||||
$current = "v" + $Matches[1]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# Check if the vendored wgpu is already up to date
|
|
||||||
if ($current -eq $WGPU_VERSION -and (Test-Path $target)) {
|
|
||||||
exit 0
|
|
||||||
}
|
|
||||||
|
|
||||||
# ------------------------------------------
|
|
||||||
# Copy vendor/wgpu
|
|
||||||
|
|
||||||
# Remove any stale copy
|
|
||||||
if (Test-Path (Join-Path $VENDOR_DIR "wgpu")) {
|
|
||||||
Remove-Item -Recurse -Force (Join-Path $VENDOR_DIR "wgpu")
|
|
||||||
}
|
|
||||||
|
|
||||||
# Copy wgpu
|
|
||||||
$wgpuPath = Join-Path $odinRoot "vendor\wgpu"
|
|
||||||
Copy-Item -Recurse $wgpuPath $VENDOR_DIR
|
|
||||||
|
|
||||||
# Point imports at the copied vendor wgpu package
|
|
||||||
Get-ChildItem -Path (Join-Path $VENDOR_DIR "wgpu") -Recurse -Filter *.odin | ForEach-Object {
|
|
||||||
(Get-Content $_.FullName -Raw) -replace '"vendor:wgpu"', '"wgpu:wgpu"' | Set-Content $_.FullName -NoNewline
|
|
||||||
}
|
|
||||||
|
|
||||||
# ------------------------------------------
|
|
||||||
# Download
|
|
||||||
|
|
||||||
$assetBase = "https://github.com/gfx-rs/wgpu-native/releases/download/$WGPU_VERSION"
|
|
||||||
|
|
||||||
if (-not (Test-Path $target)) {
|
|
||||||
$url = "$assetBase/$name.zip"
|
|
||||||
Write-Output "wgpu setup: downloading $url"
|
|
||||||
$tmp = Join-Path ([System.IO.Path]::GetTempPath()) ([System.Guid]::NewGuid().ToString())
|
|
||||||
New-Item -ItemType Directory -Path $tmp | Out-Null
|
|
||||||
try {
|
|
||||||
Invoke-WebRequest -MaximumRedirection 5 -Uri $url -OutFile (Join-Path $tmp "wgpu.zip")
|
|
||||||
Expand-Archive -Force -Path (Join-Path $tmp "wgpu.zip") -DestinationPath $target
|
|
||||||
} finally {
|
|
||||||
Remove-Item -Recurse -Force $tmp
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,268 +0,0 @@
|
|||||||
package base
|
|
||||||
|
|
||||||
import "base:intrinsics"
|
|
||||||
import "base:runtime"
|
|
||||||
import "core:fmt"
|
|
||||||
|
|
||||||
// -----------------------------------------
|
|
||||||
// Types
|
|
||||||
|
|
||||||
// event category, bitfield (?)
|
|
||||||
|
|
||||||
Event_Data :: union {
|
|
||||||
Window_Close_Event,
|
|
||||||
Window_Resize_Event,
|
|
||||||
Joystick_Connect_Event,
|
|
||||||
Joystick_Disconnect_Event,
|
|
||||||
Key_Press_Event,
|
|
||||||
Key_Release_Event,
|
|
||||||
Key_Repeat_Event,
|
|
||||||
Mouse_Button_Press_Event,
|
|
||||||
Mouse_Button_Release_Event,
|
|
||||||
Mouse_Position_Event,
|
|
||||||
Mouse_Scroll_Event,
|
|
||||||
}
|
|
||||||
|
|
||||||
Event :: struct {
|
|
||||||
data: Event_Data,
|
|
||||||
handled: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
Window_Close_Event :: struct {}
|
|
||||||
|
|
||||||
Window_Resize_Event :: struct {
|
|
||||||
width: i32,
|
|
||||||
height: i32,
|
|
||||||
}
|
|
||||||
|
|
||||||
Key_Press_Event :: struct {
|
|
||||||
key: Key,
|
|
||||||
mods: Mod_Set,
|
|
||||||
}
|
|
||||||
|
|
||||||
Key_Release_Event :: struct {
|
|
||||||
key: Key,
|
|
||||||
mods: Mod_Set,
|
|
||||||
}
|
|
||||||
|
|
||||||
Key_Repeat_Event :: struct {
|
|
||||||
key: Key,
|
|
||||||
mods: Mod_Set,
|
|
||||||
}
|
|
||||||
|
|
||||||
Mouse_Button_Press_Event :: struct {
|
|
||||||
button: Mouse_Button,
|
|
||||||
mods: Mod_Set,
|
|
||||||
}
|
|
||||||
|
|
||||||
Mouse_Button_Release_Event :: struct {
|
|
||||||
button: Mouse_Button,
|
|
||||||
mods: Mod_Set,
|
|
||||||
}
|
|
||||||
|
|
||||||
Mouse_Position_Event :: struct {
|
|
||||||
x_pos: f32,
|
|
||||||
y_pos: f32,
|
|
||||||
}
|
|
||||||
|
|
||||||
Mouse_Scroll_Event :: struct {
|
|
||||||
x_offset: f32,
|
|
||||||
y_offset: f32,
|
|
||||||
}
|
|
||||||
|
|
||||||
Joystick_Connect_Event :: struct {
|
|
||||||
id: i32,
|
|
||||||
}
|
|
||||||
|
|
||||||
Joystick_Disconnect_Event :: struct {
|
|
||||||
id: i32,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Message Receiver
|
|
||||||
@(private)
|
|
||||||
Listener :: struct {
|
|
||||||
callback: proc(event: ^Event_Data, user_ptr: rawptr) -> bool, // return true = don't propagate
|
|
||||||
user_ptr: rawptr,
|
|
||||||
// TODO: test if hot_reload keeps the same function pointers and the listeners remain working.
|
|
||||||
// if they dont add a flag "engine_listener: bool", which engine calls should set true,
|
|
||||||
// then clear all game listeners
|
|
||||||
}
|
|
||||||
|
|
||||||
// Transfers messages between Sender and Receiver
|
|
||||||
Event_Bus :: struct {
|
|
||||||
queue: [dynamic]Event,
|
|
||||||
listeners: map[typeid][dynamic]Listener,
|
|
||||||
allocator: runtime.Allocator,
|
|
||||||
}
|
|
||||||
|
|
||||||
// -----------------------------------------
|
|
||||||
// Variables
|
|
||||||
|
|
||||||
event_bus: Event_Bus
|
|
||||||
|
|
||||||
// -----------------------------------------
|
|
||||||
// Constructor/destructor
|
|
||||||
|
|
||||||
event_bus_init :: proc(allocator := context.allocator) {
|
|
||||||
// make() doesnt allocate until there's capacity, so set it > 0
|
|
||||||
queue := make([dynamic]Event, 0, 4, allocator)
|
|
||||||
listeners := make(map[typeid][dynamic]Listener, 4, allocator)
|
|
||||||
event_bus = {
|
|
||||||
queue = queue,
|
|
||||||
listeners = listeners,
|
|
||||||
allocator = allocator,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
event_bus_destroy :: proc() {
|
|
||||||
assert(event_bus.queue != nil)
|
|
||||||
assert(event_bus.listeners != nil)
|
|
||||||
|
|
||||||
for _, ls in event_bus.listeners do delete(ls)
|
|
||||||
|
|
||||||
delete(event_bus.queue)
|
|
||||||
delete(event_bus.listeners)
|
|
||||||
|
|
||||||
event_bus.queue = nil
|
|
||||||
event_bus.listeners = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// -----------------------------------------
|
|
||||||
// Public functions
|
|
||||||
|
|
||||||
event_subscribe :: proc(
|
|
||||||
$T: typeid,
|
|
||||||
callback: proc(event: ^T, user_ptr: rawptr) -> bool,
|
|
||||||
user_ptr: rawptr = nil,
|
|
||||||
) where intrinsics.type_is_variant_of(Event_Data, T) {
|
|
||||||
cb := cast(proc(_: ^Event_Data, _: rawptr) -> bool)callback // upcast
|
|
||||||
event_subscribe_raw(T, cb, user_ptr)
|
|
||||||
}
|
|
||||||
|
|
||||||
event_unsubscribe :: proc(
|
|
||||||
$T: typeid,
|
|
||||||
callback: proc(event: ^T, user_ptr: rawptr) -> bool,
|
|
||||||
user_ptr: rawptr = nil,
|
|
||||||
) where intrinsics.type_is_variant_of(Event_Data, T) {
|
|
||||||
cb := cast(proc(_: ^Event_Data, _: rawptr) -> bool)callback // upcast
|
|
||||||
event_unsubscribe_raw(T, cb, user_ptr)
|
|
||||||
}
|
|
||||||
|
|
||||||
on_event :: proc(data: Event_Data) {
|
|
||||||
assert(event_bus.queue != nil)
|
|
||||||
|
|
||||||
append(&event_bus.queue, Event{data = data})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Drain all the events that have been queued up, in enqueue order
|
|
||||||
drain_event_queue :: proc() {
|
|
||||||
assert(event_bus.queue != nil)
|
|
||||||
|
|
||||||
fmt.println("toby!")
|
|
||||||
|
|
||||||
for &event in event_bus.queue {
|
|
||||||
|
|
||||||
#partial switch e in event.data {
|
|
||||||
case Window_Close_Event:
|
|
||||||
event_dispatch(Window_Close_Event, &event)
|
|
||||||
fmt.println("Close")
|
|
||||||
case Window_Resize_Event:
|
|
||||||
event_dispatch(Window_Resize_Event, &event)
|
|
||||||
fmt.println("Resize:", e.width, e.height)
|
|
||||||
|
|
||||||
case Key_Press_Event:
|
|
||||||
event_dispatch(Key_Press_Event, &event)
|
|
||||||
fmt.println("Key press", e.key, e.mods)
|
|
||||||
case Key_Release_Event:
|
|
||||||
event_dispatch(Key_Release_Event, &event)
|
|
||||||
fmt.println("Key release", e.key, e.mods)
|
|
||||||
case Key_Repeat_Event:
|
|
||||||
event_dispatch(Key_Repeat_Event, &event)
|
|
||||||
fmt.println("Key repeat", e.key, e.mods)
|
|
||||||
|
|
||||||
case Mouse_Button_Press_Event:
|
|
||||||
event_dispatch(Mouse_Button_Press_Event, &event)
|
|
||||||
fmt.println("Mouse press", e.button, e.mods)
|
|
||||||
case Mouse_Button_Release_Event:
|
|
||||||
event_dispatch(Mouse_Button_Release_Event, &event)
|
|
||||||
fmt.println("Mouse release", e.button, e.mods)
|
|
||||||
case Mouse_Position_Event:
|
|
||||||
event_dispatch(Mouse_Position_Event, &event)
|
|
||||||
fmt.printfln("Mouse pos: {}x{}", e.x_pos, e.y_pos)
|
|
||||||
case Mouse_Scroll_Event:
|
|
||||||
event_dispatch(Mouse_Scroll_Event, &event)
|
|
||||||
fmt.printfln("Mouse scroll: {}x{}", e.x_offset, e.y_offset)
|
|
||||||
|
|
||||||
case Joystick_Connect_Event:
|
|
||||||
event_dispatch(Joystick_Connect_Event, &event)
|
|
||||||
fmt.println("Joy con:", e.id)
|
|
||||||
case Joystick_Disconnect_Event:
|
|
||||||
event_dispatch(Joystick_Disconnect_Event, &event)
|
|
||||||
fmt.println("Joy dis:", e.id)
|
|
||||||
|
|
||||||
case:
|
|
||||||
panic("[event] unimplemented event")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
clear(&event_bus.queue)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Dispatch to the listeners registered for T, in reverse subscription order.
|
|
||||||
// First listener to return true claims the event and stops propagation
|
|
||||||
event_dispatch :: proc(
|
|
||||||
$T: typeid,
|
|
||||||
event: ^Event,
|
|
||||||
) -> bool where intrinsics.type_is_variant_of(Event_Data, T) {
|
|
||||||
assert(event_bus.listeners != nil)
|
|
||||||
|
|
||||||
ls, ok := event_bus.listeners[T]
|
|
||||||
if !ok do return false
|
|
||||||
|
|
||||||
#reverse for &l in ls {
|
|
||||||
callback := cast(proc(_: ^T, _: rawptr) -> bool)l.callback // downcast
|
|
||||||
if callback(&event.data.(T), l.user_ptr) {
|
|
||||||
event.handled = true
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// -----------------------------------------
|
|
||||||
// Private functions
|
|
||||||
|
|
||||||
@(private)
|
|
||||||
event_subscribe_raw :: proc(
|
|
||||||
$T: typeid,
|
|
||||||
callback: proc(event: ^Event_Data, user_ptr: rawptr) -> bool,
|
|
||||||
user_ptr: rawptr = nil,
|
|
||||||
) where intrinsics.type_is_variant_of(Event_Data, T) {
|
|
||||||
assert(event_bus.listeners != nil)
|
|
||||||
|
|
||||||
if _, ok := event_bus.listeners[T]; !ok {
|
|
||||||
event_bus.listeners[T] = make([dynamic]Listener, event_bus.allocator)
|
|
||||||
}
|
|
||||||
|
|
||||||
append(&event_bus.listeners[T], Listener{callback, user_ptr})
|
|
||||||
}
|
|
||||||
|
|
||||||
@(private)
|
|
||||||
event_unsubscribe_raw :: proc(
|
|
||||||
$T: typeid,
|
|
||||||
callback: proc(event: ^Event_Data, user_ptr: rawptr) -> bool,
|
|
||||||
user_ptr: rawptr = nil,
|
|
||||||
) where intrinsics.type_is_variant_of(Event_Data, T) {
|
|
||||||
assert(event_bus.listeners != nil)
|
|
||||||
|
|
||||||
ls, ok := event_bus.listeners[T]
|
|
||||||
if !ok do return
|
|
||||||
|
|
||||||
for i, l in ls {
|
|
||||||
if l.callback == callback && l.user_ptr == user_ptr {
|
|
||||||
ordered_remove(&event_bus.listeners[T], i) // preserve subscription order
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
package core
|
|
||||||
|
|
||||||
import "sindri:base"
|
|
||||||
import "sindri:platform"
|
|
||||||
|
|
||||||
// -----------------------------------------
|
|
||||||
// Public functions
|
|
||||||
|
|
||||||
// Returns the state of the keyboard key
|
|
||||||
key_state :: proc(key: base.Key) -> base.Action {
|
|
||||||
return platform.os_key_state(key)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns the state of the mouse button
|
|
||||||
mouse_button_state :: proc(button: base.Mouse_Button) -> base.Action {
|
|
||||||
return platform.os_mouse_button_state(button)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns the position of the mouse cursor
|
|
||||||
mouse_position :: proc() -> (x_pos: f32, y_pos: f32) {
|
|
||||||
return platform.os_mouse_position()
|
|
||||||
}
|
|
||||||
|
|
||||||
// -----------------------------------------
|
|
||||||
// Private functions
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package base
|
package core
|
||||||
|
|
||||||
// -----------------------------------------
|
// -----------------------------------------
|
||||||
|
|
||||||
@@ -0,0 +1,138 @@
|
|||||||
|
package event
|
||||||
|
|
||||||
|
import "core:fmt"
|
||||||
|
|
||||||
|
import "sindri:input"
|
||||||
|
|
||||||
|
// -----------------------------------------
|
||||||
|
// Types
|
||||||
|
|
||||||
|
// event category, bitfield (?)
|
||||||
|
|
||||||
|
// Event_Data :: union {
|
||||||
|
Event :: union {
|
||||||
|
Window_Close_Event,
|
||||||
|
Window_Resize_Event,
|
||||||
|
Joystick_Connect_Event,
|
||||||
|
Joystick_Disconnect_Event,
|
||||||
|
Key_Press_Event,
|
||||||
|
Key_Release_Event,
|
||||||
|
Key_Repeat_Event,
|
||||||
|
Mouse_Button_Press_Event,
|
||||||
|
Mouse_Button_Release_Event,
|
||||||
|
Mouse_Position_Event,
|
||||||
|
Mouse_Scroll_Event,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Event :: struct {
|
||||||
|
// data: Event_Data,
|
||||||
|
// handled: bool,
|
||||||
|
// }
|
||||||
|
|
||||||
|
Window_Close_Event :: struct {
|
||||||
|
handled: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
Window_Resize_Event :: struct {
|
||||||
|
handled: bool,
|
||||||
|
width: i32,
|
||||||
|
height: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
Joystick_Connect_Event :: struct {
|
||||||
|
handled: bool,
|
||||||
|
id: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
Joystick_Disconnect_Event :: struct {
|
||||||
|
handled: bool,
|
||||||
|
id: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
Key_Press_Event :: struct {
|
||||||
|
handled: bool,
|
||||||
|
key: input.Key,
|
||||||
|
mods: input.Mod_Set,
|
||||||
|
}
|
||||||
|
|
||||||
|
Key_Release_Event :: struct {
|
||||||
|
handled: bool,
|
||||||
|
key: input.Key,
|
||||||
|
mods: input.Mod_Set,
|
||||||
|
}
|
||||||
|
|
||||||
|
Key_Repeat_Event :: struct {
|
||||||
|
handled: bool,
|
||||||
|
key: input.Key,
|
||||||
|
mods: input.Mod_Set,
|
||||||
|
}
|
||||||
|
|
||||||
|
Mouse_Button_Press_Event :: struct {
|
||||||
|
handled: bool,
|
||||||
|
button: input.Mouse_Button,
|
||||||
|
mods: input.Mod_Set,
|
||||||
|
}
|
||||||
|
|
||||||
|
Mouse_Button_Release_Event :: struct {
|
||||||
|
handled: bool,
|
||||||
|
button: input.Mouse_Button,
|
||||||
|
mods: input.Mod_Set,
|
||||||
|
}
|
||||||
|
|
||||||
|
Mouse_Position_Event :: struct {
|
||||||
|
handled: bool,
|
||||||
|
x_pos: f32,
|
||||||
|
y_pos: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
Mouse_Scroll_Event :: struct {
|
||||||
|
handled: bool,
|
||||||
|
x_offset: f32,
|
||||||
|
y_offset: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------
|
||||||
|
// Variables
|
||||||
|
|
||||||
|
// I want to have a list of Listeners per Event type, but this is state
|
||||||
|
// listeners: map[typeid][dynamic]Listener
|
||||||
|
|
||||||
|
// -----------------------------------------
|
||||||
|
// Public functions
|
||||||
|
|
||||||
|
// Dispatcher(Event) -> forwards to the right function
|
||||||
|
on_event :: proc(event: Event) {
|
||||||
|
fmt.println("toby!")
|
||||||
|
|
||||||
|
#partial switch e in event {
|
||||||
|
case Window_Close_Event:
|
||||||
|
fmt.println("Close")
|
||||||
|
case Window_Resize_Event:
|
||||||
|
fmt.println("Resize:", e.width, e.height)
|
||||||
|
|
||||||
|
case Key_Press_Event:
|
||||||
|
fmt.println("Key press", e.key, e.mods)
|
||||||
|
case Key_Release_Event:
|
||||||
|
fmt.println("Key release", e.key, e.mods)
|
||||||
|
case Key_Repeat_Event:
|
||||||
|
fmt.println("Key repeat", e.key, e.mods)
|
||||||
|
|
||||||
|
case Mouse_Button_Press_Event:
|
||||||
|
fmt.println("Mouse press", e.button, e.mods)
|
||||||
|
case Mouse_Button_Release_Event:
|
||||||
|
fmt.println("Mouse release", e.button, e.mods)
|
||||||
|
case Mouse_Position_Event:
|
||||||
|
fmt.printfln("Mouse pos: {}x{}", e.x_pos, e.y_pos)
|
||||||
|
|
||||||
|
case Joystick_Connect_Event:
|
||||||
|
fmt.println("Joy con:", e.id)
|
||||||
|
case Joystick_Disconnect_Event:
|
||||||
|
fmt.println("Joy dis:", e.id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Notes:
|
||||||
|
|
||||||
|
Have the events queued up into a frame buffer that gets drained at a specific time.
|
||||||
|
*/
|
||||||
@@ -1,11 +1,12 @@
|
|||||||
package base
|
package hot_reload
|
||||||
|
|
||||||
import "core:c"
|
|
||||||
import "core:dynlib"
|
import "core:dynlib"
|
||||||
import "core:fmt"
|
import "core:fmt"
|
||||||
import "core:os"
|
import "core:os"
|
||||||
import "core:time"
|
import "core:time"
|
||||||
|
|
||||||
|
import "sindri:core"
|
||||||
|
|
||||||
when ODIN_OS == .Windows {
|
when ODIN_OS == .Windows {
|
||||||
LIB_EXT :: ".dll"
|
LIB_EXT :: ".dll"
|
||||||
} else when ODIN_OS == .Darwin {
|
} else when ODIN_OS == .Darwin {
|
||||||
@@ -40,7 +41,7 @@ Game_API :: struct {
|
|||||||
memory_free: proc(),
|
memory_free: proc(),
|
||||||
memory_size: proc() -> int,
|
memory_size: proc() -> int,
|
||||||
memory_set: proc(mem: rawptr),
|
memory_set: proc(mem: rawptr),
|
||||||
settings: proc() -> Settings,
|
settings: proc() -> core.Settings,
|
||||||
init_once: proc(),
|
init_once: proc(),
|
||||||
init: proc(),
|
init: proc(),
|
||||||
update: proc(_: f32),
|
update: proc(_: f32),
|
||||||
@@ -131,20 +132,6 @@ load_game_lib :: proc(api: ^Game_API) -> bool {
|
|||||||
"error: failed initializing symbols: {0}",
|
"error: failed initializing symbols: {0}",
|
||||||
dynlib.last_error(),
|
dynlib.last_error(),
|
||||||
)
|
)
|
||||||
} else {
|
|
||||||
// Odin's shared libraries don't register a constructor, so the lib's
|
|
||||||
// runtime init (@init procs like os.stdout setup) never runs on load.
|
|
||||||
// Call it explicitly.
|
|
||||||
// Windows doesn't need this: its dynamic build exports DllMain, which
|
|
||||||
// the OS calls on load and which runs _startup_runtime itself.
|
|
||||||
when ODIN_OS != .Windows {
|
|
||||||
Entry :: #type proc "c" () -> c.int
|
|
||||||
entry_ptr, entry_ok := dynlib.symbol_address(
|
|
||||||
api.lib,
|
|
||||||
"_odin_entry_point",
|
|
||||||
)
|
|
||||||
if entry_ok do (transmute(Entry)entry_ptr)()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return ok
|
return ok
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package input
|
||||||
|
|
||||||
|
// -----------------------------------------
|
||||||
|
// Public functions
|
||||||
|
|
||||||
|
// Returns the state of the keyboard key
|
||||||
|
// implementation in platform package, to prevent cyclic dependency
|
||||||
|
key_state: proc(key: Key) -> Action
|
||||||
|
|
||||||
|
// Returns the state of the mouse button
|
||||||
|
// implementation in platform package, to prevent cyclic dependency
|
||||||
|
mouse_button_state: proc(button: Mouse_Button) -> Action
|
||||||
|
|
||||||
|
// Returns the position of the mouse cursor
|
||||||
|
// implementation in platform package, to prevent cyclic dependency
|
||||||
|
mouse_position: proc() -> (x_pos: f32, y_pos: f32)
|
||||||
|
|
||||||
|
// -----------------------------------------
|
||||||
|
// Private functions
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package base
|
package input
|
||||||
|
|
||||||
// -----------------------------------------
|
// -----------------------------------------
|
||||||
|
|
||||||
+10
-18
@@ -2,8 +2,9 @@ package sindri
|
|||||||
|
|
||||||
import "core:fmt"
|
import "core:fmt"
|
||||||
import "core:time"
|
import "core:time"
|
||||||
|
import "sindri:test"
|
||||||
|
|
||||||
import "sindri:base"
|
import "sindri:hot_reload"
|
||||||
import "sindri:platform"
|
import "sindri:platform"
|
||||||
|
|
||||||
VERSION :: #config(VERSION, "dev")
|
VERSION :: #config(VERSION, "dev")
|
||||||
@@ -14,16 +15,10 @@ main :: proc() {
|
|||||||
fmt.println("hello world!")
|
fmt.println("hello world!")
|
||||||
|
|
||||||
// Hot reload development functionality
|
// Hot reload development functionality
|
||||||
hr, err := base.hot_reload_init()
|
hr, err := hot_reload.hot_reload_init()
|
||||||
defer base.hot_reload_destroy(&hr)
|
defer hot_reload.hot_reload_destroy(&hr)
|
||||||
|
|
||||||
// Event system
|
api := hot_reload.active_lib(&hr)
|
||||||
base.event_bus_init()
|
|
||||||
defer base.event_bus_destroy()
|
|
||||||
|
|
||||||
base.event_subscribe(base.Key_Press_Event, test)
|
|
||||||
|
|
||||||
api := base.active_lib(&hr)
|
|
||||||
settings := api.settings()
|
settings := api.settings()
|
||||||
|
|
||||||
// Initialize Window
|
// Initialize Window
|
||||||
@@ -41,27 +36,24 @@ main :: proc() {
|
|||||||
gt: f64 = 0
|
gt: f64 = 0
|
||||||
dt: f32
|
dt: f32
|
||||||
|
|
||||||
for !platform.os_should_close() && !base.should_close(&hr) {
|
for !platform.os_should_close() && !hot_reload.should_close(&hr) {
|
||||||
start := time.tick_now()
|
start := time.tick_now()
|
||||||
|
|
||||||
platform.os_poll_events()
|
platform.os_poll_events()
|
||||||
base.drain_event_queue()
|
|
||||||
api.update(dt)
|
api.update(dt)
|
||||||
|
|
||||||
|
fmt.println("MAIN:", test.test)
|
||||||
|
fmt.printf("pointer: %p | %p\n", &test.test, &test.test_proc)
|
||||||
|
|
||||||
platform.frame(dt)
|
platform.frame(dt)
|
||||||
|
|
||||||
dt = f32(time.duration_seconds(time.tick_since(start)))
|
dt = f32(time.duration_seconds(time.tick_since(start)))
|
||||||
gt += f64(dt)
|
gt += f64(dt)
|
||||||
|
|
||||||
// Hot reload
|
// Hot reload
|
||||||
api, err = base.reload_game_lib(&hr)
|
api, err = hot_reload.reload_game_lib(&hr)
|
||||||
if err != nil do break
|
if err != nil do break
|
||||||
}
|
}
|
||||||
|
|
||||||
api.destroy()
|
api.destroy()
|
||||||
}
|
}
|
||||||
|
|
||||||
test :: proc(e: ^base.Key_Press_Event, _: rawptr) -> bool {
|
|
||||||
fmt.println("main: @@@@@@@@@@@", e)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|||||||
+53
-43
@@ -7,7 +7,9 @@ import "vendor:glfw"
|
|||||||
import "wgpu:wgpu"
|
import "wgpu:wgpu"
|
||||||
import "wgpu:wgpu/glfwglue"
|
import "wgpu:wgpu/glfwglue"
|
||||||
|
|
||||||
import "sindri:base"
|
import "sindri:core"
|
||||||
|
import "sindri:event"
|
||||||
|
import "sindri:input"
|
||||||
|
|
||||||
// -----------------------------------------
|
// -----------------------------------------
|
||||||
// Types
|
// Types
|
||||||
@@ -19,7 +21,7 @@ OS :: struct {
|
|||||||
// -----------------------------------------
|
// -----------------------------------------
|
||||||
// Constructor / destructor
|
// Constructor / destructor
|
||||||
|
|
||||||
os_init :: proc(settings: base.Settings) {
|
os_init :: proc(settings: core.Settings) {
|
||||||
if !glfw.Init() {
|
if !glfw.Init() {
|
||||||
panic("[glfw] init failure")
|
panic("[glfw] init failure")
|
||||||
}
|
}
|
||||||
@@ -46,6 +48,11 @@ os_init :: proc(settings: base.Settings) {
|
|||||||
glfw.SetCursorPosCallback(state.os.window, cursor_pos_callback)
|
glfw.SetCursorPosCallback(state.os.window, cursor_pos_callback)
|
||||||
glfw.SetScrollCallback(state.os.window, scroll_callback)
|
glfw.SetScrollCallback(state.os.window, scroll_callback)
|
||||||
glfw.SetJoystickCallback(joystick_callback)
|
glfw.SetJoystickCallback(joystick_callback)
|
||||||
|
|
||||||
|
// Register input functions
|
||||||
|
input.key_state = key_state
|
||||||
|
input.mouse_button_state = mouse_button_state
|
||||||
|
input.mouse_position = mouse_position
|
||||||
}
|
}
|
||||||
|
|
||||||
os_destroy :: proc() {
|
os_destroy :: proc() {
|
||||||
@@ -56,7 +63,7 @@ os_destroy :: proc() {
|
|||||||
// -----------------------------------------
|
// -----------------------------------------
|
||||||
// Public functions
|
// Public functions
|
||||||
|
|
||||||
os_set_monitor :: proc(settings: base.Settings) {
|
os_set_monitor :: proc(settings: core.Settings) {
|
||||||
monitor := glfw.GetPrimaryMonitor()
|
monitor := glfw.GetPrimaryMonitor()
|
||||||
x_pos: i32
|
x_pos: i32
|
||||||
y_pos: i32
|
y_pos: i32
|
||||||
@@ -133,44 +140,47 @@ os_get_surface :: proc(instance: wgpu.Instance) -> wgpu.Surface {
|
|||||||
return glfwglue.GetSurface(instance, state.os.window)
|
return glfwglue.GetSurface(instance, state.os.window)
|
||||||
}
|
}
|
||||||
|
|
||||||
os_key_state :: proc(key: base.Key) -> base.Action {
|
|
||||||
return input_action(glfw.GetKey(state.os.window, i32(key)))
|
|
||||||
}
|
|
||||||
|
|
||||||
os_mouse_button_state :: proc(button: base.Mouse_Button) -> base.Action {
|
|
||||||
return input_action(glfw.GetMouseButton(state.os.window, i32(button)))
|
|
||||||
}
|
|
||||||
|
|
||||||
os_mouse_position :: proc() -> (x_pos: f32, y_pos: f32) {
|
|
||||||
x_pos_f64, y_pos_64 := glfw.GetCursorPos(state.os.window)
|
|
||||||
return f32(x_pos_f64), f32(y_pos_64)
|
|
||||||
}
|
|
||||||
|
|
||||||
// -----------------------------------------
|
// -----------------------------------------
|
||||||
// Private functions
|
// Private functions
|
||||||
|
|
||||||
@(private = "file")
|
@(private = "file")
|
||||||
input_action :: proc(action: i32) -> base.Action {
|
key_state :: proc(key: input.Key) -> input.Action {
|
||||||
if action == glfw.RELEASE do return base.Action.Release
|
return input_action(glfw.GetKey(state.os.window, i32(key)))
|
||||||
else if action == glfw.PRESS do return base.Action.Press
|
}
|
||||||
else if action == glfw.REPEAT do return base.Action.Repeat
|
|
||||||
|
@(private = "file")
|
||||||
|
mouse_button_state :: proc(button: input.Mouse_Button) -> input.Action {
|
||||||
|
return input_action(glfw.GetMouseButton(state.os.window, i32(button)))
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private = "file")
|
||||||
|
mouse_position :: proc() -> (x_pos: f32, y_pos: f32) {
|
||||||
|
x_pos_f64, y_pos_64 := glfw.GetCursorPos(state.os.window)
|
||||||
|
return f32(x_pos_f64), f32(y_pos_64)
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private = "file")
|
||||||
|
input_action :: proc(action: i32) -> input.Action {
|
||||||
|
if action == glfw.RELEASE do return input.Action.Release
|
||||||
|
else if action == glfw.PRESS do return input.Action.Press
|
||||||
|
else if action == glfw.REPEAT do return input.Action.Repeat
|
||||||
when ODIN_DEBUG do panic("[glfw] unknown action")
|
when ODIN_DEBUG do panic("[glfw] unknown action")
|
||||||
return .None
|
return .None
|
||||||
}
|
}
|
||||||
|
|
||||||
@(private = "file")
|
@(private = "file")
|
||||||
input_key :: proc(key: i32) -> base.Key {
|
input_key :: proc(key: i32) -> input.Key {
|
||||||
return base.Key(key) // values match
|
return input.Key(key) // values match
|
||||||
}
|
}
|
||||||
|
|
||||||
@(private = "file")
|
@(private = "file")
|
||||||
input_mod_set :: proc(mods: i32) -> (set: base.Mod_Set) {
|
input_mod_set :: proc(mods: i32) -> (set: input.Mod_Set) {
|
||||||
return transmute(base.Mod_Set)i8(mods & 0x3f) // values match bit position
|
return transmute(input.Mod_Set)i8(mods & 0x3f) // values match bit position
|
||||||
}
|
}
|
||||||
|
|
||||||
@(private = "file")
|
@(private = "file")
|
||||||
input_mouse_button :: proc(button: i32) -> base.Mouse_Button {
|
input_mouse_button :: proc(button: i32) -> input.Mouse_Button {
|
||||||
return base.Mouse_Button(button) // values match
|
return input.Mouse_Button(button) // values match
|
||||||
}
|
}
|
||||||
|
|
||||||
// Error callback
|
// Error callback
|
||||||
@@ -184,7 +194,7 @@ error_callback :: proc "c" (error: i32, description: cstring) {
|
|||||||
@(private = "file")
|
@(private = "file")
|
||||||
window_close_callback :: proc "c" (window: glfw.WindowHandle) {
|
window_close_callback :: proc "c" (window: glfw.WindowHandle) {
|
||||||
context = state.ctx
|
context = state.ctx
|
||||||
base.on_event(base.Window_Close_Event{})
|
event.on_event(event.Window_Close_Event{})
|
||||||
} // GLFWwindowclosefun
|
} // GLFWwindowclosefun
|
||||||
|
|
||||||
// Window resize callback
|
// Window resize callback
|
||||||
@@ -201,24 +211,24 @@ key_callback :: proc "c" (
|
|||||||
) {
|
) {
|
||||||
context = state.ctx
|
context = state.ctx
|
||||||
if action == glfw.PRESS {
|
if action == glfw.PRESS {
|
||||||
base.on_event(
|
event.on_event(
|
||||||
base.Key_Press_Event {
|
event.Key_Press_Event {
|
||||||
key = input_key(key),
|
key = input_key(key),
|
||||||
mods = input_mod_set(mods),
|
mods = input_mod_set(mods),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
if action == glfw.RELEASE {
|
if action == glfw.RELEASE {
|
||||||
base.on_event(
|
event.on_event(
|
||||||
base.Key_Release_Event {
|
event.Key_Release_Event {
|
||||||
key = input_key(key),
|
key = input_key(key),
|
||||||
mods = input_mod_set(mods),
|
mods = input_mod_set(mods),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
if action == glfw.REPEAT {
|
if action == glfw.REPEAT {
|
||||||
base.on_event(
|
event.on_event(
|
||||||
base.Key_Repeat_Event {
|
event.Key_Repeat_Event {
|
||||||
key = input_key(key),
|
key = input_key(key),
|
||||||
mods = input_mod_set(mods),
|
mods = input_mod_set(mods),
|
||||||
},
|
},
|
||||||
@@ -234,16 +244,16 @@ mouse_button_callback :: proc "c" (
|
|||||||
) {
|
) {
|
||||||
context = state.ctx
|
context = state.ctx
|
||||||
if action == glfw.PRESS {
|
if action == glfw.PRESS {
|
||||||
base.on_event(
|
event.on_event(
|
||||||
base.Mouse_Button_Press_Event {
|
event.Mouse_Button_Press_Event {
|
||||||
button = input_mouse_button(button),
|
button = input_mouse_button(button),
|
||||||
mods = input_mod_set(mods),
|
mods = input_mod_set(mods),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
if action == glfw.RELEASE {
|
if action == glfw.RELEASE {
|
||||||
base.on_event(
|
event.on_event(
|
||||||
base.Mouse_Button_Release_Event {
|
event.Mouse_Button_Release_Event {
|
||||||
button = input_mouse_button(button),
|
button = input_mouse_button(button),
|
||||||
mods = input_mod_set(mods),
|
mods = input_mod_set(mods),
|
||||||
},
|
},
|
||||||
@@ -258,8 +268,8 @@ cursor_pos_callback :: proc "c" (
|
|||||||
x_pos, y_pos: f64,
|
x_pos, y_pos: f64,
|
||||||
) {
|
) {
|
||||||
context = state.ctx
|
context = state.ctx
|
||||||
base.on_event(
|
event.on_event(
|
||||||
base.Mouse_Position_Event{x_pos = f32(x_pos), y_pos = f32(y_pos)},
|
event.Mouse_Position_Event{x_pos = f32(x_pos), y_pos = f32(y_pos)},
|
||||||
)
|
)
|
||||||
} // GLFWcursorposfun
|
} // GLFWcursorposfun
|
||||||
|
|
||||||
@@ -270,8 +280,8 @@ scroll_callback :: proc "c" (
|
|||||||
x_offset, y_offset: f64,
|
x_offset, y_offset: f64,
|
||||||
) {
|
) {
|
||||||
context = state.ctx
|
context = state.ctx
|
||||||
base.on_event(
|
event.on_event(
|
||||||
base.Mouse_Scroll_Event {
|
event.Mouse_Scroll_Event {
|
||||||
x_offset = f32(x_offset),
|
x_offset = f32(x_offset),
|
||||||
y_offset = f32(y_offset),
|
y_offset = f32(y_offset),
|
||||||
},
|
},
|
||||||
@@ -283,9 +293,9 @@ scroll_callback :: proc "c" (
|
|||||||
joystick_callback :: proc "c" (id, connected: i32) {
|
joystick_callback :: proc "c" (id, connected: i32) {
|
||||||
context = state.ctx
|
context = state.ctx
|
||||||
if connected == glfw.CONNECTED {
|
if connected == glfw.CONNECTED {
|
||||||
base.on_event(base.Joystick_Connect_Event{id = id})
|
event.on_event(event.Joystick_Connect_Event{id = id})
|
||||||
} else {
|
} else {
|
||||||
base.on_event(base.Joystick_Disconnect_Event{id = id})
|
event.on_event(event.Joystick_Disconnect_Event{id = id})
|
||||||
}
|
}
|
||||||
} // GLFWjoystickfun
|
} // GLFWjoystickfun
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import "core:slice"
|
|||||||
|
|
||||||
import "wgpu:wgpu"
|
import "wgpu:wgpu"
|
||||||
|
|
||||||
import "sindri:base"
|
import "sindri:core"
|
||||||
|
|
||||||
// -----------------------------------------
|
// -----------------------------------------
|
||||||
// Variables
|
// Variables
|
||||||
@@ -33,7 +33,7 @@ state: struct {
|
|||||||
// -----------------------------------------
|
// -----------------------------------------
|
||||||
// Constructor/destructor
|
// Constructor/destructor
|
||||||
|
|
||||||
instance_init :: proc(settings: base.Settings) {
|
instance_init :: proc(settings: core.Settings) {
|
||||||
state.ctx = context
|
state.ctx = context
|
||||||
|
|
||||||
wgpu.SetLogCallback(log_callback, nil)
|
wgpu.SetLogCallback(log_callback, nil)
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
package test
|
||||||
|
|
||||||
|
test: int = 0
|
||||||
|
|
||||||
|
test_proc: proc()
|
||||||
Reference in New Issue
Block a user