Browse Source

finally committing that blooming snow leopard support

David Rose 16 years ago
parent
commit
dd007746b5

+ 357 - 139
direct/src/plugin/p3dInstance.cxx

@@ -33,6 +33,20 @@
 #ifdef __APPLE__
 #include <sys/mman.h>
 #include <ApplicationServices/ApplicationServices.h>
+
+// Lifted from NSEvent.h (which is Objective-C).
+enum {
+   NSAlphaShiftKeyMask = 1 << 16,
+   NSShiftKeyMask      = 1 << 17,
+   NSControlKeyMask    = 1 << 18,
+   NSAlternateKeyMask  = 1 << 19,
+   NSCommandKeyMask    = 1 << 20,
+   NSNumericPadKeyMask = 1 << 21,
+   NSHelpKeyMask       = 1 << 22,
+   NSFunctionKeyMask   = 1 << 23,
+   NSDeviceIndependentModifierFlagsMask = 0xffff0000U
+};
+
 #endif  // __APPLE__
 
 #ifdef _WIN32
@@ -63,6 +77,15 @@ const char *P3DInstance::_image_type_names[P3DInstance::IT_num_image_types] = {
   "none",  // Not really used.
 };
 
+static void
+write_str(ostream &out, const wchar_t *str) {
+  const wchar_t *p = str;
+  while (*p != 0) {
+    out << (int)*p << ' ';
+    ++p;
+  }
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: P3DInstance::Constructor
 //       Access: Public
@@ -137,6 +160,7 @@ P3DInstance(P3D_request_ready_func *func,
   // We have to start with _mouse_active true; firefox doesn't send
   // activate events.
   _mouse_active = true;
+  _modifiers = 0;
   _frame_timer = NULL;
 #endif  // __APPLE__
 
@@ -833,88 +857,15 @@ handle_event(const P3D_event_data &event) {
   }
 
 #if defined(__APPLE__)
-  assert(event._event_type == P3D_ET_osx_event_record);
-  EventRecord *er = event._event._osx_event_record._event;
-
-  // Need to ensure we have the correct port set, in order to
-  // convert the mouse coordinates successfully via
-  // GlobalToLocal().
-  const P3D_window_handle &handle = _wparams.get_parent_window();
-  assert(handle._window_handle_type == P3D_WHT_osx_port);
-  GrafPtr out_port = handle._handle._osx_port._port;
-  GrafPtr port_save = NULL;
-  Boolean port_changed = QDSwapPort(out_port, &port_save);
-  
-  Point pt = er->where;
-  GlobalToLocal(&pt);
-
-  if (port_changed) {
-    QDSwapPort(port_save, NULL);
-  }
-
-  SubprocessWindowBuffer::Event swb_event;
-  swb_event._source = SubprocessWindowBuffer::ES_none;
-  swb_event._type = SubprocessWindowBuffer::ET_none;
-  swb_event._code = 0;
-  swb_event._flags = 0;
-  add_modifier_flags(swb_event._flags, er->modifiers);
-
-  switch (er->what) {
-  case mouseDown:
-  case mouseUp:
-    {
-      P3D_window_handle window = _wparams.get_parent_window();
-      swb_event._source = SubprocessWindowBuffer::ES_mouse;
-      if (er->what == mouseUp) {
-        swb_event._type = SubprocessWindowBuffer::ET_button_up;
-      } else {
-        swb_event._type = SubprocessWindowBuffer::ET_button_down;
-      }
-      retval = true;
-    }
-    break;
-
-  case keyDown:
-  case keyUp:
-  case autoKey:
-    if (_swbuffer != NULL) {
-      swb_event._source = SubprocessWindowBuffer::ES_keyboard;
-      swb_event._code = er->message;
-      if (er->what == keyUp) {
-        swb_event._type = SubprocessWindowBuffer::ET_button_up;
-      } else if (er->what == keyDown) {
-        swb_event._type = SubprocessWindowBuffer::ET_button_down;
-      } else {
-        swb_event._type = SubprocessWindowBuffer::ET_button_again;
-      }
-      retval = true;
-    }
-    break;
-
-  case updateEvt:
-    paint_window();
-    retval = true;
-    break;
-
-  case activateEvt:
-    _mouse_active = ((er->modifiers & 1) != 0);
-    break;
-
-  default:
-    break;
-  }
-
-  if (_mouse_active) {
-    swb_event._x = pt.h;
-    swb_event._y = pt.v;
-    swb_event._flags |= SubprocessWindowBuffer::EF_mouse_position | SubprocessWindowBuffer::EF_has_mouse;
-  }
-
-  if (_swbuffer != NULL) {
-    _swbuffer->add_event(swb_event);
+  if (event._event_type == P3D_ET_osx_event_record) {
+    retval = handle_event_osx_event_record(event);
+  } else if (event._event_type == P3D_ET_osx_cocoa) {
+    retval = handle_event_osx_cocoa(event);
+  } else {
+    assert(false);
   }
+#endif  // __APPLE__
 
-#endif
   return retval;
 }
 
@@ -3018,28 +2969,47 @@ set_install_label(const string &install_label) {
 //     Function: P3DInstance::paint_window
 //       Access: Private
 //  Description: Actually paints the rendered image to the browser
-//               window.  This is only implemented (and needed) for
-//               OSX, where the child process isn't allowed to do it
-//               directly.
+//               window.  This is only needed for OSX, where the child
+//               process isn't allowed to do it directly.
 ////////////////////////////////////////////////////////////////////
 void P3DInstance::
 paint_window() {
+  const P3D_window_handle &handle = _wparams.get_parent_window();
+  if (handle._window_handle_type == P3D_WHT_osx_port) {
+    paint_window_osx_port();
+
+  } else if (handle._window_handle_type == P3D_WHT_osx_cgcontext) {
+    const P3D_window_handle &handle = _wparams.get_parent_window();
+    assert(handle._window_handle_type == P3D_WHT_osx_cgcontext);
+    CGContextRef context = handle._handle._osx_cgcontext._context;
+
+    paint_window_osx_cgcontext(context);
+  }
+}
+
 #ifdef __APPLE__
+////////////////////////////////////////////////////////////////////
+//     Function: P3DInstance::get_framebuffer
+//       Access: Private
+//  Description: Fills _reversed_buffer with the pixels from the
+//               current frame.  Returns true on success, or false if
+//               there is no Panda3D window visible.  Only needed on
+//               OSX.
+////////////////////////////////////////////////////////////////////
+bool P3DInstance::
+get_framebuffer() {
   if (_swbuffer == NULL || !_instance_window_opened) {
     // We don't have a Panda3D window yet.
-    return;
+    return false;
   }
   if (_splash_window != NULL && _splash_window->get_visible()) {
     // If the splash window is up, don't draw the Panda3D window.
-    return;
+    return false;
   }
 
-  QDErr err;
-
   // blit rendered framebuffer into window backing store
   int x_size = min(_wparams.get_win_width(), _swbuffer->get_x_size());
   int y_size = min(_wparams.get_win_height(), _swbuffer->get_y_size());
-
   size_t rowsize = _swbuffer->get_row_size();
 
   if (_swbuffer->ready_for_read()) {
@@ -3089,61 +3059,34 @@ paint_window() {
     // time.
   }
 
-  /*
-  // This is an attempt to paint the frame using the less-deprecated
-  // Quartz interfaces.  Sure does seem like a lot of layers to go
-  // through just to paint a bitmap.
-  CFDataRef data =
-    CFDataCreateWithBytesNoCopy(NULL, (const UInt8 *)_reversed_buffer, 
-                                y_size * rowsize, kCFAllocatorNull);
-
-  CGDataProviderRef provider = CGDataProviderCreateWithCFData(data);
-  CGColorSpaceRef color_space = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
-
-  CGImageRef image =
-    CGImageCreate(x_size, y_size, 8, 32, rowsize, color_space,
-                  kCGImageAlphaFirst | kCGBitmapByteOrder32Little, provider,
-                  NULL, false, kCGRenderingIntentDefault);
+  return true;
+}
+#endif  // __APPLE__
 
-  CGrafPtr port = _wparams.get_parent_window()._port;
-  CGContextRef context;
-  err = QDBeginCGContext(port, &context);
-  if (err != noErr) {
-    nout << "Error: QDBeginCGContext\n";
+#ifdef __APPLE__
+////////////////////////////////////////////////////////////////////
+//     Function: P3DInstance::paint_window_osx_port
+//       Access: Private
+//  Description: Actually paints the rendered image to the browser
+//               window, using the OSX deprecated QuickDraw
+//               interfaces.
+////////////////////////////////////////////////////////////////////
+void P3DInstance::
+paint_window_osx_port() {
+  if (!get_framebuffer()) {
+    // No Panda3D window is showing.
     return;
   }
 
-  //  CGContextTranslateCTM(context, 0.0, win_height);
-  //  CGContextScaleCTM(context, 1.0, -1.0);
-
-  // We have to rely on the clipping rectangle having been set up
-  // correctly in order to get the proper location to draw the image.
-  // This isn't completely right, because if the image is slightly
-  // offscreen, the top left of the clipping rectangle will no longer
-  // correspond to the top left of the original image.
-  CGRect rect = CGContextGetClipBoundingBox(context);
-  nout << "rect: " << rect.origin.x << " " << rect.origin.y
-       << " " << rect.size.width << " " << rect.size.height << "\n";
-  rect.size.width = x_size;
-  rect.size.height = y_size;
-
-  CGContextDrawImage(context, rect, image);
-  
-  //CGContextSynchronize(context);
-  CGContextFlush(context);
-  QDEndCGContext(port, &context);
-
-  CGImageRelease(image);
-  CGColorSpaceRelease(color_space);
-  CGDataProviderRelease(provider);
-
-  CFRelease(data);
-  */
+  int x_size = min(_wparams.get_win_width(), _swbuffer->get_x_size());
+  int y_size = min(_wparams.get_win_height(), _swbuffer->get_y_size());
+  size_t rowsize = _swbuffer->get_row_size();
 
-  // Painting the frame using the deprecated QuickDraw interfaces.
   Rect src_rect = {0, 0, y_size, x_size};
   Rect ddrc_rect = {0, 0, y_size, x_size};
 
+  QDErr err;
+
   GWorldPtr pGWorld;
   err = NewGWorldFromPtr(&pGWorld, k32BGRAPixelFormat, &src_rect, 0, 0, 0, 
                          _reversed_buffer, rowsize);
@@ -3172,18 +3115,269 @@ paint_window() {
   }
   
   DisposeGWorld(pGWorld);
+}
 #endif  // __APPLE__  
+
+#ifdef __APPLE__
+////////////////////////////////////////////////////////////////////
+//     Function: P3DInstance::paint_window_osx_cgcontext
+//       Access: Private
+//  Description: Actually paints the rendered image to the browser
+//               window.  This is the newer CoreGraphics
+//               implementation on OSX.
+////////////////////////////////////////////////////////////////////
+void P3DInstance::
+paint_window_osx_cgcontext(CGContextRef context) {
+  if (!get_framebuffer()) {
+    // No Panda3D window is showing.
+    return;
+  }
+
+  int x_size = min(_wparams.get_win_width(), _swbuffer->get_x_size());
+  int y_size = min(_wparams.get_win_height(), _swbuffer->get_y_size());
+  size_t rowsize = _swbuffer->get_row_size();
+
+  CGContextTranslateCTM(context, 0, y_size);
+  CGContextScaleCTM(context, 1.0, -1.0);
+
+  CFDataRef data =
+    CFDataCreateWithBytesNoCopy(NULL, (const UInt8 *)_reversed_buffer, 
+                                y_size * rowsize, kCFAllocatorNull);
+
+  CGDataProviderRef provider = CGDataProviderCreateWithCFData(data);
+  //CGColorSpaceRef color_space = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
+  CGColorSpaceRef color_space = CGColorSpaceCreateDeviceRGB();
+
+  CGImageRef image =
+    CGImageCreate(x_size, y_size, 8, 32, rowsize, color_space,
+                  kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Little, 
+                  provider, NULL, false, kCGRenderingIntentDefault);
+
+  CGRect region = { { 0, 0 }, { x_size, y_size } };
+  CGContextDrawImage(context, region, image);
+
+  CGImageRelease(image);
+  CGColorSpaceRelease(color_space);
+  CGDataProviderRelease(provider);
+
+  CFRelease(data);
 }
+#endif  // __APPLE__
 
 ////////////////////////////////////////////////////////////////////
-//     Function: P3DInstance::add_modifier_flags
+//     Function: P3DInstance::handle_event_osx_event_record
+//       Access: Private
+//  Description: Responds to the deprecated Carbon event types in Mac
+//               OSX.
+////////////////////////////////////////////////////////////////////
+bool P3DInstance::
+handle_event_osx_event_record(const P3D_event_data &event) {
+  bool retval = false;
+
+#ifdef __APPLE__
+  assert(event._event_type == P3D_ET_osx_event_record);
+  EventRecord *er = event._event._osx_event_record._event;
+
+  Point pt = er->where;
+
+  // Need to ensure we have the correct port set, in order to
+  // convert the mouse coordinates successfully via
+  // GlobalToLocal().
+  const P3D_window_handle &handle = _wparams.get_parent_window();
+  if (handle._window_handle_type == P3D_WHT_osx_port) {
+    GrafPtr out_port = handle._handle._osx_port._port;
+    GrafPtr port_save = NULL;
+    Boolean port_changed = QDSwapPort(out_port, &port_save);
+    
+    GlobalToLocal(&pt);
+    
+    if (port_changed) {
+      QDSwapPort(port_save, NULL);
+    }
+  } else {
+    // First, convert the coordinates from screen coordinates to
+    // browser window coordinates.
+    WindowRef window = handle._handle._osx_cgcontext._window;
+    CGPoint cgpt = { pt.h, pt.v };
+    HIPointConvert(&cgpt, kHICoordSpaceScreenPixel, NULL,
+                   kHICoordSpaceWindow, window);
+
+    // Then convert to plugin coordinates.
+    pt.h = cgpt.x - _wparams.get_win_x();
+    pt.v = cgpt.y - _wparams.get_win_y();
+  }
+
+  SubprocessWindowBuffer::Event swb_event;
+  swb_event._source = SubprocessWindowBuffer::ES_none;
+  swb_event._type = SubprocessWindowBuffer::ET_none;
+  swb_event._code = 0;
+  swb_event._flags = 0;
+  add_carbon_modifier_flags(swb_event._flags, er->modifiers);
+
+  switch (er->what) {
+  case mouseDown:
+    swb_event._source = SubprocessWindowBuffer::ES_mouse;
+    swb_event._type = SubprocessWindowBuffer::ET_button_down;
+    retval = true;
+    break;
+
+  case mouseUp:
+    swb_event._source = SubprocessWindowBuffer::ES_mouse;
+    swb_event._type = SubprocessWindowBuffer::ET_button_up;
+    retval = true;
+    break;
+
+  case keyDown:
+  case keyUp:
+  case autoKey:
+    if (_swbuffer != NULL) {
+      swb_event._source = SubprocessWindowBuffer::ES_keyboard;
+      swb_event._code = er->message;
+      if (er->what == keyUp) {
+        swb_event._type = SubprocessWindowBuffer::ET_button_up;
+      } else if (er->what == keyDown) {
+        swb_event._type = SubprocessWindowBuffer::ET_button_down;
+      } else {
+        swb_event._type = SubprocessWindowBuffer::ET_button_again;
+      }
+      retval = true;
+    }
+    break;
+
+  case updateEvt:
+    paint_window();
+    retval = true;
+    break;
+
+  case activateEvt:
+    _mouse_active = ((er->modifiers & 1) != 0);
+    break;
+
+  default:
+    break;
+  }
+
+  if (_mouse_active) {
+    swb_event._x = pt.h;
+    swb_event._y = pt.v;
+    swb_event._flags |= SubprocessWindowBuffer::EF_mouse_position | SubprocessWindowBuffer::EF_has_mouse;
+  }
+
+  if (_swbuffer != NULL) {
+    _swbuffer->add_event(swb_event);
+  }
+#endif  // __APPLE__
+
+  return retval;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: P3DInstance::handle_event_osx_cocoa
+//       Access: Private
+//  Description: Responds to the new Cocoa event types in Mac
+//               OSX.
+////////////////////////////////////////////////////////////////////
+bool P3DInstance::
+handle_event_osx_cocoa(const P3D_event_data &event) {
+  bool retval = false;
+
+#ifdef __APPLE__
+  assert(event._event_type == P3D_ET_osx_cocoa);
+  const P3DCocoaEvent &ce = event._event._osx_cocoa._event;
+
+  SubprocessWindowBuffer::Event swb_event;
+  swb_event._source = SubprocessWindowBuffer::ES_none;
+  swb_event._type = SubprocessWindowBuffer::ET_none;
+  swb_event._code = 0;
+  swb_event._flags = 0;
+
+  switch (ce.type) {
+  case P3DCocoaEventDrawRect:
+    {
+      CGContextRef context = ce.data.draw.context;
+      paint_window_osx_cgcontext(context);
+      retval = true;
+    }
+    break;
+
+  case P3DCocoaEventMouseDown:
+    swb_event._source = SubprocessWindowBuffer::ES_mouse;
+    swb_event._type = SubprocessWindowBuffer::ET_button_down;
+    retval = true;
+    break;
+
+  case P3DCocoaEventMouseUp:
+    swb_event._source = SubprocessWindowBuffer::ES_mouse;
+    swb_event._type = SubprocessWindowBuffer::ET_button_up;
+    retval = true;
+    break;
+
+  case P3DCocoaEventKeyDown:
+    swb_event._source = SubprocessWindowBuffer::ES_keyboard;
+    swb_event._code = ce.data.key.keyCode << 8;
+    if (ce.data.key.isARepeat) {
+      swb_event._type = SubprocessWindowBuffer::ET_button_again;
+    } else {
+      swb_event._type = SubprocessWindowBuffer::ET_button_down;
+      if (ce.data.key.characters[0] > 0 & ce.data.key.characters[0] < 0x100) {
+        swb_event._code |= ce.data.key.characters[0];
+      }
+    }
+    _modifiers = ce.data.key.modifierFlags;
+    retval = true;
+    break;
+
+  case P3DCocoaEventKeyUp:
+    swb_event._source = SubprocessWindowBuffer::ES_keyboard;
+    swb_event._type = SubprocessWindowBuffer::ET_button_up;
+    swb_event._code = ce.data.key.keyCode << 8;
+    _modifiers = ce.data.key.modifierFlags;
+    retval = true;
+    break;
+
+  case P3DCocoaEventFlagsChanged:
+    _modifiers = ce.data.key.modifierFlags;
+    retval = true;
+    break;
+
+  case P3DCocoaEventFocusChanged:
+    _mouse_active = (ce.data.focus.hasFocus != 0);
+    retval = true;
+    break;
+  }
+
+  add_cocoa_modifier_flags(swb_event._flags, _modifiers);
+
+  switch (ce.type) {
+  case P3DCocoaEventMouseDown:
+  case P3DCocoaEventMouseMoved:
+  case P3DCocoaEventMouseDragged:
+    swb_event._x = ce.data.mouse.pluginX;
+    swb_event._y = ce.data.mouse.pluginY;
+    swb_event._flags |= SubprocessWindowBuffer::EF_mouse_position;
+  }
+
+  if (_mouse_active) {
+    swb_event._flags |= SubprocessWindowBuffer::EF_has_mouse;
+  }
+
+  if (_swbuffer != NULL) {
+    _swbuffer->add_event(swb_event);
+  }
+#endif  // __APPLE__
+
+  return retval;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: P3DInstance::add_carbon_modifier_flags
 //       Access: Private
 //  Description: OSX only: adds the appropriate bits to the Event flag
 //               bitmask to correspond to the modifier buttons held in
 //               the MacOS-style EventRecord::modifiers mask.
 ////////////////////////////////////////////////////////////////////
 void P3DInstance::
-add_modifier_flags(unsigned int &swb_flags, int modifiers) {
+add_carbon_modifier_flags(unsigned int &swb_flags, int modifiers) {
 #ifdef __APPLE__
   if (modifiers & cmdKey) {
     swb_flags |= SubprocessWindowBuffer::EF_meta_held;
@@ -3200,6 +3394,31 @@ add_modifier_flags(unsigned int &swb_flags, int modifiers) {
 #endif  // __APPLE__
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: P3DInstance::add_cocoa_modifier_flags
+//       Access: Private
+//  Description: OSX only: adds the appropriate bits to the Event flag
+//               bitmask to correspond to the modifier buttons held in
+//               the P3DCocoaEvent modifierFlags mask.
+////////////////////////////////////////////////////////////////////
+void P3DInstance::
+add_cocoa_modifier_flags(unsigned int &swb_flags, int modifiers) {
+#ifdef __APPLE__
+  if (modifiers & NSCommandKeyMask) {
+    swb_flags |= SubprocessWindowBuffer::EF_meta_held;
+  }
+  if (modifiers & NSShiftKeyMask) {
+    swb_flags |= SubprocessWindowBuffer::EF_shift_held;
+  }
+  if (modifiers & NSAlternateKeyMask) {
+    swb_flags |= SubprocessWindowBuffer::EF_alt_held;
+  }
+  if (modifiers & NSControlKeyMask) {
+    swb_flags |= SubprocessWindowBuffer::EF_control_held;
+  }
+#endif  // __APPLE__
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: P3DInstance::send_notify
 //       Access: Private
@@ -3284,7 +3503,7 @@ parse_hexdigit(int &result, char digit) {
 #ifdef __APPLE__
 ////////////////////////////////////////////////////////////////////
 //     Function: P3DInstance::timer_callback
-//       Access: Private
+//       Access: Private, Static
 //  Description: OSX only: this callback is associated with a
 //               CFRunLoopTimer, to be called periodically for
 //               updating the frame.
@@ -3293,7 +3512,6 @@ void P3DInstance::
 timer_callback(CFRunLoopTimerRef timer, void *info) {
   P3DInstance *self = (P3DInstance *)info;
   self->request_refresh();
-  //self->paint_window();
 }
 #endif  // __APPLE__
 

+ 12 - 1
direct/src/plugin/p3dInstance.h

@@ -200,7 +200,17 @@ private:
   void set_install_label(const string &install_label);
 
   void paint_window();
-  void add_modifier_flags(unsigned int &swb_flags, int modifiers);
+
+#ifdef __APPLE__
+  bool get_framebuffer();
+  void paint_window_osx_port();
+  void paint_window_osx_cgcontext(CGContextRef context);
+#endif  //  __APPLE__
+
+  bool handle_event_osx_event_record(const P3D_event_data &event);
+  bool handle_event_osx_cocoa(const P3D_event_data &event);
+  void add_carbon_modifier_flags(unsigned int &swb_flags, int modifiers);
+  void add_cocoa_modifier_flags(unsigned int &swb_flags, int modifiers);
 
   void send_notify(const string &message);
 
@@ -286,6 +296,7 @@ private:
   SubprocessWindowBuffer *_swbuffer;
   char *_reversed_buffer;
   bool _mouse_active;
+  unsigned int _modifiers;
 
   CFRunLoopTimerRef _frame_timer;
 #endif  // __APPLE__

+ 178 - 73
direct/src/plugin/p3dOsxSplashWindow.cxx

@@ -220,51 +220,17 @@ set_install_progress(double install_progress,
 ////////////////////////////////////////////////////////////////////
 bool P3DOsxSplashWindow::
 handle_event(const P3D_event_data &event) {
-  assert(event._event_type == P3D_ET_osx_event_record);
-  EventRecord *er = event._event._osx_event_record._event;
-
-  // Need to ensure we have the correct port set, in order to
-  // convert the mouse coordinates successfully via
-  // GlobalToLocal().
-  const P3D_window_handle &handle = _wparams.get_parent_window();
-  assert(handle._window_handle_type == P3D_WHT_osx_port);
-  GrafPtr out_port = handle._handle._osx_port._port;
-  GrafPtr port_save = NULL;
-  Boolean port_changed = QDSwapPort(out_port, &port_save);
-  
-  Point pt = er->where;
-  GlobalToLocal(&pt);
-
-  if (port_changed) {
-    QDSwapPort(port_save, NULL);
-  }
-  
-  switch (er->what) {
-  case updateEvt:
-    paint_window();
-    break;
-
-  case mouseDown:
-    set_mouse_data(_mouse_x, _mouse_y, true);
-    break;
-
-  case mouseUp:
-    set_mouse_data(_mouse_x, _mouse_y, false);
-    break;
-
-  case activateEvt:
-    _mouse_active = ((er->modifiers & 1) != 0);
-    break;
-
-  default:
-    break;
-  }
+  bool retval = false;
 
-  if (_mouse_active) {
-    set_mouse_data(pt.h, pt.v, _mouse_down);
+  if (event._event_type == P3D_ET_osx_event_record) {
+    retval = handle_event_osx_event_record(event);
+  } else if (event._event_type == P3D_ET_osx_cocoa) {
+    retval = handle_event_osx_cocoa(event);
+  } else {
+    assert(false);
   }
 
-  return false;
+  return retval;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -297,34 +263,65 @@ paint_window() {
     return;
   }
 
-  GrafPtr out_port = NULL;
-  if (_toplevel_window != NULL) {
-    GetPort(&out_port);
+  if (_toplevel_window != NULL || 
+      _wparams.get_parent_window()._window_handle_type == P3D_WHT_osx_port) {
+
+    // The old QuickDraw-style window handle.  We use
+    // CreateCGContextForPort() to map this to the new
+    // CoreGraphics-style.
+    GrafPtr out_port = NULL;
+    if (_toplevel_window != NULL) {
+      GetPort(&out_port);
+      
+    } else {
+      const P3D_window_handle &handle = _wparams.get_parent_window();
+      assert(handle._window_handle_type == P3D_WHT_osx_port);
+      out_port = handle._handle._osx_port._port;
+    }
+    
+    CGContextRef context;
+    OSStatus err = CreateCGContextForPort(out_port, &context);
+    if (err != noErr) {
+      nout << "Couldn't create CG context\n";
+      return;
+    }
+    
+    //  Adjust for any SetOrigin calls on out_port
+    SyncCGContextOriginWithPort(context, out_port);
+    
+    //  Move the CG origin to the upper left of the port
+    Rect port_rect;
+    GetPortBounds(out_port, &port_rect);
+    CGContextTranslateCTM(context, 0, (float)(port_rect.bottom - port_rect.top));
+    
+    //  Flip the y axis so that positive Y points down
+    CGContextScaleCTM(context, 1.0, -1.0);
+    
+    paint_window_osx_cgcontext(context);
 
+    // CGContextSynchronize(context);
+    CGContextRelease(context);
+    
   } else {
+    // The new CoreGraphics-style window handle.  We can draw to this
+    // directly.
+
     const P3D_window_handle &handle = _wparams.get_parent_window();
-    assert(handle._window_handle_type == P3D_WHT_osx_port);
-    out_port = handle._handle._osx_port._port;
-  }
+    assert(handle._window_handle_type == P3D_WHT_osx_cgcontext);
+    CGContextRef context = handle._handle._osx_cgcontext._context;
 
-  CGContextRef context;
-  OSStatus err = CreateCGContextForPort(out_port, &context);
-  if (err != noErr) {
-    nout << "Couldn't create CG context\n";
-    return;
+    paint_window_osx_cgcontext(context);
   }
+}
 
-  //  Adjust for any SetOrigin calls on out_port
-  SyncCGContextOriginWithPort(context, out_port);
-  
-  //  Move the CG origin to the upper left of the port
-  Rect port_rect;
-  GetPortBounds(out_port, &port_rect);
-  CGContextTranslateCTM(context, 0, (float)(port_rect.bottom - port_rect.top));
-  
-  //  Flip the y axis so that positive Y points down
-  CGContextScaleCTM(context, 1.0, -1.0);
-
+////////////////////////////////////////////////////////////////////
+//     Function: P3DOsxSplashWindow::paint_window_osx_cgcontext
+//       Access: Private
+//  Description: Redraws the current splash window, using the new
+//               CoreGraphics interface.
+////////////////////////////////////////////////////////////////////
+void P3DOsxSplashWindow::
+paint_window_osx_cgcontext(CGContextRef context) {
   // Clear the whole region to the background color before beginning.
   CGFloat bg_components[] = { _bgcolor_r / 255.0f, _bgcolor_g / 255.0f, _bgcolor_b / 255.0f, 1 };
   CGColorSpaceRef rgb_space = CGColorSpaceCreateDeviceRGB();
@@ -364,9 +361,122 @@ paint_window() {
   if (!_progress_known || _install_progress != 0.0) {
     paint_progress_bar(context);
   }
+}
 
-  CGContextSynchronize(context);
-  CGContextRelease(context);
+////////////////////////////////////////////////////////////////////
+//     Function: P3DOsxSplashWindow::handle_event_osx_event_record
+//       Access: Private
+//  Description: Responds to the deprecated Carbon event types in Mac
+//               OSX.
+////////////////////////////////////////////////////////////////////
+bool P3DOsxSplashWindow::
+handle_event_osx_event_record(const P3D_event_data &event) {
+  assert(event._event_type == P3D_ET_osx_event_record);
+  EventRecord *er = event._event._osx_event_record._event;
+
+  Point pt = er->where;
+
+  // Need to ensure we have the correct port set, in order to
+  // convert the mouse coordinates successfully via
+  // GlobalToLocal().
+  const P3D_window_handle &handle = _wparams.get_parent_window();
+  if (handle._window_handle_type == P3D_WHT_osx_port) {
+    GrafPtr out_port = handle._handle._osx_port._port;
+    GrafPtr port_save = NULL;
+    Boolean port_changed = QDSwapPort(out_port, &port_save);
+  
+    GlobalToLocal(&pt);
+
+    if (port_changed) {
+      QDSwapPort(port_save, NULL);
+    }
+
+  } else if (handle._window_handle_type == P3D_WHT_osx_cgcontext) {
+    // First, convert the coordinates from screen coordinates to
+    // browser window coordinates.
+    WindowRef window = handle._handle._osx_cgcontext._window;
+    CGPoint cgpt = { pt.h, pt.v };
+    HIPointConvert(&cgpt, kHICoordSpaceScreenPixel, NULL,
+                   kHICoordSpaceWindow, window);
+
+    // Then convert to plugin coordinates.
+    pt.h = cgpt.x - _wparams.get_win_x();
+    pt.v = cgpt.y - _wparams.get_win_y();
+  }
+  
+  switch (er->what) {
+  case updateEvt:
+    paint_window();
+    break;
+
+  case mouseDown:
+    set_mouse_data(_mouse_x, _mouse_y, true);
+    break;
+
+  case mouseUp:
+    set_mouse_data(_mouse_x, _mouse_y, false);
+    break;
+
+  case activateEvt:
+    _mouse_active = ((er->modifiers & 1) != 0);
+    break;
+
+  default:
+    break;
+  }
+
+  if (_mouse_active) {
+    set_mouse_data(pt.h, pt.v, _mouse_down);
+  }
+
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: P3DOsxSplashWindow::handle_event_osx_cocoa
+//       Access: Private
+//  Description: Responds to the new Cocoa event types in Mac
+//               OSX.
+////////////////////////////////////////////////////////////////////
+bool P3DOsxSplashWindow::
+handle_event_osx_cocoa(const P3D_event_data &event) {
+  bool retval = false;
+
+  assert(event._event_type == P3D_ET_osx_cocoa);
+  const P3DCocoaEvent &ce = event._event._osx_cocoa._event;
+
+  switch (ce.type) {
+  case P3DCocoaEventDrawRect:
+    if (_visible) {
+      CGContextRef context = ce.data.draw.context;
+      paint_window_osx_cgcontext(context);
+      retval = true;
+    }
+    break;
+
+  case P3DCocoaEventMouseDown:
+    set_mouse_data(ce.data.mouse.pluginX, ce.data.mouse.pluginY, true);
+    retval = true;
+    break;
+
+  case P3DCocoaEventMouseUp:
+    set_mouse_data(ce.data.mouse.pluginX, ce.data.mouse.pluginY, false);
+    retval = true;
+    break;
+
+  case P3DCocoaEventMouseMoved:
+  case P3DCocoaEventMouseDragged:
+    set_mouse_data(ce.data.mouse.pluginX, ce.data.mouse.pluginY, _mouse_down);
+    retval = true;
+    break;
+
+  case P3DCocoaEventFocusChanged:
+    _mouse_active = (ce.data.focus.hasFocus != 0);
+    retval = true;
+    break;
+  }
+
+  return retval;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -681,13 +791,8 @@ event_callback(EventHandlerCallRef my_handler, EventRef event) {
                           sizeof(Point), NULL, (void *)&point);
 
         GrafPtr port;
-        if (_toplevel_window != NULL) {
-          port = GetWindowPort(_toplevel_window);
-        } else {
-          const P3D_window_handle &handle = _wparams.get_parent_window();
-          assert(handle._window_handle_type == P3D_WHT_osx_port);
-          port = handle._handle._osx_port._port;
-        }
+        assert(_toplevel_window != NULL);
+        port = GetWindowPort(_toplevel_window);
         GrafPtr port_save = NULL;
         Boolean port_changed = QDSwapPort(port, &port_save);
         GlobalToLocal(&point);

+ 3 - 0
direct/src/plugin/p3dOsxSplashWindow.h

@@ -48,6 +48,9 @@ protected:
 
 private:
   void paint_window();
+  void paint_window_osx_cgcontext(CGContextRef context);
+  bool handle_event_osx_event_record(const P3D_event_data &event);
+  bool handle_event_osx_cocoa(const P3D_event_data &event);
   class OsxImageData;
 
   void load_image(OsxImageData &image, const string &image_filename);

+ 74 - 0
direct/src/plugin/p3d_plugin.h

@@ -204,6 +204,7 @@ typedef enum {
   P3D_WHT_win_hwnd,
   P3D_WHT_osx_port,
   P3D_WHT_x11_window,
+  P3D_WHT_osx_cgcontext,
 } P3D_window_handle_type;
 
 typedef struct {
@@ -220,6 +221,15 @@ typedef struct {
 #endif
 } P3D_window_handle_osx_port;
 
+  /* A CoreGraphics handle, as used by NPAPI with
+     NPDrawingModelCoreGraphics in effect. */
+typedef struct {
+#if defined(__APPLE__)
+  CGContextRef _context;
+  WindowRef _window;
+#endif
+} P3D_window_handle_osx_cgcontext;
+
 typedef struct {
 #if defined(HAVE_X11)
   unsigned long _xwindow;
@@ -233,6 +243,7 @@ typedef struct {
     P3D_window_handle_win_hwnd _win_hwnd;
     P3D_window_handle_osx_port _osx_port;
     P3D_window_handle_x11_window _x11_window;
+    P3D_window_handle_osx_cgcontext _osx_cgcontext;
   } _handle;
 } P3D_window_handle;
 
@@ -913,6 +924,7 @@ P3D_instance_feed_url_stream_func(P3D_instance *instance, int unique_id,
 typedef enum {
   P3D_ET_none = 0,
   P3D_ET_osx_event_record,
+  P3D_ET_osx_cocoa,
 } P3D_event_type;
 
 typedef struct {
@@ -921,10 +933,72 @@ typedef struct {
 #endif
 } P3D_event_osx_event_record;
 
+// This enum reimplements NPCocoaEventTyped, used below.
+typedef enum {
+  P3DCocoaEventDrawRect = 1,
+  P3DCocoaEventMouseDown,
+  P3DCocoaEventMouseUp,
+  P3DCocoaEventMouseMoved,
+  P3DCocoaEventMouseEntered,
+  P3DCocoaEventMouseExited,
+  P3DCocoaEventMouseDragged,
+  P3DCocoaEventKeyDown,
+  P3DCocoaEventKeyUp,
+  P3DCocoaEventFlagsChanged,
+  P3DCocoaEventFocusChanged,
+  P3DCocoaEventWindowFocusChanged,
+  P3DCocoaEventScrollWheel,
+  P3DCocoaEventTextInput
+} P3DCocoaEventType;
+
+// This structure reimplements NPCocoaEvent, to pass the complex
+// cocoa event structures as generated by NPAPI.
+typedef struct {
+  P3DCocoaEventType type;
+  unsigned int version;
+  union {
+    struct {
+      unsigned int modifierFlags;
+      double pluginX;
+      double pluginY;            
+      int buttonNumber;
+      int clickCount;
+      double deltaX;
+      double deltaY;
+      double deltaZ;
+    } mouse;
+    struct {
+      unsigned int modifierFlags;
+      const wchar_t *characters;
+      const wchar_t *charactersIgnoringModifiers;
+      bool isARepeat;
+      unsigned short keyCode;
+    } key;
+    struct {
+      CGContextRef context;
+      double x;
+      double y;
+      double width;
+      double height;
+    } draw;
+    struct {
+      bool hasFocus;
+    } focus;
+    struct {
+      const wchar_t *text;
+    } text;
+  } data;
+} P3DCocoaEvent;
+
+typedef struct {
+  P3DCocoaEvent _event;
+} P3D_event_osx_cocoa;
+
 typedef struct {
   P3D_event_type _event_type;
   union {
     P3D_event_osx_event_record _osx_event_record;
+    P3D_event_osx_cocoa _osx_cocoa;
   } _event;
 } P3D_event_data;
 

+ 12 - 0
direct/src/plugin_npapi/nppanda3d_common.h

@@ -107,6 +107,18 @@ struct UC_NPString {
 #define HAS_PLUGIN_THREAD_ASYNC_CALL 1
 #endif
 
+// We also need to know whether we have Apple's new Cocoa-based
+// drawing and event callbacks.
+#if defined(NPVERS_MACOSX_HAS_EVENT_MODELS) && NP_VERSION_MINOR >= NPVERS_MACOSX_HAS_EVENT_MODELS
+#define MACOSX_HAS_EVENT_MODELS 1
+#endif
+
+// No one defined a symbol for the introduction of the Cocoa drawing,
+// but it appears to have been version 19.
+#if NP_VERSION_MINOR >= 19
+#define MACOSX_HAS_COREGRAPHICS_DRAWING_MODEL 1
+#endif
+
 // Appears in startup.cxx.
 extern NPNetscapeFuncs *browser;
 

+ 239 - 29
direct/src/plugin_npapi/ppInstance.cxx

@@ -48,11 +48,15 @@ PPInstance::FileDatas PPInstance::_file_datas;
 ////////////////////////////////////////////////////////////////////
 PPInstance::
 PPInstance(NPMIMEType pluginType, NPP instance, uint16_t mode, 
-           int16_t argc, char *argn[], char *argv[], NPSavedData *saved) {
+           int16_t argc, char *argn[], char *argv[], NPSavedData *saved,
+           P3D_window_handle_type window_handle_type,
+           P3D_event_type event_type) {
   _p3d_inst = NULL;
 
   _npp_instance = instance;
   _npp_mode = mode;
+  _window_handle_type = window_handle_type;
+  _event_type = event_type;
   _script_object = NULL;
   _failed = false;
   _started = false;
@@ -79,6 +83,10 @@ PPInstance(NPMIMEType pluginType, NPP instance, uint16_t mode,
   _got_instance_url = false;
   _got_window = false;
   _python_window_open = false;
+
+#ifdef __APPLE__
+  _request_timer = NULL;
+#endif  // __APPLE__
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -90,6 +98,14 @@ PPInstance::
 ~PPInstance() {
   cleanup_window();
 
+#ifdef __APPLE__
+    if (_request_timer != NULL) {
+      CFRunLoopTimerInvalidate(_request_timer);
+      CFRelease(_request_timer);
+      _request_timer = NULL;
+    }
+#endif  // __APPLE__
+
   if (_p3d_inst != NULL) {
     P3D_instance_finish(_p3d_inst);
     _p3d_inst = NULL;
@@ -124,7 +140,8 @@ PPInstance::
 void PPInstance::
 begin() {
   // On Windows and Linux, we must insist on having this call.  OSX
-  // doesn't necessarily require it.
+  // doesn't necessarily require it (which is lucky, since it appears
+  // that Safari doesn't necessarily provide it!)
 #ifndef __APPLE__
   if (!has_plugin_thread_async_call) {
     nout << "Browser version insufficient: we require at least NPAPI version 0.19.\n";
@@ -677,17 +694,30 @@ handle_event(void *event) {
     return retval;
   }
 
-#ifdef __APPLE__
   P3D_event_data edata;
   memset(&edata, 0, sizeof(edata));
-  edata._event_type = P3D_ET_osx_event_record;
-  edata._event._osx_event_record._event = (EventRecord *)event;
+  edata._event_type = _event_type;
+  EventAuxData aux_data;
+  if (_event_type == P3D_ET_osx_event_record) {
+#ifdef __APPLE__
+    edata._event._osx_event_record._event = (EventRecord *)event;
+#endif  // __APPLE__
+
+#ifdef MACOSX_HAS_EVENT_MODELS
+  } else if (_event_type == P3D_ET_osx_cocoa) {
+    // Copy the NPCocoaEvent structure componentwise into a
+    // P3DCocoaEvent structure.
+    NPCocoaEvent *np_event = (NPCocoaEvent *)event;
+    P3DCocoaEvent *p3d_event = &edata._event._osx_cocoa._event;
+    copy_cocoa_event(p3d_event, np_event, aux_data);
+#endif  // MACOSX_HAS_EVENT_MODELS
+
+  }
+
   if (P3D_instance_handle_event(_p3d_inst, &edata)) {
     retval = true;
   }
 
-#endif  // __APPLE__
-
   return retval;
 }
 
@@ -962,14 +992,18 @@ request_ready(P3D_instance *instance) {
   PPInstance *inst = (PPInstance *)(instance->_user_data);
   assert(inst != NULL);
 
+  {
+    static int n = 0;
+    nout << "request_ready " << ++n << "\n";
+  }
+
   if (has_plugin_thread_async_call) {
 #ifdef HAS_PLUGIN_THREAD_ASYNC_CALL
     // Since we are running at least Gecko 1.9, and we have this very
     // useful function, let's use it to ask the browser to call us back
     // in the main thread.
-    if ((void *)browser->pluginthreadasynccall != (void *)NULL) {
-      browser->pluginthreadasynccall(inst->_npp_instance, browser_sync_callback, NULL);
-    }
+    assert((void *)browser->pluginthreadasynccall != (void *)NULL);
+    browser->pluginthreadasynccall(inst->_npp_instance, browser_sync_callback, NULL);
 #endif  // HAS_PLUGIN_THREAD_ASYNC_CALL
 
   } else {
@@ -986,8 +1020,28 @@ request_ready(P3D_instance *instance) {
       PostMessage((HWND)(win->window), WM_USER, 0, 0);
     }
 #endif  // _WIN32
-    // On Mac and Linux, we ignore this asynchronous event, and rely on
-    // detecting it within HandleEvent() and similar callbacks.
+
+#ifdef __APPLE__
+    // Use an OSX timer to forward this event to the main thread.
+
+    // Stop any previously-started timer--we don't need more than one.
+    if (inst->_request_timer != NULL) {
+      CFRunLoopTimerInvalidate(inst->_request_timer);
+      CFRelease(inst->_request_timer);
+      inst->_request_timer = NULL;
+    }
+
+    // And start a new one.
+    CFRunLoopTimerContext timer_context;
+    memset(&timer_context, 0, sizeof(timer_context));
+    timer_context.info = inst;
+    inst->_request_timer = CFRunLoopTimerCreate
+      (NULL, 0, 0, 0, 0, timer_callback, &timer_context);
+    CFRunLoopRef run_loop = CFRunLoopGetMain();
+    CFRunLoopAddTimer(run_loop, inst->_request_timer, kCFRunLoopCommonModes);
+#endif  // __APPLE__
+
+    // Doesn't appear to be a reliable way to simulate this in Linux.
   }
 }
 
@@ -1380,14 +1434,17 @@ send_window() {
     y = 0;
 
 #elif defined(__APPLE__)
-    NP_Port *port = (NP_Port *)_window.window;
-    parent_window._window_handle_type = P3D_WHT_osx_port;
-    parent_window._handle._osx_port._port = port->port;
-    /*
-    NP_CGContext *context = (NP_CGContext *)_window.window;
-    parent_window._context = context->context;
-    parent_window._window = (WindowRef)context->window;
-    */
+    parent_window._window_handle_type = _window_handle_type;
+    if (_window_handle_type == P3D_WHT_osx_port) {
+      NP_Port *port = (NP_Port *)_window.window;
+      parent_window._handle._osx_port._port = port->port;
+    } else if (_window_handle_type == P3D_WHT_osx_cgcontext) {
+      NP_CGContext *context = (NP_CGContext *)_window.window;
+      if (context != NULL) {
+        parent_window._handle._osx_cgcontext._context = context->context;
+        parent_window._handle._osx_cgcontext._window = (WindowRef)context->window;
+      }
+    }
 
 #elif defined(HAVE_X11)
     // We make it an 'unsigned long' instead of 'Window'
@@ -1410,14 +1467,17 @@ send_window() {
     }
 
 #elif defined(__APPLE__)
-    NP_Port *port = (NP_Port *)_window.window;
-    parent_window._window_handle_type = P3D_WHT_osx_port;
-    parent_window._handle._osx_port._port = port->port;
-    /*
-    NP_CGContext *context = (NP_CGContext *)_window.window;
-    parent_window._context = context->context;
-    parent_window._window = (WindowRef)context->window;
-    */
+    parent_window._window_handle_type = _window_handle_type;
+    if (_window_handle_type == P3D_WHT_osx_port) {
+      NP_Port *port = (NP_Port *)_window.window;
+      parent_window._handle._osx_port._port = port->port;
+    } else if (_window_handle_type == P3D_WHT_osx_cgcontext) {
+      NP_CGContext *context = (NP_CGContext *)_window.window;
+      if (context != NULL) {
+        parent_window._handle._osx_cgcontext._context = context->context;
+        parent_window._handle._osx_cgcontext._window = (WindowRef)context->window;
+      }
+    }
 
 #elif defined(HAVE_X11)
     unsigned long win;
@@ -1440,7 +1500,7 @@ send_window() {
 #endif
 
   P3D_window_type window_type = P3D_WT_embedded;
-  if (_window.window == NULL) {
+  if (_window.window == NULL && _event_type != P3D_ET_osx_cocoa) {
     // No parent window: it must be a hidden window.
     window_type = P3D_WT_hidden;
   } else if (_window.width == 0 || _window.height == 0) {
@@ -1654,6 +1714,156 @@ window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
 }
 #endif  // _WIN32
 
+#ifdef MACOSX_HAS_EVENT_MODELS
+////////////////////////////////////////////////////////////////////
+//     Function: PPInstance::copy_cocoa_event
+//       Access: Private, Static
+//  Description: Copies the NPCocoaEvent structure componentwise into
+//               a P3DCocoaEvent structure, for passing into the core
+//               API.
+//
+//               The aux_data object is used to manage temporary
+//               storage on the strings created for the event.
+////////////////////////////////////////////////////////////////////
+void PPInstance::
+copy_cocoa_event(P3DCocoaEvent *p3d_event, NPCocoaEvent *np_event,
+                 EventAuxData &aux_data) {
+  p3d_event->version = np_event->version;
+
+  switch (np_event->type) {
+  case NPCocoaEventDrawRect:
+    p3d_event->type = P3DCocoaEventDrawRect;
+    break;
+  case NPCocoaEventMouseDown:
+    p3d_event->type = P3DCocoaEventMouseDown;
+    break;
+  case NPCocoaEventMouseUp:
+    p3d_event->type = P3DCocoaEventMouseUp;
+    break;
+  case NPCocoaEventMouseMoved:
+    p3d_event->type = P3DCocoaEventMouseMoved;
+    break;
+  case NPCocoaEventMouseEntered:
+    p3d_event->type = P3DCocoaEventMouseEntered;
+    break;
+  case NPCocoaEventMouseExited:
+    p3d_event->type = P3DCocoaEventMouseExited;
+    break;
+  case NPCocoaEventMouseDragged:
+    p3d_event->type = P3DCocoaEventMouseDragged;
+    break;
+  case NPCocoaEventKeyDown:
+    p3d_event->type = P3DCocoaEventKeyDown;
+    break;
+  case NPCocoaEventKeyUp:
+    p3d_event->type = P3DCocoaEventKeyUp;
+    break;
+  case NPCocoaEventFlagsChanged:
+    p3d_event->type = P3DCocoaEventFlagsChanged;
+    break;
+  case NPCocoaEventFocusChanged:
+    p3d_event->type = P3DCocoaEventFocusChanged;
+    break;
+  case NPCocoaEventWindowFocusChanged:
+    p3d_event->type = P3DCocoaEventWindowFocusChanged;
+    break;
+  case NPCocoaEventScrollWheel:
+    p3d_event->type = P3DCocoaEventScrollWheel;
+    break;
+  case NPCocoaEventTextInput:
+    p3d_event->type = P3DCocoaEventTextInput;
+    break;
+  }
+
+  switch (np_event->type) {
+  case NPCocoaEventDrawRect:
+    p3d_event->data.draw.context = np_event->data.draw.context;
+    p3d_event->data.draw.x = np_event->data.draw.x;
+    p3d_event->data.draw.y = np_event->data.draw.y;
+    p3d_event->data.draw.width = np_event->data.draw.width;
+    p3d_event->data.draw.height = np_event->data.draw.height;
+    break;
+
+  case NPCocoaEventMouseDown:
+  case NPCocoaEventMouseUp:
+  case NPCocoaEventMouseMoved:
+  case NPCocoaEventMouseEntered:
+  case NPCocoaEventMouseExited:
+  case NPCocoaEventMouseDragged:
+  case NPCocoaEventScrollWheel:
+    p3d_event->data.mouse.modifierFlags = np_event->data.mouse.modifierFlags;
+    p3d_event->data.mouse.pluginX = np_event->data.mouse.pluginX;
+    p3d_event->data.mouse.pluginY = np_event->data.mouse.pluginY;
+    p3d_event->data.mouse.buttonNumber = np_event->data.mouse.buttonNumber;
+    p3d_event->data.mouse.clickCount = np_event->data.mouse.clickCount;
+    p3d_event->data.mouse.deltaX = np_event->data.mouse.deltaX;
+    p3d_event->data.mouse.deltaY = np_event->data.mouse.deltaY;
+    p3d_event->data.mouse.deltaZ = np_event->data.mouse.deltaZ;
+    break;
+
+  case NPCocoaEventKeyDown:
+  case NPCocoaEventKeyUp:
+  case NPCocoaEventFlagsChanged:
+    p3d_event->data.key.modifierFlags = np_event->data.key.modifierFlags;
+    p3d_event->data.key.characters = 
+      make_ansi_string(aux_data._characters, np_event->data.key.characters);
+    p3d_event->data.key.charactersIgnoringModifiers = 
+      make_ansi_string(aux_data._characters_im, np_event->data.key.charactersIgnoringModifiers);
+    p3d_event->data.key.isARepeat = np_event->data.key.isARepeat;
+    p3d_event->data.key.keyCode = np_event->data.key.keyCode;
+    break;
+
+  case NPCocoaEventFocusChanged:
+  case NPCocoaEventWindowFocusChanged:
+    p3d_event->data.focus.hasFocus = np_event->data.focus.hasFocus;
+    break;
+
+  case NPCocoaEventTextInput:
+    p3d_event->data.text.text = 
+      make_ansi_string(aux_data._text, np_event->data.text.text);
+    break;
+  }
+}
+#endif  // MACOSX_HAS_EVENT_MODELS
+
+#ifdef MACOSX_HAS_EVENT_MODELS
+////////////////////////////////////////////////////////////////////
+//     Function: PPInstance::make_ansi_string
+//       Access: Private, Static
+//  Description: OSX only: Fills result with the unicode characters in
+//               the NPNSString.  Also returns result.c_str().
+////////////////////////////////////////////////////////////////////
+const wchar_t *PPInstance::
+make_ansi_string(wstring &result, NPNSString *ns_string) {
+  // An NPNSString is really just an NSString, which is itself just a
+  // CFString.
+  CFStringRef cfstr = (CFStringRef)ns_string;
+  CFIndex length = CFStringGetLength(cfstr);
+
+  result.clear();
+  for (CFIndex i = 0; i < length; ++i) {
+    result += (wchar_t)CFStringGetCharacterAtIndex(cfstr, i);
+  }
+
+  return result.c_str();
+}
+#endif  // MACOSX_HAS_EVENT_MODELS
+
+#ifdef __APPLE__
+////////////////////////////////////////////////////////////////////
+//     Function: PPInstance::timer_callback
+//       Access: Private, Static
+//  Description: OSX only: this callback is associated with a
+//               CFRunLoopTimer; it's used to forward request messages
+//               to the main thread.
+////////////////////////////////////////////////////////////////////
+void PPInstance::
+timer_callback(CFRunLoopTimerRef timer, void *info) {
+  PPInstance *self = (PPInstance *)info;
+  self->handle_request_loop();
+}
+#endif  // __APPLE__
+
 ////////////////////////////////////////////////////////////////////
 //     Function: PPInstance::StreamingFileData::Constructor
 //       Access: Public

+ 26 - 1
direct/src/plugin_npapi/ppInstance.h

@@ -35,7 +35,9 @@ class PPDownloadRequest;
 class PPInstance {
 public:
   PPInstance(NPMIMEType pluginType, NPP instance, uint16_t mode, 
-             int16_t argc, char *argn[], char *argv[], NPSavedData *saved);
+             int16_t argc, char *argn[], char *argv[], NPSavedData *saved,
+             P3D_window_handle_type window_handle_type,
+             P3D_event_type event_type);
   ~PPInstance();
 
   void begin();
@@ -99,9 +101,28 @@ private:
   window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);
 #endif  // _WIN32
 
+  class EventAuxData {
+  public:
+    wstring _characters;
+    wstring _characters_im;
+    wstring _text;
+  };
+#ifdef MACOSX_HAS_EVENT_MODELS
+  static void copy_cocoa_event(P3DCocoaEvent *p3d_event, 
+                               NPCocoaEvent *np_event,
+                               EventAuxData &aux_data);
+  static const wchar_t *make_ansi_string(wstring &result, NPNSString *ns_string);
+#endif  // MACOSX_HAS_EVENT_MODELS
+
+#ifdef __APPLE__
+  static void timer_callback(CFRunLoopTimerRef timer, void *info);
+#endif  // __APPLE__
+
 private:
   NPP _npp_instance;
   unsigned int _npp_mode;
+  P3D_window_handle_type _window_handle_type;
+  P3D_event_type _event_type;
   typedef vector<P3D_token> Tokens;
   Tokens _tokens;
 
@@ -165,6 +186,10 @@ private:
   LONG_PTR _orig_window_proc;
 #endif  // _WIN32
 
+#ifdef __APPLE__
+  CFRunLoopTimerRef _request_timer;
+#endif  // __APPLE__
+
   bool _python_window_open;
 
   PPToplevelObject *_script_object;

+ 56 - 16
direct/src/plugin_npapi/startup.cxx

@@ -194,7 +194,12 @@ NP_Initialize(NPNetscapeFuncs *browserFuncs,
 #ifdef HAS_PLUGIN_THREAD_ASYNC_CALL
   // Check if the browser offers this very useful call.
   if (browser_major > 0 || browser_minor >= NPVERS_HAS_PLUGIN_THREAD_ASYNC_CALL) {
-    has_plugin_thread_async_call = true;
+    if ((void *)browser->pluginthreadasynccall == (void *)NULL) {
+      nout << "Browser should have PLUGIN_THREAD_ASYNC_CALL, but the pointer is NULL.\n";
+      has_plugin_thread_async_call = false;
+    } else {
+      has_plugin_thread_async_call = true;
+    }
   }
 #endif
 
@@ -287,30 +292,65 @@ NPP_New(NPMIMEType pluginType, NPP instance, uint16_t mode,
         int16_t argc, char *argn[], char *argv[], NPSavedData *saved) {
   nout << "new instance " << instance << "\n";
 
-  /*
+  P3D_window_handle_type window_handle_type = P3D_WHT_none;
+  P3D_event_type event_type = P3D_ET_none;
+
 #ifdef __APPLE__
-  // We have to request the "core graphics" drawing model to be
+  // The default drawing model for Apple is via the deprecated
+  // QuickDraw GrafPtr, and the default event model is via the
+  // deprecated Carbon EventRecord.
+  window_handle_type = P3D_WHT_osx_port;
+  event_type = P3D_ET_osx_event_record;
+
+  // But we have to request the CoreGraphics drawing model to be
   // compatible with Snow Leopard.
-  NPBool supportsCoreGraphics = false;
+  NPBool supports_core_graphics = false;
+#ifdef MACOSX_HAS_COREGRAPHICS_DRAWING_MODEL
   NPError err = browser->getvalue(instance,
                                   NPNVsupportsCoreGraphicsBool,
-                                  &supportsCoreGraphics);
-  if (err != NPERR_NO_ERROR || !supportsCoreGraphics) {
-    return NPERR_INCOMPATIBLE_VERSION_ERROR;
+                                  &supports_core_graphics);
+  if (err != NPERR_NO_ERROR) {
+    supports_core_graphics = false;
   }
-  
-  // Set the drawing model
-  err = browser->setvalue(instance,
-                          (NPPVariable)NPNVpluginDrawingModel,
-                          (void *)NPDrawingModelCoreGraphics);
+
+  if (supports_core_graphics) {
+    // Set the drawing model
+    err = browser->setvalue(instance,
+                            (NPPVariable)NPNVpluginDrawingModel,
+                            (void *)NPDrawingModelCoreGraphics);
+    if (err == NPERR_NO_ERROR) {
+      window_handle_type = P3D_WHT_osx_cgcontext;
+    }
+  }
+#endif  // MACOSX_HAS_COREGRAPHICS_DRAWING_MODEL
+  nout << "supports_core_graphics = " << (bool)supports_core_graphics
+       << " window_handle_type = " << window_handle_type << "\n";
+
+  // And Snow Leopard also wants the new Cocoa event model.
+  NPBool supports_cocoa = false;
+#ifdef MACOSX_HAS_EVENT_MODELS
+  err = browser->getvalue(instance, NPNVsupportsCocoaBool, &supports_cocoa);
   if (err != NPERR_NO_ERROR) {
-    return NPERR_INCOMPATIBLE_VERSION_ERROR;
+    supports_cocoa = false;
   }
-#endif
-  */
+
+  if (supports_cocoa) {
+    // Set the event model
+    err = browser->setvalue(instance,
+                            (NPPVariable)NPPVpluginEventModel,
+                            (void *)NPEventModelCocoa);
+    if (err == NPERR_NO_ERROR) {
+      event_type = P3D_ET_osx_cocoa;
+    }
+  }
+#endif  // MACOSX_HAS_EVENT_MODELS
+  nout << "supports_cocoa = " << (bool)supports_cocoa
+       << " event_type = " << event_type << "\n";
+#endif  // __APPLE__
 
   PPInstance *inst = new PPInstance(pluginType, instance, mode,
-                                    argc, argn, argv, saved);
+                                    argc, argn, argv, saved,
+                                    window_handle_type, event_type);
   instance->pdata = inst;
   nout << "new instance->pdata = " << inst << "\n";