Browse Source

Implement confined mouse mode for OS X.

Note: it seems that using #set_pointer() will result in
a long delay before an absolute mouse position is updated,
even though deltas are reported.

Thus, in CocoaPandaView, the mouse deltas are sent for both
relative and confined modes, so client code (e.g. the "mouse
modes" demo) will be able to recenter the mouse without
choppy movement.
Ed Swartz 10 years ago
parent
commit
fc5a2e5e33

+ 47 - 3
panda/src/cocoadisplay/cocoaGraphicsWindow.mm

@@ -1022,6 +1022,23 @@ set_properties_now(WindowProperties &properties) {
     }
     properties.clear_z_order();
   }
+
+  if (properties.has_mouse_mode()) {
+    switch (properties.get_mouse_mode()) {
+    case WindowProperties::M_absolute:
+    case WindowProperties::M_confined:  // confined is maintained in mouse move event
+      CGAssociateMouseAndMouseCursorPosition(true);
+      _properties.set_mouse_mode(properties.get_mouse_mode());
+      properties.clear_mouse_mode();
+      break;
+
+    case WindowProperties::M_relative:
+      CGAssociateMouseAndMouseCursorPosition(false);
+      _properties.set_mouse_mode(properties.get_mouse_mode());
+      properties.clear_mouse_mode();
+      break;
+    }
+  }
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -1670,6 +1687,8 @@ handle_mouse_button_event(int button, bool down) {
 ////////////////////////////////////////////////////////////////////
 void CocoaGraphicsWindow::
 handle_mouse_moved_event(bool in_window, double x, double y, bool absolute) {
+  double nx, ny;
+
   if (absolute) {
     if (cocoadisplay_cat.is_spam()) {
       if (in_window != _input_devices[0].get_pointer().get_in_window()) {
@@ -1682,15 +1701,40 @@ handle_mouse_moved_event(bool in_window, double x, double y, bool absolute) {
     }
 
     // Strangely enough, in Cocoa, mouse Y coordinates are 1-based.
-    _input_devices[0].set_pointer(in_window, x, y - 1,
-      ClockObject::get_global_clock()->get_frame_time());
+    nx = x;
+    ny = y - 1;
 
   } else {
     // We received deltas, so add it to the current mouse position.
     MouseData md = _input_devices[0].get_pointer();
-    _input_devices[0].set_pointer_in_window(md.get_x() + x, md.get_y() + y);
+    nx = md.get_x() + x;
+    ny = md.get_y() + y;
   }
 
+  if (_properties.get_mouse_mode() == WindowProperties::M_confined
+      && !in_window) {
+    CGPoint point;
+
+    nx = std::max(0., std::min((double) get_x_size() - 1, nx));
+    ny = std::max(0., std::min((double) get_y_size() - 1, ny));
+
+    if (_properties.get_fullscreen()) {
+      point = CGPointMake(nx, ny + 1);
+    } else {
+      point = CGPointMake(nx + _properties.get_x_origin(),
+                          ny + _properties.get_y_origin() + 1);
+    }
+
+    if (CGWarpMouseCursorPosition(point) == kCGErrorSuccess) {
+      in_window = true;
+    } else {
+      cocoadisplay_cat.warning() << "Failed to return mouse pointer to window\n";
+    }
+  }
+
+  _input_devices[0].set_pointer(in_window, nx, ny,
+      ClockObject::get_global_clock()->get_frame_time());
+
   if (in_window != _mouse_hidden && _properties.get_cursor_hidden()) {
     // Hide the cursor if the mouse enters the window,
     // and unhide it when the mouse leaves the window.

+ 4 - 1
panda/src/cocoadisplay/cocoaPandaView.mm

@@ -116,7 +116,10 @@
   NSPoint loc = [self convertPoint:[event locationInWindow] fromView:nil];
   BOOL inside = [self mouse:loc inRect:[self bounds]];
 
-  if (_graphicsWindow->get_properties().get_mouse_mode() == WindowProperties::M_relative) {
+  // the correlation between mouse deltas and location
+  // are "debounced" apparently, so send deltas for both
+  // relative and confined modes
+  if (_graphicsWindow->get_properties().get_mouse_mode() != WindowProperties::M_absolute) {
     _graphicsWindow->handle_mouse_moved_event(inside, [event deltaX], [event deltaY], false);
   } else {
     _graphicsWindow->handle_mouse_moved_event(inside, loc.x, loc.y, true);

+ 19 - 0
panda/src/osxdisplay/osxGraphicsWindow.mm

@@ -2064,6 +2064,25 @@ set_properties_now(WindowProperties &properties) {
     properties.clear_minimized();
   }
 
+  if (properties.has_mouse_mode()) {
+    switch (properties.get_mouse_mode()) {
+    case WindowProperties::M_absolute:
+      CGAssociateMouseAndMouseCursorPosition(true);
+      _properties.set_mouse_mode(WindowProperties::M_absolute);
+      properties.clear_mouse_mode();
+      break;
+
+    case WindowProperties::M_relative:
+      CGAssociateMouseAndMouseCursorPosition(false);
+      _properties.set_mouse_mode(WindowProperties::M_relative);
+      properties.clear_mouse_mode();
+      break;
+
+    case WindowProperties::M_confined:
+      break;
+    }
+  }
+
   if (osxdisplay_cat.is_debug()) {
     osxdisplay_cat.debug()
       << "set_properties_now Out....." << _properties << "\n";

+ 6 - 4
samples/mouse-modes/main.py

@@ -34,7 +34,8 @@ class App(ShowBase):
         # Disable the camera trackball controls.
         self.disableMouse()
         
-        self.mouseMagnitude = 144
+        # control mapping of mouse movement to box movement
+        self.mouseMagnitude = 1
 
         self.rotateX, self.rotateY = 0, 0
 
@@ -146,7 +147,8 @@ class App(ShowBase):
         if self.manualRecenterMouse:
             # move mouse back to center
             self.recenterMouse()             
-
+            self.lastMouseX, self.lastMouseY = 0, 0  
+                
         # scale position and delta to pixels for user
         w, h = self.win.getSize()
         
@@ -158,8 +160,8 @@ class App(ShowBase):
              int(dx*w), int(dy*h))) 
 
         # rotate box by delta
-        self.rotateX += dx * 10
-        self.rotateY += dy * 10
+        self.rotateX += dx * 10 * self.mouseMagnitude
+        self.rotateY += dy * 10 * self.mouseMagnitude
 
         self.positionText.setText("Model rotation: {0}, {1}".format(
              int(self.rotateX*1000)/1000., int(self.rotateY*1000)/1000.))