vout.m 32.2 KB
Newer Older
1
/*****************************************************************************
2
 * vout.m: MacOS X video output module
3
 *****************************************************************************
4
 * Copyright (C) 2001-2009 the VideoLAN team
Benjamin Pracht's avatar
...  
Benjamin Pracht committed
5
 * $Id$
6 7 8 9
 *
 * Authors: Colin Delacroix <colin@zoy.org>
 *          Florian G. Pflug <fgp@phlo.org>
 *          Jon Lech Johansen <jon-vl@nanocrew.net>
10
 *          Derk-Jan Hartman <hartman at videolan dot org>
11
 *          Eric Petit <titer@m0k.org>
12
 *          Benjamin Pracht <bigben at videolan dot org>
13
 *          Felix Paul Kühne <fkuehne at videolan dot org>
14 15 16 17 18
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
19
 *
20 21 22 23 24 25 26
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
Antoine Cellerier's avatar
Antoine Cellerier committed
27
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
28 29 30 31 32 33
 *****************************************************************************/

/*****************************************************************************
 * Preamble
 *****************************************************************************/
#include <stdlib.h>                                                /* free() */
34
#include <string.h>
35

Felix Paul Kühne's avatar
Felix Paul Kühne committed
36 37
/* prevent system sleep */
#import <CoreServices/CoreServices.h>
38 39 40 41 42 43 44
/* FIXME: HACK!! */
#ifdef __x86_64__
#import <CoreServices/../Frameworks/OSServices.framework/Headers/Power.h>
#endif

/* SystemUIMode */
#import <Carbon/Carbon.h>
Felix Paul Kühne's avatar
Felix Paul Kühne committed
45

46

47
#include "intf.h"
48
#include "fspanel.h"
49
#include "vout.h"
50
#import "controls.h"
51
#import "embeddedwindow.h"
52

53 54 55
#include <vlc_common.h>
#include <vlc_keys.h>

56 57 58 59 60 61 62 63
/*****************************************************************************
 * DeviceCallback: Callback triggered when the video-device variable is changed
 *****************************************************************************/
int DeviceCallback( vlc_object_t *p_this, const char *psz_variable,
                     vlc_value_t old_val, vlc_value_t new_val, void *param )
{
    vlc_value_t val;
    vout_thread_t *p_vout = (vout_thread_t *)p_this;
64

65
    msg_Dbg( p_vout, "set %d", new_val.i_int );
66 67
    var_Create( p_vout->p_libvlc, "video-device", VLC_VAR_INTEGER );
    var_Set( p_vout->p_libvlc, "video-device", new_val );
68

69
    val.b_bool = true;
70 71 72 73 74
    var_Set( p_vout, "intf-change", val );
    return VLC_SUCCESS;
}


75
/*****************************************************************************
76
 * VLCEmbeddedList implementation
77
 *****************************************************************************/
78
@implementation VLCEmbeddedList
79

80
- (id)init
81
{
82 83
    [super init];
    o_embedded_array = [NSMutableArray array];
84 85 86
    return self;
}

87
- (id)embeddedVout
88
{
89
    unsigned int i;
90

91
    for( i = 0; i < [o_embedded_array count]; i++ )
92
    {
93 94
        id o_vout_view = [o_embedded_array objectAtIndex: i];
        if( ![o_vout_view isUsed] )
95
        {
96 97
            [o_vout_view setUsed: YES];
            return o_vout_view;
98
        }
99 100 101
    }
    return nil;
}
102

103 104 105 106 107
- (void)releaseEmbeddedVout: (id)o_vout_view
{
    if( [o_embedded_array containsObject: o_vout_view] )
    {
        [o_vout_view setUsed: NO];
108
    }
109
    else
110
    {
111
        msg_Warn( VLCIntf, "cannot find Video Output");
112
    }
113
}
114

115 116 117
- (void)addEmbeddedVout: (id)o_vout_view
{
    if( ![o_embedded_array containsObject: o_vout_view] )
118
    {
119
        [o_embedded_array addObject: o_vout_view];
120
    }
121 122 123 124
}

- (BOOL)windowContainsEmbedded: (id)o_window
{
125
/*    if( ![[o_window className] isEqualToString: @"VLCVoutWindow"] )
126
    {
127
        NSLog( @"We were not given a VLCVoutWindow" );
128
    }*/
129
    return ([self viewForWindow: o_window] == nil ? NO : YES );
130 131
}

