mirror of https://github.com/bsnes-emu/bsnes.git
466 lines
14 KiB
C++
Executable File
466 lines
14 KiB
C++
Executable File
#if defined(Hiro_Window)
|
|
|
|
@implementation CocoaWindow : NSWindow
|
|
|
|
-(id) initWith:(hiro::mWindow&)windowReference {
|
|
window = &windowReference;
|
|
|
|
NSUInteger style = NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask;
|
|
if(window->state.resizable) style |= NSResizableWindowMask;
|
|
|
|
if(self = [super initWithContentRect:NSMakeRect(0, 0, 640, 480) styleMask:style backing:NSBackingStoreBuffered defer:YES]) {
|
|
[self setDelegate:self];
|
|
[self setReleasedWhenClosed:NO];
|
|
[self setAcceptsMouseMovedEvents:YES];
|
|
[self setTitle:@""];
|
|
|
|
NSBundle* bundle = [NSBundle mainBundle];
|
|
NSDictionary* dictionary = [bundle infoDictionary];
|
|
NSString* applicationName = [dictionary objectForKey:@"CFBundleDisplayName"];
|
|
string hiroName = hiro::Application::state().name ? hiro::Application::state().name : string{"hiro"};
|
|
if(applicationName == nil) applicationName = [NSString stringWithUTF8String:hiroName];
|
|
|
|
menuBar = [[NSMenu alloc] init];
|
|
|
|
NSMenuItem* item;
|
|
string text;
|
|
|
|
rootMenu = [[NSMenu alloc] init];
|
|
item = [[[NSMenuItem alloc] initWithTitle:@"" action:nil keyEquivalent:@""] autorelease];
|
|
[item setSubmenu:rootMenu];
|
|
[menuBar addItem:item];
|
|
|
|
item = [[[NSMenuItem alloc] initWithTitle:[NSString stringWithFormat:@"About %@ ...", applicationName] action:@selector(menuAbout) keyEquivalent:@""] autorelease];
|
|
[item setTarget:self];
|
|
[rootMenu addItem:item];
|
|
[rootMenu addItem:[NSMenuItem separatorItem]];
|
|
|
|
item = [[[NSMenuItem alloc] initWithTitle:@"Preferences ..." action:@selector(menuPreferences) keyEquivalent:@""] autorelease];
|
|
[item setTarget:self];
|
|
[rootMenu addItem:item];
|
|
|
|
string result = nall::execute("spctl", "--status").output.strip();
|
|
if(result != "assessments disabled") {
|
|
disableGatekeeper = [[[NSMenuItem alloc] initWithTitle:@"Disable Gatekeeper" action:@selector(menuDisableGatekeeper) keyEquivalent:@""] autorelease];
|
|
[disableGatekeeper setTarget:self];
|
|
[rootMenu addItem:disableGatekeeper];
|
|
}
|
|
|
|
[rootMenu addItem:[NSMenuItem separatorItem]];
|
|
|
|
NSMenu* servicesMenu = [[[NSMenu alloc] initWithTitle:@"Services"] autorelease];
|
|
item = [[[NSMenuItem alloc] initWithTitle:@"Services" action:nil keyEquivalent:@""] autorelease];
|
|
[item setTarget:self];
|
|
[item setSubmenu:servicesMenu];
|
|
[rootMenu addItem:item];
|
|
[rootMenu addItem:[NSMenuItem separatorItem]];
|
|
[NSApp setServicesMenu:servicesMenu];
|
|
|
|
item = [[[NSMenuItem alloc] initWithTitle:[NSString stringWithFormat:@"Hide %@", applicationName] action:@selector(hide:) keyEquivalent:@""] autorelease];
|
|
[item setTarget:NSApp];
|
|
[rootMenu addItem:item];
|
|
|
|
item = [[[NSMenuItem alloc] initWithTitle:@"Hide Others" action:@selector(hideOtherApplications:) keyEquivalent:@""] autorelease];
|
|
[item setTarget:NSApp];
|
|
[rootMenu addItem:item];
|
|
|
|
item = [[[NSMenuItem alloc] initWithTitle:@"Show All" action:@selector(unhideAllApplications:) keyEquivalent:@""] autorelease];
|
|
[item setTarget:NSApp];
|
|
[rootMenu addItem:item];
|
|
|
|
[rootMenu addItem:[NSMenuItem separatorItem]];
|
|
|
|
item = [[[NSMenuItem alloc] initWithTitle:[NSString stringWithFormat:@"Quit %@", applicationName] action:@selector(menuQuit) keyEquivalent:@""] autorelease];
|
|
[item setTarget:self];
|
|
[rootMenu addItem:item];
|
|
|
|
statusBar = [[NSTextField alloc] initWithFrame:NSMakeRect(0, 0, 0, 0)];
|
|
[statusBar setAlignment:NSLeftTextAlignment];
|
|
[statusBar setBordered:YES];
|
|
[statusBar setBezeled:YES];
|
|
[statusBar setBezelStyle:NSTextFieldSquareBezel];
|
|
[statusBar setEditable:NO];
|
|
[statusBar setHidden:YES];
|
|
|
|
[[self contentView] addSubview:statusBar positioned:NSWindowBelow relativeTo:nil];
|
|
}
|
|
|
|
return self;
|
|
}
|
|
|
|
-(BOOL) canBecomeKeyWindow {
|
|
return YES;
|
|
}
|
|
|
|
-(BOOL) canBecomeMainWindow {
|
|
return YES;
|
|
}
|
|
|
|
-(void) windowDidBecomeMain:(NSNotification*)notification {
|
|
if(window->state.menuBar) {
|
|
[NSApp setMainMenu:menuBar];
|
|
}
|
|
}
|
|
|
|
-(void) windowDidMove:(NSNotification*)notification {
|
|
if(auto p = window->self()) p->moveEvent();
|
|
}
|
|
|
|
-(void) windowDidResize:(NSNotification*)notification {
|
|
if(auto p = window->self()) p->sizeEvent();
|
|
}
|
|
|
|
-(BOOL) windowShouldClose:(id)sender {
|
|
if(window->state.onClose) window->doClose();
|
|
else window->setVisible(false);
|
|
if(window->state.modal && !window->visible()) window->setModal(false);
|
|
return NO;
|
|
}
|
|
|
|
-(NSDragOperation) draggingEntered:(id<NSDraggingInfo>)sender {
|
|
return DropPathsOperation(sender);
|
|
}
|
|
|
|
-(BOOL) performDragOperation:(id<NSDraggingInfo>)sender {
|
|
auto paths = DropPaths(sender);
|
|
if(!paths) return NO;
|
|
window->doDrop(paths);
|
|
return YES;
|
|
}
|
|
|
|
-(NSMenu*) menuBar {
|
|
return menuBar;
|
|
}
|
|
|
|
-(void) menuAbout {
|
|
hiro::Application::Cocoa::doAbout();
|
|
}
|
|
|
|
-(void) menuPreferences {
|
|
hiro::Application::Cocoa::doPreferences();
|
|
}
|
|
|
|
//to hell with gatekeepers
|
|
-(void) menuDisableGatekeeper {
|
|
NSAlert* alert = [[[NSAlert alloc] init] autorelease];
|
|
[alert setMessageText:@"Disable Gatekeeper"];
|
|
|
|
AuthorizationRef authorization;
|
|
OSStatus status = AuthorizationCreate(nullptr, kAuthorizationEmptyEnvironment, kAuthorizationFlagDefaults, &authorization);
|
|
if(status == errAuthorizationSuccess) {
|
|
AuthorizationItem items = {kAuthorizationRightExecute, 0, nullptr, 0};
|
|
AuthorizationRights rights = {1, &items};
|
|
status = AuthorizationCopyRights(authorization, &rights, nullptr,
|
|
kAuthorizationFlagDefaults
|
|
| kAuthorizationFlagInteractionAllowed
|
|
| kAuthorizationFlagPreAuthorize
|
|
| kAuthorizationFlagExtendRights, nullptr);
|
|
if(status == errAuthorizationSuccess) {
|
|
{ char program[] = "/usr/sbin/spctl";
|
|
char* arguments[] = {"--master-disable", nullptr};
|
|
FILE* pipe = nullptr;
|
|
AuthorizationExecuteWithPrivileges(authorization, program, kAuthorizationFlagDefaults, arguments, &pipe);
|
|
}
|
|
{ char program[] = "/usr/bin/defaults";
|
|
char* arguments[] = {"write /Library/Preferences/com.apple.security GKAutoRearm -bool NO"};
|
|
FILE* pipe = nullptr;
|
|
AuthorizationExecuteWithPrivileges(authorization, program, kAuthorizationFlagDefaults, arguments, &pipe);
|
|
}
|
|
}
|
|
AuthorizationFree(authorization, kAuthorizationFlagDefaults);
|
|
}
|
|
|
|
string result = nall::execute("spctl", "--status").output.strip();
|
|
if(result == "assessments disabled") {
|
|
[alert setAlertStyle:NSInformationalAlertStyle];
|
|
[alert setInformativeText:@"Gatekeeper has been successfully disabled."];
|
|
[disableGatekeeper setHidden:YES];
|
|
} else {
|
|
[alert setAlertStyle:NSWarningAlertStyle];
|
|
[alert setInformativeText:@"Error: failed to disable Gatekeeper."];
|
|
}
|
|
|
|
[alert addButtonWithTitle:@"Ok"];
|
|
[alert runModal];
|
|
}
|
|
|
|
-(void) menuQuit {
|
|
hiro::Application::Cocoa::doQuit();
|
|
}
|
|
|
|
-(NSTextField*) statusBar {
|
|
return statusBar;
|
|
}
|
|
|
|
@end
|
|
|
|
namespace hiro {
|
|
|
|
auto pWindow::construct() -> void {
|
|
@autoreleasepool {
|
|
cocoaWindow = [[CocoaWindow alloc] initWith:self()];
|
|
|
|
static bool once = true;
|
|
if(once) {
|
|
once = false;
|
|
[NSApp setMainMenu:[cocoaWindow menuBar]];
|
|
}
|
|
}
|
|
}
|
|
|
|
auto pWindow::destruct() -> void {
|
|
@autoreleasepool {
|
|
[cocoaWindow release];
|
|
}
|
|
}
|
|
|
|
auto pWindow::append(sMenuBar menuBar) -> void {
|
|
}
|
|
|
|
auto pWindow::append(sSizable sizable) -> void {
|
|
sizable->setGeometry(self().geometry().setPosition());
|
|
statusBarReposition();
|
|
}
|
|
|
|
auto pWindow::append(sStatusBar statusBar) -> void {
|
|
statusBar->setEnabled(statusBar->enabled(true));
|
|
statusBar->setFont(statusBar->font(true));
|
|
statusBar->setText(statusBar->text());
|
|
statusBar->setVisible(statusBar->visible(true));
|
|
}
|
|
|
|
auto pWindow::focused() const -> bool {
|
|
@autoreleasepool {
|
|
return [cocoaWindow isMainWindow] == YES;
|
|
}
|
|
}
|
|
|
|
auto pWindow::frameMargin() const -> Geometry {
|
|
@autoreleasepool {
|
|
NSRect frame = [cocoaWindow frameRectForContentRect:NSMakeRect(0, 0, 640, 480)];
|
|
return {abs(frame.origin.x), (int)(frame.size.height - 480), (int)(frame.size.width - 640), abs(frame.origin.y)};
|
|
}
|
|
}
|
|
|
|
auto pWindow::handle() const -> uintptr_t {
|
|
return (uintptr_t)cocoaWindow;
|
|
}
|
|
|
|
auto pWindow::monitor() const -> uint {
|
|
//TODO
|
|
return 0;
|
|
}
|
|
|
|
auto pWindow::remove(sMenuBar menuBar) -> void {
|
|
}
|
|
|
|
auto pWindow::remove(sSizable sizable) -> void {
|
|
@autoreleasepool {
|
|
[[cocoaWindow contentView] setNeedsDisplay:YES];
|
|
}
|
|
}
|
|
|
|
auto pWindow::remove(sStatusBar statusBar) -> void {
|
|
@autoreleasepool {
|
|
[[cocoaWindow statusBar] setHidden:YES];
|
|
}
|
|
}
|
|
|
|
auto pWindow::setBackgroundColor(Color color) -> void {
|
|
@autoreleasepool {
|
|
[cocoaWindow
|
|
setBackgroundColor:[NSColor
|
|
colorWithCalibratedRed:color.red() / 255.0
|
|
green:color.green() / 255.0
|
|
blue:color.blue() / 255.0
|
|
alpha:color.alpha() / 255.0
|
|
]
|
|
];
|
|
}
|
|
}
|
|
|
|
auto pWindow::setDismissable(bool dismissable) -> void {
|
|
//todo: not implemented
|
|
}
|
|
|
|
auto pWindow::setDroppable(bool droppable) -> void {
|
|
@autoreleasepool {
|
|
if(droppable) {
|
|
[cocoaWindow registerForDraggedTypes:[NSArray arrayWithObject:NSFilenamesPboardType]];
|
|
} else {
|
|
[cocoaWindow unregisterDraggedTypes];
|
|
}
|
|
}
|
|
}
|
|
|
|
auto pWindow::setFocused() -> void {
|
|
@autoreleasepool {
|
|
[cocoaWindow makeKeyAndOrderFront:nil];
|
|
}
|
|
}
|
|
|
|
auto pWindow::setFullScreen(bool fullScreen) -> void {
|
|
@autoreleasepool {
|
|
if(fullScreen) {
|
|
windowedGeometry = state().geometry;
|
|
[NSApp setPresentationOptions:NSApplicationPresentationFullScreen];
|
|
[cocoaWindow setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary];
|
|
[cocoaWindow toggleFullScreen:nil];
|
|
state().geometry = _geometry();
|
|
} else {
|
|
[cocoaWindow toggleFullScreen:nil];
|
|
[cocoaWindow setCollectionBehavior:NSWindowCollectionBehaviorDefault];
|
|
[NSApp setPresentationOptions:NSApplicationPresentationDefault];
|
|
state().geometry = windowedGeometry;
|
|
}
|
|
}
|
|
}
|
|
|
|
auto pWindow::setGeometry(Geometry geometry) -> void {
|
|
lock();
|
|
|
|
@autoreleasepool {
|
|
[cocoaWindow
|
|
setFrame:[cocoaWindow
|
|
frameRectForContentRect:NSMakeRect(
|
|
geometry.x(), Desktop::size().height() - geometry.y() - geometry.height(),
|
|
geometry.width(), geometry.height() + statusBarHeight()
|
|
)
|
|
]
|
|
display:YES
|
|
];
|
|
|
|
if(auto& sizable = state().sizable) {
|
|
sizable->setGeometry(self().geometry().setPosition());
|
|
}
|
|
|
|
statusBarReposition();
|
|
}
|
|
|
|
unlock();
|
|
}
|
|
|
|
auto pWindow::setMaximized(bool maximized) -> void {
|
|
//todo
|
|
}
|
|
|
|
auto pWindow::setMaximumSize(Size size) -> void {
|
|
//todo
|
|
}
|
|
|
|
auto pWindow::setMinimized(bool minimized) -> void {
|
|
//todo
|
|
}
|
|
|
|
auto pWindow::setMinimumSize(Size size) -> void {
|
|
//todo
|
|
}
|
|
|
|
auto pWindow::setModal(bool modal) -> void {
|
|
@autoreleasepool {
|
|
if(modal == true) {
|
|
[NSApp runModalForWindow:cocoaWindow];
|
|
} else {
|
|
[NSApp stopModal];
|
|
NSEvent* event = [NSEvent otherEventWithType:NSApplicationDefined location:NSMakePoint(0, 0) modifierFlags:0 timestamp:0.0 windowNumber:0 context:nil subtype:0 data1:0 data2:0];
|
|
[NSApp postEvent:event atStart:true];
|
|
}
|
|
}
|
|
}
|
|
|
|
auto pWindow::setResizable(bool resizable) -> void {
|
|
@autoreleasepool {
|
|
NSUInteger style = NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask;
|
|
if(resizable) style |= NSResizableWindowMask;
|
|
[cocoaWindow setStyleMask:style];
|
|
}
|
|
}
|
|
|
|
auto pWindow::setTitle(const string& text) -> void {
|
|
@autoreleasepool {
|
|
[cocoaWindow setTitle:[NSString stringWithUTF8String:text]];
|
|
}
|
|
}
|
|
|
|
auto pWindow::setVisible(bool visible) -> void {
|
|
@autoreleasepool {
|
|
if(visible) [cocoaWindow makeKeyAndOrderFront:nil];
|
|
else [cocoaWindow orderOut:nil];
|
|
}
|
|
}
|
|
|
|
auto pWindow::moveEvent() -> void {
|
|
if(!locked() && !self().fullScreen() && self().visible()) {
|
|
@autoreleasepool {
|
|
NSRect area = [cocoaWindow contentRectForFrameRect:[cocoaWindow frame]];
|
|
area.size.height -= statusBarHeight();
|
|
state().geometry.setX(area.origin.x);
|
|
state().geometry.setY(Desktop::size().height() - area.origin.y - area.size.height);
|
|
}
|
|
}
|
|
|
|
if(!locked()) self().doMove();
|
|
}
|
|
|
|
auto pWindow::sizeEvent() -> void {
|
|
if(!locked() && !self().fullScreen() && self().visible()) {
|
|
@autoreleasepool {
|
|
NSRect area = [cocoaWindow contentRectForFrameRect:[cocoaWindow frame]];
|
|
area.size.height -= statusBarHeight();
|
|
state().geometry.setWidth(area.size.width);
|
|
state().geometry.setHeight(area.size.height);
|
|
}
|
|
}
|
|
|
|
if(auto& sizable = state().sizable) {
|
|
sizable->setGeometry(self().geometry().setPosition());
|
|
}
|
|
|
|
statusBarReposition();
|
|
|
|
if(!locked()) self().doSize();
|
|
}
|
|
|
|
auto pWindow::statusBarHeight() -> uint {
|
|
if(auto& statusBar = state().statusBar) {
|
|
if(statusBar->visible()) {
|
|
return pFont::size(statusBar->font(true), " ").height() + 6;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
auto pWindow::statusBarReposition() -> void {
|
|
@autoreleasepool {
|
|
NSRect area = [cocoaWindow contentRectForFrameRect:[cocoaWindow frame]];
|
|
[[cocoaWindow statusBar] setFrame:NSMakeRect(0, 0, area.size.width, statusBarHeight())];
|
|
[[cocoaWindow contentView] setNeedsDisplay:YES];
|
|
}
|
|
}
|
|
|
|
auto pWindow::_append(mWidget& widget) -> void {
|
|
@autoreleasepool {
|
|
if(auto pWidget = widget.self()) {
|
|
[pWidget->cocoaView removeFromSuperview];
|
|
[[cocoaWindow contentView] addSubview:pWidget->cocoaView positioned:NSWindowAbove relativeTo:nil];
|
|
pWidget->setGeometry(widget.geometry());
|
|
[[cocoaWindow contentView] setNeedsDisplay:YES];
|
|
}
|
|
}
|
|
}
|
|
|
|
auto pWindow::_geometry() -> Geometry {
|
|
@autoreleasepool {
|
|
NSRect area = [cocoaWindow contentRectForFrameRect:[cocoaWindow frame]];
|
|
area.size.height -= statusBarHeight();
|
|
return {
|
|
(int)area.origin.x, (int)(Monitor::geometry(Monitor::primary()).height() - area.origin.y - area.size.height),
|
|
(int)area.size.width, (int)area.size.height
|
|
};
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|