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 * );
74 static int CoToggleFullscreen ( vout_thread_t * );
76 static void VLCHideMouse ( vout_thread_t *, BOOL );
78 static void QTScaleMatrix ( vout_thread_t * );
79 static int QTCreateSequence ( vout_thread_t * );
80 static void QTDestroySequence ( vout_thread_t * );
81 static int QTNewPicture ( vout_thread_t *, picture_t * );
82 static void QTFreePicture ( vout_thread_t *, picture_t * );
84 /*****************************************************************************
85 * OpenVideo: allocates MacOS X video thread output method
86 *****************************************************************************
87 * This function allocates and initializes a MacOS X vout method.
88 *****************************************************************************/
89 int E_(OpenVideo) ( vlc_object_t *p_this )
91 vout_thread_t * p_vout = (vout_thread_t *)p_this;
96 p_vout->p_sys = malloc( sizeof( vout_sys_t ) );
97 if( p_vout->p_sys == NULL )
99 msg_Err( p_vout, "out of memory" );
103 memset( p_vout->p_sys, 0, sizeof( vout_sys_t ) );
105 /* Wait for a MacOS X interface to appear. Timeout is 2 seconds. */
106 for( i_timeout = 20 ; i_timeout-- ; )
110 msleep( INTF_IDLE_SLEEP );
116 /* no MacOS X intf, unable to communicate with MT */
117 msg_Err( p_vout, "no MacOS X interface present" );
118 free( p_vout->p_sys );
122 p_vout->pf_init = InitVideo;
123 p_vout->pf_end = EndVideo;
124 p_vout->pf_manage = ManageVideo;
125 p_vout->pf_render = NULL;
126 p_vout->pf_display = DisplayVideo;
127 p_vout->pf_control = ControlVideo;
129 p_vout->p_sys->o_pool = [[NSAutoreleasePool alloc] init];
130 p_vout->p_sys->b_mouse_moved = VLC_TRUE;
131 p_vout->p_sys->i_time_mouse_last_moved = mdate();
133 /* set original window size */
134 p_vout->p_sys->s_rect.size.width = p_vout->i_window_width;
135 p_vout->p_sys->s_rect.size.height = p_vout->i_window_height;
137 var_Create( p_vout, "macosx-vout", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
138 var_Create( p_vout, "macosx-vdev", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
139 var_Create( p_vout, "macosx-fill", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
140 var_Create( p_vout, "macosx-stretch", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
141 var_Create( p_vout, "macosx-opaqueness", VLC_VAR_FLOAT | VLC_VAR_DOINHERIT );
142 var_Create( p_vout, "macosx-opengl-effect", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
144 /* Check if we should use QuickTime or OpenGL */
145 var_Get( p_vout, "macosx-vout", &val);
147 if( !strncmp( val.psz_string, "auto", 4 ) )
149 p_vout->p_sys->i_opengl = CGDisplayUsesOpenGLAcceleration( kCGDirectMainDisplay );
151 else if( !strncmp( val.psz_string, "opengl", 6 ) )
153 p_vout->p_sys->i_opengl = VLC_TRUE;
157 p_vout->p_sys->i_opengl = VLC_FALSE;
159 free( val.psz_string );
161 if( !p_vout->p_sys->i_opengl )
163 /* Initialize QuickTime */
164 p_vout->p_sys->h_img_descr =
165 (ImageDescriptionHandle)NewHandleClear( sizeof(ImageDescription) );
166 p_vout->p_sys->p_matrix =
167 (MatrixRecordPtr)malloc( sizeof(MatrixRecord) );
168 p_vout->p_sys->p_fullscreen_state = NULL;
170 if( ( err = EnterMovies() ) != noErr )
172 msg_Err( p_vout, "EnterMovies failed: %d", err );
173 free( p_vout->p_sys->p_matrix );
174 DisposeHandle( (Handle)p_vout->p_sys->h_img_descr );
175 free( p_vout->p_sys );
179 /* Damn QT isn't thread safe. so keep a lock in the p_vlc object */
180 vlc_mutex_lock( &p_vout->p_vlc->quicktime_lock );
182 /* Can we find the right chroma ? */
183 err = FindCodec( kYUV420CodecType, bestSpeedCodec,
184 nil, &p_vout->p_sys->img_dc );
186 vlc_mutex_unlock( &p_vout->p_vlc->quicktime_lock );
188 if( err == noErr && p_vout->p_sys->img_dc != 0 )
190 p_vout->output.i_chroma = VLC_FOURCC('I','4','2','0');
191 p_vout->p_sys->i_codec = kYUV420CodecType;
195 msg_Err( p_vout, "failed to find an appropriate codec" );
198 if( p_vout->p_sys->img_dc == 0 )
200 free( p_vout->p_sys->p_matrix );
201 DisposeHandle( (Handle)p_vout->p_sys->h_img_descr );
202 free( p_vout->p_sys );
205 msg_Dbg( p_vout, "using Quartz mode" );
209 p_vout->output.i_chroma = VLC_FOURCC('Y','U','Y','2');
210 msg_Dbg( p_vout, "using OpenGL mode" );
213 /* Setup the menuitem for the multiple displays. Read the vlc preference (macosx-vdev) for the primary display */
214 NSArray * o_screens = [NSScreen screens];
215 if( [o_screens count] > 0 && var_Type( p_vout, "video-device" ) == 0 )
218 vlc_value_t val2, text;
221 var_Get( p_vout, "macosx-vdev", &val );
223 var_Create( p_vout, "video-device", VLC_VAR_INTEGER |
225 text.psz_string = _("Video device");
226 var_Change( p_vout, "video-device", VLC_VAR_SETTEXT, &text, NULL );
228 NSEnumerator * o_enumerator = [o_screens objectEnumerator];
230 while( (o_screen = [o_enumerator nextObject]) != NULL )
233 NSRect s_rect = [o_screen frame];
235 snprintf( psz_temp, sizeof(psz_temp)/sizeof(psz_temp[0])-1,
236 "%s %d (%dx%d)", _("Screen"), i,
237 (int)s_rect.size.width, (int)s_rect.size.height );
239 text.psz_string = psz_temp;
241 var_Change( p_vout, "video-device",
242 VLC_VAR_ADDCHOICE, &val2, &text );
244 if( ( i - 1 ) == val.i_int )
246 var_Set( p_vout, "video-device", val2 );
251 var_AddCallback( p_vout, "video-device", vout_VarCallback,
254 val2.b_bool = VLC_TRUE;
255 var_Set( p_vout, "intf-change", val2 );
259 if( CoCreateWindow( p_vout ) )
261 msg_Err( p_vout, "unable to create a window" );
262 if( !p_vout->p_sys->i_opengl )
264 free( p_vout->p_sys->p_matrix );
265 DisposeHandle( (Handle)p_vout->p_sys->h_img_descr );
267 free( p_vout->p_sys );
274 /*****************************************************************************
275 * CloseVideo: destroy video thread output method
276 *****************************************************************************/
277 void E_(CloseVideo) ( vlc_object_t *p_this )
279 NSAutoreleasePool *o_pool = [[NSAutoreleasePool alloc] init];
280 vout_thread_t * p_vout = (vout_thread_t *)p_this;
282 if( CoDestroyWindow( p_vout ) )
284 msg_Err( p_vout, "unable to destroy window" );
287 if ( p_vout->p_sys->p_fullscreen_state != NULL )
289 EndFullScreen ( p_vout->p_sys->p_fullscreen_state, NULL );
292 if( !p_vout->p_sys->i_opengl )
294 /* Clean Up Quicktime environment */
296 free( p_vout->p_sys->p_matrix );
297 DisposeHandle( (Handle)p_vout->p_sys->h_img_descr );
301 [p_vout->p_sys->o_glview cleanUp];
305 free( p_vout->p_sys );
308 /*****************************************************************************
309 * InitVideo: initialize video thread output method
310 *****************************************************************************/
311 static int InitOpenGL ( vout_thread_t *p_vout );
312 static int InitQuickTime( vout_thread_t *p_vout );
313 static int InitVideo ( vout_thread_t *p_vout )
315 I_OUTPUTPICTURES = 0;
317 /* Initialize the output structure; we already found a codec,
318 * and the corresponding chroma we will be using. Since we can
319 * arbitrary scale, stick to the coordinates and aspect. */
320 p_vout->output.i_width = p_vout->render.i_width;
321 p_vout->output.i_height = p_vout->render.i_height;
322 p_vout->output.i_aspect = p_vout->render.i_aspect;
324 if( p_vout->p_sys->i_opengl )
326 return InitOpenGL( p_vout );
330 return InitQuickTime( p_vout );
334 static int InitOpenGL( vout_thread_t *p_vout )
336 picture_t *p_pic = NULL;
340 /* Apple OpenGL extensions only accept YUV as YUY2 */
341 p_vout->output.i_chroma = VLC_FOURCC('Y','U','Y','2');
342 p_vout->output.i_rmask = 0xFF0000;
343 p_vout->output.i_gmask = 0x00FF00;
344 p_vout->output.i_bmask = 0x0000FF;
346 /* Allocate our 2 picture buffers */
347 i_bytes = 2 * p_vout->output.i_width * p_vout->output.i_height;
348 p_vout->p_sys->p_data[0] = vlc_memalign(
349 &p_vout->p_sys->p_data_orig[0], 16, i_bytes );
350 p_vout->p_sys->p_data[1] = vlc_memalign(
351 &p_vout->p_sys->p_data_orig[1], 16, i_bytes );
352 p_vout->p_sys->i_cur_pic = 1;
354 /* We declare only one picture and will switch buffers manually */
355 while( I_OUTPUTPICTURES < 1 )
357 /* Find an empty picture slot */
358 for( i_index = 0; i_index < VOUT_MAX_PICTURES; i_index++ )
360 if( p_vout->p_picture[ i_index ].i_status == FREE_PICTURE )
362 p_pic = p_vout->p_picture + i_index;
371 vout_InitPicture( VLC_OBJECT( p_vout ), p_pic,
372 p_vout->output.i_chroma, p_vout->output.i_width,
373 p_vout->output.i_height, p_vout->output.i_aspect );
374 p_pic->p_data = p_vout->p_sys->p_data[0];
375 p_pic->p[0].p_pixels = p_pic->p_data;
376 for( i_index = 1; i_index < p_pic->i_planes; i_index++ )
378 p_pic->p[i_index].p_pixels =
379 p_pic->p[i_index-1].p_pixels +
380 p_pic->p[i_index-1].i_lines *
381 p_pic->p[i_index-1].i_pitch;
384 p_pic->i_status = DESTROYED_PICTURE;
385 p_pic->i_type = DIRECT_PICTURE;
387 PP_OUTPUTPICTURE[ I_OUTPUTPICTURES ] = p_pic;
391 [p_vout->p_sys->o_glview lockFocus];
392 [p_vout->p_sys->o_glview initTextures];
393 [p_vout->p_sys->o_glview reshape];
394 [p_vout->p_sys->o_glview unlockFocus];
399 static int InitQuickTime( vout_thread_t *p_vout )
404 SetPort( p_vout->p_sys->p_qdport );
405 QTScaleMatrix( p_vout );
407 if( QTCreateSequence( p_vout ) )
409 msg_Err( p_vout, "unable to create sequence" );
413 /* Try to initialize up to QT_MAX_DIRECTBUFFERS direct buffers */
414 while( I_OUTPUTPICTURES < QT_MAX_DIRECTBUFFERS )
418 /* Find an empty picture slot */
419 for( i_index = 0; i_index < VOUT_MAX_PICTURES; i_index++ )
421 if( p_vout->p_picture[ i_index ].i_status == FREE_PICTURE )
423 p_pic = p_vout->p_picture + i_index;
428 /* Allocate the picture */
429 if( p_pic == NULL || QTNewPicture( p_vout, p_pic ) )
434 p_pic->i_status = DESTROYED_PICTURE;
435 p_pic->i_type = DIRECT_PICTURE;
437 PP_OUTPUTPICTURE[ I_OUTPUTPICTURES ] = p_pic;
443 /*****************************************************************************
444 * EndVideo: terminate video thread output method
445 *****************************************************************************/
446 static void EndVideo( vout_thread_t *p_vout )
450 if( !p_vout->p_sys->i_opengl )
452 QTDestroySequence( p_vout );
455 /* Free the direct buffers we allocated */
456 for( i_index = I_OUTPUTPICTURES; i_index; )
459 if( !p_vout->p_sys->i_opengl )
461 QTFreePicture( p_vout, PP_OUTPUTPICTURE[ i_index ] );
465 free( p_vout->p_sys->p_data_orig[0] );
466 free( p_vout->p_sys->p_data_orig[1] );
471 /*****************************************************************************
472 * ManageVideo: handle events
473 *****************************************************************************
474 * This function should be called regularly by video output thread. It manages
475 * console events. It returns a non null value on error.
476 *****************************************************************************/
477 static int ManageVideo( vout_thread_t *p_vout )
479 if( p_vout->i_changes & VOUT_FULLSCREEN_CHANGE )
481 if( CoToggleFullscreen( p_vout ) )
486 p_vout->i_changes &= ~VOUT_FULLSCREEN_CHANGE;
489 if( p_vout->i_changes & VOUT_SIZE_CHANGE )
491 if( !p_vout->p_sys->i_opengl )
493 QTScaleMatrix( p_vout );
494 SetDSequenceMatrix( p_vout->p_sys->i_seq,
495 p_vout->p_sys->p_matrix );
498 p_vout->i_changes &= ~VOUT_SIZE_CHANGE;
501 /* hide/show mouse cursor
502 * this code looks unnecessarily complicated, but is necessary like this.
503 * it has to deal with multiple monitors and therefore checks a lot */
504 if( !p_vout->p_sys->b_mouse_moved && p_vout->b_fullscreen )
506 if( mdate() - p_vout->p_sys->i_time_mouse_last_moved > 3000000 )
508 VLCHideMouse( p_vout, YES );
511 else if ( p_vout->p_sys->b_mouse_moved && p_vout->b_fullscreen )
513 VLCHideMouse( p_vout, NO );
516 /* disable screen saver */
517 UpdateSystemActivity( UsrActivity );
522 /*****************************************************************************
523 * vout_Display: displays previously rendered output
524 *****************************************************************************
525 * This function sends the currently rendered image to the display.
526 *****************************************************************************/
527 static void DisplayVideo( vout_thread_t *p_vout, picture_t *p_pic )
529 if( !p_vout->p_sys->i_opengl )
534 if( ( err = DecompressSequenceFrameS(
535 p_vout->p_sys->i_seq,
536 p_pic->p_sys->p_info,
537 p_pic->p_sys->i_size,
538 codecFlagUseImageBuffer, &flags, nil ) != noErr ) )
540 msg_Warn( p_vout, "DecompressSequenceFrameS failed: %d", err );
544 QDFlushPortBuffer( p_vout->p_sys->p_qdport, nil );
549 if( [p_vout->p_sys->o_glview lockFocusIfCanDraw] )
552 int i_old = p_vout->p_sys->i_cur_pic;
553 int i_new = ( i_old + 1 ) % 2;
555 /* Draw the new picture */
556 p_vout->p_sys->i_cur_pic = i_new;
557 [p_vout->p_sys->o_glview drawRect:
558 [p_vout->p_sys->o_glview bounds]];
560 /* Reload the other texture. Textures have to be reloaded
561 before the buffer is filled (thanks to gcc from
562 arstechnica forums) */
563 [p_vout->p_sys->o_glview reloadTexture: i_old];
566 p_pic->p_data = p_vout->p_sys->p_data[i_old];
567 p_pic->p[0].p_pixels = p_pic->p_data;
568 for( i_index = 1; i_index < p_pic->i_planes; i_index++ )
570 p_pic->p[i_index].p_pixels =
571 p_pic->p[i_index-1].p_pixels +
572 p_pic->p[i_index-1].i_lines *
573 p_pic->p[i_index-1].i_pitch;
575 [p_vout->p_sys->o_glview unlockFocus];
580 /*****************************************************************************
581 * ControlVideo: control facility for the vout
582 *****************************************************************************/
583 static int ControlVideo( vout_thread_t *p_vout, int i_query, va_list args )
590 case VOUT_SET_STAY_ON_TOP:
596 return vout_vaControlDefault( p_vout, i_query, args );
600 /*****************************************************************************
601 * CoCreateWindow: create new window
602 *****************************************************************************
603 * Returns 0 on success, 1 otherwise
604 *****************************************************************************/
605 static int CoCreateWindow( vout_thread_t *p_vout )
609 vlc_bool_t b_main_screen;
610 NSAutoreleasePool *o_pool = [[NSAutoreleasePool alloc] init];
612 /* Allocate the window. It will be autoreleased when it receives 'close' */
613 p_vout->p_sys->o_window = [VLCWindow alloc];
614 [p_vout->p_sys->o_window setReleasedWhenClosed: YES];
616 /* Find out on which screen to open the window */
617 var_Get( p_vout, "video-device", &val );
620 /* No preference specified. Use the main screen */
621 o_screen = [NSScreen mainScreen];
626 NSArray *o_screens = [NSScreen screens];
627 unsigned int i_index = val.i_int;
629 if( [o_screens count] < i_index )
631 o_screen = [NSScreen mainScreen];
637 o_screen = [o_screens objectAtIndex: i_index];
639 var_Set( p_vout, "macosx-vdev", val );
640 b_main_screen = (i_index == 0);
644 if( p_vout->b_fullscreen )
646 NSRect screen_rect = [o_screen frame];
647 screen_rect.origin.x = screen_rect.origin.y = 0;
649 if ( b_main_screen && p_vout->p_sys->p_fullscreen_state == NULL )
650 BeginFullScreen( &p_vout->p_sys->p_fullscreen_state, NULL, 0, 0,
651 NULL, NULL, fullScreenAllowEvents );
653 /* Creates a window with size: screen_rect on o_screen */
654 [p_vout->p_sys->o_window
655 initWithContentRect: screen_rect
656 styleMask: NSBorderlessWindowMask
657 backing: NSBackingStoreBuffered
658 defer: NO screen: o_screen];
660 [p_vout->p_sys->o_window setVout: p_vout];
661 p_vout->p_sys->b_mouse_moved = YES;
662 p_vout->p_sys->i_time_mouse_last_moved = mdate();
666 unsigned int i_stylemask = NSTitledWindowMask |
667 NSMiniaturizableWindowMask |
668 NSClosableWindowMask |
669 NSResizableWindowMask;
671 if ( p_vout->p_sys->p_fullscreen_state != NULL )
672 EndFullScreen ( p_vout->p_sys->p_fullscreen_state, NULL );
673 p_vout->p_sys->p_fullscreen_state = NULL;
675 [p_vout->p_sys->o_window
676 initWithContentRect: p_vout->p_sys->s_rect
677 styleMask: i_stylemask
678 backing: NSBackingStoreBuffered
679 defer: NO screen: o_screen];
681 [p_vout->p_sys->o_window setVout: p_vout];
682 var_Get( p_vout, "macosx-opaqueness", &val);
683 [p_vout->p_sys->o_window setAlphaValue: val.f_float];
685 var_Get( p_vout, "video-on-top", &val );
686 if( val.b_bool ) [p_vout->p_sys->o_window setLevel: NSStatusWindowLevel];
688 if( !p_vout->p_sys->b_pos_saved ) [p_vout->p_sys->o_window center];
691 if( !p_vout->p_sys->i_opengl )
693 #define o_qtview p_vout->p_sys->o_qtview
694 o_qtview = [[VLCQTView alloc] init];
695 [p_vout->p_sys->o_window setContentView: o_qtview];
696 [o_qtview autorelease];
698 /* Retrieve the QuickDraw port */
699 [o_qtview lockFocus];
700 p_vout->p_sys->p_qdport = [o_qtview qdPort];
701 [o_qtview unlockFocus];
706 #define o_glview p_vout->p_sys->o_glview
707 o_glview = [[VLCGLView alloc] initWithFrame: p_vout->p_sys->s_rect vout: p_vout];
708 [p_vout->p_sys->o_window setContentView: o_glview];
709 [o_glview autorelease];
713 [p_vout->p_sys->o_window updateTitle];
714 [p_vout->p_sys->o_window makeKeyAndOrderFront: nil];
720 /*****************************************************************************
721 * CoDestroyWindow: destroy window
722 *****************************************************************************
723 * Returns 0 on success, 1 otherwise
724 *****************************************************************************/
725 static int CoDestroyWindow( vout_thread_t *p_vout )
727 NSAutoreleasePool *o_pool = [[NSAutoreleasePool alloc] init];
728 VLCHideMouse( p_vout, NO );
730 if( !p_vout->b_fullscreen )
734 /* remember the window position before we enter fullscreen */
735 s_rect = [[p_vout->p_sys->o_window contentView] frame];
736 p_vout->p_sys->s_rect.size = s_rect.size;
738 s_rect = [p_vout->p_sys->o_window frame];
739 p_vout->p_sys->s_rect.origin = s_rect.origin;
741 p_vout->p_sys->b_pos_saved = YES;
744 p_vout->p_sys->p_qdport = nil;
745 [p_vout->p_sys->o_window close];
746 p_vout->p_sys->o_window = nil;
751 /*****************************************************************************
752 * CoToggleFullscreen: toggle fullscreen
753 *****************************************************************************
754 * Returns 0 on success, 1 otherwise
755 *****************************************************************************/
756 static int CoToggleFullscreen( vout_thread_t *p_vout )
758 NSAutoreleasePool *o_pool = [[NSAutoreleasePool alloc] init];
760 if( !p_vout->p_sys->i_opengl )
762 QTDestroySequence( p_vout );
765 if( CoDestroyWindow( p_vout ) )
767 msg_Err( p_vout, "unable to destroy window" );
771 p_vout->b_fullscreen = !p_vout->b_fullscreen;
773 if( CoCreateWindow( p_vout ) )
775 msg_Err( p_vout, "unable to create window" );
779 if( p_vout->p_sys->i_opengl )
781 [p_vout->p_sys->o_glview lockFocus];
782 [p_vout->p_sys->o_glview initTextures];
783 [p_vout->p_sys->o_glview reshape];
784 [p_vout->p_sys->o_glview drawRect:
785 [p_vout->p_sys->o_glview bounds]];
786 [p_vout->p_sys->o_glview unlockFocus];
790 SetPort( p_vout->p_sys->p_qdport );
791 QTScaleMatrix( p_vout );
793 if( QTCreateSequence( p_vout ) )
795 msg_Err( p_vout, "unable to create sequence" );
804 /*****************************************************************************
805 * VLCHideMouse: if b_hide then hide the cursor
806 *****************************************************************************/
807 static void VLCHideMouse ( vout_thread_t *p_vout, BOOL b_hide )
812 NSWindow *o_window = p_vout->p_sys->o_window;
813 NSView *o_contents = [o_window contentView];
815 s_rect = [o_contents bounds];
816 ml = [o_window convertScreenToBase:[NSEvent mouseLocation]];
817 ml = [o_contents convertPoint:ml fromView:nil];
818 b_inside = [o_contents mouse: ml inRect: s_rect];
820 if ( b_hide && b_inside )
822 /* only hide if mouse over VLCQTView */
823 [NSCursor setHiddenUntilMouseMoves: YES];
827 [NSCursor setHiddenUntilMouseMoves: NO];
829 p_vout->p_sys->b_mouse_moved = NO;
830 p_vout->p_sys->i_time_mouse_last_moved = mdate();
834 /*****************************************************************************
835 * QTScaleMatrix: scale matrix
836 *****************************************************************************/
837 static void QTScaleMatrix( vout_thread_t *p_vout )
841 unsigned int i_width, i_height;
842 Fixed factor_x, factor_y;
843 unsigned int i_offset_x = 0;
844 unsigned int i_offset_y = 0;
846 GetPortBounds( p_vout->p_sys->p_qdport, &s_rect );
848 i_width = s_rect.right - s_rect.left;
849 i_height = s_rect.bottom - s_rect.top;
851 var_Get( p_vout, "macosx-stretch", &val );
854 factor_x = FixDiv( Long2Fix( i_width ),
855 Long2Fix( p_vout->output.i_width ) );
856 factor_y = FixDiv( Long2Fix( i_height ),
857 Long2Fix( p_vout->output.i_height ) );
860 else if( i_height * p_vout->output.i_aspect < i_width * VOUT_ASPECT_FACTOR )
862 int i_adj_width = i_height * p_vout->output.i_aspect /
865 factor_x = FixDiv( Long2Fix( i_adj_width ),
866 Long2Fix( p_vout->output.i_width ) );
867 factor_y = FixDiv( Long2Fix( i_height ),
868 Long2Fix( p_vout->output.i_height ) );
870 i_offset_x = (i_width - i_adj_width) / 2;
874 int i_adj_height = i_width * VOUT_ASPECT_FACTOR /
875 p_vout->output.i_aspect;
877 factor_x = FixDiv( Long2Fix( i_width ),
878 Long2Fix( p_vout->output.i_width ) );
879 factor_y = FixDiv( Long2Fix( i_adj_height ),
880 Long2Fix( p_vout->output.i_height ) );
882 i_offset_y = (i_height - i_adj_height) / 2;
885 SetIdentityMatrix( p_vout->p_sys->p_matrix );
887 ScaleMatrix( p_vout->p_sys->p_matrix,
889 Long2Fix(0), Long2Fix(0) );
891 TranslateMatrix( p_vout->p_sys->p_matrix,
892 Long2Fix(i_offset_x), Long2Fix(i_offset_y) );
895 /*****************************************************************************
896 * QTCreateSequence: create a new sequence
897 *****************************************************************************
898 * Returns 0 on success, 1 otherwise
899 *****************************************************************************/
900 static int QTCreateSequence( vout_thread_t *p_vout )
903 ImageDescriptionPtr p_descr;
905 HLock( (Handle)p_vout->p_sys->h_img_descr );
906 p_descr = *p_vout->p_sys->h_img_descr;
908 p_descr->idSize = sizeof(ImageDescription);
909 p_descr->cType = p_vout->p_sys->i_codec;
910 p_descr->version = 1;
911 p_descr->revisionLevel = 0;
912 p_descr->vendor = 'appl';
913 p_descr->width = p_vout->output.i_width;
914 p_descr->height = p_vout->output.i_height;
915 p_descr->hRes = Long2Fix(72);
916 p_descr->vRes = Long2Fix(72);
917 p_descr->spatialQuality = codecLosslessQuality;
918 p_descr->frameCount = 1;
919 p_descr->clutID = -1;
920 p_descr->dataSize = 0;
923 HUnlock( (Handle)p_vout->p_sys->h_img_descr );
925 if( ( err = DecompressSequenceBeginS(
926 &p_vout->p_sys->i_seq,
927 p_vout->p_sys->h_img_descr,
929 p_vout->p_sys->p_qdport,
931 p_vout->p_sys->p_matrix,
933 codecFlagUseImageBuffer,
934 codecLosslessQuality,
935 p_vout->p_sys->img_dc ) ) )
937 msg_Err( p_vout, "DecompressSequenceBeginS failed: %d", err );
944 /*****************************************************************************
945 * QTDestroySequence: destroy sequence
946 *****************************************************************************/
947 static void QTDestroySequence( vout_thread_t *p_vout )
949 CDSequenceEnd( p_vout->p_sys->i_seq );
952 /*****************************************************************************
953 * QTNewPicture: allocate a picture
954 *****************************************************************************
955 * Returns 0 on success, 1 otherwise
956 *****************************************************************************/
957 static int QTNewPicture( vout_thread_t *p_vout, picture_t *p_pic )
959 int i_width = p_vout->output.i_width;
960 int i_height = p_vout->output.i_height;
962 /* We know the chroma, allocate a buffer which will be used
963 * directly by the decoder */
964 p_pic->p_sys = malloc( sizeof( picture_sys_t ) );
966 if( p_pic->p_sys == NULL )
971 switch( p_vout->output.i_chroma )
973 case VLC_FOURCC('I','4','2','0'):
975 p_pic->p_sys->p_info = (void *)&p_pic->p_sys->pixmap_i420;
976 p_pic->p_sys->i_size = sizeof(PlanarPixmapInfoYUV420);
978 /* Allocate the memory buffer */
979 p_pic->p_data = vlc_memalign( &p_pic->p_data_orig,
980 16, i_width * i_height * 3 / 2 );
983 p_pic->Y_PIXELS = p_pic->p_data;
984 p_pic->p[Y_PLANE].i_lines = i_height;
985 p_pic->p[Y_PLANE].i_pitch = i_width;
986 p_pic->p[Y_PLANE].i_pixel_pitch = 1;
987 p_pic->p[Y_PLANE].i_visible_pitch = i_width;
990 p_pic->U_PIXELS = p_pic->Y_PIXELS + i_height * i_width;
991 p_pic->p[U_PLANE].i_lines = i_height / 2;
992 p_pic->p[U_PLANE].i_pitch = i_width / 2;
993 p_pic->p[U_PLANE].i_pixel_pitch = 1;
994 p_pic->p[U_PLANE].i_visible_pitch = i_width / 2;
997 p_pic->V_PIXELS = p_pic->U_PIXELS + i_height * i_width / 4;
998 p_pic->p[V_PLANE].i_lines = i_height / 2;
999 p_pic->p[V_PLANE].i_pitch = i_width / 2;
1000 p_pic->p[V_PLANE].i_pixel_pitch = 1;
1001 p_pic->p[V_PLANE].i_visible_pitch = i_width / 2;
1003 /* We allocated 3 planes */
1004 p_pic->i_planes = 3;
1006 #define P p_pic->p_sys->pixmap_i420
1007 P.componentInfoY.offset = (void *)p_pic->Y_PIXELS
1008 - p_pic->p_sys->p_info;
1009 P.componentInfoCb.offset = (void *)p_pic->U_PIXELS
1010 - p_pic->p_sys->p_info;
1011 P.componentInfoCr.offset = (void *)p_pic->V_PIXELS
1012 - p_pic->p_sys->p_info;
1014 P.componentInfoY.rowBytes = i_width;
1015 P.componentInfoCb.rowBytes = i_width / 2;
1016 P.componentInfoCr.rowBytes = i_width / 2;
1022 /* Unknown chroma, tell the guy to get lost */
1023 free( p_pic->p_sys );
1024 msg_Err( p_vout, "never heard of chroma 0x%.8x (%4.4s)",
1025 p_vout->output.i_chroma, (char*)&p_vout->output.i_chroma );
1026 p_pic->i_planes = 0;
1033 /*****************************************************************************
1034 * QTFreePicture: destroy a picture allocated with QTNewPicture
1035 *****************************************************************************/
1036 static void QTFreePicture( vout_thread_t *p_vout, picture_t *p_pic )
1038 switch( p_vout->output.i_chroma )
1040 case VLC_FOURCC('I','4','2','0'):
1041 free( p_pic->p_data_orig );
1045 free( p_pic->p_sys );
1048 /*****************************************************************************
1049 * VLCWindow implementation
1050 *****************************************************************************/
1051 @implementation VLCWindow
1053 - (void)setVout:(vout_thread_t *)_p_vout
1058 - (vout_thread_t *)getVout
1063 - (void)scaleWindowWithFactor: (float)factor
1066 int i_corrected_height, i_corrected_width;
1067 NSPoint topleftbase;
1068 NSPoint topleftscreen;
1070 if ( !p_vout->b_fullscreen )
1073 topleftbase.y = [self frame].size.height;
1074 topleftscreen = [self convertBaseToScreen: topleftbase];
1076 if( p_vout->output.i_height * p_vout->output.i_aspect >
1077 p_vout->output.i_width * VOUT_ASPECT_FACTOR )
1079 i_corrected_width = p_vout->output.i_height * p_vout->output.i_aspect /
1081 newsize.width = (int) ( i_corrected_width * factor );
1082 newsize.height = (int) ( p_vout->render.i_height * factor );
1086 i_corrected_height = p_vout->output.i_width * VOUT_ASPECT_FACTOR /
1087 p_vout->output.i_aspect;
1088 newsize.width = (int) ( p_vout->render.i_width * factor );
1089 newsize.height = (int) ( i_corrected_height * factor );
1092 [self setContentSize: newsize];
1094 [self setFrameTopLeftPoint: topleftscreen];
1095 p_vout->i_changes |= VOUT_SIZE_CHANGE;
1099 - (void)toggleFloatOnTop
1102 if( var_Get( p_vout, "video-on-top", &val )>=0 && val.b_bool)
1104 val.b_bool = VLC_FALSE;
1105 var_Set( p_vout, "video-on-top", val );
1106 [p_vout->p_sys->o_window setLevel: NSNormalWindowLevel];
1110 val.b_bool = VLC_TRUE;
1111 var_Set( p_vout, "video-on-top", val );
1112 [p_vout->p_sys->o_window setLevel: NSStatusWindowLevel];
1116 - (void)toggleFullscreen
1119 val.b_bool = !p_vout->b_fullscreen;
1120 var_Set( p_vout, "fullscreen", val );
1123 - (BOOL)isFullscreen
1125 return( p_vout->b_fullscreen );
1128 - (BOOL)canBecomeKeyWindow
1133 - (BOOL)performKeyEquivalent:(NSEvent *)o_event
1135 return [[VLCMain sharedInstance] hasDefinedShortcutKey:o_event];
1138 - (void)keyDown:(NSEvent *)o_event
1142 unsigned int i_pressed_modifiers = 0;
1145 i_pressed_modifiers = [o_event modifierFlags];
1147 if( i_pressed_modifiers & NSShiftKeyMask )
1148 val.i_int |= KEY_MODIFIER_SHIFT;
1149 if( i_pressed_modifiers & NSControlKeyMask )
1150 val.i_int |= KEY_MODIFIER_CTRL;
1151 if( i_pressed_modifiers & NSAlternateKeyMask )
1152 val.i_int |= KEY_MODIFIER_ALT;
1153 if( i_pressed_modifiers & NSCommandKeyMask )
1154 val.i_int |= KEY_MODIFIER_COMMAND;
1156 key = [[o_event charactersIgnoringModifiers] characterAtIndex: 0];
1160 /* Escape should always get you out of fullscreen */
1161 if( key == (unichar) 0x1b )
1163 if( [self isFullscreen] )
1165 [self toggleFullscreen];
1168 else if ( key == ' ' )
1170 playlist_t *p_playlist = vlc_object_find( p_vout, VLC_OBJECT_PLAYLIST,
1172 if ( p_playlist != NULL )
1174 playlist_Pause( p_playlist );
1175 vlc_object_release( p_playlist);
1180 val.i_int |= CocoaKeyToVLC( key );
1181 var_Set( p_vout->p_vlc, "key-pressed", val );
1186 [super keyDown: o_event];
1192 NSMutableString * o_title;
1193 playlist_t * p_playlist = vlc_object_find( p_vout, VLC_OBJECT_PLAYLIST,
1196 if( p_playlist == NULL )
1201 vlc_mutex_lock( &p_playlist->object_lock );
1202 o_title = [NSMutableString stringWithUTF8String:
1203 p_playlist->pp_items[p_playlist->i_index]->input.psz_uri];
1204 vlc_mutex_unlock( &p_playlist->object_lock );
1206 vlc_object_release( p_playlist );
1208 if( o_title != nil )
1210 NSRange prefix_range = [o_title rangeOfString: @"file:"];
1211 if( prefix_range.location != NSNotFound )
1213 [o_title deleteCharactersInRange: prefix_range];
1216 [self setTitleWithRepresentedFilename: o_title];
1221 [NSString stringWithCString: VOUT_TITLE " (QuickTime)"]];
1225 /* This is actually the same as VLCControls::stop. */
1226 - (BOOL)windowShouldClose:(id)sender
1228 playlist_t * p_playlist = vlc_object_find( p_vout, VLC_OBJECT_PLAYLIST,
1230 if( p_playlist == NULL )
1235 playlist_Stop( p_playlist );
1236 vlc_object_release( p_playlist );
1238 /* The window will be closed by the intf later. */
1244 /* Common QT and OpenGL code to catch mouse events */
1245 #define CATCH_MOUSE_EVENTS \
1246 - (BOOL)acceptsFirstResponder \
1251 - (BOOL)becomeFirstResponder \
1253 id o_window = [self window]; \
1255 [o_window setAcceptsMouseMovedEvents: YES]; \
1259 - (BOOL)resignFirstResponder \
1261 vout_thread_t * vout; \
1262 id o_window = [self window]; \
1263 vout = (vout_thread_t *)[o_window getVout]; \
1265 [o_window setAcceptsMouseMovedEvents: NO]; \
1266 VLCHideMouse( vout, NO ); \
1270 - (void)mouseDown:(NSEvent *)o_event \
1272 vout_thread_t * vout; \
1273 id o_window = [self window]; \
1274 vout = (vout_thread_t *)[o_window getVout]; \
1277 switch( [o_event type] ) \
1279 case NSLeftMouseDown: \
1281 var_Get( vout, "mouse-button-down", &val ); \
1283 var_Set( vout, "mouse-button-down", val ); \
1288 [super mouseDown: o_event]; \
1293 - (void)otherMouseDown:(NSEvent *)o_event \
1295 vout_thread_t * vout; \
1296 id o_window = [self window]; \
1297 vout = (vout_thread_t *)[o_window getVout]; \
1300 switch( [o_event type] ) \
1302 case NSOtherMouseDown: \
1304 var_Get( vout, "mouse-button-down", &val ); \
1306 var_Set( vout, "mouse-button-down", val ); \
1311 [super mouseDown: o_event]; \
1316 - (void)rightMouseDown:(NSEvent *)o_event \
1318 vout_thread_t * vout; \
1319 id o_window = [self window]; \
1320 vout = (vout_thread_t *)[o_window getVout]; \
1323 switch( [o_event type] ) \
1325 case NSRightMouseDown: \
1327 var_Get( vout, "mouse-button-down", &val ); \
1329 var_Set( vout, "mouse-button-down", val ); \
1334 [super mouseDown: o_event]; \
1339 - (void)mouseUp:(NSEvent *)o_event \
1341 vout_thread_t * vout; \
1342 id o_window = [self window]; \
1343 vout = (vout_thread_t *)[o_window getVout]; \
1346 switch( [o_event type] ) \
1348 case NSLeftMouseUp: \
1350 vlc_value_t b_val; \
1351 b_val.b_bool = VLC_TRUE; \
1352 var_Set( vout, "mouse-clicked", b_val ); \
1354 var_Get( vout, "mouse-button-down", &val ); \
1356 var_Set( vout, "mouse-button-down", val ); \
1361 [super mouseUp: o_event]; \
1366 - (void)otherMouseUp:(NSEvent *)o_event \
1368 vout_thread_t * vout; \
1369 id o_window = [self window]; \
1370 vout = (vout_thread_t *)[o_window getVout]; \
1373 switch( [o_event type] ) \
1375 case NSOtherMouseUp: \
1377 var_Get( vout, "mouse-button-down", &val ); \
1379 var_Set( vout, "mouse-button-down", val ); \
1384 [super mouseUp: o_event]; \
1389 - (void)rightMouseUp:(NSEvent *)o_event \
1391 vout_thread_t * vout; \
1392 id o_window = [self window]; \
1393 vout = (vout_thread_t *)[o_window getVout]; \
1396 switch( [o_event type] ) \
1398 case NSRightMouseUp: \
1400 var_Get( vout, "mouse-button-down", &val ); \
1402 var_Set( vout, "mouse-button-down", val ); \
1407 [super mouseUp: o_event]; \
1412 - (void)mouseDragged:(NSEvent *)o_event \
1414 [self mouseMoved:o_event]; \
1417 - (void)otherMouseDragged:(NSEvent *)o_event \
1419 [self mouseMoved:o_event]; \
1422 - (void)rightMouseDragged:(NSEvent *)o_event \
1424 [self mouseMoved:o_event]; \
1427 /*****************************************************************************
1428 * VLCQTView implementation
1429 *****************************************************************************/
1430 @implementation VLCQTView
1432 - (void)drawRect:(NSRect)rect
1434 vout_thread_t * p_vout;
1435 id o_window = [self window];
1436 p_vout = (vout_thread_t *)[o_window getVout];
1438 [[NSColor blackColor] set];
1440 [super drawRect: rect];
1442 p_vout->i_changes |= VOUT_SIZE_CHANGE;
1447 - (void)mouseMoved:(NSEvent *)o_event
1453 vout_thread_t * p_vout;
1454 id o_window = [self window];
1455 p_vout = (vout_thread_t *)[o_window getVout];
1457 s_rect = [self bounds];
1458 ml = [self convertPoint: [o_event locationInWindow] fromView: nil];
1459 b_inside = [self mouse: ml inRect: s_rect];
1464 int i_width, i_height, i_x, i_y;
1466 vout_PlacePicture( p_vout, (unsigned int)s_rect.size.width,
1467 (unsigned int)s_rect.size.height,
1468 &i_x, &i_y, &i_width, &i_height );
1470 val.i_int = ( ((int)ml.x) - i_x ) *
1471 p_vout->render.i_width / i_width;
1472 var_Set( p_vout, "mouse-x", val );
1474 val.i_int = ( ((int)ml.y) - i_y ) *
1475 p_vout->render.i_height / i_height;
1476 var_Set( p_vout, "mouse-y", val );
1478 val.b_bool = VLC_TRUE;
1479 var_Set( p_vout, "mouse-moved", val );
1480 p_vout->p_sys->i_time_mouse_last_moved = mdate();
1481 p_vout->p_sys->b_mouse_moved = YES;
1484 [super mouseMoved: o_event];
1489 /*****************************************************************************
1490 * VLCGLView implementation
1491 *****************************************************************************/
1492 @implementation VLCGLView
1495 - (id) initWithFrame: (NSRect) frame vout: (vout_thread_t*) _p_vout
1500 NSOpenGLPixelFormatAttribute attribs[] =
1502 NSOpenGLPFAAccelerated,
1503 NSOpenGLPFANoRecovery,
1504 NSOpenGLPFAColorSize, 24,
1505 NSOpenGLPFAAlphaSize, 8,
1506 NSOpenGLPFADepthSize, 24,
1511 NSOpenGLPixelFormat * fmt = [[NSOpenGLPixelFormat alloc]
1512 initWithAttributes: attribs];
1516 msg_Warn( p_vout, "Cannot create NSOpenGLPixelFormat" );
1520 self = [super initWithFrame:frame pixelFormat: fmt];
1523 [[self openGLContext] makeCurrentContext];
1524 [[self openGLContext] update];
1526 /* Black background */
1527 glClearColor( 0.0, 0.0, 0.0, 0.0 );
1529 /* Check if the user asked for useless visual effects */
1530 var_Get( p_vout, "macosx-opengl-effect", &val );
1531 if( !val.psz_string || !strcmp( val.psz_string, "none" ))
1533 i_effect = OPENGL_EFFECT_NONE;
1535 else if( !strcmp( val.psz_string, "cube" ) )
1537 i_effect = OPENGL_EFFECT_CUBE;
1539 glEnable( GL_DEPTH_TEST );
1541 else if( !strcmp( val.psz_string, "transparent-cube" ) )
1543 i_effect = OPENGL_EFFECT_TRANSPARENT_CUBE;
1545 glDisable( GL_DEPTH_TEST );
1546 glEnable( GL_BLEND );
1547 glBlendFunc( GL_SRC_ALPHA, GL_ONE );
1551 msg_Warn( p_vout, "no valid opengl effect provided, using "
1553 i_effect = OPENGL_EFFECT_NONE;
1555 if( val.psz_string ) free( val.psz_string );
1557 if( i_effect & ( OPENGL_EFFECT_CUBE |
1558 OPENGL_EFFECT_TRANSPARENT_CUBE ) )
1560 /* Set the perpective */
1561 glMatrixMode( GL_PROJECTION );
1563 glFrustum( -1.0, 1.0, -1.0, 1.0, 3.0, 20.0 );
1564 glMatrixMode( GL_MODELVIEW );
1566 glTranslatef( 0.0, 0.0, - 5.0 );
1586 [[self openGLContext] makeCurrentContext];
1588 bounds = [self bounds];
1589 glViewport( 0, 0, (GLint) bounds.size.width,
1590 (GLint) bounds.size.height );
1592 var_Get( p_vout, "macosx-stretch", &val );
1600 /* Quad size is set in order to preserve the aspect ratio */
1601 var_Get( p_vout, "macosx-fill", &val );
1602 int fill = ( val.b_bool && p_vout->b_fullscreen );
1603 int large = ( bounds.size.height * p_vout->output.i_aspect <
1604 bounds.size.width * VOUT_ASPECT_FACTOR );
1605 if( ( large && !fill ) || ( !large && fill ) )
1607 f_x = bounds.size.height * p_vout->output.i_aspect /
1608 VOUT_ASPECT_FACTOR / bounds.size.width;
1614 f_y = bounds.size.width * VOUT_ASPECT_FACTOR /
1615 p_vout->output.i_aspect / bounds.size.height;
1620 - (void)initTextures
1623 [[self openGLContext] makeCurrentContext];
1625 /* Free previous texture if any */
1628 glDeleteTextures( 2, pi_textures );
1631 glEnable( GL_TEXTURE_RECTANGLE_EXT );
1632 glEnable( GL_UNPACK_CLIENT_STORAGE_APPLE );
1634 glPixelStorei( GL_UNPACK_ALIGNMENT, 1 );
1635 glPixelStorei( GL_UNPACK_ROW_LENGTH, p_vout->output.i_width );
1637 /* Tell the driver not to make a copy of the texture but to use
1639 glPixelStorei( GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE );
1641 /* Create textures */
1642 glGenTextures( 2, pi_textures );
1644 for( i = 0; i < 2; i++ )
1646 glBindTexture( GL_TEXTURE_RECTANGLE_EXT, pi_textures[i] );
1647 glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE );
1649 /* Linear interpolation */
1650 glTexParameteri( GL_TEXTURE_RECTANGLE_EXT,
1651 GL_TEXTURE_MIN_FILTER, GL_LINEAR );
1652 glTexParameteri( GL_TEXTURE_RECTANGLE_EXT,
1653 GL_TEXTURE_MAG_FILTER, GL_LINEAR );
1655 /* Use VRAM texturing */
1656 glTexParameteri( GL_TEXTURE_RECTANGLE_EXT,
1657 GL_TEXTURE_STORAGE_HINT_APPLE, GL_STORAGE_CACHED_APPLE );
1659 glTexImage2D( GL_TEXTURE_RECTANGLE_EXT, 0, GL_RGB8,
1660 p_vout->output.i_width, p_vout->output.i_height, 0,
1661 GL_YCBCR_422_APPLE, GL_UNSIGNED_SHORT_8_8_APPLE,
1662 p_vout->p_sys->p_data[i] );
1665 /* Swap buffers only during the vertical retrace of the monitor.
1666 http://developer.apple.com/documentation/GraphicsImaging/
1667 Conceptual/OpenGL/chap5/chapter_5_section_44.html */
1668 long params[] = { 1 };
1669 CGLSetParameter( CGLGetCurrentContext(), kCGLCPSwapInterval,
1675 - (void)reloadTexture: (int) index
1682 [[self openGLContext] makeCurrentContext];
1684 glBindTexture( GL_TEXTURE_RECTANGLE_EXT, pi_textures[index] );
1685 glPixelStorei( GL_UNPACK_ROW_LENGTH, p_vout->output.i_width );
1687 /* glTexSubImage2D is faster than glTexImage2D
1688 http://developer.apple.com/samplecode/Sample_Code/Graphics_3D/
1689 TextureRange/MainOpenGLView.m.htm */
1690 glTexSubImage2D( GL_TEXTURE_RECTANGLE_EXT, 0, 0, 0,
1691 p_vout->output.i_width, p_vout->output.i_height,
1692 GL_YCBCR_422_APPLE, GL_UNSIGNED_SHORT_8_8_APPLE,
1693 p_vout->p_sys->p_data[index] );
1703 glBegin( GL_QUADS );
1705 glTexCoord2f( 0.0, 0.0 );
1706 glVertex2f( - f_x, f_y );
1708 glTexCoord2f( 0.0, (float) p_vout->output.i_height );
1709 glVertex2f( - f_x, - f_y );
1711 glTexCoord2f( (float) p_vout->output.i_width,
1712 (float) p_vout->output.i_height );
1713 glVertex2f( f_x, - f_y );
1715 glTexCoord2f( (float) p_vout->output.i_width, 0.0 );
1716 glVertex2f( f_x, f_y );
1722 glBegin( GL_QUADS );
1724 glTexCoord2f( 0.0, 0.0 );
1725 glVertex3f( - 1.0, 1.0, 1.0 );
1726 glTexCoord2f( 0.0, (float) p_vout->output.i_height );
1727 glVertex3f( - 1.0, - 1.0, 1.0 );
1728 glTexCoord2f( (float) p_vout->output.i_width,
1729 (float) p_vout->output.i_height );
1730 glVertex3f( 1.0, - 1.0, 1.0 );
1731 glTexCoord2f( (float) p_vout->output.i_width, 0.0 );
1732 glVertex3f( 1.0, 1.0, 1.0 );
1735 glTexCoord2f( 0.0, 0.0 );
1736 glVertex3f( - 1.0, 1.0, - 1.0 );
1737 glTexCoord2f( 0.0, (float) p_vout->output.i_height );
1738 glVertex3f( - 1.0, - 1.0, - 1.0 );
1739 glTexCoord2f( (float) p_vout->output.i_width,
1740 (float) p_vout->output.i_height );
1741 glVertex3f( - 1.0, - 1.0, 1.0 );
1742 glTexCoord2f( (float) p_vout->output.i_width, 0.0 );
1743 glVertex3f( - 1.0, 1.0, 1.0 );
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 );
1791 - (void) drawRect: (NSRect) rect
1793 [[self openGLContext] makeCurrentContext];
1795 /* Black background */
1796 glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
1805 glBindTexture( GL_TEXTURE_RECTANGLE_EXT,
1806 pi_textures[p_vout->p_sys->i_cur_pic] );
1807 if( i_effect & ( OPENGL_EFFECT_CUBE |
1808 OPENGL_EFFECT_TRANSPARENT_CUBE ) )
1810 glRotatef( 1.0, 0.3, 0.5, 0.7 );
1824 - (void)mouseMoved:(NSEvent *)o_event
1830 s_rect = [self bounds];
1831 ml = [self convertPoint: [o_event locationInWindow] fromView: nil];
1832 b_inside = [self mouse: ml inRect: s_rect];
1837 int i_width, i_height, i_x, i_y;
1839 vout_PlacePicture( p_vout, (unsigned int)s_rect.size.width,
1840 (unsigned int)s_rect.size.height,
1841 &i_x, &i_y, &i_width, &i_height );
1843 val.i_int = ( (int)ml.x - i_x ) *
1844 p_vout->render.i_width / i_width;
1845 var_Set( p_vout, "mouse-x", val );
1847 /* Y coordinate is inverted in OpenGL */
1848 val.i_int = ( ((int)(s_rect.size.height - ml.y)) - i_y ) *
1849 p_vout->render.i_height / i_height;
1850 var_Set( p_vout, "mouse-y", val );
1852 val.b_bool = VLC_TRUE;
1853 var_Set( p_vout, "mouse-moved", val );
1854 p_vout->p_sys->i_time_mouse_last_moved = mdate();
1855 p_vout->p_sys->b_mouse_moved = YES;
1858 [super mouseMoved: o_event];