132
- (id)viewForWindow: (id)o_window
133
{
Felix Paul Kühne's avatar
Felix Paul Kühne committed
134
    if( o_embedded_array != nil )
135
    {
Felix Paul Kühne's avatar
Felix Paul Kühne committed
136 137 138
        id o_enumerator = [o_embedded_array objectEnumerator];
        id o_current_embedded;
        if( o_window != nil )
139
        {
Felix Paul Kühne's avatar
Felix Paul Kühne committed
140 141
            while( (o_current_embedded = [o_enumerator nextObject]) )
            {
142
                if( [o_current_embedded voutWindow] == o_window )
Felix Paul Kühne's avatar
Felix Paul Kühne committed
143 144 145 146
                {
                    return o_current_embedded;
                }
            }
147
        }
148
    }
149 150
    return nil;
}
151

152
@end
153

154 155 156 157 158
/*****************************************************************************
 * VLCVoutView implementation
 *****************************************************************************/
@implementation VLCVoutView

159
- (id)initWithFrame: (NSRect)frameRect
160
{
161
    self = [super initWithFrame: frameRect];
162 163 164 165 166 167 168 169 170
    p_vout = NULL;
    o_view = nil;
    s_frame = &frameRect;

    p_real_vout = NULL;
    o_window = nil;
    return self;
}

Felix Paul Kühne's avatar
typo  
Felix Paul Kühne committed
171 172
- (BOOL)setVout: (vout_thread_t *) vout
        subView: (NSView *) view
173
          frame: (NSRect *) frame
174 175 176 177 178 179 180 181 182 183 184 185 186 187 188
{
    int i_device;
    NSAutoreleasePool *o_pool = [[NSAutoreleasePool alloc] init];
    NSArray *o_screens = [NSScreen screens];

    p_vout  = vout;
    o_view  = view;
    s_frame = frame;

    if( [o_screens count] <= 0 )
    {
        msg_Err( p_vout, "no OSX screens available" );
        return NO;
    }

189
    p_real_vout = [VLCVoutView realVout: p_vout];
190

191
    /* Get the pref value when this is the first time, otherwise retrieve the device from the top level video-device var */
192
    if( var_Type( p_real_vout->p_libvlc, "video-device" ) == 0 )
193 194 195 196 197
    {
        i_device = var_GetInteger( p_vout, "macosx-vdev" );
    }
    else
    {
198
        i_device = var_GetInteger( p_real_vout->p_libvlc, "video-device" );
199 200 201 202
    }

    /* Setup the menuitem for the multiple displays. */
    if( var_Type( p_real_vout, "video-device" ) == 0 )
203 204
    {
        int i = 1;
205
        vlc_value_t val2, text;
206 207 208 209
        NSScreen * o_screen;

        var_Create( p_real_vout, "video-device", VLC_VAR_INTEGER |
                                            VLC_VAR_HASCHOICE );
210
        text.psz_string = _("Fullscreen Video Device");
211 212 213 214
        var_Change( p_real_vout, "video-device", VLC_VAR_SETTEXT, &text, NULL );

        NSEnumerator * o_enumerator = [o_screens objectEnumerator];

215 216 217 218 219 220
        val2.i_int = 0;
        text.psz_string = _("Default");
        var_Change( p_real_vout, "video-device",
                        VLC_VAR_ADDCHOICE, &val2, &text );
        var_Set( p_real_vout, "video-device", val2 );

221 222 223 224 225 226 227 228 229 230
        while( (o_screen = [o_enumerator nextObject]) != NULL )
        {
            char psz_temp[255];
            NSRect s_rect = [o_screen frame];

            snprintf( psz_temp, sizeof(psz_temp)/sizeof(psz_temp[0])-1,
                      "%s %d (%dx%d)", _("Screen"), i,
                      (int)s_rect.size.width, (int)s_rect.size.height );

            text.psz_string = psz_temp;
231
            val2.i_int = (int)[o_screen displayID];
232 233
            var_Change( p_real_vout, "video-device",
                        VLC_VAR_ADDCHOICE, &val2, &text );
234
            if( (int)[o_screen displayID] == i_device )
235 236 237 238 239 240
            {
                var_Set( p_real_vout, "video-device", val2 );
            }
            i++;
        }

241
        var_AddCallback( p_real_vout, "video-device", DeviceCallback,
242 243
                         NULL );

244
        val2.b_bool = true;
245 246 247
        var_Set( p_real_vout, "intf-change", val2 );
    }

248
    /* Add the view. It's automatically resized to fit the window */
249 250
    [self addSubview: o_view];
    [self setAutoresizesSubviews: YES];
Derk-Jan Hartman's avatar
-  
Derk-Jan Hartman committed
251
    [o_pool release];
252

253
    return YES;
254 255
}

