Run SpecBAS on macOS and Linux, through SDL2 - #26
Open
Xalior wants to merge 12 commits into
Open
Conversation
Two units stop a Free Pascal compile before it reaches anything else.
bass.pas guards its macOS library name with {$IFDEF MACOS}. That is
Delphi's symbol; Free Pascal defines DARWIN and never MACOS, so on a Free
Pascal Apple target the const block ends up empty and bassdll is
undeclared for every import below it:
bass.pas(849,1) Fatal: Syntax error, "identifier" expected but
"FUNCTION" found
RunTimeCompiler.pas imports ActiveX unconditionally, for IsEqualGUID.
ActiveX is a Windows unit:
RunTimeCompiler.pas(32,13) Fatal: Can't find unit ActiveX used by
RunTimeCompiler
Free Pascal's SysUtils declares IsEqualGUID on every target, so off
Windows the import is all that has to go. Windows keeps it, and keeps
the ActiveX implementation with it.
SpecBAS.inc auto-defines OPENGL for Free Pascal on Windows. SDL2 is the same shape for every other Free Pascal target: it says which host layer this build gets. Delphi and Windows are untouched, so the three-way split the source already carries stays as it is. SP_SDL2Backend is that host layer. It owns the window, the renderer and the streaming texture SpecBAS's frame buffer is uploaded into, and answers the clipboard, the display geometry and the clock. It knows nothing about SpecBAS beyond the address and pitch of the buffer it presents. Nothing selects it yet.
The SDL2 build links no Lazarus, so ClipBrd's Clipboard.AsText and LazUtils' CopyFile are not there to be called. SP_EditUnit, SP_MemoUnit, SP_FileIO and SP_Interpret_PostFix each reach for one of them by name. Both are answered here under the names and shapes those units already use, so nothing about the calls has to change: the clipboard from SDL2, and the copy over TFileStream, replacing an existing destination and carrying the source's modification time across when asked to. Nothing selects it yet.
The SDL2 build links no Lazarus, so a unit that names an LCL unit in its
uses clause stops the compile there. Free Pascal on Windows keeps every
one of them, and so does Delphi.
SP_Interpret_PostFix.pas(28,16) Fatal: Can't find unit Forms used by
SP_Interpret_PostFix
Three of them are replaced, by SP_SDL2Compat where the call has to keep
working and by nothing where the import was never used:
ClipBrd, in SP_EditUnit, SP_MemoUnit and SP_Interpret_PostFix, for
Clipboard.AsText.
FileUtil, in SP_FileIO and SP_Interpret_PostFix, for CopyFile. The one
qualified call site needs the qualifier to follow.
Forms, LCLType, LclIntf, GraphUtil and Dialogs, in the remaining files
here, are imported and never called.
SP_DebugPanel and SP_Components carry ClipBrd on both sides of an
existing {$IFNDEF FPC}, so the comma moves inside the conditional where
the item can now be absent.
SpecBAS names keys by the K_ constants in SP_Input.pas, which the editor, the widget set, KEYSTATE and INKEY$ all index by. SDL2 reports a scancode instead, so one of the two has to be turned into the other, and it is done in one place for every platform this backend covers. The mapping runs from the scancode rather than the keycode because a scancode names the physical key, which is what a K_ constant means as well. Which character that key produces is the layout's business and arrives separately, on the SDL_TEXTINPUT event. SpecBAS reads K_CONTROL for copy, cut, paste and select-all. macOS puts those on Command, so MAC_COMMAND_IS_CONTROL has Command deliver K_CONTROL as well and both keys work; it is a conditional inside the one mapping and Linux and Windows are unchanged by it. Nothing selects it yet.
SP_SDL2Host is what MainForm.pas is to the Lazarus build: the one
platform file. It creates the window, starts the interpreter thread,
fills in the CB_ callback table, turns SDL2 events into SpecBAS's own
input, and owns the main loop that Application.OnIdle drives on Windows.
It exports the names MainForm.pas exports, so a unit that wants the host
picks between the two in its uses clause and changes nothing else.
SpecBAS_SDL2.dpr is its entry point, as SpecBAS.dpr is Lazarus's.
Pictures load and save through Free Pascal's own fcl-image, because
there is no LCL here to supply TBitmap, TPortableNetworkGraphic,
TJPEGImage or TGIFImage. The clock is SDL2's performance counter: Free
Pascal 3.2.2 declares no clock_gettime on these targets, which is what
MainForm.pas's UNIX branch calls, and SDL_GetTicks counts whole
milliseconds, which is too coarse to pace a frame with.
SP_Display.pas is the file that would not compile at all. Every error in
it is Windows display-mode management outside any conditional:
SP_Display.pas(1010) StretchBlt
SP_Display.pas(1132) SystemParametersInfo, SPI_GETWORKAREA
SP_Display.pas(1185..1341) EnumDisplaySettings, ChangeDisplaySettings,
ChangeDisplaySettingsEx, GetMonitorInfo,
EnumDisplayDevices, TDeviceMode, TMonitor
Those become SDL_RenderCopy, SDL_GetDisplayUsableBounds,
SDL_GetDisplayBounds, SDL_GetCurrentDisplayMode and
SDL_SetWindowFullscreen. Its UNIX uses branch also omitted SysUtils,
SyncObjs and Types, which the file uses unconditionally for
TCriticalSection, TRect and PtInRect, and the frame buffer and the
window size were declared for the OpenGL path alone.
Fullscreen is desktop fullscreen. The renderer already stretches the
logical screen to whatever it is given, so there is nothing to gain from
switching the monitor's mode.
SP_BankManager and SP_UITools name MainForm in a uses clause and follow
it here.
Building the whole environment needs -O1. Free Pascal 3.2.2 for aarch64
raises internal error 200510011 on SP_FPEditor.pas at -O2.
The POSIX half of the unit is written against calls Free Pascal 3.2.2 does not have: SP_Sockets.pas(181,12) Error: Identifier not found "AddrInfo" SP_Sockets.pas(193,6) Error: Identifier not found "fpGetAddrInfo" SP_Sockets.pas(271,11) Error: Identifier not found "socket" SP_Sockets.pas(718,45) Error: Identifier not found "FIONREAD" There is no fpGetAddrInfo, no AddrInfo type and no fpFreeAddrInfo, so name resolution goes through netdb's ResolveHostByName, with StrToNetAddr answering a dotted quad without a lookup. StrToNetAddr is the network-order helper, which is the order netdb answers in; the host-order one beside it would reverse the octets and connect to the wrong machine. The socket entry points are named fpSocket, fpBind and so on. Only socket() is named in the code shared with WinSock, so it gets a name of its own here, as InitWinSock already does. FIONREAD is declared in termio. Nothing here is reached by the Windows build, which keeps WinSock2.
src/build.bat drives Delphi, and SpecBAS.lpi drives Lazarus. Neither builds this, so build-sdl2/ does: fetch-deps.sh puts the Pascal bindings and, on macOS, the SDL2 release framework into a gitignored vendor/, and the Makefile calls fpc directly. make app assembles a macOS application bundle with SDL2 inside it, which runs on a machine carrying no toolchain and no system SDL2. That is how SpecBAS already ships BASS on Windows, and it keeps a proprietary library alongside a GPLv3 application without linking it in. Every comment in these three files is there because the fact it records is not visible from the command it sits above. Free Pascal 3.2.2 needs no -k-ld_classic here: with Lazarus gone it emits no Objective-C, so there is no metadata for Xcode 26 to reject.
HandleMouseMotion takes DisplaySection with TryEnter and released it after the block that acquires it rather than inside, so a failed acquire was still followed by a release. TryEnter only fails while another thread holds the lock, which is why it killed the program on roughly half of the attempts and looked like a crash on a mouse click. Releasing a mutex this thread does not own is an error pthreads reports, which Free Pascal turns into runtime error 236 and SysUtils raises as EThreadError. Nothing on the path catches it, so the process printed two "EThreadError: Thread error" traces and stopped.
BUILDSTR was the literal '0.0.0.0', so the title bar read "SpecBAS v 0.0.0.0" where the Windows one reads the real version. Windows gets it from the executable's own VERSIONINFO resource at run time; there is no such resource here, and no way to read one back. build-sdl2/Makefile now reads src/SpecBAS.rc, which is where that resource is written, and generates SpecBAS_Version.inc for the unit to include. The file stays the project's single statement of its own version, and both builds report the same number: FILEVERSION 0,0,0,1511 shows as 0.1511 on each.
SDLB_GetMousePos derived the pointer from the global mouse position minus the window position, while motion events arrive in SDL's window-relative space. SP_Display.HandleMouse takes MOUSEX and MOUSEY from the first every frame and the host marks the area to repaint from the second, so where the two disagreed the pointer was drawn outside the rectangle redrawn for it. The global position is still what decides whether the pointer is inside the window, because HandleMouse needs to notice it leaving and SDL_GetMouseState cannot report that. Once inside, the position answered is SDL's own.
Two drawing faults showed up while dragging the mouse pointer over a window
with a drop shadow.
A shadow is blurred from the pixels around it and blended into the same buffer
it reads, so a dirty rectangle covering part of one gives a different result
from one covering all of it. The buffer keeps the previous frame, so the
difference accumulates and the area darkens and smears. Any rectangle that
touches a shadow is now widened to that whole shadow before the fragments are
generated.
The pointer's dirty rectangle started at MOUSEX, but the image is drawn at
MOUSEX - MOUSEHSX. With a hotspot, set by MOUSE GRAPHIC ... POINT, the strip
above and to the left of the pointer was drawn and never repainted. The
rectangle now starts where the image does.
Both are in code the Windows build compiles, so both changes are inside
{$IFDEF SDL2} and the Windows path selects what it did before.
PR Summary
These changes contribute to improving the robustness of the SpecBAS project by enabling a new SDL2 backend, improving the organization of source code, enhancing platform compatibility, and refining application build and setup processes. |
Open
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this does
SpecBAS builds and runs on macOS and Linux, with the same editor, console,
windows, dialogs and graphics as the Windows build.
SpecBAS draws its screen into
PixArrayand needs four things from the host:a window, a way to present that buffer, an event queue and a clock. Lazarus
supplies them on Windows. This branch adds an SDL2 implementation, selected by
the
SDL2compiler symbol.SP_SDL2Backend.pasSP_SDL2Host.pasSP_SDL2Keys.pasSP_SDL2Compat.pasSpecBAS_SDL2.dprThe Windows build is untouched
Every change to an existing file is behind
{$IFDEF SDL2}or an existingplatform conditional. A build without that symbol compiles the same text as
before.
Windows was not built or tested.
Where an existing file had to change, the commit message names the compile
error that forced it:
bass.pas--{$IFDEF MACOS}is Delphi's symbol. Free Pascal definesDARWIN, leaving aconstblock dangling.RunTimeCompiler.pas-- unconditional import ofActiveXfor a GUIDcomparison
SysUtilsprovides on every target.imports are made conditional, not removed.
SP_Display.pas-- Windows display-mode management outside any conditional,and a
usesbranch missing units used unconditionally.SP_Sockets.pas-- written againstAddrInfo,fpGetAddrInfo, a baresocket()andFIONREAD, none of which Free Pascal 3.2.2 has where thefile looks.
Building
Free Pascal 3.2.2, and SDL2 2.0.5 or later. No Lazarus, no
lazbuild.cd build-sdl2 make deps make appmake depsfillsvendor/, which is not tracked. It clones the Pascalbindings at a fixed commit. On macOS it also downloads the SDL2 framework,
checks its SHA-256, and leaves it for
make appto copy into the bundle.On Linux SDL2 comes from the distribution:
libsdl2-devon Debian, Ubuntu andRaspberry Pi OS,
SDL2-develon Fedora,sdl2on Arch.make depschecks itis installed and names the package if it is not.
make appproducesSpecBAS.appon macOS and the executable on Linux.The bundled framework is a fixed version because it must be universal, run on
Apple Silicon and pass codesign. Homebrew's
sdl2cannot do that job: it issdl2-compat, which reaches SDL3 bydlopenat run time, and it is built forthe host's own macOS version.
What has been run
headless under
SDL_VIDEODRIVER=dummy. A program was typed, run, and itsoutput seen.
The demo set from the v0.980 release was used, the 3D and Graphics folders in
particular.
What is not done
SoundEnabledfrom the result, so it runs without one. No BASS libraryships for macOS or Linux.
SAVE ... GIFwrites nothing.fcl-imagehas a GIF reader and no writer.Loading works. A GIF save target takes the same do-nothing branch an
unrecognised extension already takes.
neither Windows nor Linux.
CFBundleIdentifierin the macOS bundle is a placeholder.The commits
Each builds, and each is one topic.
Commit 12 is the only one that changes drawing code shared with Windows, and
it is inside
{$IFDEF SDL2}. It fixes two things seen while dragging thepointer over a window with a drop shadow:
covering part of one gives a different result from one covering all of it,
and the difference accumulates.
MOUSEX, but the image is drawn atMOUSEX - MOUSEHSX. With a hotspot, set byMOUSE GRAPHIC ... POINT, thestrip above and left of the pointer was never repainted.