From 262a23e09fbba4163058318d3bf528b8f2b6361d Mon Sep 17 00:00:00 2001 From: David Rose Date: Thu, 25 Aug 2011 06:01:48 +0000 Subject: [PATCH] twirling icon for mac too --- direct/src/plugin_npapi/ppInstance.cxx | 284 ++++++++++++++++++++++++- direct/src/plugin_npapi/ppInstance.h | 26 +++ 2 files changed, 299 insertions(+), 11 deletions(-) diff --git a/direct/src/plugin_npapi/ppInstance.cxx b/direct/src/plugin_npapi/ppInstance.cxx index 4affeb0583..a32f4e3731 100644 --- a/direct/src/plugin_npapi/ppInstance.cxx +++ b/direct/src/plugin_npapi/ppInstance.cxx @@ -34,7 +34,8 @@ #ifndef _WIN32 #include -#endif +#include +#endif // _WIN32 PPInstance::FileDatas PPInstance::_file_datas; @@ -101,6 +102,13 @@ PPInstance(NPMIMEType pluginType, NPP instance, uint16_t mode, _hwnd = 0; #endif // _WIN32 +#ifndef _WIN32 + // Save the startup time to improve precision of gettimeofday(). + struct timeval tv; + gettimeofday(&tv, (struct timezone *)NULL); + _init_sec = tv.tv_sec; +#endif // !_WIN32 + #ifdef __APPLE__ // Get the run loop in the browser thread. (CFRunLoopGetMain() is // only 10.5 or higher. Plus, the browser thread is not necessarily @@ -109,7 +117,20 @@ PPInstance(NPMIMEType pluginType, NPP instance, uint16_t mode, CFRetain(_run_loop_main); _request_timer = NULL; INIT_LOCK(_timer_lock); + + // Also set up a timer to twirl the icon until the instance loads. + _twirl_timer = NULL; + CFRunLoopTimerContext timer_context; + memset(&timer_context, 0, sizeof(timer_context)); + timer_context.info = this; + _twirl_timer = CFRunLoopTimerCreate + (NULL, 0, 0.1, 0, 0, st_twirl_timer_callback, &timer_context); + CFRunLoopAddTimer(_run_loop_main, _twirl_timer, kCFRunLoopCommonModes); #endif // __APPLE__ + +#ifdef MACOSX_HAS_EVENT_MODELS + _got_twirl_images = false; +#endif // MACOSX_HAS_EVENT_MODELS } //////////////////////////////////////////////////////////////////// @@ -122,6 +143,11 @@ PPInstance:: cleanup_window(); #ifdef __APPLE__ + if (_twirl_timer != NULL) { + CFRunLoopTimerInvalidate(_twirl_timer); + CFRelease(_twirl_timer); + _twirl_timer = NULL; + } if (_request_timer != NULL) { CFRunLoopTimerInvalidate(_request_timer); CFRelease(_request_timer); @@ -132,6 +158,10 @@ PPInstance:: DESTROY_LOCK(_timer_lock); #endif // __APPLE__ +#ifdef MACOSX_HAS_EVENT_MODELS + osx_release_twirl_images(); +#endif // MACOSX_HAS_EVENT_MODELS + if (_p3d_inst != NULL) { P3D_instance_finish_ptr(_p3d_inst); _p3d_inst = NULL; @@ -251,8 +281,8 @@ set_window(NPWindow *window) { assert(_window.window == window->window); } -#ifdef _WIN32 if (!_got_window) { +#ifdef _WIN32 _orig_window_proc = NULL; if (window->type == NPWindowTypeWindow) { // Save the window handle. @@ -275,8 +305,11 @@ set_window(NPWindow *window) { // slips through. SetTimer(_hwnd, 1, 100, NULL); } - } #endif // _WIN32 +#ifdef MACOSX_HAS_EVENT_MODELS + osx_get_twirl_images(); +#endif // MACOSX_HAS_EVENT_MODELS + } _window = *window; _got_window = true; @@ -810,11 +843,6 @@ bool PPInstance:: handle_event(void *event) { bool retval = false; - if (_p3d_inst == NULL) { - // Ignore events that come in before we've launched the instance. - return retval; - } - P3D_event_data edata; memset(&edata, 0, sizeof(edata)); edata._event_type = _event_type; @@ -831,12 +859,16 @@ handle_event(void *event) { NPCocoaEvent *np_event = (NPCocoaEvent *)event; P3DCocoaEvent *p3d_event = &edata._event._osx_cocoa._event; copy_cocoa_event(p3d_event, np_event, aux_data); + if (_got_window) { + handle_cocoa_event(p3d_event); + } #endif // MACOSX_HAS_EVENT_MODELS - } - if (P3D_instance_handle_event_ptr(_p3d_inst, &edata)) { - retval = true; + if (_p3d_inst != NULL) { + if (P3D_instance_handle_event_ptr(_p3d_inst, &edata)) { + retval = true; + } } return retval; @@ -1650,6 +1682,15 @@ create_instance() { return; } +#ifdef __APPLE__ + // We no longer need to twirl the icon. Stop the timer. + if (_twirl_timer != NULL) { + CFRunLoopTimerInvalidate(_twirl_timer); + CFRelease(_twirl_timer); + _twirl_timer = NULL; + } +#endif // __APPLE__ + P3D_token *tokens = NULL; if (!_tokens.empty()) { tokens = &_tokens[0]; @@ -2369,6 +2410,197 @@ make_ansi_string(wstring &result, NPNSString *ns_string) { } #endif // MACOSX_HAS_EVENT_MODELS +#ifdef MACOSX_HAS_EVENT_MODELS +//////////////////////////////////////////////////////////////////// +// Function: PPInstance::handle_cocoa_event +// Access: Private +// Description: Locally processes a Cocoa event for the window before +// sending it down to the Core API. This is used for +// drawing a twirling icon in the window while the Core +// API is downloading. +//////////////////////////////////////////////////////////////////// +void PPInstance:: +handle_cocoa_event(const P3DCocoaEvent *p3d_event) { + switch (p3d_event->type) { + case P3DCocoaEventDrawRect: + if (!_started) { + CGContextRef context = p3d_event->data.draw.context; + paint_twirl_osx_cgcontext(context); + } + break; + + default: + break; + } +} +#endif // MACOSX_HAS_EVENT_MODELS + +#ifdef MACOSX_HAS_EVENT_MODELS +//////////////////////////////////////////////////////////////////// +// Function: PPInstance::osx_get_twirl_images +// Access: Private +// Description: Fills _twirl_images with an array of images for +// drawing the twirling icon while we're waiting for the +// instance to load. +//////////////////////////////////////////////////////////////////// +void PPInstance:: +osx_get_twirl_images() { + if (_got_twirl_images) { + return; + } + _got_twirl_images = true; + + static const size_t twirl_size = twirl_width * twirl_height; + unsigned char twirl_data[twirl_size]; + + for (int step = 0; step < twirl_num_steps; ++step) { + get_twirl_data(twirl_data, twirl_size, step); + + unsigned char *new_data = new unsigned char[twirl_size * 4]; + + // Replicate out the grayscale channels into RGBA. Flip + // upside-down too. + for (int yi = 0; yi < twirl_height; ++yi) { + const unsigned char *sp = twirl_data + (twirl_height - 1 - yi) * twirl_width; + unsigned char *dp = new_data + yi * twirl_width * 4; + for (int xi = 0; xi < twirl_width; ++xi) { + dp[0] = sp[0]; + dp[1] = sp[0]; + dp[2] = sp[0]; + dp[3] = (unsigned char)0xff; + sp += 1; + dp += 4; + } + } + + OsxImageData &image = _twirl_images[step]; + image._raw_data = new_data; + + image._data = + CFDataCreateWithBytesNoCopy(NULL, (const UInt8 *)image._raw_data, + twirl_size * 4, kCFAllocatorNull); + image._provider = CGDataProviderCreateWithCFData(image._data); + image._color_space = CGColorSpaceCreateDeviceRGB(); + + image._image = + CGImageCreate(twirl_width, twirl_height, 8, 32, + twirl_width * 4, image._color_space, + kCGImageAlphaFirst | kCGBitmapByteOrder32Little, + image._provider, NULL, false, kCGRenderingIntentDefault); + } +} +#endif // MACOSX_HAS_EVENT_MODELS + +#ifdef MACOSX_HAS_EVENT_MODELS +//////////////////////////////////////////////////////////////////// +// Function: PPInstance::osx_release_twirl_images +// Access: Private +// Description: Frees the twirl_images array. +//////////////////////////////////////////////////////////////////// +void PPInstance:: +osx_release_twirl_images() { + if (!_got_twirl_images) { + return; + } + _got_twirl_images = false; + + for (int step = 0; step < twirl_num_steps; ++step) { + OsxImageData &image = _twirl_images[step]; + + if (image._image != NULL) { + CGImageRelease(image._image); + image._image = NULL; + } + if (image._color_space != NULL) { + CGColorSpaceRelease(image._color_space); + image._color_space = NULL; + } + if (image._provider != NULL) { + CGDataProviderRelease(image._provider); + image._provider = NULL; + } + if (image._data != NULL) { + CFRelease(image._data); + image._data = NULL; + } + if (image._raw_data != NULL) { + delete[] image._raw_data; + image._raw_data = NULL; + } + } +} +#endif // MACOSX_HAS_EVENT_MODELS + +#ifdef MACOSX_HAS_EVENT_MODELS +//////////////////////////////////////////////////////////////////// +// Function: PPInstance::paint_twirl_osx_cgcontext +// Access: Private +// Description: Actually paints the twirling icon in the OSX window, +// using Core Graphics. (We don't bother painting it in +// the older Cocoa interface.) +//////////////////////////////////////////////////////////////////// +void PPInstance:: +paint_twirl_osx_cgcontext(CGContextRef context) { + // Clear the whole region to white before beginning. + CGFloat bg_components[] = { 1, 1, 1, 1 }; + CGColorSpaceRef rgb_space = CGColorSpaceCreateDeviceRGB(); + CGColorRef bg = CGColorCreate(rgb_space, bg_components); + + CGRect region = { { 0, 0 }, { _window.width, _window.height } }; + CGContextBeginPath(context); + CGContextSetFillColorWithColor(context, bg); + CGContextAddRect(context, region); + CGContextFillPath(context); + + CGColorRelease(bg); + CGColorSpaceRelease(rgb_space); + + struct timeval tv; + gettimeofday(&tv, (struct timezone *)NULL); + double now = (double)(tv.tv_sec - _init_sec) + (double)tv.tv_usec / 1000000.0; + int step = ((int)(now * 10.0)) % twirl_num_steps; + + osx_paint_image(context, _twirl_images[step]); +} +#endif // MACOSX_HAS_EVENT_MODELS + +#ifdef MACOSX_HAS_EVENT_MODELS +//////////////////////////////////////////////////////////////////// +// Function: PPInstance::osx_paint_image +// Access: Private +// Description: Draws the indicated image, centered within the +// window. Returns true on success, false if the image +// is not defined. +//////////////////////////////////////////////////////////////////// +bool PPInstance:: +osx_paint_image(CGContextRef context, const OsxImageData &image) { + if (image._image == NULL) { + return false; + } + + // Determine the relative size of image and window. + int win_cx = _window.width / 2; + int win_cy = _window.height / 2; + + CGRect rect = { { 0, 0 }, { 0, 0 } }; + + // The bitmap fits within the window; center it. + + // This is the top-left corner of the bitmap in window coordinates. + int p_x = win_cx - twirl_width / 2; + int p_y = win_cy - twirl_height / 2; + + rect.origin.x += p_x; + rect.origin.y += p_y; + rect.size.width = twirl_width; + rect.size.height = twirl_height; + + CGContextDrawImage(context, rect, image._image); + + return true; +} +#endif // MACOSX_HAS_EVENT_MODELS + #ifdef __APPLE__ //////////////////////////////////////////////////////////////////// // Function: PPInstance::timer_callback @@ -2392,6 +2624,36 @@ timer_callback(CFRunLoopTimerRef timer, void *info) { } #endif // __APPLE__ +#ifdef __APPLE__ +//////////////////////////////////////////////////////////////////// +// Function: PPInstance::st_twirl_timer_callback +// Access: Private, Static +// Description: OSX only: this callback is used to twirl the icon +// before the instance loads. +//////////////////////////////////////////////////////////////////// +void PPInstance:: +st_twirl_timer_callback(CFRunLoopTimerRef timer, void *info) { + PPInstance *self = (PPInstance *)info; + self->twirl_timer_callback(); +} +#endif // __APPLE__ + +#ifdef __APPLE__ +//////////////////////////////////////////////////////////////////// +// Function: PPInstance::twirl_timer_callback +// Access: Private +// Description: OSX only: this callback is used to twirl the icon +// before the instance loads. +//////////////////////////////////////////////////////////////////// +void PPInstance:: +twirl_timer_callback() { + if (_got_window) { + NPRect rect = { 0, 0, (unsigned short)_window.height, (unsigned short)_window.width }; + browser->invalidaterect(_npp_instance, &rect); + } +} +#endif // __APPLE__ + //////////////////////////////////////////////////////////////////// // Function: PPInstance::StreamingFileData::Constructor // Access: Public diff --git a/direct/src/plugin_npapi/ppInstance.h b/direct/src/plugin_npapi/ppInstance.h index 04e3caf47d..3f362034ef 100644 --- a/direct/src/plugin_npapi/ppInstance.h +++ b/direct/src/plugin_npapi/ppInstance.h @@ -123,10 +123,18 @@ private: NPCocoaEvent *np_event, EventAuxData &aux_data); static const wchar_t *make_ansi_string(wstring &result, NPNSString *ns_string); + void handle_cocoa_event(const P3DCocoaEvent *p3d_event); + void osx_get_twirl_images(); + void osx_release_twirl_images(); + void paint_twirl_osx_cgcontext(CGContextRef context); + class OsxImageData; + bool osx_paint_image(CGContextRef context, const OsxImageData &image); #endif // MACOSX_HAS_EVENT_MODELS #ifdef __APPLE__ static void timer_callback(CFRunLoopTimerRef timer, void *info); + static void st_twirl_timer_callback(CFRunLoopTimerRef timer, void *info); + void twirl_timer_callback(); #endif // __APPLE__ private: @@ -213,8 +221,26 @@ private: CFRunLoopRef _run_loop_main; CFRunLoopTimerRef _request_timer; LOCK _timer_lock; + CFRunLoopTimerRef _twirl_timer; #endif // __APPLE__ +#ifdef MACOSX_HAS_EVENT_MODELS + class OsxImageData { + public: + unsigned char *_raw_data; + CFDataRef _data; + CGDataProviderRef _provider; + CGColorSpaceRef _color_space; + CGImageRef _image; + }; + OsxImageData _twirl_images[twirl_num_steps]; + bool _got_twirl_images; +#endif // MACOSX_HAS_EVENT_MODELS + +#ifndef _WIN32 + long _init_sec; +#endif // _WIN32 + bool _python_window_open; PPToplevelObject *_script_object;