256
- (void)resizeSubviewsWithOldSize:(NSSize)oldBoundsSize
257
{
258 259
    [super resizeSubviewsWithOldSize: oldBoundsSize];
    [o_view setFrameSize: [self frame].size];
260 261
}

262 263 264 265 266 267 268 269 270
- (void)drawRect:(NSRect)rect
{
    /* When there is no subview we draw a black background */
    [self lockFocus];
    [[NSColor blackColor] set];
    NSRectFill(rect);
    [self unlockFocus];
}

271
- (void)closeVout
272
{
273
    [[[[VLCMain sharedInstance] controls] fspanel] fadeOut];
274

275
    /* Make sure we don't see a white flash */
276
    [[self voutWindow] disableScreenUpdatesUntilFlush];
277 278 279 280 281
    [o_view removeFromSuperview];
    o_view = nil;
    p_vout = NULL;
    s_frame = nil;
    o_window = nil;
282
    p_real_vout = NULL;
Eric Petit's avatar
Eric Petit committed
283
}
284

285 286
- (void)updateTitle
{
287 288
    NSString * o_title = nil; 
    NSMutableString * o_mrl = nil;
289
    input_thread_t * p_input;
290
    char * psz_title;
291

292
    if( !p_vout ) return;
293

294
    p_input = getInput();
295

296 297 298 299 300 301 302
    if( !p_input ) return;

    input_item_t * p_item = input_GetItem( p_input );

    psz_title = input_item_GetNowPlaying ( p_item );
    if( !psz_title )
        psz_title = input_item_GetName( p_item );
303

304 305
    if( psz_title )
        o_title = [NSString stringWithUTF8String: psz_title];
306

307 308
    char *psz_uri = input_item_GetURI( p_item );
    if( psz_uri )
309
        o_mrl = [NSMutableString stringWithUTF8String: psz_uri];
310

311
    free( psz_title );
312
    free( psz_uri );
313

314
    if( !o_title )
315 316 317 318
        o_title = o_mrl;

    if( o_mrl != nil )
    {
319 320 321 322 323 324 325 326
        /* FIXME once psz_access is exported, we could check if we are
         * reading from a file in a smarter way. */

        NSRange prefix_range = [o_mrl rangeOfString: @"file:"];
        if( prefix_range.location != NSNotFound )
            [o_mrl deleteCharactersInRange: prefix_range];

        if( [o_mrl characterAtIndex:0] == '/' )
327
        {
328
            /* it's a local file */
329 330
            [o_window setRepresentedFilename: o_mrl];
        }
331 332 333 334 335 336
        else
        {
            /* it's from the network or somewhere else,
             * we clear the previous path */
            [o_window setRepresentedFilename: @""];
        }
337 338 339 340
        [o_window setTitle: o_title];
    }
    else
    {
341
        [o_window setTitle: [NSString stringWithUTF8String: VOUT_TITLE]];
342 343 344 345
    }
    vlc_object_release( p_input );
}

Derk-Jan Hartman's avatar
Derk-Jan Hartman committed
346
- (void)setOnTop:(BOOL)b_on_top
Eric Petit's avatar
Eric Petit committed
347 348
{
    if( b_on_top )
349
    {
350
        [o_window setLevel: NSStatusWindowLevel];
351 352 353
    }
    else
    {
354
        [o_window setLevel: NSNormalWindowLevel];
355
    }
356 357
}

Pierre d'Herbemont's avatar
Pierre d'Herbemont committed
358
- (NSSize)voutSizeForFactor: (float)factor
359
{
360
    int i_corrected_height, i_corrected_width;
Pierre d'Herbemont's avatar
Pierre d'Herbemont committed
361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377
    NSSize newsize;

    if( p_vout->render.i_height * p_vout->render.i_aspect >
                    p_vout->render.i_width * VOUT_ASPECT_FACTOR )
    {
        i_corrected_width = p_vout->render.i_height * p_vout->render.i_aspect /
                                        VOUT_ASPECT_FACTOR;
        newsize.width = (int) ( i_corrected_width * factor );
        newsize.height = (int) ( p_vout->render.i_height * factor );
    }
    else
    {
        i_corrected_height = p_vout->render.i_width * VOUT_ASPECT_FACTOR /
                                        p_vout->render.i_aspect;
        newsize.width = (int) ( p_vout->render.i_width * factor );
        newsize.height = (int) ( i_corrected_height * factor );
    }
378

Pierre d'Herbemont's avatar
Pierre d'Herbemont committed
379 380 381 382 383
    return newsize;
}

