vlcplugin_mac.mm 42.3 KB
Newer Older
1 2 3
/*****************************************************************************
 * vlcplugin_mac.cpp: a VLC plugin for Mozilla (Mac interface)
 *****************************************************************************
4
 * Copyright (C) 2011-2015 VLC Authors and VideoLAN
5 6
 * $Id$
 *
7
 * Authors: Felix Paul Kühne <fkuehne # videolan # org>
8
 *          Cheng Sun <chengsun9@gmail.com>
9 10
 *          Jean-Baptiste Kempf <jb@videolan.org>
 *          James Bates <james.h.bates@gmail.com>
11
 *          Pierre d'Herbemont <pdherbemont # videolan.org>
12
 *          David Fuhrmann <david dot fuhrmann at googlemail dot com>
13
 *
14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
 * 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.
 *
 * 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
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
 *****************************************************************************/

29
#pragma mark includes and fix-ups
30

31 32
#import "vlcplugin_mac.h"
#import <npapi.h>
33

34 35
#import <QuartzCore/QuartzCore.h>
#import <AppKit/AppKit.h>
36

37 38 39 40 41 42 43 44 45 46
/* compilation support for 10.6 */
#define OSX_LION NSAppKitVersionNumber >= 1115.2
#ifndef MAC_OS_X_VERSION_10_7

@interface NSView (IntroducedInLion)
- (NSRect)convertRectToBacking:(NSRect)aRect;
@end

#endif

47 48 49 50 51 52 53
#pragma mark - prototypes

CGImageRef createImageNamed(NSString *);

#pragma mark - objc class interfaces

@interface VLCNoMediaLayer : CALayer
54 55 56
{
    VlcPluginMac *_cppPlugin;
}
57

58
@property (readwrite) VlcPluginMac *cppPlugin;
59 60 61

@end

62
@interface VLCBrowserRootLayer : CALayer {
63
    NSTimer *_interfaceUpdateTimer;
64
    VlcPluginMac *_cppPlugin;
65
}
66

67
@property (readwrite) VlcPluginMac *cppPlugin;
68 69 70

- (void)startUIUpdateTimer;

71 72 73
- (void)addVoutLayer:(CALayer *)aLayer;
- (void)removeVoutLayer:(CALayer *)aLayer;
- (CGSize)currentOutputSize;
74 75
@end

76
@interface VLCControllerLayer : CALayer {
77 78 79 80 81 82 83
    CGImageRef _playImage;
    CGImageRef _pauseImage;

    CGImageRef _sliderTrackLeft;
    CGImageRef _sliderTrackRight;
    CGImageRef _sliderTrackCenter;

84 85 86
    CGImageRef _enterFullscreen;
    CGImageRef _leaveFullscreen;

87
    CGImageRef _knob;
88 89 90 91

    BOOL _wasPlayingBeforeMouseDown;
    BOOL _isScrubbing;
    CGFloat _mouseDownXDelta;
92 93 94 95 96

    double _mediaPosition;
    BOOL _isPlaying;
    BOOL _isFullscreen;
    VlcPluginMac *_cppPlugin;
97
}
98 99
@property (readwrite) double mediaPosition;
@property (readwrite) BOOL isPlaying;
100
@property (readwrite) BOOL isFullscreen;
101
@property (readwrite) VlcPluginMac *cppPlugin;
102

103 104 105
- (void)handleMouseDown:(CGPoint)point;
- (void)handleMouseUp:(CGPoint)point;
- (void)handleMouseDragged:(CGPoint)point;
106 107 108

@end

109 110 111 112 113 114
@interface VLCControllerLayer (Internal)
- (CGRect)_playPauseButtonRect;
- (CGRect)_fullscreenButtonRect;
- (CGRect)_sliderRect;
@end

115 116 117 118 119 120
@interface VLCPlaybackLayer : CALayer
- (void)mouseButtonDown:(int)buttonNumber;
- (void)mouseButtonUp:(int)buttonNumber;
- (void)mouseMovedToX:(double)xValue Y:(double)yValue;
@end

121 122
@interface VLCFullscreenContentView : NSView {
    VlcPluginMac *_cppPlugin;
123
    NSTimeInterval _timeSinceLastMouseMove;
124
}
125
@property (readwrite) VlcPluginMac *cppPlugin;
126

127 128
- (void)hideToolbar;

129 130 131
@end

@interface VLCFullscreenWindow : NSWindow {
132 133
    NSRect _initialFrame;
    VLCFullscreenContentView *_customContentView;
134
}
135
@property (readonly) VLCFullscreenContentView *customContentView;
136 137 138 139 140

- (id)initWithContentRect:(NSRect)contentRect;

@end

141 142 143 144 145 146
@interface NSScreen (VLCAdditions)
- (BOOL)hasMenuBar;
- (BOOL)hasDock;
- (CGDirectDisplayID)displayID;
@end

147
@interface VLCPerInstanceStorage : NSObject
148 149 150 151 152 153 154 155 156
{
    VlcPluginMac *_cppPlugin;
    VLCBrowserRootLayer *_browserRootLayer;
    VLCPlaybackLayer *_playbackLayer;
    VLCNoMediaLayer *_noMediaLayer;
    VLCControllerLayer *_controllerLayer;
    VLCFullscreenWindow *_fullscreenWindow;
    VLCFullscreenContentView *_fullscreenView;
}
157

158 159 160 161 162 163 164
@property (readwrite, assign) VlcPluginMac *cppPlugin;
@property (readwrite, retain) VLCBrowserRootLayer *browserRootLayer;
@property (readwrite, retain) VLCPlaybackLayer *playbackLayer;
@property (readwrite, retain) VLCNoMediaLayer *noMediaLayer;
@property (readwrite, retain) VLCControllerLayer *controllerLayer;
@property (readwrite, retain) VLCFullscreenWindow *fullscreenWindow;
@property (readwrite, retain) VLCFullscreenContentView *fullscreenView;
165

166
@end
167

168
@implementation VLCPerInstanceStorage
169

170 171
@synthesize cppPlugin = _cppPlugin, browserRootLayer = _browserRootLayer, playbackLayer = _playbackLayer, noMediaLayer = _noMediaLayer, controllerLayer = _controllerLayer, fullscreenWindow = _fullscreenWindow, fullscreenView = _fullscreenView;

172
@end
173

174
#pragma mark - handling of c++ bindings
175

176 177 178
VlcPluginMac::VlcPluginMac(NPP instance, NPuint16_t mode) :
    VlcPluginBase(instance, mode)
{
179 180
    _perInstanceStorage = [[VLCPerInstanceStorage alloc] init];
    [(VLCPerInstanceStorage *)_perInstanceStorage setCppPlugin: this];
181 182 183 184
}

