1 /*****************************************************************************
2 * vout.m: MacOS X video output module
3 *****************************************************************************
4 * Copyright (C) 2001-2003 VideoLAN
5 * $Id: vout.m,v 1.72 2004/01/27 12:11:48 titer Exp $
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>
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
25 *****************************************************************************/
27 /*****************************************************************************
29 *****************************************************************************/
30 #include <errno.h> /* ENOMEM */
31 #include <stdlib.h> /* free() */
32 #include <string.h> /* strerror() */
34 #include <QuickTime/QuickTime.h>
36 #include <OpenGL/gl.h>
37 #include <OpenGL/glext.h>
44 #define QT_MAX_DIRECTBUFFERS 10
45 #define VL_MAX_DISPLAYS 16
52 /* When using I420 output */
53 PlanarPixmapInfoYUV420 pixmap_i420;
56 /*****************************************************************************
58 *****************************************************************************/
60 static int vout_Init ( vout_thread_t * );
61 static void vout_End ( vout_thread_t * );
62 static int vout_Manage ( vout_thread_t * );
63 static void vout_Display ( vout_thread_t *, picture_t * );
65 static int CoSendRequest ( vout_thread_t *, SEL );
66 static int CoCreateWindow ( vout_thread_t * );
67 static int CoDestroyWindow ( vout_thread_t * );
68 static int CoToggleFullscreen ( vout_thread_t * );
70 static void VLCHideMouse ( vout_thread_t *, BOOL );
72 static void QTScaleMatrix ( vout_thread_t * );
73 static int QTCreateSequence ( vout_thread_t * );
74 static void QTDestroySequence ( vout_thread_t * );
75 static int QTNewPicture ( vout_thread_t *, picture_t * );
76 static void QTFreePicture ( vout_thread_t *, picture_t * );
78 /*****************************************************************************
79 * OpenVideo: allocates MacOS X video thread output method
80 *****************************************************************************
81 * This function allocates and initializes a MacOS X vout method.
82 *****************************************************************************/
83 int E_(OpenVideo) ( vlc_object_t *p_this )
85 vout_thread_t * p_vout = (vout_thread_t *)p_this;
89 p_vout->p_sys = malloc( sizeof( vout_sys_t ) );
90 if( p_vout->p_sys == NULL )
92 msg_Err( p_vout, "out of memory" );
96 memset( p_vout->p_sys, 0, sizeof( vout_sys_t ) );
98 /* Wait for a MacOS X interface to appear. Timeout is 2 seconds. */
99 for( i_timeout = 20 ; i_timeout-- ; )
103 msleep( INTF_IDLE_SLEEP );
109 /* no MacOS X intf, unable to communicate with MT */
110 msg_Err( p_vout, "no MacOS X interface present" );
111 free( p_vout->p_sys );
115 if( [NSApp respondsToSelector: @selector(getIntf)] )
117 intf_thread_t * p_intf;
119 for( i_timeout = 10 ; i_timeout-- ; )
121 if( ( p_intf = [NSApp getIntf] ) == NULL )
123 msleep( INTF_IDLE_SLEEP );
129 msg_Err( p_vout, "MacOS X intf has getIntf, but is NULL" );
130 free( p_vout->p_sys );
135 p_vout->p_sys->b_mouse_moved = VLC_TRUE;
136 p_vout->p_sys->i_time_mouse_last_moved = mdate();
138 /* set window size */
139 p_vout->p_sys->s_rect.size.width = p_vout->i_window_width;
140 p_vout->p_sys->s_rect.size.height = p_vout->i_window_height;
142 /* Check if we should use QuickTime or OpenGL */
143 p_vout->p_sys->i_opengl = config_GetInt( p_vout, "macosx-opengl" );
145 if( !p_vout->p_sys->i_opengl )
147 /* Initialize QuickTime */
148 p_vout->p_sys->h_img_descr =
149 (ImageDescriptionHandle)NewHandleClear( sizeof(ImageDescription) );
150 p_vout->p_sys->p_matrix =
151 (MatrixRecordPtr)malloc( sizeof(MatrixRecord) );
152 p_vout->p_sys->p_fullscreen_state = NULL;
154 if( ( err = EnterMovies() ) != noErr )
156 msg_Err( p_vout, "EnterMovies failed: %d", err );
157 free( p_vout->p_sys->p_matrix );
158 DisposeHandle( (Handle)p_vout->p_sys->h_img_descr );
159 free( p_vout->p_sys );
163 /* Damn QT isn't thread safe. so keep a lock in the p_vlc object */
164 vlc_mutex_lock( &p_vout->p_vlc->quicktime_lock );
166 err = FindCodec( kYUV420CodecType, bestSpeedCodec,
167 nil, &p_vout->p_sys->img_dc );
169 vlc_mutex_unlock( &p_vout->p_vlc->quicktime_lock );
170 if( err == noErr && p_vout->p_sys->img_dc != 0 )
172 p_vout->output.i_chroma = VLC_FOURCC('I','4','2','0');
173 p_vout->p_sys->i_codec = kYUV420CodecType;
177 msg_Err( p_vout, "failed to find an appropriate codec" );
180 if( p_vout->p_sys->img_dc == 0 )
182 free( p_vout->p_sys->p_matrix );
183 DisposeHandle( (Handle)p_vout->p_sys->h_img_descr );
184 free( p_vout->p_sys );
189 NSAutoreleasePool * o_pool = [[NSAutoreleasePool alloc] init];
190 NSArray * o_screens = [NSScreen screens];
191 if( [o_screens count] > 0 && var_Type( p_vout, "video-device" ) == 0 )
194 vlc_value_t val, text;
197 int i_option = config_GetInt( p_vout, "macosx-vdev" );
199 var_Create( p_vout, "video-device", VLC_VAR_INTEGER |
201 text.psz_string = _("Video device");
202 var_Change( p_vout, "video-device", VLC_VAR_SETTEXT, &text, NULL );
204 NSEnumerator * o_enumerator = [o_screens objectEnumerator];
206 while( (o_screen = [o_enumerator nextObject]) != NULL )
209 NSRect s_rect = [o_screen frame];
211 snprintf( psz_temp, sizeof(psz_temp)/sizeof(psz_temp[0])-1,
212 "%s %d (%dx%d)", _("Screen"), i,
213 (int)s_rect.size.width, (int)s_rect.size.height );
215 text.psz_string = psz_temp;
217 var_Change( p_vout, "video-device",
218 VLC_VAR_ADDCHOICE, &val, &text );
220 if( ( i - 1 ) == i_option )
222 var_Set( p_vout, "video-device", val );
227 var_AddCallback( p_vout, "video-device", vout_VarCallback,
230 val.b_bool = VLC_TRUE;
231 var_Set( p_vout, "intf-change", val );
235 if( CoCreateWindow( p_vout ) )
237 msg_Err( p_vout, "unable to create window" );
238 if( !p_vout->p_sys->i_opengl )
240 free( p_vout->p_sys->p_matrix );
241 DisposeHandle( (Handle)p_vout->p_sys->h_img_descr );
243 free( p_vout->p_sys );
247 p_vout->pf_init = vout_Init;
248 p_vout->pf_end = vout_End;
249 p_vout->pf_manage = vout_Manage;
250 p_vout->pf_render = NULL;
251 p_vout->pf_display = vout_Display;
256 /*****************************************************************************
257 * vout_Init: initialize video thread output method
258 *****************************************************************************/
259 static int vout_Init( vout_thread_t *p_vout )
264 I_OUTPUTPICTURES = 0;
266 /* Initialize the output structure; we already found a codec,
267 * and the corresponding chroma we will be using. Since we can
268 * arbitrary scale, stick to the coordinates and aspect. */
269 p_vout->output.i_width = p_vout->render.i_width;
270 p_vout->output.i_height = p_vout->render.i_height;
271 p_vout->output.i_aspect = p_vout->render.i_aspect;
273 if( !p_vout->p_sys->i_opengl )
275 SetPort( p_vout->p_sys->p_qdport );
276 QTScaleMatrix( p_vout );
278 if( QTCreateSequence( p_vout ) )
280 msg_Err( p_vout, "unable to create sequence" );
286 p_vout->output.i_chroma = VLC_FOURCC('Y','U','Y','2');
287 p_vout->output.i_rmask = 0xFF0000;
288 p_vout->output.i_gmask = 0x00FF00;
289 p_vout->output.i_bmask = 0x0000FF;
292 /* Try to initialize up to QT_MAX_DIRECTBUFFERS direct buffers */
293 while( I_OUTPUTPICTURES <
294 ( p_vout->p_sys->i_opengl ? 3 : QT_MAX_DIRECTBUFFERS ) )
298 /* Find an empty picture slot */
299 for( i_index = 0; i_index < VOUT_MAX_PICTURES; i_index++ )
301 if( p_vout->p_picture[ i_index ].i_status == FREE_PICTURE )
303 p_pic = p_vout->p_picture + i_index;
308 /* Allocate the picture */
314 if( !p_vout->p_sys->i_opengl )
316 if( QTNewPicture( p_vout, p_pic ) )
323 vout_AllocatePicture( p_vout, p_pic, p_vout->output.i_chroma,
324 p_vout->output.i_width, p_vout->output.i_height,
325 p_vout->output.i_aspect );
328 p_pic->i_status = DESTROYED_PICTURE;
329 p_pic->i_type = DIRECT_PICTURE;
331 PP_OUTPUTPICTURE[ I_OUTPUTPICTURES ] = p_pic;
336 if( p_vout->p_sys->i_opengl )
338 p_vout->p_sys->o_glview->i_index = -1;
339 p_vout->p_sys->o_glview->i_init_done = 1;
345 /*****************************************************************************
346 * vout_End: terminate video thread output method
347 *****************************************************************************/
348 static void vout_End( vout_thread_t *p_vout )
352 if( !p_vout->p_sys->i_opengl )
354 QTDestroySequence( p_vout );
357 /* Free the direct buffers we allocated */
358 for( i_index = I_OUTPUTPICTURES; i_index; )
361 if( !p_vout->p_sys->i_opengl )
363 QTFreePicture( p_vout, PP_OUTPUTPICTURE[ i_index ] );
367 free( PP_OUTPUTPICTURE[ i_index ]->p_data_orig );
372 /*****************************************************************************
373 * CloseVideo: destroy video thread output method
374 *****************************************************************************/
375 void E_(CloseVideo) ( vlc_object_t *p_this )
377 vout_thread_t * p_vout = (vout_thread_t *)p_this;
379 if( CoDestroyWindow( p_vout ) )
381 msg_Err( p_vout, "unable to destroy window" );
384 if( !p_vout->p_sys->i_opengl )
386 if ( p_vout->p_sys->p_fullscreen_state != NULL )
387 EndFullScreen ( p_vout->p_sys->p_fullscreen_state, NULL );
391 free( p_vout->p_sys->p_matrix );
392 DisposeHandle( (Handle)p_vout->p_sys->h_img_descr );
395 free( p_vout->p_sys );
398 /*****************************************************************************
399 * vout_Manage: handle events
400 *****************************************************************************
401 * This function should be called regularly by video output thread. It manages
402 * console events. It returns a non null value on error.
403 *****************************************************************************/
404 static int vout_Manage( vout_thread_t *p_vout )
406 if( p_vout->i_changes & VOUT_FULLSCREEN_CHANGE )
408 if( CoToggleFullscreen( p_vout ) )
413 p_vout->i_changes &= ~VOUT_FULLSCREEN_CHANGE;
416 if( p_vout->i_changes & VOUT_SIZE_CHANGE )
418 if( !p_vout->p_sys->i_opengl )
420 QTScaleMatrix( p_vout );
421 SetDSequenceMatrix( p_vout->p_sys->i_seq,
422 p_vout->p_sys->p_matrix );
425 p_vout->i_changes &= ~VOUT_SIZE_CHANGE;
428 /* hide/show mouse cursor
429 * this code looks unnecessarily complicated, but is necessary like this.
430 * it has to deal with multiple monitors and therefore checks a lot */
431 if( !p_vout->p_sys->b_mouse_moved && p_vout->b_fullscreen )
433 if( mdate() - p_vout->p_sys->i_time_mouse_last_moved > 3000000 )
435 VLCHideMouse( p_vout, YES );
438 else if ( p_vout->p_sys->b_mouse_moved && p_vout->b_fullscreen )
440 VLCHideMouse( p_vout, NO );
443 /* disable screen saver */
444 UpdateSystemActivity( UsrActivity );
449 /*****************************************************************************
450 * vout_Display: displays previously rendered output
451 *****************************************************************************
452 * This function sends the currently rendered image to the display.
453 *****************************************************************************/
454 static void vout_Display( vout_thread_t *p_vout, picture_t *p_pic )
456 if( !p_vout->p_sys->i_opengl )
461 if( ( err = DecompressSequenceFrameS(
462 p_vout->p_sys->i_seq,
463 p_pic->p_sys->p_info,
464 p_pic->p_sys->i_size,
465 codecFlagUseImageBuffer, &flags, nil ) != noErr ) )
467 msg_Warn( p_vout, "DecompressSequenceFrameS failed: %d", err );
471 QDFlushPortBuffer( p_vout->p_sys->p_qdport, nil );
476 p_vout->p_sys->o_glview->i_index++;
477 p_vout->p_sys->o_glview->i_index %= 3;
478 [p_vout->p_sys->o_glview setNeedsDisplay: YES];
480 PP_OUTPUTPICTURE[(p_vout->p_sys->o_glview->i_index+1)%3]->p_data;
484 /*****************************************************************************
485 * CoSendRequest: send request to interface thread
486 *****************************************************************************
487 * Returns 0 on success, 1 otherwise
488 *****************************************************************************/
489 static int CoSendRequest( vout_thread_t *p_vout, SEL sel )
493 intf_thread_t * p_intf;
495 VLCVout * o_vlv = [[VLCVout alloc] init];
497 if( ( i_ret = ExecuteOnMainThread( o_vlv, sel, (void *)p_vout ) ) )
499 msg_Err( p_vout, "SendRequest: no way to communicate with mt" );
504 /*This makes this function dependant of the presence of a macosx
505 interface. We do not check if this interface exists, since it has
506 already been done before.*/
508 p_intf = [NSApp getIntf];
510 val.b_bool = VLC_TRUE;
511 var_Create(p_intf,"intf-change",VLC_VAR_BOOL);
512 var_Set(p_intf, "intf-change",val);
517 /*****************************************************************************
518 * CoCreateWindow: create new window
519 *****************************************************************************
520 * Returns 0 on success, 1 otherwise
521 *****************************************************************************/
522 static int CoCreateWindow( vout_thread_t *p_vout )
524 if( CoSendRequest( p_vout, @selector(createWindow:) ) )
526 msg_Err( p_vout, "CoSendRequest (createWindow) failed" );
533 /*****************************************************************************
534 * CoDestroyWindow: destroy window
535 *****************************************************************************
536 * Returns 0 on success, 1 otherwise
537 *****************************************************************************/
538 static int CoDestroyWindow( vout_thread_t *p_vout )
541 VLCHideMouse( p_vout, NO );
543 if( CoSendRequest( p_vout, @selector(destroyWindow:) ) )
545 msg_Err( p_vout, "CoSendRequest (destroyWindow) failed" );
552 /*****************************************************************************
553 * CoToggleFullscreen: toggle fullscreen
554 *****************************************************************************
555 * Returns 0 on success, 1 otherwise
556 *****************************************************************************/
557 static int CoToggleFullscreen( vout_thread_t *p_vout )
559 if( p_vout->p_sys->i_opengl )
565 QTDestroySequence( p_vout );
567 if( CoDestroyWindow( p_vout ) )
569 msg_Err( p_vout, "unable to destroy window" );
573 p_vout->b_fullscreen = !p_vout->b_fullscreen;
575 if( CoCreateWindow( p_vout ) )
577 msg_Err( p_vout, "unable to create window" );
581 SetPort( p_vout->p_sys->p_qdport );
582 QTScaleMatrix( p_vout );
584 if( QTCreateSequence( p_vout ) )
586 msg_Err( p_vout, "unable to create sequence" );
593 /*****************************************************************************
594 * VLCHideMouse: if b_hide then hide the cursor
595 *****************************************************************************/
596 static void VLCHideMouse ( vout_thread_t *p_vout, BOOL b_hide )
601 NSWindow *o_window = p_vout->p_sys->o_window;
602 NSView *o_contents = [o_window contentView];
604 s_rect = [o_contents bounds];
605 ml = [o_window convertScreenToBase:[NSEvent mouseLocation]];
606 ml = [o_contents convertPoint:ml fromView:nil];
607 b_inside = [o_contents mouse: ml inRect: s_rect];
609 if ( b_hide && b_inside )
611 /* only hide if mouse over VLCQTView */
612 [NSCursor setHiddenUntilMouseMoves: YES];
616 [NSCursor setHiddenUntilMouseMoves: NO];
618 p_vout->p_sys->b_mouse_moved = NO;
619 p_vout->p_sys->i_time_mouse_last_moved = mdate();
623 /*****************************************************************************
624 * QTScaleMatrix: scale matrix
625 *****************************************************************************/
626 static void QTScaleMatrix( vout_thread_t *p_vout )
629 unsigned int i_width, i_height;
630 Fixed factor_x, factor_y;
631 unsigned int i_offset_x = 0;
632 unsigned int i_offset_y = 0;
634 GetPortBounds( p_vout->p_sys->p_qdport, &s_rect );
636 i_width = s_rect.right - s_rect.left;
637 i_height = s_rect.bottom - s_rect.top;
639 if( i_height * p_vout->output.i_aspect < i_width * VOUT_ASPECT_FACTOR )
641 int i_adj_width = i_height * p_vout->output.i_aspect /
644 factor_x = FixDiv( Long2Fix( i_adj_width ),
645 Long2Fix( p_vout->output.i_width ) );
646 factor_y = FixDiv( Long2Fix( i_height ),
647 Long2Fix( p_vout->output.i_height ) );
649 i_offset_x = (i_width - i_adj_width) / 2;
653 int i_adj_height = i_width * VOUT_ASPECT_FACTOR /
654 p_vout->output.i_aspect;
656 factor_x = FixDiv( Long2Fix( i_width ),
657 Long2Fix( p_vout->output.i_width ) );
658 factor_y = FixDiv( Long2Fix( i_adj_height ),
659 Long2Fix( p_vout->output.i_height ) );
661 i_offset_y = (i_height - i_adj_height) / 2;
664 SetIdentityMatrix( p_vout->p_sys->p_matrix );
666 ScaleMatrix( p_vout->p_sys->p_matrix,
668 Long2Fix(0), Long2Fix(0) );
670 TranslateMatrix( p_vout->p_sys->p_matrix,
671 Long2Fix(i_offset_x),
672 Long2Fix(i_offset_y) );
676 /*****************************************************************************
677 * QTCreateSequence: create a new sequence
678 *****************************************************************************
679 * Returns 0 on success, 1 otherwise
680 *****************************************************************************/
681 static int QTCreateSequence( vout_thread_t *p_vout )
684 ImageDescriptionPtr p_descr;
686 HLock( (Handle)p_vout->p_sys->h_img_descr );
687 p_descr = *p_vout->p_sys->h_img_descr;
689 p_descr->idSize = sizeof(ImageDescription);
690 p_descr->cType = p_vout->p_sys->i_codec;
691 p_descr->version = 1;
692 p_descr->revisionLevel = 0;
693 p_descr->vendor = 'appl';
694 p_descr->width = p_vout->output.i_width;
695 p_descr->height = p_vout->output.i_height;
696 p_descr->hRes = Long2Fix(72);
697 p_descr->vRes = Long2Fix(72);
698 p_descr->spatialQuality = codecLosslessQuality;
699 p_descr->frameCount = 1;
700 p_descr->clutID = -1;
701 p_descr->dataSize = 0;
704 HUnlock( (Handle)p_vout->p_sys->h_img_descr );
706 if( ( err = DecompressSequenceBeginS(
707 &p_vout->p_sys->i_seq,
708 p_vout->p_sys->h_img_descr,
710 p_vout->p_sys->p_qdport,
712 p_vout->p_sys->p_matrix,
714 codecFlagUseImageBuffer,
715 codecLosslessQuality,
716 p_vout->p_sys->img_dc ) ) )
718 msg_Err( p_vout, "DecompressSequenceBeginS failed: %d", err );
725 /*****************************************************************************
726 * QTDestroySequence: destroy sequence
727 *****************************************************************************/
728 static void QTDestroySequence( vout_thread_t *p_vout )
730 CDSequenceEnd( p_vout->p_sys->i_seq );
733 /*****************************************************************************
734 * QTNewPicture: allocate a picture
735 *****************************************************************************
736 * Returns 0 on success, 1 otherwise
737 *****************************************************************************/
738 static int QTNewPicture( vout_thread_t *p_vout, picture_t *p_pic )
740 int i_width = p_vout->output.i_width;
741 int i_height = p_vout->output.i_height;
743 /* We know the chroma, allocate a buffer which will be used
744 * directly by the decoder */
745 p_pic->p_sys = malloc( sizeof( picture_sys_t ) );
747 if( p_pic->p_sys == NULL )
752 switch( p_vout->output.i_chroma )
754 case VLC_FOURCC('I','4','2','0'):
756 p_pic->p_sys->p_info = (void *)&p_pic->p_sys->pixmap_i420;
757 p_pic->p_sys->i_size = sizeof(PlanarPixmapInfoYUV420);
759 /* Allocate the memory buffer */
760 p_pic->p_data = vlc_memalign( &p_pic->p_data_orig,
761 16, i_width * i_height * 3 / 2 );
764 p_pic->Y_PIXELS = p_pic->p_data;
765 p_pic->p[Y_PLANE].i_lines = i_height;
766 p_pic->p[Y_PLANE].i_pitch = i_width;
767 p_pic->p[Y_PLANE].i_pixel_pitch = 1;
768 p_pic->p[Y_PLANE].i_visible_pitch = i_width;
771 p_pic->U_PIXELS = p_pic->Y_PIXELS + i_height * i_width;
772 p_pic->p[U_PLANE].i_lines = i_height / 2;
773 p_pic->p[U_PLANE].i_pitch = i_width / 2;
774 p_pic->p[U_PLANE].i_pixel_pitch = 1;
775 p_pic->p[U_PLANE].i_visible_pitch = i_width / 2;
778 p_pic->V_PIXELS = p_pic->U_PIXELS + i_height * i_width / 4;
779 p_pic->p[V_PLANE].i_lines = i_height / 2;
780 p_pic->p[V_PLANE].i_pitch = i_width / 2;
781 p_pic->p[V_PLANE].i_pixel_pitch = 1;
782 p_pic->p[V_PLANE].i_visible_pitch = i_width / 2;
784 /* We allocated 3 planes */
787 #define P p_pic->p_sys->pixmap_i420
788 P.componentInfoY.offset = (void *)p_pic->Y_PIXELS
789 - p_pic->p_sys->p_info;
790 P.componentInfoCb.offset = (void *)p_pic->U_PIXELS
791 - p_pic->p_sys->p_info;
792 P.componentInfoCr.offset = (void *)p_pic->V_PIXELS
793 - p_pic->p_sys->p_info;
795 P.componentInfoY.rowBytes = i_width;
796 P.componentInfoCb.rowBytes = i_width / 2;
797 P.componentInfoCr.rowBytes = i_width / 2;
803 /* Unknown chroma, tell the guy to get lost */
804 free( p_pic->p_sys );
805 msg_Err( p_vout, "never heard of chroma 0x%.8x (%4.4s)",
806 p_vout->output.i_chroma, (char*)&p_vout->output.i_chroma );
814 /*****************************************************************************
815 * QTFreePicture: destroy a picture allocated with QTNewPicture
816 *****************************************************************************/
817 static void QTFreePicture( vout_thread_t *p_vout, picture_t *p_pic )
819 switch( p_vout->output.i_chroma )
821 case VLC_FOURCC('I','4','2','0'):
822 free( p_pic->p_data_orig );
826 free( p_pic->p_sys );
829 /*****************************************************************************
830 * VLCWindow implementation
831 *****************************************************************************/
832 @implementation VLCWindow
834 - (void)setVout:(vout_thread_t *)_p_vout
839 - (vout_thread_t *)getVout
844 - (void)scaleWindowWithFactor: (float)factor
847 int i_corrected_height, i_corrected_width;
849 NSPoint topleftscreen;
851 if ( !p_vout->b_fullscreen )
854 topleftbase.y = [self frame].size.height;
855 topleftscreen = [self convertBaseToScreen: topleftbase];
857 if( p_vout->output.i_height * p_vout->output.i_aspect >
858 p_vout->output.i_width * VOUT_ASPECT_FACTOR )
860 i_corrected_width = p_vout->output.i_height * p_vout->output.i_aspect /
862 newsize.width = (int) ( i_corrected_width * factor );
863 newsize.height = (int) ( p_vout->render.i_height * factor );
867 i_corrected_height = p_vout->output.i_width * VOUT_ASPECT_FACTOR /
868 p_vout->output.i_aspect;
869 newsize.width = (int) ( p_vout->render.i_width * factor );
870 newsize.height = (int) ( i_corrected_height * factor );
873 [self setContentSize: newsize];
875 [self setFrameTopLeftPoint: topleftscreen];
876 p_vout->i_changes |= VOUT_SIZE_CHANGE;
880 - (void)toggleFloatOnTop
882 if( config_GetInt( p_vout, "video-on-top" ) )
884 config_PutInt( p_vout, "video-on-top", 0 );
885 [p_vout->p_sys->o_window setLevel: NSNormalWindowLevel];
889 config_PutInt( p_vout, "video-on-top", 1 );
890 [p_vout->p_sys->o_window setLevel: NSStatusWindowLevel];
894 - (void)toggleFullscreen
896 config_PutInt(p_vout, "fullscreen", !p_vout->b_fullscreen);
897 p_vout->i_changes |= VOUT_FULLSCREEN_CHANGE;
902 return( p_vout->b_fullscreen );
905 - (BOOL)canBecomeKeyWindow
910 - (void)keyDown:(NSEvent *)o_event
914 unsigned int i_pressed_modifiers = 0;
917 i_pressed_modifiers = [o_event modifierFlags];
919 if( i_pressed_modifiers & NSShiftKeyMask )
920 val.i_int |= KEY_MODIFIER_SHIFT;
921 if( i_pressed_modifiers & NSControlKeyMask )
922 val.i_int |= KEY_MODIFIER_CTRL;
923 if( i_pressed_modifiers & NSAlternateKeyMask )
924 val.i_int |= KEY_MODIFIER_ALT;
925 if( i_pressed_modifiers & NSCommandKeyMask )
926 val.i_int |= KEY_MODIFIER_COMMAND;
928 key = [[o_event charactersIgnoringModifiers] characterAtIndex: 0];
932 /* Escape should always get you out of fullscreen */
933 if( key == (unichar) 0x1b )
935 if( [self isFullscreen] )
937 [self toggleFullscreen];
940 else if ( key == ' ' )
942 playlist_t *p_playlist = vlc_object_find( p_vout, VLC_OBJECT_PLAYLIST,
944 if ( p_playlist != NULL )
946 playlist_Pause( p_playlist );
947 vlc_object_release( p_playlist);
952 val.i_int |= CocoaKeyToVLC( key );
953 var_Set( p_vout->p_vlc, "key-pressed", val );
958 [super keyDown: o_event];
964 NSMutableString * o_title;
965 playlist_t * p_playlist = vlc_object_find( p_vout, VLC_OBJECT_PLAYLIST,
968 if( p_playlist == NULL )
973 vlc_mutex_lock( &p_playlist->object_lock );
974 o_title = [NSMutableString stringWithUTF8String:
975 p_playlist->pp_items[p_playlist->i_index]->psz_uri];
976 vlc_mutex_unlock( &p_playlist->object_lock );
978 vlc_object_release( p_playlist );
982 NSRange prefix_range = [o_title rangeOfString: @"file:"];
983 if( prefix_range.location != NSNotFound )
985 [o_title deleteCharactersInRange: prefix_range];
988 [self setTitleWithRepresentedFilename: o_title];
993 [NSString stringWithCString: VOUT_TITLE " (QuickTime)"]];
997 /* This is actually the same as VLCControls::stop. */
998 - (BOOL)windowShouldClose:(id)sender
1000 playlist_t * p_playlist = vlc_object_find( p_vout, VLC_OBJECT_PLAYLIST,
1002 if( p_playlist == NULL )
1007 playlist_Stop( p_playlist );
1008 vlc_object_release( p_playlist );
1010 /* The window will be closed by the intf later. */
1016 /*****************************************************************************
1017 * VLCQTView implementation
1018 *****************************************************************************/
1019 @implementation VLCQTView
1021 - (void)drawRect:(NSRect)rect
1023 vout_thread_t * p_vout;
1024 id o_window = [self window];
1025 p_vout = (vout_thread_t *)[o_window getVout];
1027 [[NSColor blackColor] set];
1029 [super drawRect: rect];
1031 p_vout->i_changes |= VOUT_SIZE_CHANGE;
1034 - (BOOL)acceptsFirstResponder
1039 - (BOOL)becomeFirstResponder
1041 vout_thread_t * p_vout;
1042 id o_window = [self window];
1043 p_vout = (vout_thread_t *)[o_window getVout];
1045 [o_window setAcceptsMouseMovedEvents: YES];
1049 - (BOOL)resignFirstResponder
1051 vout_thread_t * p_vout;
1052 id o_window = [self window];
1053 p_vout = (vout_thread_t *)[o_window getVout];
1055 [o_window setAcceptsMouseMovedEvents: NO];
1056 VLCHideMouse( p_vout, NO );
1060 - (void)mouseDown:(NSEvent *)o_event
1062 vout_thread_t * p_vout;
1063 id o_window = [self window];
1064 p_vout = (vout_thread_t *)[o_window getVout];
1067 switch( [o_event type] )
1069 case NSLeftMouseDown:
1071 var_Get( p_vout, "mouse-button-down", &val );
1073 var_Set( p_vout, "mouse-button-down", val );
1078 [super mouseDown: o_event];
1083 - (void)otherMouseDown:(NSEvent *)o_event
1085 /* This is not the the wheel button. you need to poll the
1086 * mouseWheel event for that. other is a third, forth or fifth button */
1087 vout_thread_t * p_vout;
1088 id o_window = [self window];
1089 p_vout = (vout_thread_t *)[o_window getVout];
1092 switch( [o_event type] )
1094 case NSOtherMouseDown:
1096 var_Get( p_vout, "mouse-button-down", &val );
1098 var_Set( p_vout, "mouse-button-down", val );
1103 [super mouseDown: o_event];
1108 - (void)rightMouseDown:(NSEvent *)o_event
1110 vout_thread_t * p_vout;
1111 id o_window = [self window];
1112 p_vout = (vout_thread_t *)[o_window getVout];
1115 switch( [o_event type] )
1117 case NSRightMouseDown:
1119 var_Get( p_vout, "mouse-button-down", &val );
1121 var_Set( p_vout, "mouse-button-down", val );
1126 [super mouseDown: o_event];
1131 - (void)mouseUp:(NSEvent *)o_event
1133 vout_thread_t * p_vout;
1134 id o_window = [self window];
1135 p_vout = (vout_thread_t *)[o_window getVout];
1138 switch( [o_event type] )
1143 b_val.b_bool = VLC_TRUE;
1144 var_Set( p_vout, "mouse-clicked", b_val );
1146 var_Get( p_vout, "mouse-button-down", &val );
1148 var_Set( p_vout, "mouse-button-down", val );
1153 [super mouseUp: o_event];
1158 - (void)otherMouseUp:(NSEvent *)o_event
1160 vout_thread_t * p_vout;
1161 id o_window = [self window];
1162 p_vout = (vout_thread_t *)[o_window getVout];
1165 switch( [o_event type] )
1167 case NSOtherMouseUp:
1169 var_Get( p_vout, "mouse-button-down", &val );
1171 var_Set( p_vout, "mouse-button-down", val );
1176 [super mouseUp: o_event];
1181 - (void)rightMouseUp:(NSEvent *)o_event
1183 vout_thread_t * p_vout;
1184 id o_window = [self window];
1185 p_vout = (vout_thread_t *)[o_window getVout];
1188 switch( [o_event type] )
1190 case NSRightMouseUp:
1192 var_Get( p_vout, "mouse-button-down", &val );
1194 var_Set( p_vout, "mouse-button-down", val );
1199 [super mouseUp: o_event];
1204 - (void)mouseDragged:(NSEvent *)o_event
1206 [self mouseMoved:o_event];
1209 - (void)otherMouseDragged:(NSEvent *)o_event
1211 [self mouseMoved:o_event];
1214 - (void)rightMouseDragged:(NSEvent *)o_event
1216 [self mouseMoved:o_event];
1219 - (void)mouseMoved:(NSEvent *)o_event
1225 vout_thread_t * p_vout;
1226 id o_window = [self window];
1227 p_vout = (vout_thread_t *)[o_window getVout];
1229 s_rect = [self bounds];
1230 ml = [self convertPoint: [o_event locationInWindow] fromView: nil];
1231 b_inside = [self mouse: ml inRect: s_rect];
1236 int i_width, i_height, i_x, i_y;
1238 vout_PlacePicture( p_vout, (unsigned int)s_rect.size.width,
1239 (unsigned int)s_rect.size.height,
1240 &i_x, &i_y, &i_width, &i_height );
1242 val.i_int = ( ((int)ml.x) - i_x ) *
1243 p_vout->render.i_width / i_width;
1244 var_Set( p_vout, "mouse-x", val );
1246 val.i_int = ( ((int)ml.y) - i_y ) *
1247 p_vout->render.i_height / i_height;
1248 var_Set( p_vout, "mouse-y", val );
1250 val.b_bool = VLC_TRUE;
1251 var_Set( p_vout, "mouse-moved", val );
1252 p_vout->p_sys->i_time_mouse_last_moved = mdate();
1253 p_vout->p_sys->b_mouse_moved = YES;
1256 [super mouseMoved: o_event];
1261 /*****************************************************************************
1262 * VLCGLView implementation
1263 *****************************************************************************/
1264 @implementation VLCGLView
1267 - (id) initWithFrame: (NSRect) frame
1269 NSOpenGLPixelFormatAttribute attribs[] =
1271 NSOpenGLPFAAccelerated,
1272 NSOpenGLPFANoRecovery,
1273 NSOpenGLPFADoubleBuffer,
1277 NSOpenGLPixelFormat * fmt = [[NSOpenGLPixelFormat alloc]
1278 initWithAttributes: attribs];
1280 self = [super initWithFrame:frame pixelFormat: fmt];
1282 [[self openGLContext] makeCurrentContext];
1283 [[self openGLContext] update];
1285 glPixelStorei( GL_UNPACK_ALIGNMENT, 1 );
1286 glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE );
1287 glClearColor( 0.0, 0.0, 0.0, 0.0 );
1290 i_textures_loaded = 0;
1297 [[self openGLContext] update];
1298 NSRect bounds = [self bounds];
1299 glViewport( 0, 0, (GLint) bounds.size.width, (GLint) bounds.size.height );
1302 - (void)drawRect:(NSRect)rect
1304 vout_thread_t * p_vout;
1305 id o_window = [self window];
1306 p_vout = (vout_thread_t *)[o_window getVout];
1309 /* Make this current context */
1310 [[self openGLContext] makeCurrentContext];
1312 /* Black background */
1313 glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
1320 if( !i_textures_loaded )
1322 glGenTextures( 3, pi_textures );
1323 i_textures_loaded = 1;
1327 glDisable( GL_TEXTURE_2D );
1328 glEnable( GL_TEXTURE_RECTANGLE_EXT );
1329 glBindTexture(GL_TEXTURE_RECTANGLE_EXT, pi_textures[i_index]);
1331 /* Map our buffer to the texture */
1332 glTexImage2D( GL_TEXTURE_RECTANGLE_EXT, 0, GL_RGBA,
1333 p_vout->output.i_width, p_vout->output.i_height, 0,
1334 GL_YCBCR_422_APPLE, GL_UNSIGNED_SHORT_8_8_APPLE,
1335 PP_OUTPUTPICTURE[i_index]->p_data );
1338 glDisable( GL_TEXTURE_2D );
1339 glEnable( GL_TEXTURE_RECTANGLE_EXT );
1340 glBindTexture(GL_TEXTURE_RECTANGLE_EXT, pi_textures[i_index] );
1342 /* Turn on AGP transferts */
1343 glTexParameterf( GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_PRIORITY, 0.0 );
1345 /* Use AGP texturing */
1346 glTexParameteri( GL_TEXTURE_RECTANGLE_EXT,
1347 GL_TEXTURE_STORAGE_HINT_APPLE, GL_STORAGE_CACHED_APPLE );
1349 /* Tell the driver not to make a copy of the texture but to use out
1351 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
1353 /* Linear interpolation */
1354 glTexParameteri( GL_TEXTURE_RECTANGLE_EXT,
1355 GL_TEXTURE_MIN_FILTER, GL_LINEAR );
1356 glTexParameteri( GL_TEXTURE_RECTANGLE_EXT,
1357 GL_TEXTURE_MAG_FILTER, GL_LINEAR );
1359 /* I have no idea what this exactly does, but it seems to be
1360 necessary for scaling */
1361 glTexParameteri( GL_TEXTURE_RECTANGLE_EXT,
1362 GL_TEXTURE_WRAP_S,GL_CLAMP_TO_EDGE );
1363 glTexParameteri( GL_TEXTURE_RECTANGLE_EXT,
1364 GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
1365 glPixelStorei( GL_UNPACK_ROW_LENGTH, 0 );
1368 /* Draw a quad with our texture on it */
1369 NSRect bounds = [self bounds];
1371 if( bounds.size.height * p_vout->i_window_width <
1372 bounds.size.width * p_vout->i_window_height )
1374 f_x = bounds.size.height * p_vout->i_window_width /
1375 bounds.size.width / p_vout->i_window_height;
1381 f_y = bounds.size.width * p_vout->i_window_height /
1382 bounds.size.height / p_vout->i_window_width;
1385 glBegin( GL_QUADS );
1387 glTexCoord2f( 0.0f, 0.0f );
1388 glVertex2f( - f_x, f_y );
1390 glTexCoord2f( 0.0f, (float) p_vout->output.i_height );
1391 glVertex2f( - f_x, - f_y );
1393 glTexCoord2f( (float) p_vout->output.i_width,
1394 (float) p_vout->output.i_height );
1395 glVertex2f( f_x, - f_y );
1397 glTexCoord2f( (float) p_vout->output.i_width, 0.0f );
1398 glVertex2f( f_x, f_y );
1401 /* Wait for the job to be done */
1402 [[self openGLContext] flushBuffer];
1407 /*****************************************************************************
1408 * VLCVout implementation
1409 *****************************************************************************/
1410 @implementation VLCVout
1412 - (void)createWindow:(NSValue *)o_value
1416 NSScreen * o_screen;
1417 vout_thread_t * p_vout;
1418 vlc_bool_t b_main_screen;
1420 p_vout = (vout_thread_t *)[o_value pointerValue];
1422 p_vout->p_sys->o_window = [VLCWindow alloc];
1423 [p_vout->p_sys->o_window setVout: p_vout];
1424 [p_vout->p_sys->o_window setReleasedWhenClosed: YES];
1426 if( var_Get( p_vout, "video-device", &val ) < 0 )
1428 o_screen = [NSScreen mainScreen];
1433 NSArray *o_screens = [NSScreen screens];
1434 unsigned int i_index = val.i_int;
1436 if( [o_screens count] < i_index )
1438 o_screen = [NSScreen mainScreen];
1444 o_screen = [o_screens objectAtIndex: i_index];
1445 config_PutInt( p_vout, "macosx-vdev", i_index );
1446 b_main_screen = (i_index == 0);
1450 if( p_vout->p_sys->i_opengl )
1452 /* XXX Fix fullscreen mode */
1453 p_vout->b_fullscreen = 0;
1456 if( p_vout->b_fullscreen )
1458 NSRect screen_rect = [o_screen frame];
1459 screen_rect.origin.x = screen_rect.origin.y = 0;
1461 if ( b_main_screen && p_vout->p_sys->p_fullscreen_state == NULL )
1462 BeginFullScreen( &p_vout->p_sys->p_fullscreen_state, NULL, 0, 0,
1463 NULL, NULL, fullScreenAllowEvents );
1465 [p_vout->p_sys->o_window
1466 initWithContentRect: screen_rect
1467 styleMask: NSBorderlessWindowMask
1468 backing: NSBackingStoreBuffered
1469 defer: NO screen: o_screen];
1471 //[p_vout->p_sys->o_window setLevel: NSPopUpMenuWindowLevel - 1];
1472 p_vout->p_sys->b_mouse_moved = YES;
1473 p_vout->p_sys->i_time_mouse_last_moved = mdate();
1477 unsigned int i_stylemask = NSTitledWindowMask |
1478 NSMiniaturizableWindowMask |
1479 NSClosableWindowMask |
1480 NSResizableWindowMask;
1482 if( !p_vout->p_sys->i_opengl )
1484 if ( p_vout->p_sys->p_fullscreen_state != NULL )
1485 EndFullScreen ( p_vout->p_sys->p_fullscreen_state, NULL );
1486 p_vout->p_sys->p_fullscreen_state = NULL;
1489 [p_vout->p_sys->o_window
1490 initWithContentRect: p_vout->p_sys->s_rect
1491 styleMask: i_stylemask
1492 backing: NSBackingStoreBuffered
1493 defer: NO screen: o_screen];
1495 [p_vout->p_sys->o_window setAlphaValue: config_GetFloat( p_vout, "macosx-opaqueness" )];
1497 if( config_GetInt( p_vout, "video-on-top" ) )
1499 [p_vout->p_sys->o_window setLevel: NSStatusWindowLevel];
1502 if( !p_vout->p_sys->b_pos_saved )
1504 [p_vout->p_sys->o_window center];
1508 if( !p_vout->p_sys->i_opengl )
1510 o_view = [[VLCQTView alloc] init];
1511 /* FIXME: [o_view setMenu:] */
1512 [p_vout->p_sys->o_window setContentView: o_view];
1513 [o_view autorelease];
1516 p_vout->p_sys->p_qdport = [o_view qdPort];
1517 [o_view unlockFocus];
1521 #define o_glview p_vout->p_sys->o_glview
1522 o_glview = [[VLCGLView alloc] initWithFrame: p_vout->p_sys->s_rect];
1523 [p_vout->p_sys->o_window setContentView: o_glview];
1524 [o_glview autorelease];
1528 [p_vout->p_sys->o_window updateTitle];
1529 [p_vout->p_sys->o_window makeKeyAndOrderFront: nil];
1533 - (void)destroyWindow:(NSValue *)o_value
1535 vout_thread_t * p_vout;
1537 p_vout = (vout_thread_t *)[o_value pointerValue];
1539 if( !p_vout->b_fullscreen )
1543 s_rect = [[p_vout->p_sys->o_window contentView] frame];
1544 p_vout->p_sys->s_rect.size = s_rect.size;
1546 s_rect = [p_vout->p_sys->o_window frame];
1547 p_vout->p_sys->s_rect.origin = s_rect.origin;
1549 p_vout->p_sys->b_pos_saved = YES;
1552 p_vout->p_sys->p_qdport = nil;
1553 [p_vout->p_sys->o_window close];
1554 p_vout->p_sys->o_window = nil;