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 vout_Init ( vout_thread_t * );
67 static void vout_End ( vout_thread_t * );
68 static int vout_Manage ( vout_thread_t * );
69 static void vout_Display ( vout_thread_t *, picture_t * );
71 static int CoCreateWindow ( vout_thread_t * );
72 static int CoDestroyWindow ( vout_thread_t * );
73 static int CoToggleFullscreen ( vout_thread_t * );
75 static void VLCHideMouse ( vout_thread_t *, BOOL );
77 static void QTScaleMatrix ( vout_thread_t * );
78 static int QTCreateSequence ( vout_thread_t * );
79 static void QTDestroySequence ( vout_thread_t * );
80 static int QTNewPicture ( vout_thread_t *, picture_t * );
81 static void QTFreePicture ( vout_thread_t *, picture_t * );
83 /*****************************************************************************
84 * OpenVideo: allocates MacOS X video thread output method
85 *****************************************************************************
86 * This function allocates and initializes a MacOS X vout method.
87 *****************************************************************************/
88 int E_(OpenVideo) ( vlc_object_t *p_this )
90 vout_thread_t * p_vout = (vout_thread_t *)p_this;
95 p_vout->p_sys = malloc( sizeof( vout_sys_t ) );
96 if( p_vout->p_sys == NULL )
98 msg_Err( p_vout, "out of memory" );
102 memset( p_vout->p_sys, 0, sizeof( vout_sys_t ) );
104 /* Wait for a MacOS X interface to appear. Timeout is 2 seconds. */
105 for( i_timeout = 20 ; i_timeout-- ; )
109 msleep( INTF_IDLE_SLEEP );
115 /* no MacOS X intf, unable to communicate with MT */
116 msg_Err( p_vout, "no MacOS X interface present" );
117 free( p_vout->p_sys );
121 p_vout->p_sys->o_pool = [[NSAutoreleasePool alloc] init];
122 p_vout->p_sys->b_mouse_moved = VLC_TRUE;
123 p_vout->p_sys->i_time_mouse_last_moved = mdate();
125 /* set window size */
126 p_vout->p_sys->s_rect.size.width = p_vout->i_window_width;
127 p_vout->p_sys->s_rect.size.height = p_vout->i_window_height;
129 /* Check if we should use QuickTime or OpenGL */
130 psz_vout_type = config_GetPsz( p_vout, "macosx-vout" );
132 if( !strncmp( psz_vout_type, "auto", 4 ) )
134 p_vout->p_sys->i_opengl = CGDisplayUsesOpenGLAcceleration( kCGDirectMainDisplay );
136 else if( !strncmp( psz_vout_type, "opengl", 6 ) )
138 p_vout->p_sys->i_opengl = VLC_TRUE;
142 p_vout->p_sys->i_opengl = VLC_FALSE;
144 free( psz_vout_type );
146 if( !p_vout->p_sys->i_opengl )
148 /* Initialize QuickTime */
149 p_vout->p_sys->h_img_descr =
150 (ImageDescriptionHandle)NewHandleClear( sizeof(ImageDescription) );
151 p_vout->p_sys->p_matrix =
152 (MatrixRecordPtr)malloc( sizeof(MatrixRecord) );
153 p_vout->p_sys->p_fullscreen_state = NULL;
155 if( ( err = EnterMovies() ) != noErr )
157 msg_Err( p_vout, "EnterMovies failed: %d", err );
158 free( p_vout->p_sys->p_matrix );
159 DisposeHandle( (Handle)p_vout->p_sys->h_img_descr );
160 free( p_vout->p_sys );
164 /* Damn QT isn't thread safe. so keep a lock in the p_vlc object */
165 vlc_mutex_lock( &p_vout->p_vlc->quicktime_lock );
167 err = FindCodec( kYUV420CodecType, bestSpeedCodec,
168 nil, &p_vout->p_sys->img_dc );
170 vlc_mutex_unlock( &p_vout->p_vlc->quicktime_lock );
172 if( err == noErr && p_vout->p_sys->img_dc != 0 )
174 p_vout->output.i_chroma = VLC_FOURCC('I','4','2','0');
175 p_vout->p_sys->i_codec = kYUV420CodecType;
179 msg_Err( p_vout, "failed to find an appropriate codec" );
182 if( p_vout->p_sys->img_dc == 0 )
184 free( p_vout->p_sys->p_matrix );
185 DisposeHandle( (Handle)p_vout->p_sys->h_img_descr );
186 free( p_vout->p_sys );
189 msg_Dbg( p_vout, "using Quartz mode" );
193 msg_Dbg( p_vout, "using OpenGL mode" );
196 NSArray * o_screens = [NSScreen screens];
197 if( [o_screens count] > 0 && var_Type( p_vout, "video-device" ) == 0 )
200 vlc_value_t val, text;
203 int i_option = config_GetInt( p_vout, "macosx-vdev" );
205 var_Create( p_vout, "video-device", VLC_VAR_INTEGER |
207 text.psz_string = _("Video device");
208 var_Change( p_vout, "video-device", VLC_VAR_SETTEXT, &text, NULL );
210 NSEnumerator * o_enumerator = [o_screens objectEnumerator];
212 while( (o_screen = [o_enumerator nextObject]) != NULL )
215 NSRect s_rect = [o_screen frame];
217 snprintf( psz_temp, sizeof(psz_temp)/sizeof(psz_temp[0])-1,
218 "%s %d (%dx%d)", _("Screen"), i,
219 (int)s_rect.size.width, (int)s_rect.size.height );
221 text.psz_string = psz_temp;
223 var_Change( p_vout, "video-device",
224 VLC_VAR_ADDCHOICE, &val, &text );
226 if( ( i - 1 ) == i_option )
228 var_Set( p_vout, "video-device", val );
233 var_AddCallback( p_vout, "video-device", vout_VarCallback,
236 val.b_bool = VLC_TRUE;
237 var_Set( p_vout, "intf-change", val );
240 if( CoCreateWindow( p_vout ) )
242 msg_Err( p_vout, "unable to create window" );
243 if( !p_vout->p_sys->i_opengl )
245 free( p_vout->p_sys->p_matrix );
246 DisposeHandle( (Handle)p_vout->p_sys->h_img_descr );
248 free( p_vout->p_sys );
252 p_vout->pf_init = vout_Init;
253 p_vout->pf_end = vout_End;
254 p_vout->pf_manage = vout_Manage;
255 p_vout->pf_render = NULL;
256 p_vout->pf_display = vout_Display;
261 /*****************************************************************************
262 * vout_Init: initialize video thread output method
263 *****************************************************************************/
264 static int vout_InitOpenGL( vout_thread_t *p_vout );
265 static int vout_InitQuickTime( vout_thread_t *p_vout );
266 static int vout_Init( vout_thread_t *p_vout )
268 I_OUTPUTPICTURES = 0;
270 /* Initialize the output structure; we already found a codec,
271 * and the corresponding chroma we will be using. Since we can
272 * arbitrary scale, stick to the coordinates and aspect. */
273 p_vout->output.i_width = p_vout->render.i_width;
274 p_vout->output.i_height = p_vout->render.i_height;
275 p_vout->output.i_aspect = p_vout->render.i_aspect;
277 if( p_vout->p_sys->i_opengl )
279 return vout_InitOpenGL( p_vout );
283 return vout_InitQuickTime( p_vout );
287 static int vout_InitOpenGL( vout_thread_t *p_vout )
293 /* Apple OpenGL extensions only accept YUV as YUY2 */
294 p_vout->output.i_chroma = VLC_FOURCC('Y','U','Y','2');
295 p_vout->output.i_rmask = 0xFF0000;
296 p_vout->output.i_gmask = 0x00FF00;
297 p_vout->output.i_bmask = 0x0000FF;
299 /* Allocate our 2 picture buffers */
300 i_bytes = 2 * p_vout->output.i_width * p_vout->output.i_height;
301 p_vout->p_sys->p_data[0] = vlc_memalign(
302 &p_vout->p_sys->p_data_orig[0], 16, i_bytes );
303 p_vout->p_sys->p_data[1] = vlc_memalign(
304 &p_vout->p_sys->p_data_orig[1], 16, i_bytes );
305 p_vout->p_sys->i_cur_pic = 1;
307 /* We declare only one picture and will switch buffers
310 while( I_OUTPUTPICTURES < 1 )
312 /* Find an empty picture slot */
313 for( i_index = 0; i_index < VOUT_MAX_PICTURES; i_index++ )
315 if( p_vout->p_picture[ i_index ].i_status == FREE_PICTURE )
317 p_pic = p_vout->p_picture + i_index;
326 vout_InitPicture( VLC_OBJECT( p_vout ), p_pic,
327 p_vout->output.i_chroma, p_vout->output.i_width,
328 p_vout->output.i_height, p_vout->output.i_aspect );
329 p_pic->p_data = p_vout->p_sys->p_data[0];
330 p_pic->p[0].p_pixels = p_pic->p_data;
331 for( i_index = 1; i_index < p_pic->i_planes; i_index++ )
333 p_pic->p[i_index].p_pixels =
334 p_pic->p[i_index-1].p_pixels +
335 p_pic->p[i_index-1].i_lines *
336 p_pic->p[i_index-1].i_pitch;
339 p_pic->i_status = DESTROYED_PICTURE;
340 p_pic->i_type = DIRECT_PICTURE;
342 PP_OUTPUTPICTURE[ I_OUTPUTPICTURES ] = p_pic;
346 [p_vout->p_sys->o_glview lockFocus];
347 [p_vout->p_sys->o_glview initTextures];
348 [p_vout->p_sys->o_glview reshape];
349 [p_vout->p_sys->o_glview unlockFocus];
354 static int vout_InitQuickTime( vout_thread_t *p_vout )
359 SetPort( p_vout->p_sys->p_qdport );
360 QTScaleMatrix( p_vout );
362 if( QTCreateSequence( p_vout ) )
364 msg_Err( p_vout, "unable to create sequence" );
368 /* Try to initialize up to QT_MAX_DIRECTBUFFERS direct buffers */
369 while( I_OUTPUTPICTURES < QT_MAX_DIRECTBUFFERS )
373 /* Find an empty picture slot */
374 for( i_index = 0; i_index < VOUT_MAX_PICTURES; i_index++ )
376 if( p_vout->p_picture[ i_index ].i_status == FREE_PICTURE )
378 p_pic = p_vout->p_picture + i_index;
387 /* Allocate the picture */
388 if( QTNewPicture( p_vout, p_pic ) )
393 p_pic->i_status = DESTROYED_PICTURE;
394 p_pic->i_type = DIRECT_PICTURE;
396 PP_OUTPUTPICTURE[ I_OUTPUTPICTURES ] = p_pic;
402 /*****************************************************************************
403 * vout_End: terminate video thread output method
404 *****************************************************************************/
405 static void vout_End( vout_thread_t *p_vout )
409 if( !p_vout->p_sys->i_opengl )
411 QTDestroySequence( p_vout );
414 /* Free the direct buffers we allocated */
415 for( i_index = I_OUTPUTPICTURES; i_index; )
418 if( !p_vout->p_sys->i_opengl )
420 QTFreePicture( p_vout, PP_OUTPUTPICTURE[ i_index ] );
424 free( p_vout->p_sys->p_data_orig[0] );
425 free( p_vout->p_sys->p_data_orig[1] );
430 /*****************************************************************************
431 * CloseVideo: destroy video thread output method
432 *****************************************************************************/
433 void E_(CloseVideo) ( vlc_object_t *p_this )
435 NSAutoreleasePool *o_pool = [[NSAutoreleasePool alloc] init];
436 vout_thread_t * p_vout = (vout_thread_t *)p_this;
438 if( p_vout->p_sys->i_opengl )
440 [p_vout->p_sys->o_glview cleanUp];
443 if( CoDestroyWindow( p_vout ) )
445 msg_Err( p_vout, "unable to destroy window" );
448 if ( p_vout->p_sys->p_fullscreen_state != NULL )
450 EndFullScreen ( p_vout->p_sys->p_fullscreen_state, NULL );
453 if( !p_vout->p_sys->i_opengl )
456 free( p_vout->p_sys->p_matrix );
457 DisposeHandle( (Handle)p_vout->p_sys->h_img_descr );
461 free( p_vout->p_sys );
464 /*****************************************************************************
465 * vout_Manage: handle events
466 *****************************************************************************
467 * This function should be called regularly by video output thread. It manages
468 * console events. It returns a non null value on error.
469 *****************************************************************************/
470 static int vout_Manage( vout_thread_t *p_vout )
472 if( p_vout->i_changes & VOUT_FULLSCREEN_CHANGE )
474 if( CoToggleFullscreen( p_vout ) )
479 p_vout->i_changes &= ~VOUT_FULLSCREEN_CHANGE;
482 if( p_vout->i_changes & VOUT_SIZE_CHANGE )
484 if( !p_vout->p_sys->i_opengl )
486 QTScaleMatrix( p_vout );
487 SetDSequenceMatrix( p_vout->p_sys->i_seq,
488 p_vout->p_sys->p_matrix );
491 p_vout->i_changes &= ~VOUT_SIZE_CHANGE;
494 /* hide/show mouse cursor
495 * this code looks unnecessarily complicated, but is necessary like this.
496 * it has to deal with multiple monitors and therefore checks a lot */
497 if( !p_vout->p_sys->b_mouse_moved && p_vout->b_fullscreen )
499 if( mdate() - p_vout->p_sys->i_time_mouse_last_moved > 3000000 )
501 VLCHideMouse( p_vout, YES );
504 else if ( p_vout->p_sys->b_mouse_moved && p_vout->b_fullscreen )
506 VLCHideMouse( p_vout, NO );
509 /* disable screen saver */
510 UpdateSystemActivity( UsrActivity );
515 /*****************************************************************************
516 * vout_Display: displays previously rendered output
517 *****************************************************************************
518 * This function sends the currently rendered image to the display.
519 *****************************************************************************/
520 static void vout_Display( vout_thread_t *p_vout, picture_t *p_pic )
522 if( !p_vout->p_sys->i_opengl )
527 if( ( err = DecompressSequenceFrameS(
528 p_vout->p_sys->i_seq,
529 p_pic->p_sys->p_info,
530 p_pic->p_sys->i_size,
531 codecFlagUseImageBuffer, &flags, nil ) != noErr ) )
533 msg_Warn( p_vout, "DecompressSequenceFrameS failed: %d", err );
537 QDFlushPortBuffer( p_vout->p_sys->p_qdport, nil );
542 if( [p_vout->p_sys->o_glview lockFocusIfCanDraw] )
545 int i_old = p_vout->p_sys->i_cur_pic;
546 int i_new = ( i_old + 1 ) % 2;
548 /* Draw the new picture */
549 p_vout->p_sys->i_cur_pic = i_new;
550 [p_vout->p_sys->o_glview drawRect:
551 [p_vout->p_sys->o_glview bounds]];
553 /* Reload the other texture. Textures have to be reloaded
554 before the buffer is filled (thanks to gcc from
555 arstechnica forums) */
556 [p_vout->p_sys->o_glview reloadTexture: i_old];
559 p_pic->p_data = p_vout->p_sys->p_data[i_old];
560 p_pic->p[0].p_pixels = p_pic->p_data;
561 for( i_index = 1; i_index < p_pic->i_planes; i_index++ )
563 p_pic->p[i_index].p_pixels =
564 p_pic->p[i_index-1].p_pixels +
565 p_pic->p[i_index-1].i_lines *
566 p_pic->p[i_index-1].i_pitch;
568 [p_vout->p_sys->o_glview unlockFocus];
573 /*****************************************************************************
574 * CoCreateWindow: create new window
575 *****************************************************************************
576 * Returns 0 on success, 1 otherwise
577 *****************************************************************************/
578 static int CoCreateWindow( vout_thread_t *p_vout )
583 vlc_bool_t b_main_screen;
584 NSAutoreleasePool *o_pool = [[NSAutoreleasePool alloc] init];
586 p_vout->p_sys->o_window = [VLCWindow alloc];
587 [p_vout->p_sys->o_window setReleasedWhenClosed: YES];
589 if( var_Get( p_vout, "video-device", &val ) < 0 )
591 o_screen = [NSScreen mainScreen];
596 NSArray *o_screens = [NSScreen screens];
597 unsigned int i_index = val.i_int;
599 if( [o_screens count] < i_index )
601 o_screen = [NSScreen mainScreen];
607 o_screen = [o_screens objectAtIndex: i_index];
608 config_PutInt( p_vout, "macosx-vdev", i_index );
609 b_main_screen = (i_index == 0);
613 if( p_vout->b_fullscreen )
615 NSRect screen_rect = [o_screen frame];
616 screen_rect.origin.x = screen_rect.origin.y = 0;
618 if ( b_main_screen && p_vout->p_sys->p_fullscreen_state == NULL )
619 BeginFullScreen( &p_vout->p_sys->p_fullscreen_state, NULL, 0, 0,
620 NULL, NULL, fullScreenAllowEvents );
622 [p_vout->p_sys->o_window
623 initWithContentRect: screen_rect
624 styleMask: NSBorderlessWindowMask
625 backing: NSBackingStoreBuffered
626 defer: NO screen: o_screen];
628 [p_vout->p_sys->o_window setVout: p_vout];
629 p_vout->p_sys->b_mouse_moved = YES;
630 p_vout->p_sys->i_time_mouse_last_moved = mdate();
634 unsigned int i_stylemask = NSTitledWindowMask |
635 NSMiniaturizableWindowMask |
636 NSClosableWindowMask |
637 NSResizableWindowMask;
639 if ( p_vout->p_sys->p_fullscreen_state != NULL )
640 EndFullScreen ( p_vout->p_sys->p_fullscreen_state, NULL );
641 p_vout->p_sys->p_fullscreen_state = NULL;
643 [p_vout->p_sys->o_window
644 initWithContentRect: p_vout->p_sys->s_rect
645 styleMask: i_stylemask
646 backing: NSBackingStoreBuffered
647 defer: NO screen: o_screen];
649 [p_vout->p_sys->o_window setVout: p_vout];
650 [p_vout->p_sys->o_window setAlphaValue: config_GetFloat( p_vout, "macosx-opaqueness" )];
652 if( config_GetInt( p_vout, "video-on-top" ) )
654 [p_vout->p_sys->o_window setLevel: NSStatusWindowLevel];
657 if( !p_vout->p_sys->b_pos_saved )
659 [p_vout->p_sys->o_window center];
663 if( !p_vout->p_sys->i_opengl )
665 o_view = [[VLCQTView alloc] init];
666 /* FIXME: [o_view setMenu:] */
667 [p_vout->p_sys->o_window setContentView: o_view];
668 [o_view autorelease];
671 p_vout->p_sys->p_qdport = [o_view qdPort];
672 [o_view unlockFocus];
676 #define o_glview p_vout->p_sys->o_glview
677 o_glview = [[VLCGLView alloc] initWithFrame: p_vout->p_sys->s_rect vout: p_vout];
678 [p_vout->p_sys->o_window setContentView: o_glview];
679 [o_glview autorelease];
683 [p_vout->p_sys->o_window updateTitle];
684 [p_vout->p_sys->o_window makeKeyAndOrderFront: nil];
690 /*****************************************************************************
691 * CoDestroyWindow: destroy window
692 *****************************************************************************
693 * Returns 0 on success, 1 otherwise
694 *****************************************************************************/
695 static int CoDestroyWindow( vout_thread_t *p_vout )
697 NSAutoreleasePool *o_pool = [[NSAutoreleasePool alloc] init];
698 VLCHideMouse( p_vout, NO );
700 if( !p_vout->b_fullscreen )
704 s_rect = [[p_vout->p_sys->o_window contentView] frame];
705 p_vout->p_sys->s_rect.size = s_rect.size;
707 s_rect = [p_vout->p_sys->o_window frame];
708 p_vout->p_sys->s_rect.origin = s_rect.origin;
710 p_vout->p_sys->b_pos_saved = YES;
713 p_vout->p_sys->p_qdport = nil;
714 [p_vout->p_sys->o_window close];
715 p_vout->p_sys->o_window = nil;
720 /*****************************************************************************
721 * CoToggleFullscreen: toggle fullscreen
722 *****************************************************************************
723 * Returns 0 on success, 1 otherwise
724 *****************************************************************************/
725 static int CoToggleFullscreen( vout_thread_t *p_vout )
727 NSAutoreleasePool *o_pool = [[NSAutoreleasePool alloc] init];
729 if( !p_vout->p_sys->i_opengl )
731 QTDestroySequence( p_vout );
734 if( CoDestroyWindow( p_vout ) )
736 msg_Err( p_vout, "unable to destroy window" );
740 p_vout->b_fullscreen = !p_vout->b_fullscreen;
742 if( CoCreateWindow( p_vout ) )
744 msg_Err( p_vout, "unable to create window" );
748 if( p_vout->p_sys->i_opengl )
750 [p_vout->p_sys->o_glview lockFocus];
751 [p_vout->p_sys->o_glview initTextures];
752 [p_vout->p_sys->o_glview reshape];
753 [p_vout->p_sys->o_glview drawRect:
754 [p_vout->p_sys->o_glview bounds]];
755 [p_vout->p_sys->o_glview unlockFocus];
759 SetPort( p_vout->p_sys->p_qdport );
760 QTScaleMatrix( p_vout );
762 if( QTCreateSequence( p_vout ) )
764 msg_Err( p_vout, "unable to create sequence" );
773 /*****************************************************************************
774 * VLCHideMouse: if b_hide then hide the cursor
775 *****************************************************************************/
776 static void VLCHideMouse ( vout_thread_t *p_vout, BOOL b_hide )
781 NSWindow *o_window = p_vout->p_sys->o_window;
782 NSView *o_contents = [o_window contentView];
784 s_rect = [o_contents bounds];
785 ml = [o_window convertScreenToBase:[NSEvent mouseLocation]];
786 ml = [o_contents convertPoint:ml fromView:nil];
787 b_inside = [o_contents mouse: ml inRect: s_rect];
789 if ( b_hide && b_inside )
791 /* only hide if mouse over VLCQTView */
792 [NSCursor setHiddenUntilMouseMoves: YES];
796 [NSCursor setHiddenUntilMouseMoves: NO];
798 p_vout->p_sys->b_mouse_moved = NO;
799 p_vout->p_sys->i_time_mouse_last_moved = mdate();
803 /*****************************************************************************
804 * QTScaleMatrix: scale matrix
805 *****************************************************************************/
806 static void QTScaleMatrix( vout_thread_t *p_vout )
809 unsigned int i_width, i_height;
810 Fixed factor_x, factor_y;
811 unsigned int i_offset_x = 0;
812 unsigned int i_offset_y = 0;
814 GetPortBounds( p_vout->p_sys->p_qdport, &s_rect );
816 i_width = s_rect.right - s_rect.left;
817 i_height = s_rect.bottom - s_rect.top;
819 if( config_GetInt( p_vout, "macosx-stretch" ) )
821 factor_x = FixDiv( Long2Fix( i_width ),
822 Long2Fix( p_vout->output.i_width ) );
823 factor_y = FixDiv( Long2Fix( i_height ),
824 Long2Fix( p_vout->output.i_height ) );
827 else if( i_height * p_vout->output.i_aspect < i_width * VOUT_ASPECT_FACTOR )
829 int i_adj_width = i_height * p_vout->output.i_aspect /
832 factor_x = FixDiv( Long2Fix( i_adj_width ),
833 Long2Fix( p_vout->output.i_width ) );
834 factor_y = FixDiv( Long2Fix( i_height ),
835 Long2Fix( p_vout->output.i_height ) );
837 i_offset_x = (i_width - i_adj_width) / 2;
841 int i_adj_height = i_width * VOUT_ASPECT_FACTOR /
842 p_vout->output.i_aspect;
844 factor_x = FixDiv( Long2Fix( i_width ),
845 Long2Fix( p_vout->output.i_width ) );
846 factor_y = FixDiv( Long2Fix( i_adj_height ),
847 Long2Fix( p_vout->output.i_height ) );
849 i_offset_y = (i_height - i_adj_height) / 2;
852 SetIdentityMatrix( p_vout->p_sys->p_matrix );
854 ScaleMatrix( p_vout->p_sys->p_matrix,
856 Long2Fix(0), Long2Fix(0) );
858 TranslateMatrix( p_vout->p_sys->p_matrix,
859 Long2Fix(i_offset_x), Long2Fix(i_offset_y) );
862 /*****************************************************************************
863 * QTCreateSequence: create a new sequence
864 *****************************************************************************
865 * Returns 0 on success, 1 otherwise
866 *****************************************************************************/
867 static int QTCreateSequence( vout_thread_t *p_vout )
870 ImageDescriptionPtr p_descr;
872 HLock( (Handle)p_vout->p_sys->h_img_descr );
873 p_descr = *p_vout->p_sys->h_img_descr;
875 p_descr->idSize = sizeof(ImageDescription);
876 p_descr->cType = p_vout->p_sys->i_codec;
877 p_descr->version = 1;
878 p_descr->revisionLevel = 0;
879 p_descr->vendor = 'appl';
880 p_descr->width = p_vout->output.i_width;
881 p_descr->height = p_vout->output.i_height;
882 p_descr->hRes = Long2Fix(72);
883 p_descr->vRes = Long2Fix(72);
884 p_descr->spatialQuality = codecLosslessQuality;
885 p_descr->frameCount = 1;
886 p_descr->clutID = -1;
887 p_descr->dataSize = 0;
890 HUnlock( (Handle)p_vout->p_sys->h_img_descr );
892 if( ( err = DecompressSequenceBeginS(
893 &p_vout->p_sys->i_seq,
894 p_vout->p_sys->h_img_descr,
896 p_vout->p_sys->p_qdport,
898 p_vout->p_sys->p_matrix,
900 codecFlagUseImageBuffer,
901 codecLosslessQuality,
902 p_vout->p_sys->img_dc ) ) )
904 msg_Err( p_vout, "DecompressSequenceBeginS failed: %d", err );
911 /*****************************************************************************
912 * QTDestroySequence: destroy sequence
913 *****************************************************************************/
914 static void QTDestroySequence( vout_thread_t *p_vout )
916 CDSequenceEnd( p_vout->p_sys->i_seq );
919 /*****************************************************************************
920 * QTNewPicture: allocate a picture
921 *****************************************************************************
922 * Returns 0 on success, 1 otherwise
923 *****************************************************************************/
924 static int QTNewPicture( vout_thread_t *p_vout, picture_t *p_pic )
926 int i_width = p_vout->output.i_width;
927 int i_height = p_vout->output.i_height;
929 /* We know the chroma, allocate a buffer which will be used
930 * directly by the decoder */
931 p_pic->p_sys = malloc( sizeof( picture_sys_t ) );
933 if( p_pic->p_sys == NULL )
938 switch( p_vout->output.i_chroma )
940 case VLC_FOURCC('I','4','2','0'):
942 p_pic->p_sys->p_info = (void *)&p_pic->p_sys->pixmap_i420;
943 p_pic->p_sys->i_size = sizeof(PlanarPixmapInfoYUV420);
945 /* Allocate the memory buffer */
946 p_pic->p_data = vlc_memalign( &p_pic->p_data_orig,
947 16, i_width * i_height * 3 / 2 );
950 p_pic->Y_PIXELS = p_pic->p_data;
951 p_pic->p[Y_PLANE].i_lines = i_height;
952 p_pic->p[Y_PLANE].i_pitch = i_width;
953 p_pic->p[Y_PLANE].i_pixel_pitch = 1;
954 p_pic->p[Y_PLANE].i_visible_pitch = i_width;
957 p_pic->U_PIXELS = p_pic->Y_PIXELS + i_height * i_width;
958 p_pic->p[U_PLANE].i_lines = i_height / 2;
959 p_pic->p[U_PLANE].i_pitch = i_width / 2;
960 p_pic->p[U_PLANE].i_pixel_pitch = 1;
961 p_pic->p[U_PLANE].i_visible_pitch = i_width / 2;
964 p_pic->V_PIXELS = p_pic->U_PIXELS + i_height * i_width / 4;
965 p_pic->p[V_PLANE].i_lines = i_height / 2;
966 p_pic->p[V_PLANE].i_pitch = i_width / 2;
967 p_pic->p[V_PLANE].i_pixel_pitch = 1;
968 p_pic->p[V_PLANE].i_visible_pitch = i_width / 2;
970 /* We allocated 3 planes */
973 #define P p_pic->p_sys->pixmap_i420
974 P.componentInfoY.offset = (void *)p_pic->Y_PIXELS
975 - p_pic->p_sys->p_info;
976 P.componentInfoCb.offset = (void *)p_pic->U_PIXELS
977 - p_pic->p_sys->p_info;
978 P.componentInfoCr.offset = (void *)p_pic->V_PIXELS
979 - p_pic->p_sys->p_info;
981 P.componentInfoY.rowBytes = i_width;
982 P.componentInfoCb.rowBytes = i_width / 2;
983 P.componentInfoCr.rowBytes = i_width / 2;
989 /* Unknown chroma, tell the guy to get lost */
990 free( p_pic->p_sys );
991 msg_Err( p_vout, "never heard of chroma 0x%.8x (%4.4s)",
992 p_vout->output.i_chroma, (char*)&p_vout->output.i_chroma );
1000 /*****************************************************************************
1001 * QTFreePicture: destroy a picture allocated with QTNewPicture
1002 *****************************************************************************/
1003 static void QTFreePicture( vout_thread_t *p_vout, picture_t *p_pic )
1005 switch( p_vout->output.i_chroma )
1007 case VLC_FOURCC('I','4','2','0'):
1008 free( p_pic->p_data_orig );
1012 free( p_pic->p_sys );
1015 /*****************************************************************************
1016 * VLCWindow implementation
1017 *****************************************************************************/
1018 @implementation VLCWindow
1020 - (void)setVout:(vout_thread_t *)_p_vout
1025 - (vout_thread_t *)getVout
1030 - (void)scaleWindowWithFactor: (float)factor
1033 int i_corrected_height, i_corrected_width;
1034 NSPoint topleftbase;
1035 NSPoint topleftscreen;
1037 if ( !p_vout->b_fullscreen )
1040 topleftbase.y = [self frame].size.height;
1041 topleftscreen = [self convertBaseToScreen: topleftbase];
1043 if( p_vout->output.i_height * p_vout->output.i_aspect >
1044 p_vout->output.i_width * VOUT_ASPECT_FACTOR )
1046 i_corrected_width = p_vout->output.i_height * p_vout->output.i_aspect /
1048 newsize.width = (int) ( i_corrected_width * factor );
1049 newsize.height = (int) ( p_vout->render.i_height * factor );
1053 i_corrected_height = p_vout->output.i_width * VOUT_ASPECT_FACTOR /
1054 p_vout->output.i_aspect;
1055 newsize.width = (int) ( p_vout->render.i_width * factor );
1056 newsize.height = (int) ( i_corrected_height * factor );
1059 [self setContentSize: newsize];
1061 [self setFrameTopLeftPoint: topleftscreen];
1062 p_vout->i_changes |= VOUT_SIZE_CHANGE;
1066 - (void)toggleFloatOnTop
1069 if( var_Get( p_vout, "video-on-top", &val )>=0 && val.b_bool)
1071 val.b_bool = VLC_FALSE;
1072 var_Set( p_vout, "video-on-top", val );
1073 [p_vout->p_sys->o_window setLevel: NSNormalWindowLevel];
1077 val.b_bool = VLC_TRUE;
1078 var_Set( p_vout, "video-on-top", val );
1079 [p_vout->p_sys->o_window setLevel: NSStatusWindowLevel];
1083 - (void)toggleFullscreen
1086 val.b_bool = !p_vout->b_fullscreen;
1087 var_Set( p_vout, "fullscreen", val );
1090 - (BOOL)isFullscreen
1092 return( p_vout->b_fullscreen );
1095 - (BOOL)canBecomeKeyWindow
1100 - (BOOL)performKeyEquivalent:(NSEvent *)o_event
1102 return [[VLCMain sharedInstance] hasDefinedShortcutKey:o_event];
1105 - (void)keyDown:(NSEvent *)o_event
1109 unsigned int i_pressed_modifiers = 0;
1112 i_pressed_modifiers = [o_event modifierFlags];
1114 if( i_pressed_modifiers & NSShiftKeyMask )
1115 val.i_int |= KEY_MODIFIER_SHIFT;
1116 if( i_pressed_modifiers & NSControlKeyMask )
1117 val.i_int |= KEY_MODIFIER_CTRL;
1118 if( i_pressed_modifiers & NSAlternateKeyMask )
1119 val.i_int |= KEY_MODIFIER_ALT;
1120 if( i_pressed_modifiers & NSCommandKeyMask )
1121 val.i_int |= KEY_MODIFIER_COMMAND;
1123 key = [[o_event charactersIgnoringModifiers] characterAtIndex: 0];
1127 /* Escape should always get you out of fullscreen */
1128 if( key == (unichar) 0x1b )
1130 if( [self isFullscreen] )
1132 [self toggleFullscreen];
1135 else if ( key == ' ' )
1137 playlist_t *p_playlist = vlc_object_find( p_vout, VLC_OBJECT_PLAYLIST,
1139 if ( p_playlist != NULL )
1141 playlist_Pause( p_playlist );
1142 vlc_object_release( p_playlist);
1147 val.i_int |= CocoaKeyToVLC( key );
1148 var_Set( p_vout->p_vlc, "key-pressed", val );
1153 [super keyDown: o_event];
1159 NSMutableString * o_title;
1160 playlist_t * p_playlist = vlc_object_find( p_vout, VLC_OBJECT_PLAYLIST,
1163 if( p_playlist == NULL )
1168 vlc_mutex_lock( &p_playlist->object_lock );
1169 o_title = [NSMutableString stringWithUTF8String:
1170 p_playlist->pp_items[p_playlist->i_index]->input.psz_uri];
1171 vlc_mutex_unlock( &p_playlist->object_lock );
1173 vlc_object_release( p_playlist );
1175 if( o_title != nil )
1177 NSRange prefix_range = [o_title rangeOfString: @"file:"];
1178 if( prefix_range.location != NSNotFound )
1180 [o_title deleteCharactersInRange: prefix_range];
1183 [self setTitleWithRepresentedFilename: o_title];
1188 [NSString stringWithCString: VOUT_TITLE " (QuickTime)"]];
1192 /* This is actually the same as VLCControls::stop. */
1193 - (BOOL)windowShouldClose:(id)sender
1195 playlist_t * p_playlist = vlc_object_find( p_vout, VLC_OBJECT_PLAYLIST,
1197 if( p_playlist == NULL )
1202 playlist_Stop( p_playlist );
1203 vlc_object_release( p_playlist );
1205 /* The window will be closed by the intf later. */
1211 /* Common QT and OpenGL code to catch mouse events */
1212 #define CATCH_MOUSE_EVENTS \
1213 - (BOOL)acceptsFirstResponder \
1218 - (BOOL)becomeFirstResponder \
1220 id o_window = [self window]; \
1222 [o_window setAcceptsMouseMovedEvents: YES]; \
1226 - (BOOL)resignFirstResponder \
1228 vout_thread_t * vout; \
1229 id o_window = [self window]; \
1230 vout = (vout_thread_t *)[o_window getVout]; \
1232 [o_window setAcceptsMouseMovedEvents: NO]; \
1233 VLCHideMouse( vout, NO ); \
1237 - (void)mouseDown:(NSEvent *)o_event \
1239 vout_thread_t * vout; \
1240 id o_window = [self window]; \
1241 vout = (vout_thread_t *)[o_window getVout]; \
1244 switch( [o_event type] ) \
1246 case NSLeftMouseDown: \
1248 var_Get( vout, "mouse-button-down", &val ); \
1250 var_Set( vout, "mouse-button-down", val ); \
1255 [super mouseDown: o_event]; \
1260 - (void)otherMouseDown:(NSEvent *)o_event \
1262 vout_thread_t * vout; \
1263 id o_window = [self window]; \
1264 vout = (vout_thread_t *)[o_window getVout]; \
1267 switch( [o_event type] ) \
1269 case NSOtherMouseDown: \
1271 var_Get( vout, "mouse-button-down", &val ); \
1273 var_Set( vout, "mouse-button-down", val ); \
1278 [super mouseDown: o_event]; \
1283 - (void)rightMouseDown:(NSEvent *)o_event \
1285 vout_thread_t * vout; \
1286 id o_window = [self window]; \
1287 vout = (vout_thread_t *)[o_window getVout]; \
1290 switch( [o_event type] ) \
1292 case NSRightMouseDown: \
1294 var_Get( vout, "mouse-button-down", &val ); \
1296 var_Set( vout, "mouse-button-down", val ); \
1301 [super mouseDown: o_event]; \
1306 - (void)mouseUp:(NSEvent *)o_event \
1308 vout_thread_t * vout; \
1309 id o_window = [self window]; \
1310 vout = (vout_thread_t *)[o_window getVout]; \
1313 switch( [o_event type] ) \
1315 case NSLeftMouseUp: \
1317 vlc_value_t b_val; \
1318 b_val.b_bool = VLC_TRUE; \
1319 var_Set( vout, "mouse-clicked", b_val ); \
1321 var_Get( vout, "mouse-button-down", &val ); \
1323 var_Set( vout, "mouse-button-down", val ); \
1328 [super mouseUp: o_event]; \
1333 - (void)otherMouseUp:(NSEvent *)o_event \
1335 vout_thread_t * vout; \
1336 id o_window = [self window]; \
1337 vout = (vout_thread_t *)[o_window getVout]; \
1340 switch( [o_event type] ) \
1342 case NSOtherMouseUp: \
1344 var_Get( vout, "mouse-button-down", &val ); \
1346 var_Set( vout, "mouse-button-down", val ); \
1351 [super mouseUp: o_event]; \
1356 - (void)rightMouseUp:(NSEvent *)o_event \
1358 vout_thread_t * vout; \
1359 id o_window = [self window]; \
1360 vout = (vout_thread_t *)[o_window getVout]; \
1363 switch( [o_event type] ) \
1365 case NSRightMouseUp: \
1367 var_Get( vout, "mouse-button-down", &val ); \
1369 var_Set( vout, "mouse-button-down", val ); \
1374 [super mouseUp: o_event]; \
1379 - (void)mouseDragged:(NSEvent *)o_event \
1381 [self mouseMoved:o_event]; \
1384 - (void)otherMouseDragged:(NSEvent *)o_event \
1386 [self mouseMoved:o_event]; \
1389 - (void)rightMouseDragged:(NSEvent *)o_event \
1391 [self mouseMoved:o_event]; \
1394 /*****************************************************************************
1395 * VLCQTView implementation
1396 *****************************************************************************/
1397 @implementation VLCQTView
1399 - (void)drawRect:(NSRect)rect
1401 vout_thread_t * p_vout;
1402 id o_window = [self window];
1403 p_vout = (vout_thread_t *)[o_window getVout];
1405 [[NSColor blackColor] set];
1407 [super drawRect: rect];
1409 p_vout->i_changes |= VOUT_SIZE_CHANGE;
1414 - (void)mouseMoved:(NSEvent *)o_event
1420 vout_thread_t * p_vout;
1421 id o_window = [self window];
1422 p_vout = (vout_thread_t *)[o_window getVout];
1424 s_rect = [self bounds];
1425 ml = [self convertPoint: [o_event locationInWindow] fromView: nil];
1426 b_inside = [self mouse: ml inRect: s_rect];
1431 int i_width, i_height, i_x, i_y;
1433 vout_PlacePicture( p_vout, (unsigned int)s_rect.size.width,
1434 (unsigned int)s_rect.size.height,
1435 &i_x, &i_y, &i_width, &i_height );
1437 val.i_int = ( ((int)ml.x) - i_x ) *
1438 p_vout->render.i_width / i_width;
1439 var_Set( p_vout, "mouse-x", val );
1441 val.i_int = ( ((int)ml.y) - i_y ) *
1442 p_vout->render.i_height / i_height;
1443 var_Set( p_vout, "mouse-y", val );
1445 val.b_bool = VLC_TRUE;
1446 var_Set( p_vout, "mouse-moved", val );
1447 p_vout->p_sys->i_time_mouse_last_moved = mdate();
1448 p_vout->p_sys->b_mouse_moved = YES;
1451 [super mouseMoved: o_event];
1456 /*****************************************************************************
1457 * VLCGLView implementation
1458 *****************************************************************************/
1459 @implementation VLCGLView
1462 - (id) initWithFrame: (NSRect) frame vout: (vout_thread_t*) _p_vout
1467 NSOpenGLPixelFormatAttribute attribs[] =
1469 NSOpenGLPFAAccelerated,
1470 NSOpenGLPFANoRecovery,
1471 NSOpenGLPFAColorSize, 24,
1472 NSOpenGLPFAAlphaSize, 8,
1473 NSOpenGLPFADepthSize, 24,
1478 NSOpenGLPixelFormat * fmt = [[NSOpenGLPixelFormat alloc]
1479 initWithAttributes: attribs];
1483 msg_Warn( p_vout, "Cannot create NSOpenGLPixelFormat" );
1487 self = [super initWithFrame:frame pixelFormat: fmt];
1490 [[self openGLContext] makeCurrentContext];
1491 [[self openGLContext] update];
1493 /* Black background */
1494 glClearColor( 0.0, 0.0, 0.0, 0.0 );
1496 /* Check if the user asked for useless visual effects */
1497 psz_effect = config_GetPsz( p_vout, "macosx-opengl-effect" );
1498 if( !psz_effect || !strcmp( psz_effect, "none" ))
1500 i_effect = OPENGL_EFFECT_NONE;
1502 else if( !strcmp( psz_effect, "cube" ) )
1504 i_effect = OPENGL_EFFECT_CUBE;
1506 glEnable( GL_DEPTH_TEST );
1508 else if( !strcmp( psz_effect, "transparent-cube" ) )
1510 i_effect = OPENGL_EFFECT_TRANSPARENT_CUBE;
1512 glDisable( GL_DEPTH_TEST );
1513 glEnable( GL_BLEND );
1514 glBlendFunc( GL_SRC_ALPHA, GL_ONE );
1518 msg_Warn( p_vout, "no valid opengl effect provided, using "
1520 i_effect = OPENGL_EFFECT_NONE;
1523 if( i_effect & ( OPENGL_EFFECT_CUBE |
1524 OPENGL_EFFECT_TRANSPARENT_CUBE ) )
1526 /* Set the perpective */
1527 glMatrixMode( GL_PROJECTION );
1529 glFrustum( -1.0, 1.0, -1.0, 1.0, 3.0, 20.0 );
1530 glMatrixMode( GL_MODELVIEW );
1532 glTranslatef( 0.0, 0.0, - 5.0 );
1549 [[self openGLContext] makeCurrentContext];
1551 NSRect bounds = [self bounds];
1552 glViewport( 0, 0, (GLint) bounds.size.width,
1553 (GLint) bounds.size.height );
1555 if( config_GetInt( p_vout, "macosx-stretch" ) )
1562 /* Quad size is set in order to preserve the aspect ratio */
1563 int fill = ( config_GetInt( p_vout, "macosx-fill" ) &&
1564 p_vout->b_fullscreen );
1565 int large = ( bounds.size.height * p_vout->output.i_aspect <
1566 bounds.size.width * VOUT_ASPECT_FACTOR );
1567 if( ( large && !fill ) || ( !large && fill ) )
1569 f_x = bounds.size.height * p_vout->output.i_aspect /
1570 VOUT_ASPECT_FACTOR / bounds.size.width;
1576 f_y = bounds.size.width * VOUT_ASPECT_FACTOR /
1577 p_vout->output.i_aspect / bounds.size.height;
1582 - (void) initTextures
1585 [[self openGLContext] makeCurrentContext];
1587 /* Free previous texture if any */
1590 glDeleteTextures( 2, pi_textures );
1593 glEnable( GL_TEXTURE_RECTANGLE_EXT );
1594 glEnable( GL_UNPACK_CLIENT_STORAGE_APPLE );
1596 glPixelStorei( GL_UNPACK_ALIGNMENT, 1 );
1597 glPixelStorei( GL_UNPACK_ROW_LENGTH, p_vout->output.i_width );
1599 /* Tell the driver not to make a copy of the texture but to use
1601 glPixelStorei( GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE );
1603 /* Create textures */
1604 glGenTextures( 2, pi_textures );
1606 for( i = 0; i < 2; i++ )
1608 glBindTexture( GL_TEXTURE_RECTANGLE_EXT, pi_textures[i] );
1609 glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE );
1611 /* Linear interpolation */
1612 glTexParameteri( GL_TEXTURE_RECTANGLE_EXT,
1613 GL_TEXTURE_MIN_FILTER, GL_LINEAR );
1614 glTexParameteri( GL_TEXTURE_RECTANGLE_EXT,
1615 GL_TEXTURE_MAG_FILTER, GL_LINEAR );
1617 /* Use VRAM texturing */
1618 glTexParameteri( GL_TEXTURE_RECTANGLE_EXT,
1619 GL_TEXTURE_STORAGE_HINT_APPLE, GL_STORAGE_CACHED_APPLE );
1621 glTexImage2D( GL_TEXTURE_RECTANGLE_EXT, 0, GL_RGB8,
1622 p_vout->output.i_width, p_vout->output.i_height, 0,
1623 GL_YCBCR_422_APPLE, GL_UNSIGNED_SHORT_8_8_APPLE,
1624 p_vout->p_sys->p_data[i] );
1627 /* Swap buffers only during the vertical retrace of the monitor.
1628 http://developer.apple.com/documentation/GraphicsImaging/
1629 Conceptual/OpenGL/chap5/chapter_5_section_44.html */
1630 long params[] = { 1 };
1631 CGLSetParameter( CGLGetCurrentContext(), kCGLCPSwapInterval,
1637 - (void)reloadTexture: (int) index
1644 [[self openGLContext] makeCurrentContext];
1646 glBindTexture( GL_TEXTURE_RECTANGLE_EXT, pi_textures[index] );
1647 glPixelStorei( GL_UNPACK_ROW_LENGTH, p_vout->output.i_width );
1649 /* glTexSubImage2D is faster than glTexImage2D
1650 http://developer.apple.com/samplecode/Sample_Code/Graphics_3D/
1651 TextureRange/MainOpenGLView.m.htm */
1652 glTexSubImage2D( GL_TEXTURE_RECTANGLE_EXT, 0, 0, 0,
1653 p_vout->output.i_width, p_vout->output.i_height,
1654 GL_YCBCR_422_APPLE, GL_UNSIGNED_SHORT_8_8_APPLE,
1655 p_vout->p_sys->p_data[index] );
1665 glBegin( GL_QUADS );
1667 glTexCoord2f( 0.0, 0.0 );
1668 glVertex2f( - f_x, f_y );
1670 glTexCoord2f( 0.0, (float) p_vout->output.i_height );
1671 glVertex2f( - f_x, - f_y );
1673 glTexCoord2f( (float) p_vout->output.i_width,
1674 (float) p_vout->output.i_height );
1675 glVertex2f( f_x, - f_y );
1677 glTexCoord2f( (float) p_vout->output.i_width, 0.0 );
1678 glVertex2f( f_x, f_y );
1684 glBegin( GL_QUADS );
1686 glTexCoord2f( 0.0, 0.0 );
1687 glVertex3f( - 1.0, 1.0, 1.0 );
1688 glTexCoord2f( 0.0, (float) p_vout->output.i_height );
1689 glVertex3f( - 1.0, - 1.0, 1.0 );
1690 glTexCoord2f( (float) p_vout->output.i_width,
1691 (float) p_vout->output.i_height );
1692 glVertex3f( 1.0, - 1.0, 1.0 );
1693 glTexCoord2f( (float) p_vout->output.i_width, 0.0 );
1694 glVertex3f( 1.0, 1.0, 1.0 );
1697 glTexCoord2f( 0.0, 0.0 );
1698 glVertex3f( - 1.0, 1.0, - 1.0 );
1699 glTexCoord2f( 0.0, (float) p_vout->output.i_height );
1700 glVertex3f( - 1.0, - 1.0, - 1.0 );
1701 glTexCoord2f( (float) p_vout->output.i_width,
1702 (float) p_vout->output.i_height );
1703 glVertex3f( - 1.0, - 1.0, 1.0 );
1704 glTexCoord2f( (float) p_vout->output.i_width, 0.0 );
1705 glVertex3f( - 1.0, 1.0, 1.0 );
1708 glTexCoord2f( 0.0, 0.0 );
1709 glVertex3f( 1.0, 1.0, - 1.0 );
1710 glTexCoord2f( 0.0, (float) p_vout->output.i_height );
1711 glVertex3f( 1.0, - 1.0, - 1.0 );
1712 glTexCoord2f( (float) p_vout->output.i_width,
1713 (float) p_vout->output.i_height );
1714 glVertex3f( - 1.0, - 1.0, - 1.0 );
1715 glTexCoord2f( (float) p_vout->output.i_width, 0.0 );
1716 glVertex3f( - 1.0, 1.0, - 1.0 );
1719 glTexCoord2f( 0.0, 0.0 );
1720 glVertex3f( 1.0, 1.0, 1.0 );
1721 glTexCoord2f( 0.0, (float) p_vout->output.i_height );
1722 glVertex3f( 1.0, - 1.0, 1.0 );
1723 glTexCoord2f( (float) p_vout->output.i_width,
1724 (float) p_vout->output.i_height );
1725 glVertex3f( 1.0, - 1.0, - 1.0 );
1726 glTexCoord2f( (float) p_vout->output.i_width, 0.0 );
1727 glVertex3f( 1.0, 1.0, - 1.0 );
1730 glTexCoord2f( 0.0, 0.0 );
1731 glVertex3f( - 1.0, 1.0, - 1.0 );
1732 glTexCoord2f( 0.0, (float) p_vout->output.i_height );
1733 glVertex3f( - 1.0, 1.0, 1.0 );
1734 glTexCoord2f( (float) p_vout->output.i_width,
1735 (float) p_vout->output.i_height );
1736 glVertex3f( 1.0, 1.0, 1.0 );
1737 glTexCoord2f( (float) p_vout->output.i_width, 0.0 );
1738 glVertex3f( 1.0, 1.0, - 1.0 );
1741 glTexCoord2f( 0.0, 0.0 );
1742 glVertex3f( - 1.0, - 1.0, 1.0 );
1743 glTexCoord2f( 0.0, (float) p_vout->output.i_height );
1744 glVertex3f( - 1.0, - 1.0, - 1.0 );
1745 glTexCoord2f( (float) p_vout->output.i_width,
1746 (float) p_vout->output.i_height );
1747 glVertex3f( 1.0, - 1.0, - 1.0 );
1748 glTexCoord2f( (float) p_vout->output.i_width, 0.0 );
1749 glVertex3f( 1.0, - 1.0, 1.0 );
1753 - (void) drawRect: (NSRect) rect
1755 [[self openGLContext] makeCurrentContext];
1757 /* Black background */
1758 glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
1767 glBindTexture( GL_TEXTURE_RECTANGLE_EXT,
1768 pi_textures[p_vout->p_sys->i_cur_pic] );
1769 if( i_effect & ( OPENGL_EFFECT_CUBE |
1770 OPENGL_EFFECT_TRANSPARENT_CUBE ) )
1772 glRotatef( 1.0, 0.3, 0.5, 0.7 );
1786 - (void)mouseMoved:(NSEvent *)o_event
1792 s_rect = [self bounds];
1793 ml = [self convertPoint: [o_event locationInWindow] fromView: nil];
1794 b_inside = [self mouse: ml inRect: s_rect];
1799 int i_width, i_height, i_x, i_y;
1801 vout_PlacePicture( p_vout, (unsigned int)s_rect.size.width,
1802 (unsigned int)s_rect.size.height,
1803 &i_x, &i_y, &i_width, &i_height );
1805 val.i_int = ( (int)ml.x - i_x ) *
1806 p_vout->render.i_width / i_width;
1807 var_Set( p_vout, "mouse-x", val );
1809 /* Y coordinate is inverted in OpenGL */
1810 val.i_int = ( ((int)(s_rect.size.height - ml.y)) - i_y ) *
1811 p_vout->render.i_height / i_height;
1812 var_Set( p_vout, "mouse-y", val );
1814 val.b_bool = VLC_TRUE;
1815 var_Set( p_vout, "mouse-moved", val );
1816 p_vout->p_sys->i_time_mouse_last_moved = mdate();
1817 p_vout->p_sys->b_mouse_moved = YES;
1820 [super mouseMoved: o_event];