- (void)scaleWindowWithFactor: (float)factor animate: (BOOL)animate
{
Derk-Jan Hartman's avatar
Derk-Jan Hartman committed
384 385
    if ( !p_vout->b_fullscreen )
    {
Pierre d'Herbemont's avatar
Pierre d'Herbemont committed
386 387 388
        NSSize newsize;
        NSPoint topleftbase;
        NSPoint topleftscreen;
389
        NSView *mainView;
390
        NSRect new_frame;
Derk-Jan Hartman's avatar
Derk-Jan Hartman committed
391
        topleftbase.x = 0;
392 393 394
        topleftbase.y = [o_window frame].size.height;
        topleftscreen = [o_window convertBaseToScreen: topleftbase];

Pierre d'Herbemont's avatar
Pierre d'Herbemont committed
395
        newsize = [self voutSizeForFactor:factor];
396

397 398 399 400 401 402 403
        /* In fullscreen mode we need to use a view that is different from
         * ourselves, with the VLCEmbeddedWindow */
        if([o_window isKindOfClass:[VLCEmbeddedWindow class]])
            mainView = [o_window mainView];
        else
            mainView = self;

404 405
        /* Calculate the window's new size */
        new_frame.size.width = [o_window frame].size.width -
406
                                    [mainView frame].size.width + newsize.width;
407
        new_frame.size.height = [o_window frame].size.height -
408
                                    [mainView frame].size.height + newsize.height;
409 410 411 412

        new_frame.origin.x = topleftscreen.x;
        new_frame.origin.y = topleftscreen.y - new_frame.size.height;

Pierre d'Herbemont's avatar
Pierre d'Herbemont committed
413
        [o_window setFrame:new_frame display:animate animate:animate];
Derk-Jan Hartman's avatar
Derk-Jan Hartman committed
414 415
        p_vout->i_changes |= VOUT_SIZE_CHANGE;
    }
416 417
}

418 419
- (void)toggleFloatOnTop
{
420
    vlc_value_t val;
421

422
    if( !p_real_vout ) return;
423
    if( var_Get( p_real_vout, "video-on-top", &val )>=0 && val.b_bool)
424
    {
425
        val.b_bool = false;
426 427 428
    }
    else
    {
429
        val.b_bool = true;
430
    }
431
    var_Set( p_real_vout, "video-on-top", val );
432 433
}

434 435
- (void)toggleFullscreen
{
436
    vlc_value_t val;
437
    if( !p_real_vout ) return;
Rémi Duraffort's avatar
Rémi Duraffort committed
438
    var_ToggleBool( p_real_vout, "fullscreen" );
439 440 441 442
}

- (BOOL)isFullscreen
{
443
    vlc_value_t val;
444
    if( !p_real_vout ) return NO;
445 446
    var_Get( p_real_vout, "fullscreen", &val );
    return( val.b_bool );
447 448
}

449 450
- (void)snapshot
{
451
    var_TriggerCallback( p_real_vout, "video-snapshot" );
452 453
}

454
- (void)manage
455
{
456
    /* Disable Screensaver, when we're playing something, but allow it on pause */
457
    if( !VLCIntf || !VLCIntf->p_sys )
458 459
        return;

460 461
    if( VLCIntf->p_sys->i_play_status == PLAYING_S )
        UpdateSystemActivity( UsrActivity );
462
}
463

464
- (id)voutWindow
465
{
466 467
    return o_window;
}
468

469 470
- (void)scrollWheel:(NSEvent *)theEvent
{
471
    VLCControls * o_controls = (VLCControls *)[[NSApp delegate] controls];
472
    [o_controls scrollWheel: theEvent];
473 474
}

