diff options
Diffstat (limited to 'osdep')
-rw-r--r-- | osdep/macosx_application.h | 57 | ||||
-rw-r--r-- | osdep/macosx_application.m | 349 | ||||
-rw-r--r-- | osdep/macosx_application_objc.h | 40 | ||||
-rw-r--r-- | osdep/macosx_finder_args.h | 29 | ||||
-rw-r--r-- | osdep/macosx_finder_args.m | 97 |
5 files changed, 446 insertions, 126 deletions
diff --git a/osdep/macosx_application.h b/osdep/macosx_application.h new file mode 100644 index 0000000000..c2227ae05d --- /dev/null +++ b/osdep/macosx_application.h @@ -0,0 +1,57 @@ +/* + * This file is part of mpv. + * + * mpv is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * mpv is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with mpv; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef MPV_MACOSX_APPLICATION +#define MPV_MACOSX_APPLICATION + +struct input_ctx; +struct mp_fifo; + +// Playloop callback function pointer +typedef void(*play_loop_callback)(void *); +typedef int(*should_stop_callback)(void *); + +// Menu Keys identifing menu items +typedef enum { + MPM_H_SIZE, + MPM_N_SIZE, + MPM_D_SIZE, + MPM_MINIMIZE, + MPM_ZOOM, +} MPMenuKey; + +void cocoa_register_menu_item_action(MPMenuKey key, void* action); + +// initializes Cocoa application +void init_cocoa_application(void); +void terminate_cocoa_application(void); + +// Runs the Cocoa Main Event Loop +void cocoa_run_runloop(void); +void cocoa_post_fake_event(void); + +// Adds play_loop as a timer of the Main Cocoa Event Loop +void cocoa_run_loop_schedule(play_loop_callback callback, + should_stop_callback playback_stopped, + void *context, + struct input_ctx *input_context, + struct mp_fifo *key_fifo); + +void macosx_finder_args_preinit(int *argc, char ***argv); + +#endif /* MPV_MACOSX_APPLICATION */ diff --git a/osdep/macosx_application.m b/osdep/macosx_application.m new file mode 100644 index 0000000000..39439e9fde --- /dev/null +++ b/osdep/macosx_application.m @@ -0,0 +1,349 @@ +/* + * This file is part of mpv. + * + * mpv is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * mpv is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with mpv; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "talloc.h" + +#include "core/mp_fifo.h" +#include "core/input/input.h" +#include "core/input/keycodes.h" + +#include "osdep/macosx_application_objc.h" +#include "video/out/osx_common.h" + +// 0.0001 seems too much and 0.01 too low, no idea why this works so well +#define COCOA_MAGIC_TIMER_DELAY 0.001 + +static Application *app; + +@interface Application (PrivateMethods) +- (NSMenuItem *)menuItemWithParent:(NSMenu *)parent + title:(NSString *)title + action:(SEL)selector + keyEquivalent:(NSString*)key; + +- (NSMenuItem *)mainMenuItemWithParent:(NSMenu *)parent + child:(NSMenu *)child; +- (void)registerMenuItem:(NSMenuItem*)menuItem forKey:(MPMenuKey)key; +- (NSMenu *)appleMenuWithMainMenu:(NSMenu *)mainMenu; +- (NSMenu *)movieMenu; +- (NSMenu *)windowMenu; +- (void)handleFiles; +@end + +@interface NSApplication (NiblessAdditions) +- (void)setAppleMenu:(NSMenu *)aMenu; +@end + +@implementation Application +@synthesize files = _files; +@synthesize argumentsList = _arguments_list; +@synthesize willStopOnOpenEvent = _will_stop_on_open_event; + +@synthesize callback = _callback; +@synthesize shouldStopPlayback = _should_stop_playback; +@synthesize context = _context; +@synthesize inputContext = _input_context; +@synthesize keyFIFO = _key_fifo; +@synthesize callbackTimer = _callback_timer; +@synthesize menuItems = _menu_items; + +- (id)init +{ + if (self = [super init]) { + self.menuItems = [[[NSMutableDictionary alloc] init] autorelease]; + self.files = nil; + self.argumentsList = [[[NSMutableArray alloc] init] autorelease]; + self.willStopOnOpenEvent = NO; + } + + return self; +} + +#define _R(P, T, E, K) \ + { \ + NSMenuItem *tmp = [self menuItemWithParent:(P) title:(T) \ + action:nil keyEquivalent:(E)]; \ + [self registerMenuItem:tmp forKey:(K)]; \ + } + +- (NSMenu *)appleMenuWithMainMenu:(NSMenu *)mainMenu +{ + NSMenu *menu = [[NSMenu alloc] initWithTitle:@"Apple Menu"]; + [self mainMenuItemWithParent:mainMenu child:menu]; + [self menuItemWithParent:menu title:@"Quit mpv" + action:@selector(stopPlayback) keyEquivalent: @"q"]; + return [menu autorelease]; +} + +- (NSMenu *)movieMenu +{ + NSMenu *menu = [[NSMenu alloc] initWithTitle:@"Movie"]; + _R(menu, @"Half Size", @"0", MPM_H_SIZE) + _R(menu, @"Normal Size", @"1", MPM_N_SIZE) + _R(menu, @"Double Size", @"2", MPM_D_SIZE) + return [menu autorelease]; +} + +- (NSMenu *)windowMenu +{ + NSMenu *menu = [[NSMenu alloc] initWithTitle:@"Window"]; + _R(menu, @"Minimize", @"m", MPM_MINIMIZE) + _R(menu, @"Zoom", @"z", MPM_ZOOM) + return [menu autorelease]; +} + +- (void)initialize_menu +{ + NSMenu *main_menu = [[NSMenu new] autorelease]; + [NSApp setMainMenu:main_menu]; + [NSApp setAppleMenu:[self appleMenuWithMainMenu:main_menu]]; + + [app mainMenuItemWithParent:main_menu child:[self movieMenu]]; + [app mainMenuItemWithParent:main_menu child:[self windowMenu]]; +} + +#undef _R + +- (void)call_callback +{ + if (self.shouldStopPlayback(self.context)) { + [NSApp stop:nil]; + cocoa_post_fake_event(); + } else { + self.callback(self.context); + } +} + +- (void)schedule_timer +{ + self.callbackTimer = + [NSTimer timerWithTimeInterval:COCOA_MAGIC_TIMER_DELAY + target:self + selector:@selector(call_callback) + userInfo:nil + repeats:YES]; + + [[NSRunLoop currentRunLoop] addTimer:self.callbackTimer + forMode:NSDefaultRunLoopMode]; + + [[NSRunLoop currentRunLoop] addTimer:self.callbackTimer + forMode:NSEventTrackingRunLoopMode]; +} + +- (void)stopPlayback +{ + mplayer_put_key(app.keyFIFO, MP_KEY_CLOSE_WIN); +} + +- (void)registerMenuItem:(NSMenuItem*)menuItem forKey:(MPMenuKey)key +{ + [self.menuItems setObject:menuItem forKey:[NSNumber numberWithInt:key]]; +} + +- (void)registerSelector:(SEL)action forKey:(MPMenuKey)key +{ + NSNumber *boxedKey = [NSNumber numberWithInt:key]; + NSMenuItem *item = [self.menuItems objectForKey:boxedKey]; + if (item) { + [item setAction:action]; + } +} + +- (NSMenuItem *)menuItemWithParent:(NSMenu *)parent + title:(NSString *)title + action:(SEL)action + keyEquivalent:(NSString*)key +{ + + NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:title + action:action + keyEquivalent:key]; + [parent addItem:item]; + return [item autorelease]; +} + +- (NSMenuItem *)mainMenuItemWithParent:(NSMenu *)parent + child:(NSMenu *)child +{ + NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:@"" + action:nil + keyEquivalent:@""]; + [item setSubmenu:child]; + [parent addItem:item]; + return [item autorelease]; +} + +- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)theApp { + return NSTerminateNow; +} + +- (void)handleQuitEvent:(NSAppleEventDescriptor*)e + withReplyEvent:(NSAppleEventDescriptor*)r +{ + [self stopPlayback]; +} + +- (void)application:(NSApplication *)sender openFiles:(NSArray *)filenames +{ + NSMutableArray *filesToOpen = [[[NSMutableArray alloc] init] autorelease]; + + [filenames enumerateObjectsUsingBlock:^(id obj, NSUInteger i, BOOL *_) { + NSInteger place = [app.argumentsList indexOfObject:obj]; + if (place == NSNotFound) { + // Proper new event ^_^ + [filesToOpen addObject:obj]; + } else { + // This file was already opened from the CLI. Cocoa is trying to + // open it again using events. Ignore it! + [app.argumentsList removeObjectAtIndex:place]; + } + }]; + + self.files = [filesToOpen sortedArrayUsingSelector:@selector(compare:)]; + if (self.willStopOnOpenEvent) { + [NSApp stop:nil]; + } else { + [self handleFiles]; + } +} + +- (void)handleFiles +{ + void *ctx = talloc_new(NULL); + [self.files enumerateObjectsUsingBlock:^(id obj, NSUInteger i, BOOL *_){ + const char *file = [escape_loadfile_name(obj) UTF8String]; + const char *append = (i == 0) ? "" : " append"; + char *cmd = talloc_asprintf(ctx, "loadfile \"%s\"%s", file, append); + mp_cmd_t *cmdt = mp_input_parse_cmd(bstr0(cmd), ""); + mp_input_queue_cmd(self.inputContext, cmdt); + }]; + talloc_free(ctx); +} +@end + +void cocoa_register_menu_item_action(MPMenuKey key, void* action) +{ + [app registerSelector:(SEL)action forKey:key]; +} + +void init_cocoa_application(void) +{ + NSApp = [NSApplication sharedApplication]; + app = [[Application alloc] init]; + [NSApp setDelegate:app]; + [app initialize_menu]; + [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular]; +} + +void terminate_cocoa_application(void) +{ + [NSApp hide:app]; + [NSApp terminate:app]; +} + +void cocoa_run_runloop(void) +{ + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + [NSApp run]; + [pool drain]; +} + +void cocoa_run_loop_schedule(play_loop_callback callback, + should_stop_callback stop_query, + void *context, + struct input_ctx *input_context, + struct mp_fifo *key_fifo) +{ + [NSApp setDelegate:app]; + app.callback = callback; + app.context = context; + app.shouldStopPlayback = stop_query; + app.inputContext = input_context; + app.keyFIFO = key_fifo; + [app schedule_timer]; +} + +void cocoa_post_fake_event(void) +{ + 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:NO]; +} + +static void macosx_wait_fileopen_events() +{ + app.willStopOnOpenEvent = YES; + cocoa_run_runloop(); // block until done +} + +static void macosx_redirect_output_to_logfile(const char *filename) +{ + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + NSString *log_path = [NSHomeDirectory() stringByAppendingPathComponent: + [@"Library/Logs/" stringByAppendingFormat:@"%s.log", filename]]; + freopen([log_path fileSystemRepresentation], "a", stdout); + freopen([log_path fileSystemRepresentation], "a", stderr); + [pool release]; +} + +static bool psn_matches_current_process(char *psn_arg_to_check) +{ + ProcessSerialNumber psn; + size_t psn_length = 5+10+1+10; + char psn_arg[psn_length+1]; + + GetCurrentProcess(&psn); + snprintf(psn_arg, 5+10+1+10+1, "-psn_%u_%u", + psn.highLongOfPSN, psn.lowLongOfPSN); + psn_arg[psn_length]=0; + + return strcmp(psn_arg, psn_arg_to_check) == 0; +} + +void macosx_finder_args_preinit(int *argc, char ***argv) +{ + if (*argc==2 && psn_matches_current_process((*argv)[1])) { + macosx_redirect_output_to_logfile("mpv"); + macosx_wait_fileopen_events(); + + char **cocoa_argv = talloc_zero_array(NULL, char*, [app.files count] + 2); + cocoa_argv[0] = "mpv"; + cocoa_argv[1] = "--quiet"; + int cocoa_argc = 2; + + for (NSString *filename in app.files) { + cocoa_argv[cocoa_argc] = (char*)[filename UTF8String]; + cocoa_argc++; + } + + *argc = cocoa_argc; + *argv = cocoa_argv; + } else { + for (int i = 0; i < *argc; i++ ) { + NSString *arg = [NSString stringWithUTF8String:(*argv)[i]]; + [app.argumentsList addObject:arg]; + } + } +} diff --git a/osdep/macosx_application_objc.h b/osdep/macosx_application_objc.h new file mode 100644 index 0000000000..340b2a9b1c --- /dev/null +++ b/osdep/macosx_application_objc.h @@ -0,0 +1,40 @@ +/* + * This file is part of mpv. + * + * mpv is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * mpv is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with mpv; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#import <Cocoa/Cocoa.h> +#include "osdep/macosx_application.h" + +@interface Application : NSObject<NSApplicationDelegate> +- (void)initialize_menu; +- (void)registerSelector:(SEL)selector forKey:(MPMenuKey)key; +- (void)call_callback; +- (void)schedule_timer; +- (void)stopPlayback; + +@property(nonatomic, assign) play_loop_callback callback; +@property(nonatomic, assign) should_stop_callback shouldStopPlayback; +@property(nonatomic, assign) void *context; +@property(nonatomic, assign) struct input_ctx *inputContext; +@property(nonatomic, assign) struct mp_fifo *keyFIFO; +@property(nonatomic, retain) NSTimer *callbackTimer; +@property(nonatomic, retain) NSMutableDictionary *menuItems; +@property(nonatomic, retain) NSArray *files; +@property(nonatomic, retain) NSMutableArray *argumentsList; +@property(nonatomic, assign) BOOL willStopOnOpenEvent; +@end + diff --git a/osdep/macosx_finder_args.h b/osdep/macosx_finder_args.h deleted file mode 100644 index 33a2936f05..0000000000 --- a/osdep/macosx_finder_args.h +++ /dev/null @@ -1,29 +0,0 @@ -/* - * This file is part of mplayer2. - * - * mplayer2 is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * mplayer2 is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with mplayer2; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#ifndef MPLAYER_MACOSX_FINDER_ARGS_H -#define MPLAYER_MACOSX_FINDER_ARGS_H - -#include <stdbool.h> -#include "core/m_config.h" - -struct playlist; -bool macosx_finder_args(m_config_t *config, struct playlist *files, - int argc, char **argv); - -#endif /* MPLAYER_MACOSX_FINDER_ARGS_H */ diff --git a/osdep/macosx_finder_args.m b/osdep/macosx_finder_args.m deleted file mode 100644 index e9038deaed..0000000000 --- a/osdep/macosx_finder_args.m +++ /dev/null @@ -1,97 +0,0 @@ -/* - * This file is part of mplayer2. - * - * mplayer2 is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * mplayer2 is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with mplayer2; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#import <Cocoa/Cocoa.h> -#import <ApplicationServices/ApplicationServices.h> -#include <stdio.h> -#include "talloc.h" -#include "core/playlist.h" -#include "macosx_finder_args.h" - -static struct playlist *files = NULL; - -void macosx_wait_fileopen_events(void); -void macosx_redirect_output_to_logfile(const char *filename); -bool psn_matches_current_process(char *psn_arg_to_check); - -@interface FileOpenDelegate : NSObject -- (void)application:(NSApplication *)sender openFiles:(NSArray *)filenames; -@end - -@implementation FileOpenDelegate -- (void)application:(NSApplication *)sender openFiles:(NSArray *)filenames -{ - NSArray *sorted_filenames = [filenames - sortedArrayUsingSelector:@selector(compare:)]; - files = talloc_zero(NULL, struct playlist); - for (NSString *filename in sorted_filenames) - playlist_add_file(files, [filename UTF8String]); - [NSApp stop:nil]; // stop the runloop (give back control to mplayer2 code) -} -@end - -void macosx_wait_fileopen_events() -{ - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - NSApp = [NSApplication sharedApplication]; - [NSApp setDelegate: [[[FileOpenDelegate alloc] init] autorelease]]; - [NSApp run]; // block until we recive the fileopen events - [pool release]; -} - -void macosx_redirect_output_to_logfile(const char *filename) -{ - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - NSString *log_path = [NSHomeDirectory() stringByAppendingPathComponent: - [@"Library/Logs/" stringByAppendingFormat:@"%s.log", filename]]; - freopen([log_path fileSystemRepresentation], "a", stdout); - freopen([log_path fileSystemRepresentation], "a", stderr); - [pool release]; -} - -bool psn_matches_current_process(char *psn_arg_to_check) -{ - ProcessSerialNumber psn; - char psn_arg[5+10+1+10+1]; - - GetCurrentProcess(&psn); - snprintf(psn_arg, 5+10+1+10+1, "-psn_%u_%u", - psn.highLongOfPSN, psn.lowLongOfPSN); - psn_arg[5+10+1+10]=0; - - return strcmp(psn_arg, psn_arg_to_check) == 0; -} - -bool macosx_finder_args(m_config_t *config, struct playlist *pl_files, - int argc, char **argv) -{ - if (argc==1 && psn_matches_current_process(argv[0])) { - macosx_redirect_output_to_logfile("mpv"); - m_config_set_option0(config, "quiet", NULL); - macosx_wait_fileopen_events(); - } - - if (files) { - playlist_transfer_entries(pl_files, files); - talloc_free(files); - files = NULL; - return true; - } else { - return false; - } -} |