commit 9f76c4aa660e4cb2cc8b38c5558847d86708c689 Author: Uli Kusterer Date: Sun Mar 8 17:48:07 2015 +0100 Initial check-in. diff --git a/README.md b/README.md new file mode 100644 index 0000000..c88ea19 --- /dev/null +++ b/README.md @@ -0,0 +1,49 @@ +What is it +---------- + +A bit of sample code that demonstrates a quick-and-dirty way to implement a rootless mode for an emulator that has one huge back buffer in which it draws all its windows. + +"Rootless" means that the emulated windows can be shown interleaved on the screen with platform-native window, like many commercial emulators and the Mac's Classic environment used to do it. + + +Design goals +------------ + +The design goal was to provide an easy way to hook in source code that assumes it has control over the machine. Your emulator's main function runs (renamed to EmulatorMain()) in its own thread and uses a few callbacks to talk to the actual Mac application framework. That way, its menus stay available even when the emulator itself hangs. + +The emulator needs to register every rectangle that should show up as a window with this library. As such, this approach is not suitable for emulators that do not know what OS they run on, and you might have to hook into the host OS in some way to be notified when windows appear, are moved or go away. + +It is intended as a demonstration of how it would work. As such, it currently only handles mouse events, though keyboard events could be implemented the same way. + + +What needs to be optimized? +--------------------------- + +Right now, back buffer updates are rather heavyweight, as is every API call. While it works and should be sufficiently fast for emulating older machines, one could speed it up significantly by using OpenGL directly and just uploading the pixel buffer to the screen. Maybe even use shared GPU memory for the back buffer and support partial updates only of changed areas. Similarly, the inter-thread communication could be rewritten in lower-level code, as we're really only sending across four numbers. + + +License +------- + +Note: Some submodules may be subject to different licenses. + + Copyright 2003-2014 by Uli Kusterer. + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. diff --git a/RootlessForEmulators.xcodeproj/project.pbxproj b/RootlessForEmulators.xcodeproj/project.pbxproj new file mode 100644 index 0000000..61a0aa2 --- /dev/null +++ b/RootlessForEmulators.xcodeproj/project.pbxproj @@ -0,0 +1,290 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 557FA9A21AAC8671004DF2F5 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 557FA9A11AAC8671004DF2F5 /* AppDelegate.m */; }; + 557FA9A41AAC8671004DF2F5 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 557FA9A31AAC8671004DF2F5 /* main.m */; }; + 557FA9A61AAC8671004DF2F5 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 557FA9A51AAC8671004DF2F5 /* Images.xcassets */; }; + 557FA9A91AAC8672004DF2F5 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 557FA9A71AAC8672004DF2F5 /* MainMenu.xib */; }; + 557FA9C01AACA251004DF2F5 /* EmulatorMain.c in Sources */ = {isa = PBXBuildFile; fileRef = 557FA9BF1AACA251004DF2F5 /* EmulatorMain.c */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 557FA99B1AAC8671004DF2F5 /* RootlessForEmulators.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = RootlessForEmulators.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 557FA99F1AAC8671004DF2F5 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 557FA9A01AAC8671004DF2F5 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; + 557FA9A11AAC8671004DF2F5 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; + 557FA9A31AAC8671004DF2F5 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; + 557FA9A51AAC8671004DF2F5 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; + 557FA9A81AAC8672004DF2F5 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; + 557FA9BE1AAC9BC0004DF2F5 /* RootlessForEmulators.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RootlessForEmulators.h; path = RootlessForEmulators/RootlessForEmulators.h; sourceTree = ""; }; + 557FA9BF1AACA251004DF2F5 /* EmulatorMain.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = EmulatorMain.c; path = RootlessForEmulators/EmulatorMain.c; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 557FA9981AAC8671004DF2F5 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 557FA9921AAC8671004DF2F5 = { + isa = PBXGroup; + children = ( + 557FA9BE1AAC9BC0004DF2F5 /* RootlessForEmulators.h */, + 557FA9BF1AACA251004DF2F5 /* EmulatorMain.c */, + 557FA99D1AAC8671004DF2F5 /* RootlessForEmulators */, + 557FA99C1AAC8671004DF2F5 /* Products */, + ); + sourceTree = ""; + }; + 557FA99C1AAC8671004DF2F5 /* Products */ = { + isa = PBXGroup; + children = ( + 557FA99B1AAC8671004DF2F5 /* RootlessForEmulators.app */, + ); + name = Products; + sourceTree = ""; + }; + 557FA99D1AAC8671004DF2F5 /* RootlessForEmulators */ = { + isa = PBXGroup; + children = ( + 557FA9A01AAC8671004DF2F5 /* AppDelegate.h */, + 557FA9A11AAC8671004DF2F5 /* AppDelegate.m */, + 557FA9A51AAC8671004DF2F5 /* Images.xcassets */, + 557FA9A71AAC8672004DF2F5 /* MainMenu.xib */, + 557FA99E1AAC8671004DF2F5 /* Supporting Files */, + ); + path = RootlessForEmulators; + sourceTree = ""; + }; + 557FA99E1AAC8671004DF2F5 /* Supporting Files */ = { + isa = PBXGroup; + children = ( + 557FA99F1AAC8671004DF2F5 /* Info.plist */, + 557FA9A31AAC8671004DF2F5 /* main.m */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 557FA99A1AAC8671004DF2F5 /* RootlessForEmulators */ = { + isa = PBXNativeTarget; + buildConfigurationList = 557FA9B81AAC8672004DF2F5 /* Build configuration list for PBXNativeTarget "RootlessForEmulators" */; + buildPhases = ( + 557FA9971AAC8671004DF2F5 /* Sources */, + 557FA9981AAC8671004DF2F5 /* Frameworks */, + 557FA9991AAC8671004DF2F5 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = RootlessForEmulators; + productName = RootlessForEmulators; + productReference = 557FA99B1AAC8671004DF2F5 /* RootlessForEmulators.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 557FA9931AAC8671004DF2F5 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0610; + ORGANIZATIONNAME = "Uli Kusterer"; + TargetAttributes = { + 557FA99A1AAC8671004DF2F5 = { + CreatedOnToolsVersion = 6.1.1; + }; + }; + }; + buildConfigurationList = 557FA9961AAC8671004DF2F5 /* Build configuration list for PBXProject "RootlessForEmulators" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 557FA9921AAC8671004DF2F5; + productRefGroup = 557FA99C1AAC8671004DF2F5 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 557FA99A1AAC8671004DF2F5 /* RootlessForEmulators */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 557FA9991AAC8671004DF2F5 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 557FA9A61AAC8671004DF2F5 /* Images.xcassets in Resources */, + 557FA9A91AAC8672004DF2F5 /* MainMenu.xib in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 557FA9971AAC8671004DF2F5 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 557FA9C01AACA251004DF2F5 /* EmulatorMain.c in Sources */, + 557FA9A41AAC8671004DF2F5 /* main.m in Sources */, + 557FA9A21AAC8671004DF2F5 /* AppDelegate.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + 557FA9A71AAC8672004DF2F5 /* MainMenu.xib */ = { + isa = PBXVariantGroup; + children = ( + 557FA9A81AAC8672004DF2F5 /* Base */, + ); + name = MainMenu.xib; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 557FA9B61AAC8672004DF2F5 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.10; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + }; + name = Debug; + }; + 557FA9B71AAC8672004DF2F5 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.10; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = macosx; + }; + name = Release; + }; + 557FA9B91AAC8672004DF2F5 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = RootlessForEmulators/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 557FA9BA1AAC8672004DF2F5 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = RootlessForEmulators/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 557FA9961AAC8671004DF2F5 /* Build configuration list for PBXProject "RootlessForEmulators" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 557FA9B61AAC8672004DF2F5 /* Debug */, + 557FA9B71AAC8672004DF2F5 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 557FA9B81AAC8672004DF2F5 /* Build configuration list for PBXNativeTarget "RootlessForEmulators" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 557FA9B91AAC8672004DF2F5 /* Debug */, + 557FA9BA1AAC8672004DF2F5 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 557FA9931AAC8671004DF2F5 /* Project object */; +} diff --git a/RootlessForEmulators/AppDelegate.h b/RootlessForEmulators/AppDelegate.h new file mode 100644 index 0000000..7e56b49 --- /dev/null +++ b/RootlessForEmulators/AppDelegate.h @@ -0,0 +1,18 @@ +// +// AppDelegate.h +// RootlessForEmulators +// +// Created by Uli Kusterer on 08/03/15. +// Copyright (c) 2015 Uli Kusterer. All rights reserved. +// + +#import + + +@interface AppDelegate : NSObject + +-(void) addEvent: (NSEvent*)evt; +-(NSEvent*) dequeueNextEvent; + +@end + diff --git a/RootlessForEmulators/AppDelegate.m b/RootlessForEmulators/AppDelegate.m new file mode 100644 index 0000000..f21fd93 --- /dev/null +++ b/RootlessForEmulators/AppDelegate.m @@ -0,0 +1,339 @@ +// +// AppDelegate.m +// RootlessForEmulators +// +// Created by Uli Kusterer on 08/03/15. +// Copyright (c) 2015 Uli Kusterer. All rights reserved. +// + +#import "AppDelegate.h" +#import "RootlessForEmulators.h" + + +@interface AppDelegate () + +@property (nonatomic, strong) NSImage* backBuffer; +@property (strong) NSMutableArray* windows; +@property (strong) NSMutableArray* events; + +@end + + +@interface RootlessWindowContentView : NSView + +@end + + +@implementation RootlessWindowContentView + +-(void) drawRect:(NSRect)dirtyRect +{ + NSImage *bb = [(id)NSApplication.sharedApplication.delegate backBuffer]; + NSRect myBox = [self.window contentRectForFrameRect: self.window.frame]; + [bb drawAtPoint: NSZeroPoint fromRect: myBox operation: NSCompositeCopy fraction: 1.0]; +} + + +-(void) mouseDown:(NSEvent *)theEvent +{ + [(id)NSApplication.sharedApplication.delegate addEvent: theEvent]; +} + + +-(void) mouseDragged:(NSEvent *)theEvent +{ + [(id)NSApplication.sharedApplication.delegate addEvent: theEvent]; +} + + +-(void) mouseUp:(NSEvent *)theEvent +{ + [(id)NSApplication.sharedApplication.delegate addEvent: theEvent]; +} + + +-(void) rightMouseDown:(NSEvent *)theEvent +{ + [(id)NSApplication.sharedApplication.delegate addEvent: theEvent]; +} + + +-(void) rightMouseDragged:(NSEvent *)theEvent +{ + [(id)NSApplication.sharedApplication.delegate addEvent: theEvent]; +} + + +-(void) rightMouseUp:(NSEvent *)theEvent +{ + [(id)NSApplication.sharedApplication.delegate addEvent: theEvent]; +} + + +-(void) otherMouseDown:(NSEvent *)theEvent +{ + [(id)NSApplication.sharedApplication.delegate addEvent: theEvent]; +} + + +-(void) otherMouseDragged:(NSEvent *)theEvent +{ + [(id)NSApplication.sharedApplication.delegate addEvent: theEvent]; +} + + +-(void) otherMouseUp:(NSEvent *)theEvent +{ + [(id)NSApplication.sharedApplication.delegate addEvent: theEvent]; +} + + +-(void) windowDidMove: (NSNotification*)notif +{ + [self setNeedsDisplay: YES]; +} + + +-(void) viewDidMoveToWindow +{ + [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(windowDidMove:) name: NSWindowDidMoveNotification object: self.window]; +} + +@end + + +@interface RootlessWindowAndFrame : NSObject +{ + NSRect mFrame; + NSWindow* mWindow; +} + +-(id) initWithWindow: (NSWindow*)wd frame: (NSRect)box; + +-(NSRect) frame; +-(NSWindow*) window; +-(void) setWindow: (NSWindow*)wd; + +@end + + +@implementation RootlessWindowAndFrame + +-(id) initWithWindow: (NSWindow*)wd frame: (NSRect)box +{ + self = [super init]; + if( self ) + { + mWindow = wd; + mFrame = box; + } + return self; +} + +-(NSRect) frame +{ + return mFrame; +} + + +-(NSWindow*) window +{ + return mWindow; +} + + +-(void) setWindow: (NSWindow*)wd +{ + mWindow = wd; +} + +@end + + +@implementation AppDelegate + +- (void)applicationDidFinishLaunching:(NSNotification *)aNotification +{ + NSRect backBufferBox = { NSZeroPoint, [NSScreen.screens.firstObject frame].size }; + self.backBuffer = [[NSImage alloc] initWithSize: backBufferBox.size]; + + [self.backBuffer lockFocus]; + [NSColor.blackColor set]; + NSRectFillUsingOperation( backBufferBox, NSCompositeCopy ); + [self.backBuffer unlockFocus]; + + self.windows = [[NSMutableArray alloc] init]; + self.events = [[NSMutableArray alloc] init]; + + [NSApplication.sharedApplication setPresentationOptions: NSApplicationPresentationAutoHideDock | NSApplicationPresentationAutoHideMenuBar]; + + [NSThread detachNewThreadSelector: @selector(emulatorMain) toTarget: self withObject: nil]; +} + +- (void)applicationWillTerminate:(NSNotification *)aNotification +{ + // Insert code here to tear down your application +} + + +-(void) emulatorMain +{ + NSArray* args = [NSProcessInfo processInfo].arguments; + NSInteger numArgs = args.count; + char* emulatorArgs[numArgs]; + + // First build a buffer with all C strings in it and + // fill the array with their *offsets* in the data as + // the pointers could change every time we append more data. + NSMutableData* stringData = [NSMutableData data]; + NSInteger idx = 0; + NSInteger offset = 0; + for( NSString* currArg in args ) + { + uint8_t zeroByte = 0; + emulatorArgs[idx] = (char*)offset; + NSData* currCStr = [currArg dataUsingEncoding: NSUTF8StringEncoding]; + [stringData appendBytes: currCStr.bytes length: currCStr.length]; + [stringData appendBytes: &zeroByte length: 1]; + offset += currCStr.length +1; + idx++; + } + + // Now that the strings array is stable, turn the offsets into pointers: + char* firstPointer = (char*)[stringData bytes]; + for( int x = 0; x < numArgs; x++ ) + { + emulatorArgs[x] += (intptr_t)firstPointer; + } + + EmulatorMain( numArgs, emulatorArgs ); + + [NSApplication.sharedApplication performSelectorOnMainThread: @selector(terminate:) withObject: nil waitUntilDone: NO]; +} + + +-(void) addWindow: (RootlessWindowAndFrame*)waf +{ + NSWindow* theWindow = [[NSWindow alloc] initWithContentRect: waf.frame styleMask: NSBorderlessWindowMask backing: NSBackingStoreBuffered defer: NO]; + theWindow.releasedWhenClosed = NO; + + [theWindow setContentView: [[RootlessWindowContentView alloc] initWithFrame: NSMakeRect(0, 0, 100, 100)]]; + + [self.windows addObject: theWindow]; + [theWindow makeKeyAndOrderFront: self]; + + waf.window = theWindow; +} + + +-(void) moveWindow: (RootlessWindowAndFrame*)waf +{ + [waf.window setFrame: [waf.window frameRectForContentRect: waf.frame] display: YES]; +} + + +-(void) removeWindow: (NSWindow*)theWindow +{ + [theWindow close]; + [self.windows removeObject: theWindow]; +} + + +-(void) setBackBuffer: (NSImage *)backBuffer +{ + self->_backBuffer = backBuffer; + for( NSWindow* aWindow in self.windows ) + [aWindow.contentView setNeedsDisplay: YES]; +} + + +-(void) addEvent: (NSEvent*)evt +{ + @synchronized( self ) + { + [self.events addObject: evt]; + } +} + + +-(NSEvent*) dequeueNextEvent +{ + NSEvent* evt = nil; + @synchronized( self ) + { + evt = self.events.firstObject; + if( evt ) + [self.events removeObjectAtIndex: 0]; + } + return evt; +} + +@end + + +RootlessWindow CreateWindowWithRect( int x, int y, int width, int height ) +{ + RootlessWindowAndFrame * waf = [[RootlessWindowAndFrame alloc] initWithWindow: nil frame: NSMakeRect(x, y, width, height)]; + + [(id)NSApplication.sharedApplication.delegate performSelectorOnMainThread: @selector(addWindow:) withObject: waf waitUntilDone: YES]; + + return (__bridge RootlessWindow)waf.window; +} + + +void SetWindowRect( RootlessWindow win, int x, int y, int width, int height ) +{ + RootlessWindowAndFrame * waf = [[RootlessWindowAndFrame alloc] initWithWindow: (__bridge NSWindow*)win frame: NSMakeRect(x, y, width, height)]; + + [(id)NSApplication.sharedApplication.delegate performSelectorOnMainThread: @selector(moveWindow:) withObject: waf waitUntilDone: YES]; +} + + +void FreeWindow( RootlessWindow win ) +{ + [(id)NSApplication.sharedApplication.delegate performSelectorOnMainThread: @selector(removeWindow:) withObject: (__bridge NSWindow*)win waitUntilDone: YES]; +} + + +void BackBufferChanged( void* data, int rowBytes, int width, int height ) +{ + NSImage* newBackBuffer = [[NSImage alloc] initWithSize: NSMakeSize(width, height)]; + unsigned char* pixels[4] = { data, NULL, NULL, NULL }; + NSBitmapImageRep* bir = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes: pixels pixelsWide:width pixelsHigh: height bitsPerSample: 8 samplesPerPixel: 4 hasAlpha: YES isPlanar: NO colorSpaceName:NSCalibratedRGBColorSpace bitmapFormat: NS32BitLittleEndianBitmapFormat bytesPerRow: rowBytes bitsPerPixel: 32]; + [newBackBuffer addRepresentation: bir]; + [(id)NSApplication.sharedApplication.delegate performSelectorOnMainThread: @selector(setBackBuffer:) withObject: newBackBuffer waitUntilDone: YES]; +} + + +bool QueryInputDevices( int *outButtonDown, int *outX, int *outY ) +{ + NSEvent* evt = [(id)NSApplication.sharedApplication.delegate dequeueNextEvent]; + if( !evt ) + { + return false; + } + + if( evt.type == NSLeftMouseDown || evt.type == NSLeftMouseUp || evt.type == NSLeftMouseDragged + || evt.type == NSRightMouseDown || evt.type == NSRightMouseUp || evt.type == NSRightMouseDragged + || evt.type == NSOtherMouseDown || evt.type == NSOtherMouseUp || evt.type == NSOtherMouseDragged ) + { + *outButtonDown = (int)evt.buttonNumber; + NSPoint pos = evt.locationInWindow; + pos.x += evt.window.frame.origin.x; + pos.y += evt.window.frame.origin.y; + *outX = pos.x; + *outY = pos.y; + } + + return true; +} + + +void GetScreenSize( int *outWidth, int *outHeight ) +{ + NSScreen* currScreen = NSScreen.screens.firstObject; + *outWidth = currScreen.frame.size.width; + *outHeight = currScreen.frame.size.height; +} + + diff --git a/RootlessForEmulators/Base.lproj/MainMenu.xib b/RootlessForEmulators/Base.lproj/MainMenu.xib new file mode 100644 index 0000000..6fc9fef --- /dev/null +++ b/RootlessForEmulators/Base.lproj/MainMenu.xib @@ -0,0 +1,666 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Default + + + + + + + Left to Right + + + + + + + Right to Left + + + + + + + + + + + Default + + + + + + + Left to Right + + + + + + + Right to Left + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/RootlessForEmulators/EmulatorMain.c b/RootlessForEmulators/EmulatorMain.c new file mode 100644 index 0000000..53c0a9f --- /dev/null +++ b/RootlessForEmulators/EmulatorMain.c @@ -0,0 +1,53 @@ +// +// EmulatorMain.c +// RootlessForEmulators +// +// Created by Uli Kusterer on 08/03/15. +// Copyright (c) 2015 Uli Kusterer. All rights reserved. +// + +#include "RootlessForEmulators.h" +#include +#include +#include +#include +#include + + +int EmulatorMain( int argc, const char** argv ) +{ + // We get all command-line arguments (note that MacOS X adds its own arguments when launching GUI apps, so be prepared for unknown args): + for( int x = 0; x < argc; x++ ) + { + printf( "%s ", argv[x] ); + } + + printf("\n"); + + // Create a back buffer: + int screenWidth = 1024, screenHeight = 768; + GetScreenSize( &screenWidth, &screenHeight ); + + uint32_t* backBuffer = malloc(screenWidth * screenHeight * 4); + for( int x = 0; x < (screenWidth * screenHeight); x++ ) + backBuffer[x] = 0xFF0000FF; // Red with 100% alpha. + BackBufferChanged( backBuffer, screenWidth * 4, screenWidth, screenHeight ); // And tell the rootless code to load it. You do that every time something changes. + + // Everything that should show up as a window should be registered using this call: + RootlessWindow wd = CreateWindowWithRect( 100, 100, 512, 342 ); + // SetWindowRect(wd,150,100,512,342); // Move a window. + + // Now process events coming in (We just loop until you click the right mouse button in a window): + while( true ) + { + int buttonDown, x, y; + if( QueryInputDevices( &buttonDown, &x, &y ) && buttonDown == 1 ) + { + break; + } + } + + FreeWindow(wd); // Get rid of a window. + + return 0; +} \ No newline at end of file diff --git a/RootlessForEmulators/Images.xcassets/AppIcon.appiconset/Contents.json b/RootlessForEmulators/Images.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..2db2b1c --- /dev/null +++ b/RootlessForEmulators/Images.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,58 @@ +{ + "images" : [ + { + "idiom" : "mac", + "size" : "16x16", + "scale" : "1x" + }, + { + "idiom" : "mac", + "size" : "16x16", + "scale" : "2x" + }, + { + "idiom" : "mac", + "size" : "32x32", + "scale" : "1x" + }, + { + "idiom" : "mac", + "size" : "32x32", + "scale" : "2x" + }, + { + "idiom" : "mac", + "size" : "128x128", + "scale" : "1x" + }, + { + "idiom" : "mac", + "size" : "128x128", + "scale" : "2x" + }, + { + "idiom" : "mac", + "size" : "256x256", + "scale" : "1x" + }, + { + "idiom" : "mac", + "size" : "256x256", + "scale" : "2x" + }, + { + "idiom" : "mac", + "size" : "512x512", + "scale" : "1x" + }, + { + "idiom" : "mac", + "size" : "512x512", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/RootlessForEmulators/Info.plist b/RootlessForEmulators/Info.plist new file mode 100644 index 0000000..aab199a --- /dev/null +++ b/RootlessForEmulators/Info.plist @@ -0,0 +1,34 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIconFile + + CFBundleIdentifier + com.thevoidsoftware.$(PRODUCT_NAME:rfc1034identifier) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + LSMinimumSystemVersion + $(MACOSX_DEPLOYMENT_TARGET) + NSHumanReadableCopyright + Copyright © 2015 Uli Kusterer. All rights reserved. + NSMainNibFile + MainMenu + NSPrincipalClass + NSApplication + + diff --git a/RootlessForEmulators/RootlessForEmulators.h b/RootlessForEmulators/RootlessForEmulators.h new file mode 100644 index 0000000..1a1b73b --- /dev/null +++ b/RootlessForEmulators/RootlessForEmulators.h @@ -0,0 +1,24 @@ +// +// RootlessForEmulators.h +// RootlessForEmulators +// +// Created by Uli Kusterer on 08/03/15. +// Copyright (c) 2015 Uli Kusterer. All rights reserved. +// + + +#include + + +typedef void* RootlessWindow; + +RootlessWindow CreateWindowWithRect( int x, int y, int width, int height ); +void SetWindowRect( RootlessWindow win, int x, int y, int width, int height ); +void FreeWindow( RootlessWindow win ); + +void BackBufferChanged( void* data, int rowBytes, int width, int height ); // Pixel data assumed to be 32-bit RGBA. Could be sth. else, just seemed like a useful choice. +bool QueryInputDevices( int *outButtonDown, int *outX, int *outY ); +void GetScreenSize( int *outWidth, int *outHeight ); + + +int EmulatorMain( int argc, const char** argv ); // This is what you implement. \ No newline at end of file diff --git a/RootlessForEmulators/main.m b/RootlessForEmulators/main.m new file mode 100644 index 0000000..0dc48f3 --- /dev/null +++ b/RootlessForEmulators/main.m @@ -0,0 +1,13 @@ +// +// main.m +// RootlessForEmulators +// +// Created by Uli Kusterer on 08/03/15. +// Copyright (c) 2015 Uli Kusterer. All rights reserved. +// + +#import + +int main(int argc, const char * argv[]) { + return NSApplicationMain(argc, argv); +}