VlcPluginMac::~VlcPluginMac()
{
185
    [(VLCPerInstanceStorage *)_perInstanceStorage release];
186 187 188 189
}

void VlcPluginMac::set_player_window()
{
190
    /* pass base layer to libvlc to pass it on to the vout */
191
    libvlc_media_player_set_nsobject(getMD(), [(VLCPerInstanceStorage *)this->_perInstanceStorage browserRootLayer]);
192 193 194 195
}

void VlcPluginMac::toggle_fullscreen()
{
196 197
    if (!get_options().get_enable_fs())
        return;
198
    libvlc_toggle_fullscreen(getMD());
199
    this->update_controls();
200

201
    if (get_fullscreen() != 0) {
202 203
        /* this window is kind of useless. however, we need to support 10.5, since enterFullScreenMode depends on the
         * existance of a parent window. This is solved in 10.6 and we should remove the window once we require it. */
204 205 206
        [(VLCPerInstanceStorage *)this->_perInstanceStorage setFullscreenWindow:[[VLCFullscreenWindow alloc] initWithContentRect: NSMakeRect(0., 0., npwindow.width, npwindow.height)]];
        [[(VLCPerInstanceStorage *)this->_perInstanceStorage fullscreenWindow] setLevel:CGShieldingWindowLevel()];
        [(VLCPerInstanceStorage *)this->_perInstanceStorage setFullscreenView:[[(VLCPerInstanceStorage *)this->_perInstanceStorage fullscreenWindow] customContentView]];
207 208 209

        /* CAVE: the order of these methods is important, since we want a layer-hosting view instead of
         * a layer-backed view, which you'd get if you do it the other way around */
210 211 212
        [[(VLCPerInstanceStorage *)this->_perInstanceStorage fullscreenView] setLayer:[CALayer layer]];
        [[(VLCPerInstanceStorage *)this->_perInstanceStorage fullscreenView] setWantsLayer:YES];
        [[(VLCPerInstanceStorage *)this->_perInstanceStorage fullscreenView] setCppPlugin:this];
213

214 215 216
        [[(VLCPerInstanceStorage *)this->_perInstanceStorage noMediaLayer] removeFromSuperlayer];
        [[(VLCPerInstanceStorage *)this->_perInstanceStorage playbackLayer] removeFromSuperlayer];
        [[(VLCPerInstanceStorage *)this->_perInstanceStorage controllerLayer] removeFromSuperlayer];
217

218
        if ([(VLCPerInstanceStorage *)this->_perInstanceStorage fullscreenView] == nil)
219
            return;
220
        if ([(VLCPerInstanceStorage *)this->_perInstanceStorage fullscreenView].layer == nil)
221 222
            return;

223 224 225 226
        [[(VLCPerInstanceStorage *)this->_perInstanceStorage fullscreenView].layer addSublayer: [(VLCPerInstanceStorage *)this->_perInstanceStorage noMediaLayer]];
        [[(VLCPerInstanceStorage *)this->_perInstanceStorage fullscreenView].layer addSublayer: [(VLCPerInstanceStorage *)this->_perInstanceStorage playbackLayer]];
        [[(VLCPerInstanceStorage *)this->_perInstanceStorage fullscreenView].layer addSublayer: [(VLCPerInstanceStorage *)this->_perInstanceStorage controllerLayer]];
        [[(VLCPerInstanceStorage *)this->_perInstanceStorage fullscreenView].layer setNeedsDisplay];
227

228
        [[(VLCPerInstanceStorage *)this->_perInstanceStorage fullscreenWindow].contentView enterFullScreenMode: [NSScreen mainScreen] withOptions: [NSDictionary dictionaryWithObjectsAndKeys: [NSNumber numberWithInt: 0], NSFullScreenModeAllScreens, nil]];
229 230

        [[(VLCPerInstanceStorage *)this->_perInstanceStorage fullscreenView] performSelector:@selector(hideToolbar) withObject:nil afterDelay: 4.1];
231
    } else {
232
        if (![(VLCPerInstanceStorage *)this->_perInstanceStorage fullscreenWindow])
233
            return;
234
        if (![(VLCPerInstanceStorage *)this->_perInstanceStorage fullscreenWindow].contentView)
235 236
            return;

237 238 239 240
        [[(VLCPerInstanceStorage *)this->_perInstanceStorage fullscreenWindow].contentView exitFullScreenModeWithOptions: nil];
        [[(VLCPerInstanceStorage *)this->_perInstanceStorage noMediaLayer] removeFromSuperlayer];
        [[(VLCPerInstanceStorage *)this->_perInstanceStorage playbackLayer] removeFromSuperlayer];
        [[(VLCPerInstanceStorage *)this->_perInstanceStorage controllerLayer] removeFromSuperlayer];
241

242 243 244 245 246
        [[(VLCPerInstanceStorage *)this->_perInstanceStorage browserRootLayer] addSublayer: [(VLCPerInstanceStorage *)this->_perInstanceStorage noMediaLayer]];
        [[(VLCPerInstanceStorage *)this->_perInstanceStorage browserRootLayer] addSublayer: [(VLCPerInstanceStorage *)this->_perInstanceStorage playbackLayer]];
        [[(VLCPerInstanceStorage *)this->_perInstanceStorage browserRootLayer] addSublayer: [(VLCPerInstanceStorage *)this->_perInstanceStorage controllerLayer]];
        [[(VLCPerInstanceStorage *)this->_perInstanceStorage fullscreenWindow] orderOut: nil];
        [(VLCPerInstanceStorage *)this->_perInstanceStorage setFullscreenWindow: nil];
247
    }
248 249
}

250
void VlcPluginMac::set_fullscreen(int i_value)
251
{
252 253
    if (!get_options().get_enable_fs())
        return;
254
    libvlc_set_fullscreen(getMD(), i_value);
255
    this->update_controls();
256 257
}

258
bool  VlcPluginMac::get_fullscreen()
259
{
260
    return libvlc_get_fullscreen(getMD());
261 262
}

263 264
void VlcPluginMac::set_toolbar_visible(bool b_value)
{
265
    if (!get_options().get_show_toolbar()) {
266
        [(VLCPerInstanceStorage *)this->_perInstanceStorage controllerLayer].hidden = YES;
267
        return;
268
    }
269
    [(VLCPerInstanceStorage *)this->_perInstanceStorage controllerLayer].hidden = !b_value;
270 271 272 273
}

bool VlcPluginMac::get_toolbar_visible()
{
274
    return [(VLCPerInstanceStorage *)this->_perInstanceStorage controllerLayer].isHidden;
275 276 277 278
}

