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 );
435 /* Free the direct buffers we allocated */
436 for( i_index = I_OUTPUTPICTURES; i_index; )
439 if( !p_vout->p_sys->i_opengl )
441 QTFreePicture( p_vout, PP_OUTPUTPICTURE[ i_index ] );
445 free( p_vout->p_sys->p_data_orig[0] );
446 free( p_vout->p_sys->p_data_orig[1] );
451 /*****************************************************************************
452 * CloseVideo: destroy video thread output method
453 *****************************************************************************/
454 void E_(CloseVideo) ( vlc_object_t *p_this )
456 vout_thread_t * p_vout = (vout_thread_t *)p_this;
458 if( p_vout->p_sys->i_opengl )
460 [p_vout->p_sys->o_glview cleanUp];
463 if( CoDestroyWindow( p_vout ) )
465 msg_Err( p_vout, "unable to destroy window" );
468 if ( p_vout->p_sys->p_fullscreen_state != NULL )
470 EndFullScreen ( p_vout->p_sys->p_fullscreen_state, NULL );
473 if( !p_vout->p_sys->i_opengl )
476 free( p_vout->p_sys->p_matrix );
477 DisposeHandle( (Handle)p_vout->p_sys->h_img_descr );
480 free( p_vout->p_sys );
483 /*****************************************************************************
484 * vout_Manage: handle events
485 *****************************************************************************
486 * This function should be called regularly by video output thread. It manages
487 * console events. It returns a non null value on error.
488 *****************************************************************************/
489 static int vout_Manage( vout_thread_t *p_vout )
491 if( p_vout->i_changes & VOUT_FULLSCREEN_CHANGE )
493 if( CoToggleFullscreen( p_vout ) )
498 p_vout->i_changes &= ~VOUT_FULLSCREEN_CHANGE;
501 if( p_vout->i_changes & VOUT_SIZE_CHANGE )
503 if( !p_vout->p_sys->i_opengl )
505 QTScaleMatrix( p_vout );
506 SetDSequenceMatrix( p_vout->p_sys->i_seq,
507 p_vout->p_sys->p_matrix );
510 p_vout->i_changes &= ~VOUT_SIZE_CHANGE;
513 /* hide/show mouse cursor
514 * this code looks unnecessarily complicated, but is necessary like this.
515 * it has to deal with multiple monitors and therefore checks a lot */
516 if( !p_vout->p_sys->b_mouse_moved && p_vout->b_fullscreen )
518 if( mdate() - p_vout->p_sys->i_time_mouse_last_moved > 3000000 )
520 VLCHideMouse( p_vout, YES );
523 else if ( p_vout->p_sys->b_mouse_moved && p_vout->b_fullscreen )
525 VLCHideMouse( p_vout, NO );
528 /* disable screen saver */
529 UpdateSystemActivity( UsrActivity );
534 /*****************************************************************************
535 * vout_Display: displays previously rendered output
536 *****************************************************************************
537 * This function sends the currently rendered image to the display.
538 *****************************************************************************/
539 static void vout_Display( vout_thread_t *p_vout, picture_t *p_pic )
541 if( !p_vout->p_sys->i_opengl )
546 if( ( err = DecompressSequenceFrameS(
547 p_vout->p_sys->i_seq,
548 p_pic->p_sys->p_info,
549 p_pic->p_sys->i_size,
550 codecFlagUseImageBuffer, &flags, nil ) != noErr ) )
552 msg_Warn( p_vout, "DecompressSequenceFrameS failed: %d", err );
556 QDFlushPortBuffer( p_vout->p_sys->p_qdport, nil );
561 if( [p_vout->p_sys->o_glview lockFocusIfCanDraw] )
564 int i_old = p_vout->p_sys->i_cur_pic;
565 int i_new = ( i_old + 1 ) % 2;
567 /* Draw the new picture */
568 p_vout->p_sys->i_cur_pic = i_new;
569 [p_vout->p_sys->o_glview drawRect:
570 [p_vout->p_sys->o_glview bounds]];
572 /* Reload the other texture. Textures have to be reloaded
573 before the buffer is filled (thanks to gcc from
574 arstechnica forums) */
575 [p_vout->p_sys->o_glview reloadTexture: i_old];
578 p_pic->p_data = p_vout->p_sys->p_data[i_old];
579 p_pic->p[0].p_pixels = p_pic->p_data;
580 for( i_index = 1; i_index < p_pic->i_planes; i_index++ )
582 p_pic->p[i_index].p_pixels =
583 p_pic->p[i_index-1].p_pixels +
584 p_pic->p[i_index-1].i_lines *
585 p_pic->p[i_index-1].i_pitch;
587 [p_vout->p_sys->o_glview unlockFocus];
592 /*****************************************************************************
593 * CoSendRequest: send request to interface thread
594 *****************************************************************************
595 * Returns 0 on success, 1 otherwise
596 *****************************************************************************/
597 static int CoSendRequest( vout_thread_t *p_vout, SEL sel )
601 intf_thread_t * p_intf;
603 VLCVout * o_vlv = [[VLCVout alloc] init];
605 if( ( i_ret = ExecuteOnMainThread( o_vlv, sel, (void *)p_vout ) ) )
607 msg_Err( p_vout, "SendRequest: no way to communicate with mt" );
612 /*This makes this function dependant of the presence of a macosx
613 interface. We do not check if this interface exists, since it has
614 already been done before.*/
616 p_intf = [NSApp getIntf];
618 val.b_bool = VLC_TRUE;
619 var_Create(p_intf,"intf-change",VLC_VAR_BOOL);
620 var_Set(p_intf, "intf-change",val);
625 /*****************************************************************************
626 * CoCreateWindow: create new window
627 *****************************************************************************
628 * Returns 0 on success, 1 otherwise
629 *****************************************************************************/
630 static int CoCreateWindow( vout_thread_t *p_vout )
632 if( CoSendRequest( p_vout, @selector(createWindow:) ) )
634 msg_Err( p_vout, "CoSendRequest (createWindow) failed" );
641 /*****************************************************************************
642 * CoDestroyWindow: destroy window
643 *****************************************************************************
644 * Returns 0 on success, 1 otherwise
645 *****************************************************************************/
646 static int CoDestroyWindow( vout_thread_t *p_vout )
649 VLCHideMouse( p_vout, NO );
651 if( CoSendRequest( p_vout, @selector(destroyWindow:) ) )
653 msg_Err( p_vout, "CoSendRequest (destroyWindow) failed" );
660 /*****************************************************************************
661 * CoToggleFullscreen: toggle fullscreen
662 *****************************************************************************
663 * Returns 0 on success, 1 otherwise
664 *****************************************************************************/
665 static int CoToggleFullscreen( vout_thread_t *p_vout )
667 if( !p_vout->p_sys->i_opengl )
669 QTDestroySequence( p_vout );
672 if( CoDestroyWindow( p_vout ) )
674 msg_Err( p_vout, "unable to destroy window" );
678 p_vout->b_fullscreen = !p_vout->b_fullscreen;
680 if( CoCreateWindow( p_vout ) )
682 msg_Err( p_vout, "unable to create window" );
686 if( p_vout->p_sys->i_opengl )
688 [p_vout->p_sys->o_glview lockFocus];
689 [p_vout->p_sys->o_glview initTextures];
690 [p_vout->p_sys->o_glview reshape];
691 [p_vout->p_sys->o_glview drawRect:
692 [p_vout->p_sys->o_glview bounds]];
693 [p_vout->p_sys->o_glview unlockFocus];
697 SetPort( p_vout->p_sys->p_qdport );
698 QTScaleMatrix( p_vout );
700 if( QTCreateSequence( p_vout ) )
702 msg_Err( p_vout, "unable to create sequence" );
710 /*****************************************************************************
711 * VLCHideMouse: if b_hide then hide the cursor
712 *****************************************************************************/
713 static void VLCHideMouse ( vout_thread_t *p_vout, BOOL b_hide )
718 NSWindow *o_window = p_vout->p_sys->o_window;
719 NSView *o_contents = [o_window contentView];
721 s_rect = [o_contents bounds];
722 ml = [o_window convertScreenToBase:[NSEvent mouseLocation]];
723 ml = [o_contents convertPoint:ml fromView:nil];
724 b_inside = [o_contents mouse: ml inRect: s_rect];
726 if ( b_hide && b_inside )
728 /* only hide if mouse over VLCQTView */
729 [NSCursor setHiddenUntilMouseMoves: YES];
733 [NSCursor setHiddenUntilMouseMoves: NO];
735 p_vout->p_sys->b_mouse_moved = NO;
736 p_vout->p_sys->i_time_mouse_last_moved = mdate();
740 /*****************************************************************************
741 * QTScaleMatrix: scale matrix
742 *****************************************************************************/
743 static void QTScaleMatrix( vout_thread_t *p_vout )
746 unsigned int i_width, i_height;
747 Fixed factor_x, factor_y;
748 unsigned int i_offset_x = 0;
749 unsigned int i_offset_y = 0;
751 GetPortBounds( p_vout->p_sys->p_qdport, &s_rect );
753 i_width = s_rect.right - s_rect.left;
754 i_height = s_rect.bottom - s_rect.top;
756 if( config_GetInt( p_vout, "macosx-stretch" ) )
758 factor_x = FixDiv( Long2Fix( i_width ),
759 Long2Fix( p_vout->output.i_width ) );
760 factor_y = FixDiv( Long2Fix( i_height ),
761 Long2Fix( p_vout->output.i_height ) );
764 else if( i_height * p_vout->output.i_aspect < i_width * VOUT_ASPECT_FACTOR )
766 int i_adj_width = i_height * p_vout->output.i_aspect /
769 factor_x = FixDiv( Long2Fix( i_adj_width ),
770 Long2Fix( p_vout->output.i_width ) );
771 factor_y = FixDiv( Long2Fix( i_height ),
772 Long2Fix( p_vout->output.i_height ) );
774 i_offset_x = (i_width - i_adj_width) / 2;
778 int i_adj_height = i_width * VOUT_ASPECT_FACTOR /
779 p_vout->output.i_aspect;
781 factor_x = FixDiv( Long2Fix( i_width ),
782 Long2Fix( p_vout->output.i_width ) );
783 factor_y = FixDiv( Long2Fix( i_adj_height ),
784 Long2Fix( p_vout->output.i_height ) );
786 i_offset_y = (i_height - i_adj_height) / 2;
789 SetIdentityMatrix( p_vout->p_sys->p_matrix );
791 ScaleMatrix( p_vout->p_sys->p_matrix,
793 Long2Fix(0), Long2Fix(0) );
795 TranslateMatrix( p_vout->p_sys->p_matrix,
796 Long2Fix(i_offset_x), Long2Fix(i_offset_y) );
799 /*****************************************************************************
800 * QTCreateSequence: create a new sequence
801 *****************************************************************************
802 * Returns 0 on success, 1 otherwise
803 *****************************************************************************/
804 static int QTCreateSequence( vout_thread_t *p_vout )
807 ImageDescriptionPtr p_descr;
809 HLock( (Handle)p_vout->p_sys->h_img_descr );
810 p_descr = *p_vout->p_sys->h_img_descr;
812 p_descr->idSize = sizeof(ImageDescription);
813 p_descr->cType = p_vout->p_sys->i_codec;
814 p_descr->version = 1;
815 p_descr->revisionLevel = 0;
816 p_descr->vendor = 'appl';
817 p_descr->width = p_vout->output.i_width;
818 p_descr->height = p_vout->output.i_height;
819 p_descr->hRes = Long2Fix(72);
820 p_descr->vRes = Long2Fix(72);
821 p_descr->spatialQuality = codecLosslessQuality;
822 p_descr->frameCount = 1;
823 p_descr->clutID = -1;
824 p_descr->dataSize = 0;
827 HUnlock( (Handle)p_vout->p_sys->h_img_descr );
829 if( ( err = DecompressSequenceBeginS(
830 &p_vout->p_sys->i_seq,
831 p_vout->p_sys->h_img_descr,
833 p_vout->p_sys->p_qdport,
835 p_vout->p_sys->p_matrix,
837 codecFlagUseImageBuffer,
838 codecLosslessQuality,
839 p_vout->p_sys->img_dc ) ) )
841 msg_Err( p_vout, "DecompressSequenceBeginS failed: %d", err );
848 /*****************************************************************************
849 * QTDestroySequence: destroy sequence
850 *****************************************************************************/
851 static void QTDestroySequence( vout_thread_t *p_vout )
853 CDSequenceEnd( p_vout->p_sys->i_seq );
856 /*****************************************************************************
857 * QTNewPicture: allocate a picture
858 *****************************************************************************
859 * Returns 0 on success, 1 otherwise
860 *****************************************************************************/
861 static int QTNewPicture( vout_thread_t *p_vout, picture_t *p_pic )
863 int i_width = p_vout->output.i_width;
864 int i_height = p_vout->output.i_height;
866 /* We know the chroma, allocate a buffer which will be used
867 * directly by the decoder */
868 p_pic->p_sys = malloc( sizeof( picture_sys_t ) );
870 if( p_pic->p_sys == NULL )
875 switch( p_vout->output.i_chroma )
877 case VLC_FOURCC('I','4','2','0'):
879 p_pic->p_sys->p_info = (void *)&p_pic->p_sys->pixmap_i420;
880 p_pic->p_sys->i_size = sizeof(PlanarPixmapInfoYUV420);
882 /* Allocate the memory buffer */
883 p_pic->p_data = vlc_memalign( &p_pic->p_data_orig,
884 16, i_width * i_height * 3 / 2 );
887 p_pic->Y_PIXELS = p_pic->p_data;
888 p_pic->p[Y_PLANE].i_lines = i_height;
889 p_pic->p[Y_PLANE].i_pitch = i_width;
890 p_pic->p[Y_PLANE].i_pixel_pitch = 1;
891 p_pic->p[Y_PLANE].i_visible_pitch = i_width;
894 p_pic->U_PIXELS = p_pic->Y_PIXELS + i_height * i_width;
895 p_pic->p[U_PLANE].i_lines = i_height / 2;
896 p_pic->p[U_PLANE].i_pitch = i_width / 2;
897 p_pic->p[U_PLANE].i_pixel_pitch = 1;
898 p_pic->p[U_PLANE].i_visible_pitch = i_width / 2;
901 p_pic->V_PIXELS = p_pic->U_PIXELS + i_height * i_width / 4;
902 p_pic->p[V_PLANE].i_lines = i_height / 2;
903 p_pic->p[V_PLANE].i_pitch = i_width / 2;
904 p_pic->p[V_PLANE].i_pixel_pitch = 1;
905 p_pic->p[V_PLANE].i_visible_pitch = i_width / 2;
907 /* We allocated 3 planes */
910 #define P p_pic->p_sys->pixmap_i420
911 P.componentInfoY.offset = (void *)p_pic->Y_PIXELS
912 - p_pic->p_sys->p_info;
913 P.componentInfoCb.offset = (void *)p_pic->U_PIXELS
914 - p_pic->p_sys->p_info;
915 P.componentInfoCr.offset = (void *)p_pic->V_PIXELS
916 - p_pic->p_sys->p_info;
918 P.componentInfoY.rowBytes = i_width;
919 P.componentInfoCb.rowBytes = i_width / 2;
920 P.componentInfoCr.rowBytes = i_width / 2;
926 /* Unknown chroma, tell the guy to get lost */
927 free( p_pic->p_sys );
928 msg_Err( p_vout, "never heard of chroma 0x%.8x (%4.4s)",
929 p_vout->output.i_chroma, (char*)&p_vout->output.i_chroma );
937 /*****************************************************************************
938 * QTFreePicture: destroy a picture allocated with QTNewPicture
939 *****************************************************************************/
940 static void QTFreePicture( vout_thread_t *p_vout, picture_t *p_pic )
942 switch( p_vout->output.i_chroma )
944 case VLC_FOURCC('I','4','2','0'):
945 free( p_pic->p_data_orig );
949 free( p_pic->p_sys );
952 /*****************************************************************************
953 * VLCWindow implementation
954 *****************************************************************************/
955 @implementation VLCWindow
957 - (void)setVout:(vout_thread_t *)_p_vout
962 - (vout_thread_t *)getVout
967 - (void)scaleWindowWithFactor: (float)factor
970 int i_corrected_height, i_corrected_width;
972 NSPoint topleftscreen;
974 if ( !p_vout->b_fullscreen )
977 topleftbase.y = [self frame].size.height;
978 topleftscreen = [self convertBaseToScreen: topleftbase];
980 if( p_vout->output.i_height * p_vout->output.i_aspect >
981 p_vout->output.i_width * VOUT_ASPECT_FACTOR )
983 i_corrected_width = p_vout->output.i_height * p_vout->output.i_aspect /
985 newsize.width = (int) ( i_corrected_width * factor );
986 newsize.height = (int) ( p_vout->render.i_height * factor );
990 i_corrected_height = p_vout->output.i_width * VOUT_ASPECT_FACTOR /
991 p_vout->output.i_aspect;
992 newsize.width = (int) ( p_vout->render.i_width * factor );
993 newsize.height = (int) ( i_corrected_height * factor );
996 [self setContentSize: newsize];
998 [self setFrameTopLeftPoint: topleftscreen];
999 p_vout->i_changes |= VOUT_SIZE_CHANGE;
1003 - (void)toggleFloatOnTop
1006 if( var_Get( p_vout, "video-on-top", &val )>=0 && val.b_bool)
1008 val.b_bool = VLC_FALSE;
1009 var_Set( p_vout, "video-on-top", val );
1010 [p_vout->p_sys->o_window setLevel: NSNormalWindowLevel];
1014 val.b_bool = VLC_TRUE;
1015 var_Set( p_vout, "video-on-top", val );
1016 [p_vout->p_sys->o_window setLevel: NSStatusWindowLevel];
1020 - (void)toggleFullscreen
1023 val.b_bool = !p_vout->b_fullscreen;
1024 var_Set( p_vout, "fullscreen", val );
1027 - (BOOL)isFullscreen
1029 return( p_vout->b_fullscreen );
1032 - (BOOL)canBecomeKeyWindow
1037 - (BOOL)performKeyEquivalent:(NSEvent *)o_event
1039 return [(VLCApplication *) [VLCApplication sharedApplication]
1040 hasDefinedShortcutKey:o_event];
1043 - (void)keyDown:(NSEvent *)o_event
1047 unsigned int i_pressed_modifiers = 0;
1050 i_pressed_modifiers = [o_event modifierFlags];
1052 if( i_pressed_modifiers & NSShiftKeyMask )
1053 val.i_int |= KEY_MODIFIER_SHIFT;
1054 if( i_pressed_modifiers & NSControlKeyMask )
1055 val.i_int |= KEY_MODIFIER_CTRL;
1056 if( i_pressed_modifiers & NSAlternateKeyMask )
1057 val.i_int |= KEY_MODIFIER_ALT;
1058 if( i_pressed_modifiers & NSCommandKeyMask )
1059 val.i_int |= KEY_MODIFIER_COMMAND;
1061 key = [[o_event charactersIgnoringModifiers] characterAtIndex: 0];
1065 /* Escape should always get you out of fullscreen */
1066 if( key == (unichar) 0x1b )
1068 if( [self isFullscreen] )
1070 [self toggleFullscreen];
1073 else if ( key == ' ' )
1075 playlist_t *p_playlist = vlc_object_find( p_vout, VLC_OBJECT_PLAYLIST,
1077 if ( p_playlist != NULL )
1079 playlist_Pause( p_playlist );
1080 vlc_object_release( p_playlist);
1085 val.i_int |= CocoaKeyToVLC( key );
1086 var_Set( p_vout->p_vlc, "key-pressed", val );
1091 [super keyDown: o_event];
1097 NSMutableString * o_title;
1098 playlist_t * p_playlist = vlc_object_find( p_vout, VLC_OBJECT_PLAYLIST,
1101 if( p_playlist == NULL )
1106 vlc_mutex_lock( &p_playlist->object_lock );
1107 o_title = [NSMutableString stringWithUTF8String:
1108 p_playlist->pp_items[p_playlist->i_index]->input.psz_uri];
1109 vlc_mutex_unlock( &p_playlist->object_lock );
1111 vlc_object_release( p_playlist );
1113 if( o_title != nil )
1115 NSRange prefix_range = [o_title rangeOfString: @"file:"];
1116 if( prefix_range.location != NSNotFound )
1118 [o_title deleteCharactersInRange: prefix_range];
1121 [self setTitleWithRepresentedFilename: o_title];
1126 [NSString stringWithCString: VOUT_TITLE " (QuickTime)"]];
1130 /* This is actually the same as VLCControls::stop. */
1131 - (BOOL)windowShouldClose:(id)sender
1133 playlist_t * p_playlist = vlc_object_find( p_vout, VLC_OBJECT_PLAYLIST,
1135 if( p_playlist == NULL )
1140 playlist_Stop( p_playlist );
1141 vlc_object_release( p_playlist );
1143 /* The window will be closed by the intf later. */
1149 /* Common QT and OpenGL code to catch mouse events */
1150 #define CATCH_MOUSE_EVENTS \
1151 - (BOOL)acceptsFirstResponder \
1156 - (BOOL)becomeFirstResponder \
1158 id o_window = [self window]; \
1160 [o_window setAcceptsMouseMovedEvents: YES]; \
1164 - (BOOL)resignFirstResponder \
1166 vout_thread_t * vout; \
1167 id o_window = [self window]; \
1168 vout = (vout_thread_t *)[o_window getVout]; \
1170 [o_window setAcceptsMouseMovedEvents: NO]; \
1171 VLCHideMouse( vout, NO ); \
1175 - (void)mouseDown:(NSEvent *)o_event \
1177 vout_thread_t * vout; \
1178 id o_window = [self window]; \
1179 vout = (vout_thread_t *)[o_window getVout]; \
1182 switch( [o_event type] ) \
1184 case NSLeftMouseDown: \
1186 var_Get( vout, "mouse-button-down", &val ); \
1188 var_Set( vout, "mouse-button-down", val ); \
1193 [super mouseDown: o_event]; \
1198 - (void)otherMouseDown:(NSEvent *)o_event \
1200 vout_thread_t * vout; \
1201 id o_window = [self window]; \
1202 vout = (vout_thread_t *)[o_window getVout]; \
1205 switch( [o_event type] ) \
1207 case NSOtherMouseDown: \
1209 var_Get( vout, "mouse-button-down", &val ); \
1211 var_Set( vout, "mouse-button-down", val ); \
1216 [super mouseDown: o_event]; \
1221 - (void)rightMouseDown:(NSEvent *)o_event \
1223 vout_thread_t * vout; \
1224 id o_window = [self window]; \
1225 vout = (vout_thread_t *)[o_window getVout]; \
1228 switch( [o_event type] ) \
1230 case NSRightMouseDown: \
1232 var_Get( vout, "mouse-button-down", &val ); \
1234 var_Set( vout, "mouse-button-down", val ); \
1239 [super mouseDown: o_event]; \
1244 - (void)mouseUp:(NSEvent *)o_event \
1246 vout_thread_t * vout; \
1247 id o_window = [self window]; \
1248 vout = (vout_thread_t *)[o_window getVout]; \
1251 switch( [o_event type] ) \
1253 case NSLeftMouseUp: \
1255 vlc_value_t b_val; \
1256 b_val.b_bool = VLC_TRUE; \
1257 var_Set( vout, "mouse-clicked", b_val ); \
1259 var_Get( vout, "mouse-button-down", &val ); \
1261 var_Set( vout, "mouse-button-down", val ); \
1266 [super mouseUp: o_event]; \
1271 - (void)otherMouseUp:(NSEvent *)o_event \
1273 vout_thread_t * vout; \
1274 id o_window = [self window]; \
1275 vout = (vout_thread_t *)[o_window getVout]; \
1278 switch( [o_event type] ) \
1280 case NSOtherMouseUp: \
1282 var_Get( vout, "mouse-button-down", &val ); \
1284 var_Set( vout, "mouse-button-down", val ); \
1289 [super mouseUp: o_event]; \
1294 - (void)rightMouseUp:(NSEvent *)o_event \
1296 vout_thread_t * vout; \
1297 id o_window = [self window]; \
1298 vout = (vout_thread_t *)[o_window getVout]; \
1301 switch( [o_event type] ) \
1303 case NSRightMouseUp: \
1305 var_Get( vout, "mouse-button-down", &val ); \
1307 var_Set( vout, "mouse-button-down", val ); \
1312 [super mouseUp: o_event]; \
1317 - (void)mouseDragged:(NSEvent *)o_event \
1319 [self mouseMoved:o_event]; \
1322 - (void)otherMouseDragged:(NSEvent *)o_event \
1324 [self mouseMoved:o_event]; \
1327 - (void)rightMouseDragged:(NSEvent *)o_event \
1329 [self mouseMoved:o_event]; \
1332 /*****************************************************************************
1333 * VLCQTView implementation
1334 *****************************************************************************/
1335 @implementation VLCQTView
1337 - (void)drawRect:(NSRect)rect
1339 vout_thread_t * p_vout;
1340 id o_window = [self window];
1341 p_vout = (vout_thread_t *)[o_window getVout];
1343 [[NSColor blackColor] set];
1345 [super drawRect: rect];
1347 p_vout->i_changes |= VOUT_SIZE_CHANGE;
1352 - (void)mouseMoved:(NSEvent *)o_event
1358 vout_thread_t * p_vout;
1359 id o_window = [self window];
1360 p_vout = (vout_thread_t *)[o_window getVout];
1362 s_rect = [self bounds];
1363 ml = [self convertPoint: [o_event locationInWindow] fromView: nil];
1364 b_inside = [self mouse: ml inRect: s_rect];
1369 int i_width, i_height, i_x, i_y;
1371 vout_PlacePicture( p_vout, (unsigned int)s_rect.size.width,
1372 (unsigned int)s_rect.size.height,
1373 &i_x, &i_y, &i_width, &i_height );
1375 val.i_int = ( ((int)ml.x) - i_x ) *
1376 p_vout->render.i_width / i_width;
1377 var_Set( p_vout, "mouse-x", val );
1379 val.i_int = ( ((int)ml.y) - i_y ) *
1380 p_vout->render.i_height / i_height;
1381 var_Set( p_vout, "mouse-y", val );
1383 val.b_bool = VLC_TRUE;
1384 var_Set( p_vout, "mouse-moved", val );
1385 p_vout->p_sys->i_time_mouse_last_moved = mdate();
1386 p_vout->p_sys->b_mouse_moved = YES;
1389 [super mouseMoved: o_event];
1394 /*****************************************************************************
1395 * VLCGLView implementation
1396 *****************************************************************************/
1397 @implementation VLCGLView
1400 - (id) initWithFrame: (NSRect) frame vout: (vout_thread_t*) _p_vout
1405 NSOpenGLPixelFormatAttribute attribs[] =
1407 NSOpenGLPFAAccelerated,
1408 NSOpenGLPFANoRecovery,
1409 NSOpenGLPFAColorSize, 24,
1410 NSOpenGLPFAAlphaSize, 8,
1411 NSOpenGLPFADepthSize, 24,
1416 NSOpenGLPixelFormat * fmt = [[NSOpenGLPixelFormat alloc]
1417 initWithAttributes: attribs];
1421 msg_Warn( p_vout, "Cannot create NSOpenGLPixelFormat" );
1425 self = [super initWithFrame:frame pixelFormat: fmt];
1428 [[self openGLContext] makeCurrentContext];
1429 [[self openGLContext] update];
1431 /* Black background */
1432 glClearColor( 0.0, 0.0, 0.0, 0.0 );
1434 /* Check if the user asked for useless visual effects */
1435 psz_effect = config_GetPsz( p_vout, "macosx-opengl-effect" );
1436 if( !psz_effect || !strcmp( psz_effect, "none" ))
1438 i_effect = OPENGL_EFFECT_NONE;
1440 else if( !strcmp( psz_effect, "cube" ) )
1442 i_effect = OPENGL_EFFECT_CUBE;
1444 glEnable( GL_DEPTH_TEST );
1446 else if( !strcmp( psz_effect, "transparent-cube" ) )
1448 i_effect = OPENGL_EFFECT_TRANSPARENT_CUBE;
1450 glDisable( GL_DEPTH_TEST );
1451 glEnable( GL_BLEND );
1452 glBlendFunc( GL_SRC_ALPHA, GL_ONE );
1456 msg_Warn( p_vout, "no valid opengl effect provided, using "
1458 i_effect = OPENGL_EFFECT_NONE;
1461 if( i_effect & ( OPENGL_EFFECT_CUBE |
1462 OPENGL_EFFECT_TRANSPARENT_CUBE ) )
1464 /* Set the perpective */
1465 glMatrixMode( GL_PROJECTION );
1467 glFrustum( -1.0, 1.0, -1.0, 1.0, 3.0, 20.0 );
1468 glMatrixMode( GL_MODELVIEW );
1470 glTranslatef( 0.0, 0.0, - 5.0 );
1487 [[self openGLContext] makeCurrentContext];
1489 NSRect bounds = [self bounds];
1490 glViewport( 0, 0, (GLint) bounds.size.width,
1491 (GLint) bounds.size.height );
1493 if( config_GetInt( p_vout, "macosx-stretch" ) )
1500 /* Quad size is set in order to preserve the aspect ratio */
1501 int fill = ( config_GetInt( p_vout, "macosx-fill" ) &&
1502 p_vout->b_fullscreen );
1503 int large = ( bounds.size.height * p_vout->output.i_aspect <
1504 bounds.size.width * VOUT_ASPECT_FACTOR );
1505 if( ( large && !fill ) || ( !large && fill ) )
1507 f_x = bounds.size.height * p_vout->output.i_aspect /
1508 VOUT_ASPECT_FACTOR / bounds.size.width;
1514 f_y = bounds.size.width * VOUT_ASPECT_FACTOR /
1515 p_vout->output.i_aspect / bounds.size.height;
1520 - (void) initTextures
1523 [[self openGLContext] makeCurrentContext];
1525 /* Free previous texture if any */
1528 glDeleteTextures( 2, pi_textures );
1531 glEnable( GL_TEXTURE_RECTANGLE_EXT );
1532 glEnable( GL_UNPACK_CLIENT_STORAGE_APPLE );
1534 glPixelStorei( GL_UNPACK_ALIGNMENT, 1 );
1535 glPixelStorei( GL_UNPACK_ROW_LENGTH, p_vout->output.i_width );
1537 /* Tell the driver not to make a copy of the texture but to use
1539 glPixelStorei( GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE );
1541 /* Create textures */
1542 glGenTextures( 2, pi_textures );
1544 for( i = 0; i < 2; i++ )
1546 glBindTexture( GL_TEXTURE_RECTANGLE_EXT, pi_textures[i] );
1547 glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE );
1549 /* Linear interpolation */
1550 glTexParameteri( GL_TEXTURE_RECTANGLE_EXT,
1551 GL_TEXTURE_MIN_FILTER, GL_LINEAR );
1552 glTexParameteri( GL_TEXTURE_RECTANGLE_EXT,
1553 GL_TEXTURE_MAG_FILTER, GL_LINEAR );
1555 /* Use VRAM texturing */
1556 glTexParameteri( GL_TEXTURE_RECTANGLE_EXT,
1557 GL_TEXTURE_STORAGE_HINT_APPLE, GL_STORAGE_CACHED_APPLE );
1559 glTexImage2D( GL_TEXTURE_RECTANGLE_EXT, 0, GL_RGB8,
1560 p_vout->output.i_width, p_vout->output.i_height, 0,
1561 GL_YCBCR_422_APPLE, GL_UNSIGNED_SHORT_8_8_APPLE,
1562 p_vout->p_sys->p_data[i] );
1565 /* Swap buffers only during the vertical retrace of the monitor.
1566 http://developer.apple.com/documentation/GraphicsImaging/
1567 Conceptual/OpenGL/chap5/chapter_5_section_44.html */
1568 long params[] = { 1 };
1569 CGLSetParameter( CGLGetCurrentContext(), kCGLCPSwapInterval,
1575 - (void)reloadTexture: (int) index
1582 [[self openGLContext] makeCurrentContext];
1584 glBindTexture( GL_TEXTURE_RECTANGLE_EXT, pi_textures[index] );
1585 glPixelStorei( GL_UNPACK_ROW_LENGTH, p_vout->output.i_width );
1587 /* glTexSubImage2D is faster than glTexImage2D
1588 http://developer.apple.com/samplecode/Sample_Code/Graphics_3D/
1589 TextureRange/MainOpenGLView.m.htm */
1590 glTexSubImage2D( GL_TEXTURE_RECTANGLE_EXT, 0, 0, 0,
1591 p_vout->output.i_width, p_vout->output.i_height,
1592 GL_YCBCR_422_APPLE, GL_UNSIGNED_SHORT_8_8_APPLE,
1593 p_vout->p_sys->p_data[index] );
1603 glBegin( GL_QUADS );
1605 glTexCoord2f( 0.0, 0.0 );
1606 glVertex2f( - f_x, f_y );
1608 glTexCoord2f( 0.0, (float) p_vout->output.i_height );
1609 glVertex2f( - f_x, - f_y );
1611 glTexCoord2f( (float) p_vout->output.i_width,
1612 (float) p_vout->output.i_height );
1613 glVertex2f( f_x, - f_y );
1615 glTexCoord2f( (float) p_vout->output.i_width, 0.0 );
1616 glVertex2f( f_x, f_y );
1622 glBegin( GL_QUADS );
1624 glTexCoord2f( 0.0, 0.0 );
1625 glVertex3f( - 1.0, 1.0, 1.0 );
1626 glTexCoord2f( 0.0, (float) p_vout->output.i_height );
1627 glVertex3f( - 1.0, - 1.0, 1.0 );
1628 glTexCoord2f( (float) p_vout->output.i_width,
1629 (float) p_vout->output.i_height );
1630 glVertex3f( 1.0, - 1.0, 1.0 );
1631 glTexCoord2f( (float) p_vout->output.i_width, 0.0 );
1632 glVertex3f( 1.0, 1.0, 1.0 );
1635 glTexCoord2f( 0.0, 0.0 );
1636 glVertex3f( - 1.0, 1.0, - 1.0 );
1637 glTexCoord2f( 0.0, (float) p_vout->output.i_height );
1638 glVertex3f( - 1.0, - 1.0, - 1.0 );
1639 glTexCoord2f( (float) p_vout->output.i_width,
1640 (float) p_vout->output.i_height );
1641 glVertex3f( - 1.0, - 1.0, 1.0 );
1642 glTexCoord2f( (float) p_vout->output.i_width, 0.0 );
1643 glVertex3f( - 1.0, 1.0, 1.0 );
1646 glTexCoord2f( 0.0, 0.0 );
1647 glVertex3f( 1.0, 1.0, - 1.0 );
1648 glTexCoord2f( 0.0, (float) p_vout->output.i_height );
1649 glVertex3f( 1.0, - 1.0, - 1.0 );
1650 glTexCoord2f( (float) p_vout->output.i_width,
1651 (float) p_vout->output.i_height );
1652 glVertex3f( - 1.0, - 1.0, - 1.0 );
1653 glTexCoord2f( (float) p_vout->output.i_width, 0.0 );
1654 glVertex3f( - 1.0, 1.0, - 1.0 );
1657 glTexCoord2f( 0.0, 0.0 );
1658 glVertex3f( 1.0, 1.0, 1.0 );
1659 glTexCoord2f( 0.0, (float) p_vout->output.i_height );
1660 glVertex3f( 1.0, - 1.0, 1.0 );
1661 glTexCoord2f( (float) p_vout->output.i_width,
1662 (float) p_vout->output.i_height );
1663 glVertex3f( 1.0, - 1.0, - 1.0 );
1664 glTexCoord2f( (float) p_vout->output.i_width, 0.0 );
1665 glVertex3f( 1.0, 1.0, - 1.0 );
1668 glTexCoord2f( 0.0, 0.0 );
1669 glVertex3f( - 1.0, 1.0, - 1.0 );
1670 glTexCoord2f( 0.0, (float) p_vout->output.i_height );
1671 glVertex3f( - 1.0, 1.0, 1.0 );
1672 glTexCoord2f( (float) p_vout->output.i_width,
1673 (float) p_vout->output.i_height );
1674 glVertex3f( 1.0, 1.0, 1.0 );
1675 glTexCoord2f( (float) p_vout->output.i_width, 0.0 );
1676 glVertex3f( 1.0, 1.0, - 1.0 );
1679 glTexCoord2f( 0.0, 0.0 );
1680 glVertex3f( - 1.0, - 1.0, 1.0 );
1681 glTexCoord2f( 0.0, (float) p_vout->output.i_height );
1682 glVertex3f( - 1.0, - 1.0, - 1.0 );
1683 glTexCoord2f( (float) p_vout->output.i_width,
1684 (float) p_vout->output.i_height );
1685 glVertex3f( 1.0, - 1.0, - 1.0 );
1686 glTexCoord2f( (float) p_vout->output.i_width, 0.0 );
1687 glVertex3f( 1.0, - 1.0, 1.0 );
1691 - (void) drawRect: (NSRect) rect
1693 [[self openGLContext] makeCurrentContext];
1695 /* Black background */
1696 glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
1705 glBindTexture( GL_TEXTURE_RECTANGLE_EXT,
1706 pi_textures[p_vout->p_sys->i_cur_pic] );
1707 if( i_effect & ( OPENGL_EFFECT_CUBE |
1708 OPENGL_EFFECT_TRANSPARENT_CUBE ) )
1710 glRotatef( 1.0, 0.3, 0.5, 0.7 );
1724 - (void)mouseMoved:(NSEvent *)o_event
1730 s_rect = [self bounds];
1731 ml = [self convertPoint: [o_event locationInWindow] fromView: nil];
1732 b_inside = [self mouse: ml inRect: s_rect];
1737 int i_width, i_height, i_x, i_y;
1739 vout_PlacePicture( p_vout, (unsigned int)s_rect.size.width,
1740 (unsigned int)s_rect.size.height,
1741 &i_x, &i_y, &i_width, &i_height );
1743 val.i_int = ( (int)ml.x - i_x ) *
1744 p_vout->render.i_width / i_width;
1745 var_Set( p_vout, "mouse-x", val );
1747 /* Y coordinate is inverted in OpenGL */
1748 val.i_int = ( ((int)(s_rect.size.height - ml.y)) - i_y ) *
1749 p_vout->render.i_height / i_height;
1750 var_Set( p_vout, "mouse-y", val );
1752 val.b_bool = VLC_TRUE;
1753 var_Set( p_vout, "mouse-moved", val );
1754 p_vout->p_sys->i_time_mouse_last_moved = mdate();
1755 p_vout->p_sys->b_mouse_moved = YES;
1758 [super mouseMoved: o_event];
1763 /*****************************************************************************
1764 * VLCVout implementation
1765 *****************************************************************************/
1766 @implementation VLCVout
1768 - (void)createWindow:(NSValue *)o_value
1772 NSScreen * o_screen;
1773 vout_thread_t * p_vout;
1774 vlc_bool_t b_main_screen;
1776 p_vout = (vout_thread_t *)[o_value pointerValue];
1778 p_vout->p_sys->o_window = [VLCWindow alloc];
1779 [p_vout->p_sys->o_window setVout: p_vout];
1780 [p_vout->p_sys->o_window setReleasedWhenClosed: YES];
1782 if( var_Get( p_vout, "video-device", &val ) < 0 )
1784 o_screen = [NSScreen mainScreen];
1789 NSArray *o_screens = [NSScreen screens];
1790 unsigned int i_index = val.i_int;
1792 if( [o_screens count] < i_index )
1794 o_screen = [NSScreen mainScreen];
1800 o_screen = [o_screens objectAtIndex: i_index];
1801 config_PutInt( p_vout, "macosx-vdev", i_index );
1802 b_main_screen = (i_index == 0);
1806 if( p_vout->b_fullscreen )
1808 NSRect screen_rect = [o_screen frame];
1809 screen_rect.origin.x = screen_rect.origin.y = 0;
1811 if ( b_main_screen && p_vout->p_sys->p_fullscreen_state == NULL )
1812 BeginFullScreen( &p_vout->p_sys->p_fullscreen_state, NULL, 0, 0,
1813 NULL, NULL, fullScreenAllowEvents );
1815 [p_vout->p_sys->o_window
1816 initWithContentRect: screen_rect
1817 styleMask: NSBorderlessWindowMask
1818 backing: NSBackingStoreBuffered
1819 defer: NO screen: o_screen];
1821 //[p_vout->p_sys->o_window setLevel: NSPopUpMenuWindowLevel - 1];
1822 p_vout->p_sys->b_mouse_moved = YES;
1823 p_vout->p_sys->i_time_mouse_last_moved = mdate();
1827 unsigned int i_stylemask = NSTitledWindowMask |
1828 NSMiniaturizableWindowMask |
1829 NSClosableWindowMask |
1830 NSResizableWindowMask;
1832 if ( p_vout->p_sys->p_fullscreen_state != NULL )
1833 EndFullScreen ( p_vout->p_sys->p_fullscreen_state, NULL );
1834 p_vout->p_sys->p_fullscreen_state = NULL;
1836 [p_vout->p_sys->o_window
1837 initWithContentRect: p_vout->p_sys->s_rect
1838 styleMask: i_stylemask
1839 backing: NSBackingStoreBuffered
1840 defer: NO screen: o_screen];
1842 [p_vout->p_sys->o_window setAlphaValue: config_GetFloat( p_vout, "macosx-opaqueness" )];
1844 if( config_GetInt( p_vout, "video-on-top" ) )
1846 [p_vout->p_sys->o_window setLevel: NSStatusWindowLevel];
1849 if( !p_vout->p_sys->b_pos_saved )
1851 [p_vout->p_sys->o_window center];
1855 if( !p_vout->p_sys->i_opengl )
1857 o_view = [[VLCQTView alloc] init];
1858 /* FIXME: [o_view setMenu:] */
1859 [p_vout->p_sys->o_window setContentView: o_view];
1860 [o_view autorelease];
1863 p_vout->p_sys->p_qdport = [o_view qdPort];
1864 [o_view unlockFocus];
1868 #define o_glview p_vout->p_sys->o_glview
1869 o_glview = [[VLCGLView alloc] initWithFrame: p_vout->p_sys->s_rect vout: p_vout];
1870 [p_vout->p_sys->o_window setContentView: o_glview];
1871 [o_glview autorelease];
1875 [p_vout->p_sys->o_window updateTitle];
1876 [p_vout->p_sys->o_window makeKeyAndOrderFront: nil];
1880 - (void)destroyWindow:(NSValue *)o_value
1882 vout_thread_t * p_vout;
1884 p_vout = (vout_thread_t *)[o_value pointerValue];
1886 if( !p_vout->b_fullscreen )
1890 s_rect = [[p_vout->p_sys->o_window contentView] frame];
1891 p_vout->p_sys->s_rect.size = s_rect.size;
1893 s_rect = [p_vout->p_sys->o_window frame];
1894 p_vout->p_sys->s_rect.origin = s_rect.origin;
1896 p_vout->p_sys->b_pos_saved = YES;
1899 p_vout->p_sys->p_qdport = nil;
1900 [p_vout->p_sys->o_window close];
1901 p_vout->p_sys->o_window = nil;