From ef92175fc0a265f5baaf7d6d30bf7db9e04504b5 Mon Sep 17 00:00:00 2001 From: Nycto Date: Sun, 20 Oct 2024 16:32:04 -0700 Subject: [PATCH 01/21] Organize utility code --- src/playdate/api.nim | 5 ++-- src/playdate/bindings/graphics.nim | 2 +- src/playdate/bindings/malloc.nim | 25 +------------------- src/playdate/types.nim | 1 - src/playdate/util/initreqs.nim | 21 ++++++++++++++++ src/playdate/{bindings => util}/memtrace.nim | 0 6 files changed, 25 insertions(+), 29 deletions(-) create mode 100644 src/playdate/util/initreqs.nim rename src/playdate/{bindings => util}/memtrace.nim (100%) diff --git a/src/playdate/api.nim b/src/playdate/api.nim index 262eafc..c31f492 100644 --- a/src/playdate/api.nim +++ b/src/playdate/api.nim @@ -1,7 +1,7 @@ {.push raises: [].} import macros -import std/importutils +import std/importutils, util/initreqs import bindings/api export api @@ -16,8 +16,7 @@ macro initSDK*() = proc eventHandler(playdateAPI: ptr PlaydateAPI, event: PDSystemEvent, arg: uint32): cint {.cdecl, exportc.} = privateAccess(PlaydateSys) if event == kEventInit: - when declared(setupRealloc): - setupRealloc(playdateAPI.system.realloc) + initPrereqs(playdateAPI.system.realloc, playdateAPI.system.logToConsole) NimMain() api.playdate = playdateAPI handler(event, arg) diff --git a/src/playdate/bindings/graphics.nim b/src/playdate/bindings/graphics.nim index 97cbfee..5cca074 100644 --- a/src/playdate/bindings/graphics.nim +++ b/src/playdate/bindings/graphics.nim @@ -1,6 +1,6 @@ {.push raises: [].} -import utils, types +import utils, types, ../util/initreqs type LCDRect* {.importc: "LCDRect", header: "pd_api.h".} = object left* {.importc.}: int # int32? diff --git a/src/playdate/bindings/malloc.nim b/src/playdate/bindings/malloc.nim index b352c80..8923638 100644 --- a/src/playdate/bindings/malloc.nim +++ b/src/playdate/bindings/malloc.nim @@ -18,33 +18,10 @@ when defined(memProfiler): proc nimProfile(requestedSize: int) -import memtrace -import system/ansi_c - -type PDRealloc = proc (p: pointer; size: csize_t): pointer {.tags: [], raises: [], cdecl, gcsafe.} - -proc nativeAlloc(p: pointer; size: csize_t): pointer {.tags: [], raises: [], cdecl, gcsafe.} = - if p == nil: - return c_malloc(size) - elif size == 0: - c_free(p) - return nil - else: - return c_realloc(p, size) - -var pdrealloc: PDRealloc +import ../util/[memtrace, initreqs] var trace: MemTrace - -proc setupRealloc*(allocator: PDRealloc) = - when defined(memtrace): - cfprintf(cstderr, "Setting up playdate allocator") - when defined(nativeAlloc): - pdrealloc = nativeAlloc - else: - pdrealloc = allocator - proc allocImpl(size: Natural): pointer = # Integrage with: https://nim-lang.org/docs/estp.html when defined(memProfiler): diff --git a/src/playdate/types.nim b/src/playdate/types.nim index 2d9db5f..8e2f2ae 100644 --- a/src/playdate/types.nim +++ b/src/playdate/types.nim @@ -1,4 +1,3 @@ - type SDKArrayObj[T] = object len: int data: ptr UncheckedArray[T] diff --git a/src/playdate/util/initreqs.nim b/src/playdate/util/initreqs.nim new file mode 100644 index 0000000..ccd5c96 --- /dev/null +++ b/src/playdate/util/initreqs.nim @@ -0,0 +1,21 @@ +## +## Contains direct references to playdate apis that are required to be usable before the +## full API is itself available. +## +## This file gets used _very_ early in the Nim initialization process. That means it gets +## imported and used before most of the Nim stdlib is available, so it needs to be almost +## completely self contained. +## + +type + PDRealloc* = proc (p: pointer; size: csize_t): pointer {.tags: [], raises: [], cdecl, gcsafe.} + PDLog* = proc (fmt: cstring) {.cdecl, varargs, raises: [].} + +var pdrealloc*: PDRealloc +var pdlog*: PDLog + +proc initPrereqs*(realloc: PDRealloc, log: PDLog) = + ## Sets pointers to functions from the playdate stdlib that are needed to initialize Nim integrations + log("Initializing Nim playdate globals") + pdrealloc = realloc + pdlog = log diff --git a/src/playdate/bindings/memtrace.nim b/src/playdate/util/memtrace.nim similarity index 100% rename from src/playdate/bindings/memtrace.nim rename to src/playdate/util/memtrace.nim From 13b524a37e1a416db9ad1f6b625fd96b864bd34d Mon Sep 17 00:00:00 2001 From: Nycto Date: Sun, 20 Oct 2024 16:57:47 -0700 Subject: [PATCH 02/21] Use playdate realloc directly for SDK allocated pointers This resolves the memory tracer reporting calls to deallocate unmanaged memory --- src/playdate/api.nim | 3 ++- src/playdate/bindings/graphics.nim | 9 ++++++--- src/playdate/bindings/initreqs.nim | 10 ++++++++++ src/playdate/types.nim | 4 +++- 4 files changed, 21 insertions(+), 5 deletions(-) create mode 100644 src/playdate/bindings/initreqs.nim diff --git a/src/playdate/api.nim b/src/playdate/api.nim index c31f492..30be87c 100644 --- a/src/playdate/api.nim +++ b/src/playdate/api.nim @@ -3,7 +3,7 @@ import macros import std/importutils, util/initreqs -import bindings/api +import bindings/[api, initreqs] export api import graphics, system, file, sprite, display, sound, scoreboards, lua, json, utils, types, nineslice @@ -15,6 +15,7 @@ macro initSDK*() = proc eventHandler(playdateAPI: ptr PlaydateAPI, event: PDSystemEvent, arg: uint32): cint {.cdecl, exportc.} = privateAccess(PlaydateSys) + privateAccess(PlaydateFile) if event == kEventInit: initPrereqs(playdateAPI.system.realloc, playdateAPI.system.logToConsole) NimMain() diff --git a/src/playdate/bindings/graphics.nim b/src/playdate/bindings/graphics.nim index 5cca074..2b2c6cb 100644 --- a/src/playdate/bindings/graphics.nim +++ b/src/playdate/bindings/graphics.nim @@ -74,7 +74,8 @@ type LCDFontPtr {.importc: "LCDFont*", header: "pd_api.h".} = pointer type LCDFontObj = object resource: LCDFontPtr -proc `=destroy`(this: var LCDFontObj) = deallocImpl(this.resource) +proc `=destroy`(this: var LCDFontObj) = + discard pdrealloc(this.resource, 0) type LCDFont* = ref LCDFontObj @@ -85,7 +86,8 @@ type LCDFontPagePtr {.importc: "LCDFontPage*", header: "pd_api.h".} = pointer type LCDFontPageObj = object resource: LCDFontPagePtr -proc `=destroy`(this: var LCDFontPageObj) = deallocImpl(this.resource) +proc `=destroy`(this: var LCDFontPageObj) = + discard pdrealloc(this.resource, 0) type LCDFontPage* = ref LCDFontPageObj @@ -93,7 +95,8 @@ type LCDFontGlyphPtr {.importc: "LCDFontGlyph*", header: "pd_api.h".} = pointer type LCDFontGlyphObj = object resource: LCDFontGlyphPtr -proc `=destroy`(this: var LCDFontGlyphObj) = deallocImpl(this.resource) +proc `=destroy`(this: var LCDFontGlyphObj) = + discard pdrealloc(this.resource, 0) type LCDFontGlyph* = ref LCDFontGlyphObj diff --git a/src/playdate/bindings/initreqs.nim b/src/playdate/bindings/initreqs.nim new file mode 100644 index 0000000..6142dd9 --- /dev/null +++ b/src/playdate/bindings/initreqs.nim @@ -0,0 +1,10 @@ +## +## Contains direct references to playdate apis that are required to be usable before the +## full API is itself available. +## + +var pdrealloc*: proc (p: pointer; size: csize_t): pointer {.tags: [], raises: [], cdecl, gcsafe.} + +proc initPrereqs*(realloc: auto) = + ## Sets pointers to functions that are needed early in the initialization process + pdrealloc = realloc diff --git a/src/playdate/types.nim b/src/playdate/types.nim index 8e2f2ae..b4ba3ad 100644 --- a/src/playdate/types.nim +++ b/src/playdate/types.nim @@ -1,3 +1,5 @@ +import util/initreqs + type SDKArrayObj[T] = object len: int data: ptr UncheckedArray[T] @@ -5,7 +7,7 @@ type SDKArray*[T] = ref SDKArrayObj[T] proc `=destroy`*[T](this: var SDKArrayObj[T]) = if this.data != nil: - deallocImpl(this.data) + discard pdrealloc(this.data, 0) proc `[]`*[T](this: SDKArray[T]; i: Natural): lent T = assert i < this.len From 7e880200d8e7d8d8945f6858c74b1866f389cd4c Mon Sep 17 00:00:00 2001 From: Nycto Date: Mon, 13 Jan 2025 18:40:32 -0800 Subject: [PATCH 03/21] Completely elid memtrace when disabled --- src/playdate/bindings/malloc.nim | 46 ++++++++++++++++++++++---------- src/playdate/util/memtrace.nim | 19 +++---------- 2 files changed, 36 insertions(+), 29 deletions(-) diff --git a/src/playdate/bindings/malloc.nim b/src/playdate/bindings/malloc.nim index 8923638..5748bcd 100644 --- a/src/playdate/bindings/malloc.nim +++ b/src/playdate/bindings/malloc.nim @@ -14,31 +14,48 @@ {.push stackTrace: off.} -# Forward declaration for memory profiling support +import ../util/initreqs + when defined(memProfiler): + + # Forward declaration for memory profiling support proc nimProfile(requestedSize: int) -import ../util/[memtrace, initreqs] + template rawAlloc(size): untyped = + # Integrage with: https://nim-lang.org/docs/estp.html + try: + nimProfile(size.int) + except: + discard + pdrealloc(nil, size) -var trace: MemTrace + template rawRealloc(p, size): untyped = pdrealloc(p, size) + template rawDealloc(p) = discard pdrealloc(p, 0) -proc allocImpl(size: Natural): pointer = - # Integrage with: https://nim-lang.org/docs/estp.html - when defined(memProfiler): - {.cast(tags: []).}: - try: - nimProfile(size.int) - except: - discard +elif defined(memtrace): + import ../util/memtrace + var trace: MemTrace - return trace.alloc(pdrealloc, size.csize_t) + template rawAlloc(size): untyped = traceAlloc(trace, pdrealloc, size) + template rawRealloc(p, size): untyped = traceRealloc(trace, pdrealloc, p, size) + template rawDealloc(p) = traceDealloc(trace, pdrealloc, p) + +else: + template rawAlloc(size): untyped = pdrealloc(nil, size) + template rawRealloc(p, size): untyped = pdrealloc(p, size) + template rawDealloc(p) = discard pdrealloc(p, 0) + +proc allocImpl(size: Natural): pointer = + {.cast(tags: []).}: + return rawAlloc(size.csize_t) proc alloc0Impl(size: Natural): pointer = result = allocImpl(size) zeroMem(result, size) proc reallocImpl(p: pointer, newSize: Natural): pointer = - return trace.realloc(pdrealloc, p, newSize) + {.cast(tags: []).}: + return rawRealloc(p, newSize.csize_t) proc realloc0Impl(p: pointer, oldsize, newSize: Natural): pointer = result = reallocImpl(p, newSize.csize_t) @@ -46,7 +63,8 @@ proc realloc0Impl(p: pointer, oldsize, newSize: Natural): pointer = zeroMem(cast[pointer](cast[uint](result) + uint(oldSize)), newSize - oldSize) proc deallocImpl(p: pointer) = - trace.dealloc(pdrealloc, p) + {.cast(tags: []).}: + rawDealloc(p) # The shared allocators map on the regular ones diff --git a/src/playdate/util/memtrace.nim b/src/playdate/util/memtrace.nim index 840be1e..0d41c34 100644 --- a/src/playdate/util/memtrace.nim +++ b/src/playdate/util/memtrace.nim @@ -1,4 +1,4 @@ -import system/ansi_c, ../util/sparsemap +import system/ansi_c, sparsemap, initreqs proc mprotect(a1: pointer, a2: int, a3: cint): cint {.importc, header: "".} @@ -13,8 +13,6 @@ const STACK_SIZE = 12 const BUFFER = sizeof(byte) * 8 type - Allocator* = proc (p: pointer; size: csize_t): pointer {.tags: [], raises: [], cdecl, gcsafe.} - StackString[N : static int] = object data: array[N, char] len: int32 @@ -210,7 +208,7 @@ proc record[N: static int](stack: array[N, StackFrame] = createStackFrame[N](get discard c_fwrite(addr stack[i].procname, 1, stack[i].procname.len.csize_t, handle) c_fputc('\n', handle) -proc traceAlloc(trace: var MemTrace, alloc: Allocator, size: Natural): pointer {.inline.} = +proc traceAlloc*(trace: var MemTrace, alloc: PDRealloc, size: Natural): pointer {.inline.} = trace.totalAllocs += 1 trace.check @@ -231,11 +229,8 @@ proc traceAlloc(trace: var MemTrace, alloc: Allocator, size: Natural): pointer { trace.allocs[realPointer.ord] = entry -proc alloc*(trace: var MemTrace, alloc: Allocator, size: Natural): pointer {.inline.} = +proc traceRealloc*(trace: var MemTrace, alloc: PDRealloc, p: pointer, newSize: Natural): pointer {.inline.} = record[5]() - when defined(memtrace): return traceAlloc(trace, alloc, size) else: return alloc(nil, size.csize_t) - -proc traceRealloc(trace: var MemTrace, alloc: Allocator, p: pointer, newSize: Natural): pointer {.inline.} = trace.check let realInPointer = p.input @@ -263,10 +258,7 @@ proc traceRealloc(trace: var MemTrace, alloc: Allocator, p: pointer, newSize: Na trace.allocs[realOutPointer.ord] = entry -proc realloc*(trace: var MemTrace, alloc: Allocator, p: pointer, newSize: Natural): pointer {.inline.} = - when defined(memtrace): return traceRealloc(trace, alloc, p, newSize) else: return alloc(p, newSize.csize_t) - -proc traceDealloc(trace: var MemTrace, alloc: Allocator, p: pointer) {.inline.} = +proc traceDealloc*(trace: var MemTrace, alloc: PDRealloc, p: pointer) {.inline.} = trace.check let realPointer = p.input if realPointer.ord notin trace.allocs: @@ -285,6 +277,3 @@ proc traceDealloc(trace: var MemTrace, alloc: Allocator, p: pointer) {.inline.} discard alloc(realPointer, 0) trace.deleted[realPointer.ord] = local trace.allocs.delete(realPointer.ord) - -proc dealloc*(trace: var MemTrace, alloc: Allocator, p: pointer) {.inline.} = - when defined(memtrace): traceDealloc(trace, alloc, p) else: discard alloc(p, 0) \ No newline at end of file From 1172f2d04f6bb2d1af8d3a25123418e3bbf4c9de Mon Sep 17 00:00:00 2001 From: Nino van Hooff Date: Mon, 27 Jan 2025 20:15:09 +0100 Subject: [PATCH 04/21] WIP setMenuImage --- src/playdate/bindings/system.nim | 4 ++-- src/playdate/graphics.nim | 5 ++--- src/playdate/system.nim | 10 ++++++++++ 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/src/playdate/bindings/system.nim b/src/playdate/bindings/system.nim index 1f774d5..7902d2a 100644 --- a/src/playdate/bindings/system.nim +++ b/src/playdate/bindings/system.nim @@ -58,8 +58,8 @@ sdktype: setAutoLockDisabled {.importc: "setAutoLockDisabled".}: proc (disable: cint) {. cdecl, raises: [].} - setMenuImage {.importsdk.}: proc (bitmap: LCDBitmapPtr; - xOffset: cint) + setMenuImage {.importc: "setMenuImage".}: proc (bitmap: LCDBitmapPtr; + xOffset: cint) {.cdecl, raises: [].} addMenuItem {.importc: "addMenuItem".}: proc (title: cstring; callback: PDMenuItemCallbackFunctionRaw; userdata: pointer): PDMenuItemPtr {. cdecl, raises: [].} diff --git a/src/playdate/graphics.nim b/src/playdate/graphics.nim index f7f934c..fde959e 100644 --- a/src/playdate/graphics.nim +++ b/src/playdate/graphics.nim @@ -2,7 +2,6 @@ import std/importutils -import system import bindings/[api, types] import bindings/graphics @@ -12,7 +11,7 @@ export graphics import bindings/graphics {.all.} type LCDBitmapObj = object of RootObj - resource {.requiresinit.}: LCDBitmapPtr + resource* {.requiresinit.}: LCDBitmapPtr free: bool proc `=destroy`(this: var LCDBitmapObj) = privateAccess(PlaydateGraphics) @@ -62,7 +61,7 @@ proc getInfo*(this: LCDVideoPlayer): tuple[width: int, height: int, frameRate: f proc getContext*(this: LCDVideoPlayer): LCDBitmap = privateAccess(PlaydateVideo) let bitmapPtr = playdate.graphics.video.getContext(this.resource) - playdate.system.logToConsole(fmt"video context: {bitmapPtr.repr}") + # echo(fmt"video context: {bitmapPtr.repr}") if this.context == nil or this.context.resource != bitmapPtr: this.context = LCDBitmap(resource: bitmapPtr, free: false) return this.context diff --git a/src/playdate/system.nim b/src/playdate/system.nim index 6f22205..491c9b1 100644 --- a/src/playdate/system.nim +++ b/src/playdate/system.nim @@ -6,6 +6,7 @@ import sequtils import bindings/[api, types] import bindings/system +import graphics # Only export public symbols, then import all export system @@ -187,6 +188,15 @@ proc removeAllMenuItems*(this: ptr PlaydateSys) = this.removeAllMenuItems() # --- +proc setMenuImage*(this: ptr PlaydateSys, image: LCDBitmap, xOffset: int32 = 0) = + privateAccess(PlaydateSys) + privateAccess(PlaydateGraphics) + this.setMenuImage( + # if image != nil: image.resource else: nil + image.resource, + xOffset.cint + ) + proc getReduceFlashing* (this: ptr PlaydateSys): bool = privateAccess(PlaydateSys) return this.getReduceFlashing() == 1 From 3936f5016159c11094b7d2d1797309b2d6406d79 Mon Sep 17 00:00:00 2001 From: Nycto Date: Tue, 4 Feb 2025 18:37:44 -0800 Subject: [PATCH 05/21] Fix PlaydateSys.logToConsole argument type This was causing an error in GCC 14 related to incompatible pointer types -- 'void (*)(char *, ...)' instead of 'void (* const)(const char *, ...)' --- src/playdate/bindings/system.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/playdate/bindings/system.nim b/src/playdate/bindings/system.nim index 1f774d5..96798ec 100644 --- a/src/playdate/bindings/system.nim +++ b/src/playdate/bindings/system.nim @@ -37,7 +37,7 @@ sdktype: realloc {.importc: "realloc".}: proc (`ptr`: pointer; size: csize_t): pointer {.cdecl, raises: [], tags: [], gcsafe.} formatString {.importc: "formatString".}: proc (ret: cstringArray; fmt: cstring): cint {. cdecl, varargs, raises: [].} - logToConsole {.importc: "logToConsole".}: proc (fmt: cstring) {.cdecl, varargs, raises: [].} + logToConsole {.importc: "logToConsole".}: proc (fmt: ConstChar) {.cdecl, varargs, raises: [].} error {.importc: "error".}: proc (fmt: cstring) {.cdecl, varargs, raises: [].} getLanguage {.importsdk.}: proc (): PDLanguage getCurrentTimeMilliseconds {.importsdk.}: proc (): cuint From f3b00665f32688b24a8d538d7959016f33532b81 Mon Sep 17 00:00:00 2001 From: Nycto Date: Thu, 23 Jan 2025 18:20:59 -0800 Subject: [PATCH 06/21] Support nil LCDBitmap conversion --- playdate_example/src/playdate_example.nim | 4 ++-- src/playdate/graphics.nim | 19 +++++++++++-------- src/playdate/sprite.nim | 4 ++-- 3 files changed, 15 insertions(+), 12 deletions(-) diff --git a/playdate_example/src/playdate_example.nim b/playdate_example/src/playdate_example.nim index 0789bc6..c068a0b 100644 --- a/playdate_example/src/playdate_example.nim +++ b/playdate_example/src/playdate_example.nim @@ -99,8 +99,8 @@ proc handler(event: PDSystemEvent, keycode: uint) {.raises: [].} = font = try: playdate.graphics.newFont(FONT_PATH) except: nil playdate.graphics.setFont(font) - playdateNimBitmap = try: playdate.graphics.newBitmap(PLAYDATE_NIM_IMAGE_PATH) except: return - nimLogoBitmap = try: playdate.graphics.newBitmap(NIM_IMAGE_PATH) except: return + playdateNimBitmap = try: playdate.graphics.newBitmap(PLAYDATE_NIM_IMAGE_PATH) except: nil + nimLogoBitmap = try: playdate.graphics.newBitmap(NIM_IMAGE_PATH) except: nil sprite = playdate.sprite.newSprite() sprite.add() diff --git a/src/playdate/graphics.nim b/src/playdate/graphics.nim index 5238f23..5ffda7a 100644 --- a/src/playdate/graphics.nim +++ b/src/playdate/graphics.nim @@ -31,12 +31,15 @@ proc `=destroy`(this: var LCDBitmapObj) = proc `=copy`(a: var LCDBitmapObj, b: LCDBitmapObj) {.error.} -proc bitmapPtr(point: LCDBitmapPtr): auto = +converter bitmapPtr*(point: LCDBitmapPtr): auto = LCDBitmap(managed: false, res: point) proc bitmapRef(point: LCDBitmapPtr): auto = LCDBitmap(managed: true, obj: LCDBitmapObjRef(res: point)) +proc `==`*(bitmap: LCDBitmap, point: LCDBitmapPtr): bool = + not bitmap.managed and bitmap.res == point + proc resource*(bitmap: LCDBitmap): LCDBitmapPtr = if bitmap.managed: return if bitmap.obj != nil: bitmap.obj.res else: nil @@ -53,7 +56,7 @@ type LCDVideoPlayerObj = object of RootObj proc `=destroy`(this: var LCDVideoPlayerObj) = privateAccess(PlaydateVideo) playdate.graphics.video.freePlayer(this.resource) - this.context.reset() + this.context = nil type LCDVideoPlayer* = ref LCDVideoPlayerObj proc newVideoPlayer*(this: ptr PlaydateVideo, path: string): LCDVideoPlayer {.raises: [IOError]} = @@ -72,7 +75,7 @@ proc setContext*(this: LCDVideoPlayer, context: LCDBitmap) {.raises: [CatchableE proc useScreenContext*(this: LCDVideoPlayer) = privateAccess(PlaydateVideo) playdate.graphics.video.useScreenContext(this.resource) - this.context.reset() + this.context = nil proc renderFrame*(this: LCDVideoPlayer, index: int) {.raises: [CatchableError]} = privateAccess(PlaydateVideo) @@ -90,7 +93,7 @@ proc getContext*(this: LCDVideoPlayer): LCDBitmap = privateAccess(PlaydateVideo) let bitmapPtr = playdate.graphics.video.getContext(this.resource) playdate.system.logToConsole(fmt"video context: {bitmapPtr.repr}") - if this.context.isNil or this.context.resource != bitmapPtr: + if this.context == nil or this.context.resource != bitmapPtr: this.context = bitmapPtr(bitmapPtr) return this.context @@ -107,7 +110,7 @@ proc getFont*(this: ptr PlaydateGraphics): LCDFont = proc pushContext*(this: ptr PlaydateGraphics, target: LCDBitmap) = privateAccess(PlaydateGraphics) - this.pushContext(target.resource) + this.pushContext(if target != nil: target.resource else: nil) proc draw*(this: LCDBitmap, x: int, y: int, flip: LCDBitmapFlip) = privateAccess(PlaydateGraphics) @@ -175,7 +178,7 @@ template read(bitmap: AnyBitmapData, x, y: int): untyped = proc getDataObj*(this: LCDBitmap): BitmapDataObj = ## Fetch the underlying bitmap data for an image. privateAccess(PlaydateGraphics) - assert(not this.isNil) + assert(this != nil) assert(this.resource != nil) playdate.graphics.getBitmapData( this.resource, @@ -193,7 +196,7 @@ proc getData*(this: LCDBitmap): BitmapData = proc getSize*(this: LCDBitmap): tuple[width: int, height: int] = privateAccess(PlaydateGraphics) - assert(not this.isNil) + assert(this != nil) assert(this.resource != nil) var width, height: cint playdate.graphics.getBitmapData(this.resource, addr(width), addr(height), nil, @@ -440,7 +443,7 @@ proc set*(this: var LCDBitmap, x, y: int, color: LCDSolidColor = kColorBlack) = proc setStencilImage*(this: ptr PlaydateGraphics, bitmap: LCDBitmap, tile: bool = false) = privateAccess(PlaydateGraphics) - if bitmap.isNil: + if bitmap == nil: this.setStencilImage(nil, if tile: 1 else: 0) else: this.setStencilImage(bitmap.resource, if tile: 1 else: 0) diff --git a/src/playdate/sprite.nim b/src/playdate/sprite.nim index b24fcd8..9ca7e66 100644 --- a/src/playdate/sprite.nim +++ b/src/playdate/sprite.nim @@ -404,12 +404,12 @@ proc setStencilPattern*(this: LCDSprite, pattern: array[8, uint8]) = proc clearStencil*(this: LCDSprite) = privateAccess(PlaydateSprite) playdate.sprite.clearStencil(this.resource) - this.stencil.reset() + this.stencil = nil proc setStencilImage*(this: LCDSprite, stencil: LCDBitmap, tile: bool) = privateAccess(PlaydateSprite) privateAccess(LCDBitmap) - playdate.sprite.setStencilImage(this.resource, stencil.resource, if tile: 1 else: 0) + playdate.sprite.setStencilImage(this.resource, if stencil != nil: stencil.resource else: nil, if tile: 1 else: 0) this.stencil = stencil proc setCenter*(this: LCDSprite, x: float32, y: float32) = From a384e4199a9b90e493c89e3c60fd3ccf9fc7fe2f Mon Sep 17 00:00:00 2001 From: Nino van Hooff Date: Sun, 9 Feb 2025 18:42:36 +0100 Subject: [PATCH 07/21] CHANGE separate macos compile step --- .github/actions/macos-build-setup/action.yml | 39 ++++++++++++++++++++ .github/workflows/build.yml | 17 ++++++++- 2 files changed, 54 insertions(+), 2 deletions(-) create mode 100644 .github/actions/macos-build-setup/action.yml diff --git a/.github/actions/macos-build-setup/action.yml b/.github/actions/macos-build-setup/action.yml new file mode 100644 index 0000000..86b40b7 --- /dev/null +++ b/.github/actions/macos-build-setup/action.yml @@ -0,0 +1,39 @@ +name: macos build setup +inputs: + nim-version: + required: true +runs: + using: "composite" + steps: + + - name: Git safe directory + shell: bash + run: git config --global --add safe.directory "$(pwd)" + + - uses: iffy/install-nim@v5 + with: + version: ${{ inputs.nim-version }} + - run: nimble --accept refresh + shell: bash + + - run: nimble install + shell: bash + + - name: Locally publish playdate nimble package + shell: bash + if: ${{ startsWith(inputs.nim-version, '1.') }} + run: nimble develop + + - name: Install Playdate SDK + id: playdate + uses: pd-rs/get-playdate-sdk@0.4 + with: + version: latest # possible values: version `x.x.x` or `latest` by default + + - name: print playdate sdk info + shell: bash + run: | + echo "SDK path env: $PLAYDATE_SDK_PATH" + echo "SDK root out: ${{ steps.playdate.outputs.root }}" + echo "SDK version: ${{ steps.playdate.outputs.version }}" + pdc --version # because SDK/bin already in PATH \ No newline at end of file diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index feee96f..cc25881 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -2,13 +2,26 @@ name: Build on: [push, pull_request] jobs: + compile-example-project-macos: + runs-on: macos-latest + steps: + - uses: actions/checkout@v3 + - uses: ./.github/actions/macos-build-setup + with: + nim-version: 2.0.8 + - uses: ./.github/actions/project-setup + with: + working-directory: ./playdate_example + - run: nimble simulator + working-directory: ./playdate_example + example-project: - runs-on: ubuntu-latest - container: nimlang/choosenim strategy: matrix: target: [ device, simulator ] nim-version: [ 2.0.8 ] + runs-on: ubuntu-latest + container: nimlang/choosenim steps: - uses: actions/checkout@v3 - uses: ./.github/actions/build-setup From c470f59f13ca18b764d2de38cf68580aa62a118c Mon Sep 17 00:00:00 2001 From: Nycto Date: Mon, 10 Feb 2025 21:05:01 -0800 Subject: [PATCH 08/21] Fix incompatible function pointer errors in clang --- src/playdate/bindings/system.nim | 4 ++-- src/playdate/bindings/types.nim | 14 ++++++++++---- src/playdate/util/initreqs.nim | 4 +--- 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/src/playdate/bindings/system.nim b/src/playdate/bindings/system.nim index 96798ec..d4ead8a 100644 --- a/src/playdate/bindings/system.nim +++ b/src/playdate/bindings/system.nim @@ -34,10 +34,10 @@ type PDMenuItemCallbackFunctionRaw {.importc: "PDMenuItemCallbackFunction", head # System sdktype: type PlaydateSys* {.importc: "const struct playdate_sys", header: "pd_api.h".} = object - realloc {.importc: "realloc".}: proc (`ptr`: pointer; size: csize_t): pointer {.cdecl, raises: [], tags: [], gcsafe.} + realloc {.importc: "realloc".}: PDRealloc formatString {.importc: "formatString".}: proc (ret: cstringArray; fmt: cstring): cint {. cdecl, varargs, raises: [].} - logToConsole {.importc: "logToConsole".}: proc (fmt: ConstChar) {.cdecl, varargs, raises: [].} + logToConsole {.importc: "logToConsole".}: PDLog error {.importc: "error".}: proc (fmt: cstring) {.cdecl, varargs, raises: [].} getLanguage {.importsdk.}: proc (): PDLanguage getCurrentTimeMilliseconds {.importsdk.}: proc (): cuint diff --git a/src/playdate/bindings/types.nim b/src/playdate/bindings/types.nim index 3ab3773..30aa349 100644 --- a/src/playdate/bindings/types.nim +++ b/src/playdate/bindings/types.nim @@ -1,5 +1,11 @@ -type ConstChar* {.importc: "const char*".} = cstring -type ConstCharPtr* {.importc: "const char**".} = cstring -type Char* {.importc: "char*".} = cstring +type + ConstChar* {.importc: "const char*".} = cstring + ConstCharPtr* {.importc: "const char**".} = cstring + Char* {.importc: "char*".} = cstring + LCDBitmapPtr* {.importc: "LCDBitmap*", header: "pd_api.h".} = pointer -type LCDBitmapPtr* {.importc: "LCDBitmap*", header: "pd_api.h".} = pointer \ No newline at end of file + PDLog* = proc (fmt: ConstChar) {.cdecl, varargs, raises: [].} + ## The type signature for playdate.system.logToConsole + + PDRealloc* = proc (p: pointer; size: csize_t): pointer {.tags: [], raises: [], cdecl, gcsafe.} + ## The type signature for playdate.system.realloc \ No newline at end of file diff --git a/src/playdate/util/initreqs.nim b/src/playdate/util/initreqs.nim index ccd5c96..9a25cc8 100644 --- a/src/playdate/util/initreqs.nim +++ b/src/playdate/util/initreqs.nim @@ -7,9 +7,7 @@ ## completely self contained. ## -type - PDRealloc* = proc (p: pointer; size: csize_t): pointer {.tags: [], raises: [], cdecl, gcsafe.} - PDLog* = proc (fmt: cstring) {.cdecl, varargs, raises: [].} +import ../bindings/types var pdrealloc*: PDRealloc var pdlog*: PDLog From bfd570cff450308d5d18532ae9f44d87c0df4d74 Mon Sep 17 00:00:00 2001 From: Nycto Date: Tue, 11 Feb 2025 18:49:52 -0800 Subject: [PATCH 09/21] Simplify patch file location --- src/playdate/build/config.nim | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/src/playdate/build/config.nim b/src/playdate/build/config.nim index 4f396fe..56f9bd9 100644 --- a/src/playdate/build/config.nim +++ b/src/playdate/build/config.nim @@ -18,16 +18,6 @@ const testing = headlessTesting or nimbleTesting # This does not make the playdate api callable, only the types (header files) are available. const useHostOS = defined(useHostOS) -# Path to the playdate src directory when checked out locally -const localPlaydatePath = currentSourcePath / "../../../../src" - -# The path to the nimble playdate package -let nimblePlaydatePath = - if dirExists(localPlaydatePath / "playdate"): - localPlaydatePath - else: - gorgeEx("nimble path playdate").output.split("\n")[0] - if not testing and not useHostOS: switch("noMain", "on") switch("backend", "c") @@ -180,4 +170,4 @@ else: switch("compile", sdkPath() / "C_API" / "buildsupport" / "setup.c") # Overrides the nim memory management code to ensure it uses the playdate allocator - patchFile("stdlib", "malloc", nimblePlaydatePath / "playdate/bindings/malloc") \ No newline at end of file + patchFile("stdlib", "malloc", currentSourcePath().parentDir() /../ "bindings" / "malloc") \ No newline at end of file From 6b07aaf44abe6c95e870892aa851bd3c95408b94 Mon Sep 17 00:00:00 2001 From: Nycto Date: Wed, 12 Feb 2025 18:22:50 -0800 Subject: [PATCH 10/21] Fix memtrace build action failure --- src/playdate/util/memtrace.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/playdate/util/memtrace.nim b/src/playdate/util/memtrace.nim index 0d41c34..eb8ec78 100644 --- a/src/playdate/util/memtrace.nim +++ b/src/playdate/util/memtrace.nim @@ -1,4 +1,4 @@ -import system/ansi_c, sparsemap, initreqs +import system/ansi_c, sparsemap, initreqs, ../bindings/types proc mprotect(a1: pointer, a2: int, a3: cint): cint {.importc, header: "".} From e4b7b3c0da3d6dbed0fc0d8a1af1ceba059cafa6 Mon Sep 17 00:00:00 2001 From: Nycto Date: Wed, 12 Feb 2025 18:32:30 -0800 Subject: [PATCH 11/21] Implement pdn (#98) --- .github/actions/build-setup/action.yml | 8 +- .github/actions/project-setup/action.yml | 5 - .github/workflows/build.yml | 13 +- .gitignore | 8 +- README.md | 64 ++++--- config.nims | 4 + playdate.nimble | 4 +- playdate_example/.gitignore | 3 + playdate_example/source/pdxinfo | 4 - src/pdn.nim | 66 +++++++ src/playdate/bindings/malloc.nim | 6 +- src/playdate/build/actions.nim | 211 +++++++++++++++++++++++ src/playdate/build/config.nim | 14 +- src/playdate/build/nimble.nim | 138 +-------------- src/playdate/build/utils.nim | 92 +++------- tests/.gitignore | 3 + tests/source/pdxinfo | 4 - 17 files changed, 379 insertions(+), 268 deletions(-) create mode 100644 config.nims delete mode 100644 playdate_example/source/pdxinfo create mode 100644 src/pdn.nim create mode 100644 src/playdate/build/actions.nim delete mode 100644 tests/source/pdxinfo diff --git a/.github/actions/build-setup/action.yml b/.github/actions/build-setup/action.yml index f6453b9..a47da01 100644 --- a/.github/actions/build-setup/action.yml +++ b/.github/actions/build-setup/action.yml @@ -17,7 +17,13 @@ runs: - run: nimble --accept refresh shell: bash - - run: nimble install + - run: nimble install --verbose + shell: bash + + - run: echo "/github/home/.nimble/bin" >> $GITHUB_PATH + shell: bash + + - run: which pdn shell: bash - name: Locally publish playdate nimble package diff --git a/.github/actions/project-setup/action.yml b/.github/actions/project-setup/action.yml index e441e7e..21e721d 100644 --- a/.github/actions/project-setup/action.yml +++ b/.github/actions/project-setup/action.yml @@ -6,11 +6,6 @@ runs: using: "composite" steps: - - name: Nimble example setup - shell: bash - working-directory: ${{ inputs.working-directory }} - run: nimble configure; - - name: Install dependencies shell: bash working-directory: ${{ inputs.working-directory }} diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index cc25881..7859aea 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -12,9 +12,9 @@ jobs: - uses: ./.github/actions/project-setup with: working-directory: ./playdate_example - - run: nimble simulator + - run: pdn simulator working-directory: ./playdate_example - + example-project: strategy: matrix: @@ -30,7 +30,7 @@ jobs: - uses: ./.github/actions/project-setup with: working-directory: ./playdate_example - - run: nimble ${{ matrix.target }} + - run: pdn ${{ matrix.target }} working-directory: ./playdate_example tests: @@ -77,10 +77,7 @@ jobs: pulseaudio -D --exit-idle-time=-1 pactl load-module module-null-sink sink_name=SpeakerOutput sink_properties=device.description="Dummy_Output" - - run: echo 'switch("d", "${{ matrix.build-flags }}")' >> configs.nims - working-directory: ./tests - - - run: nimble simulator + - run: pdn simulator -d:${{ matrix.build-flags }} working-directory: ./tests # The first time the simulator runs, it prompts the user with an alert. Obviously, we're running headless, @@ -99,4 +96,4 @@ jobs: working-directory: ./tests run: | export HOME="/config" - xvfb-run ../PlaydateSDK-*/bin/PlaydateSimulator tests.pdx \ No newline at end of file + xvfb-run ../PlaydateSDK-*/bin/PlaydateSimulator playdate_tests.pdx \ No newline at end of file diff --git a/.gitignore b/.gitignore index 637f30c..0cc1747 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ htmldocs/ src/tests/ pdex.bin *.pdx +tests/source/pdxinfo # macOS general .DS_Store @@ -58,4 +59,9 @@ tests/t_* tests/playdate_tests # IntelliJ IDEA -.idea \ No newline at end of file +.idea +nimble.develop +nimble.paths + +# Binaries +pdn diff --git a/README.md b/README.md index 97552ae..1e0046f 100644 --- a/README.md +++ b/README.md @@ -47,42 +47,31 @@ If you want to start from scratch, here are the steps to follow: 1. If you haven't done it already, start creating a folder (snake_case) and initializing your nimble package inside it running: -``` -nimble init -``` -Choose `binary` as the package type. + ```shell + nimble init + ``` + + Choose `binary` as the package type. 2. Install the `playdate` package: -``` -nimble install playdate -``` + ```shell + nimble install playdate + ``` -3. Add the `playdate` package as a dependency and configure the build tasks by running the following: +3. Add the `playdate` package as a dependency in your nimble file: -``` -echo 'requires "playdate"' >> *.nimble; -echo 'include playdate/build/nimble' >> *.nimble; -echo 'include playdate/build/config' > config.nims; -``` - -4. Finally, run this command to setup the structure of the project, which prepares your application to be compiled and bundled correctly: - -``` -nimble configure -``` + ```shell + echo 'requires "playdate"' >> *.nimble; + ``` ## Usage -If you haven't done it already, install the package: -``` -nimble install playdate -``` - `playdate_example` contains a basic example of the bindings utilization. The example code is in `playdate_example/src/playdate_example.nim`. Here's also a minimal snippet to make a Nim application: + ```nim import playdate/api @@ -96,35 +85,38 @@ proc handler(event: PDSystemEvent, keycode: uint) {.raises: [].} = if event == kEventInit: # Errors are handled through exceptions, this is an inline try/except nimLogoBitmap = try: playdate.graphics.newBitmap("/images/nim_logo") except: nil - # playdate is the global PlaydateAPI instance, available when playdate/api is imported + # playdate is the global PlaydateAPI instance, available when playdate/api is imported playdate.system.setUpdateCallback(update) # Used to setup the SDK entrypoint initSDK() ``` -Compile the project (pdx) for the simulator using: +Compile the project (pdx) for the simulator using the `pdn` binary that gets added when +you ran `nimble install playdate`: + ```sh -nimble simulator +pdn simulator ``` + For the device (pdx): + ```sh -nimble device -``` -For simulator + device (pdx): -```sh -nimble all +pdn device ``` You can also build for simulator and launch it in one command: + ```sh -nimble simulate +pdn simulate ``` The example project `playdate_example` also contains VSCode launch configurations to build, start and debug your Nim application from the editor. -Each project contains a `config.nims` file that can be edited to customize how the project should be built, e.g. adding libraries or other external code.
+Each project contains a `config.nims` file that can be edited to customize how the project should be built, e.g. adding libraries or other external code. + Here's an example of a `config.nims` that links a pre-built static library called chipmunk: + ```nim include playdate/build/config @@ -151,6 +143,7 @@ There are two ways you can use Nim and Lua in the same project: 2. The main loop is defined in Lua, but you want to call Nim functions. Either way, you can provide Lua with your Nim functions during Lua initialization: + ```nim proc nimInsideLua(state: LuaStatePtr): cint {.cdecl, raises: [].} = ... @@ -164,9 +157,10 @@ proc handler(event: PDSystemEvent, keycode: uint) {.raises: [].} = ``` Calling a Lua function from Nim: + ```nim try: - # Push the argument first + # Push the argument first playdate.lua.pushInt(5) playdate.lua.callFunction("funcWithOneArgument", 1) except: diff --git a/config.nims b/config.nims new file mode 100644 index 0000000..8ee48d2 --- /dev/null +++ b/config.nims @@ -0,0 +1,4 @@ +# begin Nimble config (version 2) +when withDir(thisDir(), system.fileExists("nimble.paths")): + include "nimble.paths" +# end Nimble config diff --git a/playdate.nimble b/playdate.nimble index 1e9a35d..134a215 100644 --- a/playdate.nimble +++ b/playdate.nimble @@ -5,9 +5,9 @@ author = "Samuele Zolfanelli" description = "Playdate Nim bindings with extra features." license = "MIT" srcDir = "src" - +installExt = @["nim"] +bin = @["pdn"] # Dependencies requires "nim >= 2.0.0" -# requires "nimble < 0.14.0" diff --git a/playdate_example/.gitignore b/playdate_example/.gitignore index b74210d..74a53b5 100644 --- a/playdate_example/.gitignore +++ b/playdate_example/.gitignore @@ -1,3 +1,6 @@ *.pdx source/pdex.* *.dSYM +playdate_example.pdx.zip +playdate_example.pdx +source/pdxinfo \ No newline at end of file diff --git a/playdate_example/source/pdxinfo b/playdate_example/source/pdxinfo deleted file mode 100644 index 9942484..0000000 --- a/playdate_example/source/pdxinfo +++ /dev/null @@ -1,4 +0,0 @@ -name=Playdate Example -author=Samuele Zolfanelli -description=An example application using the Playdate Nim bindings -bundleId=com.samdze.playdate_example \ No newline at end of file diff --git a/src/pdn.nim b/src/pdn.nim new file mode 100644 index 0000000..23182ed --- /dev/null +++ b/src/pdn.nim @@ -0,0 +1,66 @@ +import std/[parseopt, strutils, os, macros, options] +import playdate/build/[utils, actions] + +type + BuildCommand = enum simulate, simulator, device, clean, bundle + ## The various actions that can be executed by this script + + CliConfig = object + ## Configurations collected from the Cli + command: BuildCommand + noAutoConfig: bool + sdkPath: Option[string] + extraArgs: seq[string] + +proc getCliConfig(): CliConfig = + ## Parses the build configuration from the input options + for kind, key, val in getopt(): + template addExtraArg() = + var command = if kind == cmdLongOption: "--" else: "-" + command &= key + if val != "": + command &= ":" & val + result.extraArgs.add(command) + + case kind + of cmdArgument: + result.command = parseEnum[BuildCommand](key) + of cmdLongOption, cmdShortOption: + case key + of "sdk-path": result.sdkPath = some(val) + of "no-auto-config": result.noAutoConfig = true + else: addExtraArg() + of cmdEnd: + discard + +let build = getCliConfig() + +let dump = getNimbleDump() + +let conf = PlaydateConf( + dump: dump, + kind: if build.command == device: DeviceBuild else: SimulatorBuild, + sdkPath: sdkPath(build.sdkPath), + pdxName: dump.name & ".pdx", + nimbleArgs: build.extraArgs, + noAutoConfig: build.noAutoConfig +) + +case build.command +of device, simulate, simulator: + conf.configureBuild() +of clean, bundle: + discard + +case build.command +of simulator: + conf.simulatorBuild() +of simulate: + conf.simulatorBuild() + conf.runSimulator() +of device: + conf.deviceBuild() +of clean: + conf.runClean() +of bundle: + conf.bundlePDX() diff --git a/src/playdate/bindings/malloc.nim b/src/playdate/bindings/malloc.nim index 5748bcd..35cb89d 100644 --- a/src/playdate/bindings/malloc.nim +++ b/src/playdate/bindings/malloc.nim @@ -14,7 +14,11 @@ {.push stackTrace: off.} -import ../util/initreqs +when (compiles do: + import playdate/util/initreqs): + import playdate/util/initreqs +else: + import ../util/initreqs when defined(memProfiler): diff --git a/src/playdate/build/actions.nim b/src/playdate/build/actions.nim new file mode 100644 index 0000000..1c23e07 --- /dev/null +++ b/src/playdate/build/actions.nim @@ -0,0 +1,211 @@ +import std/[sequtils, strutils, os, times, strformat, osproc, sets, json, jsonutils], utils + +type + BuildKind* = enum SimulatorBuild, DeviceBuild + + PlaydateConf* {.requiresInit.} = object + kind*: BuildKind + sdkPath*, pdxName*: string + nimbleArgs*: seq[string] + dump*: NimbleDump + noAutoConfig*: bool + + NimbleDump* = ref object + ## The data pulled from running `nimble dump --json` + name*, version*, nimblePath*, author*, desc*, license*: string + +proc getNimbleDump*(): NimbleDump = + ## Executes nimble with the given set of arguments + let (output, exitCode) = execCmdEx("nimble dump --json") + if exitCode != 0: + echo output + raise BuildFail.newException(fmt"Unable to extract nimble dump for package") + return parseJson(output).jsonTo(NimbleDump, Joptions(allowExtraKeys: true)) + +proc exec*(command: string, args: varargs[string]) = + ## Executes nimble with the given set of arguments + let process = startProcess( + command = command, + args = args, + options = {poUsePath, poParentStreams, poEchoCmd} + ) + if process.waitForExit() != 0: + raise BuildFail.newException(fmt"Command failed: {command} {args}") + +proc nimble*(conf: PlaydateConf, args: varargs[string]) = + ## Executes nimble with the given set of arguments + exec( + "nimble", + @[ "-d:playdateSdkPath=" & conf.sdkPath ].concat(conf.nimbleArgs).concat(args.toSeq) + ) + +proc pdcPath*(conf: PlaydateConf): string = + ## Returns the path of the pdc playdate utility + return conf.sdkPath / "bin" / "pdc" + +proc fileAppend*(path, content: string) = + ## Appends a string to a file + var handle: File + doAssert handle.open(path, fmAppend) + try: + handle.write(content) + finally: + handle.close + +proc updateGitIgnore(conf: PlaydateConf) = + ## Adds entries to the gitignore file + + var toAdd = toHashSet([ + conf.pdxName, + conf.pdxName & ".zip", + "source/pdxinfo", + "source/pdex.*", + "*.dSYM" + ]) + + const gitIgnore = ".gitignore" + + if not fileExists(gitIgnore): + writeFile(gitIgnore, "") + + for line in lines(gitIgnore): + toAdd.excl(line) + + if toAdd.len > 0: + gitIgnore.fileAppend(toAdd.items.toSeq.join("\n")) + +proc updateConfig(conf: PlaydateConf) = + ## Updates the config.nims file for the project if required + + const configFile = "playdate/build/config" + + if fileExists("config.nims"): + for line in lines("config.nims"): + if configFile in line: + echo "config.nims already references ", configFile, "; skipping build configuration" + return + + echo "Updating config.nims to include build configurations" + + "configs.nims".fileAppend(fmt"\n\n# Added by pdn\nimport {configFile}\n") + +proc writePdxInfo(conf: PlaydateConf) = + ## Writes the pdx info file + + let buildId = now().format("yyyyMMddhhmmss") + + let (output, exitCode) = execCmdEx("git rev-parse HEAD") + let commit = if exitCode == 0: output[0..<8] else: conf.dump.version + + let cartridgeName = conf.dump.name + .replace("_", " ") + .split(" ") + .map(proc(s: string): string = s.capitalizeAscii()) + .join(" ") + let bundleAuthor = conf.dump.author + .toLower() + .replace(" ", "") + .replace("-", "") + .replace("_", "") + let bundleProjectName = conf.dump.name + .toLower() + .replace(" ", "") + .replace("-", "") + .replace("_", "") + + let pdx = fmt""" + name={cartridgeName} + author={conf.dump.author} + description={conf.dump.desc} + bundleId=com.{bundleAuthor}.{bundleProjectName} + imagePath=launcher + version={commit} + buildNumber={buildId} + """.dedent() + + createDir("source") + writeFile("source" / "pdxinfo", pdx) + +proc configureBuild*(conf: PlaydateConf) = + if not conf.noAutoConfig: + conf.updateConfig + conf.writePdxInfo + conf.updateGitIgnore + +proc bundlePDX*(conf: PlaydateConf) = + ## Bundles pdx file using parent directory name. + exec(conf.pdcPath, "--version") + exec(conf.pdcPath, "--verbose", "-sdkpath", conf.sdkPath, "source", conf.dump.name) + +proc mv(source, target: string) = + echo fmt"Moving {source} to {target}" + if not source.fileExists and not source.dirExists: + raise BuildFail.newException(fmt"Expecting the '{source}' to exist, but it doesn't") + moveFile(source, target) + +proc rm(target: string) = + echo fmt"Removing {target}" + removeFile(target) + +proc rmdir(target: string) = + echo fmt"Removing {target}" + removeDir(target) + +proc simulatorBuild*(conf: PlaydateConf) = + ## Performs a build for running on the simulator + + conf.nimble("build", "-d:simulator", "-d:debug") + + if defined(windows): + mv(conf.dump.name & ".exe", "source" / "pdex.dll") + elif defined(macosx): + mv(conf.dump.name, "source" / "pdex.dylib") + rmdir("source" / "pdex.dSYM") + mv(conf.dump.name & ".dSYM", "source" / "pdex.dSYM") + elif defined(linux): + mv(conf.dump.name, "source" / "pdex.so") + else: + raise BuildFail.newException(fmt"Unsupported host platform") + + conf.bundlePDX() + +proc runSimulator*(conf: PlaydateConf) = + ## Executes the simulator + if not conf.pdxName.dirExists: + raise BuildFail.newException(fmt"PDX does not exist: {conf.pdxName.absolutePath}") + + when defined(windows): + exec(conf.sdkPath / "bin" / "PlaydateSimulator.exe", conf.pdxName) + elif defined(macosx): + exec("open", conf.sdkPath / "bin" / "Playdate\\ Simulator.app", conf.pdxName) + else: + exec(conf.sdkPath / "bin" / "PlaydateSimulator", conf.pdxName) + +proc deviceBuild*(conf: PlaydateConf) = + ## Performs a build for running on device + + conf.nimble("build", "-d:device", "-d:release") + + let artifact = when defined(windows): conf.dump.name & ".exe" else: conf.dump.name + mv(artifact, "source" / "pdex.elf") + rm("game.map") + + conf.bundlePDX() + + let zip = findExe("zip") + if zip != "": + exec(zip, "-r", fmt"{conf.pdxName}.zip", conf.pdxName, "-x", "*.so") + +proc runClean*(conf: PlaydateConf) = + ## Removes all cache files and build artifacts + rmdir("source" / "pdex.dSYM") + rm("source" / "pdex.dylib") + rm("source" / "pdex.dll") + rm("source" / "pdex.so") + rm("source" / "pdex.bin") + rm("source" / "pdex.elf") + rmdir(conf.pdxName) + rm("source" / "pdex.elf") + rm(conf.dump.name) + rm(conf.dump.name & ".exe") + exec("nimble", "clean") diff --git a/src/playdate/build/config.nim b/src/playdate/build/config.nim index 56f9bd9..9d90db7 100644 --- a/src/playdate/build/config.nim +++ b/src/playdate/build/config.nim @@ -1,10 +1,8 @@ -import os, strutils +import std/[os, strutils], utils when defined(device): import strformat -import utils - # This file is designed to be `included` directly from a `config.nims` file, which will make `switch` and `task` # implicitly available. This block just fixes auto-complete in IDEs. when not compiles(task): @@ -18,6 +16,8 @@ const testing = headlessTesting or nimbleTesting # This does not make the playdate api callable, only the types (header files) are available. const useHostOS = defined(useHostOS) +let playdateSdkPath = sdkPath() + if not testing and not useHostOS: switch("noMain", "on") switch("backend", "c") @@ -37,7 +37,7 @@ switch("passC", "-DTARGET_EXTENSION=1") switch("passC", "-Wall") switch("passC", "-Wno-unknown-pragmas") switch("passC", "-Wdouble-promotion") -switch("passC", "-I" & sdkPath() / "C_API") +switch("passC", "-I" & playdateSdkPath / "C_API") if not useHostOS: echo "Setting os to any" @@ -69,7 +69,7 @@ when defined(device): switch("passL", "-nostartfiles") switch("passL", "-mthumb -mcpu=cortex-m7 -mfloat-abi=hard -mfpu=fpv5-sp-d16 -D__FPU_USED=1") - switch("passL", "-T" & sdkPath() / "C_API" / "buildsupport" / "link_map.ld") + switch("passL", "-T" & playdateSdkPath / "C_API" / "buildsupport" / "link_map.ld") switch("passL", "-Wl,-Map=game.map,--cref,--gc-sections,--emit-relocs") switch("passL", "--entry eventHandlerShim") switch("passL", "-lc -lm -lgcc") @@ -167,7 +167,7 @@ else: # they get compiled in the correct nimcache folder. # Windows doesn't like having setup.c compiled. if defined(device) or not defined(windows): - switch("compile", sdkPath() / "C_API" / "buildsupport" / "setup.c") + switch("compile", playdateSdkPath / "C_API" / "buildsupport" / "setup.c") # Overrides the nim memory management code to ensure it uses the playdate allocator - patchFile("stdlib", "malloc", currentSourcePath().parentDir() /../ "bindings" / "malloc") \ No newline at end of file + patchFile("stdlib", "malloc", currentSourcePath().parentDir() /../ "bindings" / "malloc") diff --git a/src/playdate/build/nimble.nim b/src/playdate/build/nimble.nim index 422e461..c05345e 100644 --- a/src/playdate/build/nimble.nim +++ b/src/playdate/build/nimble.nim @@ -1,146 +1,18 @@ -import sequtils, strutils, os, strformat - -import utils # This file is designed to be `included` directly from a nimble file, which will make `switch` and `task` # implicitly available. This block just fixes auto-complete in IDEs when not compiles(task): import system/nimscript - -proc bundlePDX() = - ## Bundles pdx file using parent directory name. - echo("pdc version:") - exec(pdcPath() & " --version") - exec(pdcPath() & " --verbose -sdkpath " & sdkPath() & " source " & - thisDir().splitPath.tail) - -proc postBuild(target: Target) = - ## Performs post-build cleanup and prepares files for bundling. - case target: - of simulator: - if defined(windows): - mvFile(projectName() & ".exe", "source" / "pdex.dll") - elif defined(macosx): - mvFile(projectName(), "source" / "pdex.dylib") - rmDir("source" / "pdex.dSYM") - mvFile(projectName() & ".dSYM", "source" / "pdex.dSYM") - elif defined(linux): - mvFile(projectName(), "source" / "pdex.so") - of device: - if defined(windows): - mvFile(projectName() & ".exe", "source" / "pdex.elf") - else: - mvFile(projectName(), "source" / "pdex.elf") - rmFile("game.map") - -before clean: - let args = taskArgs("clean") - # Used to remove debug (_d) and release (_r) cache folders. - let baseCacheDir = nimcacheDir()[0..^2] - if args.contains("simulator"): - rmDir((baseCacheDir & "d") / $Target.simulator) - rmDir((baseCacheDir & "r") / $Target.simulator) - rmDir("source" / "pdex.dSYM") - rmFile("source" / "pdex.dylib") - rmFile("source" / "pdex.dll") - rmFile("source" / "pdex.so") - elif args.contains("device"): - rmDir((baseCacheDir & "d") / $Target.device) - rmDir((baseCacheDir & "r") / $Target.device) - rmFile("source" / "pdex.bin") - rmFile("source" / "pdex.elf") - else: - rmDir((baseCacheDir & "d")) - rmDir((baseCacheDir & "r")) - rmDir(nimcacheDir()) - rmDir(pdxName()) - rmDir("source" / "pdex.dSYM") - rmFile("source" / "pdex.bin") - rmFile("source" / "pdex.dylib") - rmFile("source" / "pdex.dll") - rmFile("source" / "pdex.so") - rmFile("source" / "pdex.elf") - rmFile("game.map") - rmFile(projectName()) - rmFile(projectName() & ".exe") - task simulator, "Build for the simulator": - let args = taskArgs("simulator") - if args.contains("release"): - nimble "-d:simulator", "-d:release", "build", "--verbose" - else: - nimble "-d:simulator", "-d:debug", "build", "--verbose" - postBuild(Target.simulator) - bundlePDX() + exec "pdn simulator" task simulate, "Build and run in the simulator": - nimble "simulator" - exec (simulatorPath(open = true) & " " & pdxName()) + exec "pdn simulate" task device, "Build for the device": - let args = taskArgs("device") - if args.contains("debug"): - nimble "-d:device", "-d:debug", "build", "--verbose" - else: - nimble "-d:device", "-d:release", "build", "--verbose" - postBuild(Target.device) - bundlePDX() - -task all, "Build for both the simulator and the device": - let args = taskArgs("all") - var simulatorBuild = "debug" - var deviceBuild = "release" - # Only release device build are supported on macOS at the moment. - if args.contains("debug") and not defined(macosx): - deviceBuild = "debug" - elif args.contains("release"): - simulatorBuild = "release" - nimble "-d:simulator", fmt"-d:{simulatorBuild}", "build", "--verbose" - postBuild(Target.simulator) - nimble "-d:device", fmt"-d:{deviceBuild}", "build", "--verbose" - postBuild(Target.device) - bundlePDX() + exec "pdn device" task configure, "Initialize the build structure": - ## Creates a default source directory if it doesn't already exist - - # Calling `sdkPath` will ensure the SDK environment variable is saved - # to the config path - discard sdkPath() - - if not dirExists("source"): - mkDir "source" - - if not fileExists("source/pdxinfo"): - let cartridgeName = projectName() - .replace("_", " ") - .split(" ") - .map(proc(s: string): string = s.capitalizeAscii()) - .join(" ") - let bundleAuthor = author - .toLower() - .replace(" ", "") - .replace("-", "") - .replace("_", "") - let bundleProjectName = projectName() - .toLower() - .replace(" ", "") - .replace("-", "") - .replace("_", "") - writeFile( - "source/pdxinfo", - [ - "name=" & cartridgeName, - "author=" & author, - "description=" & description, - "bundleId=com." & bundleAuthor & "." & bundleProjectName - ].join("\n") - ) - - if not fileExists( ".gitignore"): - ".gitignore".writeFile([ - pdxName(), - "source/pdex.*", - "*.dSYM" - ].join("\n")) + # No longer required + discard diff --git a/src/playdate/build/utils.nim b/src/playdate/build/utils.nim index d78f95f..a54b994 100644 --- a/src/playdate/build/utils.nim +++ b/src/playdate/build/utils.nim @@ -1,74 +1,32 @@ -import sequtils, strutils, os, json +import std/[os, options] -when not compiles(task): - import system/nimscript - -type Target* = enum - ## Target of the compilation process, simulator or device - simulator = "simulator" - device = "device" - -type CompileInstructions* = object - compile: seq[array[2, string]] - -type BuildFail* = object of Defect +type + BuildFail* = object of Defect +# This file is used by both nim proper and nimscript. They have different names +# for the `createDir` method, so we need to map between them +when not declared(mkDir): + template mkDir(dir) = createDir(dir) const SDK_ENV_VAR* = "PLAYDATE_SDK_PATH" +const playdateSdkPath {.strDefine.} = "" -proc nimble*(args: varargs[string]) = - ## Executes nimble with the given set of arguments - exec @["nimble"].concat(args.toSeq).join(" ") - -proc pdxName*(): string = - ## The name of the pdx file to generate, same as the project folder name - getCurrentDir().splitPath.tail & ".pdx" - -proc sdkPath*(): string = +proc sdkPath*(inputParam: Option[string] = none(string)): string = ## Returns the path of the playdate SDK - let fromEnv = getEnv(SDK_ENV_VAR) - let sdkPathCache = getConfigDir() / projectName() / SDK_ENV_VAR - - if fromEnv != "": - mkDir(sdkPathCache.parentDir) - writeFile(sdkPathCache, fromEnv) - return fromEnv - - if fileExists(sdkPathCache): - let fromFile = readFile(sdkPathCache) - if fromFile != "": - echo "Read SDK path from file: " & sdkPathCache - echo "SDK Path: " & fromFile - return fromFile - - raise BuildFail.newException("SDK environment variable is not set: " & SDK_ENV_VAR) - -proc simulatorPath*(open: bool = false): string = - ## Returns the path of the playdate simulator - if defined(windows): - return sdkPath() / "bin" / "PlaydateSimulator.exe" - elif defined(macosx): - return (if open: "open " else: "") & sdkPath() / "bin" / "Playdate\\ Simulator.app" - else: - return sdkPath() / "bin" / "PlaydateSimulator" - -proc pdcPath*(): string = - ## Returns the path of the pdc playdate utility - return sdkPath() / "bin" / "pdc" - -proc filesToCompile*(target: Target): seq[string] = - ## Returns the list of C files that have to be compiled - let jsonString = readFile(nimcacheDir() / $target / projectName() & ".json") - let instructions = parseJson(jsonString).to(CompileInstructions) - - return instructions.compile.map( - proc(entry: array[2, string]): string = - return entry[0] - ) - -proc taskArgs*(taskName: string): seq[string] = - ## Returns the arguments the current task `taskName` has received - let args = command_line_params() - let argStart = args.find(taskName) + 1 - return args[argStart..^1] \ No newline at end of file + let sdkPathCache = getConfigDir() / "playdate-nim" / SDK_ENV_VAR + + if inputParam.isSome: + result = inputParam.get + elif playdateSdkPath != "": + result = playdateSdkPath + elif getEnv(SDK_ENV_VAR) != "": + result = getEnv(SDK_ENV_VAR) + elif sdkPathCache.fileExists: + result = readFile(sdkPathCache) + + if result == "": + raise BuildFail.newException("Unable to determine the path to the playdate SDK") + + mkDir(sdkPathCache.parentDir) + writeFile(sdkPathCache, result) diff --git a/tests/.gitignore b/tests/.gitignore index b74210d..6aa4a90 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -1,3 +1,6 @@ *.pdx source/pdex.* *.dSYM +playdate_tests.pdx.zip +playdate_tests.pdx +source/pdxinfo \ No newline at end of file diff --git a/tests/source/pdxinfo b/tests/source/pdxinfo deleted file mode 100644 index 0dd241d..0000000 --- a/tests/source/pdxinfo +++ /dev/null @@ -1,4 +0,0 @@ -name=Playdate Tests -author=Samuele Zolfanelli -description=Unit tests for the Playdate Nim bindings -bundleId=com.samdze.playdate_tests \ No newline at end of file From 9441f261d293ac375b00450b48c4ed45c4c75ffb Mon Sep 17 00:00:00 2001 From: Nino van Hooff Date: Thu, 13 Feb 2025 18:57:55 +0100 Subject: [PATCH 12/21] FIX Mac simulator path --- src/playdate/build/actions.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/playdate/build/actions.nim b/src/playdate/build/actions.nim index 1c23e07..3c2e2a4 100644 --- a/src/playdate/build/actions.nim +++ b/src/playdate/build/actions.nim @@ -177,7 +177,7 @@ proc runSimulator*(conf: PlaydateConf) = when defined(windows): exec(conf.sdkPath / "bin" / "PlaydateSimulator.exe", conf.pdxName) elif defined(macosx): - exec("open", conf.sdkPath / "bin" / "Playdate\\ Simulator.app", conf.pdxName) + exec("open", conf.sdkPath / "bin" / "Playdate Simulator.app", conf.pdxName) else: exec(conf.sdkPath / "bin" / "PlaydateSimulator", conf.pdxName) From 41ffd68872d3650266bfa6e885e7124c89b3494f Mon Sep 17 00:00:00 2001 From: Nino van Hooff Date: Thu, 13 Feb 2025 21:51:43 +0100 Subject: [PATCH 13/21] REFACTOR lcdbitmap out of graphics --- src/playdate/domain/lcdbitmap.nim | 41 +++++++++++++++++++++++++++++++ src/playdate/graphics.nim | 40 ++---------------------------- src/playdate/nineslice.nim | 1 + src/playdate/sprite.nim | 1 + src/playdate/system.nim | 3 +-- 5 files changed, 46 insertions(+), 40 deletions(-) create mode 100644 src/playdate/domain/lcdbitmap.nim diff --git a/src/playdate/domain/lcdbitmap.nim b/src/playdate/domain/lcdbitmap.nim new file mode 100644 index 0000000..d5430b5 --- /dev/null +++ b/src/playdate/domain/lcdbitmap.nim @@ -0,0 +1,41 @@ +import std/importutils +import ../bindings/[api, types, graphics] # todo add playdate folder to path so we can import relative to that folder +import ../bindings/graphics # is it ok to import this here? + +type + LCDBitmapObj = object of RootObj + res {.requiresinit.}: LCDBitmapPtr + + LCDBitmapObjRef = ref LCDBitmapObj + + LCDBitmap* = object + case managed: bool + of true: + obj: LCDBitmapObjRef + of false: + res: LCDBitmapPtr + +proc `=destroy`(this: var LCDBitmapObj) = + privateAccess(PlaydateGraphics) + if this.res != nil: + playdate.graphics.freeBitmap(this.res) + +proc `=copy`(a: var LCDBitmapObj, b: LCDBitmapObj) {.error.} + +converter bitmapPtr*(point: LCDBitmapPtr): auto = + LCDBitmap(managed: false, res: point) + +proc bitmapRef(point: LCDBitmapPtr): auto = + LCDBitmap(managed: true, obj: LCDBitmapObjRef(res: point)) + +proc `==`*(bitmap: LCDBitmap, point: LCDBitmapPtr): bool = + not bitmap.managed and bitmap.res == point + +proc resource(bitmap: LCDBitmap): LCDBitmapPtr = + if bitmap.managed: + return if bitmap.obj != nil: bitmap.obj.res else: nil + else: + return bitmap.res + +proc isNil*(bitmap: LCDBitmap): bool = + return bitmap.resource == nil \ No newline at end of file diff --git a/src/playdate/graphics.nim b/src/playdate/graphics.nim index cb7c99f..04b9f51 100644 --- a/src/playdate/graphics.nim +++ b/src/playdate/graphics.nim @@ -4,50 +4,14 @@ import std/importutils import bindings/[api, types] import bindings/graphics +import domain/lcdbitmap {.all.} +export lcdbitmap # Only export public symbols, then import all export graphics {.hint[DuplicateModuleImport]: off.} import bindings/graphics {.all.} -type - LCDBitmapObj = object of RootObj - res {.requiresinit.}: LCDBitmapPtr - - LCDBitmapObjRef = ref LCDBitmapObj - - LCDBitmap* = object - case managed: bool - of true: - obj: LCDBitmapObjRef - of false: - res: LCDBitmapPtr - -proc `=destroy`(this: var LCDBitmapObj) = - privateAccess(PlaydateGraphics) - if this.res != nil: - playdate.graphics.freeBitmap(this.res) - -proc `=copy`(a: var LCDBitmapObj, b: LCDBitmapObj) {.error.} - -converter bitmapPtr*(point: LCDBitmapPtr): auto = - LCDBitmap(managed: false, res: point) - -proc bitmapRef(point: LCDBitmapPtr): auto = - LCDBitmap(managed: true, obj: LCDBitmapObjRef(res: point)) - -proc `==`*(bitmap: LCDBitmap, point: LCDBitmapPtr): bool = - not bitmap.managed and bitmap.res == point - -proc resource*(bitmap: LCDBitmap): LCDBitmapPtr = - if bitmap.managed: - return if bitmap.obj != nil: bitmap.obj.res else: nil - else: - return bitmap.res - -proc isNil*(bitmap: LCDBitmap): bool = - return bitmap.resource == nil - type LCDVideoPlayerObj = object of RootObj resource {.requiresinit.}: LCDVideoPlayerPtr context: LCDBitmap diff --git a/src/playdate/nineslice.nim b/src/playdate/nineslice.nim index ee1aad0..9ba2653 100644 --- a/src/playdate/nineslice.nim +++ b/src/playdate/nineslice.nim @@ -1,4 +1,5 @@ import graphics +import domain/lcdbitmap import std/[importutils, bitops, math, strutils] privateAccess(BitmapData) diff --git a/src/playdate/sprite.nim b/src/playdate/sprite.nim index 9ca7e66..baed04a 100644 --- a/src/playdate/sprite.nim +++ b/src/playdate/sprite.nim @@ -2,6 +2,7 @@ import std/[importutils, lists, sequtils] +import domain/lcdbitmap import graphics {.all.} import types {.all.} import bindings/[api, utils] diff --git a/src/playdate/system.nim b/src/playdate/system.nim index 491c9b1..12d9a14 100644 --- a/src/playdate/system.nim +++ b/src/playdate/system.nim @@ -6,7 +6,7 @@ import sequtils import bindings/[api, types] import bindings/system -import graphics +import domain/lcdbitmap {.all.} # Only export public symbols, then import all export system @@ -190,7 +190,6 @@ proc removeAllMenuItems*(this: ptr PlaydateSys) = proc setMenuImage*(this: ptr PlaydateSys, image: LCDBitmap, xOffset: int32 = 0) = privateAccess(PlaydateSys) - privateAccess(PlaydateGraphics) this.setMenuImage( # if image != nil: image.resource else: nil image.resource, From 1e1c6d1afe5a762b643a33b427c1c63de226a008 Mon Sep 17 00:00:00 2001 From: Nino van Hooff Date: Thu, 13 Feb 2025 22:08:05 +0100 Subject: [PATCH 14/21] ADD stc/playdate to Nim source path --- config.nims | 5 +++++ src/playdate/domain/lcdbitmap.nim | 4 ++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/config.nims b/config.nims index 8ee48d2..bcf3510 100644 --- a/config.nims +++ b/config.nims @@ -1,4 +1,9 @@ # begin Nimble config (version 2) + +# allow imports relative to the src/playdate folder from anywhere +# ie. import domain/lcdbitmap +--path:"./src/playdate" + when withDir(thisDir(), system.fileExists("nimble.paths")): include "nimble.paths" # end Nimble config diff --git a/src/playdate/domain/lcdbitmap.nim b/src/playdate/domain/lcdbitmap.nim index d5430b5..d218a3e 100644 --- a/src/playdate/domain/lcdbitmap.nim +++ b/src/playdate/domain/lcdbitmap.nim @@ -1,6 +1,6 @@ import std/importutils -import ../bindings/[api, types, graphics] # todo add playdate folder to path so we can import relative to that folder -import ../bindings/graphics # is it ok to import this here? +import bindings/[api, types, graphics] +import bindings/graphics type LCDBitmapObj = object of RootObj From ed782f5104143f667b3042b83ef2cbe8dc351422 Mon Sep 17 00:00:00 2001 From: Nino van Hooff Date: Fri, 14 Feb 2025 12:39:15 +0100 Subject: [PATCH 15/21] FIX imports in lcdbitmap --- src/playdate/domain/lcdbitmap.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/playdate/domain/lcdbitmap.nim b/src/playdate/domain/lcdbitmap.nim index d218a3e..9e03538 100644 --- a/src/playdate/domain/lcdbitmap.nim +++ b/src/playdate/domain/lcdbitmap.nim @@ -1,6 +1,6 @@ import std/importutils -import bindings/[api, types, graphics] -import bindings/graphics +import ../bindings/[api, types, graphics] +import ../bindings/graphics type LCDBitmapObj = object of RootObj From 7f9de7777623a4f3221671685bc6649049a47550 Mon Sep 17 00:00:00 2001 From: Nino van Hooff Date: Fri, 14 Feb 2025 12:45:01 +0100 Subject: [PATCH 16/21] REMOVE duplicate import --- src/playdate/domain/lcdbitmap.nim | 1 - 1 file changed, 1 deletion(-) diff --git a/src/playdate/domain/lcdbitmap.nim b/src/playdate/domain/lcdbitmap.nim index 9e03538..949f78f 100644 --- a/src/playdate/domain/lcdbitmap.nim +++ b/src/playdate/domain/lcdbitmap.nim @@ -1,6 +1,5 @@ import std/importutils import ../bindings/[api, types, graphics] -import ../bindings/graphics type LCDBitmapObj = object of RootObj From ac27f1db26c13c87f9c4de3b110c4fce143f7bd1 Mon Sep 17 00:00:00 2001 From: Nino van Hooff Date: Fri, 14 Feb 2025 12:50:23 +0100 Subject: [PATCH 17/21] REMOVE useless imports --- src/playdate/nineslice.nim | 1 - src/playdate/sprite.nim | 1 - 2 files changed, 2 deletions(-) diff --git a/src/playdate/nineslice.nim b/src/playdate/nineslice.nim index 9ba2653..ee1aad0 100644 --- a/src/playdate/nineslice.nim +++ b/src/playdate/nineslice.nim @@ -1,5 +1,4 @@ import graphics -import domain/lcdbitmap import std/[importutils, bitops, math, strutils] privateAccess(BitmapData) diff --git a/src/playdate/sprite.nim b/src/playdate/sprite.nim index baed04a..9ca7e66 100644 --- a/src/playdate/sprite.nim +++ b/src/playdate/sprite.nim @@ -2,7 +2,6 @@ import std/[importutils, lists, sequtils] -import domain/lcdbitmap import graphics {.all.} import types {.all.} import bindings/[api, utils] From 037b6ec710735cf0dd36477d08a538c10cf48bba Mon Sep 17 00:00:00 2001 From: Nino van Hooff Date: Fri, 14 Feb 2025 12:52:40 +0100 Subject: [PATCH 18/21] REVERT disabling of echo in graphics.nim Because system can now be imported without dependency cycles again --- src/playdate/graphics.nim | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/playdate/graphics.nim b/src/playdate/graphics.nim index 04b9f51..907067f 100644 --- a/src/playdate/graphics.nim +++ b/src/playdate/graphics.nim @@ -2,6 +2,7 @@ import std/importutils +import system import bindings/[api, types] import bindings/graphics import domain/lcdbitmap {.all.} @@ -55,7 +56,7 @@ proc getInfo*(this: LCDVideoPlayer): tuple[width: int, height: int, frameRate: f proc getContext*(this: LCDVideoPlayer): LCDBitmap = privateAccess(PlaydateVideo) let bitmapPtr = playdate.graphics.video.getContext(this.resource) - # echo(fmt"video context: {bitmapPtr.repr}") + playdate.system.logToConsole(fmt"video context: {bitmapPtr.repr}") if this.context == nil or this.context.resource != bitmapPtr: this.context = bitmapPtr(bitmapPtr) return this.context From 973ac8e49b9f548d47aa6f9a807fd8e28aa0dc3d Mon Sep 17 00:00:00 2001 From: Nino van Hooff Date: Fri, 14 Feb 2025 13:38:40 +0100 Subject: [PATCH 19/21] ADD tests --- tests/src/playdate_tests.nim | 3 ++- tests/t_system.nim | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 tests/t_system.nim diff --git a/tests/src/playdate_tests.nim b/tests/src/playdate_tests.nim index a3f505b..f203fca 100644 --- a/tests/src/playdate_tests.nim +++ b/tests/src/playdate_tests.nim @@ -5,10 +5,11 @@ ## import playdate/api -import ../[t_buttons, t_graphics, t_nineslice, t_files, t_midi, t_scoreboards] +import ../[ t_system, t_buttons, t_graphics, t_nineslice, t_files, t_midi, t_scoreboards] proc runTests() {.raises: [].} = try: + execSystemTests(false) execButtonsTests() execGraphicsTests(true) execNineSliceTests(true) diff --git a/tests/t_system.nim b/tests/t_system.nim new file mode 100644 index 0000000..66d943b --- /dev/null +++ b/tests/t_system.nim @@ -0,0 +1,18 @@ +import unittest, playdate/api + +proc execSystemTests*(runnable: bool)= + suite "System API": + + test "setMenuImage with nil": + if(runnable): + playdate.system.setMenuImage(nil, 0) + + test "setMenuImage with bitmap": + if(runnable): + let bgBitmap = playdate.graphics.newBitmap(400, 240, kColorBlack) + playdate.system.setMenuImage(bgBitmap, 0) + +when isMainModule: + # We can't run these methods from the tests, so we're only interested in + # whether they compile. + execSystemTests(runnable = false) \ No newline at end of file From 7036add874e3ca1cf23ebcc63fc03d6f454a2127 Mon Sep 17 00:00:00 2001 From: Nino van Hooff Date: Sun, 16 Feb 2025 12:08:25 +0100 Subject: [PATCH 20/21] REFACTOR lcdbitmap to /playdate --- src/playdate/graphics.nim | 2 +- src/playdate/{domain => }/lcdbitmap.nim | 2 +- src/playdate/system.nim | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) rename src/playdate/{domain => }/lcdbitmap.nim (96%) diff --git a/src/playdate/graphics.nim b/src/playdate/graphics.nim index 907067f..609ae56 100644 --- a/src/playdate/graphics.nim +++ b/src/playdate/graphics.nim @@ -5,7 +5,7 @@ import std/importutils import system import bindings/[api, types] import bindings/graphics -import domain/lcdbitmap {.all.} +import lcdbitmap {.all.} export lcdbitmap # Only export public symbols, then import all diff --git a/src/playdate/domain/lcdbitmap.nim b/src/playdate/lcdbitmap.nim similarity index 96% rename from src/playdate/domain/lcdbitmap.nim rename to src/playdate/lcdbitmap.nim index 949f78f..2809928 100644 --- a/src/playdate/domain/lcdbitmap.nim +++ b/src/playdate/lcdbitmap.nim @@ -1,5 +1,5 @@ import std/importutils -import ../bindings/[api, types, graphics] +import bindings/[api, types, graphics] type LCDBitmapObj = object of RootObj diff --git a/src/playdate/system.nim b/src/playdate/system.nim index 12d9a14..975b63b 100644 --- a/src/playdate/system.nim +++ b/src/playdate/system.nim @@ -6,7 +6,7 @@ import sequtils import bindings/[api, types] import bindings/system -import domain/lcdbitmap {.all.} +import lcdbitmap {.all.} # Only export public symbols, then import all export system From 4c1013f3970884219219d66d1402494f0bc55fbd Mon Sep 17 00:00:00 2001 From: Nino van Hooff Date: Sun, 16 Feb 2025 12:11:55 +0100 Subject: [PATCH 21/21] CLEANUP leftover comments --- src/playdate/system.nim | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/playdate/system.nim b/src/playdate/system.nim index 975b63b..6f44344 100644 --- a/src/playdate/system.nim +++ b/src/playdate/system.nim @@ -186,12 +186,10 @@ proc removeAllMenuItems*(this: ptr PlaydateSys) = item.active = false menuItems.setLen(0) this.removeAllMenuItems() -# --- proc setMenuImage*(this: ptr PlaydateSys, image: LCDBitmap, xOffset: int32 = 0) = privateAccess(PlaydateSys) this.setMenuImage( - # if image != nil: image.resource else: nil image.resource, xOffset.cint )