475 476 477
- (void)keyDown:(NSEvent *)o_event
{
    unichar key = 0;
478
    vlc_value_t val;
479
    unsigned int i_pressed_modifiers = 0;
480
    val.i_int = 0;
481

482
    i_pressed_modifiers = [o_event modifierFlags];
483

484 485 486 487 488 489 490 491
    if( i_pressed_modifiers & NSShiftKeyMask )
        val.i_int |= KEY_MODIFIER_SHIFT;
    if( i_pressed_modifiers & NSControlKeyMask )
        val.i_int |= KEY_MODIFIER_CTRL;
    if( i_pressed_modifiers & NSAlternateKeyMask )
        val.i_int |= KEY_MODIFIER_ALT;
    if( i_pressed_modifiers & NSCommandKeyMask )
        val.i_int |= KEY_MODIFIER_COMMAND;
492

493
    key = [[[o_event charactersIgnoringModifiers] lowercaseString] characterAtIndex: 0];
494

495 496
    if( key )
    {
497 498 499
        /* Escape should always get you out of fullscreen */
        if( key == (unichar) 0x1b )
        {
500
             if( p_real_vout && [self isFullscreen] )
501 502 503 504
             {
                 [self toggleFullscreen];
             }
        }
505
        else if ( p_vout )
506
        {
507 508 509
            if( key == ' ')
                val.i_int = config_GetInt( p_vout, "key-play-pause" );
            else
510
                val.i_int |= (int)CocoaKeyToVLC( key );
511
            var_Set( p_vout->p_libvlc, "key-pressed", val );
512
        }
513
        else NSLog( @"Could not send keyevent to VLC core" );
514 515 516
    }
    else
        [super keyDown: o_event];
517 518
}

519
- (void)mouseDown:(NSEvent *)o_event
520
{
521 522
    vlc_value_t val;
    if( p_vout )
523
    {
524 525
        if( ( [o_event type] == NSLeftMouseDown ) &&
          ( ! ( [o_event modifierFlags] &  NSControlKeyMask ) ) )
526
        {
527
            if( [o_event clickCount] <= 1 )
528
            {
529 530 531 532
                /* single clicking */
                var_Get( p_vout, "mouse-button-down", &val );
                val.i_int |= 1;
                var_Set( p_vout, "mouse-button-down", val );
533
            }
534
            else
535
            {
536 537
                /* multiple clicking */
                [self toggleFullscreen];
538
            }
539
        }
540 541 542 543 544
        else if( ( [o_event type] == NSRightMouseDown ) ||
               ( ( [o_event type] == NSLeftMouseDown ) &&
                 ( [o_event modifierFlags] &  NSControlKeyMask ) ) )
        {
            msg_Dbg( p_vout, "received NSRightMouseDown (generic method) or Ctrl clic" );
545
            [NSMenu popUpContextMenu: [[VLCMain sharedInstance] voutMenu] withEvent: o_event forView: [[[VLCMain sharedInstance] controls] voutView]];
546
        }
547
    }
548 549

    [super mouseDown: o_event];
550 551 552 553 554 555
}

- (void)otherMouseDown:(NSEvent *)o_event
{
    vlc_value_t val;

556
    if( p_vout && [o_event type] == NSOtherMouseDown )
557
    {
558 559 560
        var_Get( p_vout, "mouse-button-down", &val );
        val.i_int |= 2;
        var_Set( p_vout, "mouse-button-down", val );
561
    }
562 563

    [super mouseDown: o_event];
564
}
565

566 567
- (void)rightMouseDown:(NSEvent *)o_event
{
568
    if( p_vout && [o_event type] == NSRightMouseDown )
569
    {
570
        msg_Dbg( p_vout, "received NSRightMouseDown (specific method)" );
571
        [NSMenu popUpContextMenu: [[VLCMain sharedInstance] voutMenu] withEvent: o_event forView: [[[VLCMain sharedInstance] controls] voutView]];
572
    }
573 574

    [super mouseDown: o_event];
575 576 577 578 579 580
}

- (void)mouseUp:(NSEvent *)o_event
{
    vlc_value_t val;

581
    if( p_vout && [o_event type] == NSLeftMouseUp )
582
    {
583
        var_SetBool( p_vout, "mouse-clicked", true );
584

585 586 587
        var_Get( p_vout, "mouse-button-down", &val );
        val.i_int &= ~1;
        var_Set( p_vout, "mouse-button-down", val );
588
    }
589 590

    [super mouseUp: o_event];
591 592
}

593
- (void)otherMouseUp:(NSEvent *)o_event
594
{
595 596
    vlc_value_t val;

597
    if( p_vout && [o_event type] == NSOtherMouseUp )
598
    {
599 600 601
        var_Get( p_vout, "mouse-button-down", &val );
        val.i_int &= ~2;
        var_Set( p_vout, "mouse-button-down", val );
602
    }
603 604

    [super mouseUp: o_event];
605
}
606

607 608
- (void)rightMouseUp:(NSEvent *)o_event
{
609
    if( p_vout && [o_event type] == NSRightMouseUp )
610
    {
611 612
        /* FIXME: this isn't the appropriate place, but we can't receive
         * NSRightMouseDown some how */
613
        msg_Dbg( p_vout, "received NSRightMouseUp" );
614
        [NSMenu popUpContextMenu: [[VLCMain sharedInstance] voutMenu] withEvent: o_event forView: [[[VLCMain sharedInstance] controls] voutView]];
615
    }
616 617

    [super mouseUp: o_event];
618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671
}

