From 694ffcad285a04795705c7b0da7100051c9163f6 Mon Sep 17 00:00:00 2001 From: fuzzard Date: Mon, 20 Sep 2021 17:46:42 +1000 Subject: [osx] relocate SDLMain.mm Relocate SDLMain to explicit SDL folder --- cmake/platform/osx/osx.cmake | 2 +- xbmc/platform/darwin/osx/SDL/SDLMain.mm | 574 +++++++++++++++++++++++++++++++ xbmc/platform/darwin/osx/SDLMain.mm | 575 -------------------------------- 3 files changed, 575 insertions(+), 576 deletions(-) create mode 100644 xbmc/platform/darwin/osx/SDL/SDLMain.mm delete mode 100644 xbmc/platform/darwin/osx/SDLMain.mm diff --git a/cmake/platform/osx/osx.cmake b/cmake/platform/osx/osx.cmake index 433ac687ce..f2d58a39c0 100644 --- a/cmake/platform/osx/osx.cmake +++ b/cmake/platform/osx/osx.cmake @@ -9,7 +9,7 @@ endif() if(NOT APP_WINDOW_SYSTEM OR APP_WINDOW_SYSTEM STREQUAL sdl) list(APPEND SYSTEM_DEFINES -DHAS_SDL) list(APPEND PLATFORM_REQUIRED_DEPS Sdl) - list(APPEND CORE_MAIN_SOURCE ${CMAKE_SOURCE_DIR}/xbmc/platform/darwin/osx/SDLMain.mm + list(APPEND CORE_MAIN_SOURCE ${CMAKE_SOURCE_DIR}/xbmc/platform/darwin/osx/SDL/SDLMain.mm ${CMAKE_SOURCE_DIR}/xbmc/platform/darwin/osx/SDLMain.h) else() message(SEND_ERROR "Currently only SDL windowing is supported. Please set APP_WINDOW_SYSTEM to \"sdl\"") diff --git a/xbmc/platform/darwin/osx/SDL/SDLMain.mm b/xbmc/platform/darwin/osx/SDL/SDLMain.mm new file mode 100644 index 0000000000..74be33bbac --- /dev/null +++ b/xbmc/platform/darwin/osx/SDL/SDLMain.mm @@ -0,0 +1,574 @@ +/* + * SDLMain.mm - main entry point for our Cocoa-ized SDL app + * Initial Version: Darrell Walisser + * Non-NIB-Code & other changes: Max Horn + * + * SPDX-License-Identifier: Unlicense + * See LICENSES/README.md for more information. + */ + +#import "messaging/ApplicationMessenger.h" + +#import "platform/darwin/osx/CocoaInterface.h" +#import "platform/darwin/osx/HotKeyController.h" +#import "platform/darwin/osx/SDLMain.h" +#import "platform/darwin/osx/storage/OSXStorageProvider.h" + +#import +#import /* for MAXPATHLEN */ +#import + +#import "PlatformDefs.h" + +// For some reason, Apple removed setAppleMenu from the headers in 10.4, +// but the method still is there and works. To avoid warnings, we declare +// it ourselves here. +@interface NSApplication(SDL_Missing_Methods) +- (void)setAppleMenu:(NSMenu *)menu; +@end + +// Use this flag to determine whether we use CPS (docking) or not +#define SDL_USE_CPS 1 +#ifdef SDL_USE_CPS +// Portions of CPS.h +typedef struct CPSProcessSerNum +{ + UInt32 lo; + UInt32 hi; +} CPSProcessSerNum; + +extern "C" { +extern OSErr CPSGetCurrentProcess(CPSProcessSerNum *psn); +extern OSErr CPSEnableForegroundOperation(CPSProcessSerNum *psn, UInt32 _arg2, UInt32 _arg3, UInt32 _arg4, UInt32 _arg5); +extern OSErr CPSSetFrontProcess(CPSProcessSerNum *psn); +} +#endif /* SDL_USE_CPS */ + +static int gArgc; +static char **gArgv; +static BOOL gCalledAppMainline = FALSE; + +static NSString *getApplicationName(void) +{ + NSDictionary *dict; + NSString *appName = 0; + + // Determine the application name + dict = (NSDictionary *)CFBundleGetInfoDictionary(CFBundleGetMainBundle()); + if (dict) + appName = [dict objectForKey: @"CFBundleName"]; + + if (![appName length]) + appName = [[NSProcessInfo processInfo] processName]; + + return appName; +} +static void setupApplicationMenu(void) +{ + // warning: this code is very odd + NSMenu *appleMenu; + NSMenuItem *menuItem; + NSString *title; + NSString *appName; + + appName = getApplicationName(); + appleMenu = [[NSMenu alloc] initWithTitle:@""]; + + // Add menu items + title = [@"About " stringByAppendingString:appName]; + [appleMenu addItemWithTitle:title action:@selector(orderFrontStandardAboutPanel:) keyEquivalent:@""]; + + [appleMenu addItem:[NSMenuItem separatorItem]]; + + title = [@"Hide " stringByAppendingString:appName]; + [appleMenu addItemWithTitle:title action:@selector(hide:) keyEquivalent:@"h"]; + + menuItem = (NSMenuItem *)[appleMenu addItemWithTitle:@"Hide Others" action:@selector(hideOtherApplications:) keyEquivalent:@"h"]; + [menuItem setKeyEquivalentModifierMask:(NSEventModifierFlagOption|NSEventModifierFlagCommand)]; + + [appleMenu addItemWithTitle:@"Show All" action:@selector(unhideAllApplications:) keyEquivalent:@""]; + + [appleMenu addItem:[NSMenuItem separatorItem]]; + + title = [@"Quit " stringByAppendingString:appName]; + [appleMenu addItemWithTitle:title action:@selector(terminate:) keyEquivalent:@"q"]; + + + // Put menu into the menubar + menuItem = [[NSMenuItem alloc] initWithTitle:@"" action:nil keyEquivalent:@""]; + [menuItem setSubmenu:appleMenu]; + [[NSApp mainMenu] addItem:menuItem]; + + // Tell the application object that this is now the application menu + [NSApp setAppleMenu:appleMenu]; +} + +// Create a window menu +static void setupWindowMenu(void) +{ + NSMenu *windowMenu; + NSMenuItem *windowMenuItem; + NSMenuItem *menuItem; + + windowMenu = [[NSMenu alloc] initWithTitle:@"Window"]; + + // "Full/Windowed Toggle" item + menuItem = [[NSMenuItem alloc] initWithTitle:@"Full/Windowed Toggle" action:@selector(fullScreenToggle:) keyEquivalent:@"f"]; + // this is just for display purposes, key handling is in CWinEventsSDL::ProcessOSXShortcuts() + menuItem.keyEquivalentModifierMask = NSEventModifierFlagCommand | NSEventModifierFlagControl; + [windowMenu addItem:menuItem]; + + // "Full/Windowed Toggle" item + menuItem = [[NSMenuItem alloc] initWithTitle:@"Float on Top" action:@selector(floatOnTopToggle:) keyEquivalent:@"t"]; + [windowMenu addItem:menuItem]; + + // "Minimize" item + menuItem = [[NSMenuItem alloc] initWithTitle:@"Minimize" action:@selector(performMiniaturize:) keyEquivalent:@"m"]; + [windowMenu addItem:menuItem]; + + // "Title Bar" item + menuItem = [[NSMenuItem alloc] initWithTitle:@"Title Bar" action:@selector(titlebarToggle:) keyEquivalent:@""]; + [windowMenu addItem:menuItem]; + [menuItem setState: true]; + + // Put menu into the menubar + windowMenuItem = [[NSMenuItem alloc] initWithTitle:@"Window" action:nil keyEquivalent:@""]; + [windowMenuItem setSubmenu:windowMenu]; + [[NSApp mainMenu] addItem:windowMenuItem]; + + // Tell the application object that this is now the window menu + [NSApp setWindowsMenu:windowMenu]; +} + +@interface XBMCApplication : NSApplication +@end + +@implementation XBMCApplication + +// Called before the internal event loop has started running. +- (void) finishLaunching +{ + [super finishLaunching]; +} + +// Invoked from the Quit menu item +- (void)terminate:(id)sender +{ + // remove any notification handlers + [[[NSWorkspace sharedWorkspace] notificationCenter] removeObserver:self]; + [[NSNotificationCenter defaultCenter] removeObserver:self]; + + // Post a SDL_QUIT event + SDL_Event event; + event.type = SDL_QUIT; + SDL_PushEvent(&event); +} + +- (void)fullScreenToggle:(id)sender +{ + // Post an toggle full-screen event to the application thread. + SDL_Event event; + memset(&event, 0, sizeof(event)); + event.type = SDL_USEREVENT; + event.user.code = TMSG_TOGGLEFULLSCREEN; + SDL_PushEvent(&event); +} + +- (void)floatOnTopToggle:(id)sender +{ + NSWindow* window = [[[NSOpenGLContext currentContext] view] window]; + if ([window level] == NSFloatingWindowLevel) + { + [window setLevel:NSNormalWindowLevel]; + [sender setState:NSOffState]; + } + else + { + [window setLevel:NSFloatingWindowLevel]; + [sender setState:NSOnState]; + } +} + +- (void)titlebarToggle:(id)sender +{ + NSWindow* window = [[[NSOpenGLContext currentContext] view] window]; + [window setStyleMask: [window styleMask] ^ NSWindowStyleMaskTitled ]; + BOOL isSet = [window styleMask] & NSWindowStyleMaskTitled; + [window setMovableByWindowBackground: !isSet]; + [sender setState: isSet]; + +} + + +@end + +// The main class of the application, the application's delegate +@implementation XBMCDelegate + +// Set the working directory to the .app's parent directory +- (void) setupWorkingDirectory +{ + char parentdir[MAXPATHLEN]; + CFURLRef url = CFBundleCopyBundleURL(CFBundleGetMainBundle()); + CFURLRef url2 = CFURLCreateCopyDeletingLastPathComponent(0, url); + if (CFURLGetFileSystemRepresentation(url2, true, (UInt8 *)parentdir, MAXPATHLEN)) + { + assert( chdir (parentdir) == 0 ); /* chdir to the binary app's parent */ + } + CFRelease(url); + CFRelease(url2); +} + +// Doesnt currently get called. Keep in case we fix the root cause +- (void) applicationWillTerminate: (NSNotification *) note +{ + // disabled as currently main() explicitly calls this. + // [self appshutdownCleanup]; +} + +- (void) applicationWillResignActive:(NSNotification *) note +{ + //[[HotKeyController sharedController] sysPower:NO]; + //[[HotKeyController sharedController] sysVolume:NO]; + [[HotKeyController sharedController] setActive:NO]; +} + +- (void) applicationWillBecomeActive:(NSNotification *) note +{ + //[[HotKeyController sharedController] sysPower:YES]; + //[[HotKeyController sharedController] sysVolume:YES]; + [[HotKeyController sharedController] setActive:YES]; +} + +// To use Cocoa on secondary POSIX threads, your application must first detach +// at least one NSThread object, which can immediately exit. Some info says this +// is not required anymore, who knows ? +- (void) kickstartMultiThreaded:(id)arg +{ + @autoreleasepool + { + // empty + } +} + +- (void)appshutdownCleanup +{ + [[[NSWorkspace sharedWorkspace] notificationCenter] removeObserver:self + name:NSWorkspaceDidMountNotification + object:nil]; + + [[[NSWorkspace sharedWorkspace] notificationCenter] + removeObserver:self + name:NSWorkspaceDidUnmountNotification + object:nil]; + + NSNotificationCenter* center = [NSNotificationCenter defaultCenter]; + + [center removeObserver:self name:MediaKeyPower object:nil]; + [center removeObserver:self name:MediaKeySoundMute object:nil]; + [center removeObserver:self name:MediaKeySoundUp object:nil]; + [center removeObserver:self name:MediaKeySoundDown object:nil]; + [center removeObserver:self name:MediaKeyPlayPauseNotification object:nil]; + [center removeObserver:self name:MediaKeyFastNotification object:nil]; + [center removeObserver:self name:MediaKeyRewindNotification object:nil]; + [center removeObserver:self name:MediaKeyNextNotification object:nil]; + [center removeObserver:self name:MediaKeyPreviousNotification object:nil]; + + [[HotKeyController sharedController] disableTap]; +} + +// Called after the internal event loop has started running. +- (void) applicationDidFinishLaunching: (NSNotification *) note +{ + // enable multithreading, we should NOT have to do this but as we are mixing NSThreads/pthreads... + if (![NSThread isMultiThreaded]) + [NSThread detachNewThreadSelector:@selector(kickstartMultiThreaded:) toTarget:self withObject:nil]; + + // Set the working directory to the .app's parent directory + [self setupWorkingDirectory]; + + [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self + selector:@selector(deviceDidMountNotification:) + name:NSWorkspaceDidMountNotification + object:nil]; + + [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self + selector:@selector(deviceDidUnMountNotification:) + name:NSWorkspaceDidUnmountNotification + object:nil]; + + NSNotificationCenter *center = [NSNotificationCenter defaultCenter]; + + // create media key handler singleton + [[HotKeyController sharedController] enableTap]; + // add media key notifications + [center addObserver:self + selector:@selector(powerKeyNotification) + name:MediaKeyPower object:nil]; + [center addObserver:self + selector:@selector(muteKeyNotification) + name:MediaKeySoundMute object:nil]; + [center addObserver:self + selector:@selector(soundUpKeyNotification) + name:MediaKeySoundUp object:nil]; + [center addObserver:self + selector:@selector(soundDownKeyNotification) + name:MediaKeySoundDown object:nil]; + [center addObserver:self + selector:@selector(playPauseKeyNotification) + name:MediaKeyPlayPauseNotification object:nil]; + [center addObserver:self + selector:@selector(fastKeyNotification) + name:MediaKeyFastNotification object:nil]; + [center addObserver:self + selector:@selector(rewindKeyNotification) + name:MediaKeyRewindNotification object:nil]; + [center addObserver:self + selector:@selector(nextKeyNotification) + name:MediaKeyNextNotification object:nil]; + [center addObserver:self + selector:@selector(previousKeyNotification) + name:MediaKeyPreviousNotification object:nil]; + + // We're going to manually manage the screensaver. + setenv("SDL_VIDEO_ALLOW_SCREENSAVER", "1", true); + + // Hand off to main application code + gCalledAppMainline = TRUE; + + // stop the main loop so we return to main (below) and can + // call SDL_main there. + [NSApp stop:nil]; + + //post a NOP event, so the run loop actually stops + //see http://www.cocoabuilder.com/archive/cocoa/219842-nsapp-stop.html + NSEvent* event = [NSEvent otherEventWithType: NSEventTypeApplicationDefined + location: NSMakePoint(0,0) + modifierFlags: 0 + timestamp: 0.0 + windowNumber: 0 + context: nil + subtype: 0 + data1: 0 + data2: 0]; + // + [NSApp postEvent: event atStart: true]; +} + +/* + * Catch document open requests...this lets us notice files when the app + * was launched by double-clicking a document, or when a document was + * dragged/dropped on the app's icon. You need to have a + * CFBundleDocumentsType section in your Info.plist to get this message, + * apparently. + * + * Files are added to gArgv, so to the app, they'll look like command line + * arguments. Previously, apps launched from the finder had nothing but + * an argv[0]. + * + * This message may be received multiple times to open several docs on launch. + * + * This message is ignored once the app's mainline has been called. + */ +- (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename +{ + const char *temparg; + size_t arglen; + char *arg; + char **newargv; + + // app has started, ignore this document. + if (gCalledAppMainline) + return FALSE; + + temparg = [filename UTF8String]; + arglen = SDL_strlen(temparg) + 1; + arg = (char *) SDL_malloc(arglen); + if (arg == NULL) + return FALSE; + + newargv = (char **) realloc(gArgv, sizeof (char *) * (gArgc + 2)); + if (newargv == NULL) + { + SDL_free(arg); + return FALSE; + } + gArgv = newargv; + + SDL_strlcpy(arg, temparg, arglen); + gArgv[gArgc++] = arg; + gArgv[gArgc] = NULL; + + return TRUE; +} + +- (void) deviceDidMountNotification:(NSNotification *) note +{ + // calling into c++ code, need to use autorelease pools + @autoreleasepool + { + NSString* volumeLabel = [note.userInfo objectForKey:@"NSWorkspaceVolumeLocalizedNameKey"]; + const char* label = [volumeLabel UTF8String]; + + NSString* volumePath = [note.userInfo objectForKey:@"NSDevicePath"]; + const char* path = [volumePath UTF8String]; + + COSXStorageProvider::VolumeMountNotification(label, path); + } +} + +- (void) deviceDidUnMountNotification:(NSNotification *) note +{ + // calling into c++ code, need to use autorelease pools + @autoreleasepool + { + NSString* volumeLabel = [note.userInfo objectForKey:@"NSWorkspaceVolumeLocalizedNameKey"]; + const char* label = [volumeLabel UTF8String]; + + NSString* volumePath = [note.userInfo objectForKey:@"NSDevicePath"]; + const char* path = [volumePath UTF8String]; + + COSXStorageProvider::VolumeUnmountNotification(label, path); + } +} + +static void keyPress(SDLKey key) +{ + SDL_Event event; + memset(&event, 0, sizeof(event)); + event.type = SDL_KEYDOWN; + event.key.keysym.sym = key; + SDL_PushEvent(&event); + event.type = SDL_KEYUP; + SDL_PushEvent(&event); +} + +#define VK_SLEEP 0x143 +#define VK_VOLUME_MUTE 0xAD +#define VK_VOLUME_DOWN 0xAE +#define VK_VOLUME_UP 0xAF +#define VK_MEDIA_NEXT_TRACK 0x9E +#define VK_MEDIA_PREV_TRACK 0x9D +#define VK_MEDIA_STOP 0xB2 +#define VK_MEDIA_PLAY_PAUSE 0xB3 +#define VK_REWIND 0xB1 +#define VK_FAST_FWD 0xB0 + +- (void)powerKeyNotification +{ + keyPress((SDLKey)VK_SLEEP); +} + +- (void)muteKeyNotification +{ + keyPress((SDLKey)VK_VOLUME_MUTE); +} +- (void)soundUpKeyNotification +{ + keyPress((SDLKey)VK_VOLUME_UP); +} +- (void)soundDownKeyNotification +{ + keyPress((SDLKey)VK_VOLUME_DOWN); +} + +- (void)playPauseKeyNotification +{ + keyPress((SDLKey)VK_MEDIA_PLAY_PAUSE); +} + +- (void)fastKeyNotification +{ + keyPress((SDLKey)VK_FAST_FWD); +} + +- (void)rewindKeyNotification +{ + keyPress((SDLKey)VK_REWIND); +} + +- (void)nextKeyNotification +{ + keyPress((SDLKey)VK_MEDIA_NEXT_TRACK); +} + +- (void)previousKeyNotification +{ + keyPress((SDLKey)VK_MEDIA_PREV_TRACK); +} + +@end + +#ifdef main +# undef main +#endif +/* Main entry point to executable - should *not* be SDL_main! */ +int main(int argc, char *argv[]) +{ + @autoreleasepool + { + XBMCDelegate* xbmc_delegate; + + // Block SIGPIPE + // SIGPIPE repeatably kills us, turn it off + { + sigset_t set; + sigemptyset(&set); + sigaddset(&set, SIGPIPE); + sigprocmask(SIG_BLOCK, &set, NULL); + } + + /* Copy the arguments into a global variable */ + /* This is passed if we are launched by double-clicking */ + if (argc >= 2 && strncmp(argv[1], "-psn", 4) == 0) + { + gArgv = (char**)SDL_malloc(sizeof(char*) * 2); + gArgv[0] = argv[0]; + gArgv[1] = NULL; + gArgc = 1; + } + else + { + gArgc = argc; + gArgv = (char**)SDL_malloc(sizeof(char*) * (argc + 1)); + for (int i = 0; i <= argc; i++) + gArgv[i] = argv[i]; + } + + // Ensure the application object is initialised + [XBMCApplication sharedApplication]; + +#ifdef SDL_USE_CPS + { + CPSProcessSerNum PSN; + /* Tell the dock about us */ + if (!CPSGetCurrentProcess(&PSN)) + if (!CPSEnableForegroundOperation(&PSN, 0x03, 0x3C, 0x2C, 0x1103)) + if (!CPSSetFrontProcess(&PSN)) + [XBMCApplication sharedApplication]; + } +#endif + + // Set up the menubars + [NSApp setMainMenu:[[NSMenu alloc] init]]; + setupApplicationMenu(); + setupWindowMenu(); + + // Create XBMCDelegate and make it the app delegate + xbmc_delegate = [[XBMCDelegate alloc] init]; + [[NSApplication sharedApplication] setDelegate:xbmc_delegate]; + + // Start the main event loop + [NSApp run]; + + // call SDL_main which calls our real main in xbmc.cpp + // see http://lists.libsdl.org/pipermail/sdl-libsdl.org/2008-September/066542.html + int status; + status = SDL_main(gArgc, gArgv); + SDL_Quit(); + + [xbmc_delegate appshutdownCleanup]; + + return status; + } +} diff --git a/xbmc/platform/darwin/osx/SDLMain.mm b/xbmc/platform/darwin/osx/SDLMain.mm deleted file mode 100644 index e760ebea1b..0000000000 --- a/xbmc/platform/darwin/osx/SDLMain.mm +++ /dev/null @@ -1,575 +0,0 @@ -/* - * SDLMain.mm - main entry point for our Cocoa-ized SDL app - * Initial Version: Darrell Walisser - * Non-NIB-Code & other changes: Max Horn - * - * SPDX-License-Identifier: Unlicense - * See LICENSES/README.md for more information. - */ - -#import "SDLMain.h" - -#import "messaging/ApplicationMessenger.h" - -#import "platform/darwin/osx/CocoaInterface.h" -#import "platform/darwin/osx/HotKeyController.h" -#import "platform/darwin/osx/storage/OSXStorageProvider.h" - -#import -#import /* for MAXPATHLEN */ -#import - -#import "PlatformDefs.h" - -// For some reason, Apple removed setAppleMenu from the headers in 10.4, -// but the method still is there and works. To avoid warnings, we declare -// it ourselves here. -@interface NSApplication(SDL_Missing_Methods) -- (void)setAppleMenu:(NSMenu *)menu; -@end - -// Use this flag to determine whether we use CPS (docking) or not -#define SDL_USE_CPS 1 -#ifdef SDL_USE_CPS -// Portions of CPS.h -typedef struct CPSProcessSerNum -{ - UInt32 lo; - UInt32 hi; -} CPSProcessSerNum; - -extern "C" { -extern OSErr CPSGetCurrentProcess(CPSProcessSerNum *psn); -extern OSErr CPSEnableForegroundOperation(CPSProcessSerNum *psn, UInt32 _arg2, UInt32 _arg3, UInt32 _arg4, UInt32 _arg5); -extern OSErr CPSSetFrontProcess(CPSProcessSerNum *psn); -} -#endif /* SDL_USE_CPS */ - -static int gArgc; -static char **gArgv; -static BOOL gCalledAppMainline = FALSE; - -static NSString *getApplicationName(void) -{ - NSDictionary *dict; - NSString *appName = 0; - - // Determine the application name - dict = (NSDictionary *)CFBundleGetInfoDictionary(CFBundleGetMainBundle()); - if (dict) - appName = [dict objectForKey: @"CFBundleName"]; - - if (![appName length]) - appName = [[NSProcessInfo processInfo] processName]; - - return appName; -} -static void setupApplicationMenu(void) -{ - // warning: this code is very odd - NSMenu *appleMenu; - NSMenuItem *menuItem; - NSString *title; - NSString *appName; - - appName = getApplicationName(); - appleMenu = [[NSMenu alloc] initWithTitle:@""]; - - // Add menu items - title = [@"About " stringByAppendingString:appName]; - [appleMenu addItemWithTitle:title action:@selector(orderFrontStandardAboutPanel:) keyEquivalent:@""]; - - [appleMenu addItem:[NSMenuItem separatorItem]]; - - title = [@"Hide " stringByAppendingString:appName]; - [appleMenu addItemWithTitle:title action:@selector(hide:) keyEquivalent:@"h"]; - - menuItem = (NSMenuItem *)[appleMenu addItemWithTitle:@"Hide Others" action:@selector(hideOtherApplications:) keyEquivalent:@"h"]; - [menuItem setKeyEquivalentModifierMask:(NSEventModifierFlagOption|NSEventModifierFlagCommand)]; - - [appleMenu addItemWithTitle:@"Show All" action:@selector(unhideAllApplications:) keyEquivalent:@""]; - - [appleMenu addItem:[NSMenuItem separatorItem]]; - - title = [@"Quit " stringByAppendingString:appName]; - [appleMenu addItemWithTitle:title action:@selector(terminate:) keyEquivalent:@"q"]; - - - // Put menu into the menubar - menuItem = [[NSMenuItem alloc] initWithTitle:@"" action:nil keyEquivalent:@""]; - [menuItem setSubmenu:appleMenu]; - [[NSApp mainMenu] addItem:menuItem]; - - // Tell the application object that this is now the application menu - [NSApp setAppleMenu:appleMenu]; -} - -// Create a window menu -static void setupWindowMenu(void) -{ - NSMenu *windowMenu; - NSMenuItem *windowMenuItem; - NSMenuItem *menuItem; - - windowMenu = [[NSMenu alloc] initWithTitle:@"Window"]; - - // "Full/Windowed Toggle" item - menuItem = [[NSMenuItem alloc] initWithTitle:@"Full/Windowed Toggle" action:@selector(fullScreenToggle:) keyEquivalent:@"f"]; - // this is just for display purposes, key handling is in CWinEventsSDL::ProcessOSXShortcuts() - menuItem.keyEquivalentModifierMask = NSEventModifierFlagCommand | NSEventModifierFlagControl; - [windowMenu addItem:menuItem]; - - // "Full/Windowed Toggle" item - menuItem = [[NSMenuItem alloc] initWithTitle:@"Float on Top" action:@selector(floatOnTopToggle:) keyEquivalent:@"t"]; - [windowMenu addItem:menuItem]; - - // "Minimize" item - menuItem = [[NSMenuItem alloc] initWithTitle:@"Minimize" action:@selector(performMiniaturize:) keyEquivalent:@"m"]; - [windowMenu addItem:menuItem]; - - // "Title Bar" item - menuItem = [[NSMenuItem alloc] initWithTitle:@"Title Bar" action:@selector(titlebarToggle:) keyEquivalent:@""]; - [windowMenu addItem:menuItem]; - [menuItem setState: true]; - - // Put menu into the menubar - windowMenuItem = [[NSMenuItem alloc] initWithTitle:@"Window" action:nil keyEquivalent:@""]; - [windowMenuItem setSubmenu:windowMenu]; - [[NSApp mainMenu] addItem:windowMenuItem]; - - // Tell the application object that this is now the window menu - [NSApp setWindowsMenu:windowMenu]; -} - -@interface XBMCApplication : NSApplication -@end - -@implementation XBMCApplication - -// Called before the internal event loop has started running. -- (void) finishLaunching -{ - [super finishLaunching]; -} - -// Invoked from the Quit menu item -- (void)terminate:(id)sender -{ - // remove any notification handlers - [[[NSWorkspace sharedWorkspace] notificationCenter] removeObserver:self]; - [[NSNotificationCenter defaultCenter] removeObserver:self]; - - // Post a SDL_QUIT event - SDL_Event event; - event.type = SDL_QUIT; - SDL_PushEvent(&event); -} - -- (void)fullScreenToggle:(id)sender -{ - // Post an toggle full-screen event to the application thread. - SDL_Event event; - memset(&event, 0, sizeof(event)); - event.type = SDL_USEREVENT; - event.user.code = TMSG_TOGGLEFULLSCREEN; - SDL_PushEvent(&event); -} - -- (void)floatOnTopToggle:(id)sender -{ - NSWindow* window = [[[NSOpenGLContext currentContext] view] window]; - if ([window level] == NSFloatingWindowLevel) - { - [window setLevel:NSNormalWindowLevel]; - [sender setState:NSOffState]; - } - else - { - [window setLevel:NSFloatingWindowLevel]; - [sender setState:NSOnState]; - } -} - -- (void)titlebarToggle:(id)sender -{ - NSWindow* window = [[[NSOpenGLContext currentContext] view] window]; - [window setStyleMask: [window styleMask] ^ NSWindowStyleMaskTitled ]; - BOOL isSet = [window styleMask] & NSWindowStyleMaskTitled; - [window setMovableByWindowBackground: !isSet]; - [sender setState: isSet]; - -} - - -@end - -// The main class of the application, the application's delegate -@implementation XBMCDelegate - -// Set the working directory to the .app's parent directory -- (void) setupWorkingDirectory -{ - char parentdir[MAXPATHLEN]; - CFURLRef url = CFBundleCopyBundleURL(CFBundleGetMainBundle()); - CFURLRef url2 = CFURLCreateCopyDeletingLastPathComponent(0, url); - if (CFURLGetFileSystemRepresentation(url2, true, (UInt8 *)parentdir, MAXPATHLEN)) - { - assert( chdir (parentdir) == 0 ); /* chdir to the binary app's parent */ - } - CFRelease(url); - CFRelease(url2); -} - -// Doesnt currently get called. Keep in case we fix the root cause -- (void) applicationWillTerminate: (NSNotification *) note -{ - // disabled as currently main() explicitly calls this. - // [self appshutdownCleanup]; -} - -- (void) applicationWillResignActive:(NSNotification *) note -{ - //[[HotKeyController sharedController] sysPower:NO]; - //[[HotKeyController sharedController] sysVolume:NO]; - [[HotKeyController sharedController] setActive:NO]; -} - -- (void) applicationWillBecomeActive:(NSNotification *) note -{ - //[[HotKeyController sharedController] sysPower:YES]; - //[[HotKeyController sharedController] sysVolume:YES]; - [[HotKeyController sharedController] setActive:YES]; -} - -// To use Cocoa on secondary POSIX threads, your application must first detach -// at least one NSThread object, which can immediately exit. Some info says this -// is not required anymore, who knows ? -- (void) kickstartMultiThreaded:(id)arg -{ - @autoreleasepool - { - // empty - } -} - -- (void)appshutdownCleanup -{ - [[[NSWorkspace sharedWorkspace] notificationCenter] removeObserver:self - name:NSWorkspaceDidMountNotification - object:nil]; - - [[[NSWorkspace sharedWorkspace] notificationCenter] - removeObserver:self - name:NSWorkspaceDidUnmountNotification - object:nil]; - - NSNotificationCenter* center = [NSNotificationCenter defaultCenter]; - - [center removeObserver:self name:MediaKeyPower object:nil]; - [center removeObserver:self name:MediaKeySoundMute object:nil]; - [center removeObserver:self name:MediaKeySoundUp object:nil]; - [center removeObserver:self name:MediaKeySoundDown object:nil]; - [center removeObserver:self name:MediaKeyPlayPauseNotification object:nil]; - [center removeObserver:self name:MediaKeyFastNotification object:nil]; - [center removeObserver:self name:MediaKeyRewindNotification object:nil]; - [center removeObserver:self name:MediaKeyNextNotification object:nil]; - [center removeObserver:self name:MediaKeyPreviousNotification object:nil]; - - [[HotKeyController sharedController] disableTap]; -} - -// Called after the internal event loop has started running. -- (void) applicationDidFinishLaunching: (NSNotification *) note -{ - // enable multithreading, we should NOT have to do this but as we are mixing NSThreads/pthreads... - if (![NSThread isMultiThreaded]) - [NSThread detachNewThreadSelector:@selector(kickstartMultiThreaded:) toTarget:self withObject:nil]; - - // Set the working directory to the .app's parent directory - [self setupWorkingDirectory]; - - [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self - selector:@selector(deviceDidMountNotification:) - name:NSWorkspaceDidMountNotification - object:nil]; - - [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self - selector:@selector(deviceDidUnMountNotification:) - name:NSWorkspaceDidUnmountNotification - object:nil]; - - NSNotificationCenter *center = [NSNotificationCenter defaultCenter]; - - // create media key handler singleton - [[HotKeyController sharedController] enableTap]; - // add media key notifications - [center addObserver:self - selector:@selector(powerKeyNotification) - name:MediaKeyPower object:nil]; - [center addObserver:self - selector:@selector(muteKeyNotification) - name:MediaKeySoundMute object:nil]; - [center addObserver:self - selector:@selector(soundUpKeyNotification) - name:MediaKeySoundUp object:nil]; - [center addObserver:self - selector:@selector(soundDownKeyNotification) - name:MediaKeySoundDown object:nil]; - [center addObserver:self - selector:@selector(playPauseKeyNotification) - name:MediaKeyPlayPauseNotification object:nil]; - [center addObserver:self - selector:@selector(fastKeyNotification) - name:MediaKeyFastNotification object:nil]; - [center addObserver:self - selector:@selector(rewindKeyNotification) - name:MediaKeyRewindNotification object:nil]; - [center addObserver:self - selector:@selector(nextKeyNotification) - name:MediaKeyNextNotification object:nil]; - [center addObserver:self - selector:@selector(previousKeyNotification) - name:MediaKeyPreviousNotification object:nil]; - - // We're going to manually manage the screensaver. - setenv("SDL_VIDEO_ALLOW_SCREENSAVER", "1", true); - - // Hand off to main application code - gCalledAppMainline = TRUE; - - // stop the main loop so we return to main (below) and can - // call SDL_main there. - [NSApp stop:nil]; - - //post a NOP event, so the run loop actually stops - //see http://www.cocoabuilder.com/archive/cocoa/219842-nsapp-stop.html - NSEvent* event = [NSEvent otherEventWithType: NSEventTypeApplicationDefined - location: NSMakePoint(0,0) - modifierFlags: 0 - timestamp: 0.0 - windowNumber: 0 - context: nil - subtype: 0 - data1: 0 - data2: 0]; - // - [NSApp postEvent: event atStart: true]; -} - -/* - * Catch document open requests...this lets us notice files when the app - * was launched by double-clicking a document, or when a document was - * dragged/dropped on the app's icon. You need to have a - * CFBundleDocumentsType section in your Info.plist to get this message, - * apparently. - * - * Files are added to gArgv, so to the app, they'll look like command line - * arguments. Previously, apps launched from the finder had nothing but - * an argv[0]. - * - * This message may be received multiple times to open several docs on launch. - * - * This message is ignored once the app's mainline has been called. - */ -- (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename -{ - const char *temparg; - size_t arglen; - char *arg; - char **newargv; - - // app has started, ignore this document. - if (gCalledAppMainline) - return FALSE; - - temparg = [filename UTF8String]; - arglen = SDL_strlen(temparg) + 1; - arg = (char *) SDL_malloc(arglen); - if (arg == NULL) - return FALSE; - - newargv = (char **) realloc(gArgv, sizeof (char *) * (gArgc + 2)); - if (newargv == NULL) - { - SDL_free(arg); - return FALSE; - } - gArgv = newargv; - - SDL_strlcpy(arg, temparg, arglen); - gArgv[gArgc++] = arg; - gArgv[gArgc] = NULL; - - return TRUE; -} - -- (void) deviceDidMountNotification:(NSNotification *) note -{ - // calling into c++ code, need to use autorelease pools - @autoreleasepool - { - NSString* volumeLabel = [note.userInfo objectForKey:@"NSWorkspaceVolumeLocalizedNameKey"]; - const char* label = [volumeLabel UTF8String]; - - NSString* volumePath = [note.userInfo objectForKey:@"NSDevicePath"]; - const char* path = [volumePath UTF8String]; - - COSXStorageProvider::VolumeMountNotification(label, path); - } -} - -- (void) deviceDidUnMountNotification:(NSNotification *) note -{ - // calling into c++ code, need to use autorelease pools - @autoreleasepool - { - NSString* volumeLabel = [note.userInfo objectForKey:@"NSWorkspaceVolumeLocalizedNameKey"]; - const char* label = [volumeLabel UTF8String]; - - NSString* volumePath = [note.userInfo objectForKey:@"NSDevicePath"]; - const char* path = [volumePath UTF8String]; - - COSXStorageProvider::VolumeUnmountNotification(label, path); - } -} - -static void keyPress(SDLKey key) -{ - SDL_Event event; - memset(&event, 0, sizeof(event)); - event.type = SDL_KEYDOWN; - event.key.keysym.sym = key; - SDL_PushEvent(&event); - event.type = SDL_KEYUP; - SDL_PushEvent(&event); -} - -#define VK_SLEEP 0x143 -#define VK_VOLUME_MUTE 0xAD -#define VK_VOLUME_DOWN 0xAE -#define VK_VOLUME_UP 0xAF -#define VK_MEDIA_NEXT_TRACK 0x9E -#define VK_MEDIA_PREV_TRACK 0x9D -#define VK_MEDIA_STOP 0xB2 -#define VK_MEDIA_PLAY_PAUSE 0xB3 -#define VK_REWIND 0xB1 -#define VK_FAST_FWD 0xB0 - -- (void)powerKeyNotification -{ - keyPress((SDLKey)VK_SLEEP); -} - -- (void)muteKeyNotification -{ - keyPress((SDLKey)VK_VOLUME_MUTE); -} -- (void)soundUpKeyNotification -{ - keyPress((SDLKey)VK_VOLUME_UP); -} -- (void)soundDownKeyNotification -{ - keyPress((SDLKey)VK_VOLUME_DOWN); -} - -- (void)playPauseKeyNotification -{ - keyPress((SDLKey)VK_MEDIA_PLAY_PAUSE); -} - -- (void)fastKeyNotification -{ - keyPress((SDLKey)VK_FAST_FWD); -} - -- (void)rewindKeyNotification -{ - keyPress((SDLKey)VK_REWIND); -} - -- (void)nextKeyNotification -{ - keyPress((SDLKey)VK_MEDIA_NEXT_TRACK); -} - -- (void)previousKeyNotification -{ - keyPress((SDLKey)VK_MEDIA_PREV_TRACK); -} - -@end - -#ifdef main -# undef main -#endif -/* Main entry point to executable - should *not* be SDL_main! */ -int main(int argc, char *argv[]) -{ - @autoreleasepool - { - XBMCDelegate* xbmc_delegate; - - // Block SIGPIPE - // SIGPIPE repeatably kills us, turn it off - { - sigset_t set; - sigemptyset(&set); - sigaddset(&set, SIGPIPE); - sigprocmask(SIG_BLOCK, &set, NULL); - } - - /* Copy the arguments into a global variable */ - /* This is passed if we are launched by double-clicking */ - if (argc >= 2 && strncmp(argv[1], "-psn", 4) == 0) - { - gArgv = (char**)SDL_malloc(sizeof(char*) * 2); - gArgv[0] = argv[0]; - gArgv[1] = NULL; - gArgc = 1; - } - else - { - gArgc = argc; - gArgv = (char**)SDL_malloc(sizeof(char*) * (argc + 1)); - for (int i = 0; i <= argc; i++) - gArgv[i] = argv[i]; - } - - // Ensure the application object is initialised - [XBMCApplication sharedApplication]; - -#ifdef SDL_USE_CPS - { - CPSProcessSerNum PSN; - /* Tell the dock about us */ - if (!CPSGetCurrentProcess(&PSN)) - if (!CPSEnableForegroundOperation(&PSN, 0x03, 0x3C, 0x2C, 0x1103)) - if (!CPSSetFrontProcess(&PSN)) - [XBMCApplication sharedApplication]; - } -#endif - - // Set up the menubars - [NSApp setMainMenu:[[NSMenu alloc] init]]; - setupApplicationMenu(); - setupWindowMenu(); - - // Create XBMCDelegate and make it the app delegate - xbmc_delegate = [[XBMCDelegate alloc] init]; - [[NSApplication sharedApplication] setDelegate:xbmc_delegate]; - - // Start the main event loop - [NSApp run]; - - // call SDL_main which calls our real main in xbmc.cpp - // see http://lists.libsdl.org/pipermail/sdl-libsdl.org/2008-September/066542.html - int status; - status = SDL_main(gArgc, gArgv); - SDL_Quit(); - - [xbmc_delegate appshutdownCleanup]; - - return status; - } -} -- cgit v1.2.3