windows support: remove _UWIN define
[mplayer.git] / libvo / cocoa_common.m
blob5658ecb7bfe2e76609dbe75a3c47d2063a528b50
1 #import <Cocoa/Cocoa.h>
2 #import <OpenGL/OpenGL.h>
3 #import <QuartzCore/QuartzCore.h>
4 #import <CoreServices/CoreServices.h> // for CGDisplayHideCursor
5 #include "cocoa_common.h"
7 #include "options.h"
8 #include "video_out.h"
9 #include "aspect.h"
11 #include "mp_fifo.h"
12 #include "talloc.h"
14 #include "input/input.h"
15 #include "input/keycodes.h"
16 #include "osx_common.h"
17 #include "mp_msg.h"
19 #define NSLeftAlternateKeyMask (0x000020 | NSAlternateKeyMask)
20 #define NSRightAlternateKeyMask (0x000040 | NSAlternateKeyMask)
22 @interface GLMPlayerWindow : NSWindow <NSWindowDelegate>
23 - (BOOL) canBecomeKeyWindow;
24 - (BOOL) canBecomeMainWindow;
25 - (void) fullscreen;
26 - (void) mouseEvent:(NSEvent *)theEvent;
27 - (void) mulSize:(float)multiplier;
28 - (void) setContentSize:(NSSize)newSize keepCentered:(BOOL)keepCentered;
29 @end
31 @interface GLMPlayerOpenGLView : NSView
32 @end
34 struct vo_cocoa_state {
35     NSAutoreleasePool *pool;
36     GLMPlayerWindow *window;
37     NSOpenGLContext *glContext;
39     NSSize current_video_size;
40     NSSize previous_video_size;
42     NSRect screen_frame;
43     NSScreen *screen_handle;
44     NSArray *screen_array;
46     NSInteger windowed_mask;
47     NSInteger fullscreen_mask;
49     NSRect windowed_frame;
51     NSString *window_title;
53     int last_screensaver_update;
55     bool did_resize;
56     bool out_fs_resize;
59 struct vo_cocoa_state *s;
61 struct vo *l_vo;
63 // local function definitions
64 struct vo_cocoa_state *vo_cocoa_init_state(void);
65 void update_screen_info(void);
66 void resize_window(struct vo *vo);
67 void create_menu(void);
69 struct vo_cocoa_state *vo_cocoa_init_state(void)
71     struct vo_cocoa_state *s = talloc_ptrtype(NULL, s);
72     *s = (struct vo_cocoa_state){
73         .did_resize = NO,
74         .current_video_size = {0,0},
75         .previous_video_size = {0,0},
76         .windowed_mask = NSTitledWindowMask|NSClosableWindowMask|NSMiniaturizableWindowMask|NSResizableWindowMask,
77         .fullscreen_mask = NSBorderlessWindowMask,
78         .windowed_frame = {{0,0},{0,0}},
79         .out_fs_resize = NO,
80     };
81     return s;
84 int vo_cocoa_init(struct vo *vo)
86     s = vo_cocoa_init_state();
87     s->pool = [[NSAutoreleasePool alloc] init];
88     NSApplicationLoad();
89     NSApp = [NSApplication sharedApplication];
90     [NSApp setActivationPolicy: NSApplicationActivationPolicyRegular];
92     return 1;
95 void vo_cocoa_uninit(struct vo *vo)
97     CGDisplayShowCursor(kCGDirectMainDisplay);
98     [s->window release];
99     s->window = nil;
100     [s->glContext release];
101     s->glContext = nil;
102     [s->pool release];
103     s->pool = nil;
105     talloc_free(s);
108 void update_screen_info(void)
110     s->screen_array = [NSScreen screens];
111     if (xinerama_screen >= (int)[s->screen_array count]) {
112         mp_msg(MSGT_VO, MSGL_INFO, "[cocoa] Device ID %d does not exist, falling back to main device\n", xinerama_screen);
113         xinerama_screen = -1;
114     }
116     if (xinerama_screen < 0) { // default behaviour
117         if (! (s->screen_handle = [s->window screen]) )
118             s->screen_handle = [s->screen_array objectAtIndex:0];
119     } else {
120         s->screen_handle = [s->screen_array objectAtIndex:(xinerama_screen)];
121     }
123     s->screen_frame = [s->screen_handle frame];
126 void vo_cocoa_update_xinerama_info(struct vo *vo)
128     update_screen_info();
129     aspect_save_screenres(vo, s->screen_frame.size.width, s->screen_frame.size.height);
132 int vo_cocoa_change_attributes(struct vo *vo)
134     return 0;
137 void resize_window(struct vo *vo)
139     vo->dwidth = [[s->window contentView] frame].size.width;
140     vo->dheight = [[s->window contentView] frame].size.height;
141     [s->glContext update];
144 int vo_cocoa_create_window(struct vo *vo, uint32_t d_width,
145                            uint32_t d_height, uint32_t flags)
147     if (s->current_video_size.width > 0 || s->current_video_size.height > 0)
148         s->previous_video_size = s->current_video_size;
149     s->current_video_size = NSMakeSize(d_width, d_height);
151     if (!(s->window || s->glContext)) { // keep using the same window
152         s->window = [[GLMPlayerWindow alloc] initWithContentRect:NSMakeRect(0, 0, d_width, d_height)
153                                              styleMask:s->windowed_mask
154                                              backing:NSBackingStoreBuffered defer:NO];
156         GLMPlayerOpenGLView *glView = [[GLMPlayerOpenGLView alloc] initWithFrame:NSMakeRect(0, 0, 100, 100)];
158         NSOpenGLPixelFormatAttribute attrs[] = {
159             NSOpenGLPFADoubleBuffer, // double buffered
160             NSOpenGLPFADepthSize, (NSOpenGLPixelFormatAttribute)16, // 16 bit depth buffer
161             (NSOpenGLPixelFormatAttribute)0
162         };
164         NSOpenGLPixelFormat *pixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:attrs];
165         s->glContext = [[NSOpenGLContext alloc] initWithFormat:pixelFormat shareContext:nil];
167         create_menu();
169         [s->window setContentView:glView];
170         [glView release];
171         [s->window setAcceptsMouseMovedEvents:YES];
172         [s->glContext setView:glView];
173         [s->glContext makeCurrentContext];
175         [NSApp setDelegate:s->window];
176         [s->window setDelegate:s->window];
177         [s->window setContentSize:s->current_video_size];
178         [s->window setContentAspectRatio:s->current_video_size];
179         [s->window center];
181         if (flags & VOFLAG_HIDDEN) {
182             [s->window orderOut:nil];
183         } else {
184             [s->window makeKeyAndOrderFront:nil];
185             [NSApp activateIgnoringOtherApps:YES];
186         }
188         if (flags & VOFLAG_FULLSCREEN)
189             vo_cocoa_fullscreen(vo);
190     } else {
191         if (s->current_video_size.width  != s->previous_video_size.width ||
192             s->current_video_size.height != s->previous_video_size.height) {
193             if (vo_fs) {
194                 // we will resize as soon as we get out of fullscreen
195                 s->out_fs_resize = YES;
196             } else {
197                 // only if we are not in fullscreen and the video size did change
198                 // we actually resize the window and set a new aspect ratio
199                 [s->window setContentSize:s->current_video_size keepCentered:YES];
200                 [s->window setContentAspectRatio:s->current_video_size];
201             }
202         }
203     }
205     resize_window(vo);
207     if (s->window_title)
208         [s->window_title release];
210     s->window_title = [[NSString alloc] initWithUTF8String:vo_get_window_title(vo)];
211     [s->window setTitle: s->window_title];
213     return 0;
216 void vo_cocoa_swap_buffers()
218     [s->glContext flushBuffer];
221 int vo_cocoa_check_events(struct vo *vo)
223     //update activity every 30 seconds to prevent
224     //screensaver from starting up.
225     int curTime = TickCount()/60;
226     if (curTime - s->last_screensaver_update >= 30 || s->last_screensaver_update == 0)
227     {
228         UpdateSystemActivity(UsrActivity);
229         s->last_screensaver_update = curTime;
230     }
232     NSEvent *event;
233     event = [NSApp nextEventMatchingMask:NSAnyEventMask untilDate:nil
234                    inMode:NSEventTrackingRunLoopMode dequeue:YES];
235     if (event == nil)
236         return 0;
237     l_vo = vo;
238     [NSApp sendEvent:event];
239     l_vo = nil;
241     if (s->did_resize) {
242         s->did_resize = NO;
243         resize_window(vo);
244         return VO_EVENT_RESIZE;
245     }
246     // Without SDL's bootstrap code (include SDL.h in mplayer.c),
247     // on Leopard, we have trouble to get the play window automatically focused
248     // when the app is actived. The Following code fix this problem.
249 #ifndef CONFIG_SDL
250     if ([event type] == NSAppKitDefined
251             && [event subtype] == NSApplicationActivatedEventType) {
252         [s->window makeMainWindow];
253         [s->window makeKeyAndOrderFront:nil];
254     }
255 #endif
256     return 0;
259 void vo_cocoa_fullscreen(struct vo *vo)
261     [s->window fullscreen];
262     resize_window(vo);
265 int vo_cocoa_swap_interval(int enabled)
267     [s->glContext setValues:&enabled forParameter:NSOpenGLCPSwapInterval];
268     return 0;
271 void create_menu()
273     NSMenu *menu;
274     NSMenuItem *menuItem;
276     menu = [[NSMenu new] autorelease];
277     menuItem = [[NSMenuItem new] autorelease];
278     [menu addItem: menuItem];
279     [NSApp setMainMenu: menu];
281     menu = [[NSMenu alloc] initWithTitle:@"Movie"];
282     menuItem = [[NSMenuItem alloc] initWithTitle:@"Half Size" action:@selector(halfSize) keyEquivalent:@"0"]; [menu addItem:menuItem];
283     menuItem = [[NSMenuItem alloc] initWithTitle:@"Normal Size" action:@selector(normalSize) keyEquivalent:@"1"]; [menu addItem:menuItem];
284     menuItem = [[NSMenuItem alloc] initWithTitle:@"Double Size" action:@selector(doubleSize) keyEquivalent:@"2"]; [menu addItem:menuItem];
286     menuItem = [[NSMenuItem alloc] initWithTitle:@"Movie" action:nil keyEquivalent:@""];
287     [menuItem setSubmenu:menu];
288     [[NSApp mainMenu] addItem:menuItem];
290     [menu release];
291     [menuItem release];
294 @implementation GLMPlayerWindow
296 - (void) windowDidResize:(NSNotification *) notification
298     if (l_vo)
299         s->did_resize = YES;
302 - (void) fullscreen
304     if (!vo_fs) {
305         [NSApp setPresentationOptions:NSApplicationPresentationHideDock|NSApplicationPresentationHideMenuBar];
306         s->windowed_frame = [self frame];
307         [self setHasShadow:NO];
308         [self setStyleMask:s->fullscreen_mask];
309         [self setFrame:s->screen_frame display:YES animate:NO];
310         [self setLevel:NSNormalWindowLevel + 1];
311         CGDisplayHideCursor(kCGDirectMainDisplay);
312         vo_fs = VO_TRUE;
313     } else {
314         [NSApp setPresentationOptions:NSApplicationPresentationDefault];
315         [self setHasShadow:YES];
316         [self setStyleMask:s->windowed_mask];
317         [self setTitle:s->window_title];
318         [self setFrame:s->windowed_frame display:YES animate:NO];
319         if (s->out_fs_resize) {
320             [self setContentSize:s->current_video_size keepCentered:YES];
321             s->out_fs_resize = NO;
322         }
323         [self setContentAspectRatio:s->current_video_size];
324         [self setLevel:NSNormalWindowLevel];
325         CGDisplayShowCursor(kCGDirectMainDisplay);
326         vo_fs = VO_FALSE;
327     }
330 - (BOOL) canBecomeMainWindow { return YES; }
331 - (BOOL) canBecomeKeyWindow { return YES; }
332 - (BOOL) acceptsFirstResponder { return YES; }
333 - (BOOL) becomeFirstResponder { return YES; }
334 - (BOOL) resignFirstResponder { return YES; }
335 - (BOOL) windowShouldClose:(id)sender
337     mplayer_put_key(l_vo->key_fifo, KEY_CLOSE_WIN);
338     // We have to wait for MPlayer to handle this,
339     // otherwise we are in trouble if the
340     // KEY_CLOSE_WIN handler is disabled
341     return NO;
344 - (void) handleQuitEvent:(NSAppleEventDescriptor*)e withReplyEvent:(NSAppleEventDescriptor*)r
346     mplayer_put_key(l_vo->key_fifo, KEY_CLOSE_WIN);
349 - (void) keyDown:(NSEvent *)theEvent
351     unsigned char charcode;
352     if (([theEvent modifierFlags] & NSRightAlternateKeyMask) == NSRightAlternateKeyMask)
353         charcode = *[[theEvent characters] UTF8String];
354     else
355         charcode = [[theEvent charactersIgnoringModifiers] characterAtIndex:0];
357     int key = convert_key([theEvent keyCode], charcode);
359     if (key > -1) {
360         if ([theEvent modifierFlags] & NSShiftKeyMask)
361             key |= KEY_MODIFIER_SHIFT;
362         if ([theEvent modifierFlags] & NSControlKeyMask)
363             key |= KEY_MODIFIER_CTRL;
364         if (([theEvent modifierFlags] & NSLeftAlternateKeyMask) == NSLeftAlternateKeyMask)
365             key |= KEY_MODIFIER_ALT;
366         if ([theEvent modifierFlags] & NSCommandKeyMask)
367             key |= KEY_MODIFIER_META;
368         mplayer_put_key(l_vo->key_fifo, key);
369     }
372 - (void) mouseDragged:(NSEvent *)theEvent
374     [self mouseEvent: theEvent];
377 - (void) mouseDown:(NSEvent *)theEvent
379     [self mouseEvent: theEvent];
382 - (void) mouseUp:(NSEvent *)theEvent
384     [self mouseEvent: theEvent];
387 - (void) rightMouseDown:(NSEvent *)theEvent
389     [self mouseEvent: theEvent];
392 - (void) rightMouseUp:(NSEvent *)theEvent
394     [self mouseEvent: theEvent];
397 - (void) otherMouseDown:(NSEvent *)theEvent
399     [self mouseEvent: theEvent];
402 - (void) otherMouseUp:(NSEvent *)theEvent
404     [self mouseEvent: theEvent];
407 - (void) scrollWheel:(NSEvent *)theEvent
409     if ([theEvent deltaY] > 0)
410         mplayer_put_key(l_vo->key_fifo, MOUSE_BTN3);
411     else
412         mplayer_put_key(l_vo->key_fifo, MOUSE_BTN4);
415 - (void) mouseEvent:(NSEvent *)theEvent
417     if ( [theEvent buttonNumber] >= 0 && [theEvent buttonNumber] <= 9 )
418     {
419         int buttonNumber = [theEvent buttonNumber];
420         // Fix to mplayer defined button order: left, middle, right
421         if (buttonNumber == 1)
422             buttonNumber = 2;
423         else if (buttonNumber == 2)
424             buttonNumber = 1;
425         switch ([theEvent type]) {
426             case NSLeftMouseDown:
427                 break;
428             case NSRightMouseDown:
429             case NSOtherMouseDown:
430                 mplayer_put_key(l_vo->key_fifo, (MOUSE_BTN0 + buttonNumber) | MP_KEY_DOWN);
431                 break;
432             case NSLeftMouseUp:
433                 break;
434             case NSRightMouseUp:
435             case NSOtherMouseUp:
436                 mplayer_put_key(l_vo->key_fifo, MOUSE_BTN0 + buttonNumber);
437                 break;
438         }
439     }
442 - (void) applicationWillBecomeActive:(NSNotification *)aNotification
444     if (vo_fs) {
445         [s->window setLevel:NSNormalWindowLevel + 1];
446         [NSApp setPresentationOptions:NSApplicationPresentationHideDock|NSApplicationPresentationHideMenuBar];
447         [s->window makeKeyAndOrderFront:nil];
448         [NSApp activateIgnoringOtherApps: YES];
449     }
452 - (void) applicationWillResignActive:(NSNotification *)aNotification
454     if (vo_fs) {
455         [s->window setLevel:NSNormalWindowLevel];
456         [NSApp setPresentationOptions:NSApplicationPresentationDefault];
457     }
460 - (void) applicationDidFinishLaunching:(NSNotification*)notification
462     // Install an event handler so the Quit menu entry works
463     // The proper way using NSApp setDelegate: and
464     // applicationShouldTerminate: does not work,
465     // probably NSApplication never installs its handler.
466     [[NSAppleEventManager sharedAppleEventManager]
467         setEventHandler:self
468         andSelector:@selector(handleQuitEvent:withReplyEvent:)
469         forEventClass:kCoreEventClass
470         andEventID:kAEQuitApplication];
473 - (void) normalSize
475     if (!vo_fs)
476       [self setContentSize:s->current_video_size keepCentered:YES];
479 - (void) halfSize { [self mulSize:0.5f];}
481 - (void) doubleSize { [self mulSize:2.0f];}
483 - (void) mulSize:(float)multiplier
485     if (!vo_fs) {
486         NSSize size = [[self contentView] frame].size;
487         size.width  = s->current_video_size.width  * (multiplier);
488         size.height = s->current_video_size.height * (multiplier);
489         [self setContentSize:size keepCentered:YES];
490     }
493 - (void) setContentSize:(NSSize)ns keepCentered:(BOOL)keepCentered
495     if (keepCentered) {
496         NSRect nf = [self frame];
497         NSRect vf = [[self screen] visibleFrame];
498         int title_height = nf.size.height - [[self contentView] bounds].size.height;
499         double ratio = (double)ns.width / (double)ns.height;
501         // clip the new size to the visibleFrame's size if needed
502         if (ns.width > vf.size.width || ns.height + title_height > vf.size.height) {
503             ns = vf.size;
504             ns.height -= title_height; // make space for the title bar
506             if (ns.width > ns.height) {
507                 ns.height = ((double)ns.width * 1/ratio + 0.5);
508             } else {
509                 ns.width = ((double)ns.height * ratio + 0.5);
510             }
511         }
513         int dw = nf.size.width - ns.width;
514         int dh = nf.size.height - ns.height - title_height;
516         nf.origin.x += dw / 2;
517         nf.origin.y += dh / 2;
519         [self setFrame: NSMakeRect(nf.origin.x, nf.origin.y, ns.width, ns.height + title_height) display:YES animate:NO];
520     } else {
521         [self setContentSize:ns];
522     }
524 @end
526 @implementation GLMPlayerOpenGLView
527 - (void) drawRect: (NSRect)rect
529     [[NSColor clearColor] set];
530     NSRectFill([self bounds]);
532 @end