diff --git a/direct/src/plugin/p3dCInstance.h b/direct/src/plugin/p3dCInstance.h index 2a52d5a8b0..2e21528c67 100755 --- a/direct/src/plugin/p3dCInstance.h +++ b/direct/src/plugin/p3dCInstance.h @@ -20,6 +20,7 @@ #include "p3d_plugin.h" #include "pvector.h" #include "get_tinyxml.h" +#include "windowHandle.h" #include @@ -28,7 +29,7 @@ class P3DSession; //////////////////////////////////////////////////////////////////// // Class : P3DCInstance // Description : This is an instance of a Panda3D window, as seen in -// the parent-level process. +// the child-level process. //////////////////////////////////////////////////////////////////// class P3DCInstance : public P3D_instance { public: @@ -37,6 +38,9 @@ public: inline int get_instance_id() const; +public: + PT(WindowHandle) _parent_window_handle; + private: P3D_request_ready_func *_func; diff --git a/direct/src/plugin/p3dInstance.cxx b/direct/src/plugin/p3dInstance.cxx index 27e1700676..afe2f3a359 100644 --- a/direct/src/plugin/p3dInstance.cxx +++ b/direct/src/plugin/p3dInstance.cxx @@ -1495,6 +1495,9 @@ handle_notify_request(const string &message) { } else if (message == "authfinished") { // Similarly for the "auth finished" message. auth_finished_main_thread(); + + } else if (message == "keyboardfocus") { + request_keyboard_focus(); } } @@ -1594,6 +1597,19 @@ handle_script_request(const string &operation, P3D_object *object, } } +//////////////////////////////////////////////////////////////////// +// Function: P3DInstance::request_keyboard_focus +// Access: Private +// Description: The Panda window is asking us to manage keyboard +// focus in proxy for it. This is used on Vista, where +// the Panda window may be disallowed from directly +// assigning itself keyboard focus. +//////////////////////////////////////////////////////////////////// +void P3DInstance:: +request_keyboard_focus() { + nout << "request_keyboard_focus\n"; +} + //////////////////////////////////////////////////////////////////// // Function: P3DInstance::make_splash_window // Access: Private diff --git a/direct/src/plugin/p3dInstance.h b/direct/src/plugin/p3dInstance.h index 52fc2b9e5a..2cbdecc6bc 100644 --- a/direct/src/plugin/p3dInstance.h +++ b/direct/src/plugin/p3dInstance.h @@ -162,6 +162,8 @@ private: void handle_script_request(const string &operation, P3D_object *object, const string &property_name, P3D_object *value, bool needs_response, int unique_id); + void request_keyboard_focus(); + void make_splash_window(); void set_background_image(ImageType image_type); void set_button_image(ImageType image_type); diff --git a/direct/src/plugin/p3dPythonRun.cxx b/direct/src/plugin/p3dPythonRun.cxx index 769d2e2e7c..20dc155ff9 100755 --- a/direct/src/plugin/p3dPythonRun.cxx +++ b/direct/src/plugin/p3dPythonRun.cxx @@ -29,6 +29,8 @@ IMPORT_THIS struct Dtool_PyTypedObject Dtool_WindowHandle; // instances of this thing. P3DPythonRun *P3DPythonRun::_global_ptr = NULL; +TypeHandle P3DPythonRun::P3DWindowHandle::_type_handle; + //////////////////////////////////////////////////////////////////// // Function: P3DPythonRun::Constructor // Access: Public @@ -38,6 +40,8 @@ P3DPythonRun:: P3DPythonRun(const char *program_name, const char *archive_file, FHandle input_handle, FHandle output_handle, const char *log_pathname, bool interactive_console) { + P3DWindowHandle::init_type(); + _read_thread_continue = false; _program_continue = true; _session_terminated = false; @@ -112,6 +116,8 @@ P3DPythonRun(const char *program_name, const char *archive_file, //////////////////////////////////////////////////////////////////// P3DPythonRun:: ~P3DPythonRun() { + nassertv(_instances.empty()); + // Close the write pipe, so the parent process will terminate us. _pipe_write.close(); @@ -380,6 +386,35 @@ run_python() { return true; } +//////////////////////////////////////////////////////////////////// +// Function: P3DPythonRun::request_keyboard_focus +// Access: Public +// Description: Called from low-level Panda (via the P3DWindowHandle +// object) when its main window requires keyboard focus, +// but is unable to assign it directly. This is +// particularly necessary under Windows Vista, where a +// child window of the browser is specifically +// disallowed from being given keyboard focus. +// +// This sends a notify request up to the parent process, +// to ask the parent to manage keyboard events by proxy, +// and send them back down to Panda, again via the +// P3DWindowHandle. +//////////////////////////////////////////////////////////////////// +void P3DPythonRun:: +request_keyboard_focus(P3DCInstance *inst) { + cerr << "requesting keyboard focus\n"; + + TiXmlDocument doc; + TiXmlElement *xrequest = new TiXmlElement("request"); + xrequest->SetAttribute("instance_id", inst->get_instance_id()); + xrequest->SetAttribute("rtype", "notify"); + xrequest->SetAttribute("message", "keyboardfocus"); + doc.LinkEndChild(xrequest); + + write_xml(_pipe_write, &doc, nout); +} + //////////////////////////////////////////////////////////////////// // Function: P3DPythonRun::run_interactive_console // Access: Private @@ -1299,6 +1334,15 @@ setup_window(P3DCInstance *inst, TiXmlElement *xwparams) { PyObject *py_handle = Py_None; if (parent_window_handle != NULL) { + + // We have a valid parent WindowHandle, but replace it with a + // P3DWindowHandle so we can get the callbacks. + parent_window_handle = new P3DWindowHandle(this, inst, *parent_window_handle); + inst->_parent_window_handle = parent_window_handle; + + // Also pass this P3DWindowHandle object down into Panda, via the + // setupWindow() call. For this, we need to create a Python + // wrapper objcet. parent_window_handle->ref(); py_handle = DTool_CreatePyInstanceTyped(parent_window_handle, Dtool_WindowHandle, true, false, parent_window_handle->get_type_index()); } @@ -1666,3 +1710,29 @@ rt_thread_run() { RELEASE_LOCK(_commands_lock); } } + +//////////////////////////////////////////////////////////////////// +// Function: P3DPythonRun::P3DWindowHandle::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +P3DPythonRun::P3DWindowHandle:: +P3DWindowHandle(P3DPythonRun *p3dpython, P3DCInstance *inst, + const WindowHandle ©) : + WindowHandle(copy.get_os_handle()), + _p3dpython(p3dpython), + _inst(inst) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: P3DPythonRun::P3DWindowHandle::request_keyboard_focus +// Access: Public, Virtual +// Description: Called on a parent handle to indicate a child +// window's wish to receive keyboard button events. +//////////////////////////////////////////////////////////////////// +void P3DPythonRun::P3DWindowHandle:: +request_keyboard_focus(WindowHandle *child) { + WindowHandle::request_keyboard_focus(child); + _p3dpython->request_keyboard_focus(_inst); +} diff --git a/direct/src/plugin/p3dPythonRun.h b/direct/src/plugin/p3dPythonRun.h index 02331386fd..7e23c40b31 100755 --- a/direct/src/plugin/p3dPythonRun.h +++ b/direct/src/plugin/p3dPythonRun.h @@ -74,6 +74,8 @@ public: bool run_python(); + void request_keyboard_focus(P3DCInstance *inst); + private: void run_interactive_console(); void handle_command(TiXmlDocument *doc); @@ -105,6 +107,42 @@ private: TiXmlElement *pyobj_to_xml(PyObject *value); PyObject *xml_to_pyobj(TiXmlElement *xvalue); +private: + // This subclass of P3DWindowHandle is associated with the parent + // window we are given by the parent process. We use it to add + // hooks for communicating with the parent window, for instance to + // ask for the parent window to manage keyboard focus when + // necessary. + class P3DWindowHandle : public WindowHandle { + public: + P3DWindowHandle(P3DPythonRun *p3dpython, P3DCInstance *inst, + const WindowHandle ©); + + private: + P3DPythonRun *_p3dpython; + P3DCInstance *_inst; + + protected: + virtual void request_keyboard_focus(WindowHandle *child); + + public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + WindowHandle::init_type(); + register_type(_type_handle, "P3DWindowHandle", + WindowHandle::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + + private: + static TypeHandle _type_handle; + }; + private: // This method runs only within the read thread. THREAD_CALLBACK_DECLARATION(P3DPythonRun, rt_thread_run); diff --git a/panda/src/display/graphicsWindow.cxx b/panda/src/display/graphicsWindow.cxx index 50a1336f52..82661d0566 100644 --- a/panda/src/display/graphicsWindow.cxx +++ b/panda/src/display/graphicsWindow.cxx @@ -749,6 +749,15 @@ void GraphicsWindow:: close_window() { display_cat.info() << "Closing " << get_type() << "\n"; + + // Tell our parent window (if any) that we're no longer its child. + if (_window_handle != (WindowHandle *)NULL && + _parent_window_handle != (WindowHandle *)NULL) { + _parent_window_handle->detach_child(_window_handle); + } + + _window_handle = NULL; + _parent_window_handle = NULL; _is_valid = false; } diff --git a/panda/src/display/graphicsWindow.h b/panda/src/display/graphicsWindow.h index 07b7f4a814..7b21c5c180 100644 --- a/panda/src/display/graphicsWindow.h +++ b/panda/src/display/graphicsWindow.h @@ -132,6 +132,7 @@ protected: protected: WindowProperties _properties; PT(WindowHandle) _window_handle; + PT(WindowHandle) _parent_window_handle; private: LightReMutex _properties_lock; diff --git a/panda/src/display/windowHandle.cxx b/panda/src/display/windowHandle.cxx index 11d4df75c4..0fa64bffcb 100644 --- a/panda/src/display/windowHandle.cxx +++ b/panda/src/display/windowHandle.cxx @@ -26,6 +26,22 @@ WindowHandle:: ~WindowHandle() { } +//////////////////////////////////////////////////////////////////// +// Function: WindowHandle::send_keyboard_event +// Access: Published +// Description: Call this method on a parent WindowHandle to deliver +// a button event to the current child window, if any. +// This is used in the web plugin system to deliver +// button events detected directly by the browser system +// into Panda. +//////////////////////////////////////////////////////////////////// +void WindowHandle:: +send_keyboard_event(const ButtonEvent &event) { + if (_keyboard_window != NULL) { + _keyboard_window->receive_keyboard_event(event); + } +} + //////////////////////////////////////////////////////////////////// // Function: WindowHandle::get_int_handle // Access: Published @@ -57,7 +73,7 @@ output(ostream &out) const { //////////////////////////////////////////////////////////////////// // Function: WindowHandle::attach_child -// Access: Protected, Virtual +// Access: Public, Virtual // Description: Called on a parent handle to indicate a child // window's intention to attach itself. //////////////////////////////////////////////////////////////////// @@ -67,25 +83,37 @@ attach_child(WindowHandle *child) { //////////////////////////////////////////////////////////////////// // Function: WindowHandle::detach_child -// Access: Protected, Virtual +// Access: Public, Virtual // Description: Called on a parent handle to indicate a child // window's intention to detach itself. //////////////////////////////////////////////////////////////////// void WindowHandle:: detach_child(WindowHandle *child) { + if (_keyboard_window == child) { + _keyboard_window = NULL; + } } //////////////////////////////////////////////////////////////////// -// Function: WindowHandle::set_keyboard_focus -// Access: Protected, Virtual +// Function: WindowHandle::request_keyboard_focus +// Access: Public, Virtual // Description: Called on a parent handle to indicate a child -// window's intention to set itself as the recipient of -// keyboard events. +// window's wish to receive keyboard button events. //////////////////////////////////////////////////////////////////// void WindowHandle:: -set_keyboard_focus(WindowHandle *child) { +request_keyboard_focus(WindowHandle *child) { + _keyboard_window = child; } +//////////////////////////////////////////////////////////////////// +// Function: WindowHandle::receive_keyboard_event +// Access: Public, Virtual +// Description: Called on a child handle to deliver a keyboard button +// event generated in the parent window. +//////////////////////////////////////////////////////////////////// +void WindowHandle:: +receive_keyboard_event(const ButtonEvent &event) { +} //////////////////////////////////////////////////////////////////// // Function: WindowHandle::OSHandle::Destructor diff --git a/panda/src/display/windowHandle.h b/panda/src/display/windowHandle.h index c16e276090..10204bdfe0 100644 --- a/panda/src/display/windowHandle.h +++ b/panda/src/display/windowHandle.h @@ -20,6 +20,8 @@ #include "typedReferenceCount.h" #include "pointerTo.h" +class ButtonEvent; + //////////////////////////////////////////////////////////////////// // Class : WindowHandle // Description : This object represents a window on the desktop, not @@ -48,10 +50,21 @@ PUBLISHED: INLINE OSHandle *get_os_handle() const; INLINE void set_os_handle(OSHandle *os_handle); + void send_keyboard_event(const ButtonEvent &event); + size_t get_int_handle() const; void output(ostream &out) const; +public: + // Callbacks for communication with the parent window. + virtual void attach_child(WindowHandle *child); + virtual void detach_child(WindowHandle *child); + + virtual void request_keyboard_focus(WindowHandle *child); + virtual void receive_keyboard_event(const ButtonEvent &event); + +PUBLISHED: // This internal pointer within WindowHandle stores the actual // OS-specific window handle type, whatever type that is. It is // subclassed for each OS. @@ -82,16 +95,11 @@ PUBLISHED: static TypeHandle _type_handle; }; -protected: - // Callbacks for communication with the parent window. - virtual void attach_child(WindowHandle *child); - virtual void detach_child(WindowHandle *child); - - virtual void set_keyboard_focus(WindowHandle *child); - protected: PT(OSHandle) _os_handle; + PT(WindowHandle) _keyboard_window; + public: static TypeHandle get_class_type() { return _type_handle; diff --git a/panda/src/egldisplay/eglGraphicsWindow.cxx b/panda/src/egldisplay/eglGraphicsWindow.cxx index d85c142749..fe96f6d378 100644 --- a/panda/src/egldisplay/eglGraphicsWindow.cxx +++ b/panda/src/egldisplay/eglGraphicsWindow.cxx @@ -685,6 +685,7 @@ open_window() { } } } + _parent_window_handle = window_handle; setup_colormap(visual_info); @@ -774,6 +775,14 @@ open_window() { } } + // Create a WindowHandle for ourselves + _window_handle = NativeWindowHandle::make_x11(_xwindow); + + // And tell our parent window that we're now its child. + if (_parent_window_handle != (WindowHandle *)NULL) { + _parent_window_handle->attach_child(_window_handle); + } + return true; } diff --git a/panda/src/glxdisplay/glxGraphicsWindow.cxx b/panda/src/glxdisplay/glxGraphicsWindow.cxx index e24985378c..9b8416dca6 100644 --- a/panda/src/glxdisplay/glxGraphicsWindow.cxx +++ b/panda/src/glxdisplay/glxGraphicsWindow.cxx @@ -216,6 +216,7 @@ open_window() { } } } + _parent_window_handle = window_handle; #ifdef HAVE_GLXFBCONFIG if (glxgsg->_fbconfig != None) { @@ -302,7 +303,15 @@ open_window() { << "Raw mice not requested.\n"; } } - + + // Create a WindowHandle for ourselves + _window_handle = NativeWindowHandle::make_x11(_xwindow); + + // And tell our parent window that we're now its child. + if (_parent_window_handle != (WindowHandle *)NULL) { + _parent_window_handle->attach_child(_window_handle); + } + return true; } diff --git a/panda/src/tinydisplay/tinyXGraphicsWindow.cxx b/panda/src/tinydisplay/tinyXGraphicsWindow.cxx index 44d8b1aa0a..9265b1c0e7 100644 --- a/panda/src/tinydisplay/tinyXGraphicsWindow.cxx +++ b/panda/src/tinydisplay/tinyXGraphicsWindow.cxx @@ -531,6 +531,7 @@ open_window() { } } } + _parent_window_handle = window_handle; setup_colormap(visual_info); @@ -616,6 +617,14 @@ open_window() { << "Raw mice not requested.\n"; } } + + // Create a WindowHandle for ourselves + _window_handle = NativeWindowHandle::make_x11(_xwindow); + + // And tell our parent window that we're now its child. + if (_parent_window_handle != (WindowHandle *)NULL) { + _parent_window_handle->attach_child(_window_handle); + } return true; } diff --git a/panda/src/x11display/x11GraphicsWindow.cxx b/panda/src/x11display/x11GraphicsWindow.cxx index e5bdca83cb..b7f4f5ea44 100644 --- a/panda/src/x11display/x11GraphicsWindow.cxx +++ b/panda/src/x11display/x11GraphicsWindow.cxx @@ -634,6 +634,7 @@ close_window() { // application, so the server hears the close request. XFlush(_display); } + GraphicsWindow::close_window(); }