1 /*****************************************************************************
2 * vout.m: MacOS X video output module
3 *****************************************************************************
4 * Copyright (C) 2001-2003 VideoLAN
7 * Authors: Colin Delacroix <colin@zoy.org>
8 * Florian G. Pflug <fgp@phlo.org>
9 * Jon Lech Johansen <jon-vl@nanocrew.net>
10 * Derk-Jan Hartman <thedj@users.sourceforge.net>
11 * Eric Petit <titer@m0k.org>
13 * This program is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License as published by
15 * the Free Software Foundation; either version 2 of the License, or
16 * (at your option) any later version.
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
23 * You should have received a copy of the GNU General Public License
24 * along with this program; if not, write to the Free Software
25 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
26 *****************************************************************************/
28 /*****************************************************************************
30 *****************************************************************************/
31 #include <errno.h> /* ENOMEM */
32 #include <stdlib.h> /* free() */
33 #include <string.h> /* strerror() */
35 #include <QuickTime/QuickTime.h>
37 #include <OpenGL/OpenGL.h>
38 #include <OpenGL/gl.h>
39 #include <OpenGL/glext.h>
46 #define QT_MAX_DIRECTBUFFERS 10
47 #define VL_MAX_DISPLAYS 16
49 #define OPENGL_EFFECT_NONE 1
50 #define OPENGL_EFFECT_CUBE 2
51 #define OPENGL_EFFECT_TRANSPARENT_CUBE 4
58 /* When using I420 output */
59 PlanarPixmapInfoYUV420 pixmap_i420;
62 /*****************************************************************************
64 *****************************************************************************/
66 static int InitVideo ( vout_thread_t * );
67 static void EndVideo ( vout_thread_t * );
68 static int ManageVideo ( vout_thread_t * );
69 static void DisplayVideo ( vout_thread_t *, picture_t * );
70 static int ControlVideo ( vout_thread_t *, int, va_list );
72 static int CoCreateWindow ( vout_thread_t * );
73 static int CoDestroyWindow ( vout_thread_t * );
75 static int CoToggleFullscreen ( vout_thread_t * );
76 static int CoSetWindowOnTop ( vout_thread_t *, BOOL );
77 static void VLCHideMouse ( vout_thread_t *, BOOL );
79 static void QTScaleMatrix ( vout_thread_t * );
80 static int QTCreateSequence ( vout_thread_t * );
81 static void QTDestroySequence ( vout_thread_t * );
82 static int QTNewPicture ( vout_thread_t *, picture_t * );
83 static void QTFreePicture ( vout_thread_t *, picture_t * );
85 /*****************************************************************************
86 * OpenVideo: allocates MacOS X video thread output method
87 *****************************************************************************
88 * This function allocates and initializes a MacOS X vout method.
89 *****************************************************************************/
90 int E_(OpenVideo) ( vlc_object_t *p_this )
92 vout_thread_t * p_vout = (vout_thread_t *)p_this;
97 p_vout->p_sys = malloc( sizeof( vout_sys_t ) );
98 if( p_vout->p_sys == NULL )
100 msg_Err( p_vout, "out of memory" );
104 memset( p_vout->p_sys, 0, sizeof( vout_sys_t ) );
106 /* Wait for a MacOS X interface to appear. Timeout is 2 seconds. */
107 for( i_timeout = 20 ; i_timeout-- ; )
111 msleep( INTF_IDLE_SLEEP );
117 /* no MacOS X intf, unable to communicate with MT */
118 msg_Err( p_vout, "no MacOS X interface present" );
119 free( p_vout->p_sys );
123 p_vout->pf_init = InitVideo;
124 p_vout->pf_end = EndVideo;
125 p_vout->pf_manage = ManageVideo;
126 p_vout->pf_render = NULL;
127 p_vout->pf_display = DisplayVideo;
128 p_vout->pf_control = ControlVideo;
130 p_vout->p_sys->o_pool = [[NSAutoreleasePool alloc] init];
131 p_vout->p_sys->b_mouse_moved = VLC_TRUE;
132 p_vout->p_sys->i_time_mouse_last_moved = mdate();
134 /* set original window size */
135 p_vout->p_sys->s_rect.size.width = p_vout->i_window_width;
136 p_vout->p_sys->s_rect.size.height = p_vout->i_window_height;
138 var_Create( p_vout, "macosx-vout", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
139 var_Create( p_vout, "macosx-vdev", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
140 var_Create( p_vout, "macosx-fill", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
141 var_Create( p_vout, "macosx-stretch", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
142 var_Create( p_vout, "macosx-opaqueness", VLC_VAR_FLOAT | VLC_VAR_DOINHERIT );
143 var_Create( p_vout, "macosx-opengl-effect", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
145 /* Check if we should use QuickTime or OpenGL */
146 var_Get( p_vout, "macosx-vout", &val);
148 if( !strncmp( val.psz_string, "auto", 4 ) )
150 p_vout->p_sys->i_opengl = CGDisplayUsesOpenGLAcceleration( kCGDirectMainDisplay );
152 else if( !strncmp( val.psz_string, "opengl", 6 ) )
154 p_vout->p_sys->i_opengl = VLC_TRUE;
158 p_vout->p_sys->i_opengl = VLC_FALSE;
160 free( val.psz_string );
162 if( !p_vout->p_sys->i_opengl )
164 /* Initialize QuickTime */
165 p_vout->p_sys->h_img_descr =
166 (ImageDescriptionHandle)NewHandleClear( sizeof(ImageDescription) );
167 p_vout->p_sys->p_matrix =
168 (MatrixRecordPtr)malloc( sizeof(MatrixRecord) );
169 p_vout->p_sys->p_fullscreen_state = NULL;
171 if( ( err = EnterMovies() ) != noErr )
173 msg_Err( p_vout, "EnterMovies failed: %d", err );
174 free( p_vout->p_sys->p_matrix );
175 DisposeHandle( (Handle)p_vout->p_sys->h_img_descr );
176 free( p_vout->p_sys );
180 /* Damn QT isn't thread safe. so keep a lock in the p_vlc object */
181 vlc_mutex_lock( &p_vout->p_vlc->quicktime_lock );
183 /* Can we find the right chroma ? */
184 err = FindCodec( kYUV420CodecType, bestSpeedCodec,
185 nil, &p_vout->p_sys->img_dc );
187 vlc_mutex_unlock( &p_vout->p_vlc->quicktime_lock );
189 if( err == noErr && p_vout->p_sys->img_dc != 0 )
191 p_vout->output.i_chroma = VLC_FOURCC('I','4','2','0');
192 p_vout->p_sys->i_codec = kYUV420CodecType;
196 msg_Err( p_vout, "failed to find an appropriate codec" );
199 if( p_vout->p_sys->img_dc == 0 )
201 free( p_vout->p_sys->p_matrix );
202 DisposeHandle( (Handle)p_vout->p_sys->h_img_descr );
203 free( p_vout->p_sys );
206 msg_Dbg( p_vout, "using Quartz mode" );
210 p_vout->output.i_chroma = VLC_FOURCC('Y','U','Y','2');
211 msg_Dbg( p_vout, "using OpenGL mode" );
214 /* Setup the menuitem for the multiple displays. Read the vlc preference (macosx-vdev) for the primary display */
215 NSArray * o_screens = [NSScreen screens];
216 if( [o_screens count] > 0 && var_Type( p_vout, "video-device" ) == 0 )
219 vlc_value_t val2, text;
222 var_Get( p_vout, "macosx-vdev", &val );
224 var_Create( p_vout, "video-device", VLC_VAR_INTEGER |
226 text.psz_string = _("Video device");
227 var_Change( p_vout, "video-device", VLC_VAR_SETTEXT, &text, NULL );
229 NSEnumerator * o_enumerator = [o_screens objectEnumerator];
231 while( (o_screen = [o_enumerator nextObject]) != NULL )
234 NSRect s_rect = [o_screen frame];
236 snprintf( psz_temp, sizeof(psz_temp)/sizeof(psz_temp[0])-1,
237 "%s %d (%dx%d)", _("Screen"), i,
238 (int)s_rect.size.width, (int)s_rect.size.height );
240 text.psz_string = psz_temp;
242 var_Change( p_vout, "video-device",
243 VLC_VAR_ADDCHOICE, &val2, &text );
245 if( ( i - 1 ) == val.i_int )
247 var_Set( p_vout, "video-device", val2 );
252 var_AddCallback( p_vout, "video-device", vout_VarCallback,
255 val2.b_bool = VLC_TRUE;
256 var_Set( p_vout, "intf-change", val2 );
260 if( CoCreateWindow( p_vout ) )
262 msg_Err( p_vout, "unable to create a window" );
263 if( !p_vout->p_sys->i_opengl )
265 free( p_vout->p_sys->p_matrix );
266 DisposeHandle( (Handle)p_vout->p_sys->h_img_descr );
268 free( p_vout->p_sys );
275 /*****************************************************************************
276 * CloseVideo: destroy video thread output method
277 *****************************************************************************/
278 void E_(CloseVideo) ( vlc_object_t *p_this )
280 NSAutoreleasePool *o_pool = [[NSAutoreleasePool alloc] init];
281 vout_thread_t * p_vout = (vout_thread_t *)p_this;
283 if( CoDestroyWindow( p_vout ) )
285 msg_Err( p_vout, "unable to destroy window" );
288 if ( p_vout->p_sys->p_fullscreen_state != NULL )
290 EndFullScreen ( p_vout->p_sys->p_fullscreen_state, NULL );
293 if( !p_vout->p_sys->i_opengl )
295 /* Clean Up Quicktime environment */
297 free( p_vout->p_sys->p_matrix );
298 DisposeHandle( (Handle)p_vout->p_sys->h_img_descr );
302 [p_vout->p_sys->o_glview cleanUp];
306 free( p_vout->p_sys );
309 /*****************************************************************************
310 * InitVideo: initialize video thread output method
311 *****************************************************************************/
312 static int InitOpenGL ( vout_thread_t *p_vout );
313 static int InitQuickTime( vout_thread_t *p_vout );
314 static int InitVideo ( vout_thread_t *p_vout )
316 I_OUTPUTPICTURES = 0;
318 /* Initialize the output structure; we already found a codec,
319 * and the corresponding chroma we will be using. Since we can
320 * arbitrary scale, stick to the coordinates and aspect. */
321 p_vout->output.i_width = p_vout->render.i_width;
322 p_vout->output.i_height = p_vout->render.i_height;
323 p_vout->output.i_aspect = p_vout->render.i_aspect;
325 if( p_vout->p_sys->i_opengl )
327 return InitOpenGL( p_vout );
331 return InitQuickTime( p_vout );
335 static int InitOpenGL( vout_thread_t *p_vout )
337 picture_t *p_pic = NULL;
341 /* Apple OpenGL extensions only accept YUV as YUY2 */
342 p_vout->output.i_chroma = VLC_FOURCC('Y','U','Y','2');
343 p_vout->output.i_rmask = 0xFF0000;
344 p_vout->output.i_gmask = 0x00FF00;
345 p_vout->output.i_bmask = 0x0000FF;
347 /* Allocate our 2 picture buffers */
348 i_bytes = 2 * p_vout->output.i_width * p_vout->output.i_height;
349 p_vout->p_sys->p_data[0] = vlc_memalign(
350 &p_vout->p_sys->p_data_orig[0], 16, i_bytes );
351 p_vout->p_sys->p_data[1] = vlc_memalign(
352 &p_vout->p_sys->p_data_orig[1], 16, i_bytes );
353 p_vout->p_sys->i_cur_pic = 1;
355 /* We declare only one picture and will switch buffers manually */
356 while( I_OUTPUTPICTURES < 1 )
358 /* Find an empty picture slot */
359 for( i_index = 0; i_index < VOUT_MAX_PICTURES; i_index++ )
361 if( p_vout->p_picture[ i_index ].i_status == FREE_PICTURE )
363 p_pic = p_vout->p_picture + i_index;
372 vout_InitPicture( VLC_OBJECT( p_vout ), p_pic,
373 p_vout->output.i_chroma, p_vout->output.i_width,
374 p_vout->output.i_height, p_vout->output.i_aspect );
375 p_pic->p_data = p_vout->p_sys->p_data[0];
376 p_pic->p[0].p_pixels = p_pic->p_data;
377 for( i_index = 1; i_index < p_pic->i_planes; i_index++ )
379 p_pic->p[i_index].p_pixels =
380 p_pic->p[i_index-1].p_pixels +
381 p_pic->p[i_index-1].i_lines *
382 p_pic->p[i_index-1].i_pitch;
385 p_pic->i_status = DESTROYED_PICTURE;
386 p_pic->i_type = DIRECT_PICTURE;
388 PP_OUTPUTPICTURE[ I_OUTPUTPICTURES ] = p_pic;
392 [p_vout->p_sys->o_glview lockFocus];
393 [p_vout->p_sys->o_glview initTextures];
394 [p_vout->p_sys->o_glview reshape];
395 [p_vout->p_sys->o_glview unlockFocus];
400 static int InitQuickTime( vout_thread_t *p_vout )
405 SetPort( p_vout->p_sys->p_qdport );
406 QTScaleMatrix( p_vout );
408 if( QTCreateSequence( p_vout ) )
410 msg_Err( p_vout, "unable to create sequence" );
414 /* Try to initialize up to QT_MAX_DIRECTBUFFERS direct buffers */
415 while( I_OUTPUTPICTURES < QT_MAX_DIRECTBUFFERS )
419 /* Find an empty picture slot */
420 for( i_index = 0; i_index < VOUT_MAX_PICTURES; i_index++ )
422 if( p_vout->p_picture[ i_index ].i_status == FREE_PICTURE )
424 p_pic = p_vout->p_picture + i_index;
429 /* Allocate the picture */
430 if( p_pic == NULL || QTNewPicture( p_vout, p_pic ) )
435 p_pic->i_status = DESTROYED_PICTURE;
436 p_pic->i_type = DIRECT_PICTURE;
438 PP_OUTPUTPICTURE[ I_OUTPUTPICTURES ] = p_pic;
444 /*****************************************************************************
445 * EndVideo: terminate video thread output method
446 *****************************************************************************/
447 static void EndVideo( vout_thread_t *p_vout )
451 if( !p_vout->p_sys->i_opengl )
453 QTDestroySequence( p_vout );
456 /* Free the direct buffers we allocated */
457 for( i_index = I_OUTPUTPICTURES; i_index; )
460 if( !p_vout->p_sys->i_opengl )
462 QTFreePicture( p_vout, PP_OUTPUTPICTURE[ i_index ] );
466 free( p_vout->p_sys->p_data_orig[0] );
467 free( p_vout->p_sys->p_data_orig[1] );
472 /*****************************************************************************
473 * ManageVideo: handle events
474 *****************************************************************************
475 * This function should be called regularly by video output thread. It manages
476 * console events. It returns a non null value on error.
477 *****************************************************************************/
478 static int ManageVideo( vout_thread_t *p_vout )
480 if( p_vout->i_changes & VOUT_FULLSCREEN_CHANGE )
482 if( CoToggleFullscreen( p_vout ) )
487 p_vout->i_changes &= ~VOUT_FULLSCREEN_CHANGE;
490 if( p_vout->i_changes & VOUT_SIZE_CHANGE )
492 if( !p_vout->p_sys->i_opengl )
494 QTScaleMatrix( p_vout );
495 SetDSequenceMatrix( p_vout->p_sys->i_seq,
496 p_vout->p_sys->p_matrix );
499 p_vout->i_changes &= ~VOUT_SIZE_CHANGE;
502 /* hide/show mouse cursor
503 * this code looks unnecessarily complicated, but is necessary like this.
504 * it has to deal with multiple monitors and therefore checks a lot */
505 if( !p_vout->p_sys->b_mouse_moved && p_vout->b_fullscreen )
507 if( mdate() - p_vout->p_sys->i_time_mouse_last_moved > 3000000 )
509 VLCHideMouse( p_vout, YES );
512 else if ( p_vout->p_sys->b_mouse_moved && p_vout->b_fullscreen )
514 VLCHideMouse( p_vout, NO );
517 /* disable screen saver */
518 UpdateSystemActivity( UsrActivity );
523 /*****************************************************************************
524 * vout_Display: displays previously rendered output
525 *****************************************************************************
526 * This function sends the currently rendered image to the display.
527 *****************************************************************************/
528 static void DisplayVideo( vout_thread_t *p_vout, picture_t *p_pic )
530 if( !p_vout->p_sys->i_opengl )
535 if( ( err = DecompressSequenceFrameS(
536 p_vout->p_sys->i_seq,
537 p_pic->p_sys->p_info,
538 p_pic->p_sys->i_size,
539 codecFlagUseImageBuffer, &flags, nil ) != noErr ) )
541 msg_Warn( p_vout, "DecompressSequenceFrameS failed: %d", err );
545 QDFlushPortBuffer( p_vout->p_sys->p_qdport, nil );
550 if( [p_vout->p_sys->o_glview lockFocusIfCanDraw] )
553 int i_old = p_vout->p_sys->i_cur_pic;
554 int i_new = ( i_old + 1 ) % 2;
556 /* Draw the new picture */
557 p_vout->p_sys->i_cur_pic = i_new;
558 [p_vout->p_sys->o_glview drawRect:
559 [p_vout->p_sys->o_glview bounds]];
561 /* Reload the other texture. Textures have to be reloaded
562 before the buffer is filled (thanks to gcc from
563 arstechnica forums) */
564 [p_vout->p_sys->o_glview reloadTexture: i_old];
567 p_pic->p_data = p_vout->p_sys->p_data[i_old];
568 p_pic->p[0].p_pixels = p_pic->p_data;
569 for( i_index = 1; i_index < p_pic->i_planes; i_index++ )
571 p_pic->p[i_index].p_pixels =
572 p_pic->p[i_index-1].p_pixels +
573 p_pic->p[i_index-1].i_lines *
574 p_pic->p[i_index-1].i_pitch;
576 [p_vout->p_sys->o_glview unlockFocus];
581 /*****************************************************************************
582 * ControlVideo: control facility for the vout
583 *****************************************************************************/
584 static int ControlVideo( vout_thread_t *p_vout, int i_query, va_list args )
590 case VOUT_SET_STAY_ON_TOP:
591 b_arg = va_arg( args, vlc_bool_t );
592 CoSetWindowOnTop( p_vout, b_arg );
598 return vout_vaControlDefault( p_vout, i_query, args );
602 /*****************************************************************************
603 * CoCreateWindow: create new window
604 *****************************************************************************
605 * Returns 0 on success, 1 otherwise
606 *****************************************************************************/
607 static int CoCreateWindow( vout_thread_t *p_vout )
611 vlc_bool_t b_main_screen;
612 NSAutoreleasePool *o_pool = [[NSAutoreleasePool alloc] init];
614 /* Allocate the window. It will be autoreleased when it receives 'close' */
615 p_vout->p_sys->o_window = [VLCWindow alloc];
616 [p_vout->p_sys->o_window setReleasedWhenClosed: YES];
618 /* Find out on which screen to open the window */
619 var_Get( p_vout, "video-device", &val );
622 /* No preference specified. Use the main screen */
623 o_screen = [NSScreen mainScreen];
628 NSArray *o_screens = [NSScreen screens];
629 unsigned int i_index = val.i_int;
631 if( [o_screens count] < i_index )
633 o_screen = [NSScreen mainScreen];
639 o_screen = [o_screens objectAtIndex: i_index];
641 var_Set( p_vout, "macosx-vdev", val );
642 b_main_screen = (i_index == 0);
646 if( p_vout->b_fullscreen )
648 NSRect screen_rect = [o_screen frame];
649 screen_rect.origin.x = screen_rect.origin.y = 0;
651 if ( b_main_screen && p_vout->p_sys->p_fullscreen_state == NULL )
652 BeginFullScreen( &p_vout->p_sys->p_fullscreen_state, NULL, 0, 0,
653 NULL, NULL, fullScreenAllowEvents );
655 /* Creates a window with size: screen_rect on o_screen */
656 [p_vout->p_sys->o_window
657 initWithContentRect: screen_rect
658 styleMask: NSBorderlessWindowMask
659 backing: NSBackingStoreBuffered
660 defer: NO screen: o_screen];
662 [p_vout->p_sys->o_window setVout: p_vout];
663 p_vout->p_sys->b_mouse_moved = YES;
664 p_vout->p_sys->i_time_mouse_last_moved = mdate();
668 unsigned int i_stylemask = NSTitledWindowMask |
669 NSMiniaturizableWindowMask |
670 NSClosableWindowMask |
671 NSResizableWindowMask;
673 if ( p_vout->p_sys->p_fullscreen_state != NULL )
674 EndFullScreen ( p_vout->p_sys->p_fullscreen_state, NULL );
675 p_vout->p_sys->p_fullscreen_state = NULL;
677 [p_vout->p_sys->o_window
678 initWithContentRect: p_vout->p_sys->s_rect
679 styleMask: i_stylemask
680 backing: NSBackingStoreBuffered
681 defer: NO screen: o_screen];
683 [p_vout->p_sys->o_window setVout: p_vout];
684 var_Get( p_vout, "macosx-opaqueness", &val);
685 [p_vout->p_sys->o_window setAlphaValue: val.f_float];
687 var_Get( p_vout, "video-on-top", &val );
688 if( val.b_bool ) [p_vout->p_sys->o_window setLevel: NSStatusWindowLevel];
690 if( !p_vout->p_sys->b_pos_saved ) [p_vout->p_sys->o_window center];
693 if( !p_vout->p_sys->i_opengl )
695 #define o_qtview p_vout->p_sys->o_qtview
696 o_qtview = [[VLCQTView alloc] init];
697 [p_vout->p_sys->o_window setContentView: o_qtview];
698 [o_qtview autorelease];
700 /* Retrieve the QuickDraw port */
701 [o_qtview lockFocus];
702 p_vout->p_sys->p_qdport = [o_qtview qdPort];
703 [o_qtview unlockFocus];
708 #define o_glview p_vout->p_sys->o_glview
709 o_glview = [[VLCGLView alloc] initWithFrame: p_vout->p_sys->s_rect vout: p_vout];
710 [p_vout->p_sys->o_window setContentView: o_glview];
711 [o_glview autorelease];
715 [p_vout->p_sys->o_window updateTitle];
716 [p_vout->p_sys->o_window makeKeyAndOrderFront: nil];
722 /*****************************************************************************
723 * CoDestroyWindow: destroy window
724 *****************************************************************************
725 * Returns 0 on success, 1 otherwise
726 *****************************************************************************/
727 static int CoDestroyWindow( vout_thread_t *p_vout )
729 NSAutoreleasePool *o_pool = [[NSAutoreleasePool alloc] init];
730 VLCHideMouse( p_vout, NO );
732 if( !p_vout->b_fullscreen )
736 /* remember the window position before we enter fullscreen */
737 s_rect = [[p_vout->p_sys->o_window contentView] frame];
738 p_vout->p_sys->s_rect.size = s_rect.size;
740 s_rect = [p_vout->p_sys->o_window frame];
741 p_vout->p_sys->s_rect.origin = s_rect.origin;
743 p_vout->p_sys->b_pos_saved = YES;
746 p_vout->p_sys->p_qdport = nil;
747 [p_vout->p_sys->o_window close];
748 p_vout->p_sys->o_window = nil;
753 /*****************************************************************************
754 * CoToggleFullscreen: toggle fullscreen
755 *****************************************************************************
756 * Returns 0 on success, 1 otherwise
757 *****************************************************************************/
758 static int CoToggleFullscreen( vout_thread_t *p_vout )
760 NSAutoreleasePool *o_pool = [[NSAutoreleasePool alloc] init];
762 if( !p_vout->p_sys->i_opengl )
764 QTDestroySequence( p_vout );
767 if( CoDestroyWindow( p_vout ) )
769 msg_Err( p_vout, "unable to destroy window" );
773 p_vout->b_fullscreen = !p_vout->b_fullscreen;
775 if( CoCreateWindow( p_vout ) )
777 msg_Err( p_vout, "unable to create window" );
781 if( p_vout->p_sys->i_opengl )
783 [p_vout->p_sys->o_glview lockFocus];
784 [p_vout->p_sys->o_glview initTextures];
785 [p_vout->p_sys->o_glview reshape];
786 [p_vout->p_sys->o_glview drawRect:
787 [p_vout->p_sys->o_glview bounds]];
788 [p_vout->p_sys->o_glview unlockFocus];
792 SetPort( p_vout->p_sys->p_qdport );
793 QTScaleMatrix( p_vout );
795 if( QTCreateSequence( p_vout ) )
797 msg_Err( p_vout, "unable to create sequence" );
806 /*****************************************************************************
807 * CoSetWindowOnTop: Switches the "always on top" state of the video window
808 *****************************************************************************
809 * Returns 0 on success, 1 otherwise
810 *****************************************************************************/
811 static int CoSetWindowOnTop( vout_thread_t *p_vout, BOOL b_on_top )
813 if( p_vout->p_sys->o_window )
817 [p_vout->p_sys->o_window setLevel: NSStatusWindowLevel];
821 [p_vout->p_sys->o_window setLevel: NSNormalWindowLevel];
828 /*****************************************************************************
829 * VLCHideMouse: if b_hide then hide the cursor
830 *****************************************************************************/
831 static void VLCHideMouse ( vout_thread_t *p_vout, BOOL b_hide )
836 NSWindow *o_window = p_vout->p_sys->o_window;
837 NSView *o_contents = [o_window contentView];
839 s_rect = [o_contents bounds];
840 ml = [o_window convertScreenToBase:[NSEvent mouseLocation]];
841 ml = [o_contents convertPoint:ml fromView:nil];
842 b_inside = [o_contents mouse: ml inRect: s_rect];
844 if ( b_hide && b_inside )
846 /* only hide if mouse over VLCQTView */
847 [NSCursor setHiddenUntilMouseMoves: YES];
851 [NSCursor setHiddenUntilMouseMoves: NO];
853 p_vout->p_sys->b_mouse_moved = NO;
854 p_vout->p_sys->i_time_mouse_last_moved = mdate();
858 /*****************************************************************************
859 * QTScaleMatrix: scale matrix
860 *****************************************************************************/
861 static void QTScaleMatrix( vout_thread_t *p_vout )
865 unsigned int i_width, i_height;
866 Fixed factor_x, factor_y;
867 unsigned int i_offset_x = 0;
868 unsigned int i_offset_y = 0;
870 GetPortBounds( p_vout->p_sys->p_qdport, &s_rect );
872 i_width = s_rect.right - s_rect.left;
873 i_height = s_rect.bottom - s_rect.top;
875 var_Get( p_vout, "macosx-stretch", &val );
878 factor_x = FixDiv( Long2Fix( i_width ),
879 Long2Fix( p_vout->output.i_width ) );
880 factor_y = FixDiv( Long2Fix( i_height ),
881 Long2Fix( p_vout->output.i_height ) );
884 else if( i_height * p_vout->output.i_aspect < i_width * VOUT_ASPECT_FACTOR )
886 int i_adj_width = i_height * p_vout->output.i_aspect /
889 factor_x = FixDiv( Long2Fix( i_adj_width ),
890 Long2Fix( p_vout->output.i_width ) );
891 factor_y = FixDiv( Long2Fix( i_height ),
892 Long2Fix( p_vout->output.i_height ) );
894 i_offset_x = (i_width - i_adj_width) / 2;
898 int i_adj_height = i_width * VOUT_ASPECT_FACTOR /
899 p_vout->output.i_aspect;
901 factor_x = FixDiv( Long2Fix( i_width ),
902 Long2Fix( p_vout->output.i_width ) );
903 factor_y = FixDiv( Long2Fix( i_adj_height ),
904 Long2Fix( p_vout->output.i_height ) );
906 i_offset_y = (i_height - i_adj_height) / 2;
909 SetIdentityMatrix( p_vout->p_sys->p_matrix );
911 ScaleMatrix( p_vout->p_sys->p_matrix,
913 Long2Fix(0), Long2Fix(0) );
915 TranslateMatrix( p_vout->p_sys->p_matrix,
916 Long2Fix(i_offset_x), Long2Fix(i_offset_y) );
919 /*****************************************************************************
920 * QTCreateSequence: create a new sequence
921 *****************************************************************************
922 * Returns 0 on success, 1 otherwise
923 *****************************************************************************/
924 static int QTCreateSequence( vout_thread_t *p_vout )
927 ImageDescriptionPtr p_descr;
929 HLock( (Handle)p_vout->p_sys->h_img_descr );
930 p_descr = *p_vout->p_sys->h_img_descr;
932 p_descr->idSize = sizeof(ImageDescription);
933 p_descr->cType = p_vout->p_sys->i_codec;
934 p_descr->version = 1;
935 p_descr->revisionLevel = 0;
936 p_descr->vendor = 'appl';
937 p_descr->width = p_vout->output.i_width;
938 p_descr->height = p_vout->output.i_height;
939 p_descr->hRes = Long2Fix(72);
940 p_descr->vRes = Long2Fix(72);
941 p_descr->spatialQuality = codecLosslessQuality;
942 p_descr->frameCount = 1;
943 p_descr->clutID = -1;
944 p_descr->dataSize = 0;
947 HUnlock( (Handle)p_vout->p_sys->h_img_descr );
949 if( ( err = DecompressSequenceBeginS(
950 &p_vout->p_sys->i_seq,
951 p_vout->p_sys->h_img_descr,
953 p_vout->p_sys->p_qdport,
955 p_vout->p_sys->p_matrix,
957 codecFlagUseImageBuffer,
958 codecLosslessQuality,
959 p_vout->p_sys->img_dc ) ) )
961 msg_Err( p_vout, "DecompressSequenceBeginS failed: %d", err );
968 /*****************************************************************************
969 * QTDestroySequence: destroy sequence
970 *****************************************************************************/
971 static void QTDestroySequence( vout_thread_t *p_vout )
973 CDSequenceEnd( p_vout->p_sys->i_seq );
976 /*****************************************************************************
977 * QTNewPicture: allocate a picture
978 *****************************************************************************
979 * Returns 0 on success, 1 otherwise
980 *****************************************************************************/
981 static int QTNewPicture( vout_thread_t *p_vout, picture_t *p_pic )
983 int i_width = p_vout->output.i_width;
984 int i_height = p_vout->output.i_height;
986 /* We know the chroma, allocate a buffer which will be used
987 * directly by the decoder */
988 p_pic->p_sys = malloc( sizeof( picture_sys_t ) );
990 if( p_pic->p_sys == NULL )
995 switch( p_vout->output.i_chroma )
997 case VLC_FOURCC('I','4','2','0'):
999 p_pic->p_sys->p_info = (void *)&p_pic->p_sys->pixmap_i420;
1000 p_pic->p_sys->i_size = sizeof(PlanarPixmapInfoYUV420);
1002 /* Allocate the memory buffer */
1003 p_pic->p_data = vlc_memalign( &p_pic->p_data_orig,
1004 16, i_width * i_height * 3 / 2 );
1007 p_pic->Y_PIXELS = p_pic->p_data;
1008 p_pic->p[Y_PLANE].i_lines = i_height;
1009 p_pic->p[Y_PLANE].i_pitch = i_width;
1010 p_pic->p[Y_PLANE].i_pixel_pitch = 1;
1011 p_pic->p[Y_PLANE].i_visible_pitch = i_width;
1014 p_pic->U_PIXELS = p_pic->Y_PIXELS + i_height * i_width;
1015 p_pic->p[U_PLANE].i_lines = i_height / 2;
1016 p_pic->p[U_PLANE].i_pitch = i_width / 2;
1017 p_pic->p[U_PLANE].i_pixel_pitch = 1;
1018 p_pic->p[U_PLANE].i_visible_pitch = i_width / 2;
1021 p_pic->V_PIXELS = p_pic->U_PIXELS + i_height * i_width / 4;
1022 p_pic->p[V_PLANE].i_lines = i_height / 2;
1023 p_pic->p[V_PLANE].i_pitch = i_width / 2;
1024 p_pic->p[V_PLANE].i_pixel_pitch = 1;
1025 p_pic->p[V_PLANE].i_visible_pitch = i_width / 2;
1027 /* We allocated 3 planes */
1028 p_pic->i_planes = 3;
1030 #define P p_pic->p_sys->pixmap_i420
1031 P.componentInfoY.offset = (void *)p_pic->Y_PIXELS
1032 - p_pic->p_sys->p_info;
1033 P.componentInfoCb.offset = (void *)p_pic->U_PIXELS
1034 - p_pic->p_sys->p_info;
1035 P.componentInfoCr.offset = (void *)p_pic->V_PIXELS
1036 - p_pic->p_sys->p_info;
1038 P.componentInfoY.rowBytes = i_width;
1039 P.componentInfoCb.rowBytes = i_width / 2;
1040 P.componentInfoCr.rowBytes = i_width / 2;
1046 /* Unknown chroma, tell the guy to get lost */
1047 free( p_pic->p_sys );
1048 msg_Err( p_vout, "never heard of chroma 0x%.8x (%4.4s)",
1049 p_vout->output.i_chroma, (char*)&p_vout->output.i_chroma );
1050 p_pic->i_planes = 0;
1057 /*****************************************************************************
1058 * QTFreePicture: destroy a picture allocated with QTNewPicture
1059 *****************************************************************************/
1060 static void QTFreePicture( vout_thread_t *p_vout, picture_t *p_pic )
1062 switch( p_vout->output.i_chroma )
1064 case VLC_FOURCC('I','4','2','0'):
1065 free( p_pic->p_data_orig );
1069 free( p_pic->p_sys );
1072 /*****************************************************************************
1073 * VLCWindow implementation
1074 *****************************************************************************/
1075 @implementation VLCWindow
1077 - (void)setVout:(vout_thread_t *)_p_vout
1082 - (vout_thread_t *)getVout
1087 - (void)scaleWindowWithFactor: (float)factor
1090 int i_corrected_height, i_corrected_width;
1091 NSPoint topleftbase;
1092 NSPoint topleftscreen;
1094 if ( !p_vout->b_fullscreen )
1097 topleftbase.y = [self frame].size.height;
1098 topleftscreen = [self convertBaseToScreen: topleftbase];
1100 if( p_vout->output.i_height * p_vout->output.i_aspect >
1101 p_vout->output.i_width * VOUT_ASPECT_FACTOR )
1103 i_corrected_width = p_vout->output.i_height * p_vout->output.i_aspect /
1105 newsize.width = (int) ( i_corrected_width * factor );
1106 newsize.height = (int) ( p_vout->render.i_height * factor );
1110 i_corrected_height = p_vout->output.i_width * VOUT_ASPECT_FACTOR /
1111 p_vout->output.i_aspect;
1112 newsize.width = (int) ( p_vout->render.i_width * factor );
1113 newsize.height = (int) ( i_corrected_height * factor );
1116 [self setContentSize: newsize];
1118 [self setFrameTopLeftPoint: topleftscreen];
1119 p_vout->i_changes |= VOUT_SIZE_CHANGE;
1123 - (void)toggleFloatOnTop
1126 if( var_Get( p_vout, "video-on-top", &val )>=0 && val.b_bool)
1128 val.b_bool = VLC_FALSE;
1129 var_Set( p_vout, "video-on-top", val );
1133 val.b_bool = VLC_TRUE;
1134 var_Set( p_vout, "video-on-top", val );
1138 - (void)toggleFullscreen
1141 val.b_bool = !p_vout->b_fullscreen;
1142 var_Set( p_vout, "fullscreen", val );
1145 - (BOOL)isFullscreen
1147 return( p_vout->b_fullscreen );
1150 - (BOOL)canBecomeKeyWindow
1155 - (BOOL)performKeyEquivalent:(NSEvent *)o_event
1157 return [[VLCMain sharedInstance] hasDefinedShortcutKey:o_event];
1160 - (void)keyDown:(NSEvent *)o_event
1164 unsigned int i_pressed_modifiers = 0;
1167 i_pressed_modifiers = [o_event modifierFlags];
1169 if( i_pressed_modifiers & NSShiftKeyMask )
1170 val.i_int |= KEY_MODIFIER_SHIFT;
1171 if( i_pressed_modifiers & NSControlKeyMask )
1172 val.i_int |= KEY_MODIFIER_CTRL;
1173 if( i_pressed_modifiers & NSAlternateKeyMask )
1174 val.i_int |= KEY_MODIFIER_ALT;
1175 if( i_pressed_modifiers & NSCommandKeyMask )
1176 val.i_int |= KEY_MODIFIER_COMMAND;
1178 key = [[o_event charactersIgnoringModifiers] characterAtIndex: 0];
1182 /* Escape should always get you out of fullscreen */
1183 if( key == (unichar) 0x1b )
1185 if( [self isFullscreen] )
1187 [self toggleFullscreen];
1190 else if ( key == ' ' )
1192 playlist_t *p_playlist = vlc_object_find( p_vout, VLC_OBJECT_PLAYLIST,
1194 if ( p_playlist != NULL )
1196 playlist_Pause( p_playlist );
1197 vlc_object_release( p_playlist);
1202 val.i_int |= CocoaKeyToVLC( key );
1203 var_Set( p_vout->p_vlc, "key-pressed", val );
1208 [super keyDown: o_event];
1214 NSMutableString * o_title;
1215 playlist_t * p_playlist = vlc_object_find( p_vout, VLC_OBJECT_PLAYLIST,
1218 if( p_playlist == NULL )
1223 vlc_mutex_lock( &p_playlist->object_lock );
1224 o_title = [NSMutableString stringWithUTF8String:
1225 p_playlist->pp_items[p_playlist->i_index]->input.psz_uri];
1226 vlc_mutex_unlock( &p_playlist->object_lock );
1228 vlc_object_release( p_playlist );
1230 if( o_title != nil )
1232 NSRange prefix_range = [o_title rangeOfString: @"file:"];
1233 if( prefix_range.location != NSNotFound )
1235 [o_title deleteCharactersInRange: prefix_range];
1238 [self setTitleWithRepresentedFilename: o_title];
1243 [NSString stringWithCString: VOUT_TITLE " (QuickTime)"]];
1247 /* This is actually the same as VLCControls::stop. */
1248 - (BOOL)windowShouldClose:(id)sender
1250 playlist_t * p_playlist = vlc_object_find( p_vout, VLC_OBJECT_PLAYLIST,
1252 if( p_playlist == NULL )
1257 playlist_Stop( p_playlist );
1258 vlc_object_release( p_playlist );
1260 /* The window will be closed by the intf later. */
1266 /* Common QT and OpenGL code to catch mouse events */
1267 #define CATCH_MOUSE_EVENTS \
1268 - (BOOL)acceptsFirstResponder \
1273 - (BOOL)becomeFirstResponder \
1275 id o_window = [self window]; \
1277 [o_window setAcceptsMouseMovedEvents: YES]; \
1281 - (BOOL)resignFirstResponder \
1283 vout_thread_t * vout; \
1284 id o_window = [self window]; \
1285 vout = (vout_thread_t *)[o_window getVout]; \
1287 [o_window setAcceptsMouseMovedEvents: NO]; \
1288 VLCHideMouse( vout, NO ); \
1292 - (void)mouseDown:(NSEvent *)o_event \
1294 vout_thread_t * vout; \
1295 id o_window = [self window]; \
1296 vout = (vout_thread_t *)[o_window getVout]; \
1299 switch( [o_event type] ) \
1301 case NSLeftMouseDown: \
1303 var_Get( vout, "mouse-button-down", &val ); \
1305 var_Set( vout, "mouse-button-down", val ); \
1310 [super mouseDown: o_event]; \
1315 - (void)otherMouseDown:(NSEvent *)o_event \
1317 vout_thread_t * vout; \
1318 id o_window = [self window]; \
1319 vout = (vout_thread_t *)[o_window getVout]; \
1322 switch( [o_event type] ) \
1324 case NSOtherMouseDown: \
1326 var_Get( vout, "mouse-button-down", &val ); \
1328 var_Set( vout, "mouse-button-down", val ); \
1333 [super mouseDown: o_event]; \
1338 - (void)rightMouseDown:(NSEvent *)o_event \
1340 vout_thread_t * vout; \
1341 id o_window = [self window]; \
1342 vout = (vout_thread_t *)[o_window getVout]; \
1345 switch( [o_event type] ) \
1347 case NSRightMouseDown: \
1349 var_Get( vout, "mouse-button-down", &val ); \
1351 var_Set( vout, "mouse-button-down", val ); \
1356 [super mouseDown: o_event]; \
1361 - (void)mouseUp:(NSEvent *)o_event \
1363 vout_thread_t * vout; \
1364 id o_window = [self window]; \
1365 vout = (vout_thread_t *)[o_window getVout]; \
1368 switch( [o_event type] ) \
1370 case NSLeftMouseUp: \
1372 vlc_value_t b_val; \
1373 b_val.b_bool = VLC_TRUE; \
1374 var_Set( vout, "mouse-clicked", b_val ); \
1376 var_Get( vout, "mouse-button-down", &val ); \
1378 var_Set( vout, "mouse-button-down", val ); \
1383 [super mouseUp: o_event]; \
1388 - (void)otherMouseUp:(NSEvent *)o_event \
1390 vout_thread_t * vout; \
1391 id o_window = [self window]; \
1392 vout = (vout_thread_t *)[o_window getVout]; \
1395 switch( [o_event type] ) \
1397 case NSOtherMouseUp: \
1399 var_Get( vout, "mouse-button-down", &val ); \
1401 var_Set( vout, "mouse-button-down", val ); \
1406 [super mouseUp: o_event]; \
1411 - (void)rightMouseUp:(NSEvent *)o_event \
1413 vout_thread_t * vout; \
1414 id o_window = [self window]; \
1415 vout = (vout_thread_t *)[o_window getVout]; \
1418 switch( [o_event type] ) \
1420 case NSRightMouseUp: \
1422 var_Get( vout, "mouse-button-down", &val ); \
1424 var_Set( vout, "mouse-button-down", val ); \
1429 [super mouseUp: o_event]; \
1434 - (void)mouseDragged:(NSEvent *)o_event \
1436 [self mouseMoved:o_event]; \
1439 - (void)otherMouseDragged:(NSEvent *)o_event \
1441 [self mouseMoved:o_event]; \
1444 - (void)rightMouseDragged:(NSEvent *)o_event \
1446 [self mouseMoved:o_event]; \
1449 /*****************************************************************************
1450 * VLCQTView implementation
1451 *****************************************************************************/
1452 @implementation VLCQTView
1454 - (void)drawRect:(NSRect)rect
1456 vout_thread_t * p_vout;
1457 id o_window = [self window];
1458 p_vout = (vout_thread_t *)[o_window getVout];
1460 [[NSColor blackColor] set];
1462 [super drawRect: rect];
1464 p_vout->i_changes |= VOUT_SIZE_CHANGE;
1469 - (void)mouseMoved:(NSEvent *)o_event
1475 vout_thread_t * p_vout;
1476 id o_window = [self window];
1477 p_vout = (vout_thread_t *)[o_window getVout];
1479 s_rect = [self bounds];
1480 ml = [self convertPoint: [o_event locationInWindow] fromView: nil];
1481 b_inside = [self mouse: ml inRect: s_rect];
1486 int i_width, i_height, i_x, i_y;
1488 vout_PlacePicture( p_vout, (unsigned int)s_rect.size.width,
1489 (unsigned int)s_rect.size.height,
1490 &i_x, &i_y, &i_width, &i_height );
1492 val.i_int = ( ((int)ml.x) - i_x ) *
1493 p_vout->render.i_width / i_width;
1494 var_Set( p_vout, "mouse-x", val );
1496 val.i_int = ( ((int)ml.y) - i_y ) *
1497 p_vout->render.i_height / i_height;
1498 var_Set( p_vout, "mouse-y", val );
1500 val.b_bool = VLC_TRUE;
1501 var_Set( p_vout, "mouse-moved", val );
1502 p_vout->p_sys->i_time_mouse_last_moved = mdate();
1503 p_vout->p_sys->b_mouse_moved = YES;
1506 [super mouseMoved: o_event];
1511 /*****************************************************************************
1512 * VLCGLView implementation
1513 *****************************************************************************/
1514 @implementation VLCGLView
1517 - (id) initWithFrame: (NSRect) frame vout: (vout_thread_t*) _p_vout
1522 NSOpenGLPixelFormatAttribute attribs[] =
1524 NSOpenGLPFAAccelerated,
1525 NSOpenGLPFANoRecovery,
1526 NSOpenGLPFAColorSize, 24,
1527 NSOpenGLPFAAlphaSize, 8,
1528 NSOpenGLPFADepthSize, 24,
1533 NSOpenGLPixelFormat * fmt = [[NSOpenGLPixelFormat alloc]
1534 initWithAttributes: attribs];
1538 msg_Warn( p_vout, "Cannot create NSOpenGLPixelFormat" );
1542 self = [super initWithFrame:frame pixelFormat: fmt];
1545 [[self openGLContext] makeCurrentContext];
1546 [[self openGLContext] update];
1548 /* Black background */
1549 glClearColor( 0.0, 0.0, 0.0, 0.0 );
1551 /* Check if the user asked for useless visual effects */
1552 var_Get( p_vout, "macosx-opengl-effect", &val );
1553 if( !val.psz_string || !strcmp( val.psz_string, "none" ))
1555 i_effect = OPENGL_EFFECT_NONE;
1557 else if( !strcmp( val.psz_string, "cube" ) )
1559 i_effect = OPENGL_EFFECT_CUBE;
1561 glEnable( GL_DEPTH_TEST );
1563 else if( !strcmp( val.psz_string, "transparent-cube" ) )
1565 i_effect = OPENGL_EFFECT_TRANSPARENT_CUBE;
1567 glDisable( GL_DEPTH_TEST );
1568 glEnable( GL_BLEND );
1569 glBlendFunc( GL_SRC_ALPHA, GL_ONE );
1573 msg_Warn( p_vout, "no valid opengl effect provided, using "
1575 i_effect = OPENGL_EFFECT_NONE;
1577 if( val.psz_string ) free( val.psz_string );
1579 if( i_effect & ( OPENGL_EFFECT_CUBE |
1580 OPENGL_EFFECT_TRANSPARENT_CUBE ) )
1582 /* Set the perpective */
1583 glMatrixMode( GL_PROJECTION );
1585 glFrustum( -1.0, 1.0, -1.0, 1.0, 3.0, 20.0 );
1586 glMatrixMode( GL_MODELVIEW );
1588 glTranslatef( 0.0, 0.0, - 5.0 );
1608 [[self openGLContext] makeCurrentContext];
1610 bounds = [self bounds];
1611 glViewport( 0, 0, (GLint) bounds.size.width,
1612 (GLint) bounds.size.height );
1614 var_Get( p_vout, "macosx-stretch", &val );
1622 /* Quad size is set in order to preserve the aspect ratio */
1623 var_Get( p_vout, "macosx-fill", &val );
1624 int fill = ( val.b_bool && p_vout->b_fullscreen );
1625 int large = ( bounds.size.height * p_vout->output.i_aspect <
1626 bounds.size.width * VOUT_ASPECT_FACTOR );
1627 if( ( large && !fill ) || ( !large && fill ) )
1629 f_x = bounds.size.height * p_vout->output.i_aspect /
1630 VOUT_ASPECT_FACTOR / bounds.size.width;
1636 f_y = bounds.size.width * VOUT_ASPECT_FACTOR /
1637 p_vout->output.i_aspect / bounds.size.height;
1642 - (void)initTextures
1645 [[self openGLContext] makeCurrentContext];
1647 /* Free previous texture if any */
1650 glDeleteTextures( 2, pi_textures );
1653 glEnable( GL_TEXTURE_RECTANGLE_EXT );
1654 glEnable( GL_UNPACK_CLIENT_STORAGE_APPLE );
1656 glPixelStorei( GL_UNPACK_ALIGNMENT, 1 );
1657 glPixelStorei( GL_UNPACK_ROW_LENGTH, p_vout->output.i_width );
1659 /* Tell the driver not to make a copy of the texture but to use
1661 glPixelStorei( GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE );
1663 /* Create textures */
1664 glGenTextures( 2, pi_textures );
1666 for( i = 0; i < 2; i++ )
1668 glBindTexture( GL_TEXTURE_RECTANGLE_EXT, pi_textures[i] );
1669 glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE );
1671 /* Linear interpolation */
1672 glTexParameteri( GL_TEXTURE_RECTANGLE_EXT,
1673 GL_TEXTURE_MIN_FILTER, GL_LINEAR );
1674 glTexParameteri( GL_TEXTURE_RECTANGLE_EXT,
1675 GL_TEXTURE_MAG_FILTER, GL_LINEAR );
1677 /* Use VRAM texturing */
1678 glTexParameteri( GL_TEXTURE_RECTANGLE_EXT,
1679 GL_TEXTURE_STORAGE_HINT_APPLE, GL_STORAGE_CACHED_APPLE );
1681 glTexImage2D( GL_TEXTURE_RECTANGLE_EXT, 0, GL_RGB8,
1682 p_vout->output.i_width, p_vout->output.i_height, 0,
1683 GL_YCBCR_422_APPLE, GL_UNSIGNED_SHORT_8_8_APPLE,
1684 p_vout->p_sys->p_data[i] );
1687 /* Swap buffers only during the vertical retrace of the monitor.
1688 http://developer.apple.com/documentation/GraphicsImaging/
1689 Conceptual/OpenGL/chap5/chapter_5_section_44.html */
1690 long params[] = { 1 };
1691 CGLSetParameter( CGLGetCurrentContext(), kCGLCPSwapInterval,
1697 - (void)reloadTexture: (int) index
1704 [[self openGLContext] makeCurrentContext];
1706 glBindTexture( GL_TEXTURE_RECTANGLE_EXT, pi_textures[index] );
1707 glPixelStorei( GL_UNPACK_ROW_LENGTH, p_vout->output.i_width );
1709 /* glTexSubImage2D is faster than glTexImage2D
1710 http://developer.apple.com/samplecode/Sample_Code/Graphics_3D/
1711 TextureRange/MainOpenGLView.m.htm */
1712 glTexSubImage2D( GL_TEXTURE_RECTANGLE_EXT, 0, 0, 0,
1713 p_vout->output.i_width, p_vout->output.i_height,
1714 GL_YCBCR_422_APPLE, GL_UNSIGNED_SHORT_8_8_APPLE,
1715 p_vout->p_sys->p_data[index] );
1725 glBegin( GL_QUADS );
1727 glTexCoord2f( 0.0, 0.0 );
1728 glVertex2f( - f_x, f_y );
1730 glTexCoord2f( 0.0, (float) p_vout->output.i_height );
1731 glVertex2f( - f_x, - f_y );
1733 glTexCoord2f( (float) p_vout->output.i_width,
1734 (float) p_vout->output.i_height );
1735 glVertex2f( f_x, - f_y );
1737 glTexCoord2f( (float) p_vout->output.i_width, 0.0 );
1738 glVertex2f( f_x, f_y );
1744 glBegin( GL_QUADS );
1746 glTexCoord2f( 0.0, 0.0 );
1747 glVertex3f( - 1.0, 1.0, 1.0 );
1748 glTexCoord2f( 0.0, (float) p_vout->output.i_height );
1749 glVertex3f( - 1.0, - 1.0, 1.0 );
1750 glTexCoord2f( (float) p_vout->output.i_width,
1751 (float) p_vout->output.i_height );
1752 glVertex3f( 1.0, - 1.0, 1.0 );
1753 glTexCoord2f( (float) p_vout->output.i_width, 0.0 );
1754 glVertex3f( 1.0, 1.0, 1.0 );
1757 glTexCoord2f( 0.0, 0.0 );
1758 glVertex3f( - 1.0, 1.0, - 1.0 );
1759 glTexCoord2f( 0.0, (float) p_vout->output.i_height );
1760 glVertex3f( - 1.0, - 1.0, - 1.0 );
1761 glTexCoord2f( (float) p_vout->output.i_width,
1762 (float) p_vout->output.i_height );
1763 glVertex3f( - 1.0, - 1.0, 1.0 );
1764 glTexCoord2f( (float) p_vout->output.i_width, 0.0 );
1765 glVertex3f( - 1.0, 1.0, 1.0 );
1768 glTexCoord2f( 0.0, 0.0 );
1769 glVertex3f( 1.0, 1.0, - 1.0 );
1770 glTexCoord2f( 0.0, (float) p_vout->output.i_height );
1771 glVertex3f( 1.0, - 1.0, - 1.0 );
1772 glTexCoord2f( (float) p_vout->output.i_width,
1773 (float) p_vout->output.i_height );
1774 glVertex3f( - 1.0, - 1.0, - 1.0 );
1775 glTexCoord2f( (float) p_vout->output.i_width, 0.0 );
1776 glVertex3f( - 1.0, 1.0, - 1.0 );
1779 glTexCoord2f( 0.0, 0.0 );
1780 glVertex3f( 1.0, 1.0, 1.0 );
1781 glTexCoord2f( 0.0, (float) p_vout->output.i_height );
1782 glVertex3f( 1.0, - 1.0, 1.0 );
1783 glTexCoord2f( (float) p_vout->output.i_width,
1784 (float) p_vout->output.i_height );
1785 glVertex3f( 1.0, - 1.0, - 1.0 );
1786 glTexCoord2f( (float) p_vout->output.i_width, 0.0 );
1787 glVertex3f( 1.0, 1.0, - 1.0 );
1790 glTexCoord2f( 0.0, 0.0 );
1791 glVertex3f( - 1.0, 1.0, - 1.0 );
1792 glTexCoord2f( 0.0, (float) p_vout->output.i_height );
1793 glVertex3f( - 1.0, 1.0, 1.0 );
1794 glTexCoord2f( (float) p_vout->output.i_width,
1795 (float) p_vout->output.i_height );
1796 glVertex3f( 1.0, 1.0, 1.0 );
1797 glTexCoord2f( (float) p_vout->output.i_width, 0.0 );
1798 glVertex3f( 1.0, 1.0, - 1.0 );
1801 glTexCoord2f( 0.0, 0.0 );
1802 glVertex3f( - 1.0, - 1.0, 1.0 );
1803 glTexCoord2f( 0.0, (float) p_vout->output.i_height );
1804 glVertex3f( - 1.0, - 1.0, - 1.0 );
1805 glTexCoord2f( (float) p_vout->output.i_width,
1806 (float) p_vout->output.i_height );
1807 glVertex3f( 1.0, - 1.0, - 1.0 );
1808 glTexCoord2f( (float) p_vout->output.i_width, 0.0 );
1809 glVertex3f( 1.0, - 1.0, 1.0 );
1813 - (void) drawRect: (NSRect) rect
1815 [[self openGLContext] makeCurrentContext];
1817 /* Black background */
1818 glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
1827 glBindTexture( GL_TEXTURE_RECTANGLE_EXT,
1828 pi_textures[p_vout->p_sys->i_cur_pic] );
1829 if( i_effect & ( OPENGL_EFFECT_CUBE |
1830 OPENGL_EFFECT_TRANSPARENT_CUBE ) )
1832 glRotatef( 1.0, 0.3, 0.5, 0.7 );
1846 - (void)mouseMoved:(NSEvent *)o_event
1852 s_rect = [self bounds];
1853 ml = [self convertPoint: [o_event locationInWindow] fromView: nil];
1854 b_inside = [self mouse: ml inRect: s_rect];
1859 int i_width, i_height, i_x, i_y;
1861 vout_PlacePicture( p_vout, (unsigned int)s_rect.size.width,
1862 (unsigned int)s_rect.size.height,
1863 &i_x, &i_y, &i_width, &i_height );
1865 val.i_int = ( (int)ml.x - i_x ) *
1866 p_vout->render.i_width / i_width;
1867 var_Set( p_vout, "mouse-x", val );
1869 /* Y coordinate is inverted in OpenGL */
1870 val.i_int = ( ((int)(s_rect.size.height - ml.y)) - i_y ) *
1871 p_vout->render.i_height / i_height;
1872 var_Set( p_vout, "mouse-y", val );
1874 val.b_bool = VLC_TRUE;
1875 var_Set( p_vout, "mouse-moved", val );
1876 p_vout->p_sys->i_time_mouse_last_moved = mdate();
1877 p_vout->p_sys->b_mouse_moved = YES;
1880 [super mouseMoved: o_event];