Bladeren bron

Added systemOpenFile() function, which will open a file in an external program.
Added non-confined mouse cursor mode.
Added Input functionality, such as setting the operating system cursor position manually.

Lasse Öörni 15 jaren geleden
bovenliggende
commit
1eb8498291

+ 20 - 0
Engine/Common/File.cpp

@@ -30,6 +30,7 @@
 #include <cstdlib>
 #include <direct.h>
 #include <windows.h>
+#include <shellapi.h>
 
 #include "DebugNew.h"
 
@@ -265,6 +266,25 @@ int systemCommand(const std::string& commandLine)
     }
 }
 
+bool systemOpenFile(const std::string& fileName, const std::string& mode)
+{
+    if (allowedDirectories.empty())
+    {
+        if ((!fileExists(fileName)) && (!directoryExists(fileName)))
+        {
+            LOGERROR("File or directory " + fileName + " not found");
+            return false;
+        }
+        
+        return (int)ShellExecute(0, !mode.empty() ? (char*)mode.c_str() : 0, (char*)getOSPath(fileName, true).c_str(), 0, 0, SW_SHOW) > 32;
+    }
+    else
+    {
+        LOGERROR("Opening a file externally is not allowed");
+        return false;
+    }
+}
+
 void registerDirectory(const std::string& pathName)
 {
     if (pathName.empty())

+ 2 - 0
Engine/Common/File.h

@@ -107,6 +107,8 @@ bool setCurrentDirectory(const std::string& pathName);
 bool createDirectory(const std::string& pathName);
 //! Execute an external command, block until it exists and return the exit code. Will fail if any allowed paths are defined
 int systemCommand(const std::string& commandLine);
+//! Open a file in an external program, with mode such as "edit" optionally specified. Will fail if any allowed paths are defined
+bool systemOpenFile(const std::string& fileName, const std::string& mode = std::string());
 //! Register a path as being allowed to access
 void registerDirectory(const std::string& pathName);
 //! Check if a path is allowed to be accessed. If no paths defined, all are allowed

+ 1 - 0
Engine/Engine/RegisterCommon.cpp

@@ -601,6 +601,7 @@ static void registerSerialization(asIScriptEngine* engine)
     engine->RegisterGlobalFunction("void setCurrentDirectory(const string& in)", asFUNCTION(setCurrentDirectory), asCALL_CDECL);
     engine->RegisterGlobalFunction("bool createDirectory(const string& in)", asFUNCTION(createDirectory), asCALL_CDECL);
     engine->RegisterGlobalFunction("int systemCommand(const string& in)", asFUNCTION(systemCommand), asCALL_CDECL);
+    engine->RegisterGlobalFunction("bool systemOpenFile(const string& in, const string& in)", asFUNCTION(systemOpenFile), asCALL_CDECL);
     engine->RegisterGlobalFunction("string getPath(const string& in)", asFUNCTION(getPath), asCALL_CDECL);
     engine->RegisterGlobalFunction("string getFileName(const string& in)", asFUNCTION(getFileName), asCALL_CDECL);
     engine->RegisterGlobalFunction("string getExtension(const string& in, bool)", asFUNCTION(getExtension), asCALL_CDECL);

+ 6 - 0
Engine/Engine/RegisterInput.cpp

@@ -195,7 +195,10 @@ static void registerInput(asIScriptEngine* engine)
     engine->RegisterObjectType("Input", 0, asOBJ_REF);
     engine->RegisterObjectBehaviour("Input", asBEHAVE_ADDREF, "void f()", asMETHOD(Input, addRef), asCALL_THISCALL);
     engine->RegisterObjectBehaviour("Input", asBEHAVE_RELEASE, "void f()", asMETHOD(Input, releaseRef), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Input", "void setClipCursor(bool)", asMETHOD(Input, setClipCursor), asCALL_THISCALL);
     engine->RegisterObjectMethod("Input", "void setToggleFullscreen(bool)", asMETHOD(Input, setToggleFullscreen), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Input", "void setMousePosition(const IntVector2& in)", asMETHODPR(Input, setMousePosition, (const IntVector2&), void), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Input", "void setMousePosition(int, int)", asMETHODPR(Input, setMousePosition, (int, int), void), asCALL_THISCALL);
     engine->RegisterObjectMethod("Input", "void suppressNextChar()", asMETHOD(Input, suppressNextChar), asCALL_THISCALL);
     engine->RegisterObjectMethod("Input", "bool getKeyDown(int) const", asMETHOD(Input, getKeyDown), asCALL_THISCALL);
     engine->RegisterObjectMethod("Input", "bool getKeyPress(int) const", asMETHOD(Input, getKeyPress), asCALL_THISCALL);
@@ -204,9 +207,12 @@ static void registerInput(asIScriptEngine* engine)
     engine->RegisterObjectMethod("Input", "bool getQualifierDown(int) const", asMETHOD(Input, getQualifierDown), asCALL_THISCALL);
     engine->RegisterObjectMethod("Input", "bool getQualifierPress(int) const", asMETHOD(Input, getQualifierPress), asCALL_THISCALL);
     engine->RegisterObjectMethod("Input", "int getQualifiers() const", asMETHOD(Input, getQualifiers), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Input", "IntVector2 getMousePosition() const", asMETHOD(Input, getMousePosition), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Input", "const IntVector2& getMouseMove() const", asMETHOD(Input, getMouseMove), asCALL_THISCALL);
     engine->RegisterObjectMethod("Input", "int getMouseMoveX() const", asMETHOD(Input, getMouseMoveX), asCALL_THISCALL);
     engine->RegisterObjectMethod("Input", "int getMouseMoveY() const", asMETHOD(Input, getMouseMoveY), asCALL_THISCALL);
     engine->RegisterObjectMethod("Input", "int getMouseMoveWheel() const", asMETHOD(Input, getMouseMoveWheel), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Input", "bool getClipCursor() const", asMETHOD(Input, getClipCursor), asCALL_THISCALL);
     engine->RegisterObjectMethod("Input", "bool getToggleFullscreen() const", asMETHOD(Input, getToggleFullscreen), asCALL_THISCALL);
     engine->RegisterObjectMethod("Input", "bool isActive() const", asMETHOD(Input, isActive), asCALL_THISCALL);
     engine->RegisterObjectMethod("Input", "bool isMinimized() const", asMETHOD(Input, isMinimized), asCALL_THISCALL);

+ 101 - 55
Engine/Input/Input.cpp

@@ -34,10 +34,11 @@
 
 Input::Input(Renderer* renderer) :
     mRenderer(renderer),
+    mClipCursor(true),
     mToggleFullscreen(true),
     mActive(false),
     mMinimized(false),
-    mActivated(false),
+    mActivated(true),
     mSuppressNextChar(false)
 {
     LOGINFO("Input created");
@@ -47,11 +48,13 @@ Input::Input(Renderer* renderer) :
     memset(&mKeyPress, 0, sizeof(mKeyPress));
     mMouseButtonDown = 0;
     mMouseButtonPress = 0;
-    
-    makeActive();
+    mLastMousePosition = IntVector2::sZero;
     
     if (mRenderer)
         subscribeToEvent(mRenderer, EVENT_WINDOWMESSAGE, EVENT_HANDLER(Input, handleWindowMessage));
+    
+    // Perform the initial update immediately so that the mouse cursor gets hidden
+    update();
 }
 
 Input::~Input()
@@ -76,57 +79,90 @@ void Input::update()
     
     if (mActive)
     {
-        if (mRenderer->getFullscreen())
+        IntVector2 mousePos = getMousePosition();
+        
+        if (mClipCursor)
         {
-            POINT mouse;
-            GetCursorPos(&mouse);
-            mMouseMoveX = mouse.x - mRenderer->getWidth() / 2;
-            mMouseMoveY = mouse.y - mRenderer->getHeight() / 2;
-            SetCursorPos(mRenderer->getWidth() / 2, mRenderer->getHeight() / 2);
+            IntVector2 center(mRenderer->getWidth() / 2, mRenderer->getHeight() / 2);
+            mMouseMove = mousePos - center;
+            setMousePosition(center);
         }
         else
         {
-            POINT mouse;
-            POINT point;
+            mMouseMove = mousePos - mLastMousePosition;
+            mLastMousePosition = mousePos;
+        }
+        
+        if (mMouseMove != IntVector2::sZero)
+        {
+            if (mClipCursor)
+            {
+                using namespace MouseMove;
+                
+                VariantMap eventData;
+                eventData[P_X] = mMouseMove.mX;
+                eventData[P_Y] = mMouseMove.mY;
+                eventData[P_BUTTONS] = mMouseButtonDown;
+                eventData[P_QUALIFIERS] = getQualifiers();
+                sendEvent(EVENT_MOUSEMOVE, eventData);
+            }
+            else
+            {
+                // Set movement as zero and send only the absolute position
+                mMouseMove = IntVector2::sZero;
+                
+                using namespace MousePos;
+                
+                VariantMap eventData;
+                eventData[P_X] = mousePos.mX;
+                eventData[P_Y] = mousePos.mY;
+                eventData[P_BUTTONS] = mMouseButtonDown;
+                eventData[P_QUALIFIERS] = getQualifiers();
+                sendEvent(EVENT_MOUSEPOS, eventData);
+            }
+        }
+        if (mMouseMoveWheel)
+        {
+            using namespace MouseWheel;
             
-            GetCursorPos(&mouse);
-            point.x = mRenderer->getWidth() / 2;
-            point.y = mRenderer->getHeight() / 2;
-            ClientToScreen((HWND)mRenderer->getWindowHandle(), &point);
-            mMouseMoveX = mouse.x - point.x;
-            mMouseMoveY = mouse.y - point.y;
-            SetCursorPos(point.x, point.y);
+            VariantMap eventData;
+            eventData[P_WHEEL] = mMouseMoveWheel;
+            eventData[P_BUTTONS] = mMouseButtonDown;
+            eventData[P_QUALIFIERS] = getQualifiers();
+            sendEvent(EVENT_MOUSEWHEEL, eventData);
         }
     }
     else
     {
-        mMouseMoveX = 0;
-        mMouseMoveY = 0;
+        mMouseMove = IntVector2::sZero;
         mMouseMoveWheel = 0;
     }
+}
+
+void Input::setClipCursor(bool enable)
+{
+    mClipCursor = enable;
+    if (!mRenderer)
+        return;
     
-    if ((mMouseMoveX) || (mMouseMoveY))
+    if ((!mRenderer->getFullscreen()) && (mActive) && (mClipCursor))
     {
-        using namespace MouseMove;
+        HWND window = (HWND)mRenderer->getWindowHandle();
+        RECT clipRect;
         
-        VariantMap eventData;
-        eventData[P_X] = mMouseMoveX;
-        eventData[P_Y] = mMouseMoveY;
-        eventData[P_BUTTONS] = mMouseButtonDown;
-        eventData[P_QUALIFIERS] = getQualifiers();
-        sendEvent(EVENT_MOUSEMOVE, eventData);
+        setMousePosition(mRenderer->getWidth() / 2, mRenderer->getHeight() / 2);
+        GetWindowRect(window, &clipRect);
+        ClipCursor(&clipRect);
     }
-    if (mMouseMoveWheel)
+    else
     {
-        using namespace MouseWheel;
+        if ((mRenderer->getFullscreen()) && (mActive) && (mClipCursor))
+            setMousePosition(mRenderer->getWidth() / 2, mRenderer->getHeight() / 2);
         
-        VariantMap eventData;
-        eventData[P_WHEEL] = mMouseMoveWheel;
-        eventData[P_BUTTONS] = mMouseButtonDown;
-        eventData[P_QUALIFIERS] = getQualifiers();
-        sendEvent(EVENT_MOUSEWHEEL, eventData);
+        ClipCursor(0);
     }
     
+    mMouseMove = IntVector2::sZero;
 }
 
 void Input::setToggleFullscreen(bool enable)
@@ -134,6 +170,20 @@ void Input::setToggleFullscreen(bool enable)
     mToggleFullscreen = enable;
 }
 
+void Input::setMousePosition(const IntVector2& position)
+{
+    POINT point;
+    point.x = position.mX;
+    point.y = position.mY;
+    ClientToScreen((HWND)mRenderer->getWindowHandle(), &point);
+    SetCursorPos(point.x, point.y);
+}
+
+void Input::setMousePosition(int x, int y)
+{
+    setMousePosition(IntVector2(x, y));
+}
+
 void Input::suppressNextChar()
 {
     mSuppressNextChar = true;
@@ -155,6 +205,14 @@ bool Input::getKeyPress(int key) const
     return mKeyPress[key];
 }
 
+IntVector2 Input::getMousePosition() const
+{
+    POINT mouse;
+    GetCursorPos(&mouse);
+    ScreenToClient((HWND)mRenderer->getWindowHandle(), &mouse);
+    return IntVector2(mouse.x, mouse.y);
+}
+
 bool Input::getMouseButtonDown(int button) const
 {
     return (mMouseButtonDown & button) != 0;
@@ -281,6 +339,8 @@ void Input::handleWindowMessage(StringHash eventType, VariantMap& eventData)
         keyChange(wParam, true);
         if ((wParam == KEY_RETURN) && (mToggleFullscreen))
             mRenderer->toggleFullscreen();
+        if (wParam != KEY_F4)
+            eventData[P_HANDLED] = true;
         break;
         
     case WM_KEYUP:
@@ -290,8 +350,9 @@ void Input::handleWindowMessage(StringHash eventType, VariantMap& eventData)
         
     case WM_SYSKEYUP:
         keyChange(wParam, false);
+        eventData[P_HANDLED] = true;
         break;
-
+        
     case WM_CHAR:
         if (!mSuppressNextChar)
         {
@@ -322,25 +383,11 @@ void Input::makeActive()
     if (!mActive)
         ShowCursor(FALSE);
     
-    if (mRenderer->getFullscreen())
-        SetCursorPos(mRenderer->getWidth() / 2, mRenderer->getHeight() / 2);
-    else
-    {
-        HWND window = (HWND)mRenderer->getWindowHandle();
-        
-        POINT point;
-        point.x = mRenderer->getWidth() / 2;
-        point.y = mRenderer->getHeight() / 2;
-        ClientToScreen(window, &point);
-        SetCursorPos(point.x, point.y);
-        
-        RECT clipRect;
-        GetWindowRect(window, &clipRect);
-        ClipCursor(&clipRect);
-    }
-    
     mActive = true;
     mActivated = false;
+    
+    // Re-establish mouse cursor clipping if necessary
+    setClipCursor(mClipCursor);
 }
 
 void Input::makeInactive()
@@ -371,8 +418,7 @@ void Input::clearState()
     mouseButtonChange(MOUSEB_RIGHT, false);
     mouseButtonChange(MOUSEB_MIDDLE, false);
     
-    mMouseMoveX = 0;
-    mMouseMoveY = 0;
+    mMouseMove = IntVector2::sZero;
     mMouseMoveWheel = 0;
     mMouseButtonPress = 0;
     memset(&mKeyPress, 0, sizeof(mKeyPress));

+ 23 - 9
Engine/Input/Input.h

@@ -41,10 +41,16 @@ public:
     //! Destruct
     virtual ~Input();
     
-    //! Poll for window messages through the Renderer
+    //! Poll for window messages. Called by Engine each frame
     void update();
+    //! Set whether mouse cursor is confined inside the window. Mouse delta movement is sent only when enabled, which is default.
+    void setClipCursor(bool enable);
     //! Set whether ALT-ENTER fullscreen toggle is enabled
     void setToggleFullscreen(bool enable);
+    //! Set absolute mouse cursor position within the window. Only useful when the cursor is not confined
+    void setMousePosition(const IntVector2& position);
+    //! Set absolute mouse cursor position within the window. Only useful when the cursor is not confined
+    void setMousePosition(int x, int y);
     //! Suppress the next char message
     void suppressNextChar();
     
@@ -62,12 +68,18 @@ public:
     bool getQualifierPress(int qualifier) const;
     //! Return the currently held down qualifiers
     int getQualifiers() const;
-    //! Return horizontal mouse movement since last frame
-    int getMouseMoveX() const { return mMouseMoveX; }
-    //! Return vertical mouse movement since last frame
-    int getMouseMoveY() const { return mMouseMoveY; }
+    //! Return absolute mouse cursor position within the window. Only useful when the cursor is not confined
+    IntVector2 getMousePosition() const;
+    //! Return mouse movement since last frame. When mouse is not confined, returns always zero
+    const IntVector2& getMouseMove() const { return mMouseMove; }
+    //! Return horizontal mouse movement since last frame. When mouse is not confined, returns always zero
+    int getMouseMoveX() const { return mMouseMove.mX; }
+    //! Return vertical mouse movement since last frame. When mouse is not confined, returns always zero
+    int getMouseMoveY() const { return mMouseMove.mY; }
     //! Return mouse wheel movement since last frame
     int getMouseMoveWheel() const { return mMouseMoveWheel; }
+    //! Return whether mouse cursor is confined inside the window
+    bool getClipCursor() const { return mClipCursor; }
     //! Return whether fullscreen toggle is enabled
     bool getToggleFullscreen() const { return mToggleFullscreen; }
     //! Return whether application window is active
@@ -99,12 +111,14 @@ private:
     unsigned mMouseButtonDown;
     //! Mouse buttons' pressed state
     unsigned mMouseButtonPress;
-    //! Horizontal mouse movement since last frame
-    int mMouseMoveX;
-    //! Vertical mouse movement since last frame
-    int mMouseMoveY;
+    //! Last mouse position for non-confined mode
+    IntVector2 mLastMousePosition;
+    //! Mouse movement since last frame
+    IntVector2 mMouseMove;
     //! Mouse wheel movement since last frame
     int mMouseMoveWheel;
+    //! Mouse cursor confine flag
+    bool mClipCursor;
     //! Fullscreen toggle flag
     bool mToggleFullscreen;
     //! Active flag

+ 9 - 0
Engine/Input/InputEvents.h

@@ -51,6 +51,15 @@ DEFINE_EVENT(EVENT_MOUSEMOVE, MouseMove)
     EVENT_PARAM(P_QUALIFIERS, Qualifiers);      // int
 }
 
+//! Mouse moved when in non-confined mode
+DEFINE_EVENT(EVENT_MOUSEPOS, MousePos)
+{
+    EVENT_PARAM(P_X, X);                        // int
+    EVENT_PARAM(P_Y, Y);                        // int
+    EVENT_PARAM(P_BUTTONS, Buttons);            // int
+    EVENT_PARAM(P_QUALIFIERS, Qualifiers);      // int
+}
+
 //! Mouse wheel moved
 DEFINE_EVENT(EVENT_MOUSEWHEEL, MouseWheel)
 {

+ 22 - 8
Engine/UI/UI.cpp

@@ -66,6 +66,7 @@ UI::UI(Renderer* renderer, ResourceCache* cache) :
     mRootElement->setSize(mRenderer->getWidth(), mRenderer->getHeight());
     subscribeToEvent(EVENT_WINDOWRESIZED, EVENT_HANDLER(UI, handleWindowResized));
     subscribeToEvent(EVENT_MOUSEMOVE, EVENT_HANDLER(UI, handleMouseMove));
+    subscribeToEvent(EVENT_MOUSEPOS, EVENT_HANDLER(UI, handleMouseMove));
     subscribeToEvent(EVENT_MOUSEBUTTONDOWN, EVENT_HANDLER(UI, handleMouseButtonDown));
     subscribeToEvent(EVENT_MOUSEBUTTONUP, EVENT_HANDLER(UI, handleMouseButtonUp));
     subscribeToEvent(EVENT_MOUSEWHEEL, EVENT_HANDLER(UI, handleMouseWheel));
@@ -469,18 +470,31 @@ void UI::handleMouseMove(StringHash eventType, VariantMap& eventData)
     mMouseButtons = eventData[P_BUTTONS].getInt();
     mQualifiers = eventData[P_QUALIFIERS].getInt();
     
-    if ((mCursor) && (mCursor->isVisible()))
+    if (mCursor)
     {
-        IntVector2 pos = mCursor->getPosition();
-        pos.mX += eventData[P_X].getInt();
-        pos.mY += eventData[P_Y].getInt();
-        const IntVector2& rootSize = mRootElement->getSize();
-        pos.mX = clamp(pos.mX, 0, rootSize.mX - 1);
-        pos.mY = clamp(pos.mY, 0, rootSize.mY - 1);
-        mCursor->setPosition(pos);
+        if (eventType == EVENT_MOUSEMOVE)
+        {
+            // When deltas are sent, move the cursor only when visible
+            if (mCursor->isVisible())
+            {
+                IntVector2 pos = mCursor->getPosition();
+                pos.mX += eventData[P_X].getInt();
+                pos.mY += eventData[P_Y].getInt();
+                const IntVector2& rootSize = mRootElement->getSize();
+                pos.mX = clamp(pos.mX, 0, rootSize.mX - 1);
+                pos.mY = clamp(pos.mY, 0, rootSize.mY - 1);
+                mCursor->setPosition(pos);
+            }
+        }
+        else
+        {
+            // When absolute positions are sent, the cursor is not confined, so do not clamp the on-screen cursor's position
+            mCursor->setPosition(eventData[P_X].getInt(), eventData[P_Y].getInt());
+        }
         
         if ((mMouseDragElement) && (mMouseButtons))
         {
+            IntVector2 pos = mCursor->getPosition();
             if ((mMouseDragElement->isEnabled()) && (mMouseDragElement->isVisible()))
                 mMouseDragElement->onDragMove(mMouseDragElement->screenToElement(pos), pos, mMouseButtons, mQualifiers, mCursor);
             else