void VlcPluginMac::update_controls()
{
279 280
    libvlc_state_t currentstate = libvlc_media_player_get_state(getMD());
    if (currentstate == libvlc_Playing || currentstate == libvlc_Paused || currentstate == libvlc_Opening) {
281 282
        [(VLCPerInstanceStorage *)this->_perInstanceStorage noMediaLayer].hidden = YES;
        [(VLCPerInstanceStorage *)this->_perInstanceStorage playbackLayer].hidden = NO;
283
    } else {
284 285
        [(VLCPerInstanceStorage *)this->_perInstanceStorage noMediaLayer].hidden = NO;
        [(VLCPerInstanceStorage *)this->_perInstanceStorage playbackLayer].hidden = YES;
286 287
    }

288
    if ([(VLCPerInstanceStorage *)this->_perInstanceStorage controllerLayer] != nil) {
Hugo Beauzée-Luyssen's avatar
Hugo Beauzée-Luyssen committed
289 290
        [[(VLCPerInstanceStorage *)this->_perInstanceStorage controllerLayer] setMediaPosition: m_player.get_mp().position()];
        [[(VLCPerInstanceStorage *)this->_perInstanceStorage controllerLayer] setIsPlaying: player().mlp().isPlaying()];
291 292
        [[(VLCPerInstanceStorage *)this->_perInstanceStorage controllerLayer] setIsFullscreen:this->get_fullscreen()];
        [[(VLCPerInstanceStorage *)this->_perInstanceStorage controllerLayer] setNeedsDisplay];
293
    }
294 295
}

296 297 298 299 300 301 302
bool VlcPluginMac::create_windows()
{
    return true;
}

bool VlcPluginMac::resize_windows()
{
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
303
    return true;
304 305 306 307
}

bool VlcPluginMac::destroy_windows()
{
308
    npwindow.window = NULL;
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
309
    return true;
310
}
311

312 313
NPError VlcPluginMac::get_root_layer(void *value)
{
314 315 316 317 318 319 320 321
    if ([(VLCPerInstanceStorage *)this->_perInstanceStorage browserRootLayer] == nil) {
        [(VLCPerInstanceStorage *)this->_perInstanceStorage setBrowserRootLayer:[[VLCBrowserRootLayer alloc] init]];
        [(VLCPerInstanceStorage *)this->_perInstanceStorage browserRootLayer].cppPlugin = this;

        const char *userAgent = NPN_UserAgent(this->getBrowser());
        if (strstr(userAgent, "Safari") && strstr(userAgent, "Version/5")) {
            NSLog(@"Safari 5 detected, deploying UI update timer");
            [[(VLCPerInstanceStorage *)this->_perInstanceStorage browserRootLayer] performSelector:@selector(startUIUpdateTimer) withObject:nil afterDelay:1.];
322 323
        } else if (strstr(userAgent, "Firefox"))
            this->runningWithinFirefox = true;
324

325 326
        [(VLCPerInstanceStorage *)this->_perInstanceStorage setNoMediaLayer:[[VLCNoMediaLayer alloc] init]];
        [(VLCPerInstanceStorage *)this->_perInstanceStorage noMediaLayer].opaque = 1.;
327 328
        [[(VLCPerInstanceStorage *)this->_perInstanceStorage noMediaLayer] setCppPlugin:this];
        [[(VLCPerInstanceStorage *)this->_perInstanceStorage browserRootLayer] addSublayer:[(VLCPerInstanceStorage *)this->_perInstanceStorage noMediaLayer]];
329

330
        [(VLCPerInstanceStorage *)this->_perInstanceStorage setControllerLayer:[[VLCControllerLayer alloc] init]];
331 332
        [[(VLCPerInstanceStorage *)this->_perInstanceStorage browserRootLayer] addSublayer:[(VLCPerInstanceStorage *)this->_perInstanceStorage controllerLayer]];
        [[(VLCPerInstanceStorage *)this->_perInstanceStorage controllerLayer] setCppPlugin:this];
333 334 335

        [[(VLCPerInstanceStorage *)this->_perInstanceStorage browserRootLayer] setNeedsDisplay];
    }
336

337
    *(CALayer **)value = [(VLCPerInstanceStorage *)this->_perInstanceStorage browserRootLayer];
338
    return NPERR_NO_ERROR;
339 340
}

