touch: fix indicators showing in touch overlay, disable additional gestures (#791)

This commit is contained in:
bicarus
2026-07-11 00:24:36 -07:00
committed by GitHub
parent c3fcf2f417
commit f52eaea76c
8 changed files with 191 additions and 67 deletions
+1 -1
View File
@@ -645,7 +645,7 @@ set(SOURCE_FILES ${SOURCE_FILES}
# touch
touch/touch.cpp
touch/touch_indicators.cpp
touch/touch_gestures.cpp
touch/win7.cpp
touch/win8.cpp
+3 -3
View File
@@ -23,7 +23,7 @@
#include "launcher/shutdown.h"
#include "overlay/overlay.h"
#include "touch/touch.h"
#include "touch/touch_indicators.h"
#include "touch/touch_gestures.h"
#include "util/detour.h"
#include "util/logging.h"
#include "util/fileutils.h"
@@ -627,7 +627,7 @@ static HWND WINAPI CreateWindowExA_hook(DWORD dwExStyle, LPCSTR lpClassName, LPC
}
}
disable_touch_indicators(result);
disable_touch_gestures(result);
log_misc(
"graphics",
"CreateWindowExA returned {}, {}",
@@ -722,7 +722,7 @@ static HWND WINAPI CreateWindowExW_hook(DWORD dwExStyle, LPCWSTR lpClassName, LP
fmt::ptr(result),
lpWindowName ? ws2s(lpWindowName) : "(null)");
disable_touch_indicators(result);
disable_touch_gestures(result);
return result;
}
+4
View File
@@ -22,6 +22,7 @@
#include "util/utils.h"
#include "handler.h"
#include "touch_gestures.h"
#include "win7.h"
#include "win8.h"
@@ -751,6 +752,9 @@ void touch_create_wnd(HWND hWnd, bool overlay) {
ShowWindow(touch_window, SW_SHOWNOACTIVATE);
UpdateWindow(touch_window);
// disable the OS touch contact visualization for our own touch window
disable_touch_gestures(touch_window);
// register
touch_register_window(touch_window);
+168
View File
@@ -0,0 +1,168 @@
// set version to Windows 8 to enable Windows 8 touch functions
#define _WIN32_WINNT 0x0602
#include <propsys.h>
#include <mutex>
#include "touch_gestures.h"
#include "util/libutils.h"
#include "util/logging.h"
// tablet/pen service flags (MicrosoftTabletPenServiceProperty atom)
// these are not present in the mingw headers
#ifndef TABLET_DISABLE_PRESSANDHOLD
#define TABLET_DISABLE_PRESSANDHOLD 0x00000001
#define TABLET_DISABLE_PENTAPFEEDBACK 0x00000008
#define TABLET_DISABLE_PENBARRELFEEDBACK 0x00000010
#define TABLET_DISABLE_TOUCHUIFORCEON 0x00000100
#define TABLET_DISABLE_TOUCHUIFORCEOFF 0x00000200
#define TABLET_DISABLE_TOUCHSWITCH 0x00008000
#define TABLET_DISABLE_FLICKS 0x00010000
#define TABLET_DISABLE_SMOOTHSCROLLING 0x00080000
#define TABLET_DISABLE_FLICKFALLBACKKEYS 0x00100000
#endif
static const char TABLET_ATOM_NAME[] = "MicrosoftTabletPenServiceProperty";
// PKEY_EdgeGesture_DisableTouchWhenFullscreen format GUID
// (not defined in the mingw headers); defined inline to avoid an initguid.h
// symbol clash with touch/win8.cpp which declares the same name
// {32CE38B2-2C9A-41B1-9BC5-B3784394AA44}
static const GUID EDGEGESTURE_DISABLE_FMT =
{ 0x32CE38B2, 0x2C9A, 0x41B1, { 0x9B, 0xC5, 0xB3, 0x78, 0x43, 0x94, 0xAA, 0x44 } };
static HINSTANCE USER32_INSTANCE = nullptr;
typedef BOOL (WINAPI *SetWindowFeedbackSetting_t)(HWND, FEEDBACK_TYPE, DWORD, UINT32, const VOID *);
static SetWindowFeedbackSetting_t pSetWindowFeedbackSetting = nullptr;
static HINSTANCE SHELL32_INSTANCE = nullptr;
typedef HRESULT (WINAPI *SHGetPropertyStoreForWindow_t)(HWND, REFIID, void **);
static SHGetPropertyStoreForWindow_t pSHGetPropertyStoreForWindow = nullptr;
static std::once_flag INIT_FLAG;
// resolve the libraries and entry points once; disable_touch_gestures may be
// called concurrently from the CreateWindowEx hooks and the touch thread
static void init_procs() {
std::call_once(INIT_FLAG, []() {
USER32_INSTANCE = libutils::load_library("user32.dll");
if (USER32_INSTANCE != nullptr) {
pSetWindowFeedbackSetting = libutils::try_proc<SetWindowFeedbackSetting_t>(
USER32_INSTANCE, "SetWindowFeedbackSetting");
}
SHELL32_INSTANCE = libutils::try_library("shell32.dll");
if (SHELL32_INSTANCE != nullptr) {
pSHGetPropertyStoreForWindow = libutils::try_proc<SHGetPropertyStoreForWindow_t>(
SHELL32_INSTANCE, "SHGetPropertyStoreForWindow");
}
});
}
static void disable_feedback_visuals(HWND hwnd) {
if (pSetWindowFeedbackSetting == nullptr) {
return;
}
BOOL enabled = FALSE;
pSetWindowFeedbackSetting(
hwnd,
FEEDBACK_TOUCH_CONTACTVISUALIZATION,
0, sizeof(enabled), &enabled);
pSetWindowFeedbackSetting(
hwnd,
FEEDBACK_TOUCH_TAP,
0, sizeof(enabled), &enabled);
pSetWindowFeedbackSetting(
hwnd,
FEEDBACK_TOUCH_DOUBLETAP,
0, sizeof(enabled), &enabled);
pSetWindowFeedbackSetting(
hwnd,
FEEDBACK_TOUCH_PRESSANDHOLD,
0, sizeof(enabled), &enabled);
pSetWindowFeedbackSetting(
hwnd,
FEEDBACK_TOUCH_RIGHTTAP,
0, sizeof(enabled), &enabled);
pSetWindowFeedbackSetting(
hwnd,
FEEDBACK_GESTURE_PRESSANDTAP,
0, sizeof(enabled), &enabled);
}
static void disable_gesture_behaviors(HWND hwnd) {
// the tablet/pen service reads this window property to decide which
// touch/pen gestures to suppress for the window. this covers:
// - press-and-hold (touch right-click / long-press ring)
// - pen tap/barrel feedback
// - flicks (edge/directional flick navigation gestures)
// - the touch keyboard invocation UI
// - smooth scrolling / flick fallback keys
DWORD tablet_flags = TABLET_DISABLE_PRESSANDHOLD |
TABLET_DISABLE_PENTAPFEEDBACK |
TABLET_DISABLE_PENBARRELFEEDBACK |
TABLET_DISABLE_FLICKS |
TABLET_DISABLE_TOUCHUIFORCEOFF |
TABLET_DISABLE_TOUCHSWITCH |
TABLET_DISABLE_SMOOTHSCROLLING |
TABLET_DISABLE_FLICKFALLBACKKEYS;
ATOM atom_id = GlobalAddAtomA(TABLET_ATOM_NAME);
if (atom_id > 0) {
SetPropA(hwnd, TABLET_ATOM_NAME, (HANDLE) ((ULONG_PTR) tablet_flags));
}
}
static void disable_edge_gestures(HWND hwnd) {
// suppress the touch edge swipes (charms/back/app bar) for the window while
// it is fullscreen. this is normally done by the touch handler's
// window_register(), but that is not called for every handler (e.g. the
// rawinput handler is a no-op), so apply it here as well.
if (pSHGetPropertyStoreForWindow == nullptr) {
return;
}
IPropertyStore *ps = nullptr;
HRESULT hr = pSHGetPropertyStoreForWindow(hwnd, IID_IPropertyStore, (void **) &ps);
if (SUCCEEDED(hr) && ps != nullptr) {
PROPERTYKEY key = { EDGEGESTURE_DISABLE_FMT, 2 };
PROPVARIANT var {};
var.vt = VT_BOOL;
var.boolVal = VARIANT_TRUE;
hr = ps->SetValue(key, var);
ps->Release();
if (FAILED(hr)) {
log_warning("touch_gestures",
"failed to disable edge gestures on HWND={}", fmt::ptr(hwnd));
}
}
}
void disable_touch_gestures(HWND hwnd) {
init_procs();
if (USER32_INSTANCE == nullptr) {
return;
}
log_misc("touch_gestures",
"disable visual feedback and gestures for touch events for HWND={}", fmt::ptr(hwnd));
// disable the visual feedback (contact circles, tap/double-tap stars, etc.)
disable_feedback_visuals(hwnd);
// disable the actual gesture behaviors (press-and-hold, flicks, etc.)
disable_gesture_behaviors(hwnd);
// disable the fullscreen touch edge swipes
disable_edge_gestures(hwnd);
}
+8
View File
@@ -0,0 +1,8 @@
#pragma once
#include <windows.h>
// disable the OS touch UX for a window: visual feedback (contact circles,
// tap/press-and-hold indicators) and gesture behaviors (press-and-hold
// right-click, flicks, etc.)
void disable_touch_gestures(HWND hwnd);
-57
View File
@@ -1,57 +0,0 @@
// set version to Windows 8 to enable Windows 8 touch functions
#define _WIN32_WINNT 0x0602
#include "touch_indicators.h"
#include "util/libutils.h"
#include "util/logging.h"
static HINSTANCE USER32_INSTANCE = nullptr;
typedef BOOL (WINAPI *SetWindowFeedbackSetting_t)(HWND, FEEDBACK_TYPE, DWORD, UINT32, const VOID *);
static SetWindowFeedbackSetting_t pSetWindowFeedbackSetting = nullptr;
void disable_touch_indicators(HWND hwnd) {
if (USER32_INSTANCE == nullptr) {
USER32_INSTANCE = libutils::load_library("user32.dll");
}
if (USER32_INSTANCE == nullptr) {
return;
}
if (pSetWindowFeedbackSetting == nullptr) {
pSetWindowFeedbackSetting = libutils::try_proc<SetWindowFeedbackSetting_t>(
USER32_INSTANCE, "SetWindowFeedbackSetting");
}
if (pSetWindowFeedbackSetting == nullptr) {
return;
}
log_info("touch_indicators", "disable visual feedback for touch events for HWND={}", fmt::ptr(hwnd));
BOOL enabled = FALSE;
pSetWindowFeedbackSetting(
hwnd,
FEEDBACK_TOUCH_CONTACTVISUALIZATION,
0, sizeof(enabled), &enabled);
pSetWindowFeedbackSetting(
hwnd,
FEEDBACK_TOUCH_TAP,
0, sizeof(enabled), &enabled);
pSetWindowFeedbackSetting(
hwnd,
FEEDBACK_TOUCH_DOUBLETAP,
0, sizeof(enabled), &enabled);
pSetWindowFeedbackSetting(
hwnd,
FEEDBACK_TOUCH_PRESSANDHOLD,
0, sizeof(enabled), &enabled);
pSetWindowFeedbackSetting(
hwnd,
FEEDBACK_TOUCH_RIGHTTAP,
0, sizeof(enabled), &enabled);
pSetWindowFeedbackSetting(
hwnd,
FEEDBACK_GESTURE_PRESSANDTAP,
0, sizeof(enabled), &enabled);
}
-5
View File
@@ -1,5 +0,0 @@
#pragma once
#include <windows.h>
void disable_touch_indicators(HWND hwnd);
+7 -1
View File
@@ -114,10 +114,16 @@ bool Win8Handler::is_available() {
bool Win8Handler::window_register(HWND hWnd) {
// atom settings
// keep this in sync with touch/touch_gestures.cpp so registering a touch
// window does not downgrade the flag set applied there
DWORD dwHwndTabletProperty = TABLET_DISABLE_PRESSANDHOLD |
TABLET_DISABLE_PENTAPFEEDBACK |
TABLET_DISABLE_PENBARRELFEEDBACK |
TABLET_DISABLE_FLICKS;
TABLET_DISABLE_FLICKS |
TABLET_DISABLE_TOUCHUIFORCEOFF |
TABLET_DISABLE_TOUCHSWITCH |
TABLET_DISABLE_SMOOTHSCROLLING |
TABLET_DISABLE_FLICKFALLBACKKEYS;
// get atom ID
ATOM atomID = GlobalAddAtom(ATOM_NAME);