diff --git a/makefile b/makefile index 5e5b22ff4..c8f5d013a 100644 --- a/makefile +++ b/makefile @@ -180,6 +180,8 @@ FBFLAGS := -maxerr 1 AS = $(BUILD_PREFIX)as AR = $(BUILD_PREFIX)ar CC = $(BUILD_PREFIX)gcc +# Objective-C compiler, used for the native Cocoa graphics driver on Darwin +OBJC = $(CC) -x objective-c prefix := /usr/local # Determine the makefile's directory, this may be a relative path when @@ -342,8 +344,13 @@ ifneq ($(filter 386 486 586 686 i386 i486 i586 i686,$(TARGET_ARCH)),) TARGET_ARCH := x86 endif -# Normalize TARGET_ARCH to arm -ifneq ($(filter arm%,$(TARGET_ARCH)),) +# Normalize TARGET_ARCH to aarch64 (macOS reports arm64) +ifneq ($(filter aarch64 arm64,$(TARGET_ARCH)),) + TARGET_ARCH := aarch64 +endif + +# Normalize TARGET_ARCH to arm (32-bit only, after aarch64 is handled) +ifneq ($(filter armv% arm,$(TARGET_ARCH)),) TARGET_ARCH := arm endif @@ -579,13 +586,15 @@ ifeq ($(TARGET_OS),solaris) endif ifeq ($(TARGET_OS),darwin) - ALLCFLAGS += -I/opt/X11/include -I/usr/include/ffi - - ifdef ENABLE_XQUARTZ - ALLFBCFLAGS += -d ENABLE_XQUARTZ - else - ALLCFLAGS += -DDISABLE_X11 - endif + # Darwin uses the native Cocoa/CoreGraphics driver, so X11 (XQuartz) is never + # built here: it would drag in an extra runtime dependency for the legacy + # path only. FB's OpenGL support lives in the X11 gfx driver, so without it + # there is no GL driver either -- gfx_opengl.c would otherwise still call + # the driver hook fb_hGL_GetProcAddress(), leaving libfbgfx with an undefined + # symbol. + ALLCFLAGS += -I/usr/include/ffi + ALLCFLAGS += -DDISABLE_X11 + ALLCFLAGS += -DDISABLE_OPENGL endif ifneq ($(filter cygwin win32,$(TARGET_OS)),) @@ -717,10 +726,15 @@ LIBFBRTMTPIC_C := $(patsubst %,$(libfbmtpicobjdir)/%,$(filter-out $(patsubst $(l LIBFBGFX_H := $(sort $(foreach i,$(GFXLIB2_DIRS),$(wildcard $(i)/*.h)) $(LIBFB_H)) LIBFBGFX_C := $(sort $(foreach i,$(GFXLIB2_DIRS),$(patsubst $(i)/%.c,$(libfbgfxobjdir)/%.o,$(wildcard $(i)/*.c)))) LIBFBGFX_S := $(sort $(foreach i,$(GFXLIB2_DIRS),$(patsubst $(i)/%.s,$(libfbgfxobjdir)/%.o,$(wildcard $(i)/*.s)))) +# Objective-C sources (the native Cocoa driver on Darwin) +LIBFBGFX_M := $(sort $(foreach i,$(GFXLIB2_DIRS),$(patsubst $(i)/%.m,$(libfbgfxobjdir)/%.o,$(wildcard $(i)/*.m)))) LIBFBGFXPIC_C := $(patsubst $(libfbgfxobjdir)/%,$(libfbgfxpicobjdir)/%,$(LIBFBGFX_C)) +LIBFBGFXPIC_M := $(patsubst $(libfbgfxobjdir)/%,$(libfbgfxpicobjdir)/%,$(LIBFBGFX_M)) LIBFBGFXMT_C := $(patsubst $(libfbgfxobjdir)/%,$(libfbgfxmtobjdir)/%,$(LIBFBGFX_C)) LIBFBGFXMT_S := $(patsubst $(libfbgfxobjdir)/%,$(libfbgfxmtobjdir)/%,$(LIBFBGFX_S)) +LIBFBGFXMT_M := $(patsubst $(libfbgfxobjdir)/%,$(libfbgfxmtobjdir)/%,$(LIBFBGFX_M)) LIBFBGFXMTPIC_C := $(patsubst $(libfbgfxobjdir)/%,$(libfbgfxmtpicobjdir)/%,$(LIBFBGFX_C)) +LIBFBGFXMTPIC_M := $(patsubst $(libfbgfxobjdir)/%,$(libfbgfxmtpicobjdir)/%,$(LIBFBGFX_M)) RTL_LIBS := $(libdir)/$(FB_LDSCRIPT) @@ -768,6 +782,7 @@ ifndef V QUIET_FBC = @echo "FBC $@"; QUIET_LINK = @echo "LINK $@"; QUIET_CC = @echo "CC $@"; + QUIET_OBJC = @echo "OBJC $@"; QUIET_CPPAS = @echo "CPPAS $@"; QUIET_AS = @echo "AS $@"; QUIET_AR = @echo "AR $@"; @@ -984,29 +999,37 @@ $(LIBFBMTRTPIC_BAS): $(libfbrtmtpicobjdir)/%.o: %.c $(LIBFBRT_BI) | $(libfbrtmtp .PHONY: gfxlib2 gfxlib2: $(GFX_LIBS) -$(libdir)/libfbgfx.a: $(LIBFBGFX_C) $(LIBFBGFX_S) | $(libdir) +$(libdir)/libfbgfx.a: $(LIBFBGFX_C) $(LIBFBGFX_S) $(LIBFBGFX_M) | $(libdir) $(QUIET_AR)rm -f $@; $(AR) rcs $@ $^ $(LIBFBGFX_C): $(libfbgfxobjdir)/%.o: %.c $(LIBFBGFX_H) | $(libfbgfxobjdir) $(QUIET_CC)$(CC) $(ALLCFLAGS) -c $< -o $@ $(LIBFBGFX_S): $(libfbgfxobjdir)/%.o: %.s $(LIBFBGFX_H) | $(libfbgfxobjdir) $(QUIET_CPPAS)$(CC) -x assembler-with-cpp $(ALLCFLAGS) -c $< -o $@ +$(LIBFBGFX_M): $(libfbgfxobjdir)/%.o: %.m $(LIBFBGFX_H) | $(libfbgfxobjdir) + $(QUIET_OBJC)$(OBJC) $(ALLCFLAGS) -c $< -o $@ -$(libdir)/libfbgfxpic.a: $(LIBFBGFXPIC_C) | $(libdir) +$(libdir)/libfbgfxpic.a: $(LIBFBGFXPIC_C) $(LIBFBGFXPIC_M) | $(libdir) $(QUIET_AR)rm -f $@; $(AR) rcs $@ $^ $(LIBFBGFXPIC_C): $(libfbgfxpicobjdir)/%.o: %.c $(LIBFBGFX_H) | $(libfbgfxpicobjdir) $(QUIET_CC)$(CC) -fPIC $(ALLCFLAGS) -c $< -o $@ +$(LIBFBGFXPIC_M): $(libfbgfxpicobjdir)/%.o: %.m $(LIBFBGFX_H) | $(libfbgfxpicobjdir) + $(QUIET_OBJC)$(OBJC) -fPIC $(ALLCFLAGS) -c $< -o $@ -$(libdir)/libfbgfxmt.a: $(LIBFBGFXMT_C) $(LIBFBGFXMT_S) | $(libdir) +$(libdir)/libfbgfxmt.a: $(LIBFBGFXMT_C) $(LIBFBGFXMT_S) $(LIBFBGFXMT_M) | $(libdir) $(QUIET_AR)rm -f $@; $(AR) rcs $@ $^ $(LIBFBGFXMT_C): $(libfbgfxmtobjdir)/%.o: %.c $(LIBFBGFX_H) | $(libfbgfxmtobjdir) $(QUIET_CC)$(CC) -DENABLE_MT $(ALLCFLAGS) -c $< -o $@ $(LIBFBGFXMT_S): $(libfbgfxmtobjdir)/%.o: %.s $(LIBFBGFX_H) | $(libfbgfxmtobjdir) $(QUIET_CPPAS)$(CC) -x assembler-with-cpp -DENABLE_MT $(ALLCFLAGS) -c $< -o $@ +$(LIBFBGFXMT_M): $(libfbgfxmtobjdir)/%.o: %.m $(LIBFBGFX_H) | $(libfbgfxmtobjdir) + $(QUIET_OBJC)$(OBJC) -DENABLE_MT $(ALLCFLAGS) -c $< -o $@ -$(libdir)/libfbgfxmtpic.a: $(LIBFBGFXMTPIC_C) | $(libdir) +$(libdir)/libfbgfxmtpic.a: $(LIBFBGFXMTPIC_C) $(LIBFBGFXMTPIC_M) | $(libdir) $(QUIET_AR)rm -f $@; $(AR) rcs $@ $^ $(LIBFBGFXMTPIC_C): $(libfbgfxmtpicobjdir)/%.o: %.c $(LIBFBGFX_H) | $(libfbgfxmtpicobjdir) $(QUIET_CC)$(CC) -DENABLE_MT -fPIC $(ALLCFLAGS) -c $< -o $@ +$(LIBFBGFXMTPIC_M): $(libfbgfxmtpicobjdir)/%.o: %.m $(LIBFBGFX_H) | $(libfbgfxmtpicobjdir) + $(QUIET_OBJC)$(OBJC) -DENABLE_MT -fPIC $(ALLCFLAGS) -c $< -o $@ ################################################################################ diff --git a/src/compiler/ast-node-data.bas b/src/compiler/ast-node-data.bas index 9d3803e57..bd5bfc8c0 100644 --- a/src/compiler/ast-node-data.bas +++ b/src/compiler/ast-node-data.bas @@ -266,9 +266,20 @@ end function private sub hCreateDataDesc( ) static as FBARRAYDIM dTB(0) + dim as integer byalign + + '' Using FIELD = 1, to pack it as done by the rtlib -- except on Darwin, + '' where the linker requires pointer-sized relocations to be aligned to + '' the pointer size. A packed { short, void* } puts the DATA entries' + '' pointers at offset 2 (and every 10 bytes after that), which ld64 + '' rejects ("pointer not aligned ...", fatal on arm64). There the layout + '' must use the natural pointer alignment, matching src/rtlib/fb_data.h. + byalign = 1 + if( fbGetOption( FB_COMPOPT_TARGET ) = FB_COMPTARGET_DARWIN ) then + byalign = env.pointersize + end if - '' Using FIELD = 1, to pack it as done by the rtlib - ast.data.desc = symbStructBegin( NULL, NULL, NULL, "__FB_DATADESC$", NULL, FALSE, 1, FALSE, 0, 0 ) + ast.data.desc = symbStructBegin( NULL, NULL, NULL, "__FB_DATADESC$", NULL, FALSE, byalign, FALSE, 0, 0 ) '' type as short symbAddField( ast.data.desc, "type", 0, dTB(), _ diff --git a/src/compiler/fb.bas b/src/compiler/fb.bas index c050c49c1..23fd1b4a6 100644 --- a/src/compiler/fb.bas +++ b/src/compiler/fb.bas @@ -1768,7 +1768,15 @@ function fbGetBackendValistType _ typedef = FB_CVA_LIST_BUILTIN_ARM case FB_CPUFAMILY_AARCH64 - typedef = FB_CVA_LIST_BUILTIN_AARCH64 + select case env.clopt.target + case FB_COMPTARGET_DARWIN + '' Apple's arm64 ABI uses a plain pointer-like va_list (8 bytes, + '' sizeof(va_list) == sizeof(char*)), not the AAPCS64 + '' __va_list_tag struct that Linux/BSD aarch64 use. + typedef = FB_CVA_LIST_BUILTIN_POINTER + case else + typedef = FB_CVA_LIST_BUILTIN_AARCH64 + end select case FB_CPUFAMILY_PPC typedef = FB_CVA_LIST_BUILTIN_POINTER diff --git a/src/compiler/fbc.bas b/src/compiler/fbc.bas index 61b77df3f..682f82a5a 100644 --- a/src/compiler/fbc.bas +++ b/src/compiler/fbc.bas @@ -258,7 +258,13 @@ private sub hSetOutName( ) select case( fbGetOption( FB_COMPOPT_TARGET ) ) case FB_COMPTARGET_CYGWIN, FB_COMPTARGET_WIN32 fbc.outname += ".dll" - case FB_COMPTARGET_LINUX, FB_COMPTARGET_DARWIN, _ + case FB_COMPTARGET_DARWIN + '' Mach-O shared libraries are .dylib; .so is the loadable + '' bundle extension, and fb_DylibLoad() looks for the .dylib + '' name first on Darwin. + fbc.outname = hStripFilename( fbc.outname ) + _ + "lib" + hStripPath( fbc.outname ) + ".dylib" + case FB_COMPTARGET_LINUX, _ FB_COMPTARGET_FREEBSD, FB_COMPTARGET_OPENBSD, _ FB_COMPTARGET_NETBSD, FB_COMPTARGET_DRAGONFLY, _ FB_COMPTARGET_SOLARIS, FB_COMPTARGET_ANDROID @@ -840,6 +846,8 @@ private function hLinkFiles( ) as integer case FB_CPUFAMILY_ARM '' fixme: this is clearly too specific ldcline += "-arch armv6 " + case FB_CPUFAMILY_AARCH64 + ldcline += "-arch arm64 " end select end select @@ -945,7 +953,13 @@ private function hLinkFiles( ) as integer if( fbGetOption( FB_COMPOPT_OUTTYPE ) = FB_OUTTYPE_DYNAMICLIB ) then dllname = hStripPath( hStripExt( fbc.outname ) ) - ldcline += " -shared -h" + hStripPath( fbc.outname ) + if( fbGetOption( FB_COMPOPT_TARGET ) = FB_COMPTARGET_DARWIN ) then + '' Darwin's linker (ld64) has no -shared/-h; it uses -dynamiclib + '' and records the install name via -install_name. + ldcline += " -dynamiclib -install_name " + QUOTE + dllname + QUOTE + else + ldcline += " -shared -h" + hStripPath( fbc.outname ) + end if '' Turn libfoo into foo, so it can be checked against -l foo below if( left( dllname, 3 ) = "lib" ) then @@ -987,7 +1001,13 @@ private function hLinkFiles( ) as integer if( (fbGetOption( FB_COMPOPT_OUTTYPE ) = FB_OUTTYPE_DYNAMICLIB) or _ fbGetOption( FB_COMPOPT_EXPORT ) ) and _ (fbGetOption( FB_COMPOPT_TARGET ) <> FB_COMPTARGET_SOLARIS) then - ldcline += " --export-dynamic" + '' Darwin links through the clang driver, which wants the option + '' spelled with -Wl, rather than passed through as --export-dynamic + if( fbGetOption( FB_COMPOPT_TARGET ) = FB_COMPTARGET_DARWIN ) then + ldcline += " -Wl,-export_dynamic" + else + ldcline += " --export-dynamic" + end if end if case FB_COMPTARGET_XBOX @@ -1166,7 +1186,10 @@ private function hLinkFiles( ) as integer FB_COMPTARGET_FREEBSD, FB_COMPTARGET_OPENBSD, _ FB_COMPTARGET_NETBSD, FB_COMPTARGET_DRAGONFLY, FB_COMPTARGET_SOLARIS - if( fbGetOption( FB_COMPOPT_OUTTYPE ) = FB_OUTTYPE_EXECUTABLE) then + '' Darwin's linker takes no explicit crt1.o; the C compiler driver + '' supplies the startup object (it lives inside libSystem). + if( (fbGetOption( FB_COMPOPT_OUTTYPE ) = FB_OUTTYPE_EXECUTABLE) and _ + (fbGetOption( FB_COMPOPT_TARGET ) <> FB_COMPTARGET_DARWIN) ) then if( fbGetOption( FB_COMPOPT_PROFILE ) ) then select case as const fbGetOption( FB_COMPOPT_TARGET ) case FB_COMPTARGET_OPENBSD, FB_COMPTARGET_NETBSD @@ -1286,6 +1309,13 @@ private function hLinkFiles( ) as integer wend end scope + '' The native Cocoa gfx driver needs these system frameworks. They ship + '' with every macOS install, and are only added for programs that use gfx. + if( (fbGetOption( FB_COMPOPT_TARGET ) = FB_COMPTARGET_DARWIN) and _ + fbGetOption( FB_COMPOPT_FBGFX ) ) then + ldcline += " -framework Cocoa -framework QuartzCore -framework CoreGraphics" + end if + if (fbGetOption( FB_COMPOPT_TARGET ) <> FB_COMPTARGET_DARWIN) then if( fbGetOption( FB_COMPOPT_TARGET ) <> FB_COMPTARGET_JS ) then '' End of lib group @@ -1322,18 +1352,17 @@ private function hLinkFiles( ) as integer end select - if( fbGetOption( FB_COMPOPT_TARGET ) = FB_COMPTARGET_DARWIN ) then - ldcline += " -macosx_version_min 10.4" - end if + '' Note: on Darwin the deployment target (-mmacosx-version-min) is supplied + '' by the C compiler driver used for linking, not hard-coded here. '' This is required for 64-bit modules on *nix-y platforms '' for the unwind tables to have any effect '' Windows doesn't need this option + '' (ld64 rejects --eh-frame-hdr; Darwin is handled by clang instead) select case as const fbGetOption( FB_COMPOPT_TARGET ) case FB_COMPTARGET_LINUX, FB_COMPTARGET_FREEBSD, _ FB_COMPTARGET_OPENBSD, FB_COMPTARGET_NETBSD, _ - FB_COMPTARGET_DRAGONFLY, FB_COMPTARGET_SOLARIS, _ - FB_COMPTARGET_DARWIN + FB_COMPTARGET_DRAGONFLY, FB_COMPTARGET_SOLARIS dim as long outtype = fbGetOption( FB_COMPOPT_OUTTYPE ) if outtype = FB_OUTTYPE_EXECUTABLE OrElse outtype = FB_OUTTYPE_DYNAMICLIB Then dim as long cpufamily = fbGetCpuFamily( ) @@ -1408,6 +1437,10 @@ private function hLinkFiles( ) as integer var ld = FBCTOOL_LD if( fbGetOption( FB_COMPOPT_TARGET ) = FB_COMPTARGET_JS ) then ld = FBCTOOL_EMLD + elseif( fbGetOption( FB_COMPOPT_TARGET ) = FB_COMPTARGET_DARWIN ) then + '' ld64 can't be driven like GNU ld (different crt handling, and it + '' rejects several GNU options), so use the C compiler driver to link. + ld = FBCTOOL_CLANG end if if( fbcRunBin( "linking", ld, ldcline ) = FALSE ) then @@ -4277,11 +4310,9 @@ private sub hAddDefaultLibs( ) fbcAddDefLibPath( "/usr/X11R6/lib" ) #endif - #if defined(__FB_DARWIN__) and defined(ENABLE_XQUARTZ) - fbcAddDefLibPAth( "/opt/X11/lib" ) - #endif - - #if (not defined(__FB_DARWIN__)) or defined(ENABLE_XQUARTZ) + '' Darwin uses the native Cocoa driver and never links X11, so the + '' X11 libraries below are only added for the other unix targets. + #if not defined(__FB_DARWIN__) fbcAddDefLib( "X11" ) fbcAddDefLib( "Xext" ) fbcAddDefLib( "Xpm" ) @@ -4308,9 +4339,9 @@ private sub hAddDefaultLibs( ) end if case FB_COMPTARGET_DARWIN - fbcAddDefLib( "gcc" ) - fbcAddDefLib( "System" ) - fbcAddDefLib( "pthread" ) + '' There is no libgcc on macOS unless a GCC toolchain is installed; + '' the clang driver pulls in libSystem (and thus libc/libm/pthread) + '' by itself. libncurses is still needed by the FB runtime. fbcAddDefLib( "ncurses" ) case FB_COMPTARGET_DOS diff --git a/src/compiler/rtl-error.bas b/src/compiler/rtl-error.bas index 17096f555..8931daf95 100644 --- a/src/compiler/rtl-error.bas +++ b/src/compiler/rtl-error.bas @@ -293,7 +293,16 @@ function rtlErrorCheck( byval expr as ASTNODE ptr ) as ASTNODE ptr t = astNewLINK( t, astNewBOP( AST_OP_EQ, expr, astNewCONSTi( 0 ), nxtlabel, AST_OPOPT_NONE ), AST_LINK_RETURN_NONE ) '' fb_ErrorThrow() - t = astNewLINK( t, astNewBRANCH( AST_OP_JUMPPTR, NULL, hErrorThrow( reslabel, nxtlabel ) ), AST_LINK_RETURN_NONE ) + '' Only -ex (-exx) enables RESUME support, and only then is a resume + '' label handed to the throw. Without one the throw can never return + '' a usable jump target, so a plain call is equivalent -- and it keeps + '' the generated C acceptable to clang, which rejects `goto *ptr` in + '' functions that take no label address. + if( env.clopt.resumeerr ) then + t = astNewLINK( t, astNewBRANCH( AST_OP_JUMPPTR, NULL, hErrorThrow( reslabel, nxtlabel ) ), AST_LINK_RETURN_NONE ) + else + t = astNewLINK( t, hErrorThrow( reslabel, nxtlabel ), AST_LINK_RETURN_NONE ) + end if '' end if t = astNewLINK( t, astNewLABEL( nxtlabel ), AST_LINK_RETURN_NONE ) @@ -361,7 +370,13 @@ sub rtlErrorThrow _ end if '' dst - astAdd( astNewBRANCH( AST_OP_JUMPPTR, NULL, proc ) ) + '' As in rtlErrorCheck(): an indirect jump is only meaningful when RESUME + '' support (-ex) is enabled and a resume label was passed along. + if( env.clopt.resumeerr ) then + astAdd( astNewBRANCH( AST_OP_JUMPPTR, NULL, proc ) ) + else + astAdd( proc ) + end if astAdd( astNewLABEL( nxtlabel ) ) end sub diff --git a/src/gfxlib2/darwin/fb_gfx_cocoa.h b/src/gfxlib2/darwin/fb_gfx_cocoa.h new file mode 100644 index 000000000..42527b612 --- /dev/null +++ b/src/gfxlib2/darwin/fb_gfx_cocoa.h @@ -0,0 +1,20 @@ +#ifndef __FB_GFX_COCOA_H__ +#define __FB_GFX_COCOA_H__ + +#include +#include "../fb_gfx.h" + +extern const GFXDRIVER fb_gfxDriverCocoa; + +extern void fb_hCocoaLock(void); +extern void fb_hCocoaUnlock(void); +extern void fb_hCocoaWaitVSync(void); +extern void fb_hCocoaSetPalette(int index, int r, int g, int b); +extern int fb_hCocoaGetMouse(int *x, int *y, int *z, int *buttons, int *clip); +extern void fb_hCocoaSetMouse(int x, int y, int cursor, int clip); +extern void fb_hCocoaSetWindowTitle(char *title); +extern int fb_hCocoaSetWindowPos(int x, int y); +extern int *fb_hCocoaFetchModes(int depth, int *size); +extern int fb_hCocoaScreenInfo(ssize_t *width, ssize_t *height, ssize_t *depth, ssize_t *refresh); + +#endif diff --git a/src/gfxlib2/darwin/gfx_driver_cocoa.m b/src/gfxlib2/darwin/gfx_driver_cocoa.m new file mode 100644 index 000000000..5dd838c6b --- /dev/null +++ b/src/gfxlib2/darwin/gfx_driver_cocoa.m @@ -0,0 +1,640 @@ +/* Native Cocoa 2D graphics driver for gfxlib2 (macOS) + + Presents FreeBASIC's software framebuffer in a real NSWindow through + CoreGraphics, so that a plain ScreenRes works without XQuartz and without + OpenGL (which is deprecated on macOS). + + The window/view/event handling is modelled on the Cocoa/OpenGL driver from + PR #448 by Markos-Th09; the software-framebuffer present path is new. + + How the pieces fit together in gfxlib2: + + - the core renders into __fb_gfx->page[] / framebuffer and marks changed + scanlines in __fb_gfx->dirty[]; + - a refresh thread converts the framebuffer to 32-bit BGRA with the + gfxlib2 blitter (fb_hGetBlitter) and hands the image to the layer; + - AppKit itself is only touched on the main thread, from the driver + hooks the core calls (flip/unlock/poll_events/wait_vsync). + + There is no NSApplication run loop here: an FB program owns the process + main thread, so events are pumped on demand instead. +*/ + +#include +#include +#include +#include +#include +#include +#include + +#include "../fb_gfx.h" +#include "fb_gfx_cocoa.h" +#include "../../rtlib/darwin/fb_private_scancodes_cocoa.h" + +#if defined(HOST_DARWIN) + +#import +#import +#import + +/* ------------------------------------------------------------ driver state */ + +typedef struct { + NSWindow *window; + NSView *view; + CGContextRef ctx; /* bitmap context over buf */ + unsigned char *buf; /* w * h * 4, BGRA */ + BLITTER *blitter; /* framebuffer -> BGRA converter */ + pthread_mutex_t mutex; + pthread_t thread; + volatile int running; + volatile int present_pending; + int w, h; + int mouse_x, mouse_y, mouse_z; + int mouse_buttons; + int mouse_visible; + int has_focus; + int cursor_shown; +} COCOA_CTX; + +static COCOA_CTX cocoa; + +static int cocoa_ready = 0; +static int cocoa_app_ready = 0; + +/* ------------------------------------------------------------------ present */ + +/* Convert the FB framebuffer into our BGRA buffer. Caller holds the mutex. */ +static void cocoa_convert(void) +{ + if (!__fb_gfx || !cocoa.buf || !cocoa.blitter) + return; + + cocoa.blitter(cocoa.buf, cocoa.w * 4); + + if (__fb_gfx->dirty) + fb_hMemSet(__fb_gfx->dirty, 0, __fb_gfx->h); +} + +/* Publish the buffer to the window. Main thread only (AppKit). */ +static void cocoa_present(void) +{ + CGImageRef image; + + if (!cocoa_ready || !__fb_gfx) + return; + + pthread_mutex_lock(&cocoa.mutex); + cocoa_convert(); + image = CGBitmapContextCreateImage(cocoa.ctx); + pthread_mutex_unlock(&cocoa.mutex); + + if (image == NULL) + return; + + /* Layer contents are set on the main thread here; the refresh thread + does its own best-effort publish for programs that never reach a + driver hook. */ + cocoa.view.layer.contents = (__bridge id)image; + CGImageRelease(image); +} + +/* Best-effort publish from the refresh thread: Core Animation is thread-safe + for layer property mutation, and an explicit flush pushes the change to the + render server without relying on a main-thread run loop. */ +static void cocoa_present_background(CGImageRef image) +{ + cocoa.view.layer.contents = (__bridge id)image; + [CATransaction flush]; +} + +/* --------------------------------------------------------------- event pump */ + +/* Optional diagnostics: set FBCOCOA_DEBUG= to log the raw AppKit events + the driver receives, which is the quickest way to tell an input delivery + problem from a translation problem. */ +static FILE *cocoa_debug = NULL; + +static void cocoa_debug_init(void) +{ + char *p = getenv("FBCOCOA_DEBUG"); + + if (p && *p) + cocoa_debug = fopen(p, "a"); +} + +static void cocoa_debug_log(const char *fmt, ...) +{ + va_list ap; + + if (cocoa_debug == NULL) + return; + + va_start(ap, fmt); + vfprintf(cocoa_debug, fmt, ap); + va_end(ap); + fflush(cocoa_debug); +} + +static void cocoa_post_key(int scancode, int ascii, int type) +{ + EVENT e; + + if (!__fb_gfx) + return; + + fb_hMemSet(&e, 0, sizeof(EVENT)); + e.type = type; + e.scancode = scancode; + e.ascii = ascii; + fb_hPostEvent(&e); +} + +static void cocoa_post_mouse(int type, int x, int y, int button) +{ + EVENT e; + + if (!__fb_gfx) + return; + + fb_hMemSet(&e, 0, sizeof(EVENT)); + e.type = type; + e.x = x; + e.y = y; + e.button = button; + fb_hPostEvent(&e); +} + +/* Map an NSEvent to FB's keycode convention: plain characters as-is, extended + keys (arrows, function keys, ...) through the scancode table, mirroring what + the X11 driver does with XLookupString/translate_key. */ +static int cocoa_translate_key(NSEvent *event, int scancode) +{ + NSString *chars = [event characters]; + unichar c; + + if ([chars length] >= 1) { + c = [chars characterAtIndex:0]; + /* Remap ASCII DEL to FB's extended DELETE keycode, as the other + drivers do */ + if (c == 0x7F) + return KEY_DEL; + if (c < 0x80) + return (int)c; + } + + return fb_hScancodeToExtendedKey(scancode); +} + +static void cocoa_handle_event(NSEvent *event) +{ + NSPoint p; + int scancode; + + if (!__fb_gfx) + return; + + cocoa_debug_log("event type=%d\n", (int)[event type]); + + switch ([event type]) { + case NSEventTypeKeyDown: + case NSEventTypeKeyUp: + scancode = fb_cocoakeycode_to_scancode[[event keyCode] & 0xFF]; + cocoa_debug_log(" key keyCode=%d scancode=%d down=%d\n", + (int)[event keyCode], scancode, + [event type] == NSEventTypeKeyDown); + if (scancode == 0) + break; + if ([event type] == NSEventTypeKeyDown) { + int key = cocoa_translate_key(event, scancode); + + cocoa_debug_log(" translated key=%d\n", key); + __fb_gfx->key[scancode] = TRUE; + /* InKey() reads the key buffer, GetKey()/the event queue read + the posted event, so feed both like the other drivers do */ + if (key) + fb_hPostKey(key); + cocoa_post_key(scancode, ((key < 0) || (key > 0xFF)) ? 0 : key, + [event isARepeat] ? EVENT_KEY_REPEAT : EVENT_KEY_PRESS); + } else { + __fb_gfx->key[scancode] = FALSE; + cocoa_post_key(scancode, 0, EVENT_KEY_RELEASE); + } + break; + + case NSEventTypeFlagsChanged: + /* Modifier keys arrive here rather than as key down/up */ + scancode = fb_cocoakeycode_to_scancode[[event keyCode] & 0xFF]; + if (scancode == 0) + break; + if (__fb_gfx->key[scancode]) { + __fb_gfx->key[scancode] = FALSE; + cocoa_post_key(scancode, 0, EVENT_KEY_RELEASE); + } else { + __fb_gfx->key[scancode] = TRUE; + cocoa_post_key(scancode, 0, EVENT_KEY_PRESS); + } + break; + + case NSEventTypeMouseMoved: + /* Only report motion inside the window; this driver sees moves for + the whole app, unlike the X11 one which only gets in-window + motion events. */ + p = [event locationInWindow]; + if ((p.x < 0) || (p.x >= cocoa.w) || (p.y < 0) || (p.y >= cocoa.h)) + break; + cocoa.mouse_x = (int)p.x; + cocoa.mouse_y = cocoa.h - (int)p.y - 1; + cocoa_post_mouse(EVENT_MOUSE_MOVE, cocoa.mouse_x, cocoa.mouse_y, 0); + break; + + case NSEventTypeLeftMouseDragged: + case NSEventTypeRightMouseDragged: + case NSEventTypeOtherMouseDragged: + /* Dragging may legitimately leave the window */ + p = [event locationInWindow]; + cocoa.mouse_x = (int)p.x; + cocoa.mouse_y = cocoa.h - (int)p.y - 1; + cocoa_post_mouse(EVENT_MOUSE_MOVE, cocoa.mouse_x, cocoa.mouse_y, 0); + break; + + case NSEventTypeLeftMouseDown: + case NSEventTypeRightMouseDown: + case NSEventTypeOtherMouseDown: + cocoa.mouse_buttons |= 1 << [event buttonNumber]; + cocoa_post_mouse(EVENT_MOUSE_BUTTON_PRESS, cocoa.mouse_x, cocoa.mouse_y, + 1 << [event buttonNumber]); + break; + + case NSEventTypeLeftMouseUp: + case NSEventTypeRightMouseUp: + case NSEventTypeOtherMouseUp: + cocoa.mouse_buttons &= ~(1 << [event buttonNumber]); + cocoa_post_mouse(EVENT_MOUSE_BUTTON_RELEASE, cocoa.mouse_x, cocoa.mouse_y, + 1 << [event buttonNumber]); + break; + + case NSEventTypeScrollWheel: + cocoa.mouse_z += (int)[event scrollingDeltaY]; + cocoa_post_mouse(EVENT_MOUSE_WHEEL, cocoa.mouse_x, cocoa.mouse_y, 0); + break; + + default: + break; + } +} + +/* Pump pending AppKit events. Main thread only. */ +static void cocoa_pump_events(void) +{ + NSEvent *event; + static int last_active = -1, last_key = -1; + int active, keywin; + + if (!cocoa_ready) + return; + + active = [NSApp isActive] ? 1 : 0; + keywin = [cocoa.window isKeyWindow] ? 1 : 0; + if ((active != last_active) || (keywin != last_key)) { + last_active = active; + last_key = keywin; + cocoa_debug_log("state isActive=%d isKeyWindow=%d\n", active, keywin); + } + + while ((event = [NSApp nextEventMatchingMask:NSEventMaskAny + untilDate:[NSDate distantPast] + inMode:NSDefaultRunLoopMode + dequeue:YES]) != nil) { + cocoa_handle_event(event); + [NSApp sendEvent:event]; + } + + /* Focus follows the window, which is what InKey/GetMouse care about */ + cocoa.has_focus = keywin ? TRUE : FALSE; +} + +/* --------------------------------------------------------- refresh thread */ + +/* Converts the framebuffer and publishes it; no AppKit window management or + event handling happens here. */ +static void *cocoa_thread(void *arg) +{ + (void)arg; + + while (cocoa.running) { + CGImageRef image; + + pthread_mutex_lock(&cocoa.mutex); + cocoa_convert(); + image = CGBitmapContextCreateImage(cocoa.ctx); + pthread_mutex_unlock(&cocoa.mutex); + + if (image) { + cocoa_present_background(image); + CGImageRelease(image); + } + + usleep(1000 * 1000 / 60); + } + + return NULL; +} + +/* --------------------------------------------------------------- callbacks */ + +void fb_hCocoaLock(void) +{ + pthread_mutex_lock(&cocoa.mutex); +} + +void fb_hCocoaUnlock(void) +{ + /* Called whenever the program unlocks the screen; a natural point to + push the current frame out on the main thread. */ + if (cocoa_ready) { + CGImageRef image; + + cocoa_convert(); + image = CGBitmapContextCreateImage(cocoa.ctx); + if (image) { + cocoa.view.layer.contents = (__bridge id)image; + CGImageRelease(image); + } + cocoa_pump_events(); + } + + pthread_mutex_unlock(&cocoa.mutex); +} + +void fb_hCocoaWaitVSync(void) +{ + usleep(1000000 / ((__fb_gfx && __fb_gfx->refresh_rate > 0) ? __fb_gfx->refresh_rate : 60)); +} + +void fb_hCocoaSetPalette(int index, int r, int g, int b) +{ + /* The blitter reads __fb_gfx->palette directly, so nothing to do here. */ + (void)index; (void)r; (void)g; (void)b; +} + +int fb_hCocoaGetMouse(int *x, int *y, int *z, int *buttons, int *clip) +{ + /* Always report the last known state. Gating this on the window being + key (as the X11 driver does with its focus tracking) makes GetMouse() + return -1 whenever another application is frontmost, which breaks + programs that poll the mouse while unattended. */ + *x = cocoa.mouse_x; + *y = cocoa.mouse_y; + *z = cocoa.mouse_z; + *buttons = cocoa.mouse_buttons; + *clip = 0; + return 0; +} + +void fb_hCocoaSetMouse(int x, int y, int cursor, int clip) +{ + (void)clip; + + cocoa.mouse_x = x; + cocoa.mouse_y = y; + + if (cursor != 0) { + if (!cocoa.cursor_shown) { + [NSCursor unhide]; + cocoa.cursor_shown = 1; + } + } else { + if (cocoa.cursor_shown) { + [NSCursor hide]; + cocoa.cursor_shown = 0; + } + } +} + +void fb_hCocoaSetWindowTitle(char *title) +{ + if (cocoa_ready && title) + cocoa.window.title = [NSString stringWithUTF8String:title]; +} + +int fb_hCocoaSetWindowPos(int x, int y) +{ + if (!cocoa_ready) + return -1; + + /* FB coordinates are top-left based; Cocoa's are bottom-left based */ + NSRect screen = [[NSScreen mainScreen] frame]; + [cocoa.window setFrameTopLeftPoint:NSMakePoint(x, screen.size.height - y)]; + return 0; +} + +int fb_hCocoaScreenInfo(ssize_t *width, ssize_t *height, ssize_t *depth, ssize_t *refresh) +{ + NSRect frame = [[NSScreen mainScreen] frame]; + + *width = (ssize_t)frame.size.width; + *height = (ssize_t)frame.size.height; + *depth = 32; + *refresh = 60; + return 0; +} + +int *fb_hCocoaFetchModes(int depth, int *size) +{ + /* No fullscreen modes: the driver is windowed only */ + (void)depth; + if (size) + *size = 0; + return NULL; +} + +/* ---------------------------------------------------------- driver entry pts */ + +static void cocoa_poll_events_hook(void) +{ + /* Called by the core (also from ScreenControl POLL_EVENTS) */ + if (!cocoa_ready) + return; + + cocoa_pump_events(); +} + +static void cocoa_flip_hook(void) +{ + if (!cocoa_ready) + return; + + cocoa_present(); + cocoa_pump_events(); +} + +static void cocoa_update_hook(void) +{ + if (!cocoa_ready) + return; + + cocoa_present(); +} + +static int driver_init(char *title, int w, int h, int depth, int refresh_rate, int flags) +{ + @autoreleasepool { + NSRect rect; + NSInteger style; + CGColorSpaceRef cs; + int stride; + + /* OpenGL screens are handled by the X11/GLX driver (via XQuartz) */ + if (flags & DRIVER_OPENGL) + return -1; + + if (w <= 0 || h <= 0) + return -1; + + fb_hMemSet(&cocoa, 0, sizeof(cocoa)); + cocoa.w = w; + cocoa.h = h; + cocoa.mouse_visible = 1; + cocoa.has_focus = 1; + pthread_mutex_init(&cocoa.mutex, NULL); + cocoa_debug_init(); + cocoa_debug_log("--- driver_init w=%d h=%d depth=%d flags=%d\n", w, h, depth, flags); + + if (!cocoa_app_ready) { + [NSApplication sharedApplication]; + [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular]; + cocoa_app_ready = 1; + } + + style = NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | + NSWindowStyleMaskMiniaturizable; + rect = NSMakeRect(0, 0, w, h); + cocoa.window = [[NSWindow alloc] initWithContentRect:rect + styleMask:style + backing:NSBackingStoreBuffered + defer:NO]; + if (cocoa.window == nil) + return -1; + + cocoa.window.title = [NSString stringWithUTF8String:(title ? title : "FreeBASIC")]; + [cocoa.window setReleasedWhenClosed:NO]; + [cocoa.window center]; + + cocoa.view = [[NSView alloc] initWithFrame:rect]; + cocoa.view.wantsLayer = YES; + /* 1:1 pixels: the image is w x h and so is the layer in points, so + a 2x backing store upscales it with nearest-neighbour filtering + rather than smoothing it. */ + cocoa.view.layer.contentsGravity = kCAGravityResize; + cocoa.view.layer.magnificationFilter = kCAFilterNearest; + cocoa.view.layer.contentsScale = 1.0; + cocoa.window.contentView = cocoa.view; + + /* Closing the window should end the program, as ALT+F4 does + elsewhere: post the close event and the quit key. */ + [[NSNotificationCenter defaultCenter] + addObserverForName:NSWindowWillCloseNotification + object:cocoa.window + queue:nil + usingBlock:^(NSNotification *note) { + EVENT e; + (void)note; + if (!__fb_gfx) + return; + fb_hMemSet(&e, 0, sizeof(EVENT)); + e.type = EVENT_WINDOW_CLOSE; + fb_hPostEvent(&e); + fb_hPostKey(KEY_QUIT); + }]; + + [cocoa.window makeKeyAndOrderFront:nil]; + [NSApp activateIgnoringOtherApps:YES]; + + /* The gfxlib2 blitter for a 32-bit device depth writes R,G,B,X + bytes: component order R,G,B with a trailing skipped byte, i.e. + kCGImageAlphaNoneSkipLast with big-endian 32-bit words. */ + stride = w * 4; + cocoa.buf = (unsigned char *)calloc(1, (size_t)stride * h); + if (cocoa.buf == NULL) + return -1; + + cs = CGColorSpaceCreateDeviceRGB(); + cocoa.ctx = CGBitmapContextCreate(cocoa.buf, w, h, 8, stride, cs, + kCGImageAlphaNoneSkipLast | + kCGBitmapByteOrder32Big); + CGColorSpaceRelease(cs); + if (cocoa.ctx == NULL) + return -1; + + cocoa.blitter = fb_hGetBlitter(32, TRUE); + if (cocoa.blitter == NULL) + return -1; + + cocoa_ready = 1; + cocoa.running = 1; + if (pthread_create(&cocoa.thread, NULL, cocoa_thread, NULL) != 0) { + cocoa.running = 0; + cocoa_ready = 0; + return -1; + } + + if (refresh_rate > 0 && __fb_gfx) + __fb_gfx->refresh_rate = refresh_rate; + + return 0; + } +} + +static void driver_exit(void) +{ + if (!cocoa_ready) + return; + + cocoa.running = 0; + pthread_join(cocoa.thread, NULL); + cocoa_ready = 0; + + @autoreleasepool { + if (cocoa.ctx) { + CGContextRelease(cocoa.ctx); + cocoa.ctx = NULL; + } + if (cocoa.buf) { + free(cocoa.buf); + cocoa.buf = NULL; + } + if (cocoa.window) { + [cocoa.window orderOut:nil]; + cocoa.window = nil; + } + cocoa.view = nil; + } + + pthread_mutex_destroy(&cocoa.mutex); +} + +/* GFXDRIVER */ +const GFXDRIVER fb_gfxDriverCocoa = +{ + "Cocoa", /* char *name; */ + driver_init, /* int (*init)(...) */ + driver_exit, /* void (*exit)(void); */ + fb_hCocoaLock, /* void (*lock)(void); */ + fb_hCocoaUnlock, /* void (*unlock)(void); */ + fb_hCocoaSetPalette, /* void (*set_palette)(...); */ + fb_hCocoaWaitVSync, /* void (*wait_vsync)(void); */ + fb_hCocoaGetMouse, /* int (*get_mouse)(...); */ + fb_hCocoaSetMouse, /* void (*set_mouse)(...); */ + fb_hCocoaSetWindowTitle, /* void (*set_window_title)(char *); */ + fb_hCocoaSetWindowPos, /* int (*set_window_pos)(int, int); */ + fb_hCocoaFetchModes, /* int *(*fetch_modes)(int, int *); */ + cocoa_flip_hook, /* void (*flip)(void); */ + cocoa_poll_events_hook, /* void (*poll_events)(void); */ + cocoa_update_hook /* void (*update)(void); */ +}; + +#else +typedef int fb_cocoa_driver_disabled_t; /* avoid an empty translation unit */ +#endif diff --git a/src/gfxlib2/unix/gfx_unix.c b/src/gfxlib2/unix/gfx_unix.c index c7baa85b6..3647026d0 100644 --- a/src/gfxlib2/unix/gfx_unix.c +++ b/src/gfxlib2/unix/gfx_unix.c @@ -1,14 +1,24 @@ #include "../fb_gfx.h" + +#ifndef DISABLE_X11 #include "fb_gfx_x11.h" +#endif #ifdef HOST_LINUX #include "../linux/fb_gfx_linux.h" #endif +#if defined HOST_DARWIN && !defined DISABLE_COCOA +#include "../darwin/fb_gfx_cocoa.h" +#endif + #if defined HOST_FREEBSD || defined HOST_OPENBSD || defined HOST_LINUX || defined HOST_DARWIN || defined HOST_SOLARIS || defined HOST_DRAGONFLY || defined HOST_NETBSD const GFXDRIVER *__fb_gfx_drivers_list[] = { + /* X11/GLX first when it was built in (XQuartz), then the native Cocoa + driver: if no X server is reachable, Cocoa takes over, and in a build + without XQuartz it is the only driver. */ #ifndef DISABLE_X11 &fb_gfxDriverX11, #ifndef DISABLE_OPENGL @@ -16,6 +26,10 @@ const GFXDRIVER *__fb_gfx_drivers_list[] = { #endif #endif +#if defined HOST_DARWIN && !defined DISABLE_COCOA + &fb_gfxDriverCocoa, +#endif + #if defined HOST_LINUX && !defined DISABLE_FBDEV &fb_gfxDriverFBDev, #endif @@ -25,6 +39,9 @@ const GFXDRIVER *__fb_gfx_drivers_list[] = { void fb_hScreenInfo(ssize_t *width, ssize_t *height, ssize_t *depth, ssize_t *refresh) { +#if defined HOST_DARWIN && !defined DISABLE_COCOA + if (fb_hCocoaScreenInfo(width, height, depth, refresh)) +#endif #ifndef DISABLE_X11 if (fb_hX11ScreenInfo(width, height, depth, refresh)) #endif diff --git a/src/rtlib/darwin/fb_private_scancodes_cocoa.h b/src/rtlib/darwin/fb_private_scancodes_cocoa.h new file mode 100644 index 000000000..8fb2d2474 --- /dev/null +++ b/src/rtlib/darwin/fb_private_scancodes_cocoa.h @@ -0,0 +1,106 @@ +#include "../fb.h" + +static const int fb_cocoakeycode_to_scancode[256] = { + [0x35] = SC_ESCAPE, + [0x7A] = SC_F1, + [0x78] = SC_F2, + [0x63] = SC_F3, + [0x76] = SC_F4, + [0x60] = SC_F5, + [0x61] = SC_F6, + [0x62] = SC_F7, + [0x64] = SC_F8, + [0x65] = SC_F9, + [0x6D] = SC_F10, + [0x67] = SC_F11, + [0x6F] = SC_F12, + [0x32] = SC_TILDE, + [0x12] = SC_1, + [0x13] = SC_2, + [0x14] = SC_3, + [0x15] = SC_4, + [0x17] = SC_5, + [0x16] = SC_6, + [0x1A] = SC_7, + [0x1C] = SC_8, + [0x19] = SC_9, + [0x1D] = SC_0, + [0x1B] = SC_MINUS, + [0x18] = SC_EQUALS, + [0x2A] = SC_BACKSLASH, + [0x33] = SC_BACKSPACE, + [0x30] = SC_TAB, + [0x0C] = SC_Q, + [0x0D] = SC_W, + [0x0E] = SC_E, + [0x0F] = SC_R, + [0x11] = SC_T, + [0x10] = SC_Y, + [0x20] = SC_U, + [0x22] = SC_I, + [0x1F] = SC_O, + [0x23] = SC_P, + [0x21] = SC_LEFTBRACKET, + [0x1E] = SC_RIGHTBRACKET, + [0x39] = SC_CAPSLOCK, + [0x00] = SC_A, + [0x01] = SC_S, + [0x02] = SC_D, + [0x03] = SC_F, + [0x05] = SC_G, + [0x04] = SC_H, + [0x26] = SC_J, + [0x28] = SC_K, + [0x25] = SC_L, + [0x29] = SC_SEMICOLON, + [0x27] = SC_QUOTE, + [0x38] = SC_LSHIFT, + [0x06] = SC_Z, + [0x07] = SC_X, + [0x08] = SC_C, + [0x09] = SC_V, + [0x0B] = SC_B, + [0x2D] = SC_N, + [0x2E] = SC_M, + [0x2B] = SC_COMMA, + [0x2F] = SC_PERIOD, + [0x2C] = SC_SLASH, + [0x3C] = SC_RSHIFT, + [0x3B] = SC_CONTROL, + [0x37] = SC_LWIN, + [0x3A] = SC_ALT, + [0x31] = SC_SPACE, + [0x3D] = SC_ALT, + [0x36] = SC_RWIN, + [0x6E] = SC_MENU, + [0x3E] = SC_CONTROL, + [0x73] = SC_HOME, + [0x74] = SC_PAGEUP, + [0x75] = SC_DELETE, + [0x77] = SC_END, + [0x79] = SC_PAGEDOWN, + [0x7E] = SC_UP, + [0x7B] = SC_LEFT, + [0x7D] = SC_DOWN, + [0x7C] = SC_RIGHT, + // Keypad + [0x4B] = SC_SLASH, + [0x43] = SC_MULTIPLY, + [0x4E] = SC_MINUS, + [0x45] = SC_PLUS, + [0x47] = SC_CLEAR, + [0x24] = SC_ENTER, + [0x41] = SC_PERIOD, + [0x4C] = SC_ENTER, + [0x51] = SC_EQUALS, + [0x52] = SC_0, + [0x53] = SC_1, + [0x54] = SC_2, + [0x55] = SC_3, + [0x56] = SC_4, + [0x57] = SC_5, + [0x58] = SC_6, + [0x59] = SC_7, + [0x5B] = SC_8, + [0x5C] = SC_9 +}; diff --git a/src/rtlib/fb_data.h b/src/rtlib/fb_data.h index 2b976807d..22de295a6 100644 --- a/src/rtlib/fb_data.h +++ b/src/rtlib/fb_data.h @@ -1,3 +1,14 @@ +/* Mach-O (Darwin) requires pointer-sized relocations to be aligned; the packed + layout would place the embedded pointers at 2-byte offsets, which ld64 + rejects ("pointer not aligned ...", a hard error on arm64). The compiler + emits the matching naturally-aligned layout for Darwin targets, see + hCreateDataDesc() in src/compiler/ast-node-data.bas. */ +#if defined(__APPLE__) + #define FB_DATADESC_PACKED +#else + #define FB_DATADESC_PACKED FBPACKED +#endif + struct _FB_DATADESC { short len; union { @@ -6,7 +17,7 @@ struct _FB_DATADESC { void *ofs; struct _FB_DATADESC *next; }; -} FBPACKED; +} FB_DATADESC_PACKED; typedef struct _FB_DATADESC FB_DATADESC; diff --git a/src/rtlib/profile_cycles.c b/src/rtlib/profile_cycles.c index c6e2a7327..00f4718fc 100644 --- a/src/rtlib/profile_cycles.c +++ b/src/rtlib/profile_cycles.c @@ -99,10 +99,20 @@ typedef struct _FB_PROFILER_CYCLES */ #if !defined(HOST_DOS) +/* Mach-O section names are limited to 16 characters and, unlike ELF, the + linker does not synthesise __start_/__stop_ boundary symbols, so the + profiler's section scan cannot work the ELF way there; keep the record as a + plain used object instead. */ +#if defined(HOST_DARWIN) + #define FB_PROFILE_SECTION_ATTR __attribute__((used)) +#else + #define FB_PROFILE_SECTION_ATTR __attribute__((section("fb_profilecycledata"), used)) +#endif + /* make sure there is at least one record in the profile data section */ static FB_PROFILE_RECORD_VERSION __attribute__ ((aligned (16))) prof_data_version -__attribute__((section("fb_profilecycledata"), used)) = +FB_PROFILE_SECTION_ATTR = { sizeof( FB_PROFILE_RECORD_VERSION ), FB_PROFILE_RECORD_VERSION_ID, @@ -301,8 +311,15 @@ static void hProfilerWriteReport( FB_PROFILER_CYCLES *prof ) fprintf( f, "Total program execution time: %5.4g seconds\n", fb_Timer() - prof->start_time ); } +#if defined(HOST_DARWIN) + /* No __start_/__stop_ section symbols on Mach-O; fall back to the single + version record so the report is simply empty. */ + data = (unsigned char *)&prof_data_version; + length = sizeof( prof_data_version ); +#else data = (unsigned char *)&__start_fb_profilecycledata[0]; length = (ssize_t)&__stop_fb_profilecycledata - (ssize_t)&__start_fb_profilecycledata[0]; +#endif count = hProfilerCountProcs( data, length ); if( count ) { diff --git a/src/rtlib/unix/fb_private_scancodes_x11.h b/src/rtlib/unix/fb_private_scancodes_x11.h index e76da67d4..bee31cd27 100644 --- a/src/rtlib/unix/fb_private_scancodes_x11.h +++ b/src/rtlib/unix/fb_private_scancodes_x11.h @@ -7,7 +7,16 @@ typedef Display *(*XOPENDISPLAY)(char *); typedef int (*XCLOSEDISPLAY)(Display *); typedef void (*XQUERYKEYMAP)(Display *, unsigned char *); typedef int (*XDISPLAYKEYCODES)(Display *, int *, int *); +/* XGetKeyboardMapping()'s first_keycode parameter is declared as KeyCode + (unsigned char) or as unsigned int depending on NeedWidePrototypes, which + Xfuncproto.h defaults to 1 unless NARROWPROTO is defined. Mirror Xlib's own + declaration, otherwise the function pointers do not match and clang (unlike + GCC, which merely warns) rejects the call. */ +#if defined(NeedWidePrototypes) && NeedWidePrototypes +typedef KeySym* (*XGETKEYBOARDMAPPING)(Display *, unsigned int, int, int *); +#else typedef KeySym* (*XGETKEYBOARDMAPPING)(Display *, KeyCode, int, int *); +#endif typedef int (*XFREE)(void *); extern unsigned char fb_x11keycode_to_scancode[256];