Initial check-in.

This commit is contained in:
Uli Kusterer 2015-03-08 17:48:07 +01:00
commit 9f76c4aa66
10 changed files with 1544 additions and 0 deletions

49
README.md Normal file
View File

@ -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.

View File

@ -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 = "<group>"; };
557FA9A01AAC8671004DF2F5 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; };
557FA9A11AAC8671004DF2F5 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
557FA9A31AAC8671004DF2F5 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
557FA9A51AAC8671004DF2F5 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = "<group>"; };
557FA9A81AAC8672004DF2F5 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = "<group>"; };
557FA9BE1AAC9BC0004DF2F5 /* RootlessForEmulators.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RootlessForEmulators.h; path = RootlessForEmulators/RootlessForEmulators.h; sourceTree = "<group>"; };
557FA9BF1AACA251004DF2F5 /* EmulatorMain.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = EmulatorMain.c; path = RootlessForEmulators/EmulatorMain.c; sourceTree = "<group>"; };
/* 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 = "<group>";
};
557FA99C1AAC8671004DF2F5 /* Products */ = {
isa = PBXGroup;
children = (
557FA99B1AAC8671004DF2F5 /* RootlessForEmulators.app */,
);
name = Products;
sourceTree = "<group>";
};
557FA99D1AAC8671004DF2F5 /* RootlessForEmulators */ = {
isa = PBXGroup;
children = (
557FA9A01AAC8671004DF2F5 /* AppDelegate.h */,
557FA9A11AAC8671004DF2F5 /* AppDelegate.m */,
557FA9A51AAC8671004DF2F5 /* Images.xcassets */,
557FA9A71AAC8672004DF2F5 /* MainMenu.xib */,
557FA99E1AAC8671004DF2F5 /* Supporting Files */,
);
path = RootlessForEmulators;
sourceTree = "<group>";
};
557FA99E1AAC8671004DF2F5 /* Supporting Files */ = {
isa = PBXGroup;
children = (
557FA99F1AAC8671004DF2F5 /* Info.plist */,
557FA9A31AAC8671004DF2F5 /* main.m */,
);
name = "Supporting Files";
sourceTree = "<group>";
};
/* 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 = "<group>";
};
/* 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 */;
}

View File

@ -0,0 +1,18 @@
//
// AppDelegate.h
// RootlessForEmulators
//
// Created by Uli Kusterer on 08/03/15.
// Copyright (c) 2015 Uli Kusterer. All rights reserved.
//
#import <Cocoa/Cocoa.h>
@interface AppDelegate : NSObject <NSApplicationDelegate>
-(void) addEvent: (NSEvent*)evt;
-(NSEvent*) dequeueNextEvent;
@end

View File

@ -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;
}

View File

