comparison libvo/osx_objc_common.m @ 35086:8848682f4035

Split out an objective-C common OS X layer, to be reused by -vo gl.
author reimar
date Thu, 13 Sep 2012 19:20:47 +0000
parents libvo/vo_corevideo.m@5a9728bbb51e
children 9fcfc9c5f038
comparison
equal deleted inserted replaced
35085:5a9728bbb51e 35086:8848682f4035
1 /*
2 * CoreVideo video output driver
3 * Copyright (c) 2005 Nicolas Plourde <nicolasplourde@gmail.com>
4 *
5 * This file is part of MPlayer.
6 *
7 * MPlayer is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * MPlayer is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License along
18 * with MPlayer; if not, write to the Free Software Foundation, Inc.,
19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 */
21
22 #import "osx_objc_common.h"
23 #include <Carbon/Carbon.h>
24 #include <CoreServices/CoreServices.h>
25 //special workaround for Apple bug #6267445
26 //(OSServices Power API disabled in OSServices.h for 64bit systems)
27 #ifndef __POWER__
28 #include <CoreServices/../Frameworks/OSServices.framework/Headers/Power.h>
29 #endif
30
31 //MPLAYER
32 #include "config.h"
33 #include "video_out.h"
34 #include "aspect.h"
35 #include "mp_msg.h"
36 #include "mp_fifo.h"
37
38 #include "input/input.h"
39 #include "input/mouse.h"
40
41 #include "osdep/keycodes.h"
42 #include "osx_common.h"
43
44 static float winAlpha = 1;
45
46 static BOOL isLeopardOrLater;
47
48 static NSAutoreleasePool *autoreleasepool;
49 static MPCommonOpenGLView *oglv;
50
51 int vo_osx_init(void)
52 {
53 autoreleasepool = [[NSAutoreleasePool alloc] init];
54 oglv = [[MPCommonOpenGLView alloc] initWithFrame:NSMakeRect(0, 0, 100, 100) pixelFormat:[MPCommonOpenGLView defaultPixelFormat]];
55 [oglv autorelease];
56 [oglv display];
57 [oglv preinit];
58 return 1;
59 }
60
61 void vo_osx_uninit(void)
62 {
63 NSAutoreleasePool *finalPool;
64 oglv = nil;
65 [autoreleasepool release];
66 finalPool = [[NSAutoreleasePool alloc] init];
67 [NSApp nextEventMatchingMask:NSAnyEventMask untilDate:nil inMode:NSDefaultRunLoopMode dequeue:YES];
68 [finalPool release];
69 }
70
71 int vo_osx_config(uint32_t width, uint32_t height, uint32_t flags)
72 {
73 [oglv config:width:height:flags];
74 return 1;
75 }
76
77 void vo_osx_ontop(void)
78 {
79 vo_ontop = !vo_ontop;
80 [oglv ontop];
81 }
82
83 void vo_osx_fullscreen(void)
84 {
85 vo_fs = !vo_fs;
86 [oglv fullscreen:YES];
87 }
88
89 int vo_osx_check_events(void)
90 {
91 [oglv check_events];
92 return 0;
93 }
94
95 void vo_osx_update_xinerama_info(void)
96 {
97 [oglv update_screen_info];
98 }
99
100 @implementation MPCommonOpenGLView
101 - (void) update_screen_info
102 {
103 int screen_id = xinerama_screen;
104 NSArray *screen_array = [NSScreen screens];
105 NSScreen *screen_handle;
106 NSRect screen_frame;
107
108 if(screen_id >= (int)[screen_array count])
109 {
110 mp_msg(MSGT_VO, MSGL_INFO, "[vo_corevideo] Device ID %d does not exist, falling back to main device\n", screen_id);
111 screen_id = -1;
112 }
113 if (screen_id < 0 && [self window])
114 screen_handle = [[self window] screen];
115 else
116 screen_handle = [screen_array objectAtIndex:(screen_id < 0 ? 0 : screen_id)];
117
118 screen_frame = [screen_handle frame];
119 vo_screenwidth = screen_frame.size.width;
120 vo_screenheight = screen_frame.size.height;
121 xinerama_x = screen_frame.origin.x;
122 xinerama_y = screen_frame.origin.y;
123 aspect_save_screenres(vo_screenwidth, vo_screenheight);
124 }
125
126 - (void) preinit
127 {
128 NSOpenGLContext *glContext;
129 GLint swapInterval = 1;
130
131 NSApplicationLoad();
132 NSApp = [NSApplication sharedApplication];
133 isLeopardOrLater = floor(NSAppKitVersionNumber) > 824;
134
135 osx_foreground_hack();
136
137 // Install an event handler so the Quit menu entry works
138 // The proper way using NSApp setDelegate: and
139 // applicationShouldTerminate: does not work,
140 // probably NSApplication never installs its handler.
141 [[NSAppleEventManager sharedAppleEventManager]
142 setEventHandler:self
143 andSelector:@selector(handleQuitEvent:withReplyEvent:)
144 forEventClass:kCoreEventClass
145 andEventID:kAEQuitApplication];
146
147 //init menu
148 [self initMenu];
149
150 //create window
151 window = [[NSWindow alloc] initWithContentRect:NSMakeRect(0, 0, 100, 100)
152 styleMask:NSTitledWindowMask|NSTexturedBackgroundWindowMask|NSClosableWindowMask|NSMiniaturizableWindowMask|NSResizableWindowMask
153 backing:NSBackingStoreBuffered defer:NO];
154
155 [window autorelease];
156 [window setDelegate:self];
157 [window setContentView:self];
158 [window setInitialFirstResponder:self];
159 [window setAcceptsMouseMovedEvents:YES];
160 [window setTitle:@"MPlayer - The Movie Player"];
161
162 winSizeMult = 1;
163
164 //create OpenGL Context
165 glContext = [[NSOpenGLContext alloc] initWithFormat:[NSOpenGLView defaultPixelFormat] shareContext:nil];
166
167 [self setOpenGLContext:glContext];
168 [glContext setValues:&swapInterval forParameter:NSOpenGLCPSwapInterval];
169 [glContext setView:self];
170 [glContext makeCurrentContext];
171 [glContext release];
172 }
173
174 - (void) dealloc
175 {
176 [self setOpenGLContext:nil];
177 [super dealloc];
178 }
179
180 - (void) config:(uint32_t)width:(uint32_t)height:(uint32_t)flags
181 {
182 config_movie_aspect((float)width/height);
183
184 vo_dwidth = width *= self->winSizeMult;
185 vo_dheight = height *= self->winSizeMult;
186
187 //config window
188 [window setContentSize:NSMakeSize(vo_dwidth, vo_dheight)];
189
190 // Do not use visibleFrame - taking the menu bar and dock into account
191 // would be nicer in principle, but currently only results in the window
192 // being placed strangely off-center since vo_dx/vo_dy calculation is
193 // not aware of it.
194 // Also flip vo_dy since the screen origin is in the bottom left on OSX.
195 [self update_screen_info];
196 [window setFrameTopLeftPoint:NSMakePoint(
197 vo_dx,
198 2*xinerama_y + vo_screenheight - vo_dy)];
199
200 vo_fs = flags & VOFLAG_FULLSCREEN;
201
202 [self rootwin];
203
204 if(vo_fs)
205 [self fullscreen: NO];
206
207 [self ontop];
208
209 if (!(flags & VOFLAG_HIDDEN))
210 //show window
211 [window makeKeyAndOrderFront:self];
212 }
213
214 /*
215 Init Menu
216 */
217 - (void)initMenu
218 {
219 NSMenu *mainMenu = [[NSMenu alloc] init];
220 NSMenu *menu, *aspectMenu;
221 NSMenuItem *menuItem;
222
223 menu = [[NSMenu alloc] init];
224 menuItem = [[NSMenuItem alloc] init];
225 [menuItem setSubmenu:menu];
226 [mainMenu addItem:menuItem];
227 // Note: setAppleMenu seems to be unnecessary from 10.6 on,
228 // but is needed for all earlier versions or the menu is
229 // messed up.
230 // Round-about way with performSelector used to avoid compiler
231 // warnings.
232 [NSApp performSelector:@selector(setAppleMenu:) withObject:menu];
233
234 //Create Movie Menu
235 menu = [[NSMenu alloc] initWithTitle:@"Movie"];
236 menuItem = [[NSMenuItem alloc] initWithTitle:@"Half Size" action:@selector(menuAction:) keyEquivalent:@"0"]; [menu addItem:menuItem];
237 kHalfScreenCmd = menuItem;
238 menuItem = [[NSMenuItem alloc] initWithTitle:@"Normal Size" action:@selector(menuAction:) keyEquivalent:@"1"]; [menu addItem:menuItem];
239 kNormalScreenCmd = menuItem;
240 menuItem = [[NSMenuItem alloc] initWithTitle:@"Double Size" action:@selector(menuAction:) keyEquivalent:@"2"]; [menu addItem:menuItem];
241 kDoubleScreenCmd = menuItem;
242 menuItem = [[NSMenuItem alloc] initWithTitle:@"Full Size" action:@selector(menuAction:) keyEquivalent:@"f"]; [menu addItem:menuItem];
243 kFullScreenCmd = menuItem;
244 menuItem = [NSMenuItem separatorItem]; [menu addItem:menuItem];
245
246 aspectMenu = [[NSMenu alloc] initWithTitle:@"Aspect Ratio"];
247 menuItem = [[NSMenuItem alloc] initWithTitle:@"Keep" action:@selector(menuAction:) keyEquivalent:@""]; [aspectMenu addItem:menuItem];
248 if(vo_keepaspect) [menuItem setState:NSOnState];
249 kKeepAspectCmd = menuItem;
250 menuItem = [[NSMenuItem alloc] initWithTitle:@"Pan-Scan" action:@selector(menuAction:) keyEquivalent:@""]; [aspectMenu addItem:menuItem];
251 if(vo_panscan) [menuItem setState:NSOnState];
252 kPanScanCmd = menuItem;
253 menuItem = [NSMenuItem separatorItem]; [aspectMenu addItem:menuItem];
254 menuItem = [[NSMenuItem alloc] initWithTitle:@"Original" action:@selector(menuAction:) keyEquivalent:@""]; [aspectMenu addItem:menuItem];
255 kAspectOrgCmd = menuItem;
256 menuItem = [[NSMenuItem alloc] initWithTitle:@"4:3" action:@selector(menuAction:) keyEquivalent:@""]; [aspectMenu addItem:menuItem];
257 kAspectFullCmd = menuItem;
258 menuItem = [[NSMenuItem alloc] initWithTitle:@"16:9" action:@selector(menuAction:) keyEquivalent:@""]; [aspectMenu addItem:menuItem];
259 kAspectWideCmd = menuItem;
260 menuItem = [[NSMenuItem alloc] initWithTitle:@"Aspect Ratio" action:nil keyEquivalent:@""];
261 [menuItem setSubmenu:aspectMenu];
262 [menu addItem:menuItem];
263 [aspectMenu release];
264
265 //Add to menubar
266 menuItem = [[NSMenuItem alloc] initWithTitle:@"Movie" action:nil keyEquivalent:@""];
267 [menuItem setSubmenu:menu];
268 [mainMenu addItem:menuItem];
269
270 //Create Window Menu
271 menu = [[NSMenu alloc] initWithTitle:@"Window"];
272
273 menuItem = [[NSMenuItem alloc] initWithTitle:@"Minimize" action:@selector(performMiniaturize:) keyEquivalent:@"m"]; [menu addItem:menuItem];
274 menuItem = [[NSMenuItem alloc] initWithTitle:@"Zoom" action:@selector(performZoom:) keyEquivalent:@""]; [menu addItem:menuItem];
275
276 //Add to menubar
277 menuItem = [[NSMenuItem alloc] initWithTitle:@"Window" action:nil keyEquivalent:@""];
278 [menuItem setSubmenu:menu];
279 [mainMenu addItem:menuItem];
280 [NSApp setWindowsMenu:menu];
281 [NSApp setMainMenu:mainMenu];
282
283 [menu release];
284 [menuItem release];
285 }
286
287 - (void)set_winSizeMult:(float)mult
288 {
289 NSRect frame;
290 int d_width, d_height;
291 aspect(&d_width, &d_height, A_NOZOOM);
292
293 if (vo_fs) {
294 vo_fs = !vo_fs;
295 [self fullscreen:NO];
296 }
297
298 winSizeMult = mult;
299 frame.size.width = d_width * mult;
300 frame.size.height = d_height * mult;
301 [window setContentSize: frame.size];
302 [self reshape];
303 }
304
305 /*
306 Menu Action
307 */
308 - (void)menuAction:(id)sender
309 {
310 if(sender == kHalfScreenCmd)
311 [self set_winSizeMult: 0.5];
312 if(sender == kNormalScreenCmd)
313 [self set_winSizeMult: 1];
314 if(sender == kDoubleScreenCmd)
315 [self set_winSizeMult: 2];
316 if(sender == kFullScreenCmd)
317 {
318 vo_fs = !vo_fs;
319 [self fullscreen:NO];
320 }
321
322 if(sender == kKeepAspectCmd)
323 {
324 vo_keepaspect = !vo_keepaspect;
325 if(vo_keepaspect)
326 [kKeepAspectCmd setState:NSOnState];
327 else
328 [kKeepAspectCmd setState:NSOffState];
329
330 [self reshape];
331 }
332
333 if(sender == kPanScanCmd)
334 {
335 vo_panscan = !vo_panscan;
336 if(vo_panscan)
337 [kPanScanCmd setState:NSOnState];
338 else
339 [kPanScanCmd setState:NSOffState];
340
341 panscan_calc();
342 }
343
344 if(sender == kAspectOrgCmd)
345 change_movie_aspect(-1);
346
347 if(sender == kAspectFullCmd)
348 change_movie_aspect(4.0f/3.0f);
349
350 if(sender == kAspectWideCmd)
351 change_movie_aspect(16.0f/9.0f);
352 }
353
354 /*
355 Toggle Fullscreen
356 */
357 - (void) fullscreen: (BOOL) animate
358 {
359 static NSRect old_frame;
360 static NSRect old_view_frame;
361
362 panscan_calc();
363
364 //go fullscreen
365 if(vo_fs)
366 {
367 if(!vo_rootwin)
368 {
369 SetSystemUIMode( kUIModeAllHidden, kUIOptionAutoShowMenuBar);
370 CGDisplayHideCursor(kCGDirectMainDisplay);
371 mouseHide = YES;
372 }
373
374 old_frame = [window frame]; //save main window size & position
375 [self update_screen_info];
376
377 [window setFrame:NSMakeRect(xinerama_x, xinerama_y, vo_screenwidth, vo_screenheight) display:YES animate:animate]; //zoom-in window with nice useless sfx
378 old_view_frame = [self bounds];
379
380 [self setFrame:NSMakeRect(0, 0, vo_screenwidth, vo_screenheight)];
381 [self setNeedsDisplay:YES];
382 [window setHasShadow:NO];
383 }
384 else
385 {
386 SetSystemUIMode( kUIModeNormal, 0);
387
388 CGDisplayShowCursor(kCGDirectMainDisplay);
389 mouseHide = NO;
390
391 //revert window to previous setting
392 [self setFrame:old_view_frame];
393 [self setNeedsDisplay:YES];
394 [window setHasShadow:YES];
395 [window setFrame:old_frame display:YES animate:animate];//zoom-out window with nice useless sfx
396 }
397 }
398
399 /*
400 Toggle ontop
401 */
402 - (void) ontop
403 {
404 if(vo_ontop)
405 {
406 [window setLevel:NSScreenSaverWindowLevel];
407 }
408 else
409 {
410 [window setLevel:NSNormalWindowLevel];
411 }
412 }
413
414 /*
415 Toggle rootwin
416 */
417 - (void) rootwin
418 {
419 if(vo_rootwin)
420 {
421 [window setLevel:CGWindowLevelForKey(kCGDesktopWindowLevelKey)];
422 [window orderBack:self];
423 }
424 else
425 {
426 [window setLevel:NSNormalWindowLevel];
427 }
428 }
429
430 /*
431 Check event for new event
432 */
433 - (void) check_events
434 {
435 NSEvent *event;
436 int curTime = TickCount()/60;
437
438 //automatically hide mouse cursor (and future on-screen control?)
439 if(vo_fs && !mouseHide && !vo_rootwin)
440 {
441 if(curTime - lastMouseHide >= 5 || lastMouseHide == 0)
442 {
443 CGDisplayHideCursor(kCGDirectMainDisplay);
444 mouseHide = TRUE;
445 lastMouseHide = curTime;
446 }
447 }
448
449 //update activity every 30 seconds to prevent
450 //screensaver from starting up.
451 if(curTime - lastScreensaverUpdate >= 30 || lastScreensaverUpdate == 0)
452 {
453 UpdateSystemActivity(UsrActivity);
454 lastScreensaverUpdate = curTime;
455 }
456
457 event = [NSApp nextEventMatchingMask:NSAnyEventMask untilDate:nil inMode:NSEventTrackingRunLoopMode dequeue:YES];
458 if (event == nil)
459 return;
460 [NSApp sendEvent:event];
461 // Without SDL's bootstrap code (include SDL.h in mplayer.c),
462 // on Leopard, we have trouble to get the play window automatically focused
463 // when the app is actived. The Following code fix this problem.
464 #ifndef CONFIG_SDL
465 if (isLeopardOrLater && [event type] == NSAppKitDefined
466 && [event subtype] == NSApplicationActivatedEventType) {
467 [window makeMainWindow];
468 [window makeKeyAndOrderFront:self];
469 }
470 #endif
471 }
472
473 /*
474 From NSView, respond to key equivalents.
475 */
476 - (BOOL)performKeyEquivalent:(NSEvent *)theEvent
477 {
478 switch([theEvent keyCode])
479 {
480 case 0x21: [window setAlphaValue: winAlpha-=0.05]; return YES;
481 case 0x1e: [window setAlphaValue: winAlpha+=0.05]; return YES;
482 }
483 return NO;
484 }
485
486 /*
487 Process key event
488 */
489 - (void) keyDown: (NSEvent *) theEvent
490 {
491 int key = convert_key([theEvent keyCode], *[[theEvent characters] UTF8String]);
492 if (key != -1)
493 mplayer_put_key(key);
494 }
495
496 /*
497 Process mouse button event
498 */
499 - (void) mouseMoved: (NSEvent *) theEvent
500 {
501 if(vo_fs && !vo_rootwin)
502 {
503 CGDisplayShowCursor(kCGDirectMainDisplay);
504 mouseHide = NO;
505 }
506 if (enable_mouse_movements && !vo_rootwin) {
507 NSPoint p =[self convertPoint:[theEvent locationInWindow] fromView:nil];
508 if ([self mouse:p inRect:[self frame]]) {
509 vo_mouse_movement(p.x, [self frame].size.height - p.y);
510 }
511 }
512 }
513
514 - (void) mouseDown: (NSEvent *) theEvent
515 {
516 [self mouseEvent: theEvent];
517 }
518
519 - (void) mouseUp: (NSEvent *) theEvent
520 {
521 [self mouseEvent: theEvent];
522 }
523
524 - (void) rightMouseDown: (NSEvent *) theEvent
525 {
526 [self mouseEvent: theEvent];
527 }
528
529 - (void) rightMouseUp: (NSEvent *) theEvent
530 {
531 [self mouseEvent: theEvent];
532 }
533
534 - (void) otherMouseDown: (NSEvent *) theEvent
535 {
536 [self mouseEvent: theEvent];
537 }
538
539 - (void) otherMouseUp: (NSEvent *) theEvent
540 {
541 [self mouseEvent: theEvent];
542 }
543
544 - (void) scrollWheel: (NSEvent *) theEvent
545 {
546 if([theEvent deltaY] > 0)
547 mplayer_put_key(MOUSE_BTN3);
548 else
549 mplayer_put_key(MOUSE_BTN4);
550 }
551
552 - (void) mouseEvent: (NSEvent *) theEvent
553 {
554 if ( [theEvent buttonNumber] >= 0 && [theEvent buttonNumber] <= 9 )
555 {
556 int buttonNumber = [theEvent buttonNumber];
557 // Fix to mplayer defined button order: left, middle, right
558 if (buttonNumber == 1)
559 buttonNumber = 2;
560 else if (buttonNumber == 2)
561 buttonNumber = 1;
562 switch([theEvent type])
563 {
564 case NSLeftMouseDown:
565 case NSRightMouseDown:
566 case NSOtherMouseDown:
567 mplayer_put_key((MOUSE_BTN0 + buttonNumber) | MP_KEY_DOWN);
568 break;
569 case NSLeftMouseUp:
570 case NSRightMouseUp:
571 case NSOtherMouseUp:
572 mplayer_put_key(MOUSE_BTN0 + buttonNumber);
573 break;
574 }
575 }
576 }
577
578 /*
579 NSResponder
580 */
581 - (BOOL) acceptsFirstResponder
582 {
583 return YES;
584 }
585
586 - (BOOL) becomeFirstResponder
587 {
588 return YES;
589 }
590
591 - (BOOL) resignFirstResponder
592 {
593 return YES;
594 }
595
596 - (BOOL)windowShouldClose:(id)sender
597 {
598 mplayer_put_key(KEY_CLOSE_WIN);
599 // We have to wait for MPlayer to handle this,
600 // otherwise we are in trouble if the
601 // KEY_CLOSE_WIN handler is disabled
602 return NO;
603 }
604
605 - (void)handleQuitEvent:(NSAppleEventDescriptor*)e withReplyEvent:(NSAppleEventDescriptor*)r
606 {
607 mplayer_put_key(KEY_CLOSE_WIN);
608 }
609 @end