- (void)mouseDragged:(NSEvent *)o_event
{
    [self mouseMoved: o_event];
}

- (void)otherMouseDragged:(NSEvent *)o_event
{
    [self mouseMoved: o_event];
}

- (void)rightMouseDragged:(NSEvent *)o_event
{
    [self mouseMoved: o_event];
}

- (void)mouseMoved:(NSEvent *)o_event
{
    NSPoint ml;
    NSRect s_rect;
    BOOL b_inside;

    if( p_vout )
    {
        s_rect = [o_view bounds];
        ml = [o_view convertPoint: [o_event locationInWindow] fromView: nil];
        b_inside = [o_view mouse: ml inRect: s_rect];

        if( b_inside )
        {
            vlc_value_t val;
            unsigned int i_width, i_height, i_x, i_y;

            vout_PlacePicture( p_vout, (unsigned int)s_rect.size.width,
                                       (unsigned int)s_rect.size.height,
                                       &i_x, &i_y, &i_width, &i_height );

            val.i_int = ( ((int)ml.x) - i_x ) *
                        p_vout->render.i_width / i_width;
            var_Set( p_vout, "mouse-x", val );

            if( [[o_view className] isEqualToString: @"VLCGLView"] )
            {
                val.i_int = ( ((int)(s_rect.size.height - ml.y)) - i_y ) *
                            p_vout->render.i_height / i_height;
            }
            else
            {
                val.i_int = ( ((int)ml.y) - i_y ) *
                            p_vout->render.i_height / i_height;
            }
            var_Set( p_vout, "mouse-y", val );

672
            val.b_bool = true;
673 674
            var_Set( p_vout, "mouse-moved", val );
        }
675
        if( [self isFullscreen] )
676
            [[[[VLCMain sharedInstance] controls] fspanel] fadeIn];
677
    }
678

679
    [super mouseMoved: o_event];
680 681
}

Eric Petit's avatar
Eric Petit committed
682 683 684
- (BOOL)acceptsFirstResponder
{
    return YES;
685 686
}

Eric Petit's avatar
Eric Petit committed
687
- (BOOL)becomeFirstResponder
688
{
Eric Petit's avatar
Eric Petit committed
689
    return YES;
690 691
}

Eric Petit's avatar
Eric Petit committed
692
- (BOOL)resignFirstResponder
693
{
Eric Petit's avatar
Eric Petit committed
694 695 696 697
    /* We need to stay the first responder or we'll miss some
       events */
    return NO;
}
698

699
/* Class methods used by the different vout modules */
700

701
+ (vout_thread_t *)realVout: (vout_thread_t *)p_vout
702 703 704
{
    /* p_real_vout: the vout we have to use to check for video-on-top
       and a few other things. If we are the QuickTime output, it's us.
705 706 707 708 709
       It we are the OpenGL provider, it is our parent.
       Since we can't be the QuickTime output anymore, we need to be
       the parent.
       FIXME: check with the caca and x11 vouts! */
    return (vout_thread_t *) p_vout->p_parent;
710 711
}

712 713
+ (id)voutView: (vout_thread_t *)p_vout subView: (NSView *)view
         frame: (NSRect *)s_frame
