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 CoSendRequest ( vout_thread_t *, SEL );
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 if( [NSApp respondsToSelector: @selector(getIntf)] )
124 intf_thread_t * p_intf;
126 for( i_timeout = 10 ; i_timeout-- ; )
128 if( ( p_intf = [NSApp getIntf] ) == NULL )
130 msleep( INTF_IDLE_SLEEP );
136 msg_Err( p_vout, "MacOS X intf has getIntf, but is NULL" );
137 free( p_vout->p_sys );
142 p_vout->p_sys->b_mouse_moved = VLC_TRUE;
143 p_vout->p_sys->i_time_mouse_last_moved = mdate();
145 /* set window size */
146 p_vout->p_sys->s_rect.size.width = p_vout->i_window_width;
147 p_vout->p_sys->s_rect.size.height = p_vout->i_window_height;
149 /* Check if we should use QuickTime or OpenGL */
150 psz_vout_type = config_GetPsz( p_vout, "macosx-vout" );
152 if( !strncmp( psz_vout_type, "auto", 4 ) )
154 p_vout->p_sys->i_opengl = CGDisplayUsesOpenGLAcceleration( kCGDirectMainDisplay );
156 else if( !strncmp( psz_vout_type, "opengl", 6 ) )
158 p_vout->p_sys->i_opengl = VLC_TRUE;
162 p_vout->p_sys->i_opengl = VLC_FALSE;
164 free( psz_vout_type );
166 if( !p_vout->p_sys->i_opengl )
168 /* Initialize QuickTime */
169 p_vout->p_sys->h_img_descr =
170 (ImageDescriptionHandle)NewHandleClear( sizeof(ImageDescription) );
171 p_vout->p_sys->p_matrix =
172 (MatrixRecordPtr)malloc( sizeof(MatrixRecord) );
173 p_vout->p_sys->p_fullscreen_state = NULL;
175 if( ( err = EnterMovies() ) != noErr )
177 msg_Err( p_vout, "EnterMovies failed: %d", err );
178 free( p_vout->p_sys->p_matrix );
179 DisposeHandle( (Handle)p_vout->p_sys->h_img_descr );
180 free( p_vout->p_sys );
184 /* Damn QT isn't thread safe. so keep a lock in the p_vlc object */
185 vlc_mutex_lock( &p_vout->p_vlc->quicktime_lock );
187 err = FindCodec( kYUV420CodecType, bestSpeedCodec,
188 nil, &p_vout->p_sys->img_dc );
190 vlc_mutex_unlock( &p_vout->p_vlc->quicktime_lock );
191 if( err == noErr && p_vout->p_sys->img_dc != 0 )
193 p_vout->output.i_chroma = VLC_FOURCC('I','4','2','0');
194 p_vout->p_sys->i_codec = kYUV420CodecType;
198 msg_Err( p_vout, "failed to find an appropriate codec" );
201 if( p_vout->p_sys->img_dc == 0 )
203 free( p_vout->p_sys->p_matrix );
204 DisposeHandle( (Handle)p_vout->p_sys->h_img_descr );
205 free( p_vout->p_sys );
208 msg_Dbg( p_vout, "using Quartz mode" );
212 msg_Dbg( p_vout, "using OpenGL mode" );
215 NSAutoreleasePool * o_pool = [[NSAutoreleasePool alloc] init];
216 NSArray * o_screens = [NSScreen screens];
217 if( [o_screens count] > 0 && var_Type( p_vout, "video-device" ) == 0 )
220 vlc_value_t val, text;
223 int i_option = config_GetInt( p_vout, "macosx-vdev" );
225 var_Create( p_vout, "video-device", VLC_VAR_INTEGER |
227 text.psz_string = _("Video device");
228 var_Change( p_vout, "video-device", VLC_VAR_SETTEXT, &text, NULL );
230 NSEnumerator * o_enumerator = [o_screens objectEnumerator];
232 while( (o_screen = [o_enumerator nextObject]) != NULL )
235 NSRect s_rect = [o_screen frame];
237 snprintf( psz_temp, sizeof(psz_temp)/sizeof(psz_temp[0])-1,
238 "%s %d (%dx%d)", _("Screen"), i,
239 (int)s_rect.size.width, (int)s_rect.size.height );
241 text.psz_string = psz_temp;
243 var_Change( p_vout, "video-device",
244 VLC_VAR_ADDCHOICE, &val, &text );
246 if( ( i - 1 ) == i_option )
248 var_Set( p_vout, "video-device", val );
253 var_AddCallback( p_vout, "video-device", vout_VarCallback,
256 val.b_bool = VLC_TRUE;
257 var_Set( p_vout, "intf-change", val );
261 if( CoCreateWindow( p_vout ) )
263 msg_Err( p_vout, "unable to create window" );
264 if( !p_vout->p_sys->i_opengl )
266 free( p_vout->p_sys->p_matrix );
267 DisposeHandle( (Handle)p_vout->p_sys->h_img_descr );
269 free( p_vout->p_sys );
273 p_vout->pf_init = vout_Init;
274 p_vout->pf_end = vout_End;
275 p_vout->pf_manage = vout_Manage;
276 p_vout->pf_render = NULL;
277 p_vout->pf_display = vout_Display;
282 /*****************************************************************************
283 * vout_Init: initialize video thread output method
284 *****************************************************************************/
285 static int vout_InitOpenGL( vout_thread_t *p_vout );
286 static int vout_InitQuickTime( vout_thread_t *p_vout );
287 static int vout_Init( vout_thread_t *p_vout )
289 I_OUTPUTPICTURES = 0;
291 /* Initialize the output structure; we already found a codec,
292 * and the corresponding chroma we will be using. Since we can
293 * arbitrary scale, stick to the coordinates and aspect. */
294 p_vout->output.i_width = p_vout->render.i_width;
295 p_vout->output.i_height = p_vout->render.i_height;
296 p_vout->output.i_aspect = p_vout->render.i_aspect;
298 if( p_vout->p_sys->i_opengl )
300 return vout_InitOpenGL( p_vout );
304 return vout_InitQuickTime( p_vout );
308 static int vout_InitOpenGL( vout_thread_t *p_vout )
314 /* Apple OpenGL extensions only accept YUV as YUY2 */
315 p_vout->output.i_chroma = VLC_FOURCC('Y','U','Y','2');
316 p_vout->output.i_rmask = 0xFF0000;
317 p_vout->output.i_gmask = 0x00FF00;
318 p_vout->output.i_bmask = 0x0000FF;
320 /* Allocate our 2 picture buffers */
321 i_bytes = 2 * p_vout->output.i_width * p_vout->output.i_height;
322 p_vout->p_sys->p_data[0] = vlc_memalign(
323 &p_vout->p_sys->p_data_orig[0], 16, i_bytes );
324 p_vout->p_sys->p_data[1] = vlc_memalign(
325 &p_vout->p_sys->p_data_orig[1], 16, i_bytes );
326 p_vout->p_sys->i_cur_pic = 1;
328 /* We declare only one picture and will switch buffers
331 while( I_OUTPUTPICTURES < 1 )
333 /* Find an empty picture slot */
334 for( i_index = 0; i_index < VOUT_MAX_PICTURES; i_index++ )
336 if( p_vout->p_picture[ i_index ].i_status == FREE_PICTURE )
338 p_pic = p_vout->p_picture + i_index;
347 vout_InitPicture( VLC_OBJECT( p_vout ), p_pic,
348 p_vout->output.i_chroma, p_vout->output.i_width,
349 p_vout->output.i_height, p_vout->output.i_aspect );
350 p_pic->p_data = p_vout->p_sys->p_data[0];
351 p_pic->p[0].p_pixels = p_pic->p_data;
352 for( i_index = 1; i_index < p_pic->i_planes; i_index++ )
354 p_pic->p[i_index].p_pixels =
355 p_pic->p[i_index-1].p_pixels +
356 p_pic->p[i_index-1].i_lines *
357 p_pic->p[i_index-1].i_pitch;
360 p_pic->i_status = DESTROYED_PICTURE;
361 p_pic->i_type = DIRECT_PICTURE;
363 PP_OUTPUTPICTURE[ I_OUTPUTPICTURES ] = p_pic;
367 [p_vout->p_sys->o_glview lockFocus];
368 [p_vout->p_sys->o_glview initTextures];
369 [p_vout->p_sys->o_glview reshape];
370 [p_vout->p_sys->o_glview unlockFocus];
375 static int vout_InitQuickTime( vout_thread_t *p_vout )
380 SetPort( p_vout->p_sys->p_qdport );
381 QTScaleMatrix( p_vout );
383 if( QTCreateSequence( p_vout ) )
385 msg_Err( p_vout, "unable to create sequence" );
389 /* Try to initialize up to QT_MAX_DIRECTBUFFERS direct buffers */
390 while( I_OUTPUTPICTURES < QT_MAX_DIRECTBUFFERS )
394 /* Find an empty picture slot */
395 for( i_index = 0; i_index < VOUT_MAX_PICTURES; i_index++ )
397 if( p_vout->p_picture[ i_index ].i_status == FREE_PICTURE )
399 p_pic = p_vout->p_picture + i_index;
408 /* Allocate the picture */
409 if( QTNewPicture( p_vout, p_pic ) )
414 p_pic->i_status = DESTROYED_PICTURE;
415 p_pic->i_type = DIRECT_PICTURE;
417 PP_OUTPUTPICTURE[ I_OUTPUTPICTURES ] = p_pic;
423 /*****************************************************************************
424 * vout_End: terminate video thread output method
425 *****************************************************************************/
426 static void vout_End( vout_thread_t *p_vout )
430 if( !p_vout->p_sys->i_opengl )
432 QTDestroySequence( p_vout );
436 [p_vout->p_sys->o_glview cleanUp];
439 /* Free the direct buffers we allocated */
440 for( i_index = I_OUTPUTPICTURES; i_index; )
443 if( !p_vout->p_sys->i_opengl )
445 QTFreePicture( p_vout, PP_OUTPUTPICTURE[ i_index ] );
449 free( p_vout->p_sys->p_data_orig[0] );
450 free( p_vout->p_sys->p_data_orig[1] );
455 /*****************************************************************************
456 * CloseVideo: destroy video thread output method
457 *****************************************************************************/
458 void E_(CloseVideo) ( vlc_object_t *p_this )
460 vout_thread_t * p_vout = (vout_thread_t *)p_this;
462 if( CoDestroyWindow( p_vout ) )
464 msg_Err( p_vout, "unable to destroy window" );
467 if( !p_vout->p_sys->i_opengl )
469 if ( p_vout->p_sys->p_fullscreen_state != NULL )
470 EndFullScreen ( p_vout->p_sys->p_fullscreen_state, NULL );
474 free( p_vout->p_sys->p_matrix );
475 DisposeHandle( (Handle)p_vout->p_sys->h_img_descr );
478 free( p_vout->p_sys );
481 /*****************************************************************************
482 * vout_Manage: handle events
483 *****************************************************************************
484 * This function should be called regularly by video output thread. It manages
485 * console events. It returns a non null value on error.
486 *****************************************************************************/
487 static int vout_Manage( vout_thread_t *p_vout )
489 if( p_vout->i_changes & VOUT_FULLSCREEN_CHANGE )
491 if( CoToggleFullscreen( p_vout ) )
496 p_vout->i_changes &= ~VOUT_FULLSCREEN_CHANGE;
499 if( p_vout->i_changes & VOUT_SIZE_CHANGE )
501 if( !p_vout->p_sys->i_opengl )
503 QTScaleMatrix( p_vout );
504 SetDSequenceMatrix( p_vout->p_sys->i_seq,
505 p_vout->p_sys->p_matrix );
508 p_vout->i_changes &= ~VOUT_SIZE_CHANGE;
511 /* hide/show mouse cursor
512 * this code looks unnecessarily complicated, but is necessary like this.
513 * it has to deal with multiple monitors and therefore checks a lot */
514 if( !p_vout->p_sys->b_mouse_moved && p_vout->b_fullscreen )
516 if( mdate() - p_vout->p_sys->i_time_mouse_last_moved > 3000000 )
518 VLCHideMouse( p_vout, YES );
521 else if ( p_vout->p_sys->b_mouse_moved && p_vout->b_fullscreen )
523 VLCHideMouse( p_vout, NO );
526 /* disable screen saver */
527 UpdateSystemActivity( UsrActivity );
532 /*****************************************************************************
533 * vout_Display: displays previously rendered output
534 *****************************************************************************
535 * This function sends the currently rendered image to the display.
536 *****************************************************************************/
537 static void vout_Display( vout_thread_t *p_vout, picture_t *p_pic )
539 if( !p_vout->p_sys->i_opengl )
544 if( ( err = DecompressSequenceFrameS(
545 p_vout->p_sys->i_seq,
546 p_pic->p_sys->p_info,
547 p_pic->p_sys->i_size,
548 codecFlagUseImageBuffer, &flags, nil ) != noErr ) )
550 msg_Warn( p_vout, "DecompressSequenceFrameS failed: %d", err );
554 QDFlushPortBuffer( p_vout->p_sys->p_qdport, nil );
559 if( [p_vout->p_sys->o_glview lockFocusIfCanDraw] )
562 int i_old = p_vout->p_sys->i_cur_pic;
563 int i_new = ( i_old + 1 ) % 2;
565 /* Draw the new picture */
566 p_vout->p_sys->i_cur_pic = i_new;
567 [p_vout->p_sys->o_glview drawRect:
568 [p_vout->p_sys->o_glview bounds]];
570 /* Reload the other texture. Textures have to be reloaded
571 before the buffer is filled (thanks to gcc from
572 arstechnica forums) */
573 [p_vout->p_sys->o_glview reloadTexture: i_old];
576 p_pic->p_data = p_vout->p_sys->p_data[i_old];
577 p_pic->p[0].p_pixels = p_pic->p_data;
578 for( i_index = 1; i_index < p_pic->i_planes; i_index++ )
580 p_pic->p[i_index].p_pixels =
581 p_pic->p[i_index-1].p_pixels +
582 p_pic->p[i_index-1].i_lines *
583 p_pic->p[i_index-1].i_pitch;
585 [p_vout->p_sys->o_glview unlockFocus];
590 /*****************************************************************************
591 * CoSendRequest: send request to interface thread
592 *****************************************************************************
593 * Returns 0 on success, 1 otherwise
594 *****************************************************************************/
595 static int CoSendRequest( vout_thread_t *p_vout, SEL sel )
599 intf_thread_t * p_intf;
601 VLCVout * o_vlv = [[VLCVout alloc] init];
603 if( ( i_ret = ExecuteOnMainThread( o_vlv, sel, (void *)p_vout ) ) )
605 msg_Err( p_vout, "SendRequest: no way to communicate with mt" );
610 /*This makes this function dependant of the presence of a macosx
611 interface. We do not check if this interface exists, since it has
612 already been done before.*/
614 p_intf = [NSApp getIntf];
616 val.b_bool = VLC_TRUE;
617 var_Create(p_intf,"intf-change",VLC_VAR_BOOL);
618 var_Set(p_intf, "intf-change",val);
623 /*****************************************************************************
624 * CoCreateWindow: create new window
625 *****************************************************************************
626 * Returns 0 on success, 1 otherwise
627 *****************************************************************************/
628 static int CoCreateWindow( vout_thread_t *p_vout )
630 if( CoSendRequest( p_vout, @selector(createWindow:) ) )
632 msg_Err( p_vout, "CoSendRequest (createWindow) failed" );
639 /*****************************************************************************
640 * CoDestroyWindow: destroy window
641 *****************************************************************************
642 * Returns 0 on success, 1 otherwise
643 *****************************************************************************/
644 static int CoDestroyWindow( vout_thread_t *p_vout )
647 VLCHideMouse( p_vout, NO );
649 if( CoSendRequest( p_vout, @selector(destroyWindow:) ) )
651 msg_Err( p_vout, "CoSendRequest (destroyWindow) failed" );
658 /*****************************************************************************
659 * CoToggleFullscreen: toggle fullscreen
660 *****************************************************************************
661 * Returns 0 on success, 1 otherwise
662 *****************************************************************************/
663 static int CoToggleFullscreen( vout_thread_t *p_vout )
667 intf_thread_t * p_intf;
669 if( p_vout->p_sys->i_opengl )
671 p_vout->b_fullscreen = !p_vout->b_fullscreen;
672 if( p_vout->b_fullscreen )
674 [p_vout->p_sys->o_glview goFullScreen];
678 [p_vout->p_sys->o_glview exitFullScreen];
680 /*This makes this function dependant of the presence of a macosx
681 interface. We do not check if this interface exists, since it has
682 already been done before.*/
684 p_intf = [NSApp getIntf];
686 val.b_bool = VLC_TRUE;
687 var_Create(p_intf,"intf-change",VLC_VAR_BOOL);
688 var_Set(p_intf, "intf-change",val);
693 QTDestroySequence( p_vout );
695 if( CoDestroyWindow( p_vout ) )
697 msg_Err( p_vout, "unable to destroy window" );
701 p_vout->b_fullscreen = !p_vout->b_fullscreen;
703 if( CoCreateWindow( p_vout ) )
705 msg_Err( p_vout, "unable to create window" );
709 SetPort( p_vout->p_sys->p_qdport );
710 QTScaleMatrix( p_vout );
712 if( QTCreateSequence( p_vout ) )
714 msg_Err( p_vout, "unable to create sequence" );
721 /*****************************************************************************
722 * VLCHideMouse: if b_hide then hide the cursor
723 *****************************************************************************/
724 static void VLCHideMouse ( vout_thread_t *p_vout, BOOL b_hide )
729 NSWindow *o_window = p_vout->p_sys->o_window;
730 NSView *o_contents = [o_window contentView];
732 s_rect = [o_contents bounds];
733 ml = [o_window convertScreenToBase:[NSEvent mouseLocation]];
734 ml = [o_contents convertPoint:ml fromView:nil];
735 b_inside = [o_contents mouse: ml inRect: s_rect];
737 if ( b_hide && b_inside )
739 /* only hide if mouse over VLCQTView */
740 [NSCursor setHiddenUntilMouseMoves: YES];
744 [NSCursor setHiddenUntilMouseMoves: NO];
746 p_vout->p_sys->b_mouse_moved = NO;
747 p_vout->p_sys->i_time_mouse_last_moved = mdate();
751 /*****************************************************************************
752 * QTScaleMatrix: scale matrix
753 *****************************************************************************/
754 static void QTScaleMatrix( vout_thread_t *p_vout )
757 unsigned int i_width, i_height;
758 Fixed factor_x, factor_y;
759 unsigned int i_offset_x = 0;
760 unsigned int i_offset_y = 0;
762 GetPortBounds( p_vout->p_sys->p_qdport, &s_rect );
764 i_width = s_rect.right - s_rect.left;
765 i_height = s_rect.bottom - s_rect.top;
767 if( config_GetInt( p_vout, "macosx-stretch" ) )
769 factor_x = FixDiv( Long2Fix( i_width ),
770 Long2Fix( p_vout->output.i_width ) );
771 factor_y = FixDiv( Long2Fix( i_height ),
772 Long2Fix( p_vout->output.i_height ) );
775 else if( i_height * p_vout->output.i_aspect < i_width * VOUT_ASPECT_FACTOR )
777 int i_adj_width = i_height * p_vout->output.i_aspect /
780 factor_x = FixDiv( Long2Fix( i_adj_width ),
781 Long2Fix( p_vout->output.i_width ) );
782 factor_y = FixDiv( Long2Fix( i_height ),
783 Long2Fix( p_vout->output.i_height ) );
785 i_offset_x = (i_width - i_adj_width) / 2;
789 int i_adj_height = i_width * VOUT_ASPECT_FACTOR /
790 p_vout->output.i_aspect;
792 factor_x = FixDiv( Long2Fix( i_width ),
793 Long2Fix( p_vout->output.i_width ) );
794 factor_y = FixDiv( Long2Fix( i_adj_height ),
795 Long2Fix( p_vout->output.i_height ) );
797 i_offset_y = (i_height - i_adj_height) / 2;
800 SetIdentityMatrix( p_vout->p_sys->p_matrix );
802 ScaleMatrix( p_vout->p_sys->p_matrix,
804 Long2Fix(0), Long2Fix(0) );
806 TranslateMatrix( p_vout->p_sys->p_matrix,
807 Long2Fix(i_offset_x), Long2Fix(i_offset_y) );
810 /*****************************************************************************
811 * QTCreateSequence: create a new sequence
812 *****************************************************************************
813 * Returns 0 on success, 1 otherwise
814 *****************************************************************************/
815 static int QTCreateSequence( vout_thread_t *p_vout )
818 ImageDescriptionPtr p_descr;
820 HLock( (Handle)p_vout->p_sys->h_img_descr );
821 p_descr = *p_vout->p_sys->h_img_descr;
823 p_descr->idSize = sizeof(ImageDescription);
824 p_descr->cType = p_vout->p_sys->i_codec;
825 p_descr->version = 1;
826 p_descr->revisionLevel = 0;
827 p_descr->vendor = 'appl';
828 p_descr->width = p_vout->output.i_width;
829 p_descr->height = p_vout->output.i_height;
830 p_descr->hRes = Long2Fix(72);
831 p_descr->vRes = Long2Fix(72);
832 p_descr->spatialQuality = codecLosslessQuality;
833 p_descr->frameCount = 1;
834 p_descr->clutID = -1;
835 p_descr->dataSize = 0;
838 HUnlock( (Handle)p_vout->p_sys->h_img_descr );
840 if( ( err = DecompressSequenceBeginS(
841 &p_vout->p_sys->i_seq,
842 p_vout->p_sys->h_img_descr,
844 p_vout->p_sys->p_qdport,
846 p_vout->p_sys->p_matrix,
848 codecFlagUseImageBuffer,
849 codecLosslessQuality,
850 p_vout->p_sys->img_dc ) ) )
852 msg_Err( p_vout, "DecompressSequenceBeginS failed: %d", err );
859 /*****************************************************************************
860 * QTDestroySequence: destroy sequence
861 *****************************************************************************/
862 static void QTDestroySequence( vout_thread_t *p_vout )
864 CDSequenceEnd( p_vout->p_sys->i_seq );
867 /*****************************************************************************
868 * QTNewPicture: allocate a picture
869 *****************************************************************************
870 * Returns 0 on success, 1 otherwise
871 *****************************************************************************/
872 static int QTNewPicture( vout_thread_t *p_vout, picture_t *p_pic )
874 int i_width = p_vout->output.i_width;
875 int i_height = p_vout->output.i_height;
877 /* We know the chroma, allocate a buffer which will be used
878 * directly by the decoder */
879 p_pic->p_sys = malloc( sizeof( picture_sys_t ) );
881 if( p_pic->p_sys == NULL )
886 switch( p_vout->output.i_chroma )
888 case VLC_FOURCC('I','4','2','0'):
890 p_pic->p_sys->p_info = (void *)&p_pic->p_sys->pixmap_i420;
891 p_pic->p_sys->i_size = sizeof(PlanarPixmapInfoYUV420);
893 /* Allocate the memory buffer */
894 p_pic->p_data = vlc_memalign( &p_pic->p_data_orig,
895 16, i_width * i_height * 3 / 2 );
898 p_pic->Y_PIXELS = p_pic->p_data;
899 p_pic->p[Y_PLANE].i_lines = i_height;
900 p_pic->p[Y_PLANE].i_pitch = i_width;
901 p_pic->p[Y_PLANE].i_pixel_pitch = 1;
902 p_pic->p[Y_PLANE].i_visible_pitch = i_width;
905 p_pic->U_PIXELS = p_pic->Y_PIXELS + i_height * i_width;
906 p_pic->p[U_PLANE].i_lines = i_height / 2;
907 p_pic->p[U_PLANE].i_pitch = i_width / 2;
908 p_pic->p[U_PLANE].i_pixel_pitch = 1;
909 p_pic->p[U_PLANE].i_visible_pitch = i_width / 2;
912 p_pic->V_PIXELS = p_pic->U_PIXELS + i_height * i_width / 4;
913 p_pic->p[V_PLANE].i_lines = i_height / 2;
914 p_pic->p[V_PLANE].i_pitch = i_width / 2;
915 p_pic->p[V_PLANE].i_pixel_pitch = 1;
916 p_pic->p[V_PLANE].i_visible_pitch = i_width / 2;
918 /* We allocated 3 planes */
921 #define P p_pic->p_sys->pixmap_i420
922 P.componentInfoY.offset = (void *)p_pic->Y_PIXELS
923 - p_pic->p_sys->p_info;
924 P.componentInfoCb.offset = (void *)p_pic->U_PIXELS
925 - p_pic->p_sys->p_info;
926 P.componentInfoCr.offset = (void *)p_pic->V_PIXELS
927 - p_pic->p_sys->p_info;
929 P.componentInfoY.rowBytes = i_width;
930 P.componentInfoCb.rowBytes = i_width / 2;
931 P.componentInfoCr.rowBytes = i_width / 2;
937 /* Unknown chroma, tell the guy to get lost */
938 free( p_pic->p_sys );
939 msg_Err( p_vout, "never heard of chroma 0x%.8x (%4.4s)",
940 p_vout->output.i_chroma, (char*)&p_vout->output.i_chroma );
948 /*****************************************************************************
949 * QTFreePicture: destroy a picture allocated with QTNewPicture
950 *****************************************************************************/
951 static void QTFreePicture( vout_thread_t *p_vout, picture_t *p_pic )
953 switch( p_vout->output.i_chroma )
955 case VLC_FOURCC('I','4','2','0'):
956 free( p_pic->p_data_orig );
960 free( p_pic->p_sys );
963 /*****************************************************************************
964 * VLCWindow implementation
965 *****************************************************************************/
966 @implementation VLCWindow
968 - (void)setVout:(vout_thread_t *)_p_vout
973 - (vout_thread_t *)getVout
978 - (void)scaleWindowWithFactor: (float)factor
981 int i_corrected_height, i_corrected_width;
983 NSPoint topleftscreen;
985 if ( !p_vout->b_fullscreen )
988 topleftbase.y = [self frame].size.height;
989 topleftscreen = [self convertBaseToScreen: topleftbase];
991 if( p_vout->output.i_height * p_vout->output.i_aspect >
992 p_vout->output.i_width * VOUT_ASPECT_FACTOR )
994 i_corrected_width = p_vout->output.i_height * p_vout->output.i_aspect /
996 newsize.width = (int) ( i_corrected_width * factor );
997 newsize.height = (int) ( p_vout->render.i_height * factor );
1001 i_corrected_height = p_vout->output.i_width * VOUT_ASPECT_FACTOR /
1002 p_vout->output.i_aspect;
1003 newsize.width = (int) ( p_vout->render.i_width * factor );
1004 newsize.height = (int) ( i_corrected_height * factor );
1007 [self setContentSize: newsize];
1009 [self setFrameTopLeftPoint: topleftscreen];
1010 p_vout->i_changes |= VOUT_SIZE_CHANGE;
1014 - (void)toggleFloatOnTop
1017 if( var_Get( p_vout, "video-on-top", &val )>=0 && val.b_bool)
1019 val.b_bool = VLC_FALSE;
1020 var_Set( p_vout, "video-on-top", val );
1021 [p_vout->p_sys->o_window setLevel: NSNormalWindowLevel];
1025 val.b_bool = VLC_TRUE;
1026 var_Set( p_vout, "video-on-top", val );
1027 [p_vout->p_sys->o_window setLevel: NSStatusWindowLevel];
1031 - (void)toggleFullscreen
1034 val.b_bool = !p_vout->b_fullscreen;
1035 var_Set( p_vout, "fullscreen", val );
1038 - (BOOL)isFullscreen
1040 return( p_vout->b_fullscreen );
1043 - (BOOL)canBecomeKeyWindow
1048 - (BOOL)performKeyEquivalent:(NSEvent *)o_event
1050 return [(VLCApplication *) [VLCApplication sharedApplication]
1051 hasDefinedShortcutKey:o_event];
1054 - (void)keyDown:(NSEvent *)o_event
1058 unsigned int i_pressed_modifiers = 0;
1061 i_pressed_modifiers = [o_event modifierFlags];
1063 if( i_pressed_modifiers & NSShiftKeyMask )
1064 val.i_int |= KEY_MODIFIER_SHIFT;
1065 if( i_pressed_modifiers & NSControlKeyMask )
1066 val.i_int |= KEY_MODIFIER_CTRL;
1067 if( i_pressed_modifiers & NSAlternateKeyMask )
1068 val.i_int |= KEY_MODIFIER_ALT;
1069 if( i_pressed_modifiers & NSCommandKeyMask )
1070 val.i_int |= KEY_MODIFIER_COMMAND;
1072 key = [[o_event charactersIgnoringModifiers] characterAtIndex: 0];
1076 /* Escape should always get you out of fullscreen */
1077 if( key == (unichar) 0x1b )
1079 if( [self isFullscreen] )
1081 [self toggleFullscreen];
1084 else if ( key == ' ' )
1086 playlist_t *p_playlist = vlc_object_find( p_vout, VLC_OBJECT_PLAYLIST,
1088 if ( p_playlist != NULL )
1090 playlist_Pause( p_playlist );
1091 vlc_object_release( p_playlist);
1096 val.i_int |= CocoaKeyToVLC( key );
1097 var_Set( p_vout->p_vlc, "key-pressed", val );
1102 [super keyDown: o_event];
1108 NSMutableString * o_title;
1109 playlist_t * p_playlist = vlc_object_find( p_vout, VLC_OBJECT_PLAYLIST,
1112 if( p_playlist == NULL )
1117 vlc_mutex_lock( &p_playlist->object_lock );
1118 o_title = [NSMutableString stringWithUTF8String:
1119 p_playlist->pp_items[p_playlist->i_index]->input.psz_uri];
1120 vlc_mutex_unlock( &p_playlist->object_lock );
1122 vlc_object_release( p_playlist );
1124 if( o_title != nil )
1126 NSRange prefix_range = [o_title rangeOfString: @"file:"];
1127 if( prefix_range.location != NSNotFound )
1129 [o_title deleteCharactersInRange: prefix_range];
1132 [self setTitleWithRepresentedFilename: o_title];
1137 [NSString stringWithCString: VOUT_TITLE " (QuickTime)"]];
1141 /* This is actually the same as VLCControls::stop. */
1142 - (BOOL)windowShouldClose:(id)sender
1144 playlist_t * p_playlist = vlc_object_find( p_vout, VLC_OBJECT_PLAYLIST,
1146 if( p_playlist == NULL )
1151 playlist_Stop( p_playlist );
1152 vlc_object_release( p_playlist );
1154 /* The window will be closed by the intf later. */
1160 /*****************************************************************************
1161 * VLCQTView implementation
1162 *****************************************************************************/
1163 @implementation VLCQTView
1165 - (void)drawRect:(NSRect)rect
1167 vout_thread_t * p_vout;
1168 id o_window = [self window];
1169 p_vout = (vout_thread_t *)[o_window getVout];
1171 [[NSColor blackColor] set];
1173 [super drawRect: rect];
1175 p_vout->i_changes |= VOUT_SIZE_CHANGE;
1178 - (BOOL)acceptsFirstResponder
1183 - (BOOL)becomeFirstResponder
1185 vout_thread_t * p_vout;
1186 id o_window = [self window];
1187 p_vout = (vout_thread_t *)[o_window getVout];
1189 [o_window setAcceptsMouseMovedEvents: YES];
1193 - (BOOL)resignFirstResponder
1195 vout_thread_t * p_vout;
1196 id o_window = [self window];
1197 p_vout = (vout_thread_t *)[o_window getVout];
1199 [o_window setAcceptsMouseMovedEvents: NO];
1200 VLCHideMouse( p_vout, NO );
1204 - (void)mouseDown:(NSEvent *)o_event
1206 vout_thread_t * p_vout;
1207 id o_window = [self window];
1208 p_vout = (vout_thread_t *)[o_window getVout];
1211 switch( [o_event type] )
1213 case NSLeftMouseDown:
1215 var_Get( p_vout, "mouse-button-down", &val );
1217 var_Set( p_vout, "mouse-button-down", val );
1222 [super mouseDown: o_event];
1227 - (void)otherMouseDown:(NSEvent *)o_event
1229 /* This is not the the wheel button. you need to poll the
1230 * mouseWheel event for that. other is a third, forth or fifth button */
1231 vout_thread_t * p_vout;
1232 id o_window = [self window];
1233 p_vout = (vout_thread_t *)[o_window getVout];
1236 switch( [o_event type] )
1238 case NSOtherMouseDown:
1240 var_Get( p_vout, "mouse-button-down", &val );
1242 var_Set( p_vout, "mouse-button-down", val );
1247 [super mouseDown: o_event];
1252 - (void)rightMouseDown:(NSEvent *)o_event
1254 vout_thread_t * p_vout;
1255 id o_window = [self window];
1256 p_vout = (vout_thread_t *)[o_window getVout];
1259 switch( [o_event type] )
1261 case NSRightMouseDown:
1263 var_Get( p_vout, "mouse-button-down", &val );
1265 var_Set( p_vout, "mouse-button-down", val );
1270 [super mouseDown: o_event];
1275 - (void)mouseUp:(NSEvent *)o_event
1277 vout_thread_t * p_vout;
1278 id o_window = [self window];
1279 p_vout = (vout_thread_t *)[o_window getVout];
1282 switch( [o_event type] )
1287 b_val.b_bool = VLC_TRUE;
1288 var_Set( p_vout, "mouse-clicked", b_val );
1290 var_Get( p_vout, "mouse-button-down", &val );
1292 var_Set( p_vout, "mouse-button-down", val );
1297 [super mouseUp: o_event];
1302 - (void)otherMouseUp:(NSEvent *)o_event
1304 vout_thread_t * p_vout;
1305 id o_window = [self window];
1306 p_vout = (vout_thread_t *)[o_window getVout];
1309 switch( [o_event type] )
1311 case NSOtherMouseUp:
1313 var_Get( p_vout, "mouse-button-down", &val );
1315 var_Set( p_vout, "mouse-button-down", val );
1320 [super mouseUp: o_event];
1325 - (void)rightMouseUp:(NSEvent *)o_event
1327 vout_thread_t * p_vout;
1328 id o_window = [self window];
1329 p_vout = (vout_thread_t *)[o_window getVout];
1332 switch( [o_event type] )
1334 case NSRightMouseUp:
1336 var_Get( p_vout, "mouse-button-down", &val );
1338 var_Set( p_vout, "mouse-button-down", val );
1343 [super mouseUp: o_event];
1348 - (void)mouseDragged:(NSEvent *)o_event
1350 [self mouseMoved:o_event];
1353 - (void)otherMouseDragged:(NSEvent *)o_event
1355 [self mouseMoved:o_event];
1358 - (void)rightMouseDragged:(NSEvent *)o_event
1360 [self mouseMoved:o_event];
1363 - (void)mouseMoved:(NSEvent *)o_event
1369 vout_thread_t * p_vout;
1370 id o_window = [self window];
1371 p_vout = (vout_thread_t *)[o_window getVout];
1373 s_rect = [self bounds];
1374 ml = [self convertPoint: [o_event locationInWindow] fromView: nil];
1375 b_inside = [self mouse: ml inRect: s_rect];
1380 int i_width, i_height, i_x, i_y;
1382 vout_PlacePicture( p_vout, (unsigned int)s_rect.size.width,
1383 (unsigned int)s_rect.size.height,
1384 &i_x, &i_y, &i_width, &i_height );
1386 val.i_int = ( ((int)ml.x) - i_x ) *
1387 p_vout->render.i_width / i_width;
1388 var_Set( p_vout, "mouse-x", val );
1390 val.i_int = ( ((int)ml.y) - i_y ) *
1391 p_vout->render.i_height / i_height;
1392 var_Set( p_vout, "mouse-y", val );
1394 val.b_bool = VLC_TRUE;
1395 var_Set( p_vout, "mouse-moved", val );
1396 p_vout->p_sys->i_time_mouse_last_moved = mdate();
1397 p_vout->p_sys->b_mouse_moved = YES;
1400 [super mouseMoved: o_event];
1405 /*****************************************************************************
1406 * VLCGLView implementation
1407 *****************************************************************************/
1408 @implementation VLCGLView
1411 - (id) initWithFrame: (NSRect) frame vout: (vout_thread_t*) _p_vout
1416 NSOpenGLPixelFormatAttribute attribs[] =
1418 NSOpenGLPFAAccelerated,
1419 NSOpenGLPFANoRecovery,
1420 NSOpenGLPFADoubleBuffer,
1421 NSOpenGLPFAColorSize, 24,
1422 NSOpenGLPFAAlphaSize, 8,
1423 NSOpenGLPFADepthSize, 24,
1428 NSOpenGLPixelFormat * fmt = [[NSOpenGLPixelFormat alloc]
1429 initWithAttributes: attribs];
1433 msg_Warn( p_vout, "Cannot create NSOpenGLPixelFormat" );
1437 self = [super initWithFrame:frame pixelFormat: fmt];
1439 currentContext = [self openGLContext];
1440 [currentContext makeCurrentContext];
1441 [currentContext update];
1443 /* Black background */
1444 glClearColor( 0.0, 0.0, 0.0, 0.0 );
1446 /* Check if the user asked for useless visual effects */
1447 psz_effect = config_GetPsz( p_vout, "macosx-opengl-effect" );
1448 if( !psz_effect || !strcmp( psz_effect, "none" ))
1450 i_effect = OPENGL_EFFECT_NONE;
1452 else if( !strcmp( psz_effect, "cube" ) )
1454 i_effect = OPENGL_EFFECT_CUBE;
1456 glEnable( GL_DEPTH_TEST );
1458 else if( !strcmp( psz_effect, "transparent-cube" ) )
1460 i_effect = OPENGL_EFFECT_TRANSPARENT_CUBE;
1462 glDisable( GL_DEPTH_TEST );
1463 glEnable( GL_BLEND );
1464 glBlendFunc( GL_SRC_ALPHA, GL_ONE );
1468 msg_Warn( p_vout, "no valid opengl effect provided, using "
1470 i_effect = OPENGL_EFFECT_NONE;
1473 if( i_effect & ( OPENGL_EFFECT_CUBE |
1474 OPENGL_EFFECT_TRANSPARENT_CUBE ) )
1476 /* Set the perpective */
1477 glMatrixMode( GL_PROJECTION );
1479 glFrustum( -1.0, 1.0, -1.0, 1.0, 3.0, 20.0 );
1480 glMatrixMode( GL_MODELVIEW );
1482 glTranslatef( 0.0, 0.0, - 5.0 );
1500 [currentContext makeCurrentContext];
1502 NSRect bounds = [self bounds];
1503 glViewport( 0, 0, (GLint) bounds.size.width,
1504 (GLint) bounds.size.height );
1506 if( config_GetInt( p_vout, "macosx-stretch" ) )
1511 /* Quad size is set in order to preserve the aspect ratio */
1512 else if( bounds.size.height * p_vout->output.i_aspect <
1513 bounds.size.width * VOUT_ASPECT_FACTOR )
1515 f_x = bounds.size.height * p_vout->output.i_aspect /
1516 VOUT_ASPECT_FACTOR / bounds.size.width;
1522 f_y = bounds.size.width * VOUT_ASPECT_FACTOR /
1523 p_vout->output.i_aspect / bounds.size.height;
1527 - (void) initTextures
1530 [currentContext makeCurrentContext];
1532 /* Free previous texture if any */
1535 glDeleteTextures( 2, pi_textures );
1538 /* Create textures */
1539 glGenTextures( 2, pi_textures );
1541 glEnable( GL_TEXTURE_RECTANGLE_EXT );
1542 glEnable( GL_UNPACK_CLIENT_STORAGE_APPLE );
1544 for( i = 0; i < 2; i++ )
1546 glBindTexture( GL_TEXTURE_RECTANGLE_EXT, pi_textures[i] );
1548 /* Use VRAM texturing */
1549 glTexParameteri( GL_TEXTURE_RECTANGLE_EXT,
1550 GL_TEXTURE_STORAGE_HINT_APPLE, GL_STORAGE_CACHED_APPLE );
1552 /* Tell the driver not to make a copy of the texture but to use
1554 glPixelStorei( GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE );
1556 /* Linear interpolation */
1557 glTexParameteri( GL_TEXTURE_RECTANGLE_EXT,
1558 GL_TEXTURE_MIN_FILTER, GL_LINEAR );
1559 glTexParameteri( GL_TEXTURE_RECTANGLE_EXT,
1560 GL_TEXTURE_MAG_FILTER, GL_LINEAR );
1562 /* I have no idea what this exactly does, but it seems to be
1563 necessary for scaling */
1564 glTexParameteri( GL_TEXTURE_RECTANGLE_EXT,
1565 GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
1566 glTexParameteri( GL_TEXTURE_RECTANGLE_EXT,
1567 GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
1568 glPixelStorei( GL_UNPACK_ROW_LENGTH, 0 );
1570 glTexImage2D( GL_TEXTURE_RECTANGLE_EXT, 0, GL_RGBA,
1571 p_vout->output.i_width, p_vout->output.i_height, 0,
1572 GL_YCBCR_422_APPLE, GL_UNSIGNED_SHORT_8_8_APPLE,
1573 p_vout->p_sys->p_data[i] );
1579 - (void)reloadTexture: (int) index
1586 [currentContext makeCurrentContext];
1588 glBindTexture( GL_TEXTURE_RECTANGLE_EXT, pi_textures[index] );
1590 /* glTexSubImage2D is faster than glTexImage2D
1591 http://developer.apple.com/samplecode/Sample_Code/Graphics_3D/
1592 TextureRange/MainOpenGLView.m.htm */
1593 glTexSubImage2D( GL_TEXTURE_RECTANGLE_EXT, 0, 0, 0,
1594 p_vout->output.i_width, p_vout->output.i_height,
1595 GL_YCBCR_422_APPLE, GL_UNSIGNED_SHORT_8_8_APPLE,
1596 p_vout->p_sys->p_data[index] );
1599 - (void)goFullScreen
1601 /* Create the new pixel format */
1602 NSOpenGLPixelFormatAttribute attribs[] =
1604 NSOpenGLPFAAccelerated,
1605 NSOpenGLPFANoRecovery,
1606 NSOpenGLPFADoubleBuffer,
1607 NSOpenGLPFAColorSize, 24,
1608 NSOpenGLPFAAlphaSize, 8,
1609 NSOpenGLPFADepthSize, 24,
1610 NSOpenGLPFAFullScreen,
1611 NSOpenGLPFAScreenMask,
1612 /* TODO multi monitor support */
1613 CGDisplayIDToOpenGLDisplayMask( kCGDirectMainDisplay ),
1616 NSOpenGLPixelFormat * fmt = [[NSOpenGLPixelFormat alloc]
1617 initWithAttributes: attribs];
1620 msg_Warn( p_vout, "Cannot create NSOpenGLPixelFormat" );
1624 /* Create the new OpenGL context */
1625 /* TODO have the shared context working so we don't have to
1627 fullScreenContext = [[NSOpenGLContext alloc]
1628 initWithFormat: fmt shareContext: nil];
1629 if( !fullScreenContext )
1631 msg_Warn( p_vout, "Failed to create new NSOpenGLContext" );
1634 currentContext = fullScreenContext;
1636 /* Capture display, switch to fullscreen */
1637 /* XXX we should capture only the display the user wants the
1639 if( CGCaptureAllDisplays() != CGDisplayNoErr )
1641 msg_Warn( p_vout, "CGCaptureAllDisplays() failed" );
1644 [fullScreenContext setFullScreen];
1645 [fullScreenContext makeCurrentContext];
1648 unsigned width = CGDisplayPixelsWide( kCGDirectMainDisplay );
1649 unsigned height = CGDisplayPixelsHigh( kCGDirectMainDisplay );
1650 int stretch = config_GetInt( p_vout, "macosx-stretch" );
1651 int fill = config_GetInt( p_vout, "macosx-fill" );
1652 int bigRatio = ( height * p_vout->output.i_aspect <
1653 width * VOUT_ASPECT_FACTOR );
1659 else if( ( bigRatio && !fill ) || ( !bigRatio && fill ) )
1661 f_x = (float) height * p_vout->output.i_aspect /
1662 width / VOUT_ASPECT_FACTOR;
1668 f_y = (float) width * VOUT_ASPECT_FACTOR /
1669 p_vout->output.i_aspect / height;
1672 /* Update viewport, re-init textures */
1673 glViewport( 0, 0, width, height );
1674 [self initTextures];
1676 /* Redraw the last picture */
1677 [self setNeedsDisplay: YES];
1682 - (void)exitFullScreen
1684 /* Free current OpenGL context */
1685 [NSOpenGLContext clearCurrentContext];
1686 [fullScreenContext clearDrawable];
1687 [fullScreenContext release];
1688 CGReleaseAllDisplays();
1690 currentContext = [self openGLContext];
1691 [self initTextures];
1694 /* Redraw the last picture */
1695 [self setNeedsDisplay: YES];
1704 [self exitFullScreen];
1711 glBegin( GL_QUADS );
1713 glTexCoord2f( 0.0, 0.0 );
1714 glVertex2f( - f_x, f_y );
1716 glTexCoord2f( 0.0, (float) p_vout->output.i_height );
1717 glVertex2f( - f_x, - f_y );
1719 glTexCoord2f( (float) p_vout->output.i_width,
1720 (float) p_vout->output.i_height );
1721 glVertex2f( f_x, - f_y );
1723 glTexCoord2f( (float) p_vout->output.i_width, 0.0 );
1724 glVertex2f( f_x, f_y );
1730 glBegin( GL_QUADS );
1732 glTexCoord2f( 0.0, 0.0 );
1733 glVertex3f( - 1.0, 1.0, 1.0 );
1734 glTexCoord2f( 0.0, (float) p_vout->output.i_height );
1735 glVertex3f( - 1.0, - 1.0, 1.0 );
1736 glTexCoord2f( (float) p_vout->output.i_width,
1737 (float) p_vout->output.i_height );
1738 glVertex3f( 1.0, - 1.0, 1.0 );
1739 glTexCoord2f( (float) p_vout->output.i_width, 0.0 );
1740 glVertex3f( 1.0, 1.0, 1.0 );
1743 glTexCoord2f( 0.0, 0.0 );
1744 glVertex3f( - 1.0, 1.0, - 1.0 );
1745 glTexCoord2f( 0.0, (float) p_vout->output.i_height );
1746 glVertex3f( - 1.0, - 1.0, - 1.0 );
1747 glTexCoord2f( (float) p_vout->output.i_width,
1748 (float) p_vout->output.i_height );
1749 glVertex3f( - 1.0, - 1.0, 1.0 );
1750 glTexCoord2f( (float) p_vout->output.i_width, 0.0 );
1751 glVertex3f( - 1.0, 1.0, 1.0 );
1754 glTexCoord2f( 0.0, 0.0 );
1755 glVertex3f( 1.0, 1.0, - 1.0 );
1756 glTexCoord2f( 0.0, (float) p_vout->output.i_height );
1757 glVertex3f( 1.0, - 1.0, - 1.0 );
1758 glTexCoord2f( (float) p_vout->output.i_width,
1759 (float) p_vout->output.i_height );
1760 glVertex3f( - 1.0, - 1.0, - 1.0 );
1761 glTexCoord2f( (float) p_vout->output.i_width, 0.0 );
1762 glVertex3f( - 1.0, 1.0, - 1.0 );
1765 glTexCoord2f( 0.0, 0.0 );
1766 glVertex3f( 1.0, 1.0, 1.0 );
1767 glTexCoord2f( 0.0, (float) p_vout->output.i_height );
1768 glVertex3f( 1.0, - 1.0, 1.0 );
1769 glTexCoord2f( (float) p_vout->output.i_width,
1770 (float) p_vout->output.i_height );
1771 glVertex3f( 1.0, - 1.0, - 1.0 );
1772 glTexCoord2f( (float) p_vout->output.i_width, 0.0 );
1773 glVertex3f( 1.0, 1.0, - 1.0 );
1776 glTexCoord2f( 0.0, 0.0 );
1777 glVertex3f( - 1.0, 1.0, - 1.0 );
1778 glTexCoord2f( 0.0, (float) p_vout->output.i_height );
1779 glVertex3f( - 1.0, 1.0, 1.0 );
1780 glTexCoord2f( (float) p_vout->output.i_width,
1781 (float) p_vout->output.i_height );
1782 glVertex3f( 1.0, 1.0, 1.0 );
1783 glTexCoord2f( (float) p_vout->output.i_width, 0.0 );
1784 glVertex3f( 1.0, 1.0, - 1.0 );
1787 glTexCoord2f( 0.0, 0.0 );
1788 glVertex3f( - 1.0, - 1.0, 1.0 );
1789 glTexCoord2f( 0.0, (float) p_vout->output.i_height );
1790 glVertex3f( - 1.0, - 1.0, - 1.0 );
1791 glTexCoord2f( (float) p_vout->output.i_width,
1792 (float) p_vout->output.i_height );
1793 glVertex3f( 1.0, - 1.0, - 1.0 );
1794 glTexCoord2f( (float) p_vout->output.i_width, 0.0 );
1795 glVertex3f( 1.0, - 1.0, 1.0 );
1799 - (void) drawRect: (NSRect) rect
1801 [currentContext makeCurrentContext];
1803 /* Swap buffers only during the vertical retrace of the monitor.
1804 http://developer.apple.com/documentation/GraphicsImaging/
1805 Conceptual/OpenGL/chap5/chapter_5_section_44.html */
1806 long params[] = { 1 };
1807 CGLSetParameter( CGLGetCurrentContext(), kCGLCPSwapInterval,
1810 /* Black background */
1811 glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
1815 [currentContext flushBuffer];
1820 glBindTexture( GL_TEXTURE_RECTANGLE_EXT,
1821 pi_textures[p_vout->p_sys->i_cur_pic] );
1822 if( i_effect & ( OPENGL_EFFECT_CUBE |
1823 OPENGL_EFFECT_TRANSPARENT_CUBE ) )
1825 glRotatef( 1.0, 0.3, 0.5, 0.7 );
1833 /* Wait for the job to be done */
1834 [currentContext flushBuffer];
1839 /*****************************************************************************
1840 * VLCVout implementation
1841 *****************************************************************************/
1842 @implementation VLCVout
1844 - (void)createWindow:(NSValue *)o_value
1848 NSScreen * o_screen;
1849 vout_thread_t * p_vout;
1850 vlc_bool_t b_main_screen;
1852 p_vout = (vout_thread_t *)[o_value pointerValue];
1854 p_vout->p_sys->o_window = [VLCWindow alloc];
1855 [p_vout->p_sys->o_window setVout: p_vout];
1856 [p_vout->p_sys->o_window setReleasedWhenClosed: YES];
1858 if( var_Get( p_vout, "video-device", &val ) < 0 )
1860 o_screen = [NSScreen mainScreen];
1865 NSArray *o_screens = [NSScreen screens];
1866 unsigned int i_index = val.i_int;
1868 if( [o_screens count] < i_index )
1870 o_screen = [NSScreen mainScreen];
1876 o_screen = [o_screens objectAtIndex: i_index];
1877 config_PutInt( p_vout, "macosx-vdev", i_index );
1878 b_main_screen = (i_index == 0);
1882 if( p_vout->b_fullscreen && !p_vout->p_sys->i_opengl )
1884 NSRect screen_rect = [o_screen frame];
1885 screen_rect.origin.x = screen_rect.origin.y = 0;
1887 if ( b_main_screen && p_vout->p_sys->p_fullscreen_state == NULL )
1888 BeginFullScreen( &p_vout->p_sys->p_fullscreen_state, NULL, 0, 0,
1889 NULL, NULL, fullScreenAllowEvents );
1891 [p_vout->p_sys->o_window
1892 initWithContentRect: screen_rect
1893 styleMask: NSBorderlessWindowMask
1894 backing: NSBackingStoreBuffered
1895 defer: NO screen: o_screen];
1897 //[p_vout->p_sys->o_window setLevel: NSPopUpMenuWindowLevel - 1];
1898 p_vout->p_sys->b_mouse_moved = YES;
1899 p_vout->p_sys->i_time_mouse_last_moved = mdate();
1903 unsigned int i_stylemask = NSTitledWindowMask |
1904 NSMiniaturizableWindowMask |
1905 NSClosableWindowMask |
1906 NSResizableWindowMask;
1908 if( !p_vout->p_sys->i_opengl )
1910 if ( p_vout->p_sys->p_fullscreen_state != NULL )
1911 EndFullScreen ( p_vout->p_sys->p_fullscreen_state, NULL );
1912 p_vout->p_sys->p_fullscreen_state = NULL;
1915 [p_vout->p_sys->o_window
1916 initWithContentRect: p_vout->p_sys->s_rect
1917 styleMask: i_stylemask
1918 backing: NSBackingStoreBuffered
1919 defer: NO screen: o_screen];
1921 [p_vout->p_sys->o_window setAlphaValue: config_GetFloat( p_vout, "macosx-opaqueness" )];
1923 if( config_GetInt( p_vout, "video-on-top" ) )
1925 [p_vout->p_sys->o_window setLevel: NSStatusWindowLevel];
1928 if( !p_vout->p_sys->b_pos_saved )
1930 [p_vout->p_sys->o_window center];
1934 if( !p_vout->p_sys->i_opengl )
1936 o_view = [[VLCQTView alloc] init];
1937 /* FIXME: [o_view setMenu:] */
1938 [p_vout->p_sys->o_window setContentView: o_view];
1939 [o_view autorelease];
1942 p_vout->p_sys->p_qdport = [o_view qdPort];
1943 [o_view unlockFocus];
1947 #define o_glview p_vout->p_sys->o_glview
1948 o_glview = [[VLCGLView alloc] initWithFrame: p_vout->p_sys->s_rect vout: p_vout];
1949 [p_vout->p_sys->o_window setContentView: o_glview];
1950 [o_glview autorelease];
1954 [p_vout->p_sys->o_window updateTitle];
1955 [p_vout->p_sys->o_window makeKeyAndOrderFront: nil];
1959 - (void)destroyWindow:(NSValue *)o_value
1961 vout_thread_t * p_vout;
1963 p_vout = (vout_thread_t *)[o_value pointerValue];
1965 if( !p_vout->b_fullscreen )
1969 s_rect = [[p_vout->p_sys->o_window contentView] frame];
1970 p_vout->p_sys->s_rect.size = s_rect.size;
1972 s_rect = [p_vout->p_sys->o_window frame];
1973 p_vout->p_sys->s_rect.origin = s_rect.origin;
1975 p_vout->p_sys->b_pos_saved = YES;
1978 p_vout->p_sys->p_qdport = nil;
1979 [p_vout->p_sys->o_window close];
1980 p_vout->p_sys->o_window = nil;