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( CoDestroyWindow( p_vout ) )
460 msg_Err( p_vout, "unable to destroy window" );
463 if( !p_vout->p_sys->i_opengl )
465 if ( p_vout->p_sys->p_fullscreen_state != NULL )
466 EndFullScreen ( p_vout->p_sys->p_fullscreen_state, NULL );
470 free( p_vout->p_sys->p_matrix );
471 DisposeHandle( (Handle)p_vout->p_sys->h_img_descr );
474 free( p_vout->p_sys );
477 /*****************************************************************************
478 * vout_Manage: handle events
479 *****************************************************************************
480 * This function should be called regularly by video output thread. It manages
481 * console events. It returns a non null value on error.
482 *****************************************************************************/
483 static int vout_Manage( vout_thread_t *p_vout )
485 if( p_vout->i_changes & VOUT_FULLSCREEN_CHANGE )
487 if( CoToggleFullscreen( p_vout ) )
492 p_vout->i_changes &= ~VOUT_FULLSCREEN_CHANGE;
495 if( p_vout->i_changes & VOUT_SIZE_CHANGE )
497 if( !p_vout->p_sys->i_opengl )
499 QTScaleMatrix( p_vout );
500 SetDSequenceMatrix( p_vout->p_sys->i_seq,
501 p_vout->p_sys->p_matrix );
504 p_vout->i_changes &= ~VOUT_SIZE_CHANGE;
507 /* hide/show mouse cursor
508 * this code looks unnecessarily complicated, but is necessary like this.
509 * it has to deal with multiple monitors and therefore checks a lot */
510 if( !p_vout->p_sys->b_mouse_moved && p_vout->b_fullscreen )
512 if( mdate() - p_vout->p_sys->i_time_mouse_last_moved > 3000000 )
514 VLCHideMouse( p_vout, YES );
517 else if ( p_vout->p_sys->b_mouse_moved && p_vout->b_fullscreen )
519 VLCHideMouse( p_vout, NO );
522 /* disable screen saver */
523 UpdateSystemActivity( UsrActivity );
528 /*****************************************************************************
529 * vout_Display: displays previously rendered output
530 *****************************************************************************
531 * This function sends the currently rendered image to the display.
532 *****************************************************************************/
533 static void vout_Display( vout_thread_t *p_vout, picture_t *p_pic )
535 if( !p_vout->p_sys->i_opengl )
540 if( ( err = DecompressSequenceFrameS(
541 p_vout->p_sys->i_seq,
542 p_pic->p_sys->p_info,
543 p_pic->p_sys->i_size,
544 codecFlagUseImageBuffer, &flags, nil ) != noErr ) )
546 msg_Warn( p_vout, "DecompressSequenceFrameS failed: %d", err );
550 QDFlushPortBuffer( p_vout->p_sys->p_qdport, nil );
555 if( [p_vout->p_sys->o_glview lockFocusIfCanDraw] )
558 int i_old = p_vout->p_sys->i_cur_pic;
559 int i_new = ( i_old + 1 ) % 2;
561 /* Draw the new picture */
562 p_vout->p_sys->i_cur_pic = i_new;
563 [p_vout->p_sys->o_glview drawRect:
564 [p_vout->p_sys->o_glview bounds]];
566 /* Reload the other texture. Textures have to be reloaded
567 before the buffer is filled (thanks to gcc from
568 arstechnica forums) */
569 [p_vout->p_sys->o_glview reloadTexture: i_old];
572 p_pic->p_data = p_vout->p_sys->p_data[i_old];
573 p_pic->p[0].p_pixels = p_pic->p_data;
574 for( i_index = 1; i_index < p_pic->i_planes; i_index++ )
576 p_pic->p[i_index].p_pixels =
577 p_pic->p[i_index-1].p_pixels +
578 p_pic->p[i_index-1].i_lines *
579 p_pic->p[i_index-1].i_pitch;
581 [p_vout->p_sys->o_glview unlockFocus];
586 /*****************************************************************************
587 * CoSendRequest: send request to interface thread
588 *****************************************************************************
589 * Returns 0 on success, 1 otherwise
590 *****************************************************************************/
591 static int CoSendRequest( vout_thread_t *p_vout, SEL sel )
595 intf_thread_t * p_intf;
597 VLCVout * o_vlv = [[VLCVout alloc] init];
599 if( ( i_ret = ExecuteOnMainThread( o_vlv, sel, (void *)p_vout ) ) )
601 msg_Err( p_vout, "SendRequest: no way to communicate with mt" );
606 /*This makes this function dependant of the presence of a macosx
607 interface. We do not check if this interface exists, since it has
608 already been done before.*/
610 p_intf = [NSApp getIntf];
612 val.b_bool = VLC_TRUE;
613 var_Create(p_intf,"intf-change",VLC_VAR_BOOL);
614 var_Set(p_intf, "intf-change",val);
619 /*****************************************************************************
620 * CoCreateWindow: create new window
621 *****************************************************************************
622 * Returns 0 on success, 1 otherwise
623 *****************************************************************************/
624 static int CoCreateWindow( vout_thread_t *p_vout )
626 if( CoSendRequest( p_vout, @selector(createWindow:) ) )
628 msg_Err( p_vout, "CoSendRequest (createWindow) failed" );
635 /*****************************************************************************
636 * CoDestroyWindow: destroy window
637 *****************************************************************************
638 * Returns 0 on success, 1 otherwise
639 *****************************************************************************/
640 static int CoDestroyWindow( vout_thread_t *p_vout )
643 VLCHideMouse( p_vout, NO );
645 if( CoSendRequest( p_vout, @selector(destroyWindow:) ) )
647 msg_Err( p_vout, "CoSendRequest (destroyWindow) failed" );
654 /*****************************************************************************
655 * CoToggleFullscreen: toggle fullscreen
656 *****************************************************************************
657 * Returns 0 on success, 1 otherwise
658 *****************************************************************************/
659 static int CoToggleFullscreen( vout_thread_t *p_vout )
661 if( !p_vout->p_sys->i_opengl )
663 QTDestroySequence( p_vout );
666 if( CoDestroyWindow( p_vout ) )
668 msg_Err( p_vout, "unable to destroy window" );
672 p_vout->b_fullscreen = !p_vout->b_fullscreen;
674 if( CoCreateWindow( p_vout ) )
676 msg_Err( p_vout, "unable to create window" );
680 if( p_vout->p_sys->i_opengl )
682 [p_vout->p_sys->o_glview lockFocus];
683 [p_vout->p_sys->o_glview initTextures];
684 [p_vout->p_sys->o_glview reshape];
685 [p_vout->p_sys->o_glview drawRect:
686 [p_vout->p_sys->o_glview bounds]];
687 [p_vout->p_sys->o_glview unlockFocus];
691 SetPort( p_vout->p_sys->p_qdport );
692 QTScaleMatrix( p_vout );
694 if( QTCreateSequence( p_vout ) )
696 msg_Err( p_vout, "unable to create sequence" );
704 /*****************************************************************************
705 * VLCHideMouse: if b_hide then hide the cursor
706 *****************************************************************************/
707 static void VLCHideMouse ( vout_thread_t *p_vout, BOOL b_hide )
712 NSWindow *o_window = p_vout->p_sys->o_window;
713 NSView *o_contents = [o_window contentView];
715 s_rect = [o_contents bounds];
716 ml = [o_window convertScreenToBase:[NSEvent mouseLocation]];
717 ml = [o_contents convertPoint:ml fromView:nil];
718 b_inside = [o_contents mouse: ml inRect: s_rect];
720 if ( b_hide && b_inside )
722 /* only hide if mouse over VLCQTView */
723 [NSCursor setHiddenUntilMouseMoves: YES];
727 [NSCursor setHiddenUntilMouseMoves: NO];
729 p_vout->p_sys->b_mouse_moved = NO;
730 p_vout->p_sys->i_time_mouse_last_moved = mdate();
734 /*****************************************************************************
735 * QTScaleMatrix: scale matrix
736 *****************************************************************************/
737 static void QTScaleMatrix( vout_thread_t *p_vout )
740 unsigned int i_width, i_height;
741 Fixed factor_x, factor_y;
742 unsigned int i_offset_x = 0;
743 unsigned int i_offset_y = 0;
745 GetPortBounds( p_vout->p_sys->p_qdport, &s_rect );
747 i_width = s_rect.right - s_rect.left;
748 i_height = s_rect.bottom - s_rect.top;
750 if( config_GetInt( p_vout, "macosx-stretch" ) )
752 factor_x = FixDiv( Long2Fix( i_width ),
753 Long2Fix( p_vout->output.i_width ) );
754 factor_y = FixDiv( Long2Fix( i_height ),
755 Long2Fix( p_vout->output.i_height ) );
758 else if( i_height * p_vout->output.i_aspect < i_width * VOUT_ASPECT_FACTOR )
760 int i_adj_width = i_height * p_vout->output.i_aspect /
763 factor_x = FixDiv( Long2Fix( i_adj_width ),
764 Long2Fix( p_vout->output.i_width ) );
765 factor_y = FixDiv( Long2Fix( i_height ),
766 Long2Fix( p_vout->output.i_height ) );
768 i_offset_x = (i_width - i_adj_width) / 2;
772 int i_adj_height = i_width * VOUT_ASPECT_FACTOR /
773 p_vout->output.i_aspect;
775 factor_x = FixDiv( Long2Fix( i_width ),
776 Long2Fix( p_vout->output.i_width ) );
777 factor_y = FixDiv( Long2Fix( i_adj_height ),
778 Long2Fix( p_vout->output.i_height ) );
780 i_offset_y = (i_height - i_adj_height) / 2;
783 SetIdentityMatrix( p_vout->p_sys->p_matrix );
785 ScaleMatrix( p_vout->p_sys->p_matrix,
787 Long2Fix(0), Long2Fix(0) );
789 TranslateMatrix( p_vout->p_sys->p_matrix,
790 Long2Fix(i_offset_x), Long2Fix(i_offset_y) );
793 /*****************************************************************************
794 * QTCreateSequence: create a new sequence
795 *****************************************************************************
796 * Returns 0 on success, 1 otherwise
797 *****************************************************************************/
798 static int QTCreateSequence( vout_thread_t *p_vout )
801 ImageDescriptionPtr p_descr;
803 HLock( (Handle)p_vout->p_sys->h_img_descr );
804 p_descr = *p_vout->p_sys->h_img_descr;
806 p_descr->idSize = sizeof(ImageDescription);
807 p_descr->cType = p_vout->p_sys->i_codec;
808 p_descr->version = 1;
809 p_descr->revisionLevel = 0;
810 p_descr->vendor = 'appl';
811 p_descr->width = p_vout->output.i_width;
812 p_descr->height = p_vout->output.i_height;
813 p_descr->hRes = Long2Fix(72);
814 p_descr->vRes = Long2Fix(72);
815 p_descr->spatialQuality = codecLosslessQuality;
816 p_descr->frameCount = 1;
817 p_descr->clutID = -1;
818 p_descr->dataSize = 0;
821 HUnlock( (Handle)p_vout->p_sys->h_img_descr );
823 if( ( err = DecompressSequenceBeginS(
824 &p_vout->p_sys->i_seq,
825 p_vout->p_sys->h_img_descr,
827 p_vout->p_sys->p_qdport,
829 p_vout->p_sys->p_matrix,
831 codecFlagUseImageBuffer,
832 codecLosslessQuality,
833 p_vout->p_sys->img_dc ) ) )
835 msg_Err( p_vout, "DecompressSequenceBeginS failed: %d", err );
842 /*****************************************************************************
843 * QTDestroySequence: destroy sequence
844 *****************************************************************************/
845 static void QTDestroySequence( vout_thread_t *p_vout )
847 CDSequenceEnd( p_vout->p_sys->i_seq );
850 /*****************************************************************************
851 * QTNewPicture: allocate a picture
852 *****************************************************************************
853 * Returns 0 on success, 1 otherwise
854 *****************************************************************************/
855 static int QTNewPicture( vout_thread_t *p_vout, picture_t *p_pic )
857 int i_width = p_vout->output.i_width;
858 int i_height = p_vout->output.i_height;
860 /* We know the chroma, allocate a buffer which will be used
861 * directly by the decoder */
862 p_pic->p_sys = malloc( sizeof( picture_sys_t ) );
864 if( p_pic->p_sys == NULL )
869 switch( p_vout->output.i_chroma )
871 case VLC_FOURCC('I','4','2','0'):
873 p_pic->p_sys->p_info = (void *)&p_pic->p_sys->pixmap_i420;
874 p_pic->p_sys->i_size = sizeof(PlanarPixmapInfoYUV420);
876 /* Allocate the memory buffer */
877 p_pic->p_data = vlc_memalign( &p_pic->p_data_orig,
878 16, i_width * i_height * 3 / 2 );
881 p_pic->Y_PIXELS = p_pic->p_data;
882 p_pic->p[Y_PLANE].i_lines = i_height;
883 p_pic->p[Y_PLANE].i_pitch = i_width;
884 p_pic->p[Y_PLANE].i_pixel_pitch = 1;
885 p_pic->p[Y_PLANE].i_visible_pitch = i_width;
888 p_pic->U_PIXELS = p_pic->Y_PIXELS + i_height * i_width;
889 p_pic->p[U_PLANE].i_lines = i_height / 2;
890 p_pic->p[U_PLANE].i_pitch = i_width / 2;
891 p_pic->p[U_PLANE].i_pixel_pitch = 1;
892 p_pic->p[U_PLANE].i_visible_pitch = i_width / 2;
895 p_pic->V_PIXELS = p_pic->U_PIXELS + i_height * i_width / 4;
896 p_pic->p[V_PLANE].i_lines = i_height / 2;
897 p_pic->p[V_PLANE].i_pitch = i_width / 2;
898 p_pic->p[V_PLANE].i_pixel_pitch = 1;
899 p_pic->p[V_PLANE].i_visible_pitch = i_width / 2;
901 /* We allocated 3 planes */
904 #define P p_pic->p_sys->pixmap_i420
905 P.componentInfoY.offset = (void *)p_pic->Y_PIXELS
906 - p_pic->p_sys->p_info;
907 P.componentInfoCb.offset = (void *)p_pic->U_PIXELS
908 - p_pic->p_sys->p_info;
909 P.componentInfoCr.offset = (void *)p_pic->V_PIXELS
910 - p_pic->p_sys->p_info;
912 P.componentInfoY.rowBytes = i_width;
913 P.componentInfoCb.rowBytes = i_width / 2;
914 P.componentInfoCr.rowBytes = i_width / 2;
920 /* Unknown chroma, tell the guy to get lost */
921 free( p_pic->p_sys );
922 msg_Err( p_vout, "never heard of chroma 0x%.8x (%4.4s)",
923 p_vout->output.i_chroma, (char*)&p_vout->output.i_chroma );
931 /*****************************************************************************
932 * QTFreePicture: destroy a picture allocated with QTNewPicture
933 *****************************************************************************/
934 static void QTFreePicture( vout_thread_t *p_vout, picture_t *p_pic )
936 switch( p_vout->output.i_chroma )
938 case VLC_FOURCC('I','4','2','0'):
939 free( p_pic->p_data_orig );
943 free( p_pic->p_sys );
946 /*****************************************************************************
947 * VLCWindow implementation
948 *****************************************************************************/
949 @implementation VLCWindow
951 - (void)setVout:(vout_thread_t *)_p_vout
956 - (vout_thread_t *)getVout
961 - (void)scaleWindowWithFactor: (float)factor
964 int i_corrected_height, i_corrected_width;
966 NSPoint topleftscreen;
968 if ( !p_vout->b_fullscreen )
971 topleftbase.y = [self frame].size.height;
972 topleftscreen = [self convertBaseToScreen: topleftbase];
974 if( p_vout->output.i_height * p_vout->output.i_aspect >
975 p_vout->output.i_width * VOUT_ASPECT_FACTOR )
977 i_corrected_width = p_vout->output.i_height * p_vout->output.i_aspect /
979 newsize.width = (int) ( i_corrected_width * factor );
980 newsize.height = (int) ( p_vout->render.i_height * factor );
984 i_corrected_height = p_vout->output.i_width * VOUT_ASPECT_FACTOR /
985 p_vout->output.i_aspect;
986 newsize.width = (int) ( p_vout->render.i_width * factor );
987 newsize.height = (int) ( i_corrected_height * factor );
990 [self setContentSize: newsize];
992 [self setFrameTopLeftPoint: topleftscreen];
993 p_vout->i_changes |= VOUT_SIZE_CHANGE;
997 - (void)toggleFloatOnTop
1000 if( var_Get( p_vout, "video-on-top", &val )>=0 && val.b_bool)
1002 val.b_bool = VLC_FALSE;
1003 var_Set( p_vout, "video-on-top", val );
1004 [p_vout->p_sys->o_window setLevel: NSNormalWindowLevel];
1008 val.b_bool = VLC_TRUE;
1009 var_Set( p_vout, "video-on-top", val );
1010 [p_vout->p_sys->o_window setLevel: NSStatusWindowLevel];
1014 - (void)toggleFullscreen
1017 val.b_bool = !p_vout->b_fullscreen;
1018 var_Set( p_vout, "fullscreen", val );
1021 - (BOOL)isFullscreen
1023 return( p_vout->b_fullscreen );
1026 - (BOOL)canBecomeKeyWindow
1031 - (BOOL)performKeyEquivalent:(NSEvent *)o_event
1033 return [(VLCApplication *) [VLCApplication sharedApplication]
1034 hasDefinedShortcutKey:o_event];
1037 - (void)keyDown:(NSEvent *)o_event
1041 unsigned int i_pressed_modifiers = 0;
1044 i_pressed_modifiers = [o_event modifierFlags];
1046 if( i_pressed_modifiers & NSShiftKeyMask )
1047 val.i_int |= KEY_MODIFIER_SHIFT;
1048 if( i_pressed_modifiers & NSControlKeyMask )
1049 val.i_int |= KEY_MODIFIER_CTRL;
1050 if( i_pressed_modifiers & NSAlternateKeyMask )
1051 val.i_int |= KEY_MODIFIER_ALT;
1052 if( i_pressed_modifiers & NSCommandKeyMask )
1053 val.i_int |= KEY_MODIFIER_COMMAND;
1055 key = [[o_event charactersIgnoringModifiers] characterAtIndex: 0];
1059 /* Escape should always get you out of fullscreen */
1060 if( key == (unichar) 0x1b )
1062 if( [self isFullscreen] )
1064 [self toggleFullscreen];
1067 else if ( key == ' ' )
1069 playlist_t *p_playlist = vlc_object_find( p_vout, VLC_OBJECT_PLAYLIST,
1071 if ( p_playlist != NULL )
1073 playlist_Pause( p_playlist );
1074 vlc_object_release( p_playlist);
1079 val.i_int |= CocoaKeyToVLC( key );
1080 var_Set( p_vout->p_vlc, "key-pressed", val );
1085 [super keyDown: o_event];
1091 NSMutableString * o_title;
1092 playlist_t * p_playlist = vlc_object_find( p_vout, VLC_OBJECT_PLAYLIST,
1095 if( p_playlist == NULL )
1100 vlc_mutex_lock( &p_playlist->object_lock );
1101 o_title = [NSMutableString stringWithUTF8String:
1102 p_playlist->pp_items[p_playlist->i_index]->input.psz_uri];
1103 vlc_mutex_unlock( &p_playlist->object_lock );
1105 vlc_object_release( p_playlist );
1107 if( o_title != nil )
1109 NSRange prefix_range = [o_title rangeOfString: @"file:"];
1110 if( prefix_range.location != NSNotFound )
1112 [o_title deleteCharactersInRange: prefix_range];
1115 [self setTitleWithRepresentedFilename: o_title];
1120 [NSString stringWithCString: VOUT_TITLE " (QuickTime)"]];
1124 /* This is actually the same as VLCControls::stop. */
1125 - (BOOL)windowShouldClose:(id)sender
1127 playlist_t * p_playlist = vlc_object_find( p_vout, VLC_OBJECT_PLAYLIST,
1129 if( p_playlist == NULL )
1134 playlist_Stop( p_playlist );
1135 vlc_object_release( p_playlist );
1137 /* The window will be closed by the intf later. */
1143 /* Common QT and OpenGL code to catch mouse events */
1144 #define CATCH_MOUSE_EVENTS \
1145 - (BOOL)acceptsFirstResponder \
1150 - (BOOL)becomeFirstResponder \
1152 id o_window = [self window]; \
1154 [o_window setAcceptsMouseMovedEvents: YES]; \
1158 - (BOOL)resignFirstResponder \
1160 vout_thread_t * vout; \
1161 id o_window = [self window]; \
1162 vout = (vout_thread_t *)[o_window getVout]; \
1164 [o_window setAcceptsMouseMovedEvents: NO]; \
1165 VLCHideMouse( vout, NO ); \
1169 - (void)mouseDown:(NSEvent *)o_event \
1171 vout_thread_t * vout; \
1172 id o_window = [self window]; \
1173 vout = (vout_thread_t *)[o_window getVout]; \
1176 switch( [o_event type] ) \
1178 case NSLeftMouseDown: \
1180 var_Get( vout, "mouse-button-down", &val ); \
1182 var_Set( vout, "mouse-button-down", val ); \
1187 [super mouseDown: o_event]; \
1192 - (void)otherMouseDown:(NSEvent *)o_event \
1194 vout_thread_t * vout; \
1195 id o_window = [self window]; \
1196 vout = (vout_thread_t *)[o_window getVout]; \
1199 switch( [o_event type] ) \
1201 case NSOtherMouseDown: \
1203 var_Get( vout, "mouse-button-down", &val ); \
1205 var_Set( vout, "mouse-button-down", val ); \
1210 [super mouseDown: o_event]; \
1215 - (void)rightMouseDown:(NSEvent *)o_event \
1217 vout_thread_t * vout; \
1218 id o_window = [self window]; \
1219 vout = (vout_thread_t *)[o_window getVout]; \
1222 switch( [o_event type] ) \
1224 case NSRightMouseDown: \
1226 var_Get( vout, "mouse-button-down", &val ); \
1228 var_Set( vout, "mouse-button-down", val ); \
1233 [super mouseDown: o_event]; \
1238 - (void)mouseUp:(NSEvent *)o_event \
1240 vout_thread_t * vout; \
1241 id o_window = [self window]; \
1242 vout = (vout_thread_t *)[o_window getVout]; \
1245 switch( [o_event type] ) \
1247 case NSLeftMouseUp: \
1249 vlc_value_t b_val; \
1250 b_val.b_bool = VLC_TRUE; \
1251 var_Set( vout, "mouse-clicked", b_val ); \
1253 var_Get( vout, "mouse-button-down", &val ); \
1255 var_Set( vout, "mouse-button-down", val ); \
1260 [super mouseUp: o_event]; \
1265 - (void)otherMouseUp:(NSEvent *)o_event \
1267 vout_thread_t * vout; \
1268 id o_window = [self window]; \
1269 vout = (vout_thread_t *)[o_window getVout]; \
1272 switch( [o_event type] ) \
1274 case NSOtherMouseUp: \
1276 var_Get( vout, "mouse-button-down", &val ); \
1278 var_Set( vout, "mouse-button-down", val ); \
1283 [super mouseUp: o_event]; \
1288 - (void)rightMouseUp:(NSEvent *)o_event \
1290 vout_thread_t * vout; \
1291 id o_window = [self window]; \
1292 vout = (vout_thread_t *)[o_window getVout]; \
1295 switch( [o_event type] ) \
1297 case NSRightMouseUp: \
1299 var_Get( vout, "mouse-button-down", &val ); \
1301 var_Set( vout, "mouse-button-down", val ); \
1306 [super mouseUp: o_event]; \
1311 - (void)mouseDragged:(NSEvent *)o_event \
1313 [self mouseMoved:o_event]; \
1316 - (void)otherMouseDragged:(NSEvent *)o_event \
1318 [self mouseMoved:o_event]; \
1321 - (void)rightMouseDragged:(NSEvent *)o_event \
1323 [self mouseMoved:o_event]; \
1326 /*****************************************************************************
1327 * VLCQTView implementation
1328 *****************************************************************************/
1329 @implementation VLCQTView
1331 - (void)drawRect:(NSRect)rect
1333 vout_thread_t * p_vout;
1334 id o_window = [self window];
1335 p_vout = (vout_thread_t *)[o_window getVout];
1337 [[NSColor blackColor] set];
1339 [super drawRect: rect];
1341 p_vout->i_changes |= VOUT_SIZE_CHANGE;
1346 - (void)mouseMoved:(NSEvent *)o_event
1352 vout_thread_t * p_vout;
1353 id o_window = [self window];
1354 p_vout = (vout_thread_t *)[o_window getVout];
1356 s_rect = [self bounds];
1357 ml = [self convertPoint: [o_event locationInWindow] fromView: nil];
1358 b_inside = [self mouse: ml inRect: s_rect];
1363 int i_width, i_height, i_x, i_y;
1365 vout_PlacePicture( p_vout, (unsigned int)s_rect.size.width,
1366 (unsigned int)s_rect.size.height,
1367 &i_x, &i_y, &i_width, &i_height );
1369 val.i_int = ( ((int)ml.x) - i_x ) *
1370 p_vout->render.i_width / i_width;
1371 var_Set( p_vout, "mouse-x", val );
1373 val.i_int = ( ((int)ml.y) - i_y ) *
1374 p_vout->render.i_height / i_height;
1375 var_Set( p_vout, "mouse-y", val );
1377 val.b_bool = VLC_TRUE;
1378 var_Set( p_vout, "mouse-moved", val );
1379 p_vout->p_sys->i_time_mouse_last_moved = mdate();
1380 p_vout->p_sys->b_mouse_moved = YES;
1383 [super mouseMoved: o_event];
1388 /*****************************************************************************
1389 * VLCGLView implementation
1390 *****************************************************************************/
1391 @implementation VLCGLView
1394 - (id) initWithFrame: (NSRect) frame vout: (vout_thread_t*) _p_vout
1399 NSOpenGLPixelFormatAttribute attribs[] =
1401 NSOpenGLPFAAccelerated,
1402 NSOpenGLPFANoRecovery,
1403 NSOpenGLPFAColorSize, 24,
1404 NSOpenGLPFAAlphaSize, 8,
1405 NSOpenGLPFADepthSize, 24,
1410 NSOpenGLPixelFormat * fmt = [[NSOpenGLPixelFormat alloc]
1411 initWithAttributes: attribs];
1415 msg_Warn( p_vout, "Cannot create NSOpenGLPixelFormat" );
1419 self = [super initWithFrame:frame pixelFormat: fmt];
1422 [[self openGLContext] makeCurrentContext];
1423 [[self openGLContext] update];
1425 /* Black background */
1426 glClearColor( 0.0, 0.0, 0.0, 0.0 );
1428 /* Check if the user asked for useless visual effects */
1429 psz_effect = config_GetPsz( p_vout, "macosx-opengl-effect" );
1430 if( !psz_effect || !strcmp( psz_effect, "none" ))
1432 i_effect = OPENGL_EFFECT_NONE;
1434 else if( !strcmp( psz_effect, "cube" ) )
1436 i_effect = OPENGL_EFFECT_CUBE;
1438 glEnable( GL_DEPTH_TEST );
1440 else if( !strcmp( psz_effect, "transparent-cube" ) )
1442 i_effect = OPENGL_EFFECT_TRANSPARENT_CUBE;
1444 glDisable( GL_DEPTH_TEST );
1445 glEnable( GL_BLEND );
1446 glBlendFunc( GL_SRC_ALPHA, GL_ONE );
1450 msg_Warn( p_vout, "no valid opengl effect provided, using "
1452 i_effect = OPENGL_EFFECT_NONE;
1455 if( i_effect & ( OPENGL_EFFECT_CUBE |
1456 OPENGL_EFFECT_TRANSPARENT_CUBE ) )
1458 /* Set the perpective */
1459 glMatrixMode( GL_PROJECTION );
1461 glFrustum( -1.0, 1.0, -1.0, 1.0, 3.0, 20.0 );
1462 glMatrixMode( GL_MODELVIEW );
1464 glTranslatef( 0.0, 0.0, - 5.0 );
1481 [[self openGLContext] makeCurrentContext];
1483 NSRect bounds = [self bounds];
1484 glViewport( 0, 0, (GLint) bounds.size.width,
1485 (GLint) bounds.size.height );
1487 if( config_GetInt( p_vout, "macosx-stretch" ) )
1494 /* Quad size is set in order to preserve the aspect ratio */
1495 int fill = ( config_GetInt( p_vout, "macosx-fill" ) &&
1496 p_vout->b_fullscreen );
1497 int large = ( bounds.size.height * p_vout->output.i_aspect <
1498 bounds.size.width * VOUT_ASPECT_FACTOR );
1499 if( ( large && !fill ) || ( !large && fill ) )
1501 f_x = bounds.size.height * p_vout->output.i_aspect /
1502 VOUT_ASPECT_FACTOR / bounds.size.width;
1508 f_y = bounds.size.width * VOUT_ASPECT_FACTOR /
1509 p_vout->output.i_aspect / bounds.size.height;
1514 - (void) initTextures
1517 [[self openGLContext] makeCurrentContext];
1519 /* Free previous texture if any */
1522 glDeleteTextures( 2, pi_textures );
1525 glEnable( GL_TEXTURE_RECTANGLE_EXT );
1526 glEnable( GL_UNPACK_CLIENT_STORAGE_APPLE );
1528 glPixelStorei( GL_UNPACK_ALIGNMENT, 1 );
1529 glPixelStorei( GL_UNPACK_ROW_LENGTH, p_vout->output.i_width );
1531 /* Tell the driver not to make a copy of the texture but to use
1533 glPixelStorei( GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE );
1535 /* Create textures */
1536 glGenTextures( 2, pi_textures );
1538 for( i = 0; i < 2; i++ )
1540 glBindTexture( GL_TEXTURE_RECTANGLE_EXT, pi_textures[i] );
1541 glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE );
1543 /* Linear interpolation */
1544 glTexParameteri( GL_TEXTURE_RECTANGLE_EXT,
1545 GL_TEXTURE_MIN_FILTER, GL_LINEAR );
1546 glTexParameteri( GL_TEXTURE_RECTANGLE_EXT,
1547 GL_TEXTURE_MAG_FILTER, GL_LINEAR );
1549 /* Use VRAM texturing */
1550 glTexParameteri( GL_TEXTURE_RECTANGLE_EXT,
1551 GL_TEXTURE_STORAGE_HINT_APPLE, GL_STORAGE_CACHED_APPLE );
1553 glTexImage2D( GL_TEXTURE_RECTANGLE_EXT, 0, GL_RGB8,
1554 p_vout->output.i_width, p_vout->output.i_height, 0,
1555 GL_YCBCR_422_APPLE, GL_UNSIGNED_SHORT_8_8_APPLE,
1556 p_vout->p_sys->p_data[i] );
1559 /* Swap buffers only during the vertical retrace of the monitor.
1560 http://developer.apple.com/documentation/GraphicsImaging/
1561 Conceptual/OpenGL/chap5/chapter_5_section_44.html */
1562 long params[] = { 1 };
1563 CGLSetParameter( CGLGetCurrentContext(), kCGLCPSwapInterval,
1569 - (void)reloadTexture: (int) index
1576 [[self openGLContext] makeCurrentContext];
1578 glBindTexture( GL_TEXTURE_RECTANGLE_EXT, pi_textures[index] );
1579 glPixelStorei( GL_UNPACK_ROW_LENGTH, p_vout->output.i_width );
1581 /* glTexSubImage2D is faster than glTexImage2D
1582 http://developer.apple.com/samplecode/Sample_Code/Graphics_3D/
1583 TextureRange/MainOpenGLView.m.htm */
1584 glTexSubImage2D( GL_TEXTURE_RECTANGLE_EXT, 0, 0, 0,
1585 p_vout->output.i_width, p_vout->output.i_height,
1586 GL_YCBCR_422_APPLE, GL_UNSIGNED_SHORT_8_8_APPLE,
1587 p_vout->p_sys->p_data[index] );
1592 glBegin( GL_QUADS );
1594 glTexCoord2f( 0.0, 0.0 );
1595 glVertex2f( - f_x, f_y );
1597 glTexCoord2f( 0.0, (float) p_vout->output.i_height );
1598 glVertex2f( - f_x, - f_y );
1600 glTexCoord2f( (float) p_vout->output.i_width,
1601 (float) p_vout->output.i_height );
1602 glVertex2f( f_x, - f_y );
1604 glTexCoord2f( (float) p_vout->output.i_width, 0.0 );
1605 glVertex2f( f_x, f_y );
1611 glBegin( GL_QUADS );
1613 glTexCoord2f( 0.0, 0.0 );
1614 glVertex3f( - 1.0, 1.0, 1.0 );
1615 glTexCoord2f( 0.0, (float) p_vout->output.i_height );
1616 glVertex3f( - 1.0, - 1.0, 1.0 );
1617 glTexCoord2f( (float) p_vout->output.i_width,
1618 (float) p_vout->output.i_height );
1619 glVertex3f( 1.0, - 1.0, 1.0 );
1620 glTexCoord2f( (float) p_vout->output.i_width, 0.0 );
1621 glVertex3f( 1.0, 1.0, 1.0 );
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 );
1680 - (void) drawRect: (NSRect) rect
1682 [[self openGLContext] makeCurrentContext];
1684 /* Black background */
1685 glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
1694 glBindTexture( GL_TEXTURE_RECTANGLE_EXT,
1695 pi_textures[p_vout->p_sys->i_cur_pic] );
1696 if( i_effect & ( OPENGL_EFFECT_CUBE |
1697 OPENGL_EFFECT_TRANSPARENT_CUBE ) )
1699 glRotatef( 1.0, 0.3, 0.5, 0.7 );
1713 - (void)mouseMoved:(NSEvent *)o_event
1719 s_rect = [self bounds];
1720 ml = [self convertPoint: [o_event locationInWindow] fromView: nil];
1721 b_inside = [self mouse: ml inRect: s_rect];
1726 int i_width, i_height, i_x, i_y;
1728 vout_PlacePicture( p_vout, (unsigned int)s_rect.size.width,
1729 (unsigned int)s_rect.size.height,
1730 &i_x, &i_y, &i_width, &i_height );
1732 val.i_int = ( (int)ml.x - i_x ) *
1733 p_vout->render.i_width / i_width;
1734 var_Set( p_vout, "mouse-x", val );
1736 /* Y coordinate is inverted in OpenGL */
1737 val.i_int = ( ((int)(s_rect.size.height - ml.y)) - i_y ) *
1738 p_vout->render.i_height / i_height;
1739 var_Set( p_vout, "mouse-y", val );
1741 val.b_bool = VLC_TRUE;
1742 var_Set( p_vout, "mouse-moved", val );
1743 p_vout->p_sys->i_time_mouse_last_moved = mdate();
1744 p_vout->p_sys->b_mouse_moved = YES;
1747 [super mouseMoved: o_event];
1752 /*****************************************************************************
1753 * VLCVout implementation
1754 *****************************************************************************/
1755 @implementation VLCVout
1757 - (void)createWindow:(NSValue *)o_value
1761 NSScreen * o_screen;
1762 vout_thread_t * p_vout;
1763 vlc_bool_t b_main_screen;
1765 p_vout = (vout_thread_t *)[o_value pointerValue];
1767 p_vout->p_sys->o_window = [VLCWindow alloc];
1768 [p_vout->p_sys->o_window setVout: p_vout];
1769 [p_vout->p_sys->o_window setReleasedWhenClosed: YES];
1771 if( var_Get( p_vout, "video-device", &val ) < 0 )
1773 o_screen = [NSScreen mainScreen];
1778 NSArray *o_screens = [NSScreen screens];
1779 unsigned int i_index = val.i_int;
1781 if( [o_screens count] < i_index )
1783 o_screen = [NSScreen mainScreen];
1789 o_screen = [o_screens objectAtIndex: i_index];
1790 config_PutInt( p_vout, "macosx-vdev", i_index );
1791 b_main_screen = (i_index == 0);
1795 if( p_vout->b_fullscreen )
1797 NSRect screen_rect = [o_screen frame];
1798 screen_rect.origin.x = screen_rect.origin.y = 0;
1800 if ( b_main_screen && p_vout->p_sys->p_fullscreen_state == NULL )
1801 BeginFullScreen( &p_vout->p_sys->p_fullscreen_state, NULL, 0, 0,
1802 NULL, NULL, fullScreenAllowEvents );
1804 [p_vout->p_sys->o_window
1805 initWithContentRect: screen_rect
1806 styleMask: NSBorderlessWindowMask
1807 backing: NSBackingStoreBuffered
1808 defer: NO screen: o_screen];
1810 //[p_vout->p_sys->o_window setLevel: NSPopUpMenuWindowLevel - 1];
1811 p_vout->p_sys->b_mouse_moved = YES;
1812 p_vout->p_sys->i_time_mouse_last_moved = mdate();
1816 unsigned int i_stylemask = NSTitledWindowMask |
1817 NSMiniaturizableWindowMask |
1818 NSClosableWindowMask |
1819 NSResizableWindowMask;
1821 if ( p_vout->p_sys->p_fullscreen_state != NULL )
1822 EndFullScreen ( p_vout->p_sys->p_fullscreen_state, NULL );
1823 p_vout->p_sys->p_fullscreen_state = NULL;
1825 [p_vout->p_sys->o_window
1826 initWithContentRect: p_vout->p_sys->s_rect
1827 styleMask: i_stylemask
1828 backing: NSBackingStoreBuffered
1829 defer: NO screen: o_screen];
1831 [p_vout->p_sys->o_window setAlphaValue: config_GetFloat( p_vout, "macosx-opaqueness" )];
1833 if( config_GetInt( p_vout, "video-on-top" ) )
1835 [p_vout->p_sys->o_window setLevel: NSStatusWindowLevel];
1838 if( !p_vout->p_sys->b_pos_saved )
1840 [p_vout->p_sys->o_window center];
1844 if( !p_vout->p_sys->i_opengl )
1846 o_view = [[VLCQTView alloc] init];
1847 /* FIXME: [o_view setMenu:] */
1848 [p_vout->p_sys->o_window setContentView: o_view];
1849 [o_view autorelease];
1852 p_vout->p_sys->p_qdport = [o_view qdPort];
1853 [o_view unlockFocus];
1857 #define o_glview p_vout->p_sys->o_glview
1858 o_glview = [[VLCGLView alloc] initWithFrame: p_vout->p_sys->s_rect vout: p_vout];
1859 [p_vout->p_sys->o_window setContentView: o_glview];
1860 [o_glview autorelease];
1864 [p_vout->p_sys->o_window updateTitle];
1865 [p_vout->p_sys->o_window makeKeyAndOrderFront: nil];
1869 - (void)destroyWindow:(NSValue *)o_value
1871 vout_thread_t * p_vout;
1873 p_vout = (vout_thread_t *)[o_value pointerValue];
1875 if( !p_vout->b_fullscreen )
1879 s_rect = [[p_vout->p_sys->o_window contentView] frame];
1880 p_vout->p_sys->s_rect.size = s_rect.size;
1882 s_rect = [p_vout->p_sys->o_window frame];
1883 p_vout->p_sys->s_rect.origin = s_rect.origin;
1885 p_vout->p_sys->b_pos_saved = YES;
1888 p_vout->p_sys->p_qdport = nil;
1889 [p_vout->p_sys->o_window close];
1890 p_vout->p_sys->o_window = nil;