341 342
bool VlcPluginMac::handle_event(void *event)
{
343
    NPCocoaEvent* cocoaEvent = (NPCocoaEvent*)event;
344

345 346 347 348 349 350 351
    if (!event)
        return false;

    NPCocoaEventType eventType = cocoaEvent->type;

    switch (eventType) {
        case NPCocoaEventMouseDown:
352
        {
353 354 355
            if ([(VLCPerInstanceStorage *)this->_perInstanceStorage playbackLayer] != nil) {
                if ([[(VLCPerInstanceStorage *)this->_perInstanceStorage playbackLayer] respondsToSelector:@selector(mouseButtonDown:)])
                    [[(VLCPerInstanceStorage *)this->_perInstanceStorage playbackLayer] mouseButtonDown:cocoaEvent->data.mouse.buttonNumber];
356
            }
357
            if (cocoaEvent->data.mouse.clickCount >= 2)
358
                this->toggle_fullscreen();
359

360 361 362
            CGPoint point = CGPointMake(cocoaEvent->data.mouse.pluginX,
                                        // Flip the y coordinate
                                        npwindow.height - cocoaEvent->data.mouse.pluginY);
363 364
            if ([(VLCPerInstanceStorage *)this->_perInstanceStorage controllerLayer] != nil)
                [[(VLCPerInstanceStorage *)this->_perInstanceStorage controllerLayer] handleMouseDown:[[(VLCPerInstanceStorage *)this->_perInstanceStorage browserRootLayer] convertPoint:point toLayer:[(VLCPerInstanceStorage *)this->_perInstanceStorage controllerLayer]]];
365

366 367
            return true;
        }
368
        case NPCocoaEventMouseUp:
369
        {
370 371 372
            if ([(VLCPerInstanceStorage *)this->_perInstanceStorage playbackLayer] != nil) {
                if ([[(VLCPerInstanceStorage *)this->_perInstanceStorage playbackLayer] respondsToSelector:@selector(mouseButtonUp:)])
                    [[(VLCPerInstanceStorage *)this->_perInstanceStorage playbackLayer] mouseButtonUp:cocoaEvent->data.mouse.buttonNumber];
373
            }
374 375 376 377
            CGPoint point = CGPointMake(cocoaEvent->data.mouse.pluginX,
                                        // Flip the y coordinate
                                        npwindow.height - cocoaEvent->data.mouse.pluginY);

378 379
            if ([(VLCPerInstanceStorage *)this->_perInstanceStorage controllerLayer] != nil)
                [[(VLCPerInstanceStorage *)this->_perInstanceStorage controllerLayer] handleMouseUp:[[(VLCPerInstanceStorage *)this->_perInstanceStorage browserRootLayer] convertPoint:point toLayer:[(VLCPerInstanceStorage *)this->_perInstanceStorage controllerLayer]]];
380 381 382

            return true;
        }
383 384
        case NPCocoaEventMouseMoved:
        {
385 386 387
            if ([(VLCPerInstanceStorage *)this->_perInstanceStorage playbackLayer] != nil) {
                if ([[(VLCPerInstanceStorage *)this->_perInstanceStorage playbackLayer] respondsToSelector:@selector(mouseMovedToX:Y:)])
                    [[(VLCPerInstanceStorage *)this->_perInstanceStorage playbackLayer] mouseMovedToX:cocoaEvent->data.mouse.pluginX Y:cocoaEvent->data.mouse.pluginY];
388 389
            }
        }
390 391 392 393 394 395
        case NPCocoaEventMouseDragged:
        {
            CGPoint point = CGPointMake(cocoaEvent->data.mouse.pluginX,
                                        // Flip the y coordinate
                                        npwindow.height - cocoaEvent->data.mouse.pluginY);

396 397
            if ([(VLCPerInstanceStorage *)this->_perInstanceStorage controllerLayer] != nil)
                [[(VLCPerInstanceStorage *)this->_perInstanceStorage controllerLayer] handleMouseDragged:[[(VLCPerInstanceStorage *)this->_perInstanceStorage browserRootLayer] convertPoint:point toLayer:[(VLCPerInstanceStorage *)this->_perInstanceStorage controllerLayer]]];
398 399 400

            return true;
        }
401 402
        case NPCocoaEventMouseEntered:
        {
403
            this->set_toolbar_visible(true);
404 405 406 407
            return true;
        }
        case NPCocoaEventMouseExited:
        {
408
            this->set_toolbar_visible(false);
409 410
            return true;
        }
411
        case NPCocoaEventKeyDown:
412 413 414 415
        {
            if (cocoaEvent->data.key.keyCode == 53) {
                toggle_fullscreen();
                return true;
416
            } else if (cocoaEvent->data.key.keyCode == 49) {
Hugo Beauzée-Luyssen's avatar
Hugo Beauzée-Luyssen committed
417
                m_player.mlp().pause();
418
                return true;
419 420 421
            }
        }
        case NPCocoaEventKeyUp:
422 423
        case NPCocoaEventFocusChanged:
        case NPCocoaEventScrollWheel:
424
            return true;
425 426 427 428 429

        default:
            break;
    }

430
    if (eventType == NPCocoaEventDrawRect) {
431 432 433 434 435 436 437 438
        /* even though we are using the CoreAnimation drawing model
         * this can be called by the browser, especially when doing
         * screenshots.
         * Since speed isn't important in this case, we could fetch
         * fetch the current frame from libvlc and render it as an
         * image.
         * However, for sakes of simplicity, just show a black
         * rectancle for now. */
439 440 441
        CGContextRef cgContext = cocoaEvent->data.draw.context;
        if (!cgContext) {
            return false;
442
        }
443 444 445 446 447 448 449 450 451 452

        float windowWidth = npwindow.width;
        float windowHeight = npwindow.height;

        CGContextSaveGState(cgContext);

        // this context is flipped..
        CGContextTranslateCTM(cgContext, 0.0, windowHeight);
        CGContextScaleCTM(cgContext, 1., -1.);

453
        // draw black rectancle
454
        CGContextAddRect(cgContext, CGRectMake(0, 0, windowWidth, windowHeight));
455
        CGContextSetGrayFillColor(cgContext, 0., 1.);
456 457 458 459 460
        CGContextDrawPath(cgContext, kCGPathFill);

        CGContextRestoreGState(cgContext);

        return true;
461
    }
462

463 464
    return VlcPluginBase::handle_event(event);
}
465

466 467
#pragma mark - objc class implementations

468
@implementation VLCBrowserRootLayer
469

470 471
@synthesize cppPlugin = _cppPlugin;

472 473 474 475 476 477 478 479 480 481
- (id)init
{
    if (self = [super init]) {
        self.needsDisplayOnBoundsChange = YES;
        self.autoresizingMask = kCALayerWidthSizable | kCALayerHeightSizable;
    }

    return self;
}

482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504
- (void)startUIUpdateTimer
{
    _interfaceUpdateTimer = [NSTimer scheduledTimerWithTimeInterval:0.3 target:self selector:@selector(_updateUI) userInfo:nil repeats:YES];
    [_interfaceUpdateTimer retain];
    [_interfaceUpdateTimer fire];
}

- (void)dealloc
{
    if (_interfaceUpdateTimer) {
        [_interfaceUpdateTimer invalidate];
        [_interfaceUpdateTimer release];
    }

    [super dealloc];
}

- (void)_updateUI
{
    if (_cppPlugin)
        _cppPlugin->update_controls();
}

505
- (void)addVoutLayer:(CALayer *)aLayer
506
{
507
    [CATransaction begin];
508
    VLCPlaybackLayer *playbackLayer = (VLCPlaybackLayer *)[aLayer retain];
509 510
    playbackLayer.opaque = 1.;
    playbackLayer.hidden = NO;
511 512 513 514

    if (libvlc_get_fullscreen(_cppPlugin->getMD()) != 0) {
        /* work-around a 32bit runtime limitation where we can't cast
         * NSRect to CGRect */
515
        NSRect fullscreenViewFrame = [(VLCPerInstanceStorage *)_cppPlugin->_perInstanceStorage fullscreenView].frame;
516 517 518 519 520
        playbackLayer.bounds = CGRectMake(fullscreenViewFrame.origin.x,
                                          fullscreenViewFrame.origin.y,
                                          fullscreenViewFrame.size.width,
                                          fullscreenViewFrame.size.height);
        playbackLayer.autoresizingMask = kCALayerWidthSizable | kCALayerHeightSizable;
521
        [(VLCPerInstanceStorage *)_cppPlugin->_perInstanceStorage setPlaybackLayer: playbackLayer];
522

523 524 525 526
        [[(VLCPerInstanceStorage *)_cppPlugin->_perInstanceStorage controllerLayer] removeFromSuperlayer];
        [[(VLCPerInstanceStorage *)_cppPlugin->_perInstanceStorage fullscreenView].layer addSublayer: [(VLCPerInstanceStorage *)_cppPlugin->_perInstanceStorage playbackLayer]];
        [[(VLCPerInstanceStorage *)_cppPlugin->_perInstanceStorage fullscreenView].layer addSublayer: [(VLCPerInstanceStorage *)_cppPlugin->_perInstanceStorage controllerLayer]];
        [[(VLCPerInstanceStorage *)_cppPlugin->_perInstanceStorage fullscreenView].layer setNeedsDisplay];
527
    } else {
528 529 530
        [(VLCPerInstanceStorage *)_cppPlugin->_perInstanceStorage setPlaybackLayer: playbackLayer];
        [(VLCPerInstanceStorage *)_cppPlugin->_perInstanceStorage playbackLayer].bounds = [(VLCPerInstanceStorage *)_cppPlugin->_perInstanceStorage noMediaLayer].bounds;
        [self insertSublayer:[(VLCPerInstanceStorage *)_cppPlugin->_perInstanceStorage playbackLayer] below:[(VLCPerInstanceStorage *)_cppPlugin->_perInstanceStorage controllerLayer]];
531
    }
532
    [self setNeedsDisplay];
533 534
    [[(VLCPerInstanceStorage *)_cppPlugin->_perInstanceStorage playbackLayer] setNeedsDisplay];
    CGRect frame = [(VLCPerInstanceStorage *)_cppPlugin->_perInstanceStorage playbackLayer].bounds;
535 536
    frame.origin.x = 0.;
    frame.origin.y = 0.;
537
    [(VLCPerInstanceStorage *)_cppPlugin->_perInstanceStorage playbackLayer].frame = frame;
538
    [CATransaction commit];
539 540
}