714
{
715
    int i_drawable_gl;
716 717 718
    int i_timeout;
    id o_return = nil;

719
    i_drawable_gl = var_GetInteger( p_vout->p_libvlc, "drawable-gl" );
720 721 722 723 724

    var_Create( p_vout, "macosx-vdev", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
    var_Create( p_vout, "macosx-stretch", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
    var_Create( p_vout, "macosx-opaqueness", VLC_VAR_FLOAT | VLC_VAR_DOINHERIT );
    var_Create( p_vout, "macosx-background", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
725
    var_Create( p_vout, "macosx-black", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
726
    var_Create( p_vout, "embedded-video", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
727 728 729

    /* We only wait for NSApp to initialise if we're not embedded (as in the
     * case of the Mozilla plugin).  We can tell whether we're embedded or not
730
     * by examining the "drawable-gl" value: if it's zero, we're running in the
731
     * main Mac intf; if it's non-zero, we're embedded. */
732
    if( i_drawable_gl == 0 )
733
    {
734 735
        /* Wait for a MacOS X interface to appear. Timeout is 2 seconds. */
        for( i_timeout = 20 ; i_timeout-- ; )
Eric Petit's avatar
Eric Petit committed
736
        {
737 738 739 740 741 742 743 744 745 746 747
            if( NSApp == NULL )
            {
                msleep( INTF_IDLE_SLEEP );
            }
        }

        if( NSApp == NULL )
        {
            /* No MacOS X intf, unable to communicate with MT */
            msg_Err( p_vout, "no MacOS X interface present" );
            return nil;
Eric Petit's avatar
Eric Petit committed
748
        }
749 750 751
        else
        {
            if ( VLCIntf && !(p_vout->b_fullscreen) &&
752
                        !(var_GetBool( p_vout, "macosx-background" )) &&
753
                        var_GetBool( p_vout, "embedded-video") )
754
            {
755
                o_return = [[[VLCMain sharedInstance] embeddedList] embeddedVout];
756 757 758
            }
        }
    }
759

760 761 762 763 764 765
    /* No embedded vout is available */
    if( o_return == nil )
    {
        NSRect null_rect;
        bzero( &null_rect, sizeof( NSRect ) );
        o_return = [[VLCDetachedVoutView alloc] initWithFrame: null_rect ];
766
    }
767 768
    [o_return setVout: p_vout subView: view frame: s_frame];
    return o_return;
769 770
}

771 772
- (void)enterFullscreen
{
773
    /* Save the settings for next playing item */
Antoine Cellerier's avatar
Antoine Cellerier committed
774
    playlist_t * p_playlist = pl_Hold( p_real_vout );
775 776
    var_SetBool( p_playlist, "fullscreen", true );
    pl_Release( p_real_vout );
777 778 779 780
}

- (void)leaveFullscreen
{
781
    /* Save the settings for next playing item */
Antoine Cellerier's avatar
Antoine Cellerier committed
782
    playlist_t * p_playlist = pl_Hold( p_real_vout );
783 784
    var_SetBool( p_playlist, "fullscreen", false );
    pl_Release( p_real_vout );
785 786
}

787 788 789 790 791 792 793 794
@end

/*****************************************************************************
 * VLCDetachedVoutView implementation
 *****************************************************************************/
@implementation VLCDetachedVoutView

- (id)initWithFrame: (NSRect)frameRect
795
{
796 797 798 799
    [super initWithFrame: frameRect];
    i_time_mouse_last_moved = 0;
    return self;
}
Eric Petit's avatar
Eric Petit committed
800

801 802 803 804 805
- (BOOL)mouseDownCanMoveWindow
{
    return YES;
}

Felix Paul Kühne's avatar
Felix Paul Kühne committed
806
- (BOOL)setVout: (vout_thread_t *) p_arg_vout subView: (NSView *) view
807 808 809 810
                     frame: (NSRect *) s_arg_frame
{
    BOOL b_return = [super setVout: p_arg_vout subView: view frame:s_arg_frame];
    i_time_mouse_last_moved = mdate();
811
    o_window = [[VLCVoutWindow alloc] initWithVout: p_arg_vout view: self
812
                                                    frame: s_arg_frame];
813
    
814
    [self updateTitle];
815 816 817 818
    if([self isFullscreen])
        [o_window performSelectorOnMainThread: @selector(enterFullscreen) withObject: NULL waitUntilDone: YES];
    else
        [view setFrame: [self frame]];
819

820 821 822 823 824
    return b_return;
}

- (void)closeVout
{
825
    [o_window performSelectorOnMainThread: @selector(close) withObject: NULL waitUntilDone: YES];
826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857
    i_time_mouse_last_moved = 0;
    [super closeVout];
}

- (void)mouseMoved:(NSEvent *)o_event
{
    i_time_mouse_last_moved = mdate();
    [super mouseMoved: o_event];
}

- (void)hideMouse:(BOOL)b_hide
{
    BOOL b_inside;
    NSPoint ml;
    NSView *o_contents = [o_window contentView];

    ml = [o_window convertScreenToBase:[NSEvent mouseLocation]];
    ml = [o_contents convertPoint:ml fromView:nil];
    b_inside = [o_contents mouse: ml inRect: [o_contents bounds]];

    if( b_hide && b_inside )
    {
        [NSCursor setHiddenUntilMouseMoves: YES];
    }
    else if( !b_hide )
    {
        [NSCursor setHiddenUntilMouseMoves: NO];
    }
}

- (void)manage
{
858
    /* Dooh, why do we spend processor time doing this kind of stuff? */
859
    [super manage];
860
    unsigned int i_mouse_hide_timeout =
861
        var_CreateGetInteger(p_vout, "mouse-hide-timeout") * 1000;
862 863 864

    if( i_mouse_hide_timeout < 100000 )
        i_mouse_hide_timeout = 100000;
865
    if( p_vout->b_fullscreen )
Eric Petit's avatar
Eric Petit committed
866
    {
867
        if( mdate() - i_time_mouse_last_moved > i_mouse_hide_timeout )
868
        {
869
            i_time_mouse_last_moved = mdate();
870
            [self hideMouse: YES];
871
        }
872 873 874 875
    }
    else
    {
        [self hideMouse: NO];
Eric Petit's avatar
Eric Petit committed
876
    }
877 878
}

879 880 881

- (void)enterFullscreen
{
882
    [o_window performSelectorOnMainThread: @selector(enterFullscreen) withObject: NULL waitUntilDone: NO];
883
    [super enterFullscreen];
884 885 886 887 888

}

- (void)leaveFullscreen
{
889
    [o_window performSelectorOnMainThread: @selector(leaveFullscreen) withObject: NULL waitUntilDone: NO];
890
    [super leaveFullscreen];
891 892
}

893 894 895 896 897 898 899 900 901

- (void)scaleWindowWithFactor: (float)factor animate: (BOOL)animate
{
    if( p_vout->b_fullscreen )
        return;
    [o_window setMovableByWindowBackground: NO];
    [super scaleWindowWithFactor: factor animate: animate];
    [o_window setMovableByWindowBackground: YES];
}
902 903 904 905 906 907 908 909
@end

/*****************************************************************************
 * VLCEmbeddedVoutView implementation
 *****************************************************************************/

@implementation VLCEmbeddedVoutView

910 911 912 913 914
- (void)awakeFromNib
{
    o_embeddedwindow = [self window];
}

915 916 917 918 919
- (BOOL)mouseDownCanMoveWindow
{
    return YES;
}

920
- (id)initWithFrame: (NSRect)frameRect
921
{
922 923 924
    if(self = [super initWithFrame: frameRect])
    {
        b_used = NO;
925
        [[[VLCMain sharedInstance] embeddedList] addEmbeddedVout: self];
926 927
        o_embeddedwindow = nil; /* Filled later on in -awakeFromNib */
    }
928 929 930 931
    return self;
}

- (BOOL)setVout: (vout_thread_t *) p_arg_vout subView: (NSView *) view
932
                 frame: (NSRect *)s_arg_frame
933 934
{
    BOOL b_return;
935 936 937

    [NSObject cancelPreviousPerformRequestsWithTarget:o_window];

938 939
    b_return = [super setVout: p_arg_vout subView: view frame: s_arg_frame];
    if( b_return )
940
    {
941
        o_window = [self window];
942

943
        [o_window setAcceptsMouseMovedEvents: TRUE];
944

945
        if( var_CreateGetBool( p_real_vout, "video-on-top" ) )
946 947 948 949
        {
            [o_window setLevel: NSStatusWindowLevel];
        }

950 951
        [view setFrameSize: [self frame].size];
    }
952

953 954 955
    /* o_window needs to point to our o_embeddedwindow, super might have set it
     * to the fullscreen window that o_embeddedwindow setups during fullscreen */
    o_window = o_embeddedwindow;
956
 
957 958
    if( b_return )
    {
959 960
        [o_window lockFullscreenAnimation];

961
        [o_window setAlphaValue: var_GetFloat( p_vout, "macosx-opaqueness" )];
962

963
        [self updateTitle];
964

965 966
        [NSObject cancelPreviousPerformRequestsWithTarget:o_window];

967 968
        /* Make the window the front and key window before animating */
        if ([o_window isVisible] && (![o_window isFullscreen]))
969 970
            [o_window makeKeyAndOrderFront: self];

971 972
        if ( [self window] != o_embeddedwindow )
            [self scaleWindowWithFactor: 1.0 animate: [o_window isVisible] && (![o_window isFullscreen])];
973

Pierre d'Herbemont's avatar
Pierre d'Herbemont committed
974
        [o_embeddedwindow setVideoRatio:[self voutSizeForFactor:1.0]];
975

976 977 978
        /* Make sure our window is visible, if we are not in fullscreen */
        if (![o_window isFullscreen])
            [o_window makeKeyAndOrderFront: self];
979
        [o_window unlockFullscreenAnimation];
980

981
    }
982

983 984 985
    return b_return;
}

986 987 988 989 990 991