@ -0,0 +1,666 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="6254" systemVersion="14C109" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="6254"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="NSApplication">
<connections>
<outlet property="delegate" destination="Voe-Tx-rLC" id="GzC-gU-4Uq"/>
</connections>
</customObject>
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
<customObject id="Voe-Tx-rLC" customClass="AppDelegate"/>
<customObject id="YLy-65-1bz" customClass="NSFontManager"/>
<menu title="Main Menu" systemMenu="main" id="AYu-sK-qS6">
<items>
<menuItem title="RootlessForEmulators" id="1Xt-HY-uBw">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="RootlessForEmulators" systemMenu="apple" id="uQy-DD-JDr">
<items>
<menuItem title="About RootlessForEmulators" id="5kV-Vb-QxS">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="orderFrontStandardAboutPanel:" target="-1" id="Exp-CZ-Vem"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="VOq-y0-SEH"/>
<menuItem title="Preferences…" keyEquivalent="," id="BOF-NM-1cW"/>
<menuItem isSeparatorItem="YES" id="wFC-TO-SCJ"/>
<menuItem title="Services" id="NMo-om-nkz">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Services" systemMenu="services" id="hz9-B4-Xy5"/>
</menuItem>
<menuItem isSeparatorItem="YES" id="4je-JR-u6R"/>
<menuItem title="Hide RootlessForEmulators" keyEquivalent="h" id="Olw-nP-bQN">
<connections>
<action selector="hide:" target="-1" id="PnN-Uc-m68"/>
</connections>
</menuItem>
<menuItem title="Hide Others" keyEquivalent="h" id="Vdr-fp-XzO">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections>
<action selector="hideOtherApplications:" target="-1" id="VT4-aY-XCT"/>
</connections>
</menuItem>
<menuItem title="Show All" id="Kd2-mp-pUS">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="unhideAllApplications:" target="-1" id="Dhg-Le-xox"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="kCx-OE-vgT"/>
<menuItem title="Quit RootlessForEmulators" keyEquivalent="q" id="4sb-4s-VLi">
<connections>
<action selector="terminate:" target="-1" id="Te7-pn-YzF"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="File" id="dMs-cI-mzQ">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="File" id="bib-Uj-vzu">
<items>
<menuItem title="New" keyEquivalent="n" id="Was-JA-tGl">
<connections>
<action selector="newDocument:" target="-1" id="4Si-XN-c54"/>
</connections>
</menuItem>
<menuItem title="Open…" keyEquivalent="o" id="IAo-SY-fd9">
<connections>
<action selector="openDocument:" target="-1" id="bVn-NM-KNZ"/>
</connections>
</menuItem>
<menuItem title="Open Recent" id="tXI-mr-wws">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Open Recent" systemMenu="recentDocuments" id="oas-Oc-fiZ">
<items>
<menuItem title="Clear Menu" id="vNY-rz-j42">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="clearRecentDocuments:" target="-1" id="Daa-9d-B3U"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem isSeparatorItem="YES" id="m54-Is-iLE"/>
<menuItem title="Close" keyEquivalent="w" id="DVo-aG-piG">
<connections>
<action selector="performClose:" target="-1" id="HmO-Ls-i7Q"/>
</connections>
</menuItem>
<menuItem title="Save…" keyEquivalent="s" id="pxx-59-PXV">
<connections>
<action selector="saveDocument:" target="-1" id="teZ-XB-qJY"/>
</connections>
</menuItem>
<menuItem title="Save As…" keyEquivalent="S" id="Bw7-FT-i3A">
<connections>
<action selector="saveDocumentAs:" target="-1" id="mDf-zr-I0C"/>
</connections>
</menuItem>
<menuItem title="Revert to Saved" id="KaW-ft-85H">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="revertDocumentToSaved:" target="-1" id="iJ3-Pv-kwq"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="aJh-i4-bef"/>
<menuItem title="Page Setup…" keyEquivalent="P" id="qIS-W8-SiK">
<modifierMask key="keyEquivalentModifierMask" shift="YES" command="YES"/>
<connections>
<action selector="runPageLayout:" target="-1" id="Din-rz-gC5"/>
</connections>
</menuItem>
<menuItem title="Print…" keyEquivalent="p" id="aTl-1u-JFS">
<connections>
<action selector="print:" target="-1" id="qaZ-4w-aoO"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Edit" id="5QF-Oa-p0T">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Edit" id="W48-6f-4Dl">
<items>
<menuItem title="Undo" keyEquivalent="z" id="dRJ-4n-Yzg">
<connections>
<action selector="undo:" target="-1" id="M6e-cu-g7V"/>
</connections>
</menuItem>
<menuItem title="Redo" keyEquivalent="Z" id="6dh-zS-Vam">
<connections>
<action selector="redo:" target="-1" id="oIA-Rs-6OD"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="WRV-NI-Exz"/>
<menuItem title="Cut" keyEquivalent="x" id="uRl-iY-unG">
<connections>
<action selector="cut:" target="-1" id="YJe-68-I9s"/>
</connections>
</menuItem>
<menuItem title="Copy" keyEquivalent="c" id="x3v-GG-iWU">
<connections>
<action selector="copy:" target="-1" id="G1f-GL-Joy"/>
</connections>
</menuItem>
<menuItem title="Paste" keyEquivalent="v" id="gVA-U4-sdL">
<connections>
<action selector="paste:" target="-1" id="UvS-8e-Qdg"/>
</connections>
</menuItem>
<menuItem title="Paste and Match Style" keyEquivalent="V" id="WeT-3V-zwk">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections>
<action selector="pasteAsPlainText:" target="-1" id="cEh-KX-wJQ"/>
</connections>
</menuItem>
<menuItem title="Delete" id="pa3-QI-u2k">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="delete:" target="-1" id="0Mk-Ml-PaM"/>
</connections>
</menuItem>
<menuItem title="Select All" keyEquivalent="a" id="Ruw-6m-B2m">
<connections>
<action selector="selectAll:" target="-1" id="VNm-Mi-diN"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="uyl-h8-XO2"/>
<menuItem title="Find" id="4EN-yA-p0u">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Find" id="1b7-l0-nxx">
<items>
<menuItem title="Find…" tag="1" keyEquivalent="f" id="Xz5-n4-O0W">
<connections>
<action selector="performFindPanelAction:" target="-1" id="cD7-Qs-BN4"/>
</connections>
</menuItem>
<menuItem title="Find and Replace…" tag="12" keyEquivalent="f" id="YEy-JH-Tfz">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections>
<action selector="performFindPanelAction:" target="-1" id="WD3-Gg-5AJ"/>
</connections>
</menuItem>
<menuItem title="Find Next" tag="2" keyEquivalent="g" id="q09-fT-Sye">
<connections>
<action selector="performFindPanelAction:" target="-1" id="NDo-RZ-v9R"/>
</connections>
</menuItem>
<menuItem title="Find Previous" tag="3" keyEquivalent="G" id="OwM-mh-QMV">
<connections>
<action selector="performFindPanelAction:" target="-1" id="HOh-sY-3ay"/>
</connections>
</menuItem>
<menuItem title="Use Selection for Find" tag="7" keyEquivalent="e" id="buJ-ug-pKt">
<connections>
<action selector="performFindPanelAction:" target="-1" id="U76-nv-p5D"/>
</connections>
</menuItem>
<menuItem title="Jump to Selection" keyEquivalent="j" id="S0p-oC-mLd">
<connections>
<action selector="centerSelectionInVisibleArea:" target="-1" id="IOG-6D-g5B"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Spelling and Grammar" id="Dv1-io-Yv7">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Spelling" id="3IN-sU-3Bg">
<items>
<menuItem title="Show Spelling and Grammar" keyEquivalent=":" id="HFo-cy-zxI">
<connections>
<action selector="showGuessPanel:" target="-1" id="vFj-Ks-hy3"/>
</connections>
</menuItem>
<menuItem title="Check Document Now" keyEquivalent=";" id="hz2-CU-CR7">
<connections>
<action selector="checkSpelling:" target="-1" id="fz7-VC-reM"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="bNw-od-mp5"/>
<menuItem title="Check Spelling While Typing" id="rbD-Rh-wIN">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleContinuousSpellChecking:" target="-1" id="7w6-Qz-0kB"/>
</connections>
</menuItem>
<menuItem title="Check Grammar With Spelling" id="mK6-2p-4JG">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleGrammarChecking:" target="-1" id="muD-Qn-j4w"/>
</connections>
</menuItem>
<menuItem title="Correct Spelling Automatically" id="78Y-hA-62v">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleAutomaticSpellingCorrection:" target="-1" id="2lM-Qi-WAP"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Substitutions" id="9ic-FL-obx">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Substitutions" id="FeM-D8-WVr">
<items>
<menuItem title="Show Substitutions" id="z6F-FW-3nz">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="orderFrontSubstitutionsPanel:" target="-1" id="oku-mr-iSq"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="gPx-C9-uUO"/>
<menuItem title="Smart Copy/Paste" id="9yt-4B-nSM">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleSmartInsertDelete:" target="-1" id="3IJ-Se-DZD"/>
</connections>
</menuItem>
<menuItem title="Smart Quotes" id="hQb-2v-fYv">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleAutomaticQuoteSubstitution:" target="-1" id="ptq-xd-QOA"/>
</connections>
</menuItem>
<menuItem title="Smart Dashes" id="rgM-f4-ycn">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleAutomaticDashSubstitution:" target="-1" id="oCt-pO-9gS"/>
</connections>
</menuItem>
<menuItem title="Smart Links" id="cwL-P1-jid">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleAutomaticLinkDetection:" target="-1" id="Gip-E3-Fov"/>
</connections>
</menuItem>
<menuItem title="Data Detectors" id="tRr-pd-1PS">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleAutomaticDataDetection:" target="-1" id="R1I-Nq-Kbl"/>
</connections>
</menuItem>
<menuItem title="Text Replacement" id="HFQ-gK-NFA">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleAutomaticTextReplacement:" target="-1" id="DvP-Fe-Py6"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Transformations" id="2oI-Rn-ZJC">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Transformations" id="c8a-y6-VQd">
<items>
<menuItem title="Make Upper Case" id="vmV-6d-7jI">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="uppercaseWord:" target="-1" id="sPh-Tk-edu"/>
</connections>
</menuItem>
<menuItem title="Make Lower Case" id="d9M-CD-aMd">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="lowercaseWord:" target="-1" id="iUZ-b5-hil"/>
</connections>
</menuItem>
<menuItem title="Capitalize" id="UEZ-Bs-lqG">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="capitalizeWord:" target="-1" id="26H-TL-nsh"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Speech" id="xrE-MZ-jX0">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Speech" id="3rS-ZA-NoH">
<items>
<menuItem title="Start Speaking" id="Ynk-f8-cLZ">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="startSpeaking:" target="-1" id="654-Ng-kyl"/>
</connections>
</menuItem>
<menuItem title="Stop Speaking" id="Oyz-dy-DGm">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="stopSpeaking:" target="-1" id="dX8-6p-jy9"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Format" id="jxT-CU-nIS">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Format" id="GEO-Iw-cKr">
<items>
<menuItem title="Font" id="Gi5-1S-RQB">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Font" systemMenu="font" id="aXa-aM-Jaq">
<items>
<menuItem title="Show Fonts" keyEquivalent="t" id="Q5e-8K-NDq">
<connections>
<action selector="orderFrontFontPanel:" target="YLy-65-1bz" id="WHr-nq-2xA"/>
</connections>
</menuItem>
<menuItem title="Bold" tag="2" keyEquivalent="b" id="GB9-OM-e27">
<connections>
<action selector="addFontTrait:" target="YLy-65-1bz" id="hqk-hr-sYV"/>
</connections>
</menuItem>
<menuItem title="Italic" tag="1" keyEquivalent="i" id="Vjx-xi-njq">
<connections>
<action selector="addFontTrait:" target="YLy-65-1bz" id="IHV-OB-c03"/>
</connections>
</menuItem>
<menuItem title="Underline" keyEquivalent="u" id="WRG-CD-K1S">
<connections>
<action selector="underline:" target="-1" id="FYS-2b-JAY"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="5gT-KC-WSO"/>
<menuItem title="Bigger" tag="3" keyEquivalent="+" id="Ptp-SP-VEL">
<connections>
<action selector="modifyFont:" target="YLy-65-1bz" id="Uc7-di-UnL"/>
</connections>
</menuItem>
<menuItem title="Smaller" tag="4" keyEquivalent="-" id="i1d-Er-qST">
<connections>
<action selector="modifyFont:" target="YLy-65-1bz" id="HcX-Lf-eNd"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="kx3-Dk-x3B"/>
<menuItem title="Kern" id="jBQ-r6-VK2">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Kern" id="tlD-Oa-oAM">
<items>
<menuItem title="Use Default" id="GUa-eO-cwY">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="useStandardKerning:" target="-1" id="6dk-9l-Ckg"/>
</connections>
</menuItem>
<menuItem title="Use None" id="cDB-IK-hbR">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="turnOffKerning:" target="-1" id="U8a-gz-Maa"/>
</connections>
</menuItem>
<menuItem title="Tighten" id="46P-cB-AYj">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="tightenKerning:" target="-1" id="hr7-Nz-8ro"/>
</connections>
</menuItem>
<menuItem title="Loosen" id="ogc-rX-tC1">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="loosenKerning:" target="-1" id="8i4-f9-FKE"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Ligatures" id="o6e-r0-MWq">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Ligatures" id="w0m-vy-SC9">
<items>
<menuItem title="Use Default" id="agt-UL-0e3">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="useStandardLigatures:" target="-1" id="7uR-wd-Dx6"/>
</connections>
</menuItem>
<menuItem title="Use None" id="J7y-lM-qPV">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="turnOffLigatures:" target="-1" id="iX2-gA-Ilz"/>
</connections>
</menuItem>
<menuItem title="Use All" id="xQD-1f-W4t">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="useAllLigatures:" target="-1" id="KcB-kA-TuK"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Baseline" id="OaQ-X3-Vso">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Baseline" id="ijk-EB-dga">
<items>
<menuItem title="Use Default" id="3Om-Ey-2VK">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="unscript:" target="-1" id="0vZ-95-Ywn"/>
</connections>
</menuItem>
<menuItem title="Superscript" id="Rqc-34-cIF">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="superscript:" target="-1" id="3qV-fo-wpU"/>
</connections>
</menuItem>
<menuItem title="Subscript" id="I0S-gh-46l">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="subscript:" target="-1" id="Q6W-4W-IGz"/>
</connections>
</menuItem>
<menuItem title="Raise" id="2h7-ER-AoG">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="raiseBaseline:" target="-1" id="4sk-31-7Q9"/>
</connections>
</menuItem>
<menuItem title="Lower" id="1tx-W0-xDw">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="lowerBaseline:" target="-1" id="OF1-bc-KW4"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem isSeparatorItem="YES" id="Ndw-q3-faq"/>
<menuItem title="Show Colors" keyEquivalent="C" id="bgn-CT-cEk">
<connections>
<action selector="orderFrontColorPanel:" target="-1" id="mSX-Xz-DV3"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="iMs-zA-UFJ"/>
<menuItem title="Copy Style" keyEquivalent="c" id="5Vv-lz-BsD">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections>
<action selector="copyFont:" target="-1" id="GJO-xA-L4q"/>
</connections>
</menuItem>
<menuItem title="Paste Style" keyEquivalent="v" id="vKC-jM-MkH">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections>
<action selector="pasteFont:" target="-1" id="JfD-CL-leO"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Text" id="Fal-I4-PZk">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Text" id="d9c-me-L2H">
<items>
<menuItem title="Align Left" keyEquivalent="{" id="ZM1-6Q-yy1">
<connections>
<action selector="alignLeft:" target="-1" id="zUv-R1-uAa"/>
</connections>
</menuItem>
<menuItem title="Center" keyEquivalent="|" id="VIY-Ag-zcb">
<connections>
<action selector="alignCenter:" target="-1" id="spX-mk-kcS"/>
</connections>
</menuItem>
<menuItem title="Justify" id="J5U-5w-g23">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="alignJustified:" target="-1" id="ljL-7U-jND"/>
</connections>
</menuItem>
<menuItem title="Align Right" keyEquivalent="}" id="wb2-vD-lq4">
<connections>
<action selector="alignRight:" target="-1" id="r48-bG-YeY"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="4s2-GY-VfK"/>
<menuItem title="Writing Direction" id="H1b-Si-o9J">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Writing Direction" id="8mr-sm-Yjd">
<items>
<menuItem title="Paragraph" enabled="NO" id="ZvO-Gk-QUH">
<modifierMask key="keyEquivalentModifierMask"/>
</menuItem>
<menuItem id="YGs-j5-SAR">
<string key="title"> Default</string>
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="makeBaseWritingDirectionNatural:" target="-1" id="qtV-5e-UBP"/>
</connections>
</menuItem>
<menuItem id="Lbh-J2-qVU">
<string key="title"> Left to Right</string>
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="makeBaseWritingDirectionLeftToRight:" target="-1" id="S0X-9S-QSf"/>
</connections>
</menuItem>
<menuItem id="jFq-tB-4Kx">
<string key="title"> Right to Left</string>
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="makeBaseWritingDirectionRightToLeft:" target="-1" id="5fk-qB-AqJ"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="swp-gr-a21"/>
<menuItem title="Selection" enabled="NO" id="cqv-fj-IhA">
<modifierMask key="keyEquivalentModifierMask"/>
</menuItem>
<menuItem id="Nop-cj-93Q">
<string key="title"> Default</string>
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="makeTextWritingDirectionNatural:" target="-1" id="lPI-Se-ZHp"/>
</connections>
</menuItem>
<menuItem id="BgM-ve-c93">
<string key="title"> Left to Right</string>
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="makeTextWritingDirectionLeftToRight:" target="-1" id="caW-Bv-w94"/>
</connections>
</menuItem>
<menuItem id="RB4-Sm-HuC">
<string key="title"> Right to Left</string>
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="makeTextWritingDirectionRightToLeft:" target="-1" id="EXD-6r-ZUu"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem isSeparatorItem="YES" id="fKy-g9-1gm"/>
<menuItem title="Show Ruler" id="vLm-3I-IUL">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleRuler:" target="-1" id="FOx-HJ-KwY"/>
</connections>
</menuItem>
<menuItem title="Copy Ruler" keyEquivalent="c" id="MkV-Pr-PK5">
<modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/>
<connections>
<action selector="copyRuler:" target="-1" id="71i-fW-3W2"/>
</connections>
</menuItem>
<menuItem title="Paste Ruler" keyEquivalent="v" id="LVM-kO-fVI">
<modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/>
<connections>
<action selector="pasteRuler:" target="-1" id="cSh-wd-qM2"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="View" id="H8h-7b-M4v">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="View" id="HyV-fh-RgO">
<items>
<menuItem title="Show Toolbar" keyEquivalent="t" id="snW-S8-Cw5">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections>
<action selector="toggleToolbarShown:" target="-1" id="BXY-wc-z0C"/>
</connections>
</menuItem>
<menuItem title="Customize Toolbar…" id="1UK-8n-QPP">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="runToolbarCustomizationPalette:" target="-1" id="pQI-g3-MTW"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Window" id="aUF-d1-5bR">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Window" systemMenu="window" id="Td7-aD-5lo">
<items>
<menuItem title="Minimize" keyEquivalent="m" id="OY7-WF-poV">
<connections>
<action selector="performMiniaturize:" target="-1" id="VwT-WD-YPe"/>
</connections>
</menuItem>
<menuItem title="Zoom" id="R4o-n2-Eq4">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="performZoom:" target="-1" id="DIl-cC-cCs"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="eu3-7i-yIM"/>
<menuItem title="Bring All to Front" id="LE2-aR-0XJ">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="arrangeInFront:" target="-1" id="DRN-fu-gQh"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Help" id="wpr-3q-Mcd">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Help" systemMenu="help" id="F2S-fz-NVQ">
<items>
<menuItem title="RootlessForEmulators Help" keyEquivalent="?" id="FKE-Sm-Kum">
<connections>
<action selector="showHelp:" target="-1" id="y7X-2Q-9no"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
</items>
</menu>
</objects>
</document>