541
- (void)removeVoutLayer:(CALayer *)aLayer
542
{
543 544 545
    [CATransaction begin];
    [aLayer removeFromSuperlayer];
    [CATransaction commit];
546

547 548
    if ([(VLCPerInstanceStorage *)_cppPlugin->_perInstanceStorage playbackLayer] == aLayer)
        [(VLCPerInstanceStorage *)_cppPlugin->_perInstanceStorage setPlaybackLayer:nil];
549
}
550

551 552
- (CGSize)currentOutputSize
{
553
    return [(VLCPerInstanceStorage *)_cppPlugin->_perInstanceStorage browserRootLayer].visibleRect.size;
554 555 556 557
}

@end

558
@implementation VLCNoMediaLayer
559

560 561
@synthesize cppPlugin = _cppPlugin;

562 563 564 565
- (id)init
{
    if (self = [super init]) {
        self.needsDisplayOnBoundsChange = YES;
566
        self.autoresizingMask = kCALayerWidthSizable | kCALayerHeightSizable;
567 568 569 570 571
    }

    return self;
}

572
- (void)drawInContext:(CGContextRef)cgContext
573 574 575 576 577 578
{
    float windowWidth = self.visibleRect.size.width;
    float windowHeight = self.visibleRect.size.height;

    CGContextSaveGState(cgContext);

579 580 581 582 583
    CGColorRef backgroundColor;
    unsigned r = 0, g = 0, b = 0;
    HTMLColor2RGB(self.cppPlugin->get_options().get_bg_color().c_str(), &r, &g, &b);
    backgroundColor = CGColorCreateGenericRGB(r, g, b, 1.);

584 585 586 587 588 589 590 591 592 593 594 595
    if (self.cppPlugin->get_options().get_enable_branding()) {
        // draw background
        CGContextAddRect(cgContext, CGRectMake(0, 0, windowWidth, windowHeight));
        CGContextSetFillColorWithColor(cgContext, backgroundColor);
        CGContextDrawPath(cgContext, kCGPathFill);

        // draw gradient
        CGImageRef gradient = createImageNamed(@"gradient");
        CGContextDrawImage(cgContext, CGRectMake(0., 0., windowWidth, 150.), gradient);
        CGImageRelease(gradient);

        // draw info text
596 597 598 599 600 601 602
        CGContextSetGrayStrokeColor(cgContext, .95, 1.);
        CGContextSetTextDrawingMode(cgContext, kCGTextFill);
        CGContextSetGrayFillColor(cgContext, 1., 1.);
        CFStringRef keys[2];
        keys[0] = kCTFontAttributeName;
        keys[1] = kCTForegroundColorFromContextAttributeName;
        CFTypeRef values[2];
603
        values[0] = CTFontCreateWithName(CFSTR("HelveticaNeue-Light"),18,NULL);
604 605 606 607 608
        values[1] = kCFBooleanTrue;
        CFDictionaryRef stylesDict = CFDictionaryCreate(kCFAllocatorDefault,
                                                        (const void **)&keys,
                                                        (const void **)&values,
                                                        2, NULL, NULL);
609
        CFAttributedStringRef attRef = CFAttributedStringCreate(kCFAllocatorDefault, CFSTR("VLC Web Plugin"), stylesDict);
610
        CTLineRef textLine = CTLineCreateWithAttributedString(attRef);
611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634
        CGContextSetTextPosition(cgContext, 25., 60.);
        CTLineDraw(textLine, cgContext);
        CFRelease(textLine);
        CFRelease(attRef);

        // print smaller text from here
        CFRelease(stylesDict);
        values[0] = CTFontCreateWithName(CFSTR("Helvetica"),12,NULL);
        stylesDict = CFDictionaryCreate(kCFAllocatorDefault,
                                        (const void **)&keys,
                                        (const void **)&values,
                                        2, NULL, NULL);

        // draw version string
        CFStringRef arch;
    #ifdef __x86_64__
        arch = CFSTR("64-bit");
    #else
        arch = CFSTR("32-bit");
    #endif

        attRef = CFAttributedStringCreate(kCFAllocatorDefault, CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%s — windowed mode — %@"), libvlc_get_version(), arch), stylesDict);
        textLine = CTLineCreateWithAttributedString(attRef);
        CGContextSetTextPosition(cgContext, 25., 40.);
635 636 637 638
        CTLineDraw(textLine, cgContext);
        CFRelease(textLine);
        CFRelease(attRef);
        CFRelease(stylesDict);
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 672 673 674 675 676 677 678 679 680 681 682

        // draw cone
        CGImageRef cone = createImageNamed(@"cone");
        CGFloat coneWidth = CGImageGetWidth(cone);
        CGFloat coneHeight = CGImageGetHeight(cone);
        if (windowHeight <= 320.) {
            coneWidth = coneWidth / 2.;
            coneHeight = coneHeight / 2.;
        }
        CGContextDrawImage(cgContext, CGRectMake((windowWidth - coneWidth) / 2., (windowHeight - coneHeight) / 2., coneWidth, coneHeight), cone);
        CGImageRelease(cone);

        // draw custom text
        values[0] = CTFontCreateWithName(CFSTR("Helvetica"),14,NULL);
        stylesDict = CFDictionaryCreate(kCFAllocatorDefault,
                                        (const void **)&keys,
                                        (const void **)&values,
                                        2, NULL, NULL);
        const char *text = self.cppPlugin->get_options().get_bg_text().c_str();
        if (text != NULL) {
            attRef = CFAttributedStringCreate(kCFAllocatorDefault, CFStringCreateWithCString(kCFAllocatorDefault, text, kCFStringEncodingUTF8), stylesDict);
            textLine = CTLineCreateWithAttributedString(attRef);
            CGRect textRect = CTLineGetImageBounds(textLine, cgContext);
            CGContextSetTextPosition(cgContext, ((windowWidth - textRect.size.width) / 2.), (windowHeight / 2.) + (coneHeight / 2.) + textRect.size.height + 50.);
            CTLineDraw(textLine, cgContext);
            CFRelease(textLine);
            CFRelease(attRef);
        }
        CFRelease(stylesDict);
    } else {
        // draw a background colored rect
        CGContextAddRect(cgContext, CGRectMake(0, 0, windowWidth, windowHeight));
        CGContextSetFillColorWithColor(cgContext, backgroundColor);
        CGContextDrawPath(cgContext, kCGPathFill);

        const char *text = self.cppPlugin->get_options().get_bg_text().c_str();
        if (text != NULL) {
            CGContextSetGrayStrokeColor(cgContext, .95, 1.);
            CGContextSetTextDrawingMode(cgContext, kCGTextFill);
            CGContextSetGrayFillColor(cgContext, 1., 1.);
            CFStringRef keys[2];
            keys[0] = kCTFontAttributeName;
            keys[1] = kCTForegroundColorFromContextAttributeName;
            CFTypeRef values[2];
683
            values[0] = CTFontCreateWithName(CFSTR("HelveticaNeue-Light"),18,NULL);
684 685 686 687 688 689 690 691 692 693 694 695 696 697
            values[1] = kCFBooleanTrue;
            CFDictionaryRef stylesDict = CFDictionaryCreate(kCFAllocatorDefault,
                                                            (const void **)&keys,
                                                            (const void **)&values,
                                                            2, NULL, NULL);
            CFAttributedStringRef attRef = CFAttributedStringCreate(kCFAllocatorDefault, CFStringCreateWithCString(kCFAllocatorDefault, text, kCFStringEncodingUTF8), stylesDict);
            CTLineRef textLine = CTLineCreateWithAttributedString(attRef);
            CGRect textRect = CTLineGetImageBounds(textLine, cgContext);
            CGContextSetTextPosition(cgContext, ((windowWidth - textRect.size.width) / 2.), (windowHeight / 2.));
            CTLineDraw(textLine, cgContext);
            CFRelease(textLine);
            CFRelease(attRef);
            CFRelease(stylesDict);
        }
698
    }
699
    CGColorRelease(backgroundColor);
700 701 702 703

    CGContextRestoreGState(cgContext);
}

704 705 706 707
@end

@implementation VLCControllerLayer

708 709
@synthesize cppPlugin = _cppPlugin, mediaPosition = _mediaPosition, isPlaying = _isPlaying, isFullscreen = _isFullscreen;

710 711 712 713 714 715 716 717 718 719 720 721 722
- (id)init
{
    if (self = [super init]) {
        self.needsDisplayOnBoundsChange = YES;
        self.frame = CGRectMake(0, 0, 0, 25);
        self.autoresizingMask = kCALayerWidthSizable;

        _playImage = createImageNamed(@"Play");
        _pauseImage = createImageNamed(@"Pause");
        _sliderTrackLeft = createImageNamed(@"SliderTrackLeft");
        _sliderTrackRight = createImageNamed(@"SliderTrackRight");
        _sliderTrackCenter = createImageNamed(@"SliderTrackCenter");

723 724 725
        _enterFullscreen = createImageNamed(@"enter-fullscreen");
        _leaveFullscreen = createImageNamed(@"leave-fullscreen");

726
        _knob = createImageNamed(@"Knob");
727
    }
728 729 730 731 732 733 734 735 736 737 738 739 740

    return self;
}

- (void)dealloc
{
    CGImageRelease(_playImage);
    CGImageRelease(_pauseImage);

    CGImageRelease(_sliderTrackLeft);
    CGImageRelease(_sliderTrackRight);
    CGImageRelease(_sliderTrackCenter);

741 742 743
    CGImageRelease(_enterFullscreen);
    CGImageRelease(_leaveFullscreen);

744 745 746 747 748 749 750
    CGImageRelease(_knob);

    [super dealloc];
}

- (CGRect)_playPauseButtonRect
{
751 752 753 754 755 756
    return CGRectMake(4., (25. - CGImageGetHeight(_playImage)) / 2., CGImageGetWidth(_playImage), CGImageGetHeight(_playImage));
}

- (CGRect)_fullscreenButtonRect
{
    return CGRectMake( CGRectGetMaxX([self _sliderRect]), (25. - CGImageGetHeight(_enterFullscreen)) / 2., CGImageGetWidth(_enterFullscreen), CGImageGetHeight(_enterFullscreen));
757 758 759 760
}

- (CGRect)_sliderRect
{
761
    CGFloat sliderYPosition = (self.bounds.size.height - CGImageGetHeight(_sliderTrackLeft)) / 2.;
762
    CGFloat playPauseButtonWidth = [self _playPauseButtonRect].size.width;
763
    CGFloat fullscreenButtonWidth = self.cppPlugin->get_options().get_enable_fs() ? CGImageGetWidth(_enterFullscreen) : 0.;
764

765
    return CGRectMake(playPauseButtonWidth + 7, sliderYPosition,
766
                      self.bounds.size.width - playPauseButtonWidth - fullscreenButtonWidth - 15., CGImageGetHeight(_sliderTrackLeft));
767 768 769 770 771 772
}

- (CGRect)_sliderThumbRect
{
    CGRect sliderRect = [self _sliderRect];

773
    CGFloat x = self.mediaPosition * (CGRectGetWidth(sliderRect) - CGImageGetWidth(_knob));
774

775
    return CGRectMake(CGRectGetMinX(sliderRect) + x, CGRectGetMinY(sliderRect) + 1,
776 777 778 779 780 781 782 783 784 785
                      CGImageGetWidth(_knob), CGImageGetHeight(_knob));
}

- (CGRect)_innerSliderRect
{
    return CGRectInset([self _sliderRect], CGRectGetWidth([self _sliderThumbRect]) / 2, 0);
}

- (void)_drawPlayPauseButtonInContext:(CGContextRef)context
{
786
    CGContextDrawImage(context, [self _playPauseButtonRect], self.isPlaying ? _pauseImage : _playImage);
787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810
}

- (void)_drawSliderInContext:(CGContextRef)context
{
    // Draw the thumb
    CGRect sliderThumbRect = [self _sliderThumbRect];
    CGContextDrawImage(context, sliderThumbRect, _knob);

    CGRect sliderRect = [self _sliderRect];

    // Draw left part
    CGRect sliderLeftTrackRect = CGRectMake(CGRectGetMinX(sliderRect), CGRectGetMinY(sliderRect),
                                            CGImageGetWidth(_sliderTrackLeft), CGImageGetHeight(_sliderTrackLeft));
    CGContextDrawImage(context, sliderLeftTrackRect, _sliderTrackLeft);

    // Draw center part
    CGRect sliderCenterTrackRect = CGRectInset(sliderRect, CGImageGetWidth(_sliderTrackLeft), 0);
    CGContextDrawImage(context, sliderCenterTrackRect, _sliderTrackCenter);

    // Draw right part
    CGRect sliderRightTrackRect = CGRectMake(CGRectGetMaxX(sliderCenterTrackRect), CGRectGetMinY(sliderRect),
                                             CGImageGetWidth(_sliderTrackRight), CGImageGetHeight(_sliderTrackRight));
    CGContextDrawImage(context, sliderRightTrackRect, _sliderTrackRight);

811
    // Draw fullscreen button
812 813 814 815 816
    if (self.cppPlugin->get_options().get_enable_fs()) {
        CGRect fullscreenButtonRect = [self _fullscreenButtonRect];
        fullscreenButtonRect.origin.x = CGRectGetMaxX(sliderRightTrackRect) + 5;
        CGContextDrawImage(context, fullscreenButtonRect, self.isFullscreen ? _leaveFullscreen : _enterFullscreen);
    }
817 818 819 820 821 822 823 824 825
}

- (void)drawInContext:(CGContextRef)cgContext
{
    CGContextSetFillColorWithColor(cgContext, CGColorGetConstantColor(kCGColorBlack));
    CGContextFillRect(cgContext, self.bounds);

    [self _drawPlayPauseButtonInContext:cgContext];
    [self _drawSliderInContext:cgContext];
826 827
}

828 829 830 831
- (void)_setNewTimeForThumbCenterX:(CGFloat)centerX
{
    CGRect innerRect = [self _innerSliderRect];

832
    double fraction = (centerX - CGRectGetMinX(innerRect)) / CGRectGetWidth(innerRect);
833 834 835 836 837
    if (fraction > 1.0)
        fraction = 1.0;
    else if (fraction < 0.0)
        fraction = 0.0;

838
    libvlc_media_player_set_position(self.cppPlugin->getMD(), fraction);
839 840 841 842 843 844 845

    [self setNeedsDisplay];
}

- (void)handleMouseDown:(CGPoint)point
{
    if (CGRectContainsPoint([self _sliderRect], point)) {
846
        _wasPlayingBeforeMouseDown = self.isPlaying;
847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863
        _isScrubbing = YES;

        if (CGRectContainsPoint([self _sliderThumbRect], point))
            _mouseDownXDelta = point.x - CGRectGetMidX([self _sliderThumbRect]);
        else {
            [self _setNewTimeForThumbCenterX:point.x];
            _mouseDownXDelta = 0;
        }
    }
}

- (void)handleMouseUp:(CGPoint)point
{
    if (_isScrubbing) {
        _isScrubbing = NO;
        _mouseDownXDelta = 0;

864
        return;
865 866 867
    }

    if (CGRectContainsPoint([self _playPauseButtonRect], point)) {
Hugo Beauzée-Luyssen's avatar
Hugo Beauzée-Luyssen committed
868
        self.cppPlugin->player().mlp().pause();
869 870
        return;
    }
871 872 873 874
    if (CGRectContainsPoint([self _fullscreenButtonRect], point)) {
        self.cppPlugin->toggle_fullscreen();
        return;
    }
875 876 877 878 879 880 881 882 883 884 885 886
}

- (void)handleMouseDragged:(CGPoint)point
{
    if (!_isScrubbing)
        return;

    point.x -= _mouseDownXDelta;

    [self _setNewTimeForThumbCenterX:point.x];
}

887
@end
888

889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917
@implementation NSScreen (VLCAdditions)

- (BOOL)hasMenuBar
{
    return ([self displayID] == [[[NSScreen screens] objectAtIndex:0] displayID]);
}

- (BOOL)hasDock
{
    NSRect screen_frame = [self frame];
    NSRect screen_visible_frame = [self visibleFrame];
    CGFloat f_menu_bar_thickness = [self hasMenuBar] ? [[NSStatusBar systemStatusBar] thickness] : 0.0;

    BOOL b_found_dock = NO;
    if (screen_visible_frame.size.width < screen_frame.size.width)
        b_found_dock = YES;
    else if (screen_visible_frame.size.height + f_menu_bar_thickness < screen_frame.size.height)
        b_found_dock = YES;

    return b_found_dock;
}

- (CGDirectDisplayID)displayID
{
    return (CGDirectDisplayID)[[[self deviceDescription] objectForKey: @"NSScreenNumber"] intValue];
}

@end

918 919
@implementation VLCFullscreenWindow

920 921
@synthesize customContentView = _customContentView;

922 923
- (id)initWithContentRect:(NSRect)contentRect
{
924
    if (self = [super initWithContentRect:contentRect styleMask:NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:NO]) {
925
        _initialFrame = contentRect;
926
        [self setBackgroundColor:[NSColor blackColor]];
927
        [self setAcceptsMouseMovedEvents: YES];
928 929

        _customContentView = [[VLCFullscreenContentView alloc] initWithFrame:_initialFrame];
930
        [_customContentView setAutoresizingMask: NSViewWidthSizable | NSViewHeightSizable];
931
        [[self contentView] addSubview: _customContentView];
932
        [self setInitialFirstResponder:_customContentView];
933 934 935 936
    }
    return self;
}

937 938 939 940 941 942
- (void)dealloc
{
    [_customContentView release];
    [super dealloc];
}

943 944 945 946 947 948 949 950 951 952
- (BOOL)canBecomeKeyWindow
{
    return YES;
}

- (BOOL)canBecomeMainWindow
{
    return YES;
}

953 954
@end

955
@implementation VLCFullscreenContentView
956

957 958
@synthesize cppPlugin = _cppPlugin;

959 960 961 962 963
- (BOOL)acceptsFirstResponder
{
    return YES;
}

964 965 966 967 968
- (BOOL)canBecomeKeyView
{
    return YES;
}

969 970 971 972 973 974 975 976 977 978
- (void)keyDown:(NSEvent *)theEvent
{
    NSString * characters = [theEvent charactersIgnoringModifiers];
    unichar key = 0;

    if ([characters length] > 0) {
        key = [[characters lowercaseString] characterAtIndex: 0];
        if (key) {
            /* Escape should always get you out of fullscreen */
            if (key == (unichar) 0x1b) {
979
                _cppPlugin->toggle_fullscreen();
980
                return;
981
            } else if (key == ' ') {
Hugo Beauzée-Luyssen's avatar
Hugo Beauzée-Luyssen committed
982
                _cppPlugin->player().mlp().pause();
983
                return;
984 985 986 987 988
            }
        }
    }
    [super keyDown: theEvent];
}
989

990 991
- (void)mouseDown:(NSEvent *)theEvent
{
992 993 994
    NSEventType eventType = [theEvent type];

    if (eventType == NSLeftMouseDown && !([theEvent modifierFlags] & NSControlKeyMask)) {
995
        if ([theEvent clickCount] >= 2)
996
            _cppPlugin->toggle_fullscreen();
997 998
        else {
            NSPoint point = [NSEvent mouseLocation];
999 1000 1001 1002 1003 1004
            /* for Firefox, retina doesn't exist yet so it will return pixels instead of points when doing the conversation
             * so don't convert for Firefox */
            if (!_cppPlugin->runningWithinFirefox)
                [[(VLCPerInstanceStorage *)_cppPlugin->_perInstanceStorage controllerLayer] handleMouseDown:[[(VLCPerInstanceStorage *)_cppPlugin->_perInstanceStorage browserRootLayer] convertPoint:CGPointMake(point.x, point.y) toLayer:[(VLCPerInstanceStorage *)_cppPlugin->_perInstanceStorage controllerLayer]]];
            else
                [[(VLCPerInstanceStorage *)_cppPlugin->_perInstanceStorage controllerLayer] handleMouseDown:CGPointMake(point.x, point.y)];
1005
        }
1006
    }
1007 1008
    if ([(VLCPerInstanceStorage *)_cppPlugin->_perInstanceStorage playbackLayer] != nil) {
        if ([[(VLCPerInstanceStorage *)_cppPlugin->_perInstanceStorage playbackLayer] respondsToSelector:@selector(mouseButtonDown:)]) {
1009
            if (eventType == NSLeftMouseDown)
1010
                [[(VLCPerInstanceStorage *)_cppPlugin->_perInstanceStorage playbackLayer] mouseButtonDown:0];
1011
            else if (eventType == NSRightMouseDown)
1012
                [[(VLCPerInstanceStorage *)_cppPlugin->_perInstanceStorage playbackLayer] mouseButtonDown:1];
1013
            else
1014
                [[(VLCPerInstanceStorage *)_cppPlugin->_perInstanceStorage playbackLayer] mouseButtonDown:2];
1015 1016
        }
    }
1017 1018 1019 1020

    [super mouseDown: theEvent];
}

1021 1022 1023
- (void)mouseUp:(NSEvent *)theEvent
{
    NSPoint point = [NSEvent mouseLocation];
1024
    NSEventType eventType = [theEvent type];
1025

1026 1027 1028 1029 1030 1031
    /* for Firefox, retina doesn't exist yet so it will return pixels instead of points when doing the conversation
     * so don't convert for Firefox */
    if (!_cppPlugin->runningWithinFirefox)
        [[(VLCPerInstanceStorage *)_cppPlugin->_perInstanceStorage controllerLayer] handleMouseUp:[[(VLCPerInstanceStorage *)_cppPlugin->_perInstanceStorage browserRootLayer] convertPoint:CGPointMake(point.x, point.y) toLayer:[(VLCPerInstanceStorage *)_cppPlugin->_perInstanceStorage controllerLayer]]];
    else
        [[(VLCPerInstanceStorage *)_cppPlugin->_perInstanceStorage controllerLayer] handleMouseUp:CGPointMake(point.x, point.y)];
1032

1033 1034
    if ([(VLCPerInstanceStorage *)_cppPlugin->_perInstanceStorage playbackLayer] != nil) {
        if ([[(VLCPerInstanceStorage *)_cppPlugin->_perInstanceStorage playbackLayer] respondsToSelector:@selector(mouseButtonUp:)]) {
1035
            if (eventType == NSLeftMouseUp)
1036
                [[(VLCPerInstanceStorage *)_cppPlugin->_perInstanceStorage playbackLayer] mouseButtonUp:0];
1037
            else if (eventType == NSRightMouseUp)
1038
                [[(VLCPerInstanceStorage *)_cppPlugin->_perInstanceStorage playbackLayer] mouseButtonUp:1];
1039
            else
1040
                [[(VLCPerInstanceStorage *)_cppPlugin->_perInstanceStorage playbackLayer] mouseButtonUp:2];
1041 1042 1043
        }
    }

1044 1045 1046 1047 1048 1049 1050
    [super mouseUp: theEvent];
}

- (void)mouseDragged:(NSEvent *)theEvent
{
    NSPoint point = [NSEvent mouseLocation];

1051
    [[(VLCPerInstanceStorage *)_cppPlugin->_perInstanceStorage controllerLayer] handleMouseDragged:[[(VLCPerInstanceStorage *)_cppPlugin->_perInstanceStorage browserRootLayer] convertPoint:CGPointMake(point.x, point.y) toLayer:[(VLCPerInstanceStorage *)_cppPlugin->_perInstanceStorage controllerLayer]]];
1052 1053 1054 1055 1056 1057 1058

    [super mouseDragged: theEvent];
}

- (void)mouseMoved:(NSEvent *)theEvent
{
    self.cppPlugin->set_toolbar_visible(true);
1059
    _timeSinceLastMouseMove = [NSDate timeIntervalSinceReferenceDate];
1060
    [self performSelector:@selector(_hideToolbar) withObject:nil afterDelay: 4.1];
1061

1062 1063
    if ([(VLCPerInstanceStorage *)_cppPlugin->_perInstanceStorage playbackLayer] != nil) {
        if ([[(VLCPerInstanceStorage *)_cppPlugin->_perInstanceStorage playbackLayer] respondsToSelector:@selector(mouseMovedToX:Y:)]) {
1064
            NSPoint ml = [theEvent locationInWindow];
1065
            [[(VLCPerInstanceStorage *)_cppPlugin->_perInstanceStorage playbackLayer] mouseMovedToX:ml.x Y:([self.window frame].size.height - ml.y)];
1066 1067 1068
        }
    }

1069 1070 1071
    [super mouseMoved: theEvent];
}

1072 1073 1074 1075 1076 1077
- (void)_hideToolbar
{
    if ([NSDate timeIntervalSinceReferenceDate] - _timeSinceLastMouseMove >= 4)
        [self hideToolbar];
}

1078 1079
- (void)hideToolbar
{
1080 1081
    self.cppPlugin->set_toolbar_visible(false);
    [NSCursor setHiddenUntilMouseMoves:YES];
1082 1083
}

1084 1085
@end

1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103
#pragma mark - helpers

CGImageRef createImageNamed(NSString *name)
{
    CFURLRef url = CFBundleCopyResourceURL(CFBundleGetBundleWithIdentifier(CFSTR("org.videolan.vlc-npapi-plugin")), (CFStringRef)name, CFSTR("png"), NULL);

    if (!url)
        return NULL;

    CGImageSourceRef imageSource = CGImageSourceCreateWithURL(url, NULL);
    if (!imageSource)
        return NULL;

    CGImageRef image = CGImageSourceCreateImageAtIndex(imageSource, 0, NULL);
    CFRelease(imageSource);

    return image;
}