Quelle gdkinput-dmanipulation.c
Sprache: C
/* gdkinput-dmanipulation.c
*
* Copyright © 2022 the GTK team
*
* This library is free software ; you can redistribute it and / or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation ; either
* version 2 of the License , or ( at your option ) any later version .
*
* This library 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
* Lesser General Public License for more details .
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library . If not , see < http : //www.gnu.org/licenses/>.
*
* SPDX - License - Identifier : LGPL - 2 . 1 - or - later
*/
/* {{{ */
#include "config.h"
#include <gdk/gdk.h>
#include "gdkwin32.h"
#include "gdkprivate-win32.h"
#include "gdkdevicemanager-win32.h"
#include "gdkdevice-virtual.h"
#include "gdkdeviceprivate.h"
#include "gdkdisplay-win32.h"
#include "gdkprivate-win32.h"
#include "gdkeventsprivate.h"
#include "gdkinput-dmanipulation.h"
#include "winpointer.h"
#include <windows.h>
#include <directmanipulation.h>
typedef BOOL
(WINAPI *getPointerType_t)(UINT32 pointerId, POINTER_INPUT_TYPE *pointerType);
typedef struct
{
IDirectManipulationViewportEventHandlerVtbl *vtable;
LONG reference_count;
enum {
GESTURE_PAN,
GESTURE_ZOOM,
} gesture;
GdkTouchpadGesturePhase phase;
gpointer sequence;
float scale;
float pan_x;
float pan_y;
GdkSurface *surface;
GdkDevice *device;
}
DManipEventHandler;
static void dmanip_event_handler_running_state_clear (DManipEventHandler *handler);
static void reset_viewport (IDirectManipulationViewport *viewport);
static gpointer util_get_next_sequence (void );
static GdkModifierType util_get_modifier_state (void );
/* }}} */
/* {{{ ViewportEventHandler */
static STDMETHODIMP_ (ULONG)
DManipEventHandler_AddRef (IDirectManipulationViewportEventHandler *self_)
{
DManipEventHandler *self = (DManipEventHandler*) self_;
return (ULONG) InterlockedIncrement (&self->reference_count);
}
static void
dmanip_event_handler_free (DManipEventHandler *handler)
{
g_free (handler);
}
static STDMETHODIMP_ (ULONG)
DManipEventHandler_Release (IDirectManipulationViewportEventHandler *self_)
{
DManipEventHandler *self = (DManipEventHandler*) self_;
/* NOTE: This may run from a worker thread */
LONG new_reference_count = InterlockedDecrement (&self->reference_count);
if (new_reference_count <= 0 )
{
dmanip_event_handler_free (self);
return 0 ;
}
return (ULONG) new_reference_count;
}
static STDMETHODIMP
DManipEventHandler_QueryInterface (IDirectManipulationViewportEventHandler *self_,
REFIID riid,
void **ppvObject)
{
DManipEventHandler *self = (DManipEventHandler*) self_;
if G_UNLIKELY (!self || !ppvObject)
return E_POINTER;
*ppvObject = NULL;
if (IsEqualGUID (riid, &IID_IUnknown))
*ppvObject = self;
else if (IsEqualGUID (riid, &IID_IDirectManipulationViewportEventHandler))
*ppvObject = self;
if (*ppvObject == NULL)
return E_NOINTERFACE;
DManipEventHandler_AddRef (self_);
return S_OK;
}
/* NOTE:
*
* All DManipEventHandler callbacks are fired from the main thread */
static STDMETHODIMP
DManipEventHandler_OnViewportUpdated (IDirectManipulationViewportEventHandler *self_,
IDirectManipulationViewport *viewport)
{
return S_OK;
}
static STDMETHODIMP
DManipEventHandler_OnContentUpdated (IDirectManipulationViewportEventHandler *self_,
IDirectManipulationViewport *viewport,
IDirectManipulationContent *content)
{
DManipEventHandler *self = (DManipEventHandler*) self_;
float transform[6 ] = {1 ., 0 ., 0 ., 1 ., 0 ., 0 .};
HRESULT hr;
hr = IDirectManipulationContent_GetContentTransform (content, transform,
G_N_ELEMENTS (transform));
if G_UNLIKELY (FAILED (hr))
return E_FAIL;
switch (self->gesture)
{
case GESTURE_PAN:
{
GdkWin32Surface *surface_win32;
GdkModifierType state;
uint32_t time;
int scale;
float pan_x;
float pan_y;
GdkEvent *event;
pan_x = transform[4 ];
pan_y = transform[5 ];
surface_win32 = GDK_WIN32_SURFACE (self->surface);
scale = surface_win32->surface_scale;
state = util_get_modifier_state ();
time = (uint32_t) GetMessageTime ();
event = gdk_scroll_event_new (self->surface,
self->device,
NULL, time, state,
(self->pan_x - pan_x) / scale,
(self->pan_y - pan_y) / scale,
FALSE ,
GDK_SCROLL_UNIT_SURFACE,
GDK_SCROLL_RELATIVE_DIRECTION_UNKNOWN);
_gdk_win32_append_event (event);
self->pan_x = pan_x;
self->pan_y = pan_y;
}
break ;
case GESTURE_ZOOM:
{
GdkModifierType state;
uint32_t time;
POINT cursor = {0 , 0 };
float scale;
GdkEvent *event;
GdkDisplay *display = gdk_surface_get_display (self->surface);
scale = transform[0 ];
state = util_get_modifier_state ();
time = (uint32_t) GetMessageTime ();
_gdk_win32_get_cursor_pos (display, &cursor);
ScreenToClient (GDK_SURFACE_HWND (self->surface), &cursor);
if (!self->sequence)
self->sequence = util_get_next_sequence ();
event = gdk_touchpad_event_new_pinch (self->surface, self->sequence, self->device,
time, state, self->phase, cursor.x, cursor.y,
2 , 0 .0 , 0 .0 , scale, 0 .0 );
_gdk_win32_append_event (event);
self->scale = scale;
self->phase = GDK_TOUCHPAD_GESTURE_PHASE_UPDATE;
}
break ;
default :
g_assert_not_reached ();
break ;
}
return S_OK;
}
static STDMETHODIMP
DManipEventHandler_OnViewportStatusChanged (IDirectManipulationViewportEventHandler *self_,
IDirectManipulationViewport *viewport,
DIRECTMANIPULATION_STATUS current,
DIRECTMANIPULATION_STATUS previous)
{
DManipEventHandler *self = (DManipEventHandler*) self_;
if (previous == DIRECTMANIPULATION_RUNNING)
{
switch (self->gesture)
{
case GESTURE_PAN:
{
GdkModifierType state;
uint32_t time;
GdkEvent *event;
state = util_get_modifier_state ();
time = (uint32_t) GetMessageTime ();
event = gdk_scroll_event_new (self->surface, self->device,
NULL, time, state,
0 .0 , 0 .0 , TRUE ,
GDK_SCROLL_UNIT_SURFACE,
GDK_SCROLL_RELATIVE_DIRECTION_UNKNOWN);
_gdk_win32_append_event (event);
}
break ;
case GESTURE_ZOOM:
{
GdkModifierType state;
uint32_t time;
POINT cursor = {0 , 0 };
GdkEvent *event;
GdkDisplay *display = gdk_surface_get_display (self->surface);
if (self->phase == GDK_TOUCHPAD_GESTURE_PHASE_BEGIN)
break ;
state = util_get_modifier_state ();
time = (uint32_t) GetMessageTime ();
_gdk_win32_get_cursor_pos (display, &cursor);
ScreenToClient (GDK_SURFACE_HWND (self->surface), &cursor);
event = gdk_touchpad_event_new_pinch (self->surface, self->sequence, self->device,
time, state, GDK_TOUCHPAD_GESTURE_PHASE_END,
cursor.x, cursor.y, 2 , 0 ., 0 ., self->scale,
0 .);
_gdk_win32_append_event (event);
}
break ;
default :
g_assert_not_reached ();
break ;
}
dmanip_event_handler_running_state_clear (self);
reset_viewport (viewport);
}
return S_OK;
}
static void
dmanip_event_handler_running_state_clear (DManipEventHandler *handler)
{
handler->scale = 1 .0 ;
handler->pan_x = 0 .0 ;
handler->pan_y = 0 .0 ;
handler->phase = GDK_TOUCHPAD_GESTURE_PHASE_BEGIN;
handler->sequence = NULL;
}
static DManipEventHandler*
dmanip_event_handler_new (GdkSurface *surface,
int gesture)
{
static IDirectManipulationViewportEventHandlerVtbl vtable = {
DManipEventHandler_QueryInterface,
DManipEventHandler_AddRef,
DManipEventHandler_Release,
DManipEventHandler_OnViewportStatusChanged,
DManipEventHandler_OnViewportUpdated,
DManipEventHandler_OnContentUpdated,
};
DManipEventHandler *handler;
handler = g_new0 (DManipEventHandler, 1 );
handler->vtable = &vtable;
handler->reference_count = 1 ;
handler->gesture = gesture;
handler->surface = surface;
handler->device = GDK_WIN32_DISPLAY (gdk_surface_get_display (surface))->device_manager->core_pointer;
dmanip_event_handler_running_state_clear (handler);
return handler;
}
/* }}} */
/* {{{ Viewport utils */
static void
reset_viewport (IDirectManipulationViewport *viewport)
{
IDirectManipulationContent *content = NULL;
REFIID iid = &IID_IDirectManipulationContent;
float identity[6 ] = {1 ., 0 ., 0 ., 1 ., 0 ., 0 .};
HRESULT hr;
hr = IDirectManipulationViewport_GetPrimaryContent (viewport, iid, (void **)&content);
if (G_UNLIKELY (FAILED (hr)))
goto failed;
hr = IDirectManipulationContent_SyncContentTransform (content, identity,
G_N_ELEMENTS (identity));
if (G_UNLIKELY (FAILED (hr)))
goto failed;
failed:
gdk_win32_com_clear (&content);
}
static void
close_viewport (IDirectManipulationViewport **p_viewport)
{
IDirectManipulationViewport *viewport = *p_viewport;
if (viewport)
{
IDirectManipulationViewport_Abandon (viewport);
IUnknown_Release (viewport);
*p_viewport = NULL;
}
}
#define GDK_DISPLAY_GET_DMANIP_MANAGER(d) GDK_WIN32_DISPLAY(d)->dmanip_items != NULL ? \
(IDirectManipulationManager *) ((dmanip_items *)(GDK_WIN32_DISPLAY(d)->dmanip_items)->manager) : \
NULL
#define GDK_DISPLAY_GET_GET_POINTER_TYPE(d) GDK_WIN32_DISPLAY(d)->dmanip_items != NULL ? \
(getPointerType_t) ((dmanip_items *)(GDK_WIN32_DISPLAY(d)->dmanip_items)->getPointerType) : \
NULL
void
gdk_win32_display_close_dmanip_manager (GdkDisplay *display)
{
if (GDK_WIN32_DISPLAY (display)->dmanip_items != NULL)
{
IDirectManipulationManager *manager = GDK_DISPLAY_GET_DMANIP_MANAGER (display);
gdk_win32_com_clear (&manager);
g_clear_pointer (&GDK_WIN32_DISPLAY (display)->dmanip_items, g_free);
}
}
static void
create_viewport (GdkSurface *surface,
int gesture,
IDirectManipulationViewport **pViewport)
{
DIRECTMANIPULATION_CONFIGURATION configuration = 0 ;
HWND hwnd = GDK_SURFACE_HWND (surface);
IDirectManipulationViewportEventHandler *handler = NULL;
DWORD cookie = 0 ;
HRESULT hr;
IDirectManipulationManager *dmanipulation_manager = GDK_DISPLAY_GET_DMANIP_MANAGER (gdk_surface_get_display (surface));
hr = IDirectManipulationManager_CreateViewport (dmanipulation_manager, NULL, hwnd,
&IID_IDirectManipulationViewport,
(void **) pViewport);
if (G_UNLIKELY (FAILED (hr)))
goto failed;
switch (gesture)
{
case GESTURE_PAN:
configuration = DIRECTMANIPULATION_CONFIGURATION_INTERACTION |
DIRECTMANIPULATION_CONFIGURATION_TRANSLATION_X |
DIRECTMANIPULATION_CONFIGURATION_TRANSLATION_Y;
break ;
case GESTURE_ZOOM:
configuration = DIRECTMANIPULATION_CONFIGURATION_INTERACTION |
DIRECTMANIPULATION_CONFIGURATION_SCALING;
break ;
default :
g_assert_not_reached ();
break ;
}
handler = (IDirectManipulationViewportEventHandler*)
dmanip_event_handler_new (surface, gesture);
hr = IDirectManipulationViewport_AddEventHandler (*pViewport, hwnd, handler, &cookie);
if (G_UNLIKELY (FAILED (hr)))
goto failed;
hr = IDirectManipulationViewport_ActivateConfiguration (*pViewport, configuration);
if (G_UNLIKELY (FAILED (hr)))
goto failed;
hr = IDirectManipulationViewport_SetViewportOptions (*pViewport,
DIRECTMANIPULATION_VIEWPORT_OPTIONS_DISABLEPIXELSNAPPING);
if (G_UNLIKELY (FAILED (hr)))
goto failed;
hr = IDirectManipulationViewport_Enable (*pViewport);
if (G_UNLIKELY (FAILED (hr)))
goto failed;
// drop our initial reference
IUnknown_Release (handler);
return ;
failed:
gdk_win32_com_clear (&handler);
close_viewport (pViewport);
}
/* }}} */
/* {{{ Public */
void gdk_dmanipulation_initialize (GdkWin32Display *display)
{
if (display->dmanip_items == NULL)
{
IDirectManipulationManager *dmanipulation_manager;
getPointerType_t getPointerType;
HMODULE user32_mod;
HRESULT hr;
user32_mod = LoadLibraryW (L"user32.dll" );
if (!user32_mod)
{
WIN32_API_FAILED ("LoadLibraryW" );
return ;
}
getPointerType = (getPointerType_t)
GetProcAddress (user32_mod, "GetPointerType" );
if (!getPointerType)
return ;
if (!gdk_win32_ensure_com ())
return ;
display->dmanip_items = g_new0 (dmanip_items, 1 );
display->dmanip_items->getPointerType = getPointerType;
hr = CoCreateInstance (&CLSID_DirectManipulationManager,
NULL,
CLSCTX_INPROC_SERVER,
&IID_IDirectManipulationManager,
(LPVOID*)&dmanipulation_manager);
if (SUCCEEDED (hr))
display->dmanip_items->manager = dmanipulation_manager;
if (FAILED (hr))
{
if (hr == REGDB_E_CLASSNOTREG || hr == E_NOINTERFACE)
{
/* Not an error,
* DirectManipulation is not available */
}
else
{
hr_warn (hr);
}
}
}
}
void gdk_dmanipulation_initialize_surface (GdkSurface *surface)
{
GdkWin32Surface *surface_win32;
IDirectManipulationManager *dmanipulation_manager = GDK_DISPLAY_GET_DMANIP_MANAGER (gdk_surface_get_display (surface));
if (!dmanipulation_manager)
return ;
surface_win32 = GDK_WIN32_SURFACE (surface);
create_viewport (surface, GESTURE_PAN,
&surface_win32->dmanipulation_viewport_pan);
create_viewport (surface, GESTURE_ZOOM,
&surface_win32->dmanipulation_viewport_zoom);
IDirectManipulationManager_Activate (dmanipulation_manager,
GDK_SURFACE_HWND (surface));
}
void gdk_dmanipulation_finalize_surface (GdkSurface *surface)
{
GdkWin32Surface *surface_win32 = GDK_WIN32_SURFACE (surface);
IDirectManipulationManager_Deactivate (GDK_DISPLAY_GET_DMANIP_MANAGER (gdk_surface_get_display (surface)),
GDK_SURFACE_HWND (surface));
close_viewport (&surface_win32->dmanipulation_viewport_zoom);
close_viewport (&surface_win32->dmanipulation_viewport_pan);
}
void gdk_dmanipulation_maybe_add_contact (GdkSurface *surface,
MSG *msg)
{
POINTER_INPUT_TYPE type = PT_POINTER;
UINT32 pointer_id = GET_POINTERID_WPARAM (msg->wParam);
GdkDisplay *display = gdk_surface_get_display (surface);
IDirectManipulationManager *dmanipulation_manager = GDK_DISPLAY_GET_DMANIP_MANAGER (display);
getPointerType_t getPointerType = GDK_DISPLAY_GET_GET_POINTER_TYPE (display);
if (!dmanipulation_manager)
return ;
if (!getPointerType)
return ;
if G_UNLIKELY (!getPointerType (pointer_id, &type))
{
WIN32_API_FAILED_LOG_ONCE ("GetPointerType" );
return ;
}
if (type == PT_TOUCHPAD)
{
GdkWin32Surface *surface_win32 = GDK_WIN32_SURFACE (surface);
IDirectManipulationViewport_SetContact (surface_win32->dmanipulation_viewport_pan,
pointer_id);
IDirectManipulationViewport_SetContact (surface_win32->dmanipulation_viewport_zoom,
pointer_id);
}
}
/* }}} */
/* {{{ Utils */
static gpointer
util_get_next_sequence (void )
{
//TODO: sequence of other input types?
static unsigned char *sequence_counter = 0 ;
if (++sequence_counter == 0 )
sequence_counter++;
return sequence_counter;
}
static GdkModifierType
util_get_modifier_state (void )
{
GdkModifierType mask = 0 ;
BYTE kbd[256 ];
GetKeyboardState (kbd);
if (kbd[VK_SHIFT] & 0 x80)
mask |= GDK_SHIFT_MASK;
if (kbd[VK_CAPITAL] & 0 x80)
mask |= GDK_LOCK_MASK;
if (kbd[VK_CONTROL] & 0 x80)
mask |= GDK_CONTROL_MASK;
if (kbd[VK_MENU] & 0 x80)
mask |= GDK_ALT_MASK;
return mask;
}
/* }}} */
Messung V0.5 in Prozent C=98 H=92 G=94
¤ Dauer der Verarbeitung: 0.15 Sekunden
(vorverarbeitet am 2026-07-03)
¤
*© Formatika GbR, Deutschland
2026-07-09