View File

@ -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 <stdio.h>
#include <memory.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdbool.h>
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;
}

View File

@ -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"
}
}

View File

@ -0,0 +1,34 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIconFile</key>
<string></string>
<key>CFBundleIdentifier</key>
<string>com.thevoidsoftware.$(PRODUCT_NAME:rfc1034identifier)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>LSMinimumSystemVersion</key>
<string>$(MACOSX_DEPLOYMENT_TARGET)</string>
<key>NSHumanReadableCopyright</key>
<string>Copyright © 2015 Uli Kusterer. All rights reserved.</string>
<key>NSMainNibFile</key>
<string>MainMenu</string>
<key>NSPrincipalClass</key>
<string>NSApplication</string>
</dict>
</plist>

View File

@ -0,0 +1,24 @@
//
// RootlessForEmulators.h
// RootlessForEmulators
//
// Created by Uli Kusterer on 08/03/15.
// Copyright (c) 2015 Uli Kusterer. All rights reserved.
//
#include <stdbool.h>
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.

View File

@ -0,0 +1,13 @@
//
// main.m
// RootlessForEmulators
//
// Created by Uli Kusterer on 08/03/15.
// Copyright (c) 2015 Uli Kusterer. All rights reserved.
//
#import <Cocoa/Cocoa.h>
int main(int argc, const char * argv[]) {
return NSApplicationMain(argc, argv);
}