mirror of
https://github.com/aseprite/aseprite.git
synced 2025-03-28 05:37:19 +00:00
Merge branch 'win8-pointer-api' (fix #1595)
This commit is contained in:
commit
83d86cdbc8
@ -405,8 +405,9 @@ if(WIN32)
|
||||
kernel32 user32 gdi32 comdlg32 ole32 winmm
|
||||
shlwapi psapi wininet comctl32 dbghelp)
|
||||
|
||||
# Windows XP is the minimum supported platform.
|
||||
add_definitions(-D_WIN32_WINNT=0x0501 -DWINVER=0x0501)
|
||||
# Windows Vista is the minimum supported platform but we're defining
|
||||
# Windows 10 to get the all constant/structure definitions.
|
||||
add_definitions(-D_WIN32_WINNT=0x0A00 -DWINVER=0x0A00)
|
||||
|
||||
# We need Unicode support
|
||||
add_definitions(-DUNICODE -D_UNICODE)
|
||||
|
@ -122,6 +122,7 @@ if(USE_SKIA_BACKEND)
|
||||
skia/skia_window_win.cpp
|
||||
win/pen.cpp
|
||||
win/vk.cpp
|
||||
win/winapi.cpp
|
||||
win/window.cpp
|
||||
win/window_dde.cpp)
|
||||
elseif(APPLE)
|
||||
|
@ -7,3 +7,4 @@ back-ends:
|
||||
* Previous version were using Allegro 4 (it still uses Allegro 4 on Linux)
|
||||
* Now we use our own implementation on Windows and macOS to handle
|
||||
events, and [Skia](https://skia.org/) to render graphics.
|
||||
* Minimum Windows platform: Windows Vista
|
||||
|
@ -86,7 +86,6 @@ namespace she {
|
||||
// We suppose that if we are receiving precise scrolling deltas,
|
||||
// it means that the user is using a touch-like surface (trackpad,
|
||||
// magic mouse scrolling, touch wacom tablet, etc.)
|
||||
// TODO change this with the new PointerType::Multitouch
|
||||
bool preciseWheel() const { return m_preciseWheel; }
|
||||
|
||||
PointerType pointerType() const { return m_pointerType; }
|
||||
|
@ -454,7 +454,7 @@ using namespace she;
|
||||
scale = [(OSXWindow*)self.window scale];
|
||||
|
||||
if (event.hasPreciseScrollingDeltas) {
|
||||
ev.setPointerType(she::PointerType::Multitouch);
|
||||
ev.setPointerType(she::PointerType::Touchpad);
|
||||
ev.setWheelDelta(gfx::Point(-event.scrollingDeltaX / scale,
|
||||
-event.scrollingDeltaY / scale));
|
||||
ev.setPreciseWheel(true);
|
||||
@ -485,7 +485,7 @@ using namespace she;
|
||||
ev.setMagnification(event.magnification);
|
||||
ev.setPosition(get_local_mouse_pos(self, event));
|
||||
ev.setModifiers(get_modifiers_from_nsevent(event));
|
||||
ev.setPointerType(she::PointerType::Multitouch);
|
||||
ev.setPointerType(she::PointerType::Touchpad);
|
||||
queue_event(ev);
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
// SHE library
|
||||
// Copyright (C) 2016 David Capello
|
||||
// Copyright (C) 2016-2017 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
@ -14,7 +14,8 @@ namespace she {
|
||||
enum class PointerType {
|
||||
Unknown,
|
||||
Mouse, // A regular mouse
|
||||
Multitouch, // Trackpad/multitouch surface
|
||||
Touchpad, // Touchpad/trackpad
|
||||
Touch, // Touch screen
|
||||
Pen, // Stylus pen
|
||||
Cursor, // Puck like device
|
||||
Eraser // Eraser end of a stylus pen
|
||||
|
@ -1,5 +1,5 @@
|
||||
// SHE library
|
||||
// Copyright (C) 2012-2016 David Capello
|
||||
// Copyright (C) 2012-2017 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
@ -10,6 +10,7 @@
|
||||
|
||||
#include "she/common/system.h"
|
||||
#include "she/win/pen.h"
|
||||
#include "she/win/winapi.h"
|
||||
|
||||
namespace she {
|
||||
|
||||
@ -21,9 +22,8 @@ public:
|
||||
WindowSystem() { }
|
||||
~WindowSystem() { }
|
||||
|
||||
PenAPI& penApi() {
|
||||
return m_penApi;
|
||||
}
|
||||
WinAPI& winApi() { return m_winApi; }
|
||||
PenAPI& penApi() { return m_penApi; }
|
||||
|
||||
bool isKeyPressed(KeyScancode scancode) override {
|
||||
return win_is_key_pressed(scancode);
|
||||
@ -34,6 +34,7 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
WinAPI m_winApi;
|
||||
PenAPI m_penApi;
|
||||
};
|
||||
|
||||
|
67
src/she/win/winapi.cpp
Normal file
67
src/she/win/winapi.cpp
Normal file
@ -0,0 +1,67 @@
|
||||
// SHE library
|
||||
// Copyright (C) 2017 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "she/win/winapi.h"
|
||||
|
||||
namespace she {
|
||||
|
||||
#define GET_PROC(dll, name) \
|
||||
name = base::get_dll_proc<name##_Func>(dll, #name)
|
||||
|
||||
WinAPI::WinAPI()
|
||||
: EnableMouseInPointer(nullptr)
|
||||
, IsMouseInPointerEnabled(nullptr)
|
||||
, GetPointerInfo(nullptr)
|
||||
, GetPointerPenInfo(nullptr)
|
||||
, CreateInteractionContext(nullptr)
|
||||
, DestroyInteractionContext(nullptr)
|
||||
, StopInteractionContext(nullptr)
|
||||
, RegisterOutputCallbackInteractionContext(nullptr)
|
||||
, AddPointerInteractionContext(nullptr)
|
||||
, RemovePointerInteractionContext(nullptr)
|
||||
, SetInteractionConfigurationInteractionContext(nullptr)
|
||||
, ProcessPointerFramesInteractionContext(nullptr)
|
||||
, m_user32(nullptr)
|
||||
, m_ninput(nullptr)
|
||||
{
|
||||
m_user32 = base::load_dll("user32.dll");
|
||||
m_ninput = base::load_dll("ninput.dll");
|
||||
if (m_user32) {
|
||||
GET_PROC(m_user32, EnableMouseInPointer);
|
||||
GET_PROC(m_user32, IsMouseInPointerEnabled);
|
||||
GET_PROC(m_user32, GetPointerInfo);
|
||||
GET_PROC(m_user32, GetPointerPenInfo);
|
||||
}
|
||||
if (m_ninput) {
|
||||
GET_PROC(m_ninput, CreateInteractionContext);
|
||||
GET_PROC(m_ninput, DestroyInteractionContext);
|
||||
GET_PROC(m_ninput, StopInteractionContext);
|
||||
GET_PROC(m_ninput, RegisterOutputCallbackInteractionContext);
|
||||
GET_PROC(m_ninput, AddPointerInteractionContext);
|
||||
GET_PROC(m_ninput, RemovePointerInteractionContext);
|
||||
GET_PROC(m_ninput, SetInteractionConfigurationInteractionContext);
|
||||
GET_PROC(m_ninput, SetPropertyInteractionContext);
|
||||
GET_PROC(m_ninput, ProcessPointerFramesInteractionContext);
|
||||
}
|
||||
}
|
||||
|
||||
WinAPI::~WinAPI()
|
||||
{
|
||||
if (m_user32) {
|
||||
base::unload_dll(m_user32);
|
||||
m_user32 = nullptr;
|
||||
}
|
||||
if (m_ninput) {
|
||||
base::unload_dll(m_ninput);
|
||||
m_ninput = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace she
|
79
src/she/win/winapi.h
Normal file
79
src/she/win/winapi.h
Normal file
@ -0,0 +1,79 @@
|
||||
// SHE library
|
||||
// Copyright (C) 2017 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifndef SHE_WIN_WINAPI_H_INCLUDED
|
||||
#define SHE_WIN_WINAPI_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#include "base/dll.h"
|
||||
|
||||
#include <windows.h>
|
||||
#include <interactioncontext.h>
|
||||
|
||||
namespace she {
|
||||
|
||||
typedef BOOL (WINAPI* EnableMouseInPointer_Func)(BOOL fEnable);
|
||||
typedef BOOL (WINAPI* IsMouseInPointerEnabled_Func)(void);
|
||||
typedef BOOL (WINAPI* GetPointerInfo_Func)(UINT32 pointerId, POINTER_INFO* pointerInfo);
|
||||
typedef BOOL (WINAPI* GetPointerPenInfo_Func)(UINT32 pointerId, POINTER_PEN_INFO* penInfo);
|
||||
|
||||
typedef HRESULT (WINAPI* CreateInteractionContext_Func)(HINTERACTIONCONTEXT* interactionContext);
|
||||
typedef HRESULT (WINAPI* DestroyInteractionContext_Func)(HINTERACTIONCONTEXT interactionContext);
|
||||
typedef HRESULT (WINAPI* StopInteractionContext_Func)(HINTERACTIONCONTEXT interactionContext);
|
||||
typedef HRESULT (WINAPI* RegisterOutputCallbackInteractionContext_Func)(
|
||||
HINTERACTIONCONTEXT interactionContext,
|
||||
INTERACTION_CONTEXT_OUTPUT_CALLBACK outputCallback,
|
||||
void* clientData);
|
||||
typedef HRESULT (WINAPI* AddPointerInteractionContext_Func)(
|
||||
HINTERACTIONCONTEXT interactionContext,
|
||||
UINT32 pointerId);
|
||||
typedef HRESULT (WINAPI* RemovePointerInteractionContext_Func)(
|
||||
HINTERACTIONCONTEXT interactionContext,
|
||||
UINT32 pointerId);
|
||||
typedef HRESULT (WINAPI* SetInteractionConfigurationInteractionContext_Func)(
|
||||
HINTERACTIONCONTEXT interactionContext,
|
||||
UINT32 configurationCount,
|
||||
const INTERACTION_CONTEXT_CONFIGURATION* configuration);
|
||||
typedef HRESULT (WINAPI* SetPropertyInteractionContext_Func)(
|
||||
HINTERACTIONCONTEXT interactionContext,
|
||||
INTERACTION_CONTEXT_PROPERTY contextProperty,
|
||||
UINT32 value);
|
||||
typedef HRESULT (WINAPI* ProcessPointerFramesInteractionContext_Func)(
|
||||
HINTERACTIONCONTEXT interactionContext,
|
||||
UINT32 entriesCount,
|
||||
UINT32 pointerCount,
|
||||
const POINTER_INFO* pointerInfo);
|
||||
|
||||
class WinAPI {
|
||||
public:
|
||||
WinAPI();
|
||||
~WinAPI();
|
||||
|
||||
// These functions are availble only since Windows 8
|
||||
EnableMouseInPointer_Func EnableMouseInPointer;
|
||||
IsMouseInPointerEnabled_Func IsMouseInPointerEnabled;
|
||||
GetPointerInfo_Func GetPointerInfo;
|
||||
GetPointerPenInfo_Func GetPointerPenInfo;
|
||||
|
||||
// InteractionContext introduced on Windows 8
|
||||
CreateInteractionContext_Func CreateInteractionContext;
|
||||
DestroyInteractionContext_Func DestroyInteractionContext;
|
||||
StopInteractionContext_Func StopInteractionContext;
|
||||
RegisterOutputCallbackInteractionContext_Func RegisterOutputCallbackInteractionContext;
|
||||
AddPointerInteractionContext_Func AddPointerInteractionContext;
|
||||
RemovePointerInteractionContext_Func RemovePointerInteractionContext;
|
||||
SetInteractionConfigurationInteractionContext_Func SetInteractionConfigurationInteractionContext;
|
||||
SetPropertyInteractionContext_Func SetPropertyInteractionContext;
|
||||
ProcessPointerFramesInteractionContext_Func ProcessPointerFramesInteractionContext;
|
||||
|
||||
private:
|
||||
base::dll m_user32;
|
||||
base::dll m_ninput;
|
||||
};
|
||||
|
||||
} // namespace she
|
||||
|
||||
#endif
|
@ -25,29 +25,104 @@
|
||||
#include "she/win/vk.h"
|
||||
#include "she/win/window_dde.h"
|
||||
|
||||
#ifndef WM_MOUSEHWHEEL
|
||||
#define WM_MOUSEHWHEEL 0x020E
|
||||
#endif
|
||||
|
||||
#define SHE_WND_CLASS_NAME L"Aseprite.Window"
|
||||
|
||||
#define MOUSE_TRACE(...)
|
||||
|
||||
// Gets the window client are in absolute/screen coordinates
|
||||
#define ABS_CLIENT_RC(rc) \
|
||||
RECT rc; \
|
||||
GetClientRect(m_hwnd, &rc); \
|
||||
MapWindowPoints(m_hwnd, NULL, (POINT*)&rc, 2)
|
||||
|
||||
// Not yet ready because if we start receiving WM_POINTERDOWN messages
|
||||
// instead of WM_LBUTTONDBLCLK we lost the automatic double-click
|
||||
// messages.
|
||||
#define USE_EnableMouseInPointer 0
|
||||
|
||||
#ifndef INTERACTION_CONTEXT_PROPERTY_MEASUREMENT_UNITS_SCREEN
|
||||
#define INTERACTION_CONTEXT_PROPERTY_MEASUREMENT_UNITS_SCREEN 1
|
||||
#endif
|
||||
|
||||
namespace she {
|
||||
|
||||
WinWindow::WinWindow(int width, int height, int scale)
|
||||
: m_hwnd(nullptr)
|
||||
, m_hcursor(nullptr)
|
||||
, m_clientSize(1, 1)
|
||||
, m_restoredSize(0, 0)
|
||||
, m_scale(scale)
|
||||
, m_isCreated(false)
|
||||
, m_translateDeadKeys(false)
|
||||
, m_hasMouse(false)
|
||||
, m_captureMouse(false)
|
||||
, m_customHcursor(false)
|
||||
, m_usePointerApi(false)
|
||||
, m_ignoreMouseMessages(false)
|
||||
, m_lastPointerId(0)
|
||||
, m_ictx(nullptr)
|
||||
, m_hpenctx(nullptr)
|
||||
, m_pointerType(PointerType::Unknown)
|
||||
, m_pressure(0.0)
|
||||
{
|
||||
m_hcursor = nullptr;
|
||||
m_customHcursor = false;
|
||||
m_scale = scale;
|
||||
auto& winApi = system()->winApi();
|
||||
if (winApi.EnableMouseInPointer &&
|
||||
winApi.IsMouseInPointerEnabled &&
|
||||
winApi.GetPointerInfo &&
|
||||
winApi.GetPointerPenInfo) {
|
||||
#if USE_EnableMouseInPointer == 1
|
||||
if (!winApi.IsMouseInPointerEnabled()) {
|
||||
// Prefer pointer messages (WM_POINTER*) since Windows 8 instead
|
||||
// of mouse messages (WM_MOUSE*)
|
||||
winApi.EnableMouseInPointer(TRUE);
|
||||
m_ignoreMouseMessages = (winApi.IsMouseInPointerEnabled() ? true: false);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Initialize a Interaction Context to convert WM_POINTER messages
|
||||
// into gestures processed by handleInteractionContextOutput().
|
||||
if (winApi.CreateInteractionContext &&
|
||||
winApi.RegisterOutputCallbackInteractionContext &&
|
||||
winApi.SetInteractionConfigurationInteractionContext) {
|
||||
HRESULT hr = winApi.CreateInteractionContext(&m_ictx);
|
||||
if (SUCCEEDED(hr)) {
|
||||
hr = winApi.RegisterOutputCallbackInteractionContext(
|
||||
m_ictx, &WinWindow::staticInteractionContextCallback, this);
|
||||
}
|
||||
if (SUCCEEDED(hr)) {
|
||||
INTERACTION_CONTEXT_CONFIGURATION cfg[] = {
|
||||
{ INTERACTION_ID_MANIPULATION,
|
||||
INTERACTION_CONFIGURATION_FLAG_MANIPULATION |
|
||||
INTERACTION_CONFIGURATION_FLAG_MANIPULATION_TRANSLATION_X |
|
||||
INTERACTION_CONFIGURATION_FLAG_MANIPULATION_TRANSLATION_Y |
|
||||
INTERACTION_CONFIGURATION_FLAG_MANIPULATION_SCALING |
|
||||
INTERACTION_CONFIGURATION_FLAG_MANIPULATION_TRANSLATION_INERTIA |
|
||||
INTERACTION_CONFIGURATION_FLAG_MANIPULATION_SCALING_INERTIA },
|
||||
{ INTERACTION_ID_TAP,
|
||||
INTERACTION_CONFIGURATION_FLAG_TAP |
|
||||
INTERACTION_CONFIGURATION_FLAG_TAP_DOUBLE },
|
||||
{ INTERACTION_ID_SECONDARY_TAP,
|
||||
INTERACTION_CONFIGURATION_FLAG_SECONDARY_TAP },
|
||||
{ INTERACTION_ID_HOLD,
|
||||
INTERACTION_CONFIGURATION_FLAG_NONE },
|
||||
{ INTERACTION_ID_DRAG,
|
||||
INTERACTION_CONFIGURATION_FLAG_NONE },
|
||||
{ INTERACTION_ID_CROSS_SLIDE,
|
||||
INTERACTION_CONFIGURATION_FLAG_NONE }
|
||||
};
|
||||
hr = winApi.SetInteractionConfigurationInteractionContext(
|
||||
m_ictx, sizeof(cfg) / sizeof(INTERACTION_CONTEXT_CONFIGURATION), cfg);
|
||||
}
|
||||
if (SUCCEEDED(hr)) {
|
||||
hr = winApi.SetPropertyInteractionContext(
|
||||
m_ictx,
|
||||
INTERACTION_CONTEXT_PROPERTY_MEASUREMENT_UNITS,
|
||||
INTERACTION_CONTEXT_PROPERTY_MEASUREMENT_UNITS_SCREEN);
|
||||
}
|
||||
}
|
||||
|
||||
m_usePointerApi = true;
|
||||
}
|
||||
|
||||
registerClass();
|
||||
|
||||
@ -68,6 +143,10 @@ WinWindow::WinWindow(int width, int height, int scale)
|
||||
|
||||
WinWindow::~WinWindow()
|
||||
{
|
||||
auto& winApi = system()->winApi();
|
||||
if (m_ictx && winApi.DestroyInteractionContext)
|
||||
winApi.DestroyInteractionContext(m_ictx);
|
||||
|
||||
if (m_hwnd)
|
||||
DestroyWindow(m_hwnd);
|
||||
}
|
||||
@ -127,11 +206,21 @@ void WinWindow::setTitle(const std::string& title)
|
||||
void WinWindow::captureMouse()
|
||||
{
|
||||
m_captureMouse = true;
|
||||
|
||||
if (GetCapture() != m_hwnd) {
|
||||
MOUSE_TRACE("SetCapture\n");
|
||||
SetCapture(m_hwnd);
|
||||
}
|
||||
}
|
||||
|
||||
void WinWindow::releaseMouse()
|
||||
{
|
||||
m_captureMouse = false;
|
||||
|
||||
if (GetCapture() == m_hwnd) {
|
||||
MOUSE_TRACE("ReleaseCapture\n");
|
||||
ReleaseCapture();
|
||||
}
|
||||
}
|
||||
|
||||
void WinWindow::setMousePosition(const gfx::Point& position)
|
||||
@ -436,22 +525,23 @@ LRESULT WinWindow::wndProc(UINT msg, WPARAM wparam, LPARAM lparam)
|
||||
}
|
||||
break;
|
||||
|
||||
// Mouse and Trackpad Messages
|
||||
|
||||
case WM_MOUSEMOVE: {
|
||||
// Adjust capture
|
||||
if (m_captureMouse) {
|
||||
if (GetCapture() != m_hwnd)
|
||||
SetCapture(m_hwnd);
|
||||
}
|
||||
else {
|
||||
if (GetCapture() == m_hwnd)
|
||||
ReleaseCapture();
|
||||
}
|
||||
// If the pointer API is enable, we use WM_POINTERUPDATE instead
|
||||
// of WM_MOUSEMOVE. This check is here because Windows keeps
|
||||
// sending us WM_MOUSEMOVE messages even when we call
|
||||
// EnableMouseInPointer() (mainly when we use Alt+stylus we
|
||||
// receive WM_MOUSEMOVE with the position of the mouse/trackpad
|
||||
// + WM_POINTERUPDATE with the position of the pen)
|
||||
if (m_ignoreMouseMessages)
|
||||
break;
|
||||
|
||||
Event ev;
|
||||
ev.setModifiers(get_modifiers_from_last_win32_message());
|
||||
ev.setPosition(gfx::Point(
|
||||
GET_X_LPARAM(lparam) / m_scale,
|
||||
GET_Y_LPARAM(lparam) / m_scale));
|
||||
mouseEvent(lparam, ev);
|
||||
|
||||
MOUSE_TRACE("MOUSEMOVE xy=%d,%d\n",
|
||||
ev.position().x, ev.position().y);
|
||||
|
||||
if (!m_hasMouse) {
|
||||
m_hasMouse = true;
|
||||
@ -459,6 +549,8 @@ LRESULT WinWindow::wndProc(UINT msg, WPARAM wparam, LPARAM lparam)
|
||||
ev.setType(Event::MouseEnter);
|
||||
queueEvent(ev);
|
||||
|
||||
MOUSE_TRACE("-> Event::MouseEnter\n");
|
||||
|
||||
// Track mouse to receive WM_MOUSELEAVE message.
|
||||
TRACKMOUSEEVENT tme;
|
||||
tme.cbSize = sizeof(TRACKMOUSEEVENT);
|
||||
@ -486,6 +578,8 @@ LRESULT WinWindow::wndProc(UINT msg, WPARAM wparam, LPARAM lparam)
|
||||
ev.setType(Event::MouseLeave);
|
||||
ev.setModifiers(get_modifiers_from_last_win32_message());
|
||||
queueEvent(ev);
|
||||
|
||||
MOUSE_TRACE("-> Event::MouseLeave\n");
|
||||
}
|
||||
break;
|
||||
|
||||
@ -494,11 +588,8 @@ LRESULT WinWindow::wndProc(UINT msg, WPARAM wparam, LPARAM lparam)
|
||||
case WM_MBUTTONDOWN:
|
||||
case WM_XBUTTONDOWN: {
|
||||
Event ev;
|
||||
mouseEvent(lparam, ev);
|
||||
ev.setType(Event::MouseDown);
|
||||
ev.setModifiers(get_modifiers_from_last_win32_message());
|
||||
ev.setPosition(gfx::Point(
|
||||
GET_X_LPARAM(lparam) / m_scale,
|
||||
GET_Y_LPARAM(lparam) / m_scale));
|
||||
ev.setButton(
|
||||
msg == WM_LBUTTONDOWN ? Event::LeftButton:
|
||||
msg == WM_RBUTTONDOWN ? Event::RightButton:
|
||||
@ -511,8 +602,11 @@ LRESULT WinWindow::wndProc(UINT msg, WPARAM wparam, LPARAM lparam)
|
||||
ev.setPointerType(m_pointerType);
|
||||
ev.setPressure(m_pressure);
|
||||
}
|
||||
|
||||
queueEvent(ev);
|
||||
|
||||
MOUSE_TRACE("BUTTONDOWN xy=%d,%d button=%d\n",
|
||||
ev.position().x, ev.position().y,
|
||||
ev.button());
|
||||
break;
|
||||
}
|
||||
|
||||
@ -521,11 +615,8 @@ LRESULT WinWindow::wndProc(UINT msg, WPARAM wparam, LPARAM lparam)
|
||||
case WM_MBUTTONUP:
|
||||
case WM_XBUTTONUP: {
|
||||
Event ev;
|
||||
mouseEvent(lparam, ev);
|
||||
ev.setType(Event::MouseUp);
|
||||
ev.setModifiers(get_modifiers_from_last_win32_message());
|
||||
ev.setPosition(gfx::Point(
|
||||
GET_X_LPARAM(lparam) / m_scale,
|
||||
GET_Y_LPARAM(lparam) / m_scale));
|
||||
ev.setButton(
|
||||
msg == WM_LBUTTONUP ? Event::LeftButton:
|
||||
msg == WM_RBUTTONUP ? Event::RightButton:
|
||||
@ -538,9 +629,12 @@ LRESULT WinWindow::wndProc(UINT msg, WPARAM wparam, LPARAM lparam)
|
||||
ev.setPointerType(m_pointerType);
|
||||
ev.setPressure(m_pressure);
|
||||
}
|
||||
|
||||
queueEvent(ev);
|
||||
|
||||
MOUSE_TRACE("BUTTONUP xy=%d,%d button=%d\n",
|
||||
ev.position().x, ev.position().y,
|
||||
ev.button());
|
||||
|
||||
// Avoid popup menu for scrollbars
|
||||
if (msg == WM_RBUTTONUP)
|
||||
return 0;
|
||||
@ -553,11 +647,8 @@ LRESULT WinWindow::wndProc(UINT msg, WPARAM wparam, LPARAM lparam)
|
||||
case WM_RBUTTONDBLCLK:
|
||||
case WM_XBUTTONDBLCLK: {
|
||||
Event ev;
|
||||
mouseEvent(lparam, ev);
|
||||
ev.setType(Event::MouseDoubleClick);
|
||||
ev.setModifiers(get_modifiers_from_last_win32_message());
|
||||
ev.setPosition(gfx::Point(
|
||||
GET_X_LPARAM(lparam) / m_scale,
|
||||
GET_Y_LPARAM(lparam) / m_scale));
|
||||
ev.setButton(
|
||||
msg == WM_LBUTTONDBLCLK ? Event::LeftButton:
|
||||
msg == WM_RBUTTONDBLCLK ? Event::RightButton:
|
||||
@ -570,8 +661,11 @@ LRESULT WinWindow::wndProc(UINT msg, WPARAM wparam, LPARAM lparam)
|
||||
ev.setPointerType(m_pointerType);
|
||||
ev.setPressure(m_pressure);
|
||||
}
|
||||
|
||||
queueEvent(ev);
|
||||
|
||||
MOUSE_TRACE("BUTTONDBLCLK xy=%d,%d button=%d\n",
|
||||
ev.position().x, ev.position().y,
|
||||
ev.button());
|
||||
break;
|
||||
}
|
||||
|
||||
@ -599,8 +693,11 @@ LRESULT WinWindow::wndProc(UINT msg, WPARAM wparam, LPARAM lparam)
|
||||
(msg == WM_MOUSEHWHEEL ? z: 0),
|
||||
(msg == WM_MOUSEWHEEL ? -z: 0));
|
||||
ev.setWheelDelta(delta);
|
||||
|
||||
queueEvent(ev);
|
||||
|
||||
MOUSE_TRACE("MOUSEWHEEL xy=%d,%d delta=%d,%d\n",
|
||||
ev.position().x, ev.position().y,
|
||||
ev.wheelDelta().x, ev.wheelDelta().y);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -646,11 +743,213 @@ LRESULT WinWindow::wndProc(UINT msg, WPARAM wparam, LPARAM lparam)
|
||||
ev.setWheelDelta(delta);
|
||||
|
||||
SetScrollPos(m_hwnd, bar, 50, FALSE);
|
||||
|
||||
queueEvent(ev);
|
||||
|
||||
MOUSE_TRACE("HVSCROLL xy=%d,%d delta=%d,%d\n",
|
||||
ev.position().x, ev.position().y,
|
||||
ev.wheelDelta().x, ev.wheelDelta().y);
|
||||
break;
|
||||
}
|
||||
|
||||
// Pointer API (since Windows 8.0)
|
||||
|
||||
case WM_POINTERCAPTURECHANGED: {
|
||||
MOUSE_TRACE("POINTERCAPTURECHANGED\n");
|
||||
releaseMouse();
|
||||
break;
|
||||
}
|
||||
|
||||
case WM_POINTERENTER: {
|
||||
POINTER_INFO pi;
|
||||
Event ev;
|
||||
if (!pointerEvent(wparam, ev, pi))
|
||||
break;
|
||||
|
||||
MOUSE_TRACE("POINTERENTER id=%d xy=%d,%d\n",
|
||||
pi.pointerId, ev.position().x, ev.position().y);
|
||||
|
||||
#if USE_EnableMouseInPointer == 0
|
||||
// This is necessary to avoid receiving random WM_MOUSEMOVE from
|
||||
// the mouse position when we use Alt+pen tip.
|
||||
// TODO Remove this line when we enable EnableMouseInPointer(TRUE);
|
||||
m_ignoreMouseMessages = true;
|
||||
#endif
|
||||
|
||||
if (pi.pointerType == PT_TOUCH) {
|
||||
auto& winApi = system()->winApi();
|
||||
if (m_ictx && winApi.AddPointerInteractionContext)
|
||||
winApi.AddPointerInteractionContext(m_ictx, pi.pointerId);
|
||||
}
|
||||
|
||||
if (!m_hasMouse) {
|
||||
m_hasMouse = true;
|
||||
|
||||
ev.setType(Event::MouseEnter);
|
||||
queueEvent(ev);
|
||||
|
||||
MOUSE_TRACE("-> Event::MouseEnter\n");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
case WM_POINTERLEAVE: {
|
||||
POINTER_INFO pi;
|
||||
Event ev;
|
||||
if (!pointerEvent(wparam, ev, pi))
|
||||
break;
|
||||
|
||||
MOUSE_TRACE("POINTERLEAVE id=%d\n", pi.pointerId);
|
||||
|
||||
#if USE_EnableMouseInPointer == 0
|
||||
m_ignoreMouseMessages = false;
|
||||
#endif
|
||||
|
||||
if (pi.pointerType == PT_TOUCH) {
|
||||
auto& winApi = system()->winApi();
|
||||
if (m_ictx && winApi.RemovePointerInteractionContext)
|
||||
winApi.RemovePointerInteractionContext(m_ictx, pi.pointerId);
|
||||
}
|
||||
|
||||
#if 0 // Don't generate MouseLeave from pen/touch messages
|
||||
// TODO we should generate this message, but after this touch
|
||||
// messages don't work anymore, so we have to fix that problem.
|
||||
if (m_hasMouse) {
|
||||
m_hasMouse = false;
|
||||
|
||||
ev.setType(Event::MouseLeave);
|
||||
queueEvent(ev);
|
||||
|
||||
MOUSE_TRACE("-> Event::MouseLeave\n");
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
case WM_POINTERDOWN: {
|
||||
POINTER_INFO pi;
|
||||
Event ev;
|
||||
if (!pointerEvent(wparam, ev, pi))
|
||||
break;
|
||||
|
||||
if (pi.pointerType == PT_TOUCH) {
|
||||
auto& winApi = system()->winApi();
|
||||
if (m_ictx && winApi.ProcessPointerFramesInteractionContext) {
|
||||
winApi.ProcessPointerFramesInteractionContext(m_ictx, 1, 1, &pi);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
ev.setType(Event::MouseDown);
|
||||
ev.setButton(
|
||||
pi.ButtonChangeType == POINTER_CHANGE_FIRSTBUTTON_DOWN ? Event::LeftButton:
|
||||
pi.ButtonChangeType == POINTER_CHANGE_SECONDBUTTON_DOWN ? Event::RightButton:
|
||||
pi.ButtonChangeType == POINTER_CHANGE_THIRDBUTTON_DOWN ? Event::MiddleButton:
|
||||
pi.ButtonChangeType == POINTER_CHANGE_FOURTHBUTTON_DOWN ? Event::X1Button:
|
||||
pi.ButtonChangeType == POINTER_CHANGE_FIFTHBUTTON_DOWN ? Event::X2Button:
|
||||
Event::NoneButton);
|
||||
queueEvent(ev);
|
||||
|
||||
MOUSE_TRACE("POINTERDOWN id=%d xy=%d,%d button=%d\n",
|
||||
pi.pointerId, ev.position().x, ev.position().y,
|
||||
ev.button());
|
||||
return 0;
|
||||
}
|
||||
|
||||
case WM_POINTERUP: {
|
||||
POINTER_INFO pi;
|
||||
Event ev;
|
||||
if (!pointerEvent(wparam, ev, pi))
|
||||
break;
|
||||
|
||||
if (pi.pointerType == PT_TOUCH) {
|
||||
auto& winApi = system()->winApi();
|
||||
if (m_ictx && winApi.ProcessPointerFramesInteractionContext) {
|
||||
winApi.ProcessPointerFramesInteractionContext(m_ictx, 1, 1, &pi);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
ev.setType(Event::MouseUp);
|
||||
ev.setButton(
|
||||
pi.ButtonChangeType == POINTER_CHANGE_FIRSTBUTTON_UP ? Event::LeftButton:
|
||||
pi.ButtonChangeType == POINTER_CHANGE_SECONDBUTTON_UP ? Event::RightButton:
|
||||
pi.ButtonChangeType == POINTER_CHANGE_THIRDBUTTON_UP ? Event::MiddleButton:
|
||||
pi.ButtonChangeType == POINTER_CHANGE_FOURTHBUTTON_UP ? Event::X1Button:
|
||||
pi.ButtonChangeType == POINTER_CHANGE_FIFTHBUTTON_UP ? Event::X2Button:
|
||||
Event::NoneButton);
|
||||
queueEvent(ev);
|
||||
|
||||
MOUSE_TRACE("POINTERUP id=%d xy=%d,%d button=%d\n",
|
||||
pi.pointerId, ev.position().x, ev.position().y,
|
||||
ev.button());
|
||||
return 0;
|
||||
}
|
||||
|
||||
case WM_POINTERUPDATE: {
|
||||
POINTER_INFO pi;
|
||||
Event ev;
|
||||
if (!pointerEvent(wparam, ev, pi))
|
||||
break;
|
||||
|
||||
if (pi.pointerType == PT_TOUCH) {
|
||||
auto& winApi = system()->winApi();
|
||||
if (m_ictx && winApi.ProcessPointerFramesInteractionContext) {
|
||||
winApi.ProcessPointerFramesInteractionContext(m_ictx, 1, 1, &pi);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (!m_hasMouse) {
|
||||
m_hasMouse = true;
|
||||
|
||||
ev.setType(Event::MouseEnter);
|
||||
queueEvent(ev);
|
||||
|
||||
MOUSE_TRACE("-> Event::MouseEnter\n");
|
||||
}
|
||||
|
||||
ev.setType(Event::MouseMove);
|
||||
queueEvent(ev);
|
||||
|
||||
MOUSE_TRACE("POINTERUPDATE id=%d xy=%d,%d\n",
|
||||
pi.pointerId, ev.position().x, ev.position().y);
|
||||
return 0;
|
||||
}
|
||||
|
||||
case WM_POINTERWHEEL:
|
||||
case WM_POINTERHWHEEL: {
|
||||
POINTER_INFO pi;
|
||||
Event ev;
|
||||
if (!pointerEvent(wparam, ev, pi))
|
||||
break;
|
||||
|
||||
ev.setType(Event::MouseWheel);
|
||||
|
||||
int z = GET_WHEEL_DELTA_WPARAM(wparam);
|
||||
if (ABS(z) >= WHEEL_DELTA)
|
||||
z /= WHEEL_DELTA;
|
||||
else {
|
||||
// TODO use floating point numbers or something similar
|
||||
// (so we could use: z /= double(WHEEL_DELTA))
|
||||
z = SGN(z);
|
||||
}
|
||||
|
||||
gfx::Point delta(
|
||||
(msg == WM_POINTERHWHEEL ? z: 0),
|
||||
(msg == WM_POINTERWHEEL ? -z: 0));
|
||||
ev.setWheelDelta(delta);
|
||||
queueEvent(ev);
|
||||
|
||||
MOUSE_TRACE("POINTERWHEEL xy=%d,%d delta=%d,%d\n",
|
||||
ev.position().x, ev.position().y,
|
||||
ev.wheelDelta().x, ev.wheelDelta().y);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Keyboard Messages
|
||||
|
||||
case WM_SYSKEYDOWN:
|
||||
case WM_KEYDOWN: {
|
||||
int vk = wparam;
|
||||
@ -749,11 +1048,10 @@ LRESULT WinWindow::wndProc(UINT msg, WPARAM wparam, LPARAM lparam)
|
||||
|
||||
case WM_NCHITTEST: {
|
||||
LRESULT result = CallWindowProc(DefWindowProc, m_hwnd, msg, wparam, lparam);
|
||||
gfx::Point pt(GET_X_LPARAM(lparam), GET_Y_LPARAM(lparam));
|
||||
gfx::Point pt(GET_X_LPARAM(lparam),
|
||||
GET_Y_LPARAM(lparam));
|
||||
|
||||
RECT rc;
|
||||
GetClientRect(m_hwnd, &rc);
|
||||
MapWindowPoints(m_hwnd, NULL, (POINT*)&rc, 2);
|
||||
ABS_CLIENT_RC(rc);
|
||||
gfx::Rect area(rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top);
|
||||
|
||||
//LOG("NCHITTEST: %d %d - %d %d %d %d - %s\n", pt.x, pt.y, area.x, area.y, area.w, area.h, area.contains(pt) ? "true": "false");
|
||||
@ -772,6 +1070,8 @@ LRESULT WinWindow::wndProc(UINT msg, WPARAM wparam, LPARAM lparam)
|
||||
return result;
|
||||
}
|
||||
|
||||
// Wintab API Messages
|
||||
|
||||
case WT_PROXIMITY: {
|
||||
bool entering_ctx = (LOWORD(lparam) ? true: false);
|
||||
if (!entering_ctx)
|
||||
@ -836,6 +1136,127 @@ LRESULT WinWindow::wndProc(UINT msg, WPARAM wparam, LPARAM lparam)
|
||||
return DefWindowProc(m_hwnd, msg, wparam, lparam);
|
||||
}
|
||||
|
||||
void WinWindow::mouseEvent(LPARAM lparam, Event& ev)
|
||||
{
|
||||
ev.setModifiers(get_modifiers_from_last_win32_message());
|
||||
ev.setPosition(gfx::Point(
|
||||
GET_X_LPARAM(lparam) / m_scale,
|
||||
GET_Y_LPARAM(lparam) / m_scale));
|
||||
}
|
||||
|
||||
bool WinWindow::pointerEvent(WPARAM wparam, Event& ev, POINTER_INFO& pi)
|
||||
{
|
||||
if (!m_usePointerApi)
|
||||
return false;
|
||||
|
||||
auto& winApi = system()->winApi();
|
||||
if (!winApi.GetPointerInfo(GET_POINTERID_WPARAM(wparam), &pi))
|
||||
return false;
|
||||
|
||||
ABS_CLIENT_RC(rc);
|
||||
|
||||
ev.setModifiers(get_modifiers_from_last_win32_message());
|
||||
ev.setPosition(gfx::Point((pi.ptPixelLocation.x - rc.left) / m_scale,
|
||||
(pi.ptPixelLocation.y - rc.top) / m_scale));
|
||||
|
||||
switch (pi.pointerType) {
|
||||
case PT_MOUSE: {
|
||||
ev.setPointerType(PointerType::Mouse);
|
||||
break;
|
||||
}
|
||||
case PT_TOUCH: {
|
||||
ev.setPointerType(PointerType::Touch);
|
||||
break;
|
||||
}
|
||||
case PT_TOUCHPAD: {
|
||||
ev.setPointerType(PointerType::Touchpad);
|
||||
break;
|
||||
}
|
||||
case PT_PEN: {
|
||||
ev.setPointerType(PointerType::Pen);
|
||||
|
||||
POINTER_PEN_INFO ppi;
|
||||
if (winApi.GetPointerPenInfo(pi.pointerId, &ppi)) {
|
||||
if (ppi.penFlags & PEN_FLAG_ERASER)
|
||||
ev.setPointerType(PointerType::Eraser);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
m_lastPointerId = pi.pointerId;
|
||||
return true;
|
||||
}
|
||||
|
||||
void WinWindow::handleInteractionContextOutput(
|
||||
const INTERACTION_CONTEXT_OUTPUT* output)
|
||||
{
|
||||
MOUSE_TRACE("%s (%d) xy=%.16g %.16g flags=%d type=%d\n",
|
||||
output->interactionId == INTERACTION_ID_MANIPULATION ? "INTERACTION_ID_MANIPULATION":
|
||||
output->interactionId == INTERACTION_ID_TAP ? "INTERACTION_ID_TAP":
|
||||
output->interactionId == INTERACTION_ID_SECONDARY_TAP ? "INTERACTION_ID_SECONDARY_TAP":
|
||||
output->interactionId == INTERACTION_ID_HOLD ? "INTERACTION_ID_HOLD": "INTERACTION_ID_???",
|
||||
output->interactionId,
|
||||
output->x, output->y,
|
||||
output->interactionFlags,
|
||||
output->inputType);
|
||||
|
||||
// We use the InteractionContext to interpret touch gestures only.
|
||||
if (output->inputType == PT_TOUCH) {
|
||||
ABS_CLIENT_RC(rc);
|
||||
|
||||
gfx::Point pos(int((output->x - rc.left) / m_scale),
|
||||
int((output->y - rc.top) / m_scale));
|
||||
|
||||
Event ev;
|
||||
ev.setModifiers(get_modifiers_from_last_win32_message());
|
||||
ev.setPosition(pos);
|
||||
|
||||
switch (output->interactionId) {
|
||||
case INTERACTION_ID_MANIPULATION: {
|
||||
MOUSE_TRACE(" - delta xy=%.16g %.16g scale=%.16g expansion=%.16g rotation=%.16g\n",
|
||||
output->arguments.manipulation.delta.translationX,
|
||||
output->arguments.manipulation.delta.translationY,
|
||||
output->arguments.manipulation.delta.scale,
|
||||
output->arguments.manipulation.delta.expansion,
|
||||
output->arguments.manipulation.delta.rotation);
|
||||
|
||||
gfx::Point delta(-int(output->arguments.manipulation.delta.translationX) / m_scale,
|
||||
-int(output->arguments.manipulation.delta.translationY) / m_scale);
|
||||
|
||||
ev.setType(Event::MouseWheel);
|
||||
ev.setWheelDelta(delta);
|
||||
ev.setPreciseWheel(true);
|
||||
queueEvent(ev);
|
||||
|
||||
ev.setType(Event::TouchMagnify);
|
||||
ev.setMagnification(output->arguments.manipulation.delta.scale - 1.0);
|
||||
queueEvent(ev);
|
||||
break;
|
||||
}
|
||||
|
||||
case INTERACTION_ID_TAP:
|
||||
MOUSE_TRACE(" - count=%d\n", output->arguments.tap.count);
|
||||
ev.setButton(Event::LeftButton);
|
||||
if (output->arguments.tap.count == 2) {
|
||||
ev.setType(Event::MouseDoubleClick); queueEvent(ev);
|
||||
}
|
||||
else {
|
||||
ev.setType(Event::MouseDown); queueEvent(ev);
|
||||
ev.setType(Event::MouseUp); queueEvent(ev);
|
||||
}
|
||||
break;
|
||||
|
||||
case INTERACTION_ID_SECONDARY_TAP:
|
||||
case INTERACTION_ID_HOLD:
|
||||
ev.setButton(Event::RightButton);
|
||||
ev.setType(Event::MouseDown); queueEvent(ev);
|
||||
ev.setType(Event::MouseUp); queueEvent(ev);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//static
|
||||
void WinWindow::registerClass()
|
||||
{
|
||||
@ -937,6 +1358,15 @@ LRESULT CALLBACK WinWindow::staticWndProc(HWND hwnd, UINT msg, WPARAM wparam, LP
|
||||
}
|
||||
}
|
||||
|
||||
//static
|
||||
void CALLBACK WinWindow::staticInteractionContextCallback(
|
||||
void* clientData,
|
||||
const INTERACTION_CONTEXT_OUTPUT* output)
|
||||
{
|
||||
WinWindow* self = reinterpret_cast<WinWindow*>(clientData);
|
||||
self->handleInteractionContextOutput(output);
|
||||
}
|
||||
|
||||
// static
|
||||
WindowSystem* WinWindow::system()
|
||||
{
|
||||
|
@ -15,6 +15,7 @@
|
||||
|
||||
#include <string>
|
||||
#include <windows.h>
|
||||
#include <interactioncontext.h>
|
||||
|
||||
namespace she {
|
||||
class Event;
|
||||
@ -54,6 +55,10 @@ namespace she {
|
||||
private:
|
||||
bool setCursor(HCURSOR hcursor, bool custom);
|
||||
LRESULT wndProc(UINT msg, WPARAM wparam, LPARAM lparam);
|
||||
void mouseEvent(LPARAM lparam, Event& ev);
|
||||
bool pointerEvent(WPARAM wparam, Event& ev, POINTER_INFO& pi);
|
||||
void handleInteractionContextOutput(
|
||||
const INTERACTION_CONTEXT_OUTPUT* output);
|
||||
|
||||
virtual void onQueueEvent(Event& ev) { }
|
||||
virtual void onResize(const gfx::Size& sz) { }
|
||||
@ -61,7 +66,11 @@ namespace she {
|
||||
|
||||
static void registerClass();
|
||||
static HWND createHwnd(WinWindow* self, int width, int height);
|
||||
static LRESULT CALLBACK staticWndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);
|
||||
static LRESULT CALLBACK staticWndProc(
|
||||
HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);
|
||||
static void CALLBACK staticInteractionContextCallback(
|
||||
void* clientData,
|
||||
const INTERACTION_CONTEXT_OUTPUT* output);
|
||||
|
||||
static WindowSystem* system();
|
||||
|
||||
@ -76,6 +85,13 @@ namespace she {
|
||||
bool m_captureMouse;
|
||||
bool m_customHcursor;
|
||||
|
||||
// Windows 8 pointer API
|
||||
bool m_usePointerApi;
|
||||
bool m_ignoreMouseMessages;
|
||||
UINT32 m_lastPointerId;
|
||||
UINT32 m_capturePointerId;
|
||||
HINTERACTIONCONTEXT m_ictx;
|
||||
|
||||
// Wintab API data
|
||||
HCTX m_hpenctx;
|
||||
PointerType m_pointerType;
|
||||
|
Loading…
x
Reference in New Issue
Block a user