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 p_vout->p_sys->b_mouse_moved = VLC_TRUE;
123 p_vout->p_sys->i_time_mouse_last_moved = mdate();
125 /* set window size */
126 p_vout->p_sys->s_rect.size.width = p_vout->i_window_width;
127 p_vout->p_sys->s_rect.size.height = p_vout->i_window_height;
129 /* Check if we should use QuickTime or OpenGL */
130 psz_vout_type = config_GetPsz( p_vout, "macosx-vout" );
132 if( !strncmp( psz_vout_type, "auto", 4 ) )
134 p_vout->p_sys->i_opengl = CGDisplayUsesOpenGLAcceleration( kCGDirectMainDisplay );
136 else if( !strncmp( psz_vout_type, "opengl", 6 ) )
138 p_vout->p_sys->i_opengl = VLC_TRUE;
142 p_vout->p_sys->i_opengl = VLC_FALSE;
144 free( psz_vout_type );
146 if( !p_vout->p_sys->i_opengl )
148 /* Initialize QuickTime */
149 p_vout->p_sys->h_img_descr =
150 (ImageDescriptionHandle)NewHandleClear( sizeof(ImageDescription) );
151 p_vout->p_sys->p_matrix =
152 (MatrixRecordPtr)malloc( sizeof(MatrixRecord) );
153 p_vout->p_sys->p_fullscreen_state = NULL;
155 if( ( err = EnterMovies() ) != noErr )
157 msg_Err( p_vout, "EnterMovies failed: %d", err );
158 free( p_vout->p_sys->p_matrix );
159 DisposeHandle( (Handle)p_vout->p_sys->h_img_descr );
160 free( p_vout->p_sys );
164 /* Damn QT isn't thread safe. so keep a lock in the p_vlc object */
165 vlc_mutex_lock( &p_vout->p_vlc->quicktime_lock );
167 err = FindCodec( kYUV420CodecType, bestSpeedCodec,
168 nil, &p_vout->p_sys->img_dc );
170 vlc_mutex_unlock( &p_vout->p_vlc->quicktime_lock );
171 if( err == noErr && p_vout->p_sys->img_dc != 0 )
173 p_vout->output.i_chroma = VLC_FOURCC('I','4','2','0');
174 p_vout->p_sys->i_codec = kYUV420CodecType;
178 msg_Err( p_vout, "failed to find an appropriate codec" );
181 if( p_vout->p_sys->img_dc == 0 )
183 free( p_vout->p_sys->p_matrix );
184 DisposeHandle( (Handle)p_vout->p_sys->h_img_descr );
185 free( p_vout->p_sys );
188 msg_Dbg( p_vout, "using Quartz mode" );
192 msg_Dbg( p_vout, "using OpenGL mode" );
195 NSAutoreleasePool * o_pool = [[NSAutoreleasePool alloc] init];
196 NSArray * o_screens = [NSScreen screens];
197 if( [o_screens count] > 0 && var_Type( p_vout, "video-device" ) == 0 )
200 vlc_value_t val, text;
203 int i_option = config_GetInt( p_vout, "macosx-vdev" );
205 var_Create( p_vout, "video-device", VLC_VAR_INTEGER |
207 text.psz_string = _("Video device");
208 var_Change( p_vout, "video-device", VLC_VAR_SETTEXT, &text, NULL );
210 NSEnumerator * o_enumerator = [o_screens objectEnumerator];
212 while( (o_screen = [o_enumerator nextObject]) != NULL )
215 NSRect s_rect = [o_screen frame];
217 snprintf( psz_temp, sizeof(psz_temp)/sizeof(psz_temp[0])-1,
218 "%s %d (%dx%d)", _("Screen"), i,
219 (int)s_rect.size.width, (int)s_rect.size.height );
221 text.psz_string = psz_temp;
223 var_Change( p_vout, "video-device",
224 VLC_VAR_ADDCHOICE, &val, &text );
226 if( ( i - 1 ) == i_option )
228 var_Set( p_vout, "video-device", val );
233 var_AddCallback( p_vout, "video-device", vout_VarCallback,
236 val.b_bool = VLC_TRUE;
237 var_Set( p_vout, "intf-change", val );
241 if( CoCreateWindow( p_vout ) )
243 msg_Err( p_vout, "unable to create window" );
244 if( !p_vout->p_sys->i_opengl )
246 free( p_vout->p_sys->p_matrix );
247 DisposeHandle( (Handle)p_vout->p_sys->h_img_descr );
249 free( p_vout->p_sys );
253 p_vout->pf_init = vout_Init;
254 p_vout->pf_end = vout_End;
255 p_vout->pf_manage = vout_Manage;
256 p_vout->pf_render = NULL;
257 p_vout->pf_display = vout_Display;
262 /*****************************************************************************
263 * vout_Init: initialize video thread output method
264 *****************************************************************************/
265 static int vout_InitOpenGL( vout_thread_t *p_vout );
266 static int vout_InitQuickTime( vout_thread_t *p_vout );
267 static int vout_Init( vout_thread_t *p_vout )
269 I_OUTPUTPICTURES = 0;
271 /* Initialize the output structure; we already found a codec,
272 * and the corresponding chroma we will be using. Since we can
273 * arbitrary scale, stick to the coordinates and aspect. */
274 p_vout->output.i_width = p_vout->render.i_width;
275 p_vout->output.i_height = p_vout->render.i_height;
276 p_vout->output.i_aspect = p_vout->render.i_aspect;
278 if( p_vout->p_sys->i_opengl )
280 return vout_InitOpenGL( p_vout );
284 return vout_InitQuickTime( p_vout );
288 static int vout_InitOpenGL( vout_thread_t *p_vout )
294 /* Apple OpenGL extensions only accept YUV as YUY2 */
295 p_vout->output.i_chroma = VLC_FOURCC('Y','U','Y','2');
296 p_vout->output.i_rmask = 0xFF0000;
297 p_vout->output.i_gmask = 0x00FF00;
298 p_vout->output.i_bmask = 0x0000FF;
300 /* Allocate our 2 picture buffers */
301 i_bytes = 2 * p_vout->output.i_width * p_vout->output.i_height;
302 p_vout->p_sys->p_data[0] = vlc_memalign(
303 &p_vout->p_sys->p_data_orig[0], 16, i_bytes );
304 p_vout->p_sys->p_data[1] = vlc_memalign(
305 &p_vout->p_sys->p_data_orig[1], 16, i_bytes );
306 p_vout->p_sys->i_cur_pic = 1;
308 /* We declare only one picture and will switch buffers
311 while( I_OUTPUTPICTURES < 1 )
313 /* Find an empty picture slot */
314 for( i_index = 0; i_index < VOUT_MAX_PICTURES; i_index++ )
316 if( p_vout->p_picture[ i_index ].i_status == FREE_PICTURE )
318 p_pic = p_vout->p_picture + i_index;
327 vout_InitPicture( VLC_OBJECT( p_vout ), p_pic,
328 p_vout->output.i_chroma, p_vout->output.i_width,
329 p_vout->output.i_height, p_vout->output.i_aspect );
330 p_pic->p_data = p_vout->p_sys->p_data[0];
331 p_pic->p[0].p_pixels = p_pic->p_data;
332 for( i_index = 1; i_index < p_pic->i_planes; i_index++ )
334 p_pic->p[i_index].p_pixels =
335 p_pic->p[i_index-1].p_pixels +
336 p_pic->p[i_index-1].i_lines *
337 p_pic->p[i_index-1].i_pitch;
340 p_pic->i_status = DESTROYED_PICTURE;
341 p_pic->i_type = DIRECT_PICTURE;
343 PP_OUTPUTPICTURE[ I_OUTPUTPICTURES ] = p_pic;
347 [p_vout->p_sys->o_glview lockFocus];
348 [p_vout->p_sys->o_glview initTextures];
349 [p_vout->p_sys->o_glview reshape];
350 [p_vout->p_sys->o_glview unlockFocus];
355 static int vout_InitQuickTime( vout_thread_t *p_vout )
360 SetPort( p_vout->p_sys->p_qdport );
361 QTScaleMatrix( p_vout );
363 if( QTCreateSequence( p_vout ) )
365 msg_Err( p_vout, "unable to create sequence" );
369 /* Try to initialize up to QT_MAX_DIRECTBUFFERS direct buffers */
370 while( I_OUTPUTPICTURES < QT_MAX_DIRECTBUFFERS )
374 /* Find an empty picture slot */
375 for( i_index = 0; i_index < VOUT_MAX_PICTURES; i_index++ )
377 if( p_vout->p_picture[ i_index ].i_status == FREE_PICTURE )
379 p_pic = p_vout->p_picture + i_index;
388 /* Allocate the picture */
389 if( QTNewPicture( p_vout, p_pic ) )
394 p_pic->i_status = DESTROYED_PICTURE;
395 p_pic->i_type = DIRECT_PICTURE;
397 PP_OUTPUTPICTURE[ I_OUTPUTPICTURES ] = p_pic;
403 /*****************************************************************************
404 * vout_End: terminate video thread output method
405 *****************************************************************************/
406 static void vout_End( vout_thread_t *p_vout )
410 if( !p_vout->p_sys->i_opengl )
412 QTDestroySequence( p_vout );
415 /* Free the direct buffers we allocated */
416 for( i_index = I_OUTPUTPICTURES; i_index; )
419 if( !p_vout->p_sys->i_opengl )
421 QTFreePicture( p_vout, PP_OUTPUTPICTURE[ i_index ] );
425 free( p_vout->p_sys->p_data_orig[0] );
426 free( p_vout->p_sys->p_data_orig[1] );
431 /*****************************************************************************
432 * CloseVideo: destroy video thread output method
433 *****************************************************************************/
434 void E_(CloseVideo) ( vlc_object_t *p_this )
436 vout_thread_t * p_vout = (vout_thread_t *)p_this;
438 if( p_vout->p_sys->i_opengl )
440 [p_vout->p_sys->o_glview cleanUp];
443 if( CoDestroyWindow( p_vout ) )
445 msg_Err( p_vout, "unable to destroy window" );
448 if ( p_vout->p_sys->p_fullscreen_state != NULL )
450 EndFullScreen ( p_vout->p_sys->p_fullscreen_state, NULL );
453 if( !p_vout->p_sys->i_opengl )
456 free( p_vout->p_sys->p_matrix );
457 DisposeHandle( (Handle)p_vout->p_sys->h_img_descr );
460 free( p_vout->p_sys );
463 /*****************************************************************************
464 * vout_Manage: handle events
465 *****************************************************************************
466 * This function should be called regularly by video output thread. It manages
467 * console events. It returns a non null value on error.
468 *****************************************************************************/
469 static int vout_Manage( vout_thread_t *p_vout )
471 if( p_vout->i_changes & VOUT_FULLSCREEN_CHANGE )
473 if( CoToggleFullscreen( p_vout ) )
478 p_vout->i_changes &= ~VOUT_FULLSCREEN_CHANGE;
481 if( p_vout->i_changes & VOUT_SIZE_CHANGE )
483 if( !p_vout->p_sys->i_opengl )
485 QTScaleMatrix( p_vout );
486 SetDSequenceMatrix( p_vout->p_sys->i_seq,
487 p_vout->p_sys->p_matrix );
490 p_vout->i_changes &= ~VOUT_SIZE_CHANGE;
493 /* hide/show mouse cursor
494 * this code looks unnecessarily complicated, but is necessary like this.
495 * it has to deal with multiple monitors and therefore checks a lot */
496 if( !p_vout->p_sys->b_mouse_moved && p_vout->b_fullscreen )
498 if( mdate() - p_vout->p_sys->i_time_mouse_last_moved > 3000000 )
500 VLCHideMouse( p_vout, YES );
503 else if ( p_vout->p_sys->b_mouse_moved && p_vout->b_fullscreen )
505 VLCHideMouse( p_vout, NO );
508 /* disable screen saver */
509 UpdateSystemActivity( UsrActivity );
514 /*****************************************************************************
515 * vout_Display: displays previously rendered output
516 *****************************************************************************
517 * This function sends the currently rendered image to the display.
518 *****************************************************************************/
519 static void vout_Display( vout_thread_t *p_vout, picture_t *p_pic )
521 if( !p_vout->p_sys->i_opengl )
526 if( ( err = DecompressSequenceFrameS(
527 p_vout->p_sys->i_seq,
528 p_pic->p_sys->p_info,
529 p_pic->p_sys->i_size,
530 codecFlagUseImageBuffer, &flags, nil ) != noErr ) )
532 msg_Warn( p_vout, "DecompressSequenceFrameS failed: %d", err );
536 QDFlushPortBuffer( p_vout->p_sys->p_qdport, nil );
541 if( [p_vout->p_sys->o_glview lockFocusIfCanDraw] )
544 int i_old = p_vout->p_sys->i_cur_pic;
545 int i_new = ( i_old + 1 ) % 2;
547 /* Draw the new picture */
548 p_vout->p_sys->i_cur_pic = i_new;
549 [p_vout->p_sys->o_glview drawRect:
550 [p_vout->p_sys->o_glview bounds]];
552 /* Reload the other texture. Textures have to be reloaded
553 before the buffer is filled (thanks to gcc from
554 arstechnica forums) */
555 [p_vout->p_sys->o_glview reloadTexture: i_old];
558 p_pic->p_data = p_vout->p_sys->p_data[i_old];
559 p_pic->p[0].p_pixels = p_pic->p_data;
560 for( i_index = 1; i_index < p_pic->i_planes; i_index++ )
562 p_pic->p[i_index].p_pixels =
563 p_pic->p[i_index-1].p_pixels +
564 p_pic->p[i_index-1].i_lines *
565 p_pic->p[i_index-1].i_pitch;
567 [p_vout->p_sys->o_glview unlockFocus];
572 /*****************************************************************************
573 * CoSendRequest: send request to interface thread
574 *****************************************************************************
575 * Returns 0 on success, 1 otherwise
576 *****************************************************************************/
577 static int CoSendRequest( vout_thread_t *p_vout, SEL sel )
581 intf_thread_t * p_intf;
583 VLCVout * o_vlv = [[VLCVout alloc] init];
585 if( ( i_ret = ExecuteOnMainThread( o_vlv, sel, (void *)p_vout ) ) )
587 msg_Err( p_vout, "SendRequest: no way to communicate with mt" );
592 /*This makes this function dependant of the presence of a macosx
593 interface. We do not check if this interface exists, since it has
594 already been done before.*/
598 val.b_bool = VLC_TRUE;
599 var_Create(p_intf,"intf-change",VLC_VAR_BOOL);
600 var_Set(p_intf, "intf-change",val);
605 /*****************************************************************************
606 * CoCreateWindow: create new window
607 *****************************************************************************
608 * Returns 0 on success, 1 otherwise
609 *****************************************************************************/
610 static int CoCreateWindow( vout_thread_t *p_vout )
612 if( CoSendRequest( p_vout, @selector(createWindow:) ) )
614 msg_Err( p_vout, "CoSendRequest (createWindow) failed" );
621 /*****************************************************************************
622 * CoDestroyWindow: destroy window
623 *****************************************************************************
624 * Returns 0 on success, 1 otherwise
625 *****************************************************************************/
626 static int CoDestroyWindow( vout_thread_t *p_vout )
629 VLCHideMouse( p_vout, NO );
631 if( CoSendRequest( p_vout, @selector(destroyWindow:) ) )
633 msg_Err( p_vout, "CoSendRequest (destroyWindow) failed" );
640 /*****************************************************************************
641 * CoToggleFullscreen: toggle fullscreen
642 *****************************************************************************
643 * Returns 0 on success, 1 otherwise
644 *****************************************************************************/
645 static int CoToggleFullscreen( vout_thread_t *p_vout )
647 if( !p_vout->p_sys->i_opengl )
649 QTDestroySequence( p_vout );
652 if( CoDestroyWindow( p_vout ) )
654 msg_Err( p_vout, "unable to destroy window" );
658 p_vout->b_fullscreen = !p_vout->b_fullscreen;
660 if( CoCreateWindow( p_vout ) )
662 msg_Err( p_vout, "unable to create window" );
666 if( p_vout->p_sys->i_opengl )
668 [p_vout->p_sys->o_glview lockFocus];
669 [p_vout->p_sys->o_glview initTextures];
670 [p_vout->p_sys->o_glview reshape];
671 [p_vout->p_sys->o_glview drawRect:
672 [p_vout->p_sys->o_glview bounds]];
673 [p_vout->p_sys->o_glview unlockFocus];
677 SetPort( p_vout->p_sys->p_qdport );
678 QTScaleMatrix( p_vout );
680 if( QTCreateSequence( p_vout ) )
682 msg_Err( p_vout, "unable to create sequence" );
690 /*****************************************************************************
691 * VLCHideMouse: if b_hide then hide the cursor
692 *****************************************************************************/
693 static void VLCHideMouse ( vout_thread_t *p_vout, BOOL b_hide )
698 NSWindow *o_window = p_vout->p_sys->o_window;
699 NSView *o_contents = [o_window contentView];
701 s_rect = [o_contents bounds];
702 ml = [o_window convertScreenToBase:[NSEvent mouseLocation]];
703 ml = [o_contents convertPoint:ml fromView:nil];
704 b_inside = [o_contents mouse: ml inRect: s_rect];
706 if ( b_hide && b_inside )
708 /* only hide if mouse over VLCQTView */
709 [NSCursor setHiddenUntilMouseMoves: YES];
713 [NSCursor setHiddenUntilMouseMoves: NO];
715 p_vout->p_sys->b_mouse_moved = NO;
716 p_vout->p_sys->i_time_mouse_last_moved = mdate();
720 /*****************************************************************************
721 * QTScaleMatrix: scale matrix
722 *****************************************************************************/
723 static void QTScaleMatrix( vout_thread_t *p_vout )
726 unsigned int i_width, i_height;
727 Fixed factor_x, factor_y;
728 unsigned int i_offset_x = 0;
729 unsigned int i_offset_y = 0;
731 GetPortBounds( p_vout->p_sys->p_qdport, &s_rect );
733 i_width = s_rect.right - s_rect.left;
734 i_height = s_rect.bottom - s_rect.top;
736 if( config_GetInt( p_vout, "macosx-stretch" ) )
738 factor_x = FixDiv( Long2Fix( i_width ),
739 Long2Fix( p_vout->output.i_width ) );
740 factor_y = FixDiv( Long2Fix( i_height ),
741 Long2Fix( p_vout->output.i_height ) );
744 else if( i_height * p_vout->output.i_aspect < i_width * VOUT_ASPECT_FACTOR )
746 int i_adj_width = i_height * p_vout->output.i_aspect /
749 factor_x = FixDiv( Long2Fix( i_adj_width ),
750 Long2Fix( p_vout->output.i_width ) );
751 factor_y = FixDiv( Long2Fix( i_height ),
752 Long2Fix( p_vout->output.i_height ) );
754 i_offset_x = (i_width - i_adj_width) / 2;
758 int i_adj_height = i_width * VOUT_ASPECT_FACTOR /
759 p_vout->output.i_aspect;
761 factor_x = FixDiv( Long2Fix( i_width ),
762 Long2Fix( p_vout->output.i_width ) );
763 factor_y = FixDiv( Long2Fix( i_adj_height ),
764 Long2Fix( p_vout->output.i_height ) );
766 i_offset_y = (i_height - i_adj_height) / 2;
769 SetIdentityMatrix( p_vout->p_sys->p_matrix );
771 ScaleMatrix( p_vout->p_sys->p_matrix,
773 Long2Fix(0), Long2Fix(0) );
775 TranslateMatrix( p_vout->p_sys->p_matrix,
776 Long2Fix(i_offset_x), Long2Fix(i_offset_y) );
779 /*****************************************************************************
780 * QTCreateSequence: create a new sequence
781 *****************************************************************************
782 * Returns 0 on success, 1 otherwise
783 *****************************************************************************/
784 static int QTCreateSequence( vout_thread_t *p_vout )
787 ImageDescriptionPtr p_descr;
789 HLock( (Handle)p_vout->p_sys->h_img_descr );
790 p_descr = *p_vout->p_sys->h_img_descr;
792 p_descr->idSize = sizeof(ImageDescription);
793 p_descr->cType = p_vout->p_sys->i_codec;
794 p_descr->version = 1;
795 p_descr->revisionLevel = 0;
796 p_descr->vendor = 'appl';
797 p_descr->width = p_vout->output.i_width;
798 p_descr->height = p_vout->output.i_height;
799 p_descr->hRes = Long2Fix(72);
800 p_descr->vRes = Long2Fix(72);
801 p_descr->spatialQuality = codecLosslessQuality;
802 p_descr->frameCount = 1;
803 p_descr->clutID = -1;
804 p_descr->dataSize = 0;
807 HUnlock( (Handle)p_vout->p_sys->h_img_descr );
809 if( ( err = DecompressSequenceBeginS(
810 &p_vout->p_sys->i_seq,
811 p_vout->p_sys->h_img_descr,
813 p_vout->p_sys->p_qdport,
815 p_vout->p_sys->p_matrix,
817 codecFlagUseImageBuffer,
818 codecLosslessQuality,
819 p_vout->p_sys->img_dc ) ) )
821 msg_Err( p_vout, "DecompressSequenceBeginS failed: %d", err );
828 /*****************************************************************************
829 * QTDestroySequence: destroy sequence
830 *****************************************************************************/
831 static void QTDestroySequence( vout_thread_t *p_vout )
833 CDSequenceEnd( p_vout->p_sys->i_seq );
836 /*****************************************************************************
837 * QTNewPicture: allocate a picture
838 *****************************************************************************
839 * Returns 0 on success, 1 otherwise
840 *****************************************************************************/
841 static int QTNewPicture( vout_thread_t *p_vout, picture_t *p_pic )
843 int i_width = p_vout->output.i_width;
844 int i_height = p_vout->output.i_height;
846 /* We know the chroma, allocate a buffer which will be used
847 * directly by the decoder */
848 p_pic->p_sys = malloc( sizeof( picture_sys_t ) );
850 if( p_pic->p_sys == NULL )
855 switch( p_vout->output.i_chroma )
857 case VLC_FOURCC('I','4','2','0'):
859 p_pic->p_sys->p_info = (void *)&p_pic->p_sys->pixmap_i420;
860 p_pic->p_sys->i_size = sizeof(PlanarPixmapInfoYUV420);
862 /* Allocate the memory buffer */
863 p_pic->p_data = vlc_memalign( &p_pic->p_data_orig,
864 16, i_width * i_height * 3 / 2 );
867 p_pic->Y_PIXELS = p_pic->p_data;
868 p_pic->p[Y_PLANE].i_lines = i_height;
869 p_pic->p[Y_PLANE].i_pitch = i_width;
870 p_pic->p[Y_PLANE].i_pixel_pitch = 1;
871 p_pic->p[Y_PLANE].i_visible_pitch = i_width;
874 p_pic->U_PIXELS = p_pic->Y_PIXELS + i_height * i_width;
875 p_pic->p[U_PLANE].i_lines = i_height / 2;
876 p_pic->p[U_PLANE].i_pitch = i_width / 2;
877 p_pic->p[U_PLANE].i_pixel_pitch = 1;
878 p_pic->p[U_PLANE].i_visible_pitch = i_width / 2;
881 p_pic->V_PIXELS = p_pic->U_PIXELS + i_height * i_width / 4;
882 p_pic->p[V_PLANE].i_lines = i_height / 2;
883 p_pic->p[V_PLANE].i_pitch = i_width / 2;
884 p_pic->p[V_PLANE].i_pixel_pitch = 1;
885 p_pic->p[V_PLANE].i_visible_pitch = i_width / 2;
887 /* We allocated 3 planes */
890 #define P p_pic->p_sys->pixmap_i420
891 P.componentInfoY.offset = (void *)p_pic->Y_PIXELS
892 - p_pic->p_sys->p_info;
893 P.componentInfoCb.offset = (void *)p_pic->U_PIXELS
894 - p_pic->p_sys->p_info;
895 P.componentInfoCr.offset = (void *)p_pic->V_PIXELS
896 - p_pic->p_sys->p_info;
898 P.componentInfoY.rowBytes = i_width;
899 P.componentInfoCb.rowBytes = i_width / 2;
900 P.componentInfoCr.rowBytes = i_width / 2;
906 /* Unknown chroma, tell the guy to get lost */
907 free( p_pic->p_sys );
908 msg_Err( p_vout, "never heard of chroma 0x%.8x (%4.4s)",
909 p_vout->output.i_chroma, (char*)&p_vout->output.i_chroma );
917 /*****************************************************************************
918 * QTFreePicture: destroy a picture allocated with QTNewPicture
919 *****************************************************************************/
920 static void QTFreePicture( vout_thread_t *p_vout, picture_t *p_pic )
922 switch( p_vout->output.i_chroma )
924 case VLC_FOURCC('I','4','2','0'):
925 free( p_pic->p_data_orig );
929 free( p_pic->p_sys );
932 /*****************************************************************************
933 * VLCWindow implementation
934 *****************************************************************************/
935 @implementation VLCWindow
937 - (void)setVout:(vout_thread_t *)_p_vout
942 - (vout_thread_t *)getVout
947 - (void)scaleWindowWithFactor: (float)factor
950 int i_corrected_height, i_corrected_width;
952 NSPoint topleftscreen;
954 if ( !p_vout->b_fullscreen )
957 topleftbase.y = [self frame].size.height;
958 topleftscreen = [self convertBaseToScreen: topleftbase];
960 if( p_vout->output.i_height * p_vout->output.i_aspect >
961 p_vout->output.i_width * VOUT_ASPECT_FACTOR )
963 i_corrected_width = p_vout->output.i_height * p_vout->output.i_aspect /
965 newsize.width = (int) ( i_corrected_width * factor );
966 newsize.height = (int) ( p_vout->render.i_height * factor );
970 i_corrected_height = p_vout->output.i_width * VOUT_ASPECT_FACTOR /
971 p_vout->output.i_aspect;
972 newsize.width = (int) ( p_vout->render.i_width * factor );
973 newsize.height = (int) ( i_corrected_height * factor );
976 [self setContentSize: newsize];
978 [self setFrameTopLeftPoint: topleftscreen];
979 p_vout->i_changes |= VOUT_SIZE_CHANGE;
983 - (void)toggleFloatOnTop
986 if( var_Get( p_vout, "video-on-top", &val )>=0 && val.b_bool)
988 val.b_bool = VLC_FALSE;
989 var_Set( p_vout, "video-on-top", val );
990 [p_vout->p_sys->o_window setLevel: NSNormalWindowLevel];
994 val.b_bool = VLC_TRUE;
995 var_Set( p_vout, "video-on-top", val );
996 [p_vout->p_sys->o_window setLevel: NSStatusWindowLevel];
1000 - (void)toggleFullscreen
1003 val.b_bool = !p_vout->b_fullscreen;
1004 var_Set( p_vout, "fullscreen", val );
1007 - (BOOL)isFullscreen
1009 return( p_vout->b_fullscreen );
1012 - (BOOL)canBecomeKeyWindow
1017 - (BOOL)performKeyEquivalent:(NSEvent *)o_event
1019 return [[VLCMain sharedInstance] hasDefinedShortcutKey:o_event];
1022 - (void)keyDown:(NSEvent *)o_event
1026 unsigned int i_pressed_modifiers = 0;
1029 i_pressed_modifiers = [o_event modifierFlags];
1031 if( i_pressed_modifiers & NSShiftKeyMask )
1032 val.i_int |= KEY_MODIFIER_SHIFT;
1033 if( i_pressed_modifiers & NSControlKeyMask )
1034 val.i_int |= KEY_MODIFIER_CTRL;
1035 if( i_pressed_modifiers & NSAlternateKeyMask )
1036 val.i_int |= KEY_MODIFIER_ALT;
1037 if( i_pressed_modifiers & NSCommandKeyMask )
1038 val.i_int |= KEY_MODIFIER_COMMAND;
1040 key = [[o_event charactersIgnoringModifiers] characterAtIndex: 0];
1044 /* Escape should always get you out of fullscreen */
1045 if( key == (unichar) 0x1b )
1047 if( [self isFullscreen] )
1049 [self toggleFullscreen];
1052 else if ( key == ' ' )
1054 playlist_t *p_playlist = vlc_object_find( p_vout, VLC_OBJECT_PLAYLIST,
1056 if ( p_playlist != NULL )
1058 playlist_Pause( p_playlist );
1059 vlc_object_release( p_playlist);
1064 val.i_int |= CocoaKeyToVLC( key );
1065 var_Set( p_vout->p_vlc, "key-pressed", val );
1070 [super keyDown: o_event];
1076 NSMutableString * o_title;
1077 playlist_t * p_playlist = vlc_object_find( p_vout, VLC_OBJECT_PLAYLIST,
1080 if( p_playlist == NULL )
1085 vlc_mutex_lock( &p_playlist->object_lock );
1086 o_title = [NSMutableString stringWithUTF8String:
1087 p_playlist->pp_items[p_playlist->i_index]->input.psz_uri];
1088 vlc_mutex_unlock( &p_playlist->object_lock );
1090 vlc_object_release( p_playlist );
1092 if( o_title != nil )
1094 NSRange prefix_range = [o_title rangeOfString: @"file:"];
1095 if( prefix_range.location != NSNotFound )
1097 [o_title deleteCharactersInRange: prefix_range];
1100 [self setTitleWithRepresentedFilename: o_title];
1105 [NSString stringWithCString: VOUT_TITLE " (QuickTime)"]];
1109 /* This is actually the same as VLCControls::stop. */
1110 - (BOOL)windowShouldClose:(id)sender
1112 playlist_t * p_playlist = vlc_object_find( p_vout, VLC_OBJECT_PLAYLIST,
1114 if( p_playlist == NULL )
1119 playlist_Stop( p_playlist );
1120 vlc_object_release( p_playlist );
1122 /* The window will be closed by the intf later. */
1128 /* Common QT and OpenGL code to catch mouse events */
1129 #define CATCH_MOUSE_EVENTS \
1130 - (BOOL)acceptsFirstResponder \
1135 - (BOOL)becomeFirstResponder \
1137 id o_window = [self window]; \
1139 [o_window setAcceptsMouseMovedEvents: YES]; \
1143 - (BOOL)resignFirstResponder \
1145 vout_thread_t * vout; \
1146 id o_window = [self window]; \
1147 vout = (vout_thread_t *)[o_window getVout]; \
1149 [o_window setAcceptsMouseMovedEvents: NO]; \
1150 VLCHideMouse( vout, NO ); \
1154 - (void)mouseDown:(NSEvent *)o_event \
1156 vout_thread_t * vout; \
1157 id o_window = [self window]; \
1158 vout = (vout_thread_t *)[o_window getVout]; \
1161 switch( [o_event type] ) \
1163 case NSLeftMouseDown: \
1165 var_Get( vout, "mouse-button-down", &val ); \
1167 var_Set( vout, "mouse-button-down", val ); \
1172 [super mouseDown: o_event]; \
1177 - (void)otherMouseDown:(NSEvent *)o_event \
1179 vout_thread_t * vout; \
1180 id o_window = [self window]; \
1181 vout = (vout_thread_t *)[o_window getVout]; \
1184 switch( [o_event type] ) \
1186 case NSOtherMouseDown: \
1188 var_Get( vout, "mouse-button-down", &val ); \
1190 var_Set( vout, "mouse-button-down", val ); \
1195 [super mouseDown: o_event]; \
1200 - (void)rightMouseDown:(NSEvent *)o_event \
1202 vout_thread_t * vout; \
1203 id o_window = [self window]; \
1204 vout = (vout_thread_t *)[o_window getVout]; \
1207 switch( [o_event type] ) \
1209 case NSRightMouseDown: \
1211 var_Get( vout, "mouse-button-down", &val ); \
1213 var_Set( vout, "mouse-button-down", val ); \
1218 [super mouseDown: o_event]; \
1223 - (void)mouseUp:(NSEvent *)o_event \
1225 vout_thread_t * vout; \
1226 id o_window = [self window]; \
1227 vout = (vout_thread_t *)[o_window getVout]; \
1230 switch( [o_event type] ) \
1232 case NSLeftMouseUp: \
1234 vlc_value_t b_val; \
1235 b_val.b_bool = VLC_TRUE; \
1236 var_Set( vout, "mouse-clicked", b_val ); \
1238 var_Get( vout, "mouse-button-down", &val ); \
1240 var_Set( vout, "mouse-button-down", val ); \
1245 [super mouseUp: o_event]; \
1250 - (void)otherMouseUp:(NSEvent *)o_event \
1252 vout_thread_t * vout; \
1253 id o_window = [self window]; \
1254 vout = (vout_thread_t *)[o_window getVout]; \
1257 switch( [o_event type] ) \
1259 case NSOtherMouseUp: \
1261 var_Get( vout, "mouse-button-down", &val ); \
1263 var_Set( vout, "mouse-button-down", val ); \
1268 [super mouseUp: o_event]; \
1273 - (void)rightMouseUp:(NSEvent *)o_event \
1275 vout_thread_t * vout; \
1276 id o_window = [self window]; \
1277 vout = (vout_thread_t *)[o_window getVout]; \
1280 switch( [o_event type] ) \
1282 case NSRightMouseUp: \
1284 var_Get( vout, "mouse-button-down", &val ); \
1286 var_Set( vout, "mouse-button-down", val ); \
1291 [super mouseUp: o_event]; \
1296 - (void)mouseDragged:(NSEvent *)o_event \
1298 [self mouseMoved:o_event]; \
1301 - (void)otherMouseDragged:(NSEvent *)o_event \
1303 [self mouseMoved:o_event]; \
1306 - (void)rightMouseDragged:(NSEvent *)o_event \
1308 [self mouseMoved:o_event]; \
1311 /*****************************************************************************
1312 * VLCQTView implementation
1313 *****************************************************************************/
1314 @implementation VLCQTView
1316 - (void)drawRect:(NSRect)rect
1318 vout_thread_t * p_vout;
1319 id o_window = [self window];
1320 p_vout = (vout_thread_t *)[o_window getVout];
1322 [[NSColor blackColor] set];
1324 [super drawRect: rect];
1326 p_vout->i_changes |= VOUT_SIZE_CHANGE;
1331 - (void)mouseMoved:(NSEvent *)o_event
1337 vout_thread_t * p_vout;
1338 id o_window = [self window];
1339 p_vout = (vout_thread_t *)[o_window getVout];
1341 s_rect = [self bounds];
1342 ml = [self convertPoint: [o_event locationInWindow] fromView: nil];
1343 b_inside = [self mouse: ml inRect: s_rect];
1348 int i_width, i_height, i_x, i_y;
1350 vout_PlacePicture( p_vout, (unsigned int)s_rect.size.width,
1351 (unsigned int)s_rect.size.height,
1352 &i_x, &i_y, &i_width, &i_height );
1354 val.i_int = ( ((int)ml.x) - i_x ) *
1355 p_vout->render.i_width / i_width;
1356 var_Set( p_vout, "mouse-x", val );
1358 val.i_int = ( ((int)ml.y) - i_y ) *
1359 p_vout->render.i_height / i_height;
1360 var_Set( p_vout, "mouse-y", val );
1362 val.b_bool = VLC_TRUE;
1363 var_Set( p_vout, "mouse-moved", val );
1364 p_vout->p_sys->i_time_mouse_last_moved = mdate();
1365 p_vout->p_sys->b_mouse_moved = YES;
1368 [super mouseMoved: o_event];
1373 /*****************************************************************************
1374 * VLCGLView implementation
1375 *****************************************************************************/
1376 @implementation VLCGLView
1379 - (id) initWithFrame: (NSRect) frame vout: (vout_thread_t*) _p_vout
1384 NSOpenGLPixelFormatAttribute attribs[] =
1386 NSOpenGLPFAAccelerated,
1387 NSOpenGLPFANoRecovery,
1388 NSOpenGLPFAColorSize, 24,
1389 NSOpenGLPFAAlphaSize, 8,
1390 NSOpenGLPFADepthSize, 24,
1395 NSOpenGLPixelFormat * fmt = [[NSOpenGLPixelFormat alloc]
1396 initWithAttributes: attribs];
1400 msg_Warn( p_vout, "Cannot create NSOpenGLPixelFormat" );
1404 self = [super initWithFrame:frame pixelFormat: fmt];
1407 [[self openGLContext] makeCurrentContext];
1408 [[self openGLContext] update];
1410 /* Black background */
1411 glClearColor( 0.0, 0.0, 0.0, 0.0 );
1413 /* Check if the user asked for useless visual effects */
1414 psz_effect = config_GetPsz( p_vout, "macosx-opengl-effect" );
1415 if( !psz_effect || !strcmp( psz_effect, "none" ))
1417 i_effect = OPENGL_EFFECT_NONE;
1419 else if( !strcmp( psz_effect, "cube" ) )
1421 i_effect = OPENGL_EFFECT_CUBE;
1423 glEnable( GL_DEPTH_TEST );
1425 else if( !strcmp( psz_effect, "transparent-cube" ) )
1427 i_effect = OPENGL_EFFECT_TRANSPARENT_CUBE;
1429 glDisable( GL_DEPTH_TEST );
1430 glEnable( GL_BLEND );
1431 glBlendFunc( GL_SRC_ALPHA, GL_ONE );
1435 msg_Warn( p_vout, "no valid opengl effect provided, using "
1437 i_effect = OPENGL_EFFECT_NONE;
1440 if( i_effect & ( OPENGL_EFFECT_CUBE |
1441 OPENGL_EFFECT_TRANSPARENT_CUBE ) )
1443 /* Set the perpective */
1444 glMatrixMode( GL_PROJECTION );
1446 glFrustum( -1.0, 1.0, -1.0, 1.0, 3.0, 20.0 );
1447 glMatrixMode( GL_MODELVIEW );
1449 glTranslatef( 0.0, 0.0, - 5.0 );
1466 [[self openGLContext] makeCurrentContext];
1468 NSRect bounds = [self bounds];
1469 glViewport( 0, 0, (GLint) bounds.size.width,
1470 (GLint) bounds.size.height );
1472 if( config_GetInt( p_vout, "macosx-stretch" ) )
1479 /* Quad size is set in order to preserve the aspect ratio */
1480 int fill = ( config_GetInt( p_vout, "macosx-fill" ) &&
1481 p_vout->b_fullscreen );
1482 int large = ( bounds.size.height * p_vout->output.i_aspect <
1483 bounds.size.width * VOUT_ASPECT_FACTOR );
1484 if( ( large && !fill ) || ( !large && fill ) )
1486 f_x = bounds.size.height * p_vout->output.i_aspect /
1487 VOUT_ASPECT_FACTOR / bounds.size.width;
1493 f_y = bounds.size.width * VOUT_ASPECT_FACTOR /
1494 p_vout->output.i_aspect / bounds.size.height;
1499 - (void) initTextures
1502 [[self openGLContext] makeCurrentContext];
1504 /* Free previous texture if any */
1507 glDeleteTextures( 2, pi_textures );
1510 glEnable( GL_TEXTURE_RECTANGLE_EXT );
1511 glEnable( GL_UNPACK_CLIENT_STORAGE_APPLE );
1513 glPixelStorei( GL_UNPACK_ALIGNMENT, 1 );
1514 glPixelStorei( GL_UNPACK_ROW_LENGTH, p_vout->output.i_width );
1516 /* Tell the driver not to make a copy of the texture but to use
1518 glPixelStorei( GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE );
1520 /* Create textures */
1521 glGenTextures( 2, pi_textures );
1523 for( i = 0; i < 2; i++ )
1525 glBindTexture( GL_TEXTURE_RECTANGLE_EXT, pi_textures[i] );
1526 glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE );
1528 /* Linear interpolation */
1529 glTexParameteri( GL_TEXTURE_RECTANGLE_EXT,
1530 GL_TEXTURE_MIN_FILTER, GL_LINEAR );
1531 glTexParameteri( GL_TEXTURE_RECTANGLE_EXT,
1532 GL_TEXTURE_MAG_FILTER, GL_LINEAR );
1534 /* Use VRAM texturing */
1535 glTexParameteri( GL_TEXTURE_RECTANGLE_EXT,
1536 GL_TEXTURE_STORAGE_HINT_APPLE, GL_STORAGE_CACHED_APPLE );
1538 glTexImage2D( GL_TEXTURE_RECTANGLE_EXT, 0, GL_RGB8,
1539 p_vout->output.i_width, p_vout->output.i_height, 0,
1540 GL_YCBCR_422_APPLE, GL_UNSIGNED_SHORT_8_8_APPLE,
1541 p_vout->p_sys->p_data[i] );
1544 /* Swap buffers only during the vertical retrace of the monitor.
1545 http://developer.apple.com/documentation/GraphicsImaging/
1546 Conceptual/OpenGL/chap5/chapter_5_section_44.html */
1547 long params[] = { 1 };
1548 CGLSetParameter( CGLGetCurrentContext(), kCGLCPSwapInterval,
1554 - (void)reloadTexture: (int) index
1561 [[self openGLContext] makeCurrentContext];
1563 glBindTexture( GL_TEXTURE_RECTANGLE_EXT, pi_textures[index] );
1564 glPixelStorei( GL_UNPACK_ROW_LENGTH, p_vout->output.i_width );
1566 /* glTexSubImage2D is faster than glTexImage2D
1567 http://developer.apple.com/samplecode/Sample_Code/Graphics_3D/
1568 TextureRange/MainOpenGLView.m.htm */
1569 glTexSubImage2D( GL_TEXTURE_RECTANGLE_EXT, 0, 0, 0,
1570 p_vout->output.i_width, p_vout->output.i_height,
1571 GL_YCBCR_422_APPLE, GL_UNSIGNED_SHORT_8_8_APPLE,
1572 p_vout->p_sys->p_data[index] );
1582 glBegin( GL_QUADS );
1584 glTexCoord2f( 0.0, 0.0 );
1585 glVertex2f( - f_x, f_y );
1587 glTexCoord2f( 0.0, (float) p_vout->output.i_height );
1588 glVertex2f( - f_x, - f_y );
1590 glTexCoord2f( (float) p_vout->output.i_width,
1591 (float) p_vout->output.i_height );
1592 glVertex2f( f_x, - f_y );
1594 glTexCoord2f( (float) p_vout->output.i_width, 0.0 );
1595 glVertex2f( f_x, f_y );
1601 glBegin( GL_QUADS );
1603 glTexCoord2f( 0.0, 0.0 );
1604 glVertex3f( - 1.0, 1.0, 1.0 );
1605 glTexCoord2f( 0.0, (float) p_vout->output.i_height );
1606 glVertex3f( - 1.0, - 1.0, 1.0 );
1607 glTexCoord2f( (float) p_vout->output.i_width,
1608 (float) p_vout->output.i_height );
1609 glVertex3f( 1.0, - 1.0, 1.0 );
1610 glTexCoord2f( (float) p_vout->output.i_width, 0.0 );
1611 glVertex3f( 1.0, 1.0, 1.0 );
1614 glTexCoord2f( 0.0, 0.0 );
1615 glVertex3f( - 1.0, 1.0, - 1.0 );
1616 glTexCoord2f( 0.0, (float) p_vout->output.i_height );
1617 glVertex3f( - 1.0, - 1.0, - 1.0 );
1618 glTexCoord2f( (float) p_vout->output.i_width,
1619 (float) p_vout->output.i_height );
1620 glVertex3f( - 1.0, - 1.0, 1.0 );
1621 glTexCoord2f( (float) p_vout->output.i_width, 0.0 );
1622 glVertex3f( - 1.0, 1.0, 1.0 );
1625 glTexCoord2f( 0.0, 0.0 );
1626 glVertex3f( 1.0, 1.0, - 1.0 );
1627 glTexCoord2f( 0.0, (float) p_vout->output.i_height );
1628 glVertex3f( 1.0, - 1.0, - 1.0 );
1629 glTexCoord2f( (float) p_vout->output.i_width,
1630 (float) p_vout->output.i_height );
1631 glVertex3f( - 1.0, - 1.0, - 1.0 );
1632 glTexCoord2f( (float) p_vout->output.i_width, 0.0 );
1633 glVertex3f( - 1.0, 1.0, - 1.0 );
1636 glTexCoord2f( 0.0, 0.0 );
1637 glVertex3f( 1.0, 1.0, 1.0 );
1638 glTexCoord2f( 0.0, (float) p_vout->output.i_height );
1639 glVertex3f( 1.0, - 1.0, 1.0 );
1640 glTexCoord2f( (float) p_vout->output.i_width,
1641 (float) p_vout->output.i_height );
1642 glVertex3f( 1.0, - 1.0, - 1.0 );
1643 glTexCoord2f( (float) p_vout->output.i_width, 0.0 );
1644 glVertex3f( 1.0, 1.0, - 1.0 );
1647 glTexCoord2f( 0.0, 0.0 );
1648 glVertex3f( - 1.0, 1.0, - 1.0 );
1649 glTexCoord2f( 0.0, (float) p_vout->output.i_height );
1650 glVertex3f( - 1.0, 1.0, 1.0 );
1651 glTexCoord2f( (float) p_vout->output.i_width,
1652 (float) p_vout->output.i_height );
1653 glVertex3f( 1.0, 1.0, 1.0 );
1654 glTexCoord2f( (float) p_vout->output.i_width, 0.0 );
1655 glVertex3f( 1.0, 1.0, - 1.0 );
1658 glTexCoord2f( 0.0, 0.0 );
1659 glVertex3f( - 1.0, - 1.0, 1.0 );
1660 glTexCoord2f( 0.0, (float) p_vout->output.i_height );
1661 glVertex3f( - 1.0, - 1.0, - 1.0 );
1662 glTexCoord2f( (float) p_vout->output.i_width,
1663 (float) p_vout->output.i_height );
1664 glVertex3f( 1.0, - 1.0, - 1.0 );
1665 glTexCoord2f( (float) p_vout->output.i_width, 0.0 );
1666 glVertex3f( 1.0, - 1.0, 1.0 );
1670 - (void) drawRect: (NSRect) rect
1672 [[self openGLContext] makeCurrentContext];
1674 /* Black background */
1675 glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
1684 glBindTexture( GL_TEXTURE_RECTANGLE_EXT,
1685 pi_textures[p_vout->p_sys->i_cur_pic] );
1686 if( i_effect & ( OPENGL_EFFECT_CUBE |
1687 OPENGL_EFFECT_TRANSPARENT_CUBE ) )
1689 glRotatef( 1.0, 0.3, 0.5, 0.7 );
1703 - (void)mouseMoved:(NSEvent *)o_event
1709 s_rect = [self bounds];
1710 ml = [self convertPoint: [o_event locationInWindow] fromView: nil];
1711 b_inside = [self mouse: ml inRect: s_rect];
1716 int i_width, i_height, i_x, i_y;
1718 vout_PlacePicture( p_vout, (unsigned int)s_rect.size.width,
1719 (unsigned int)s_rect.size.height,
1720 &i_x, &i_y, &i_width, &i_height );
1722 val.i_int = ( (int)ml.x - i_x ) *
1723 p_vout->render.i_width / i_width;
1724 var_Set( p_vout, "mouse-x", val );
1726 /* Y coordinate is inverted in OpenGL */
1727 val.i_int = ( ((int)(s_rect.size.height - ml.y)) - i_y ) *
1728 p_vout->render.i_height / i_height;
1729 var_Set( p_vout, "mouse-y", val );
1731 val.b_bool = VLC_TRUE;
1732 var_Set( p_vout, "mouse-moved", val );
1733 p_vout->p_sys->i_time_mouse_last_moved = mdate();
1734 p_vout->p_sys->b_mouse_moved = YES;
1737 [super mouseMoved: o_event];
1742 /*****************************************************************************
1743 * VLCVout implementation
1744 *****************************************************************************/
1745 @implementation VLCVout
1747 - (void)createWindow:(NSValue *)o_value
1751 NSScreen * o_screen;
1752 vout_thread_t * p_vout;
1753 vlc_bool_t b_main_screen;
1755 p_vout = (vout_thread_t *)[o_value pointerValue];
1757 p_vout->p_sys->o_window = [VLCWindow alloc];
1758 [p_vout->p_sys->o_window setVout: p_vout];
1759 [p_vout->p_sys->o_window setReleasedWhenClosed: YES];
1761 if( var_Get( p_vout, "video-device", &val ) < 0 )
1763 o_screen = [NSScreen mainScreen];
1768 NSArray *o_screens = [NSScreen screens];
1769 unsigned int i_index = val.i_int;
1771 if( [o_screens count] < i_index )
1773 o_screen = [NSScreen mainScreen];
1779 o_screen = [o_screens objectAtIndex: i_index];
1780 config_PutInt( p_vout, "macosx-vdev", i_index );
1781 b_main_screen = (i_index == 0);
1785 if( p_vout->b_fullscreen )
1787 NSRect screen_rect = [o_screen frame];
1788 screen_rect.origin.x = screen_rect.origin.y = 0;
1790 if ( b_main_screen && p_vout->p_sys->p_fullscreen_state == NULL )
1791 BeginFullScreen( &p_vout->p_sys->p_fullscreen_state, NULL, 0, 0,
1792 NULL, NULL, fullScreenAllowEvents );
1794 [p_vout->p_sys->o_window
1795 initWithContentRect: screen_rect
1796 styleMask: NSBorderlessWindowMask
1797 backing: NSBackingStoreBuffered
1798 defer: NO screen: o_screen];
1800 //[p_vout->p_sys->o_window setLevel: NSPopUpMenuWindowLevel - 1];
1801 p_vout->p_sys->b_mouse_moved = YES;
1802 p_vout->p_sys->i_time_mouse_last_moved = mdate();
1806 unsigned int i_stylemask = NSTitledWindowMask |
1807 NSMiniaturizableWindowMask |
1808 NSClosableWindowMask |
1809 NSResizableWindowMask;
1811 if ( p_vout->p_sys->p_fullscreen_state != NULL )
1812 EndFullScreen ( p_vout->p_sys->p_fullscreen_state, NULL );
1813 p_vout->p_sys->p_fullscreen_state = NULL;
1815 [p_vout->p_sys->o_window
1816 initWithContentRect: p_vout->p_sys->s_rect
1817 styleMask: i_stylemask
1818 backing: NSBackingStoreBuffered
1819 defer: NO screen: o_screen];
1821 [p_vout->p_sys->o_window setAlphaValue: config_GetFloat( p_vout, "macosx-opaqueness" )];
1823 if( config_GetInt( p_vout, "video-on-top" ) )
1825 [p_vout->p_sys->o_window setLevel: NSStatusWindowLevel];
1828 if( !p_vout->p_sys->b_pos_saved )
1830 [p_vout->p_sys->o_window center];
1834 if( !p_vout->p_sys->i_opengl )
1836 o_view = [[VLCQTView alloc] init];
1837 /* FIXME: [o_view setMenu:] */
1838 [p_vout->p_sys->o_window setContentView: o_view];
1839 [o_view autorelease];
1842 p_vout->p_sys->p_qdport = [o_view qdPort];
1843 [o_view unlockFocus];
1847 #define o_glview p_vout->p_sys->o_glview
1848 o_glview = [[VLCGLView alloc] initWithFrame: p_vout->p_sys->s_rect vout: p_vout];
1849 [p_vout->p_sys->o_window setContentView: o_glview];
1850 [o_glview autorelease];
1854 [p_vout->p_sys->o_window updateTitle];
1855 [p_vout->p_sys->o_window makeKeyAndOrderFront: nil];
1859 - (void)destroyWindow:(NSValue *)o_value
1861 vout_thread_t * p_vout;
1863 p_vout = (vout_thread_t *)[o_value pointerValue];
1865 if( !p_vout->b_fullscreen )
1869 s_rect = [[p_vout->p_sys->o_window contentView] frame];
1870 p_vout->p_sys->s_rect.size = s_rect.size;
1872 s_rect = [p_vout->p_sys->o_window frame];
1873 p_vout->p_sys->s_rect.origin = s_rect.origin;
1875 p_vout->p_sys->b_pos_saved = YES;
1878 p_vout->p_sys->p_qdport = nil;
1879 [p_vout->p_sys->o_window close];
1880 p_vout->p_sys->o_window = nil;