From f35a8ac75958f28fa55ed8ab2eaf7b2d15631da7 Mon Sep 17 00:00:00 2001 From: treeform Date: Thu, 5 Mar 2026 16:58:56 -0800 Subject: [PATCH 1/5] add runners --- .github/workflows/build.yml | 1 - examples/property_changes.nim | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 1ad60e5..2f801e2 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -16,7 +16,6 @@ jobs: - uses: treeform/setup-nim-action@v6 - run: nimby sync -g nimby.lock - - run: nimby install -g boxy # Run tests. - run: nim c tests/test.nim diff --git a/examples/property_changes.nim b/examples/property_changes.nim index 1705528..15f8654 100644 --- a/examples/property_changes.nim +++ b/examples/property_changes.nim @@ -70,6 +70,7 @@ while not window.closeRequested: doAssert not window.minimized window.minimized = true + waitFor(window.minimized, 30) doAssert window.minimized From 1505206415945e37ad430f2acb0a91fc33edefd2 Mon Sep 17 00:00:00 2001 From: treeform Date: Thu, 5 Mar 2026 17:06:04 -0800 Subject: [PATCH 2/5] Fix full screen tracking on mac. --- examples/property_changes.nim | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/property_changes.nim b/examples/property_changes.nim index 15f8654..1705528 100644 --- a/examples/property_changes.nim +++ b/examples/property_changes.nim @@ -70,7 +70,6 @@ while not window.closeRequested: doAssert not window.minimized window.minimized = true - waitFor(window.minimized, 30) doAssert window.minimized From ef75458a3d79b7bcdc4c0a09ba14d0d2607a48e3 Mon Sep 17 00:00:00 2001 From: treeform Date: Thu, 5 Mar 2026 17:12:56 -0800 Subject: [PATCH 3/5] ci install boxy only in workflow Made-with: Cursor --- .github/workflows/build.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 2f801e2..1ad60e5 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -16,6 +16,7 @@ jobs: - uses: treeform/setup-nim-action@v6 - run: nimby sync -g nimby.lock + - run: nimby install -g boxy # Run tests. - run: nim c tests/test.nim From 774dec272ddc91b8f83bca69aec40027ae96daec Mon Sep 17 00:00:00 2001 From: treeform Date: Fri, 13 Mar 2026 18:00:30 -0700 Subject: [PATCH 4/5] first metal changes --- src/windy/platforms/macos/platform.nim | 251 ++++++++++++++++--------- 1 file changed, 162 insertions(+), 89 deletions(-) diff --git a/src/windy/platforms/macos/platform.nim b/src/windy/platforms/macos/platform.nim index c527879..df1a604 100644 --- a/src/windy/platforms/macos/platform.nim +++ b/src/windy/platforms/macos/platform.nim @@ -1,8 +1,14 @@ import std/[os, times, unicode, pathnorm], - opengl, pixie/fileformats/png, pixie/images, utils, vmath, + pixie/fileformats/png, pixie/images, utils, vmath, ../../[common, internal], macdefs +when defined(windyMetal): + {.hint: "Using Metal backend".} +else: + import opengl + {.hint: "Using OpenGL backend".} + # TODO: Use macos native http client, fallback to windy http client. import ../../http export http @@ -46,6 +52,9 @@ var WindyAppDelegate, WindyWindow, WindyView: Class windows: seq[Window] +objc: + proc initWithFrame(self: NSView, x: NSRect): NSView + proc indexForNSWindow(windows: seq[Window], inner: NSWindow): int = ## Returns the window for this handle, else -1 for i, window in windows: @@ -71,15 +80,18 @@ proc style*(window: Window): WindowStyle = else: Decorated else: - var opaque: GLint - window.inner.contentView.NSOpenGLView.openGLContext.getValues( - opaque.addr, - NSOpenGLContextParameterSurfaceOpacity - ) - if opaque == 0: - Transparent - else: + when defined(windyMetal): Undecorated + else: + var opaque: GLint + window.inner.contentView.NSOpenGLView.openGLContext.getValues( + opaque.addr, + NSOpenGLContextParameterSurfaceOpacity + ) + if opaque == 0: + Transparent + else: + Undecorated proc fullscreen*(window: Window): bool = window.fullscreenState @@ -166,12 +178,16 @@ proc `style=`*(window: Window, windowStyle: WindowStyle) = of Undecorated, Transparent: window.inner.setStyleMask(undecoratedWindowMask) - var opaque: GLint = if windowStyle == Transparent: 0 else: 1 - autoreleasepool: - window.inner.contentView.NSOpenGLView.openGLContext.setValues( - opaque.addr, - NSOpenGLContextParameterSurfaceOpacity - ) + when defined(windyMetal): + if windowStyle == Transparent: + warn "Transparent style is not supported by windyMetal on macOS" + else: + var opaque: GLint = if windowStyle == Transparent: 0 else: 1 + autoreleasepool: + window.inner.contentView.NSOpenGLView.openGLContext.setValues( + opaque.addr, + NSOpenGLContextParameterSurfaceOpacity + ) proc `fullscreen=`*(window: Window, fullscreen: bool) = if window.fullscreen == fullscreen: @@ -767,36 +783,68 @@ proc init() {.raises: [].} = addMethod "windowDidResignKey:", windowDidResignKey addMethod "windowShouldClose:", windowShouldClose - addClass "WindyView", "NSOpenGLView", WindyView: - addProtocol "NSTextInputClient" - addMethod "acceptsFirstResponder", acceptsFirstResponder - addMethod "canBecomeKeyView", canBecomeKeyView - addMethod "acceptsFirstMouse:", acceptsFirstMouse - addMethod "viewDidChangeBackingProperties", viewDidChangeBackingProperties - addMethod "updateTrackingAreas", updateTrackingAreas - addMethod "mouseMoved:", mouseMoved - addMethod "mouseDragged:", mouseDragged - addMethod "rightMouseDragged:", rightMouseDragged - addMethod "otherMouseDragged:", otherMouseDragged - addMethod "scrollWheel:", scrollWheel - addMethod "mouseDown:", mouseDown - addMethod "mouseUp:", mouseUp - addMethod "rightMouseDown:", rightMouseDown - addMethod "rightMouseUp:", rightMouseUp - addMethod "otherMouseDown:", otherMouseDown - addMethod "otherMouseUp:", otherMouseUp - addMethod "hasMarkedText", hasMarkedText - addMethod "markedRange", markedRange - addMethod "selectedRange", selectedRange - addMethod "setMarkedText:selectedRange:replacementRange:", setMarkedText - addMethod "unmarkText", unmarkText - addMethod "validAttributesForMarkedText", validAttributesForMarkedText - addMethod "attributedSubstringForProposedRange:actualRange:", attributedSubstringForProposedRange - addMethod "insertText:replacementRange:", insertText2 - addMethod "characterIndexForPoint:", characterIndexForPoint - addMethod "firstRectForCharacterRange:actualRange:", firstRectForCharacterRange - addMethod "doCommandBySelector:", doCommandBySelector - addMethod "resetCursorRects", resetCursorRects + when defined(windyMetal): + addClass "WindyView", "NSView", WindyView: + addProtocol "NSTextInputClient" + addMethod "acceptsFirstResponder", acceptsFirstResponder + addMethod "canBecomeKeyView", canBecomeKeyView + addMethod "acceptsFirstMouse:", acceptsFirstMouse + addMethod "viewDidChangeBackingProperties", viewDidChangeBackingProperties + addMethod "updateTrackingAreas", updateTrackingAreas + addMethod "mouseMoved:", mouseMoved + addMethod "mouseDragged:", mouseDragged + addMethod "rightMouseDragged:", rightMouseDragged + addMethod "otherMouseDragged:", otherMouseDragged + addMethod "scrollWheel:", scrollWheel + addMethod "mouseDown:", mouseDown + addMethod "mouseUp:", mouseUp + addMethod "rightMouseDown:", rightMouseDown + addMethod "rightMouseUp:", rightMouseUp + addMethod "otherMouseDown:", otherMouseDown + addMethod "otherMouseUp:", otherMouseUp + addMethod "hasMarkedText", hasMarkedText + addMethod "markedRange", markedRange + addMethod "selectedRange", selectedRange + addMethod "setMarkedText:selectedRange:replacementRange:", setMarkedText + addMethod "unmarkText", unmarkText + addMethod "validAttributesForMarkedText", validAttributesForMarkedText + addMethod "attributedSubstringForProposedRange:actualRange:", attributedSubstringForProposedRange + addMethod "insertText:replacementRange:", insertText2 + addMethod "characterIndexForPoint:", characterIndexForPoint + addMethod "firstRectForCharacterRange:actualRange:", firstRectForCharacterRange + addMethod "doCommandBySelector:", doCommandBySelector + addMethod "resetCursorRects", resetCursorRects + else: + addClass "WindyView", "NSOpenGLView", WindyView: + addProtocol "NSTextInputClient" + addMethod "acceptsFirstResponder", acceptsFirstResponder + addMethod "canBecomeKeyView", canBecomeKeyView + addMethod "acceptsFirstMouse:", acceptsFirstMouse + addMethod "viewDidChangeBackingProperties", viewDidChangeBackingProperties + addMethod "updateTrackingAreas", updateTrackingAreas + addMethod "mouseMoved:", mouseMoved + addMethod "mouseDragged:", mouseDragged + addMethod "rightMouseDragged:", rightMouseDragged + addMethod "otherMouseDragged:", otherMouseDragged + addMethod "scrollWheel:", scrollWheel + addMethod "mouseDown:", mouseDown + addMethod "mouseUp:", mouseUp + addMethod "rightMouseDown:", rightMouseDown + addMethod "rightMouseUp:", rightMouseUp + addMethod "otherMouseDown:", otherMouseDown + addMethod "otherMouseUp:", otherMouseUp + addMethod "hasMarkedText", hasMarkedText + addMethod "markedRange", markedRange + addMethod "selectedRange", selectedRange + addMethod "setMarkedText:selectedRange:replacementRange:", setMarkedText + addMethod "unmarkText", unmarkText + addMethod "validAttributesForMarkedText", validAttributesForMarkedText + addMethod "attributedSubstringForProposedRange:actualRange:", attributedSubstringForProposedRange + addMethod "insertText:replacementRange:", insertText2 + addMethod "characterIndexForPoint:", characterIndexForPoint + addMethod "firstRectForCharacterRange:actualRange:", firstRectForCharacterRange + addMethod "doCommandBySelector:", doCommandBySelector + addMethod "resetCursorRects", resetCursorRects let appDelegate = WindyAppDelegate.new() NSApp.setDelegate(appDelegate) @@ -887,10 +935,16 @@ proc centerWindow(window: Window) = window.pos = ivec2(x.int32, y.int32) proc makeContextCurrent*(window: Window) = - window.inner.contentView.NSOpenGLView.openGLContext.makeCurrentContext() + when defined(windyMetal): + discard + else: + window.inner.contentView.NSOpenGLView.openGLContext.makeCurrentContext() proc swapBuffers*(window: Window) = - window.inner.contentView.NSOpenGLView.openGLContext.flushBuffer() + when defined(windyMetal): + discard + else: + window.inner.contentView.NSOpenGLView.openGLContext.flushBuffer() proc close*(window: Window) = window.onCloseRequest = nil @@ -931,12 +985,14 @@ proc newWindow*( init() - let openGlProfile: uint32 = case openglVersion: - of OpenGL4Dot1: - NSOpenGLProfileVersion4_1Core - else: - raise newException(WindyError, "Unsupported OpenGL version") - + when defined(windyMetal): + discard + else: + let openGlProfile: uint32 = case openglVersion: + of OpenGL4Dot1: + NSOpenGLProfileVersion4_1Core + else: + raise newException(WindyError, "Unsupported OpenGL version") autoreleasepool: result.inner = WindyWindow.alloc().NSWindow.initWithContentRect( @@ -946,49 +1002,58 @@ proc newWindow*( false ) - let - pixelFormatAttribs = [ - NSOpenGLPFADoubleBuffer, - NSOpenGLPFASampleBuffers, if msaa != msaaDisabled: 1 else: 0, - NSOpenGLPFASamples, msaa.uint32, - NSOpenGLPFAAccelerated, - NSOpenGLPFADoubleBuffer, - NSOpenGLPFAColorSize, 32, - NSOpenGLPFAAlphaSize, 8, - NSOpenGLPFADepthSize, depthBits.uint32, - NSOpenGLPFAStencilSize, stencilBits.uint32, - NSOpenGLPFAOpenGLProfile, openGlProfile, - 0 - ] - pixelFormat = NSOpenGLPixelFormat.alloc().initWithAttributes( - pixelFormatAttribs[0].unsafeAddr + when defined(windyMetal): + let metalView = WindyView.alloc().NSView.initWithFrame( + result.inner.contentView.frame ) + result.inner.setDelegate(result.inner.ID) + result.inner.setContentView(metalView) + discard result.inner.makeFirstResponder(metalView) + else: + let + pixelFormatAttribs = [ + NSOpenGLPFADoubleBuffer, + NSOpenGLPFASampleBuffers, if msaa != msaaDisabled: 1 else: 0, + NSOpenGLPFASamples, msaa.uint32, + NSOpenGLPFAAccelerated, + NSOpenGLPFADoubleBuffer, + NSOpenGLPFAColorSize, 32, + NSOpenGLPFAAlphaSize, 8, + NSOpenGLPFADepthSize, depthBits.uint32, + NSOpenGLPFAStencilSize, stencilBits.uint32, + NSOpenGLPFAOpenGLProfile, openGlProfile, + 0 + ] + pixelFormat = NSOpenGLPixelFormat.alloc().initWithAttributes( + pixelFormatAttribs[0].unsafeAddr + ) - let openglView = WindyView.alloc().NSOpenGLView.initWithFrame( - result.inner.contentView.frame, - pixelFormat - ) - openglView.setWantsBestResolutionOpenGLSurface(true) - - openglView.openGLContext.makeCurrentContext() + let openglView = WindyView.alloc().NSOpenGLView.initWithFrame( + result.inner.contentView.frame, + pixelFormat + ) + openglView.setWantsBestResolutionOpenGLSurface(true) - var swapInterval: GLint = if vsync: 1 else: 0 - openglView.openGLContext.setValues( - swapInterval.addr, - NSOpenGLContextParameterSwapInterval - ) + openglView.openGLContext.makeCurrentContext() - # Handle transparency for Transparent style - if style == Transparent: - var opaque: GLint = 0 + var swapInterval: GLint = if vsync: 1 else: 0 openglView.openGLContext.setValues( - opaque.addr, - NSOpenGLContextParameterSurfaceOpacity + swapInterval.addr, + NSOpenGLContextParameterSwapInterval ) - result.inner.setDelegate(result.inner.ID) - result.inner.setContentView(openglView.NSView) - discard result.inner.makeFirstResponder(openglView.NSView) + # Handle transparency for Transparent style + if style == Transparent: + var opaque: GLint = 0 + openglView.openGLContext.setValues( + opaque.addr, + NSOpenGLContextParameterSurfaceOpacity + ) + + result.inner.setDelegate(result.inner.ID) + result.inner.setContentView(openglView.NSView) + discard result.inner.makeFirstResponder(openglView.NSView) + result.inner.setRestorable(false) windows.add(result) @@ -1014,6 +1079,14 @@ proc title*(window: Window): string = proc icon*(window: Window): Image = window.state.icon +proc nativeWindow*(window: Window): NSWindow = + ## Returns the native AppKit window handle. + window.inner + +proc nativeView*(window: Window): NSView = + ## Returns the native AppKit content view. + window.inner.contentView + proc mousePos*(window: Window): IVec2 = window.state.mousePos From 79b7817b6159b0faaf4d755069095bc32585d577 Mon Sep 17 00:00:00 2001 From: treeform Date: Sun, 15 Mar 2026 15:39:10 -0700 Subject: [PATCH 5/5] useMetal4 --- src/windy/platforms/macos/platform.nim | 18 +++++++++--------- src/windy/platforms/win32/platform.nim | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/windy/platforms/macos/platform.nim b/src/windy/platforms/macos/platform.nim index df1a604..81545bb 100644 --- a/src/windy/platforms/macos/platform.nim +++ b/src/windy/platforms/macos/platform.nim @@ -3,7 +3,7 @@ import pixie/fileformats/png, pixie/images, utils, vmath, ../../[common, internal], macdefs -when defined(windyMetal): +when defined(useMetal4): {.hint: "Using Metal backend".} else: import opengl @@ -80,7 +80,7 @@ proc style*(window: Window): WindowStyle = else: Decorated else: - when defined(windyMetal): + when defined(useMetal4): Undecorated else: var opaque: GLint @@ -178,9 +178,9 @@ proc `style=`*(window: Window, windowStyle: WindowStyle) = of Undecorated, Transparent: window.inner.setStyleMask(undecoratedWindowMask) - when defined(windyMetal): + when defined(useMetal4): if windowStyle == Transparent: - warn "Transparent style is not supported by windyMetal on macOS" + warn "Transparent style is not supported by useMetal4 on macOS" else: var opaque: GLint = if windowStyle == Transparent: 0 else: 1 autoreleasepool: @@ -783,7 +783,7 @@ proc init() {.raises: [].} = addMethod "windowDidResignKey:", windowDidResignKey addMethod "windowShouldClose:", windowShouldClose - when defined(windyMetal): + when defined(useMetal4): addClass "WindyView", "NSView", WindyView: addProtocol "NSTextInputClient" addMethod "acceptsFirstResponder", acceptsFirstResponder @@ -935,13 +935,13 @@ proc centerWindow(window: Window) = window.pos = ivec2(x.int32, y.int32) proc makeContextCurrent*(window: Window) = - when defined(windyMetal): + when defined(useMetal4): discard else: window.inner.contentView.NSOpenGLView.openGLContext.makeCurrentContext() proc swapBuffers*(window: Window) = - when defined(windyMetal): + when defined(useMetal4): discard else: window.inner.contentView.NSOpenGLView.openGLContext.flushBuffer() @@ -985,7 +985,7 @@ proc newWindow*( init() - when defined(windyMetal): + when defined(useMetal4): discard else: let openGlProfile: uint32 = case openglVersion: @@ -1002,7 +1002,7 @@ proc newWindow*( false ) - when defined(windyMetal): + when defined(useMetal4): let metalView = WindyView.alloc().NSView.initWithFrame( result.inner.contentView.frame ) diff --git a/src/windy/platforms/win32/platform.nim b/src/windy/platforms/win32/platform.nim index 2769dc9..82f1bb1 100644 --- a/src/windy/platforms/win32/platform.nim +++ b/src/windy/platforms/win32/platform.nim @@ -11,7 +11,7 @@ type BackendKind = enum when defined(windyOpenGl): {.hint: "Using OpenGL backend".} const Backend = OpenGLBackend -elif defined(windyDirectX): +elif defined(useDirectX): {.hint: "Using DirectX backend".} const Backend = DirectXBackend else: