Преглед изворни кода

add support for OS time for keypress/release

David Rose пре 22 година
родитељ
комит
038230f7ec

+ 14 - 0
direct/src/showbase/ShowBase.py

@@ -594,6 +594,20 @@ class ShowBase(DirectObject.DirectObject):
         mods.addButton(KeyboardButton.alt())
         self.buttonThrower.node().setModifierButtons(mods)
 
+        # A special ButtonThrower to generate keyboard events and
+        # include the time from the OS.  This is separate only to
+        # support legacy code that did not expect a time parameter; it
+        # will eventually be folded into the normal ButtonThrower,
+        # above.
+
+        # Temporary hasattr() for old pandas.
+        if hasattr(ButtonThrower, "setTimeFlag"):
+            self.timeButtonThrower = self.mouseWatcher.attachNewNode(ButtonThrower('timeButtons'))
+            self.timeButtonThrower.node().setPrefix('time-')
+            self.timeButtonThrower.node().setTimeFlag(1)
+        else:
+            self.timeButtonThrower = None
+
         # Tell the gui system about our new mouse watcher.
         self.aspect2d.node().setMouseWatcher(self.mouseWatcherNode)
         self.mouseWatcherNode.addRegion(PGMouseWatcherBackground())

+ 8 - 8
panda/src/display/graphicsWindowInputDevice.cxx

@@ -154,8 +154,8 @@ get_button_event() {
 //  Description: Records that the indicated button has been depressed.
 ////////////////////////////////////////////////////////////////////
 void GraphicsWindowInputDevice::
-button_down(ButtonHandle button) {
-  _button_events.push_back(ButtonEvent(button, ButtonEvent::T_down));
+button_down(ButtonHandle button, double time) {
+  _button_events.push_back(ButtonEvent(button, ButtonEvent::T_down, time));
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -167,8 +167,8 @@ button_down(ButtonHandle button) {
 //               state of modifier keys.
 ////////////////////////////////////////////////////////////////////
 void GraphicsWindowInputDevice::
-button_resume_down(ButtonHandle button) {
-  _button_events.push_back(ButtonEvent(button, ButtonEvent::T_resume_down));
+button_resume_down(ButtonHandle button, double time) {
+  _button_events.push_back(ButtonEvent(button, ButtonEvent::T_resume_down, time));
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -177,8 +177,8 @@ button_resume_down(ButtonHandle button) {
 //  Description: Records that the indicated button has been released.
 ////////////////////////////////////////////////////////////////////
 void GraphicsWindowInputDevice::
-button_up(ButtonHandle button) {
-  _button_events.push_back(ButtonEvent(button, ButtonEvent::T_up));
+button_up(ButtonHandle button, double time) {
+  _button_events.push_back(ButtonEvent(button, ButtonEvent::T_up, time));
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -188,6 +188,6 @@ button_up(ButtonHandle button) {
 //               generated.
 ////////////////////////////////////////////////////////////////////
 void GraphicsWindowInputDevice::
-keystroke(int keycode) {
-  _button_events.push_back(ButtonEvent(keycode));
+keystroke(int keycode, double time) {
+  _button_events.push_back(ButtonEvent(keycode, time));
 }

+ 5 - 4
panda/src/display/graphicsWindowInputDevice.h

@@ -23,6 +23,7 @@
 
 #include "buttonEvent.h"
 #include "mouseData.h"
+#include "clockObject.h"
 
 #include "pdeque.h"
 #include "pvector.h"
@@ -61,10 +62,10 @@ public:
 public:
   // The following interface is for the various kinds of
   // GraphicsWindows to record the data incoming on the device.
-  void button_down(ButtonHandle button);
-  void button_resume_down(ButtonHandle button);
-  void button_up(ButtonHandle button);
-  void keystroke(int keycode);
+  void button_down(ButtonHandle button, double time = ClockObject::get_global_clock()->get_frame_time());
+  void button_resume_down(ButtonHandle button, double time = ClockObject::get_global_clock()->get_frame_time());
+  void button_up(ButtonHandle button, double time = ClockObject::get_global_clock()->get_frame_time());
+  void keystroke(int keycode, double time = ClockObject::get_global_clock()->get_frame_time());
   INLINE void set_pointer_in_window(int x, int y);
   INLINE void set_pointer_out_of_window();
 

+ 13 - 7
panda/src/putil/buttonEvent.I

@@ -26,7 +26,8 @@ INLINE ButtonEvent::
 ButtonEvent() :
   _button(ButtonHandle::none()),
   _keycode(0),
-  _type(T_down)
+  _type(T_down),
+  _time(0.0)
 {
 }
 
@@ -36,10 +37,11 @@ ButtonEvent() :
 //  Description:
 ////////////////////////////////////////////////////////////////////
 INLINE ButtonEvent::
-ButtonEvent(ButtonHandle button, ButtonEvent::Type type) :
+ButtonEvent(ButtonHandle button, ButtonEvent::Type type, double time) :
   _button(button),
   _keycode(0),
-  _type(type)
+  _type(type),
+  _time(time)
 {
 }
 
@@ -49,10 +51,11 @@ ButtonEvent(ButtonHandle button, ButtonEvent::Type type) :
 //  Description:
 ////////////////////////////////////////////////////////////////////
 INLINE ButtonEvent::
-ButtonEvent(short keycode) :
+ButtonEvent(short keycode, double time) :
   _button(ButtonHandle::none()),
   _keycode(keycode),
-  _type(T_keystroke)
+  _type(T_keystroke),
+  _time(time)
 {
 }
 
@@ -65,7 +68,8 @@ INLINE ButtonEvent::
 ButtonEvent(const ButtonEvent &copy) :
   _button(copy._button),
   _keycode(copy._keycode),
-  _type(copy._type)
+  _type(copy._type),
+  _time(copy._time)
 {
 }
 
@@ -79,12 +83,14 @@ operator = (const ButtonEvent &copy) {
   _button = copy._button;
   _keycode = copy._keycode;
   _type = copy._type;
+  _time = copy._time;
 }
 
 ////////////////////////////////////////////////////////////////////
 //     Function: ButtonEvent::Equality Operator
 //       Access: Public
-//  Description:
+//  Description: The equality operator does not consider time
+//               significant.
 ////////////////////////////////////////////////////////////////////
 INLINE bool ButtonEvent::
 operator == (const ButtonEvent &other) const {

+ 10 - 2
panda/src/putil/buttonEvent.h

@@ -22,6 +22,7 @@
 #include "pandabase.h"
 
 #include "buttonHandle.h"
+#include "clockObject.h"
 
 ////////////////////////////////////////////////////////////////////
 //       Class : ButtonEvent
@@ -71,8 +72,8 @@ public:
   };
 
   INLINE ButtonEvent();
-  INLINE ButtonEvent(ButtonHandle button, Type type);
-  INLINE ButtonEvent(short keycode);
+  INLINE ButtonEvent(ButtonHandle button, Type type, double time = ClockObject::get_global_clock()->get_frame_time());
+  INLINE ButtonEvent(short keycode, double time = ClockObject::get_global_clock()->get_frame_time());
   INLINE ButtonEvent(const ButtonEvent &copy);
   INLINE void operator = (const ButtonEvent &copy);
 
@@ -90,7 +91,14 @@ public:
   // the Unicode character that was typed.
   short _keycode;
 
+  // This is the type of the button event (see above).
   Type _type;
+
+  // This is the time the event occurred, as recorded from the OS if
+  // that information is available.  It is in seconds elapsed from an
+  // arbitrary epoch, and it matches the time reported by
+  // ClockObject::get_global_clock().
+  double _time;
 };
 
 INLINE ostream &operator << (ostream &out, const ButtonEvent &be) {

+ 2 - 2
panda/src/tform/Sources.pp

@@ -10,7 +10,7 @@
   #define COMBINED_SOURCES $[TARGET]_composite1.cxx $[TARGET]_composite2.cxx 
 
   #define SOURCES  \
-    buttonThrower.h \
+    buttonThrower.I buttonThrower.h \
     config_tform.h \
     driveInterface.I driveInterface.h \
     mouseWatcher.I mouseWatcher.h \
@@ -31,7 +31,7 @@
     transform2sg.cxx 
 
   #define INSTALL_HEADERS \
-    buttonThrower.h \
+    buttonThrower.I buttonThrower.h \
     driveInterface.I driveInterface.h \
     mouseWatcher.I mouseWatcher.h \
     mouseWatcherGroup.h \

+ 148 - 0
panda/src/tform/buttonThrower.I

@@ -0,0 +1,148 @@
+// Filename: buttonThrower.I
+// Created by:  drose (26Dec03)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: ButtonThrower::set_prefix
+//       Access: Published
+//  Description: Sets the prefix which is prepended to all event names
+//               thrown by this object.
+////////////////////////////////////////////////////////////////////
+INLINE void ButtonThrower::
+set_prefix(const string &prefix) {
+  _prefix = prefix;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ButtonThrower::has_prefix
+//       Access: Published
+//  Description: Returns true if the ButtonThrower has a prefix set,
+//               false otherwise.
+////////////////////////////////////////////////////////////////////
+INLINE bool ButtonThrower::
+has_prefix() const {
+  return !_prefix.empty();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ButtonThrower::get_prefix
+//       Access: Published
+//  Description: Returns the prefix that has been set on this
+//               ButtonThrower.  See set_prefix().
+////////////////////////////////////////////////////////////////////
+INLINE string ButtonThrower::
+get_prefix() const {
+  return _prefix;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ButtonThrower::set_time_flag
+//       Access: Published
+//  Description: Sets the flag that indicates whether the time of the
+//               button event should be passed as a parameter or not.
+//               When this is true, an additional parameter is
+//               generated on each event (before all the parameters
+//               named by add_parameter) that consists of a single
+//               double value, and reflects the time the button was
+//               pressed or released, as a value from
+//               ClockObject::get_global_clock().
+////////////////////////////////////////////////////////////////////
+INLINE void ButtonThrower::
+set_time_flag(bool time_flag) {
+  _time_flag = time_flag;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ButtonThrower::get_time_flag
+//       Access: Published
+//  Description: Returns the flag that indicates whether the time of
+//               the button event should be passed as a parameter.
+////////////////////////////////////////////////////////////////////
+INLINE bool ButtonThrower::
+get_time_flag() const {
+  return _time_flag;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ButtonThrower::get_modifier_buttons
+//       Access: Published
+//  Description: Returns the set of ModifierButtons that the
+//               ButtonThrower will consider important enough to
+//               prepend the event name with.  Normally, this set will
+//               be empty, and the ButtonThrower will therefore ignore
+//               all ModifierButtons attached to the key events, but
+//               if one or more buttons have been added to this set,
+//               and those modifier buttons are set on the button
+//               event, then the event name will be prepended with the
+//               names of the modifier buttons.
+////////////////////////////////////////////////////////////////////
+INLINE const ModifierButtons &ButtonThrower::
+get_modifier_buttons() const {
+  return _mods;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ButtonThrower::set_modifier_buttons
+//       Access: Published
+//  Description: Changes the set of ModifierButtons that the
+//               ButtonThrower will consider important enough to
+//               prepend the event name with.  Normally, this set will
+//               be empty, and the ButtonThrower will therefore ignore
+//               all ModifierButtons attached to the key events, but
+//               if one or more buttons have been added to this set,
+//               then the event name will be prepended with the names
+//               of the modifier buttons.
+//
+//               It is recommended that you change this setting by
+//               first calling get_modifier_buttons(), making
+//               adjustments, and passing the new value to
+//               set_modifier_buttons().  This way the current state
+//               of the modifier buttons will not be lost.
+////////////////////////////////////////////////////////////////////
+INLINE void ButtonThrower::
+set_modifier_buttons(const ModifierButtons &mods) {
+  _mods = mods;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ButtonThrower::set_throw_buttons_active
+//       Access: Published
+//  Description: Sets the flag that indicates whether the
+//               ButtonThrower will only process events for the
+//               explicitly named buttons or not.  Normally this is
+//               false, meaning all buttons are processed; set it true
+//               to indicate that only some buttons should be
+//               processed.  See add_throw_button().
+////////////////////////////////////////////////////////////////////
+INLINE void ButtonThrower::
+set_throw_buttons_active(bool flag) {
+  _throw_buttons_active = flag;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ButtonThrower::get_throw_buttons_active
+//       Access: Published
+//  Description: Returns the flag that indicates whether the
+//               ButtonThrower will only process events for the
+//               explicitly named buttons or not.  See
+//               set_throw_buttons_active().
+////////////////////////////////////////////////////////////////////
+INLINE bool ButtonThrower::
+get_throw_buttons_active() const {
+  return _throw_buttons_active;
+}

+ 8 - 106
panda/src/tform/buttonThrower.cxx

@@ -43,6 +43,7 @@ ButtonThrower(const string &name) :
 
   _button_events = new ButtonEventList;
 
+  _time_flag = false;
   _throw_buttons_active = false;
 }
 
@@ -56,40 +57,6 @@ ButtonThrower::
 }
 
 
-////////////////////////////////////////////////////////////////////
-//     Function: ButtonThrower::set_prefix
-//       Access: Published
-//  Description: Sets the prefix which is prepended to all event names
-//               thrown by this object.
-////////////////////////////////////////////////////////////////////
-void ButtonThrower::
-set_prefix(const string &prefix) {
-  _prefix = prefix;
-}
-
-
-////////////////////////////////////////////////////////////////////
-//     Function: ButtonThrower::has_prefix
-//       Access: Published
-//  Description: Returns true if the ButtonThrower has a prefix set,
-//               false otherwise.
-////////////////////////////////////////////////////////////////////
-bool ButtonThrower::
-has_prefix() const {
-  return !_prefix.empty();
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: ButtonThrower::get_prefix
-//       Access: Published
-//  Description: Returns the prefix that has been set on this
-//               ButtonThrower.  See set_prefix().
-////////////////////////////////////////////////////////////////////
-string ButtonThrower::
-get_prefix() const {
-  return _prefix;
-}
-
 ////////////////////////////////////////////////////////////////////
 //     Function: ButtonThrower::add_parameter
 //       Access: Public
@@ -128,75 +95,6 @@ get_parameter(int n) const {
   return _parameters[n];
 }
 
-////////////////////////////////////////////////////////////////////
-//     Function: ButtonThrower::get_modifier_buttons
-//       Access: Published
-//  Description: Returns the set of ModifierButtons that the
-//               ButtonThrower will consider important enough to
-//               prepend the event name with.  Normally, this set will
-//               be empty, and the ButtonThrower will therefore ignore
-//               all ModifierButtons attached to the key events, but
-//               if one or more buttons have been added to this set,
-//               and those modifier buttons are set on the button
-//               event, then the event name will be prepended with the
-//               names of the modifier buttons.
-////////////////////////////////////////////////////////////////////
-const ModifierButtons &ButtonThrower::
-get_modifier_buttons() const {
-  return _mods;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: ButtonThrower::set_modifier_buttons
-//       Access: Published
-//  Description: Changes the set of ModifierButtons that the
-//               ButtonThrower will consider important enough to
-//               prepend the event name with.  Normally, this set will
-//               be empty, and the ButtonThrower will therefore ignore
-//               all ModifierButtons attached to the key events, but
-//               if one or more buttons have been added to this set,
-//               then the event name will be prepended with the names
-//               of the modifier buttons.
-//
-//               It is recommended that you change this setting by
-//               first calling get_modifier_buttons(), making
-//               adjustments, and passing the new value to
-//               set_modifier_buttons().  This way the current state
-//               of the modifier buttons will not be lost.
-////////////////////////////////////////////////////////////////////
-void ButtonThrower::
-set_modifier_buttons(const ModifierButtons &mods) {
-  _mods = mods;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: ButtonThrower::set_throw_buttons_active
-//       Access: Published
-//  Description: Sets the flag that indicates whether the
-//               ButtonThrower will only process events for the
-//               explicitly named buttons or not.  Normally this is
-//               false, meaning all buttons are processed; set it true
-//               to indicate that only some buttons should be
-//               processed.  See add_throw_button().
-////////////////////////////////////////////////////////////////////
-void ButtonThrower::
-set_throw_buttons_active(bool flag) {
-  _throw_buttons_active = flag;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: ButtonThrower::get_throw_buttons_active
-//       Access: Published
-//  Description: Returns the flag that indicates whether the
-//               ButtonThrower will only process events for the
-//               explicitly named buttons or not.  See
-//               set_throw_buttons_active().
-////////////////////////////////////////////////////////////////////
-bool ButtonThrower::
-get_throw_buttons_active() const {
-  return _throw_buttons_active;
-}
-
 ////////////////////////////////////////////////////////////////////
 //     Function: ButtonThrower::add_throw_button
 //       Access: Published
@@ -370,9 +268,13 @@ write(ostream &out, int indent_level) const {
 //               all of the user-requested parameters.
 ////////////////////////////////////////////////////////////////////
 void ButtonThrower::
-do_throw_event(const string &event_name) {
+do_throw_event(const string &event_name, double time) {
   Event *event = new Event(_prefix + event_name);
 
+  if (_time_flag) {
+    event->add_parameter(time);
+  }
+
   ParameterList::const_iterator pi;
   for (pi = _parameters.begin(); pi != _parameters.end(); ++pi) {
     event->add_parameter(*pi);
@@ -419,7 +321,7 @@ do_transmit_data(const DataNodeTransmit &input, DataNodeTransmit &output) {
 
         if (!_throw_buttons_active || has_throw_button(_mods, be._button)) {
           // Process this button.
-          do_throw_event(event_name);
+          do_throw_event(event_name, be._time);
           
         } else {
           // Don't process this button; instead, pass it down to future
@@ -442,7 +344,7 @@ do_transmit_data(const DataNodeTransmit &input, DataNodeTransmit &output) {
         // definition for the button at all, regardless of the state
         // of the modifier keys.
         if (!_throw_buttons_active || has_throw_button(be._button)) {
-          do_throw_event(event_name + "-up");
+          do_throw_event(event_name + "-up", be._time);
         }
         if (_throw_buttons_active) {
           // Now pass the event on to future generations.  We always

+ 15 - 8
panda/src/tform/buttonThrower.h

@@ -45,19 +45,22 @@ PUBLISHED:
   ButtonThrower(const string &name);
   ~ButtonThrower();
 
-  void set_prefix(const string &prefix);
-  bool has_prefix() const;
-  string get_prefix() const;
+  INLINE void set_prefix(const string &prefix);
+  INLINE bool has_prefix() const;
+  INLINE string get_prefix() const;
+
+  INLINE void set_time_flag(bool time_flag);
+  INLINE bool get_time_flag() const;
 
   void add_parameter(const EventParameter &obj);
   int get_num_parameters() const;
   EventParameter get_parameter(int n) const;
 
-  const ModifierButtons &get_modifier_buttons() const;
-  void set_modifier_buttons(const ModifierButtons &mods);
+  INLINE const ModifierButtons &get_modifier_buttons() const;
+  INLINE void set_modifier_buttons(const ModifierButtons &mods);
 
-  void set_throw_buttons_active(bool flag);
-  bool get_throw_buttons_active() const;
+  INLINE void set_throw_buttons_active(bool flag);
+  INLINE bool get_throw_buttons_active() const;
 
   bool add_throw_button(const ModifierButtons &mods, const ButtonHandle &button);
   bool remove_throw_button(const ModifierButtons &mods, const ButtonHandle &button);
@@ -69,12 +72,14 @@ public:
   virtual void write(ostream &out, int indent_level = 0) const;
 
 private:
-  void do_throw_event(const string &event_name);
+  void do_throw_event(const string &event_name, double time);
 
 private:
   string _prefix;
   ModifierButtons _mods;
 
+  bool _time_flag;
+
   typedef pvector<EventParameter> ParameterList;
   ParameterList _parameters;
 
@@ -114,4 +119,6 @@ private:
   static TypeHandle _type_handle;
 };
 
+#include "buttonThrower.I"
+
 #endif

+ 22 - 6
panda/src/windisplay/winGraphicsWindow.I

@@ -23,10 +23,10 @@
 //  Description:
 ////////////////////////////////////////////////////////////////////
 INLINE void WinGraphicsWindow::
-handle_keypress(ButtonHandle key, int x, int y) {
+handle_keypress(ButtonHandle key, int x, int y, double time) {
   _input_devices[0].set_pointer_in_window(x, y);
   if (key != ButtonHandle::none()) {
-    _input_devices[0].button_down(key);
+    _input_devices[0].button_down(key, time);
   }
 }
 
@@ -38,9 +38,9 @@ handle_keypress(ButtonHandle key, int x, int y) {
 //               tracking the state of modifier keys.
 ////////////////////////////////////////////////////////////////////
 INLINE void WinGraphicsWindow::
-handle_keyresume(ButtonHandle key) {
+handle_keyresume(ButtonHandle key, double time) {
   if (key != ButtonHandle::none()) {
-    _input_devices[0].button_resume_down(key);
+    _input_devices[0].button_resume_down(key, time);
   }
 }
 
@@ -50,9 +50,9 @@ handle_keyresume(ButtonHandle key) {
 //  Description:
 ////////////////////////////////////////////////////////////////////
 INLINE void WinGraphicsWindow::
-handle_keyrelease(ButtonHandle key) {
+handle_keyrelease(ButtonHandle key, double time) {
   if (key != ButtonHandle::none()) {
-    _input_devices[0].button_up(key);
+    _input_devices[0].button_up(key, time);
   }
 }
 
@@ -108,3 +108,19 @@ set_cursor_out_of_window() {
     update_cursor_window(NULL);
   }
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: WinGraphicsWindow::get_message_time
+//       Access: Private, Static
+//  Description: May be called only during the servicing of a Windows
+//               message.  This returns the time the message was added
+//               to the Windows message queue (as reported via
+//               GetMessageTime()), converted into global clock units.
+////////////////////////////////////////////////////////////////////
+INLINE double WinGraphicsWindow::
+get_message_time() {
+  DWORD now_ticks = GetTickCount();
+  double now_time = ClockObject::get_global_clock()->get_real_time();
+  DWORD elapsed_ticks = now_ticks - GetMessageTime();
+  return now_time - (double)elapsed_ticks / 1000.0;
+}

+ 18 - 9
panda/src/windisplay/winGraphicsWindow.cxx

@@ -782,7 +782,8 @@ window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
         }
         SetCapture(hwnd);
         handle_keypress(MouseButton::button(button), 
-                        translate_mouse(LOWORD(lparam)), translate_mouse(HIWORD(lparam)));
+                        translate_mouse(LOWORD(lparam)), translate_mouse(HIWORD(lparam)),
+                        get_message_time());
         break;
     
       case WM_LBUTTONUP:
@@ -801,7 +802,7 @@ window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
           button = 2;
         }
         ReleaseCapture();
-        handle_keyrelease(MouseButton::button(button));
+        handle_keyrelease(MouseButton::button(button), get_message_time());
         break;
     
       case WM_IME_NOTIFY:
@@ -913,7 +914,8 @@ window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
           POINT point;
           GetCursorPos(&point);
           ScreenToClient(hwnd, &point);
-          handle_keypress(lookup_key(wparam), point.x, point.y);
+          handle_keypress(lookup_key(wparam), point.x, point.y, 
+                          get_message_time());
           if (wparam == VK_F10) {
             // bypass default windproc F10 behavior (it activates the main
             // menu, but we have none)
@@ -944,12 +946,17 @@ window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
           windisplay_cat.debug()
             << "keydown: " << wparam << " (" << lookup_key(wparam) << ")\n";
         }
-        {
+
+        // If this bit is not zero, this is just a keyrepeat echo; we
+        // ignore these for handle_keypress (we respect keyrepeat only
+        // for handle_keystroke).
+        if ((lparam & 0x40000000) == 0) {
           POINT point;
           
           GetCursorPos(&point);
           ScreenToClient(hwnd, &point);
-          handle_keypress(lookup_key(wparam), point.x, point.y);
+          handle_keypress(lookup_key(wparam), point.x, point.y,
+                          get_message_time());
     
           // Handle Cntrl-V paste from clipboard.  Is there a better way
           // to detect this hotkey?
@@ -987,7 +994,7 @@ window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
           windisplay_cat.debug()
             << "keyup: " << wparam << " (" << lookup_key(wparam) << ")\n";
         }
-        handle_keyrelease(lookup_key(wparam));
+        handle_keyrelease(lookup_key(wparam), get_message_time());
         break;
     
       case WM_KILLFOCUS: 
@@ -1017,10 +1024,11 @@ window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
             // If we don't want to remember the keystate while the
             // window focus is lost, then generate a keyup event
             // right now for each key currently held.
+            double message_time = get_message_time();
             for (int i = 0; i < num_virtual_keys; i++) {
               if (i != VK_SHIFT && i != VK_CONTROL && i != VK_MENU) {
                 if ((_keyboard_state[i] & 0x80) != 0) {
-                  handle_keyrelease(lookup_key(i));
+                  handle_keyrelease(lookup_key(i), message_time);
                   _keyboard_state[i] &= ~0x80;
                 }
               }
@@ -1135,6 +1143,7 @@ resend_lost_keypresses() {
   BYTE new_keyboard_state[num_virtual_keys];
   GetKeyboardState(new_keyboard_state);
 
+  double message_time = get_message_time();
   for (int i = 0; i < num_virtual_keys; i++) {
     // Filter out these particular three.  We don't want to test
     // these, because these are virtual duplicates for
@@ -1152,14 +1161,14 @@ resend_lost_keypresses() {
               << "key has gone down: " << i << " (" << lookup_key(i) << ")\n";
           }
           
-          handle_keyresume(lookup_key(i));
+          handle_keyresume(lookup_key(i), message_time);
         } else {
           // The key is now released.
           if (windisplay_cat.is_debug()) {
             windisplay_cat.debug()
               << "key has gone up: " << i << " (" << lookup_key(i) << ")\n";
           }
-          handle_keyrelease(lookup_key(i));
+          handle_keyrelease(lookup_key(i), message_time);
         }
       } else {
         // This key is in the same state.

+ 5 - 3
panda/src/windisplay/winGraphicsWindow.h

@@ -85,14 +85,16 @@ private:
 
   static void process_1_event();
 
-  INLINE void handle_keypress(ButtonHandle key, int x, int y);
-  INLINE void handle_keyresume(ButtonHandle key);
-  INLINE void handle_keyrelease(ButtonHandle key);
+  INLINE void handle_keypress(ButtonHandle key, int x, int y, double time);
+  INLINE void handle_keyresume(ButtonHandle key, double time);
+  INLINE void handle_keyrelease(ButtonHandle key, double time);
   ButtonHandle lookup_key(WPARAM wparam) const;
   INLINE int translate_mouse(int pos) const;
   INLINE void set_cursor_in_window();
   INLINE void set_cursor_out_of_window();
 
+  INLINE static double get_message_time();
+
   void resend_lost_keypresses();
   static void update_cursor_window(WinGraphicsWindow *to_window);