1/* 2 File: MovieControllerLayer.m 3 4 Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple 5 Inc. ("Apple") in consideration of your agreement to the following 6 terms, and your use, installation, modification or redistribution of 7 this Apple software constitutes acceptance of these terms. If you do 8 not agree with these terms, please do not use, install, modify or 9 redistribute this Apple software. 10 11 In consideration of your agreement to abide by the following terms, and 12 subject to these terms, Apple grants you a personal, non-exclusive 13 license, under Apple's copyrights in this original Apple software (the 14 "Apple Software"), to use, reproduce, modify and redistribute the Apple 15 Software, with or without modifications, in source and/or binary forms; 16 provided that if you redistribute the Apple Software in its entirety and 17 without modifications, you must retain this notice and the following 18 text and disclaimers in all such redistributions of the Apple Software. 19 Neither the name, trademarks, service marks or logos of Apple Inc. may 20 be used to endorse or promote products derived from the Apple Software 21 without specific prior written permission from Apple. Except as 22 expressly stated in this notice, no other rights or licenses, express or 23 implied, are granted by Apple herein, including but not limited to any 24 patent rights that may be infringed by your derivative works or by other 25 works in which the Apple Software may be incorporated. 26 27 The Apple Software is provided by Apple on an "AS IS" basis. APPLE 28 MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION 29 THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS 30 FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND 31 OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. 32 33 IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL 34 OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 35 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 36 INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, 37 MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED 38 AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), 39 STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE 40 POSSIBILITY OF SUCH DAMAGE. 41 42 Copyright (C) 2009 Apple Inc. All Rights Reserved. 43 44 */ 45 46#import <WebKit/npapi.h> 47#import <WebKit/npfunctions.h> 48#import <WebKit/npruntime.h> 49 50#import <QuartzCore/QuartzCore.h> 51#import <QTKit/QTKit.h> 52 53#import "MovieControllerLayer.h" 54 55// Browser function table 56static NPNetscapeFuncs* browser; 57 58// Structure for per-instance storage 59typedef struct PluginObject 60{ 61 NPP npp; 62 63 NPWindow window; 64 65 CALayer *rootLayer; 66 MovieControllerLayer *controllerLayer; 67 QTMovieLayer *movieLayer; 68 69 CALayer *mouseDownLayer; 70 71 NSURL *movieURL; 72 QTMovie *movie; 73} PluginObject; 74 75NPError NPP_New(NPMIMEType pluginType, NPP instance, uint16 mode, int16 argc, char* argn[], char* argv[], NPSavedData* saved); 76NPError NPP_Destroy(NPP instance, NPSavedData** save); 77NPError NPP_SetWindow(NPP instance, NPWindow* window); 78NPError NPP_NewStream(NPP instance, NPMIMEType type, NPStream* stream, NPBool seekable, uint16* stype); 79NPError NPP_DestroyStream(NPP instance, NPStream* stream, NPReason reason); 80int32 NPP_WriteReady(NPP instance, NPStream* stream); 81int32 NPP_Write(NPP instance, NPStream* stream, int32 offset, int32 len, void* buffer); 82void NPP_StreamAsFile(NPP instance, NPStream* stream, const char* fname); 83void NPP_Print(NPP instance, NPPrint* platformPrint); 84int16 NPP_HandleEvent(NPP instance, void* event); 85void NPP_URLNotify(NPP instance, const char* URL, NPReason reason, void* notifyData); 86NPError NPP_GetValue(NPP instance, NPPVariable variable, void *value); 87NPError NPP_SetValue(NPP instance, NPNVariable variable, void *value); 88 89#pragma export on 90// Mach-o entry points 91NPError NP_Initialize(NPNetscapeFuncs *browserFuncs); 92NPError NP_GetEntryPoints(NPPluginFuncs *pluginFuncs); 93void NP_Shutdown(void); 94#pragma export off 95 96NPError NP_Initialize(NPNetscapeFuncs* browserFuncs) 97{ 98 browser = browserFuncs; 99 return NPERR_NO_ERROR; 100} 101 102NPError NP_GetEntryPoints(NPPluginFuncs* pluginFuncs) 103{ 104 pluginFuncs->version = 11; 105 pluginFuncs->size = sizeof(pluginFuncs); 106 pluginFuncs->newp = NPP_New; 107 pluginFuncs->destroy = NPP_Destroy; 108 pluginFuncs->setwindow = NPP_SetWindow; 109 pluginFuncs->newstream = NPP_NewStream; 110 pluginFuncs->destroystream = NPP_DestroyStream; 111 pluginFuncs->asfile = NPP_StreamAsFile; 112 pluginFuncs->writeready = NPP_WriteReady; 113 pluginFuncs->write = (NPP_WriteProcPtr)NPP_Write; 114 pluginFuncs->print = NPP_Print; 115 pluginFuncs->event = NPP_HandleEvent; 116 pluginFuncs->urlnotify = NPP_URLNotify; 117 pluginFuncs->getvalue = NPP_GetValue; 118 pluginFuncs->setvalue = NPP_SetValue; 119 120 return NPERR_NO_ERROR; 121} 122 123void NP_Shutdown(void) 124{ 125 126} 127 128NPError NPP_New(NPMIMEType pluginType, NPP instance, uint16 mode, int16 argc, char* argn[], char* argv[], NPSavedData* saved) 129{ 130 // Create per-instance storage 131 PluginObject *obj = (PluginObject *)malloc(sizeof(PluginObject)); 132 bzero(obj, sizeof(PluginObject)); 133 134 obj->npp = instance; 135 instance->pdata = obj; 136 137 // Ask the browser if it supports the Core Animation drawing model 138 NPBool supportsCoreAnimation; 139 if (browser->getvalue(instance, NPNVsupportsCoreAnimationBool, &supportsCoreAnimation) != NPERR_NO_ERROR) 140 supportsCoreAnimation = FALSE; 141 142 if (!supportsCoreAnimation) 143 return NPERR_INCOMPATIBLE_VERSION_ERROR; 144 145 // If the browser supports the Core Animation drawing model, enable it. 146 browser->setvalue(instance, NPPVpluginDrawingModel, (void *)NPDrawingModelCoreAnimation); 147 148 // If the browser supports the Cocoa event model, enable it. 149 NPBool supportsCocoa; 150 if (browser->getvalue(instance, NPNVsupportsCocoaBool, &supportsCocoa) != NPERR_NO_ERROR) 151 supportsCocoa = FALSE; 152 153 if (!supportsCocoa) 154 return NPERR_INCOMPATIBLE_VERSION_ERROR; 155 156 browser->setvalue(instance, NPPVpluginEventModel, (void *)NPEventModelCocoa); 157 158 for (int16 i = 0; i < argc; i++) { 159 if (strcasecmp(argn[i], "movieurl") == 0) { 160 NSString *urlString = [NSString stringWithUTF8String:argv[i]]; 161 if (urlString) 162 obj->movieURL = [[NSURL URLWithString:urlString] retain]; 163 break; 164 } 165 166 } 167 168 return NPERR_NO_ERROR; 169} 170 171NPError NPP_Destroy(NPP instance, NPSavedData** save) 172{ 173 // Free per-instance storage 174 PluginObject *obj = instance->pdata; 175 176 [obj->movie stop]; 177 [obj->rootLayer release]; 178 179 free(obj); 180 181 return NPERR_NO_ERROR; 182} 183 184NPError NPP_SetWindow(NPP instance, NPWindow* window) 185{ 186 PluginObject *obj = instance->pdata; 187 obj->window = *window; 188 189 return NPERR_NO_ERROR; 190} 191 192 193NPError NPP_NewStream(NPP instance, NPMIMEType type, NPStream* stream, NPBool seekable, uint16* stype) 194{ 195 *stype = NP_ASFILEONLY; 196 return NPERR_NO_ERROR; 197} 198 199NPError NPP_DestroyStream(NPP instance, NPStream* stream, NPReason reason) 200{ 201 return NPERR_NO_ERROR; 202} 203 204int32 NPP_WriteReady(NPP instance, NPStream* stream) 205{ 206 return 0; 207} 208 209int32 NPP_Write(NPP instance, NPStream* stream, int32 offset, int32 len, void* buffer) 210{ 211 return 0; 212} 213 214void NPP_StreamAsFile(NPP instance, NPStream* stream, const char* fname) 215{ 216} 217 218void NPP_Print(NPP instance, NPPrint* platformPrint) 219{ 220 221} 222 223static void handleMouseDown(PluginObject *obj, NPCocoaEvent *event) 224{ 225 CGPoint point = CGPointMake(event->data.mouse.pluginX, 226 // Flip the y coordinate 227 obj->window.height - event->data.mouse.pluginY); 228 229 obj->mouseDownLayer = [obj->rootLayer hitTest:point]; 230 if (obj->mouseDownLayer == obj->controllerLayer) { 231 [obj->controllerLayer handleMouseDown:[obj->rootLayer convertPoint:point toLayer:obj->controllerLayer]]; 232 return; 233 } 234} 235 236static void togglePlayPause(PluginObject *obj) 237{ 238 if (!obj->movie) 239 return; 240 241 if ([obj->movie rate] == 0) 242 [obj->movie play]; 243 else 244 [obj->movie stop]; 245 246} 247 248static void handleMouseUp(PluginObject *obj, NPCocoaEvent *event) 249{ 250 CGPoint point = CGPointMake(event->data.mouse.pluginX, 251 // Flip the y coordinate 252 obj->window.height - event->data.mouse.pluginY); 253 254 CALayer *mouseDownLayer = obj->mouseDownLayer; 255 obj->mouseDownLayer = nil; 256 if (mouseDownLayer == obj->controllerLayer) { 257 [obj->controllerLayer handleMouseUp:[obj->rootLayer convertPoint:point toLayer:obj->controllerLayer]]; 258 return; 259 } 260} 261 262static void handleMouseDragged(PluginObject *obj, NPCocoaEvent *event) 263{ 264 CGPoint point = CGPointMake(event->data.mouse.pluginX, 265 // Flip the y coordinate 266 obj->window.height - event->data.mouse.pluginY); 267 268 if (obj->mouseDownLayer == obj->controllerLayer) { 269 [obj->controllerLayer handleMouseDragged:[obj->rootLayer convertPoint:point toLayer:obj->controllerLayer]]; 270 return; 271 } 272} 273 274static void handleMouseEntered(PluginObject *obj) 275{ 276 // Show the controller layer. 277 obj->controllerLayer.opacity = 1.0; 278} 279 280static void handleMouseExited(PluginObject *obj) 281{ 282 // Hide the controller layer if the movie is playing. 283 if ([obj->movie rate]) 284 obj->controllerLayer.opacity = 0.0; 285} 286 287static int handleKeyDown(PluginObject *obj, NPCocoaEvent *event) 288{ 289 NSString *characters = (NSString *)event->data.key.characters; 290 291 if ([characters length] == 1 && [characters characterAtIndex:0] == ' ') { 292 togglePlayPause(obj); 293 return 1; 294 } 295 296 return 0; 297} 298 299 300static int handleScrollEvent(PluginObject *obj, NPCocoaEvent *event) 301{ 302 double delta = event->data.mouse.deltaY; 303 if (delta < 0) 304 [obj->movie stepForward]; 305 else 306 [obj->movie stepBackward]; 307 return 0; 308} 309 310 311 312int16 NPP_HandleEvent(NPP instance, void* event) 313{ 314 PluginObject *obj = instance->pdata; 315 316 NPCocoaEvent *cocoaEvent = event; 317 318 switch(cocoaEvent->type) { 319 case NPCocoaEventMouseDown: 320 handleMouseDown(obj, cocoaEvent); 321 return 1; 322 case NPCocoaEventMouseUp: 323 handleMouseUp(obj, cocoaEvent); 324 return 1; 325 case NPCocoaEventMouseDragged: 326 handleMouseDragged(obj, cocoaEvent); 327 return 1; 328 case NPCocoaEventMouseEntered: 329 handleMouseEntered(obj); 330 return 1; 331 case NPCocoaEventMouseExited: 332 handleMouseExited(obj); 333 return 1; 334 case NPCocoaEventKeyDown: 335 return handleKeyDown(obj, cocoaEvent); 336 case NPCocoaEventScrollWheel: 337 return handleScrollEvent(obj, cocoaEvent); 338 } 339 340 return 0; 341} 342 343void NPP_URLNotify(NPP instance, const char* url, NPReason reason, void* notifyData) 344{ 345 346} 347 348NPError NPP_GetValue(NPP instance, NPPVariable variable, void *value) 349{ 350 PluginObject *obj = instance->pdata; 351 352 switch (variable) { 353 case NPPVpluginCoreAnimationLayer: 354 if (!obj->rootLayer) { 355 // Setup layer hierarchy. 356 obj->rootLayer = [[CALayer layer] retain]; 357 358 obj->movieLayer = [QTMovieLayer layer]; 359 obj->movieLayer.autoresizingMask = kCALayerWidthSizable | kCALayerHeightSizable; 360 [obj->rootLayer addSublayer:obj->movieLayer]; 361 362 obj->controllerLayer = [MovieControllerLayer layer]; 363 [obj->rootLayer addSublayer:obj->controllerLayer]; 364 365 if (obj->movieURL) { 366 NSDictionary *attributes = [NSDictionary dictionaryWithObjectsAndKeys:obj->movieURL, QTMovieURLAttribute, 367 [NSNumber numberWithBool:YES], QTMovieOpenForPlaybackAttribute, 368 [NSNumber numberWithBool:YES], QTMovieLoopsAttribute, 369 nil]; 370 obj->movie = [QTMovie movieWithAttributes:attributes error:nil]; 371 372 if (obj->movie) { 373 obj->movieLayer.movie = obj->movie; 374 [obj->controllerLayer setMovie:obj->movie]; 375 } 376 } 377 378 } 379 380 // Make sure to return a retained layer 381 *((CALayer **)value) = [obj->rootLayer retain]; 382 383 return NPERR_NO_ERROR; 384 385 default: 386 return NPERR_GENERIC_ERROR; 387 } 388} 389 390NPError NPP_SetValue(NPP instance, NPNVariable variable, void *value) 391{ 392 return NPERR_GENERIC_ERROR; 393} 394