diff --git a/modules/juce_gui_basics/juce_gui_basics.cpp b/modules/juce_gui_basics/juce_gui_basics.cpp index d4ea475251..190516ec12 100644 --- a/modules/juce_gui_basics/juce_gui_basics.cpp +++ b/modules/juce_gui_basics/juce_gui_basics.cpp @@ -242,6 +242,7 @@ BEGIN_JUCE_NAMESPACE #include "../juce_graphics/native/juce_mac_CoreGraphicsContext.h" #if JUCE_IOS + #include "native/juce_MultiTouchMapper.h" #include "native/juce_ios_UIViewComponentPeer.mm" #include "native/juce_ios_Windowing.mm" #else @@ -256,6 +257,7 @@ BEGIN_JUCE_NAMESPACE #elif JUCE_WINDOWS #include "../juce_core/native/juce_win32_ComSmartPtr.h" #include "../juce_events/native/juce_win32_HiddenMessageWindow.h" + #include "native/juce_MultiTouchMapper.h" #include "native/juce_win32_Windowing.cpp" #include "native/juce_win32_DragAndDrop.cpp" #include "native/juce_win32_FileChooser.cpp" diff --git a/modules/juce_gui_basics/native/juce_MultiTouchMapper.h b/modules/juce_gui_basics/native/juce_MultiTouchMapper.h new file mode 100644 index 0000000000..122c106408 --- /dev/null +++ b/modules/juce_gui_basics/native/juce_MultiTouchMapper.h @@ -0,0 +1,78 @@ +/* + ============================================================================== + + This file is part of the JUCE library - "Jules' Utility Class Extensions" + Copyright 2004-11 by Raw Material Software Ltd. + + ------------------------------------------------------------------------------ + + JUCE can be redistributed and/or modified under the terms of the GNU General + Public License (Version 2), as published by the Free Software Foundation. + A copy of the license is included in the JUCE distribution, or can be found + online at www.gnu.org/licenses. + + JUCE is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + ------------------------------------------------------------------------------ + + To release a closed-source product which uses JUCE, commercial licenses are + available: visit www.rawmaterialsoftware.com/juce for more information. + + ============================================================================== +*/ + +#ifndef __JUCE_MULTITOUCHMAPPER_JUCEHEADER__ +#define __JUCE_MULTITOUCHMAPPER_JUCEHEADER__ + +template +class MultiTouchMapper +{ +public: + MultiTouchMapper() {} + + int getIndexOfTouch (IDType touchID) + { + jassert (touchID != 0); // need to rethink this if IDs can be 0! + + int touchIndex = currentTouches.indexOf (touchID); + + if (touchIndex < 0) + { + for (touchIndex = 0; touchIndex < currentTouches.size(); ++touchIndex) + if (currentTouches.getUnchecked (touchIndex) == 0) + break; + + currentTouches.set (touchIndex, touchID); + } + + return touchIndex; + } + + void clear() + { + currentTouches.clear(); + } + + void clearTouch (int index) + { + currentTouches.set (index, 0); + } + + bool areAnyTouchesActive() const noexcept + { + for (int i = currentTouches.size(); --i >= 0;) + if (currentTouches.getUnchecked(i) != 0) + return true; + + return false; + } + +private: + Array currentTouches; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MultiTouchMapper); +}; + +#endif // __JUCE_MULTITOUCHMAPPER_JUCEHEADER__ diff --git a/modules/juce_gui_basics/native/juce_ios_UIViewComponentPeer.mm b/modules/juce_gui_basics/native/juce_ios_UIViewComponentPeer.mm index 85fd5297f1..9e7a824c51 100644 --- a/modules/juce_gui_basics/native/juce_ios_UIViewComponentPeer.mm +++ b/modules/juce_gui_basics/native/juce_ios_UIViewComponentPeer.mm @@ -160,7 +160,7 @@ public: + (int64) ([e timestamp] * 1000.0); } - static const Rectangle rotatedScreenPosToReal (const Rectangle& r) + static Rectangle rotatedScreenPosToReal (const Rectangle& r) { const Rectangle screen (convertToRectInt ([[UIScreen mainScreen] bounds])); @@ -187,7 +187,7 @@ public: return r; } - static const Rectangle realScreenPosToRotated (const Rectangle& r) + static Rectangle realScreenPosToRotated (const Rectangle& r) { const Rectangle screen (convertToRectInt ([[UIScreen mainScreen] bounds])); @@ -214,7 +214,7 @@ public: return r; } - Array currentTouches; + MultiTouchMapper currentTouches; private: JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (UIViewComponentPeer); @@ -739,17 +739,7 @@ void UIViewComponentPeer::handleTouches (UIEvent* event, const bool isDown, cons juce_lastMousePos = pos + getScreenPosition(); const int64 time = getMouseTime (event); - - int touchIndex = currentTouches.indexOf (touch); - - if (touchIndex < 0) - { - for (touchIndex = 0; touchIndex < currentTouches.size(); ++touchIndex) - if (currentTouches.getUnchecked (touchIndex) == nil) - break; - - currentTouches.set (touchIndex, touch); - } + const int touchIndex = currentTouches.getIndexOfTouch (touch); ModifierKeys modsToSend (currentModifiers); @@ -772,14 +762,9 @@ void UIViewComponentPeer::handleTouches (UIEvent* event, const bool isDown, cons continue; modsToSend = modsToSend.withoutMouseButtons(); - currentTouches.set (touchIndex, nil); + currentTouches.clearTouch (touchIndex); - int totalActiveTouches = 0; - for (int j = currentTouches.size(); --j >= 0;) - if (currentTouches.getUnchecked(j) != nil) - ++totalActiveTouches; - - if (totalActiveTouches == 0) + if (! currentTouches.areAnyTouchesActive()) isCancel = true; } diff --git a/modules/juce_gui_basics/native/juce_win32_Windowing.cpp b/modules/juce_gui_basics/native/juce_win32_Windowing.cpp index 731cad216f..4512ea7358 100644 --- a/modules/juce_gui_basics/native/juce_win32_Windowing.cpp +++ b/modules/juce_gui_basics/native/juce_win32_Windowing.cpp @@ -46,7 +46,7 @@ static bool shouldDeactivateTitleBar = true; //============================================================================== typedef BOOL (WINAPI* UpdateLayeredWinFunc) (HWND, HDC, POINT*, SIZE*, HDC, POINT*, COLORREF, BLENDFUNCTION*, DWORD); -static UpdateLayeredWinFunc updateLayeredWindow = 0; +static UpdateLayeredWinFunc updateLayeredWindow = nullptr; bool Desktop::canUseSemiTransparentWindows() noexcept { @@ -55,6 +55,7 @@ bool Desktop::canUseSemiTransparentWindows() noexcept if (! juce_IsRunningInWine()) { HMODULE user32Mod = GetModuleHandle (_T("user32.dll")); + jassert (user32Mod != 0); updateLayeredWindow = (UpdateLayeredWinFunc) GetProcAddress (user32Mod, "UpdateLayeredWindow"); } } @@ -62,6 +63,59 @@ bool Desktop::canUseSemiTransparentWindows() noexcept return updateLayeredWindow != 0; } +//============================================================================== +#ifndef WM_TOUCH + #define WM_TOUCH 0x0240 + DECLARE_HANDLE (HTOUCHINPUT); + + typedef struct tagTOUCHINPUT + { + LONG x; + LONG y; + HANDLE hSource; + DWORD dwID; + DWORD dwFlags; + DWORD dwMask; + DWORD dwTime; + ULONG_PTR dwExtraInfo; + DWORD cxContact; + DWORD cyContact; + } TOUCHINPUT, *PTOUCHINPUT; + + #define TOUCH_COORD_TO_PIXEL(l) ((l) / 100) + + #define TOUCHEVENTF_MOVE 0x0001 + #define TOUCHEVENTF_DOWN 0x0002 + #define TOUCHEVENTF_UP 0x0004 +#endif + +typedef BOOL (WINAPI* RegisterTouchWindowFunc) (HWND, ULONG); +typedef BOOL (WINAPI* GetTouchInputInfoFunc) (HTOUCHINPUT, UINT, PTOUCHINPUT, int); +typedef BOOL (WINAPI* CloseTouchInputHandleFunc) (HTOUCHINPUT); + +static RegisterTouchWindowFunc registerTouchWindow = nullptr; +static GetTouchInputInfoFunc getTouchInputInfo = nullptr; +static CloseTouchInputHandleFunc closeTouchInputHandle = nullptr; +static bool hasCheckedForMultiTouch = false; + +static bool canUseMultiTouch() +{ + if (registerTouchWindow == nullptr && ! hasCheckedForMultiTouch) + { + hasCheckedForMultiTouch = true; + + HMODULE user32Mod = GetModuleHandle (_T("user32.dll")); + jassert (user32Mod != 0); + + registerTouchWindow = (RegisterTouchWindowFunc) GetProcAddress (user32Mod, "RegisterTouchWindow"); + getTouchInputInfo = (GetTouchInputInfoFunc) GetProcAddress (user32Mod, "GetTouchInputInfo"); + closeTouchInputHandle = (CloseTouchInputHandleFunc) GetProcAddress (user32Mod, "CloseTouchInputHandle"); + } + + return registerTouchWindow != nullptr; +} + +//============================================================================== Desktop::DisplayOrientation Desktop::getCurrentOrientation() const { return upright; @@ -864,6 +918,7 @@ private: HICON currentWindowIcon; IDropTarget* dropTarget; uint8 updateLayeredWindowAlpha; + MultiTouchMapper currentTouches; //============================================================================== class TemporaryImage : public Timer @@ -981,6 +1036,7 @@ private: case WM_MOUSEACTIVATE: case WM_NCMOUSEHOVER: case WM_MOUSEHOVER: + case WM_TOUCH: return isHWNDBlockedByModalComponents (m.hwnd); case WM_NCLBUTTONDOWN: @@ -1086,6 +1142,9 @@ private: RegisterDragDrop (hwnd, dropTarget); + if (canUseMultiTouch()) + registerTouchWindow (hwnd, 0); + updateBorderSize(); // Calling this function here is (for some reason) necessary to make Windows @@ -1472,6 +1531,75 @@ private: isVertical ? amount : 0.0f); } + void doTouchEvent (const int numInputs, HTOUCHINPUT eventHandle) + { + HeapBlock inputInfo (numInputs); + + if (getTouchInputInfo (eventHandle, numInputs, inputInfo, sizeof (TOUCHINPUT))) + { + for (int i = 0; i < numInputs; ++i) + { + const DWORD flags = inputInfo[i].dwFlags; + + if ((flags & (TOUCHEVENTF_DOWN | TOUCHEVENTF_MOVE | TOUCHEVENTF_UP)) != 0) + handleTouchInput (inputInfo[i], (flags & TOUCHEVENTF_DOWN) != 0, + (flags & TOUCHEVENTF_UP) != 0, + false); + } + } + + closeTouchInputHandle (eventHandle); + } + + void handleTouchInput (const TOUCHINPUT& touch, const bool isDown, const bool isUp, bool isCancel) + { + POINT p = { TOUCH_COORD_TO_PIXEL (touch.x), + TOUCH_COORD_TO_PIXEL (touch.y) }; + ScreenToClient (hwnd, &p); + + const Point pos ((int) p.x, (int) p.y); + const int64 time = getMouseEventTime(); + const int touchIndex = currentTouches.getIndexOfTouch (touch.dwID); + + ModifierKeys modsToSend (currentModifiers); + + if (isDown) + { + currentModifiers = currentModifiers.withoutMouseButtons().withFlags (ModifierKeys::leftButtonModifier); + modsToSend = currentModifiers; + + // this forces a mouse-enter/up event, in case for some reason we didn't get a mouse-up before. + handleMouseEvent (touchIndex + 1, pos, modsToSend.withoutMouseButtons(), time); + if (! isValidPeer (this)) // (in case this component was deleted by the event) + return; + } + else if (isUp) + { + modsToSend = modsToSend.withoutMouseButtons(); + currentTouches.clearTouch (touchIndex); + + if (! currentTouches.areAnyTouchesActive()) + isCancel = true; + } + + if (isCancel) + { + currentTouches.clear(); + currentModifiers = currentModifiers.withoutMouseButtons(); + } + + handleMouseEvent (touchIndex + 1, pos, modsToSend, time); + if (! isValidPeer (this)) // (in case this component was deleted by the event) + return; + + if (isUp || isCancel) + { + handleMouseEvent (touchIndex + 1, Point (-1, -1), currentModifiers, time); + if (! isValidPeer (this)) + return; + } + } + //============================================================================== void sendModifierKeyChangeIfNeeded() { @@ -2008,6 +2136,13 @@ private: doMouseWheel (getCurrentMousePosGlobal(), wParam, message == 0x020A); return 0; + case WM_TOUCH: + if (getTouchInputInfo == nullptr) + break; + + doTouchEvent ((int) wParam, (HTOUCHINPUT) lParam); + return 0; + //============================================================================== case WM_SIZING: return handleSizeConstraining ((RECT*) lParam, wParam); case WM_WINDOWPOSCHANGING: return handlePositionChanging ((WINDOWPOS*) lParam); @@ -2680,6 +2815,10 @@ int JUCE_CALLTYPE NativeMessageBox::showYesNoCancelBox (AlertWindow::AlertIconTy void Desktop::createMouseInputSources() { mouseSources.add (new MouseInputSource (0, true)); + + if (canUseMultiTouch()) + for (int i = 1; i <= 10; ++i) + mouseSources.add (new MouseInputSource (i, false)); } Point MouseInputSource::getCurrentMousePosition()