| /* |
| File: MovieControllerLayer.m |
| |
| Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple |
| Inc. ("Apple") in consideration of your agreement to the following |
| terms, and your use, installation, modification or redistribution of |
| this Apple software constitutes acceptance of these terms. If you do |
| not agree with these terms, please do not use, install, modify or |
| redistribute this Apple software. |
| |
| In consideration of your agreement to abide by the following terms, and |
| subject to these terms, Apple grants you a personal, non-exclusive |
| license, under Apple's copyrights in this original Apple software (the |
| "Apple Software"), to use, reproduce, modify and redistribute the Apple |
| Software, with or without modifications, in source and/or binary forms; |
| provided that if you redistribute the Apple Software in its entirety and |
| without modifications, you must retain this notice and the following |
| text and disclaimers in all such redistributions of the Apple Software. |
| Neither the name, trademarks, service marks or logos of Apple Inc. may |
| be used to endorse or promote products derived from the Apple Software |
| without specific prior written permission from Apple. Except as |
| expressly stated in this notice, no other rights or licenses, express or |
| implied, are granted by Apple herein, including but not limited to any |
| patent rights that may be infringed by your derivative works or by other |
| works in which the Apple Software may be incorporated. |
| |
| The Apple Software is provided by Apple on an "AS IS" basis. APPLE |
| MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION |
| THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS |
| FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND |
| OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. |
| |
| IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL |
| OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, |
| MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED |
| AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), |
| STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE |
| POSSIBILITY OF SUCH DAMAGE. |
| |
| Copyright (C) 2009 Apple Inc. All Rights Reserved. |
| |
| */ |
| |
| #import <WebKit/npapi.h> |
| #import <WebKit/npfunctions.h> |
| #import <WebKit/npruntime.h> |
| |
| #import <QuartzCore/QuartzCore.h> |
| #import <QTKit/QTKit.h> |
| |
| #import "MovieControllerLayer.h" |
| |
| // Browser function table |
| static NPNetscapeFuncs* browser; |
| |
| // Structure for per-instance storage |
| typedef struct PluginObject |
| { |
| NPP npp; |
| |
| NPWindow window; |
| |
| CALayer *rootLayer; |
| MovieControllerLayer *controllerLayer; |
| QTMovieLayer *movieLayer; |
| |
| CALayer *mouseDownLayer; |
| |
| NSURL *movieURL; |
| QTMovie *movie; |
| } PluginObject; |
| |
| NPError NPP_New(NPMIMEType pluginType, NPP instance, uint16_t mode, int16_t argc, char* argn[], char* argv[], NPSavedData* saved); |
| NPError NPP_Destroy(NPP instance, NPSavedData** save); |
| NPError NPP_SetWindow(NPP instance, NPWindow* window); |
| NPError NPP_NewStream(NPP instance, NPMIMEType type, NPStream* stream, NPBool seekable, uint16* stype); |
| NPError NPP_DestroyStream(NPP instance, NPStream* stream, NPReason reason); |
| int32_t NPP_WriteReady(NPP instance, NPStream* stream); |
| int32_t NPP_Write(NPP instance, NPStream* stream, int32_t offset, int32_t len, void* buffer); |
| void NPP_StreamAsFile(NPP instance, NPStream* stream, const char* fname); |
| void NPP_Print(NPP instance, NPPrint* platformPrint); |
| int16_t NPP_HandleEvent(NPP instance, void* event); |
| void NPP_URLNotify(NPP instance, const char* URL, NPReason reason, void* notifyData); |
| NPError NPP_GetValue(NPP instance, NPPVariable variable, void *value); |
| NPError NPP_SetValue(NPP instance, NPNVariable variable, void *value); |
| |
| #pragma export on |
| // Mach-o entry points |
| NPError NP_Initialize(NPNetscapeFuncs *browserFuncs); |
| NPError NP_GetEntryPoints(NPPluginFuncs *pluginFuncs); |
| void NP_Shutdown(void); |
| #pragma export off |
| |
| NPError NP_Initialize(NPNetscapeFuncs* browserFuncs) |
| { |
| browser = browserFuncs; |
| return NPERR_NO_ERROR; |
| } |
| |
| NPError NP_GetEntryPoints(NPPluginFuncs* pluginFuncs) |
| { |
| pluginFuncs->version = 11; |
| pluginFuncs->size = sizeof(pluginFuncs); |
| pluginFuncs->newp = NPP_New; |
| pluginFuncs->destroy = NPP_Destroy; |
| pluginFuncs->setwindow = NPP_SetWindow; |
| pluginFuncs->newstream = NPP_NewStream; |
| pluginFuncs->destroystream = NPP_DestroyStream; |
| pluginFuncs->asfile = NPP_StreamAsFile; |
| pluginFuncs->writeready = NPP_WriteReady; |
| pluginFuncs->write = (NPP_WriteProcPtr)NPP_Write; |
| pluginFuncs->print = NPP_Print; |
| pluginFuncs->event = NPP_HandleEvent; |
| pluginFuncs->urlnotify = NPP_URLNotify; |
| pluginFuncs->getvalue = NPP_GetValue; |
| pluginFuncs->setvalue = NPP_SetValue; |
| |
| return NPERR_NO_ERROR; |
| } |
| |
| void NP_Shutdown(void) |
| { |
| |
| } |
| |
| NPError NPP_New(NPMIMEType pluginType, NPP instance, uint16_t mode, int16_t argc, char* argn[], char* argv[], NPSavedData* saved) |
| { |
| // Create per-instance storage |
| PluginObject *obj = (PluginObject *)malloc(sizeof(PluginObject)); |
| bzero(obj, sizeof(PluginObject)); |
| |
| obj->npp = instance; |
| instance->pdata = obj; |
| |
| // Ask the browser if it supports the Core Animation drawing model |
| NPBool supportsCoreAnimation; |
| if (browser->getvalue(instance, NPNVsupportsCoreAnimationBool, &supportsCoreAnimation) != NPERR_NO_ERROR) |
| supportsCoreAnimation = FALSE; |
| |
| if (!supportsCoreAnimation) |
| return NPERR_INCOMPATIBLE_VERSION_ERROR; |
| |
| // If the browser supports the Core Animation drawing model, enable it. |
| browser->setvalue(instance, NPPVpluginDrawingModel, (void *)NPDrawingModelCoreAnimation); |
| |
| // If the browser supports the Cocoa event model, enable it. |
| NPBool supportsCocoa; |
| if (browser->getvalue(instance, NPNVsupportsCocoaBool, &supportsCocoa) != NPERR_NO_ERROR) |
| supportsCocoa = FALSE; |
| |
| if (!supportsCocoa) |
| return NPERR_INCOMPATIBLE_VERSION_ERROR; |
| |
| browser->setvalue(instance, NPPVpluginEventModel, (void *)NPEventModelCocoa); |
| |
| for (int16_t i = 0; i < argc; i++) { |
| if (strcasecmp(argn[i], "movieurl") == 0) { |
| NSString *urlString = [NSString stringWithUTF8String:argv[i]]; |
| if (urlString) |
| obj->movieURL = [[NSURL URLWithString:urlString] retain]; |
| break; |
| } |
| |
| } |
| |
| return NPERR_NO_ERROR; |
| } |
| |
| NPError NPP_Destroy(NPP instance, NPSavedData** save) |
| { |
| // Free per-instance storage |
| PluginObject *obj = instance->pdata; |
| |
| [obj->movie stop]; |
| [obj->rootLayer release]; |
| |
| free(obj); |
| |
| return NPERR_NO_ERROR; |
| } |
| |
| NPError NPP_SetWindow(NPP instance, NPWindow* window) |
| { |
| PluginObject *obj = instance->pdata; |
| obj->window = *window; |
| |
| return NPERR_NO_ERROR; |
| } |
| |
| |
| NPError NPP_NewStream(NPP instance, NPMIMEType type, NPStream* stream, NPBool seekable, uint16* stype) |
| { |
| *stype = NP_ASFILEONLY; |
| return NPERR_NO_ERROR; |
| } |
| |
| NPError NPP_DestroyStream(NPP instance, NPStream* stream, NPReason reason) |
| { |
| return NPERR_NO_ERROR; |
| } |
| |
| int32_t NPP_WriteReady(NPP instance, NPStream* stream) |
| { |
| return 0; |
| } |
| |
| int32_t NPP_Write(NPP instance, NPStream* stream, int32_t offset, int32_t len, void* buffer) |
| { |
| return 0; |
| } |
| |
| void NPP_StreamAsFile(NPP instance, NPStream* stream, const char* fname) |
| { |
| } |
| |
| void NPP_Print(NPP instance, NPPrint* platformPrint) |
| { |
| |
| } |
| |
| static void handleMouseDown(PluginObject *obj, NPCocoaEvent *event) |
| { |
| CGPoint point = CGPointMake(event->data.mouse.pluginX, |
| // Flip the y coordinate |
| obj->window.height - event->data.mouse.pluginY); |
| |
| obj->mouseDownLayer = [obj->rootLayer hitTest:point]; |
| if (obj->mouseDownLayer == obj->controllerLayer) { |
| [obj->controllerLayer handleMouseDown:[obj->rootLayer convertPoint:point toLayer:obj->controllerLayer]]; |
| return; |
| } |
| } |
| |
| static void togglePlayPause(PluginObject *obj) |
| { |
| if (!obj->movie) |
| return; |
| |
| if ([obj->movie rate] == 0) |
| [obj->movie play]; |
| else |
| [obj->movie stop]; |
| |
| } |
| |
| static void handleMouseUp(PluginObject *obj, NPCocoaEvent *event) |
| { |
| CGPoint point = CGPointMake(event->data.mouse.pluginX, |
| // Flip the y coordinate |
| obj->window.height - event->data.mouse.pluginY); |
| |
| CALayer *mouseDownLayer = obj->mouseDownLayer; |
| obj->mouseDownLayer = nil; |
| if (mouseDownLayer == obj->controllerLayer) { |
| [obj->controllerLayer handleMouseUp:[obj->rootLayer convertPoint:point toLayer:obj->controllerLayer]]; |
| return; |
| } |
| } |
| |
| static void handleMouseDragged(PluginObject *obj, NPCocoaEvent *event) |
| { |
| CGPoint point = CGPointMake(event->data.mouse.pluginX, |
| // Flip the y coordinate |
| obj->window.height - event->data.mouse.pluginY); |
| |
| if (obj->mouseDownLayer == obj->controllerLayer) { |
| [obj->controllerLayer handleMouseDragged:[obj->rootLayer convertPoint:point toLayer:obj->controllerLayer]]; |
| return; |
| } |
| } |
| |
| static void handleMouseEntered(PluginObject *obj) |
| { |
| // Show the controller layer. |
| obj->controllerLayer.opacity = 1.0; |
| } |
| |
| static void handleMouseExited(PluginObject *obj) |
| { |
| // Hide the controller layer if the movie is playing. |
| if ([obj->movie rate]) |
| obj->controllerLayer.opacity = 0.0; |
| } |
| |
| static int handleKeyDown(PluginObject *obj, NPCocoaEvent *event) |
| { |
| NSString *characters = (NSString *)event->data.key.characters; |
| |
| if ([characters length] == 1 && [characters characterAtIndex:0] == ' ') { |
| togglePlayPause(obj); |
| return 1; |
| } |
| |
| return 0; |
| } |
| |
| |
| static int handleScrollEvent(PluginObject *obj, NPCocoaEvent *event) |
| { |
| double delta = event->data.mouse.deltaY; |
| if (delta < 0) |
| [obj->movie stepForward]; |
| else |
| [obj->movie stepBackward]; |
| return 0; |
| } |
| |
| |
| |
| int16_t NPP_HandleEvent(NPP instance, void* event) |
| { |
| PluginObject *obj = instance->pdata; |
| |
| NPCocoaEvent *cocoaEvent = event; |
| |
| switch(cocoaEvent->type) { |
| case NPCocoaEventMouseDown: |
| handleMouseDown(obj, cocoaEvent); |
| return 1; |
| case NPCocoaEventMouseUp: |
| handleMouseUp(obj, cocoaEvent); |
| return 1; |
| case NPCocoaEventMouseDragged: |
| handleMouseDragged(obj, cocoaEvent); |
| return 1; |
| case NPCocoaEventMouseEntered: |
| handleMouseEntered(obj); |
| return 1; |
| case NPCocoaEventMouseExited: |
| handleMouseExited(obj); |
| return 1; |
| case NPCocoaEventKeyDown: |
| return handleKeyDown(obj, cocoaEvent); |
| case NPCocoaEventScrollWheel: |
| return handleScrollEvent(obj, cocoaEvent); |
| } |
| |
| return 0; |
| } |
| |
| void NPP_URLNotify(NPP instance, const char* url, NPReason reason, void* notifyData) |
| { |
| |
| } |
| |
| NPError NPP_GetValue(NPP instance, NPPVariable variable, void *value) |
| { |
| PluginObject *obj = instance->pdata; |
| |
| switch (variable) { |
| case NPPVpluginCoreAnimationLayer: |
| if (!obj->rootLayer) { |
| // Setup layer hierarchy. |
| obj->rootLayer = [[CALayer layer] retain]; |
| |
| obj->movieLayer = [QTMovieLayer layer]; |
| obj->movieLayer.autoresizingMask = kCALayerWidthSizable | kCALayerHeightSizable; |
| [obj->rootLayer addSublayer:obj->movieLayer]; |
| |
| obj->controllerLayer = [MovieControllerLayer layer]; |
| [obj->rootLayer addSublayer:obj->controllerLayer]; |
| |
| if (obj->movieURL) { |
| NSDictionary *attributes = [NSDictionary dictionaryWithObjectsAndKeys:obj->movieURL, QTMovieURLAttribute, |
| [NSNumber numberWithBool:YES], QTMovieOpenForPlaybackAttribute, |
| [NSNumber numberWithBool:YES], QTMovieLoopsAttribute, |
| nil]; |
| obj->movie = [QTMovie movieWithAttributes:attributes error:nil]; |
| |
| if (obj->movie) { |
| obj->movieLayer.movie = obj->movie; |
| [obj->controllerLayer setMovie:obj->movie]; |
| } |
| } |
| |
| } |
| |
| // Make sure to return a retained layer |
| *((CALayer **)value) = [obj->rootLayer retain]; |
| |
| return NPERR_NO_ERROR; |
| |
| default: |
| return NPERR_GENERIC_ERROR; |
| } |
| } |
| |
| NPError NPP_SetValue(NPP instance, NPNVariable variable, void *value) |
| { |
| return NPERR_GENERIC_ERROR; |
| } |