1 /*****************************************************************************
2 * vout.m: MacOS X video output plugin
3 *****************************************************************************
4 * Copyright (C) 2001-2003 VideoLAN
5 * $Id: vout.m,v 1.48 2003/05/21 15:40:03 hartman 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>
39 #define QT_MAX_DIRECTBUFFERS 10
40 #define VL_MAX_DISPLAYS 16
47 /* When using I420 output */
48 PlanarPixmapInfoYUV420 pixmap_i420;
51 /*****************************************************************************
53 *****************************************************************************/
54 static int vout_Init ( vout_thread_t * );
55 static void vout_End ( vout_thread_t * );
56 static int vout_Manage ( vout_thread_t * );
57 static void vout_Display ( vout_thread_t *, picture_t * );
59 static int CoSendRequest ( vout_thread_t *, SEL );
60 static int CoCreateWindow ( vout_thread_t * );
61 static int CoDestroyWindow ( vout_thread_t * );
62 static int CoToggleFullscreen ( vout_thread_t * );
64 static void VLCHideMouse ( vout_thread_t *, BOOL );
66 static void QTScaleMatrix ( vout_thread_t * );
67 static int QTCreateSequence ( vout_thread_t * );
68 static void QTDestroySequence ( vout_thread_t * );
69 static int QTNewPicture ( vout_thread_t *, picture_t * );
70 static void QTFreePicture ( vout_thread_t *, picture_t * );
72 /*****************************************************************************
73 * OpenVideo: allocates MacOS X video thread output method
74 *****************************************************************************
75 * This function allocates and initializes a MacOS X vout method.
76 *****************************************************************************/
77 int E_(OpenVideo) ( vlc_object_t *p_this )
79 vout_thread_t * p_vout = (vout_thread_t *)p_this;
83 p_vout->p_sys = malloc( sizeof( vout_sys_t ) );
84 if( p_vout->p_sys == NULL )
86 msg_Err( p_vout, "out of memory" );
90 memset( p_vout->p_sys, 0, sizeof( vout_sys_t ) );
92 /* Wait for a MacOS X interface to appear. Timeout is 2 seconds. */
93 for( i_timeout = 20 ; i_timeout-- ; )
97 msleep( INTF_IDLE_SLEEP );
103 /* no MacOS X intf, unable to communicate with MT */
104 msg_Err( p_vout, "no MacOS X interface present" );
105 free( p_vout->p_sys );
109 if( [NSApp respondsToSelector: @selector(getIntf)] )
111 intf_thread_t * p_intf;
113 for( i_timeout = 10 ; i_timeout-- ; )
115 if( ( p_intf = [NSApp getIntf] ) == NULL )
117 msleep( INTF_IDLE_SLEEP );
123 msg_Err( p_vout, "MacOS X intf has getIntf, but is NULL" );
124 free( p_vout->p_sys );
129 p_vout->p_sys->h_img_descr =
130 (ImageDescriptionHandle)NewHandleClear( sizeof(ImageDescription) );
131 p_vout->p_sys->p_matrix = (MatrixRecordPtr)malloc( sizeof(MatrixRecord) );
132 p_vout->p_sys->p_fullscreen_state = NULL;
134 p_vout->p_sys->b_mouse_pointer_visible = YES;
135 p_vout->p_sys->b_mouse_moved = YES;
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 if( ( err = EnterMovies() ) != noErr )
144 msg_Err( p_vout, "EnterMovies failed: %d", err );
145 free( p_vout->p_sys->p_matrix );
146 DisposeHandle( (Handle)p_vout->p_sys->h_img_descr );
147 free( p_vout->p_sys );
151 if( vout_ChromaCmp( p_vout->render.i_chroma, VLC_FOURCC('I','4','2','0') ) )
153 /* Damn QT isn't thread safe. so keep a lock in the p_vlc object */
154 vlc_mutex_lock( &p_vout->p_vlc->quicktime_lock );
156 err = FindCodec( kYUV420CodecType, bestSpeedCodec,
157 nil, &p_vout->p_sys->img_dc );
159 vlc_mutex_unlock( &p_vout->p_vlc->quicktime_lock );
160 if( err == noErr && p_vout->p_sys->img_dc != 0 )
162 p_vout->output.i_chroma = VLC_FOURCC('I','4','2','0');
163 p_vout->p_sys->i_codec = kYUV420CodecType;
167 msg_Err( p_vout, "failed to find an appropriate codec" );
172 msg_Err( p_vout, "chroma 0x%08x not supported",
173 p_vout->render.i_chroma );
176 if( p_vout->p_sys->img_dc == 0 )
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 NSAutoreleasePool * o_pool = [[NSAutoreleasePool alloc] init];
185 NSArray * o_screens = [NSScreen screens];
186 if( [o_screens count] > 0 && var_Type( p_vout, "video-device" ) == 0 )
189 vlc_value_t val, text;
192 int i_option = config_GetInt( p_vout, "macosx-vdev" );
194 var_Create( p_vout, "video-device", VLC_VAR_INTEGER |
196 text.psz_string = _("Video device");
197 var_Change( p_vout, "video-device", VLC_VAR_SETTEXT, &text, NULL );
199 NSEnumerator * o_enumerator = [o_screens objectEnumerator];
201 while( (o_screen = [o_enumerator nextObject]) != NULL )
204 NSRect s_rect = [o_screen frame];
206 snprintf( psz_temp, sizeof(psz_temp)/sizeof(psz_temp[0])-1,
207 "%s %d (%dx%d)", _("Screen"), i,
208 (int)s_rect.size.width, (int)s_rect.size.height );
210 text.psz_string = psz_temp;
212 var_Change( p_vout, "video-device",
213 VLC_VAR_ADDCHOICE, &val, &text );
215 if( ( i - 1 ) == i_option )
217 var_Set( p_vout, "video-device", val );
222 var_AddCallback( p_vout, "video-device", vout_VarCallback,
225 val.b_bool = VLC_TRUE;
226 var_Set( p_vout, "intf-change", val );
230 if( CoCreateWindow( p_vout ) )
232 msg_Err( p_vout, "unable to create window" );
233 free( p_vout->p_sys->p_matrix );
234 DisposeHandle( (Handle)p_vout->p_sys->h_img_descr );
235 free( p_vout->p_sys );
239 p_vout->pf_init = vout_Init;
240 p_vout->pf_end = vout_End;
241 p_vout->pf_manage = vout_Manage;
242 p_vout->pf_render = NULL;
243 p_vout->pf_display = vout_Display;
248 /*****************************************************************************
249 * vout_Init: initialize video thread output method
250 *****************************************************************************/
251 static int vout_Init( vout_thread_t *p_vout )
256 I_OUTPUTPICTURES = 0;
258 /* Initialize the output structure; we already found a codec,
259 * and the corresponding chroma we will be using. Since we can
260 * arbitrary scale, stick to the coordinates and aspect. */
261 p_vout->output.i_width = p_vout->render.i_width;
262 p_vout->output.i_height = p_vout->render.i_height;
263 p_vout->output.i_aspect = p_vout->render.i_aspect;
265 SetPort( p_vout->p_sys->p_qdport );
266 QTScaleMatrix( p_vout );
268 if( QTCreateSequence( p_vout ) )
270 msg_Err( p_vout, "unable to create sequence" );
274 /* Try to initialize up to QT_MAX_DIRECTBUFFERS direct buffers */
275 while( I_OUTPUTPICTURES < QT_MAX_DIRECTBUFFERS )
279 /* Find an empty picture slot */
280 for( i_index = 0; i_index < VOUT_MAX_PICTURES; i_index++ )
282 if( p_vout->p_picture[ i_index ].i_status == FREE_PICTURE )
284 p_pic = p_vout->p_picture + i_index;
289 /* Allocate the picture */
290 if( p_pic == NULL || QTNewPicture( p_vout, p_pic ) )
295 p_pic->i_status = DESTROYED_PICTURE;
296 p_pic->i_type = DIRECT_PICTURE;
298 PP_OUTPUTPICTURE[ I_OUTPUTPICTURES ] = p_pic;
306 /*****************************************************************************
307 * vout_End: terminate video thread output method
308 *****************************************************************************/
309 static void vout_End( vout_thread_t *p_vout )
313 QTDestroySequence( p_vout );
315 /* Free the direct buffers we allocated */
316 for( i_index = I_OUTPUTPICTURES; i_index; )
319 QTFreePicture( p_vout, PP_OUTPUTPICTURE[ i_index ] );
323 /*****************************************************************************
324 * CloseVideo: destroy video thread output method
325 *****************************************************************************/
326 void E_(CloseVideo) ( vlc_object_t *p_this )
328 vout_thread_t * p_vout = (vout_thread_t *)p_this;
330 if( CoDestroyWindow( p_vout ) )
332 msg_Err( p_vout, "unable to destroy window" );
335 if ( p_vout->p_sys->p_fullscreen_state != NULL )
336 EndFullScreen ( p_vout->p_sys->p_fullscreen_state, NULL );
340 free( p_vout->p_sys->p_matrix );
341 DisposeHandle( (Handle)p_vout->p_sys->h_img_descr );
343 free( p_vout->p_sys );
346 /*****************************************************************************
347 * vout_Manage: handle events
348 *****************************************************************************
349 * This function should be called regularly by video output thread. It manages
350 * console events. It returns a non null value on error.
351 *****************************************************************************/
352 static int vout_Manage( vout_thread_t *p_vout )
354 if( p_vout->i_changes & VOUT_FULLSCREEN_CHANGE )
356 if( CoToggleFullscreen( p_vout ) )
361 p_vout->i_changes &= ~VOUT_FULLSCREEN_CHANGE;
364 if( p_vout->i_changes & VOUT_SIZE_CHANGE )
366 QTScaleMatrix( p_vout );
367 SetDSequenceMatrix( p_vout->p_sys->i_seq,
368 p_vout->p_sys->p_matrix );
370 p_vout->i_changes &= ~VOUT_SIZE_CHANGE;
373 /* hide/show mouse cursor
374 * this code looks unnecessarily complicated, but is necessary like this.
375 * it has to deal with multiple monitors and therefore checks a lot */
376 if( !p_vout->p_sys->b_mouse_moved && p_vout->b_fullscreen )
378 if( mdate() - p_vout->p_sys->i_time_mouse_last_moved > 2000000 &&
379 p_vout->p_sys->b_mouse_pointer_visible )
381 VLCHideMouse( p_vout, YES );
383 else if ( !p_vout->p_sys->b_mouse_pointer_visible )
385 vlc_bool_t b_playing = NO;
386 playlist_t * p_playlist = vlc_object_find( p_vout, VLC_OBJECT_PLAYLIST,
389 if ( p_playlist != nil )
391 vlc_mutex_lock( &p_playlist->object_lock );
392 if( p_playlist->p_input != NULL )
394 vlc_mutex_lock( &p_playlist->p_input->stream.stream_lock );
395 b_playing = p_playlist->p_input->stream.control.i_status != PAUSE_S;
396 vlc_mutex_unlock( &p_playlist->p_input->stream.stream_lock );
398 vlc_mutex_unlock( &p_playlist->object_lock );
399 vlc_object_release( p_playlist );
403 VLCHideMouse( p_vout, NO );
407 else if ( p_vout->p_sys->b_mouse_moved && p_vout->b_fullscreen )
409 if( !p_vout->p_sys->b_mouse_pointer_visible )
411 VLCHideMouse( p_vout, NO );
415 p_vout->p_sys->b_mouse_moved = NO;
422 /*****************************************************************************
423 * vout_Display: displays previously rendered output
424 *****************************************************************************
425 * This function sends the currently rendered image to the display.
426 *****************************************************************************/
427 static void vout_Display( vout_thread_t *p_vout, picture_t *p_pic )
432 if( ( err = DecompressSequenceFrameS(
433 p_vout->p_sys->i_seq,
434 p_pic->p_sys->p_info,
435 p_pic->p_sys->i_size,
436 codecFlagUseImageBuffer, &flags, nil ) != noErr ) )
438 msg_Warn( p_vout, "DecompressSequenceFrameS failed: %d", err );
442 QDFlushPortBuffer( p_vout->p_sys->p_qdport, nil );
446 /*****************************************************************************
447 * CoSendRequest: send request to interface thread
448 *****************************************************************************
449 * Returns 0 on success, 1 otherwise
450 *****************************************************************************/
451 static int CoSendRequest( vout_thread_t *p_vout, SEL sel )
455 VLCVout * o_vlv = [[VLCVout alloc] init];
457 if( ( i_ret = ExecuteOnMainThread( o_vlv, sel, (void *)p_vout ) ) )
459 msg_Err( p_vout, "SendRequest: no way to communicate with mt" );
467 /*****************************************************************************
468 * CoCreateWindow: create new window
469 *****************************************************************************
470 * Returns 0 on success, 1 otherwise
471 *****************************************************************************/
472 static int CoCreateWindow( vout_thread_t *p_vout )
474 if( CoSendRequest( p_vout, @selector(createWindow:) ) )
476 msg_Err( p_vout, "CoSendRequest (createWindow) failed" );
483 /*****************************************************************************
484 * CoDestroyWindow: destroy window
485 *****************************************************************************
486 * Returns 0 on success, 1 otherwise
487 *****************************************************************************/
488 static int CoDestroyWindow( vout_thread_t *p_vout )
490 if( !p_vout->p_sys->b_mouse_pointer_visible )
492 VLCHideMouse( p_vout, NO );
495 if( CoSendRequest( p_vout, @selector(destroyWindow:) ) )
497 msg_Err( p_vout, "CoSendRequest (destroyWindow) failed" );
504 /*****************************************************************************
505 * CoToggleFullscreen: toggle fullscreen
506 *****************************************************************************
507 * Returns 0 on success, 1 otherwise
508 *****************************************************************************/
509 static int CoToggleFullscreen( vout_thread_t *p_vout )
511 QTDestroySequence( p_vout );
513 if( CoDestroyWindow( p_vout ) )
515 msg_Err( p_vout, "unable to destroy window" );
519 p_vout->b_fullscreen = !p_vout->b_fullscreen;
521 config_PutInt( p_vout, "fullscreen", p_vout->b_fullscreen );
523 if( CoCreateWindow( p_vout ) )
525 msg_Err( p_vout, "unable to create window" );
529 SetPort( p_vout->p_sys->p_qdport );
530 QTScaleMatrix( p_vout );
532 if( QTCreateSequence( p_vout ) )
534 msg_Err( p_vout, "unable to create sequence" );
541 /*****************************************************************************
542 * VLCHideMouse: if b_hide then hide the cursor
543 *****************************************************************************/
544 static void VLCHideMouse ( vout_thread_t *p_vout, BOOL b_hide )
549 NSWindow *o_window = p_vout->p_sys->o_window;
550 NSView *o_contents = [o_window contentView];
552 s_rect = [o_contents bounds];
553 ml = [o_window convertScreenToBase:[NSEvent mouseLocation]];
554 ml = [o_contents convertPoint:ml fromView:nil];
555 b_inside = [o_contents mouse: ml inRect: s_rect];
557 if ( b_hide && b_inside )
559 /* only hide if mouse over VLCView */
561 p_vout->p_sys->b_mouse_pointer_visible = 0;
566 p_vout->p_sys->b_mouse_pointer_visible = 1;
568 p_vout->p_sys->b_mouse_moved = NO;
569 p_vout->p_sys->i_time_mouse_last_moved = mdate();
573 /*****************************************************************************
574 * QTScaleMatrix: scale matrix
575 *****************************************************************************/
576 static void QTScaleMatrix( vout_thread_t *p_vout )
579 unsigned int i_width, i_height;
580 Fixed factor_x, factor_y;
581 unsigned int i_offset_x = 0;
582 unsigned int i_offset_y = 0;
584 GetPortBounds( p_vout->p_sys->p_qdport, &s_rect );
586 i_width = s_rect.right - s_rect.left;
587 i_height = s_rect.bottom - s_rect.top;
589 if( i_height * p_vout->output.i_aspect < i_width * VOUT_ASPECT_FACTOR )
591 int i_adj_width = i_height * p_vout->output.i_aspect /
594 factor_x = FixDiv( Long2Fix( i_adj_width ),
595 Long2Fix( p_vout->output.i_width ) );
596 factor_y = FixDiv( Long2Fix( i_height ),
597 Long2Fix( p_vout->output.i_height ) );
599 i_offset_x = (i_width - i_adj_width) / 2;
603 int i_adj_height = i_width * VOUT_ASPECT_FACTOR /
604 p_vout->output.i_aspect;
606 factor_x = FixDiv( Long2Fix( i_width ),
607 Long2Fix( p_vout->output.i_width ) );
608 factor_y = FixDiv( Long2Fix( i_adj_height ),
609 Long2Fix( p_vout->output.i_height ) );
611 i_offset_y = (i_height - i_adj_height) / 2;
614 SetIdentityMatrix( p_vout->p_sys->p_matrix );
616 ScaleMatrix( p_vout->p_sys->p_matrix,
618 Long2Fix(0), Long2Fix(0) );
620 TranslateMatrix( p_vout->p_sys->p_matrix,
621 Long2Fix(i_offset_x),
622 Long2Fix(i_offset_y) );
625 /*****************************************************************************
626 * QTCreateSequence: create a new sequence
627 *****************************************************************************
628 * Returns 0 on success, 1 otherwise
629 *****************************************************************************/
630 static int QTCreateSequence( vout_thread_t *p_vout )
633 ImageDescriptionPtr p_descr;
635 HLock( (Handle)p_vout->p_sys->h_img_descr );
636 p_descr = *p_vout->p_sys->h_img_descr;
638 p_descr->idSize = sizeof(ImageDescription);
639 p_descr->cType = p_vout->p_sys->i_codec;
640 p_descr->version = 1;
641 p_descr->revisionLevel = 0;
642 p_descr->vendor = 'appl';
643 p_descr->width = p_vout->output.i_width;
644 p_descr->height = p_vout->output.i_height;
645 p_descr->hRes = Long2Fix(72);
646 p_descr->vRes = Long2Fix(72);
647 p_descr->spatialQuality = codecLosslessQuality;
648 p_descr->frameCount = 1;
649 p_descr->clutID = -1;
650 p_descr->dataSize = 0;
653 HUnlock( (Handle)p_vout->p_sys->h_img_descr );
655 if( ( err = DecompressSequenceBeginS(
656 &p_vout->p_sys->i_seq,
657 p_vout->p_sys->h_img_descr,
659 p_vout->p_sys->p_qdport,
661 p_vout->p_sys->p_matrix,
663 codecFlagUseImageBuffer,
664 codecLosslessQuality,
665 p_vout->p_sys->img_dc ) ) )
667 msg_Err( p_vout, "DecompressSequenceBeginS failed: %d", err );
674 /*****************************************************************************
675 * QTDestroySequence: destroy sequence
676 *****************************************************************************/
677 static void QTDestroySequence( vout_thread_t *p_vout )
679 CDSequenceEnd( p_vout->p_sys->i_seq );
682 /*****************************************************************************
683 * QTNewPicture: allocate a picture
684 *****************************************************************************
685 * Returns 0 on success, 1 otherwise
686 *****************************************************************************/
687 static int QTNewPicture( vout_thread_t *p_vout, picture_t *p_pic )
689 int i_width = p_vout->output.i_width;
690 int i_height = p_vout->output.i_height;
692 /* We know the chroma, allocate a buffer which will be used
693 * directly by the decoder */
694 p_pic->p_sys = malloc( sizeof( picture_sys_t ) );
696 if( p_pic->p_sys == NULL )
701 switch( p_vout->output.i_chroma )
703 case VLC_FOURCC('I','4','2','0'):
705 p_pic->p_sys->p_info = (void *)&p_pic->p_sys->pixmap_i420;
706 p_pic->p_sys->i_size = sizeof(PlanarPixmapInfoYUV420);
708 /* Allocate the memory buffer */
709 p_pic->p_data = vlc_memalign( &p_pic->p_data_orig,
710 16, i_width * i_height * 3 / 2 );
713 p_pic->Y_PIXELS = p_pic->p_data;
714 p_pic->p[Y_PLANE].i_lines = i_height;
715 p_pic->p[Y_PLANE].i_pitch = i_width;
716 p_pic->p[Y_PLANE].i_pixel_pitch = 1;
717 p_pic->p[Y_PLANE].i_visible_pitch = i_width;
720 p_pic->U_PIXELS = p_pic->Y_PIXELS + i_height * i_width;
721 p_pic->p[U_PLANE].i_lines = i_height / 2;
722 p_pic->p[U_PLANE].i_pitch = i_width / 2;
723 p_pic->p[U_PLANE].i_pixel_pitch = 1;
724 p_pic->p[U_PLANE].i_visible_pitch = i_width / 2;
727 p_pic->V_PIXELS = p_pic->U_PIXELS + i_height * i_width / 4;
728 p_pic->p[V_PLANE].i_lines = i_height / 2;
729 p_pic->p[V_PLANE].i_pitch = i_width / 2;
730 p_pic->p[V_PLANE].i_pixel_pitch = 1;
731 p_pic->p[V_PLANE].i_visible_pitch = i_width / 2;
733 /* We allocated 3 planes */
736 #define P p_pic->p_sys->pixmap_i420
737 P.componentInfoY.offset = (void *)p_pic->Y_PIXELS
738 - p_pic->p_sys->p_info;
739 P.componentInfoCb.offset = (void *)p_pic->U_PIXELS
740 - p_pic->p_sys->p_info;
741 P.componentInfoCr.offset = (void *)p_pic->V_PIXELS
742 - p_pic->p_sys->p_info;
744 P.componentInfoY.rowBytes = i_width;
745 P.componentInfoCb.rowBytes = i_width / 2;
746 P.componentInfoCr.rowBytes = i_width / 2;
752 /* Unknown chroma, tell the guy to get lost */
753 free( p_pic->p_sys );
754 msg_Err( p_vout, "never heard of chroma 0x%.8x (%4.4s)",
755 p_vout->output.i_chroma, (char*)&p_vout->output.i_chroma );
763 /*****************************************************************************
764 * QTFreePicture: destroy a picture allocated with QTNewPicture
765 *****************************************************************************/
766 static void QTFreePicture( vout_thread_t *p_vout, picture_t *p_pic )
768 switch( p_vout->output.i_chroma )
770 case VLC_FOURCC('I','4','2','0'):
771 free( p_pic->p_data_orig );
775 free( p_pic->p_sys );
778 /*****************************************************************************
779 * VLCWindow implementation
780 *****************************************************************************/
781 @implementation VLCWindow
783 - (void)setVout:(vout_thread_t *)_p_vout
788 - (vout_thread_t *)getVout
793 - (void)scaleWindowWithFactor: (float)factor
796 int i_corrected_height, i_corrected_width;
798 NSPoint topleftscreen;
800 if ( !p_vout->b_fullscreen )
803 topleftbase.y = [self frame].size.height;
804 topleftscreen = [self convertBaseToScreen: topleftbase];
806 if( p_vout->output.i_height * p_vout->output.i_aspect >
807 p_vout->output.i_width * VOUT_ASPECT_FACTOR )
809 i_corrected_width = p_vout->output.i_height * p_vout->output.i_aspect /
811 newsize.width = (int) ( i_corrected_width * factor );
812 newsize.height = (int) ( p_vout->render.i_height * factor );
816 i_corrected_height = p_vout->output.i_width * VOUT_ASPECT_FACTOR /
817 p_vout->output.i_aspect;
818 newsize.width = (int) ( p_vout->render.i_width * factor );
819 newsize.height = (int) ( i_corrected_height * factor );
822 [self setContentSize: newsize];
824 [self setFrameTopLeftPoint: topleftscreen];
825 p_vout->i_changes |= VOUT_SIZE_CHANGE;
829 - (void)toggleFloatOnTop
831 if( config_GetInt( p_vout, "macosx-float" ) )
833 config_PutInt( p_vout, "macosx-float", 0 );
834 [p_vout->p_sys->o_window setLevel: NSNormalWindowLevel];
838 config_PutInt( p_vout, "macosx-float", 1 );
839 [p_vout->p_sys->o_window setLevel: NSStatusWindowLevel];
843 - (void)toggleFullscreen
845 p_vout->i_changes |= VOUT_FULLSCREEN_CHANGE;
850 return( p_vout->b_fullscreen );
853 - (BOOL)canBecomeKeyWindow
858 - (void)keyDown:(NSEvent *)o_event
862 if( [[o_event characters] length] )
864 key = [[o_event characters] characterAtIndex: 0];
870 [self toggleFullscreen];
873 case (unichar)0x1b: /* escape */
874 if( [self isFullscreen] )
876 [self toggleFullscreen];
881 p_vout->p_vlc->b_die = VLC_TRUE;
885 input_SetStatus( p_vout, INPUT_STATUS_PAUSE );
889 [super keyDown: o_event];
896 NSMutableString * o_title;
897 playlist_t * p_playlist = vlc_object_find( p_vout, VLC_OBJECT_PLAYLIST,
900 if( p_playlist == NULL )
905 vlc_mutex_lock( &p_playlist->object_lock );
906 o_title = [NSMutableString stringWithUTF8String:
907 p_playlist->pp_items[p_playlist->i_index]->psz_name];
908 vlc_mutex_unlock( &p_playlist->object_lock );
910 vlc_object_release( p_playlist );
914 NSRange prefix_range = [o_title rangeOfString: @"file:"];
915 if( prefix_range.location != NSNotFound )
917 [o_title deleteCharactersInRange: prefix_range];
920 [self setTitleWithRepresentedFilename: o_title];
925 [NSString stringWithCString: VOUT_TITLE " (QuickTime)"]];
929 /* This is actually the same as VLCControls::stop. */
930 - (BOOL)windowShouldClose:(id)sender
932 playlist_t * p_playlist = vlc_object_find( p_vout, VLC_OBJECT_PLAYLIST,
934 if( p_playlist == NULL )
939 playlist_Stop( p_playlist );
940 vlc_object_release( p_playlist );
942 /* The window will be closed by the intf later. */
948 /*****************************************************************************
949 * VLCView implementation
950 *****************************************************************************/
951 @implementation VLCView
953 - (void)drawRect:(NSRect)rect
955 vout_thread_t * p_vout;
956 id o_window = [self window];
957 p_vout = (vout_thread_t *)[o_window getVout];
959 [[NSColor blackColor] set];
961 [super drawRect: rect];
963 p_vout->i_changes |= VOUT_SIZE_CHANGE;
966 - (BOOL)acceptsFirstResponder
971 - (BOOL)becomeFirstResponder
973 vout_thread_t * p_vout;
974 id o_window = [self window];
975 p_vout = (vout_thread_t *)[o_window getVout];
977 [o_window setAcceptsMouseMovedEvents: YES];
981 - (BOOL)resignFirstResponder
983 vout_thread_t * p_vout;
984 id o_window = [self window];
985 p_vout = (vout_thread_t *)[o_window getVout];
987 [o_window setAcceptsMouseMovedEvents: NO];
988 VLCHideMouse( p_vout, NO );
992 - (void)mouseDown:(NSEvent *)o_event
994 vout_thread_t * p_vout;
995 id o_window = [self window];
996 p_vout = (vout_thread_t *)[o_window getVout];
999 switch( [o_event type] )
1001 case NSLeftMouseDown:
1003 var_Get( p_vout, "mouse-button-down", &val );
1005 var_Set( p_vout, "mouse-button-down", val );
1010 [super mouseDown: o_event];
1015 - (void)otherMouseDown:(NSEvent *)o_event
1017 /* This is not the the wheel button. you need to poll the
1018 * mouseWheel event for that. other is a third, forth or fifth button */
1019 vout_thread_t * p_vout;
1020 id o_window = [self window];
1021 p_vout = (vout_thread_t *)[o_window getVout];
1024 switch( [o_event type] )
1026 case NSOtherMouseDown:
1028 var_Get( p_vout, "mouse-button-down", &val );
1030 var_Set( p_vout, "mouse-button-down", val );
1035 [super mouseDown: o_event];
1040 - (void)rightMouseDown:(NSEvent *)o_event
1042 vout_thread_t * p_vout;
1043 id o_window = [self window];
1044 p_vout = (vout_thread_t *)[o_window getVout];
1047 switch( [o_event type] )
1049 case NSRightMouseDown:
1051 var_Get( p_vout, "mouse-button-down", &val );
1053 var_Set( p_vout, "mouse-button-down", val );
1058 [super mouseDown: o_event];
1063 - (void)mouseUp:(NSEvent *)o_event
1065 vout_thread_t * p_vout;
1066 id o_window = [self window];
1067 p_vout = (vout_thread_t *)[o_window getVout];
1070 switch( [o_event type] )
1075 b_val.b_bool = VLC_TRUE;
1076 var_Set( p_vout, "mouse-clicked", b_val );
1078 var_Get( p_vout, "mouse-button-down", &val );
1080 var_Set( p_vout, "mouse-button-down", val );
1085 [super mouseUp: o_event];
1090 - (void)otherMouseUp:(NSEvent *)o_event
1092 vout_thread_t * p_vout;
1093 id o_window = [self window];
1094 p_vout = (vout_thread_t *)[o_window getVout];
1097 switch( [o_event type] )
1099 case NSOtherMouseUp:
1101 var_Get( p_vout, "mouse-button-down", &val );
1103 var_Set( p_vout, "mouse-button-down", val );
1108 [super mouseUp: o_event];
1113 - (void)rightMouseUp:(NSEvent *)o_event
1115 vout_thread_t * p_vout;
1116 id o_window = [self window];
1117 p_vout = (vout_thread_t *)[o_window getVout];
1120 switch( [o_event type] )
1122 case NSRightMouseUp:
1124 var_Get( p_vout, "mouse-button-down", &val );
1126 var_Set( p_vout, "mouse-button-down", val );
1131 [super mouseUp: o_event];
1136 - (void)mouseDragged:(NSEvent *)o_event
1138 [self mouseMoved:o_event];
1141 - (void)otherMouseDragged:(NSEvent *)o_event
1143 [self mouseMoved:o_event];
1146 - (void)rightMouseDragged:(NSEvent *)o_event
1148 [self mouseMoved:o_event];
1151 - (void)mouseMoved:(NSEvent *)o_event
1157 vout_thread_t * p_vout;
1158 id o_window = [self window];
1159 p_vout = (vout_thread_t *)[o_window getVout];
1161 s_rect = [self bounds];
1162 ml = [self convertPoint: [o_event locationInWindow] fromView: nil];
1163 b_inside = [self mouse: ml inRect: s_rect];
1168 int i_width, i_height, i_x, i_y;
1170 vout_PlacePicture( p_vout, (unsigned int)s_rect.size.width,
1171 (unsigned int)s_rect.size.height,
1172 &i_x, &i_y, &i_width, &i_height );
1174 val.i_int = ( ((int)ml.x) - i_x ) *
1175 p_vout->render.i_width / i_width;
1176 var_Set( p_vout, "mouse-x", val );
1178 val.i_int = ( ((int)ml.y) - i_y ) *
1179 p_vout->render.i_height / i_height;
1180 var_Set( p_vout, "mouse-y", val );
1182 val.b_bool = VLC_TRUE;
1183 var_Set( p_vout, "mouse-moved", val );
1184 p_vout->p_sys->i_time_mouse_last_moved = mdate();
1185 p_vout->p_sys->b_mouse_moved = YES;
1187 else if ( !b_inside && !p_vout->p_sys->b_mouse_pointer_visible )
1189 /* people with multiple monitors need their mouse,
1190 * even if VLCView in fullscreen. */
1191 VLCHideMouse( p_vout, NO );
1194 [super mouseMoved: o_event];
1199 /*****************************************************************************
1200 * VLCVout implementation
1201 *****************************************************************************/
1202 @implementation VLCVout
1204 - (void)createWindow:(NSValue *)o_value
1208 NSScreen * o_screen;
1209 vout_thread_t * p_vout;
1210 vlc_bool_t b_main_screen;
1212 p_vout = (vout_thread_t *)[o_value pointerValue];
1214 p_vout->p_sys->o_window = [VLCWindow alloc];
1215 [p_vout->p_sys->o_window setVout: p_vout];
1216 [p_vout->p_sys->o_window setReleasedWhenClosed: YES];
1218 if( var_Get( p_vout, "video-device", &val ) < 0 )
1220 o_screen = [NSScreen mainScreen];
1225 NSArray *o_screens = [NSScreen screens];
1226 unsigned int i_index = val.i_int;
1228 if( [o_screens count] < i_index )
1230 o_screen = [NSScreen mainScreen];
1236 o_screen = [o_screens objectAtIndex: i_index];
1237 config_PutInt( p_vout, "macosx-vdev", i_index );
1238 b_main_screen = (i_index == 0);
1242 if( p_vout->b_fullscreen )
1244 NSRect screen_rect = [o_screen frame];
1245 screen_rect.origin.x = screen_rect.origin.y = 0;
1247 if ( b_main_screen && p_vout->p_sys->p_fullscreen_state == NULL )
1248 BeginFullScreen( &p_vout->p_sys->p_fullscreen_state, NULL, 0, 0,
1249 NULL, NULL, fullScreenAllowEvents );
1251 [p_vout->p_sys->o_window
1252 initWithContentRect: screen_rect
1253 styleMask: NSBorderlessWindowMask
1254 backing: NSBackingStoreBuffered
1255 defer: NO screen: o_screen];
1257 [p_vout->p_sys->o_window setLevel: NSPopUpMenuWindowLevel - 1];
1258 p_vout->p_sys->b_mouse_moved = YES;
1259 p_vout->p_sys->i_time_mouse_last_moved = mdate();
1263 unsigned int i_stylemask = NSTitledWindowMask |
1264 NSMiniaturizableWindowMask |
1265 NSClosableWindowMask |
1266 NSResizableWindowMask;
1268 if ( p_vout->p_sys->p_fullscreen_state != NULL )
1269 EndFullScreen ( p_vout->p_sys->p_fullscreen_state, NULL );
1270 p_vout->p_sys->p_fullscreen_state = NULL;
1272 [p_vout->p_sys->o_window
1273 initWithContentRect: p_vout->p_sys->s_rect
1274 styleMask: i_stylemask
1275 backing: NSBackingStoreBuffered
1276 defer: NO screen: o_screen];
1278 [p_vout->p_sys->o_window setAlphaValue: config_GetFloat( p_vout, "macosx-opaqueness" )];
1280 if( config_GetInt( p_vout, "macosx-float" ) )
1282 [p_vout->p_sys->o_window setLevel: NSStatusWindowLevel];
1285 if( !p_vout->p_sys->b_pos_saved )
1287 [p_vout->p_sys->o_window center];
1291 o_view = [[VLCView alloc] init];
1292 /* FIXME: [o_view setMenu:] */
1293 [p_vout->p_sys->o_window setContentView: o_view];
1294 [o_view autorelease];
1297 p_vout->p_sys->p_qdport = [o_view qdPort];
1298 [o_view unlockFocus];
1300 [p_vout->p_sys->o_window updateTitle];
1301 [p_vout->p_sys->o_window makeKeyAndOrderFront: nil];
1304 - (void)destroyWindow:(NSValue *)o_value
1306 vout_thread_t * p_vout;
1308 p_vout = (vout_thread_t *)[o_value pointerValue];
1310 if( !p_vout->b_fullscreen )
1314 s_rect = [[p_vout->p_sys->o_window contentView] frame];
1315 p_vout->p_sys->s_rect.size = s_rect.size;
1317 s_rect = [p_vout->p_sys->o_window frame];
1318 p_vout->p_sys->s_rect.origin = s_rect.origin;
1320 p_vout->p_sys->b_pos_saved = YES;
1323 p_vout->p_sys->p_qdport = nil;
1324 [p_vout->p_sys->o_window close];
1325 p_vout->p_sys->o_window = nil;