diff --git a/ChangeLog b/ChangeLog index e11a158c..c065bd3c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,16 @@ +2026-10-23 Frederik Seiffert + * Source/CFRunLoop.c (CFRunLoopAddCommonMode, CFRunLoopCopyAllModes, + CFRunLoopCopyCurrentMode): Dispatch to the corresponding NSRunLoop + methods when passed an Objective-C object. Move the implementations + into private functions for use by the bridge. + * Source/NSCFRunLoop.m: New bridge class for CFRunLoop implementing + -addCommonMode:, -allModes and -currentMode, so the functions of the + same names in GNUstep Base also work on CFRunLoopRefs. + * Source/NSCFType.m: Bridge CFRunLoop. Fix count of classes required + before bridging is initialized. + * Source/GNUmakefile.in: Add NSCFRunLoop.m. + * Tests/CFRunLoop/bridge.m: New test. + 2026-07-11 Todd White * Source/CFPropertyList.c (CFPropertyListCreateDeepCopy): Return NULL for an invalid property list. diff --git a/Source/CFRunLoop.c b/Source/CFRunLoop.c index 88a4ad23..3aae1fdb 100644 --- a/Source/CFRunLoop.c +++ b/Source/CFRunLoop.c @@ -969,8 +969,13 @@ CFRunLoopIsWaiting (CFRunLoopRef rl) return rl->_isWaiting; } -void -CFRunLoopAddCommonMode (CFRunLoopRef rl, CFStringRef mode) +/* The ObjC bridge (NSCFRunLoop) calls these private implementations rather + * than the public functions, because GNUstep Base exports functions of the + * same names which send the corresponding messages to the run loop, and + * calling those would recurse. + */ +GS_PRIVATE void +_CFRunLoopAddCommonMode (CFRunLoopRef rl, CFStringRef mode) { GSMutexLock (&rl->_lock); @@ -985,10 +990,18 @@ CFRunLoopAddCommonMode (CFRunLoopRef rl, CFStringRef mode) GSRunLoopContextAddFunc, new); CFDictionaryAddValue (rl->_contexts, mode, new); } - + GSMutexUnlock (&rl->_lock); } +void +CFRunLoopAddCommonMode (CFRunLoopRef rl, CFStringRef mode) +{ + CF_OBJC_FUNCDISPATCHV(_kCFRunLoopTypeID, void, rl, + "addCommonMode:", mode); + _CFRunLoopAddCommonMode (rl, mode); +} + static void CFRunLoopCopyKeysFunc (const void *key, const void *value, void *context) { @@ -996,8 +1009,8 @@ CFRunLoopCopyKeysFunc (const void *key, const void *value, void *context) CFArrayAppendValue (array, key); } -CFArrayRef -CFRunLoopCopyAllModes (CFRunLoopRef rl) +GS_PRIVATE CFArrayRef +_CFRunLoopCopyAllModes (CFRunLoopRef rl) { CFArrayRef ret; CFMutableArrayRef a; @@ -1013,12 +1026,20 @@ CFRunLoopCopyAllModes (CFRunLoopRef rl) ret = CFArrayCreateCopy (NULL, a); CFRelease (a); - + return ret; } -CFStringRef -CFRunLoopCopyCurrentMode (CFRunLoopRef rl) +CFArrayRef +CFRunLoopCopyAllModes (CFRunLoopRef rl) +{ + CF_OBJC_FUNCDISPATCHV_RETAINED(_kCFRunLoopTypeID, CFArrayRef, rl, + "allModes"); + return _CFRunLoopCopyAllModes (rl); +} + +GS_PRIVATE CFStringRef +_CFRunLoopCopyCurrentMode (CFRunLoopRef rl) { CFStringRef mode = rl->_currentMode; @@ -1028,6 +1049,14 @@ CFRunLoopCopyCurrentMode (CFRunLoopRef rl) return NULL; } +CFStringRef +CFRunLoopCopyCurrentMode (CFRunLoopRef rl) +{ + CF_OBJC_FUNCDISPATCHV_RETAINED(_kCFRunLoopTypeID, CFStringRef, rl, + "currentMode"); + return _CFRunLoopCopyCurrentMode (rl); +} + static void CFRunLoopPerformBlock_nolock (CFRunLoopRef rl, CFTypeRef mode, PerformBlockType block) diff --git a/Source/GNUmakefile.in b/Source/GNUmakefile.in index e3a787af..c6077da4 100644 --- a/Source/GNUmakefile.in +++ b/Source/GNUmakefile.in @@ -104,6 +104,7 @@ libgnustep-corebase_OBJC_FILES = \ NSCFInputStream.m \ NSCFOutputStream.m \ NSCFDate.m \ + NSCFRunLoop.m \ NSCFType.m libgnustep-corebase_LIBRARIES_DEPEND_UPON += @LIBS@ $(FND_LIBS) diff --git a/Source/NSCFRunLoop.m b/Source/NSCFRunLoop.m new file mode 100644 index 00000000..e307859b --- /dev/null +++ b/Source/NSCFRunLoop.m @@ -0,0 +1,88 @@ +/* NSCFRunLoop.m + + Copyright (C) 2026 Free Software Foundation, Inc. + + Written by: Frederik Seiffert + Date: October, 2026 + + This file is part of GNUstep CoreBase Library. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; see the file COPYING.LIB. + If not, see or write to the + Free Software Foundation, 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#import +#import +#import + +#include "NSCFType.h" +#include "GSPrivate.h" +#include "CoreFoundation/CFRunLoop.h" + +GS_PRIVATE void +_CFRunLoopAddCommonMode (CFRunLoopRef rl, CFStringRef mode); +GS_PRIVATE CFArrayRef +_CFRunLoopCopyAllModes (CFRunLoopRef rl); +GS_PRIVATE CFStringRef +_CFRunLoopCopyCurrentMode (CFRunLoopRef rl); + +/* Implements the NSRunLoop methods used by the CFRunLoop mode functions + * in GNUstep Base, so that those functions also work on CFRunLoopRefs. + */ +@interface NSCFRunLoop : NSObject +NSCFTYPE_VARS +@end + +@implementation NSCFRunLoop ++ (void) load +{ + NSCFInitialize (); +} + ++ (void) initialize +{ + GSObjCAddClassBehavior (self, [NSCFType class]); +} + +- (void) addCommonMode: (NSString*)mode +{ + _CFRunLoopAddCommonMode ((CFRunLoopRef)self, (CFStringRef)mode); +} + +/* These return Base objects rather than autoreleasing the CF objects, as + * autoreleasing bridged CF objects crashes with the gnustep-1.9 runtime. + */ +- (NSArray*) allModes +{ + CFArrayRef modes = _CFRunLoopCopyAllModes ((CFRunLoopRef)self); + NSArray *result = [NSArray arrayWithArray: (NSArray*)modes]; + + CFRelease (modes); + return result; +} + +- (NSString*) currentMode +{ + CFStringRef mode = _CFRunLoopCopyCurrentMode ((CFRunLoopRef)self); + NSString *result; + + if (mode == NULL) + return nil; + result = [NSString stringWithString: (NSString*)mode]; + CFRelease (mode); + return result; +} +@end diff --git a/Source/NSCFType.m b/Source/NSCFType.m index 387590da..741de8c2 100644 --- a/Source/NSCFType.m +++ b/Source/NSCFType.m @@ -40,7 +40,7 @@ void NSCFInitialize (void) { - static int requiredClasses = 12; + static int requiredClasses = 14; --requiredClasses; if (requiredClasses == 0) @@ -68,6 +68,7 @@ void NSCFInitialize (void) CFRuntimeBridgeClass (CFReadStreamGetTypeID(), "NSCFInputStream"); CFRuntimeBridgeClass (CFWriteStreamGetTypeID(), "NSCFOutputStream"); CFRuntimeBridgeClass (CFDateGetTypeID(), "NSCFDate"); + CFRuntimeBridgeClass (CFRunLoopGetTypeID(), "NSCFRunLoop"); GSRuntimeInitializeConstants (); } diff --git a/Tests/CFRunLoop/bridge.m b/Tests/CFRunLoop/bridge.m new file mode 100644 index 00000000..5025bef4 --- /dev/null +++ b/Tests/CFRunLoop/bridge.m @@ -0,0 +1,56 @@ +#import +#import +#include "CoreFoundation/CFArray.h" +#include "CoreFoundation/CFRunLoop.h" +#include "../CFTesting.h" + +void testNSonCF(void); +void testCFonNS(void); + +static Boolean +containsMode (CFArrayRef modes, CFStringRef mode) +{ + return CFArrayContainsValue (modes, CFRangeMake (0, CFArrayGetCount (modes)), + mode); +} + +int main(void) +{ + testNSonCF(); + testCFonNS(); + return 0; +} + +void testNSonCF(void) +{ + CFRunLoopRef cfrl = CFRunLoopGetCurrent(); + NSRunLoop *nsrl = (NSRunLoop*) cfrl; + + testHopeful = true; + PASS_CF(cfrl != NULL, "CFRunLoopGetCurrent() returns a run loop"); + testHopeful = false; + if (cfrl == NULL) + return; + + [nsrl addCommonMode: (NSString*) CFSTR("ns_on_cf")]; + PASS_CF(containsMode((CFArrayRef) [nsrl allModes], CFSTR("ns_on_cf")), + "-addCommonMode: and -allModes work on a CFRunLoop"); + PASS_CF([nsrl currentMode] == nil, + "-currentMode works on a CFRunLoop"); +} + +void testCFonNS(void) +{ + NSRunLoop *nsrl = [NSRunLoop currentRunLoop]; + CFRunLoopRef cfrl = [nsrl getCFRunLoop]; + CFArrayRef modes; + + CFRunLoopAddCommonMode(cfrl, CFSTR("cf_on_ns")); + modes = CFRunLoopCopyAllModes(cfrl); + PASS_CF(containsMode(modes, CFSTR("cf_on_ns")), + "CFRunLoopAddCommonMode() and CFRunLoopCopyAllModes() work on an NSRunLoop"); + CFRelease(modes); + + PASS_CF(CFRunLoopCopyCurrentMode(cfrl) == NULL, + "CFRunLoopCopyCurrentMode() works on an NSRunLoop"); +}