Parcourir la source

Fixed multiple instance SDL event handling.

Lasse Öörni il y a 12 ans
Parent
commit
14fc3cbe5c
3 fichiers modifiés avec 100 ajouts et 9 suppressions
  1. 4 0
      Engine/IO/Log.cpp
  2. 91 9
      Engine/Input/Input.cpp
  3. 5 0
      Engine/Input/Input.h

+ 4 - 0
Engine/IO/Log.cpp

@@ -70,6 +70,10 @@ Log::Log(Context* context) :
     Object(context)
 {
     MutexLock lock(GetStaticMutex());
+#ifdef ANDROID
+    // On Android we may have instances from a previous run
+    logInstances.Clear();
+#endif
     logInstances.Push(this);
 }
 

+ 91 - 9
Engine/Input/Input.cpp

@@ -54,10 +54,13 @@ int ConvertSDLKeyCode(int keySym, int scanCode)
         return SDL_toupper(keySym);
 }
 
+static PODVector<Input*> inputInstances;
+
 OBJECTTYPESTATIC(Input);
 
 Input::Input(Context* context) :
     Object(context),
+    eventQueue_(new PODVector<SDL_Event>()),
     mouseButtonDown_(0),
     mouseButtonPress_(0),
     mouseMoveWheel_(0),
@@ -70,6 +73,15 @@ Input::Input(Context* context) :
     suppressNextMouseMove_(false),
     initialized_(false)
 {
+    {
+        MutexLock lock(GetStaticMutex());
+#ifdef ANDROID
+        // On Android we may have instances from a previous run
+        inputInstances.Clear();
+#endif
+        inputInstances.Push(this);
+    }
+    
     SubscribeToEvent(E_SCREENMODE, HANDLER(Input, HandleScreenMode));
     
     // Try to initialize right now, but skip if screen mode is not yet set
@@ -78,6 +90,12 @@ Input::Input(Context* context) :
 
 Input::~Input()
 {
+    PODVector<SDL_Event>* eventQueue = reinterpret_cast<PODVector<SDL_Event>* >(eventQueue_);
+    delete eventQueue;
+    eventQueue_ = 0;
+    
+    MutexLock lock(GetStaticMutex());
+    inputInstances.Remove(this);
 }
 
 void Input::Update()
@@ -105,24 +123,88 @@ void Input::Update()
         state.delta_ = IntVector2::ZERO;
     }
     
+    // Check for SDL events
     {
         MutexLock lock(GetStaticMutex());
         
-        // Pump SDL events
-        /// \todo This does not handle multiple input instances properly. Each instance will need its own event queue,
-        /// where SDL events are copied and which it handles in its own main thread
         SDL_Event evt;
-        SDL_PumpEvents();
         while (SDL_PollEvent(&evt))
-            HandleSDLEvent(&evt);
+        {
+            // If only one instance, can handle event directly. Otherwise put to the correct input instance's event queue
+            if (inputInstances.Size() > 1)
+            {
+                for (PODVector<Input*>::Iterator i = inputInstances.Begin(); i != inputInstances.End(); ++i)
+                {
+                    bool storeEvent = false;
+                    
+                    switch (evt.type)
+                    {
+                    case SDL_KEYDOWN:
+                    case SDL_KEYUP:
+                        storeEvent = evt.key.windowID == (*i)->windowID_;
+                        break;
+                        
+                    case SDL_TEXTINPUT:
+                        storeEvent = evt.text.windowID == (*i)->windowID_;
+                    
+                    case SDL_MOUSEBUTTONDOWN:
+                    case SDL_MOUSEBUTTONUP:
+                        storeEvent = evt.button.windowID == (*i)->windowID_;
+                        break;
+                        
+                    case SDL_MOUSEWHEEL:
+                        storeEvent = evt.wheel.windowID == (*i)->windowID_;
+                        break;
+                        
+                    // Joystick and touch events do not have a windowID. Store depending on input focus
+                    case SDL_JOYBUTTONDOWN:
+                    case SDL_JOYBUTTONUP:
+                    case SDL_JOYAXISMOTION:
+                    case SDL_JOYHATMOTION:
+                    case SDL_FINGERDOWN:
+                    case SDL_FINGERUP:
+                    case SDL_FINGERMOTION:
+                        storeEvent = (*i)->inputFocus_;
+                        break;
+                        
+                    case SDL_WINDOWEVENT:
+                        storeEvent = evt.window.windowID == (*i)->windowID_;
+                        break;
+                    }
+                    
+                    if (storeEvent)
+                    {
+                        (*i)->eventQueueMutex_.Acquire();
+                        PODVector<SDL_Event>* eventQueue = reinterpret_cast<PODVector<SDL_Event>* >((*i)->eventQueue_);
+                        eventQueue->Push(evt);
+                        (*i)->eventQueueMutex_.Release();
+                    }
+                }
+            }
+            else
+                HandleSDLEvent(&evt);
+        }
+    }
+    
+    // Handle own event queue now if necessary
+    if (inputInstances.Size() > 1)
+    {
+        eventQueueMutex_.Acquire();
+        
+        PODVector<SDL_Event>* eventQueue = reinterpret_cast<PODVector<SDL_Event>* >(eventQueue_);
+        for (PODVector<SDL_Event>::Iterator i = eventQueue->Begin(); i != eventQueue->End(); ++i)
+            HandleSDLEvent(&(*i));
+        eventQueue->Clear();
+        
+        eventQueueMutex_.Release();
     }
     
     // Check for activation and inactivation from SDL window flags. Must nullcheck the window pointer because it may have
     // been closed due to input events
     SDL_Window* window = graphics_->GetImpl()->GetWindow();
+    unsigned flags = window ? SDL_GetWindowFlags(window) & (SDL_WINDOW_INPUT_FOCUS | SDL_WINDOW_MOUSE_FOCUS) : 0;
     if (window)
     {
-        unsigned flags = SDL_GetWindowFlags(window) & (SDL_WINDOW_INPUT_FOCUS | SDL_WINDOW_MOUSE_FOCUS);
 #ifdef REQUIRE_CLICK_TO_FOCUS
         if (!inputFocus_ && (graphics_->GetFullscreen() || mouseVisible_) && flags == (SDL_WINDOW_INPUT_FOCUS |
             SDL_WINDOW_MOUSE_FOCUS))
@@ -130,12 +212,12 @@ void Input::Update()
         if (!inputFocus_ && (flags & SDL_WINDOW_INPUT_FOCUS))
 #endif
             focusedThisFrame_ = true;
-        else if (inputFocus_ && (flags & SDL_WINDOW_INPUT_FOCUS) == 0)
-            LoseFocus();
         
-        // Activate input now if necessary
         if (focusedThisFrame_)
             GainFocus();
+        
+        if (inputFocus_ && (flags & SDL_WINDOW_INPUT_FOCUS) == 0)
+            LoseFocus();
     }
     else
         return;

+ 5 - 0
Engine/Input/Input.h

@@ -24,6 +24,7 @@
 
 #include "HashSet.h"
 #include "InputEvents.h"
+#include "Mutex.h"
 #include "Object.h"
 
 namespace Urho3D
@@ -217,6 +218,10 @@ private:
     HashMap<int, TouchState> touches_;
     /// Opened joysticks.
     Vector<JoystickState> joysticks_;
+    /// Per-instance SDL event queue.
+    void* eventQueue_;
+    /// Per-instance SDL event queue mutex.
+    Mutex eventQueueMutex_;
     /// Mouse buttons' down state.
     unsigned mouseButtonDown_;
     /// Mouse buttons' pressed state.