Merge pull request #14346 from RandomShaper/adpod-mt-2.1
Implement multitouch on X11 and improve it on Windows (2.1)
This commit is contained in:
commit
7b97ef5d3c
|
@ -98,18 +98,8 @@ def is_active():
|
||||||
|
|
||||||
def get_name():
|
def get_name():
|
||||||
return "Windows"
|
return "Windows"
|
||||||
if (os.getenv("MINGW32_PREFIX")):
|
|
||||||
mingw32=os.getenv("MINGW32_PREFIX")
|
|
||||||
mingw = mingw32
|
|
||||||
if (os.getenv("MINGW64_PREFIX")):
|
|
||||||
mingw64=os.getenv("MINGW64_PREFIX")
|
|
||||||
|
|
||||||
|
|
||||||
return [
|
|
||||||
('mingw_prefix','Mingw Prefix',mingw32),
|
|
||||||
('mingw_prefix_64','Mingw Prefix 64 bits',mingw64),
|
|
||||||
]
|
|
||||||
|
|
||||||
def can_build():
|
def can_build():
|
||||||
|
|
||||||
if (os.name == "nt"):
|
if (os.name == "nt"):
|
||||||
|
@ -176,6 +166,8 @@ def get_opts():
|
||||||
return [
|
return [
|
||||||
('mingw_prefix', 'Mingw Prefix', mingw32),
|
('mingw_prefix', 'Mingw Prefix', mingw32),
|
||||||
('mingw_prefix_64', 'Mingw Prefix 64 bits', mingw64),
|
('mingw_prefix_64', 'Mingw Prefix 64 bits', mingw64),
|
||||||
|
# Targeted Windows version: Vista (and later)
|
||||||
|
('target_win_version', 'Targeted Windows version, >= 0x0600 (Vista)', '0x0600'),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@ -210,16 +202,13 @@ def configure(env):
|
||||||
|
|
||||||
env.Append(CPPPATH=['#platform/windows'])
|
env.Append(CPPPATH=['#platform/windows'])
|
||||||
|
|
||||||
# Targeted Windows version: Vista (and later)
|
|
||||||
winver = "0x0600" # Windows Vista is the minimum target for windows builds
|
|
||||||
|
|
||||||
env['is_mingw'] = False
|
env['is_mingw'] = False
|
||||||
if (os.name == "nt" and os.getenv("VCINSTALLDIR")):
|
if (os.name == "nt" and os.getenv("VCINSTALLDIR")):
|
||||||
# build using visual studio
|
# build using visual studio
|
||||||
env['ENV']['TMP'] = os.environ['TMP']
|
env['ENV']['TMP'] = os.environ['TMP']
|
||||||
env.Append(CPPPATH=['#platform/windows/include'])
|
env.Append(CPPPATH=['#platform/windows/include'])
|
||||||
env.Append(LIBPATH=['#platform/windows/lib'])
|
env.Append(LIBPATH=['#platform/windows/lib'])
|
||||||
env.Append(CCFLAGS=['/DWINVER=%s' % winver, '/D_WIN32_WINNT=%s' % winver])
|
env.Append(CCFLAGS=['/DWINVER=%s' % env['target_win_version'], '/D_WIN32_WINNT=%s' % env['target_win_version']])
|
||||||
|
|
||||||
if (env["target"] == "release"):
|
if (env["target"] == "release"):
|
||||||
|
|
||||||
|
|
|
@ -231,6 +231,18 @@ bool OS_Windows::can_draw() const {
|
||||||
|
|
||||||
void OS_Windows::_touch_event(bool p_pressed, int p_x, int p_y, int idx) {
|
void OS_Windows::_touch_event(bool p_pressed, int p_x, int p_y, int idx) {
|
||||||
|
|
||||||
|
#if WINVER >= 0x0601 // for windows 7
|
||||||
|
// Defensive
|
||||||
|
if (touch_state.has(idx) == p_pressed)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (p_pressed) {
|
||||||
|
touch_state.insert(idx, Point2i(p_x, p_y));
|
||||||
|
} else {
|
||||||
|
touch_state.erase(idx);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
InputEvent event;
|
InputEvent event;
|
||||||
event.type = InputEvent::SCREEN_TOUCH;
|
event.type = InputEvent::SCREEN_TOUCH;
|
||||||
event.ID = ++last_id;
|
event.ID = ++last_id;
|
||||||
|
@ -248,6 +260,18 @@ void OS_Windows::_touch_event(bool p_pressed, int p_x, int p_y, int idx) {
|
||||||
|
|
||||||
void OS_Windows::_drag_event(int p_x, int p_y, int idx) {
|
void OS_Windows::_drag_event(int p_x, int p_y, int idx) {
|
||||||
|
|
||||||
|
#if WINVER >= 0x0601 // for windows 7
|
||||||
|
Map<int, Point2i>::Element *curr = touch_state.find(idx);
|
||||||
|
// Defensive
|
||||||
|
if (!curr)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (curr->get() == Point2i(p_x, p_y))
|
||||||
|
return;
|
||||||
|
|
||||||
|
curr->get() = Point2i(p_x, p_y);
|
||||||
|
#endif
|
||||||
|
|
||||||
InputEvent event;
|
InputEvent event;
|
||||||
event.type = InputEvent::SCREEN_DRAG;
|
event.type = InputEvent::SCREEN_DRAG;
|
||||||
event.ID = ++last_id;
|
event.ID = ++last_id;
|
||||||
|
@ -292,6 +316,17 @@ LRESULT OS_Windows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
||||||
return 0; // Return To The Message Loop
|
return 0; // Return To The Message Loop
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case WM_KILLFOCUS: {
|
||||||
|
|
||||||
|
#if WINVER >= 0x0601 // for windows 7
|
||||||
|
// Release every touch to avoid sticky points
|
||||||
|
for (Map<int, Point2i>::Element *E = touch_state.front(); E; E = E->next()) {
|
||||||
|
_touch_event(false, E->get().x, E->get().y, E->key());
|
||||||
|
}
|
||||||
|
touch_state.clear();
|
||||||
|
#endif
|
||||||
|
} break;
|
||||||
|
|
||||||
case WM_PAINT:
|
case WM_PAINT:
|
||||||
|
|
||||||
Main::force_redraw();
|
Main::force_redraw();
|
||||||
|
@ -682,7 +717,7 @@ LRESULT OS_Windows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
||||||
_drag_event(ti.x / 100, ti.y / 100, ti.dwID);
|
_drag_event(ti.x / 100, ti.y / 100, ti.dwID);
|
||||||
} else if (ti.dwFlags & (TOUCHEVENTF_UP | TOUCHEVENTF_DOWN)) {
|
} else if (ti.dwFlags & (TOUCHEVENTF_UP | TOUCHEVENTF_DOWN)) {
|
||||||
|
|
||||||
_touch_event(ti.dwFlags & TOUCHEVENTF_DOWN != 0, ti.x / 100, ti.y / 100, ti.dwID);
|
_touch_event(ti.dwFlags & TOUCHEVENTF_DOWN, ti.x / 100, ti.y / 100, ti.dwID);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
bHandled = TRUE;
|
bHandled = TRUE;
|
||||||
|
@ -1080,7 +1115,9 @@ void OS_Windows::initialize(const VideoMode &p_desired, int p_video_driver, int
|
||||||
tme.dwHoverTime = HOVER_DEFAULT;
|
tme.dwHoverTime = HOVER_DEFAULT;
|
||||||
TrackMouseEvent(&tme);
|
TrackMouseEvent(&tme);
|
||||||
|
|
||||||
//RegisterTouchWindow(hWnd, 0); // Windows 7
|
#if WINVER >= 0x0601 // for windows 7
|
||||||
|
RegisterTouchWindow(hWnd, 0); // Windows 7
|
||||||
|
#endif
|
||||||
|
|
||||||
_ensure_data_dir();
|
_ensure_data_dir();
|
||||||
|
|
||||||
|
@ -1188,6 +1225,9 @@ void OS_Windows::finalize() {
|
||||||
|
|
||||||
memdelete(joystick);
|
memdelete(joystick);
|
||||||
memdelete(input);
|
memdelete(input);
|
||||||
|
#if WINVER >= 0x0601 // for windows 7
|
||||||
|
touch_state.clear();
|
||||||
|
#endif
|
||||||
|
|
||||||
visual_server->finish();
|
visual_server->finish();
|
||||||
memdelete(visual_server);
|
memdelete(visual_server);
|
||||||
|
|
|
@ -130,6 +130,9 @@ class OS_Windows : public OS {
|
||||||
|
|
||||||
InputDefault *input;
|
InputDefault *input;
|
||||||
joystick_windows *joystick;
|
joystick_windows *joystick;
|
||||||
|
#if WINVER >= 0x0601 // for windows 7
|
||||||
|
Map<int, Point2i> touch_state;
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef WASAPI_ENABLED
|
#ifdef WASAPI_ENABLED
|
||||||
AudioDriverWASAPI driver_wasapi;
|
AudioDriverWASAPI driver_wasapi;
|
||||||
|
|
|
@ -59,6 +59,7 @@ def get_opts():
|
||||||
('pulseaudio', 'Detect & Use pulseaudio', 'yes'),
|
('pulseaudio', 'Detect & Use pulseaudio', 'yes'),
|
||||||
('udev', 'Use udev for gamepad connection callbacks', 'no'),
|
('udev', 'Use udev for gamepad connection callbacks', 'no'),
|
||||||
('debug_release', 'Add debug symbols to release version', 'no'),
|
('debug_release', 'Add debug symbols to release version', 'no'),
|
||||||
|
('touch', 'Enable touch events', 'yes'),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@ -145,6 +146,14 @@ def configure(env):
|
||||||
env.ParseConfig('pkg-config xcursor --cflags --libs')
|
env.ParseConfig('pkg-config xcursor --cflags --libs')
|
||||||
env.ParseConfig('pkg-config xrandr --cflags --libs')
|
env.ParseConfig('pkg-config xrandr --cflags --libs')
|
||||||
|
|
||||||
|
if (env['touch'] == 'yes'):
|
||||||
|
x11_error = os.system("pkg-config xi --modversion > /dev/null ")
|
||||||
|
if (x11_error):
|
||||||
|
print("xi not found.. cannot build with touch. Aborting.")
|
||||||
|
sys.exit(255)
|
||||||
|
env.ParseConfig('pkg-config xi --cflags --libs')
|
||||||
|
env.Append(CPPFLAGS=['-DTOUCH_ENABLED'])
|
||||||
|
|
||||||
if (env['builtin_openssl'] == 'no'):
|
if (env['builtin_openssl'] == 'no'):
|
||||||
env.ParseConfig('pkg-config openssl --cflags --libs')
|
env.ParseConfig('pkg-config openssl --cflags --libs')
|
||||||
|
|
||||||
|
|
|
@ -154,6 +154,50 @@ void OS_X11::initialize(const VideoMode &p_desired, int p_video_driver, int p_au
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef TOUCH_ENABLED
|
||||||
|
if (!XQueryExtension(x11_display, "XInputExtension", &touch.opcode, &event_base, &error_base)) {
|
||||||
|
fprintf(stderr, "XInput extension not available");
|
||||||
|
} else {
|
||||||
|
// 2.2 is the first release with multitouch
|
||||||
|
int xi_major = 2;
|
||||||
|
int xi_minor = 2;
|
||||||
|
if (XIQueryVersion(x11_display, &xi_major, &xi_minor) != Success) {
|
||||||
|
fprintf(stderr, "XInput 2.2 not available (server supports %d.%d)\n", xi_major, xi_minor);
|
||||||
|
touch.opcode = 0;
|
||||||
|
} else {
|
||||||
|
int dev_count;
|
||||||
|
XIDeviceInfo *info = XIQueryDevice(x11_display, XIAllDevices, &dev_count);
|
||||||
|
|
||||||
|
for (int i = 0; i < dev_count; i++) {
|
||||||
|
XIDeviceInfo *dev = &info[i];
|
||||||
|
if (!dev->enabled)
|
||||||
|
continue;
|
||||||
|
/*if (dev->use != XIMasterPointer)
|
||||||
|
continue;*/
|
||||||
|
|
||||||
|
bool direct_touch = false;
|
||||||
|
for (int j = 0; j < dev->num_classes; j++) {
|
||||||
|
if (dev->classes[j]->type == XITouchClass && ((XITouchClassInfo *)dev->classes[j])->mode == XIDirectTouch) {
|
||||||
|
direct_touch = true;
|
||||||
|
printf("%d) %d %s\n", i, dev->attachment, dev->name);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (direct_touch) {
|
||||||
|
touch.devices.push_back(dev->deviceid);
|
||||||
|
fprintf(stderr, "Using touch device: %s\n", dev->name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
XIFreeDeviceInfo(info);
|
||||||
|
|
||||||
|
if (!touch.devices.size()) {
|
||||||
|
fprintf(stderr, "No suitable touch device found\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
xim = XOpenIM(x11_display, NULL, NULL, NULL);
|
xim = XOpenIM(x11_display, NULL, NULL, NULL);
|
||||||
|
|
||||||
if (xim == NULL) {
|
if (xim == NULL) {
|
||||||
|
@ -320,6 +364,32 @@ void OS_X11::initialize(const VideoMode &p_desired, int p_video_driver, int p_au
|
||||||
|
|
||||||
XChangeWindowAttributes(x11_display, x11_window, CWEventMask, &new_attr);
|
XChangeWindowAttributes(x11_display, x11_window, CWEventMask, &new_attr);
|
||||||
|
|
||||||
|
#ifdef TOUCH_ENABLED
|
||||||
|
if (touch.devices.size()) {
|
||||||
|
|
||||||
|
// Must be alive after this block
|
||||||
|
static unsigned char mask_data[XIMaskLen(XI_LASTEVENT)] = {};
|
||||||
|
|
||||||
|
touch.event_mask.deviceid = XIAllMasterDevices;
|
||||||
|
touch.event_mask.mask_len = sizeof(mask_data);
|
||||||
|
touch.event_mask.mask = mask_data;
|
||||||
|
|
||||||
|
XISetMask(touch.event_mask.mask, XI_TouchBegin);
|
||||||
|
XISetMask(touch.event_mask.mask, XI_TouchUpdate);
|
||||||
|
XISetMask(touch.event_mask.mask, XI_TouchEnd);
|
||||||
|
XISetMask(touch.event_mask.mask, XI_TouchOwnership);
|
||||||
|
|
||||||
|
XISelectEvents(x11_display, x11_window, &touch.event_mask, 1);
|
||||||
|
|
||||||
|
XIClearMask(touch.event_mask.mask, XI_TouchOwnership);
|
||||||
|
|
||||||
|
// Grab touch devices to avoid OS gesture interference
|
||||||
|
for (int i = 0; i < touch.devices.size(); ++i) {
|
||||||
|
XIGrabDevice(x11_display, touch.devices[i], x11_window, CurrentTime, None, XIGrabModeAsync, XIGrabModeAsync, False, &touch.event_mask);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
XClassHint *classHint;
|
XClassHint *classHint;
|
||||||
|
|
||||||
/* set the titlebar name */
|
/* set the titlebar name */
|
||||||
|
@ -473,6 +543,10 @@ void OS_X11::finalize() {
|
||||||
|
|
||||||
#ifdef JOYDEV_ENABLED
|
#ifdef JOYDEV_ENABLED
|
||||||
memdelete(joystick);
|
memdelete(joystick);
|
||||||
|
#endif
|
||||||
|
#ifdef TOUCH_ENABLED
|
||||||
|
touch.devices.clear();
|
||||||
|
touch.state.clear();
|
||||||
#endif
|
#endif
|
||||||
memdelete(input);
|
memdelete(input);
|
||||||
|
|
||||||
|
@ -1294,6 +1368,73 @@ void OS_X11::process_xevents() {
|
||||||
XEvent event;
|
XEvent event;
|
||||||
XNextEvent(x11_display, &event);
|
XNextEvent(x11_display, &event);
|
||||||
|
|
||||||
|
#ifdef TOUCH_ENABLED
|
||||||
|
if (XGetEventData(x11_display, &event.xcookie)) {
|
||||||
|
|
||||||
|
if (event.xcookie.extension == touch.opcode) {
|
||||||
|
|
||||||
|
InputEvent input_event;
|
||||||
|
input_event.ID = ++event_id;
|
||||||
|
input_event.device = 0;
|
||||||
|
|
||||||
|
XIDeviceEvent *event_data = (XIDeviceEvent *)event.xcookie.data;
|
||||||
|
int index = event_data->detail;
|
||||||
|
Point2i pos = Point2i(event_data->event_x, event_data->event_y);
|
||||||
|
|
||||||
|
switch (event_data->evtype) {
|
||||||
|
|
||||||
|
case XI_TouchBegin: // Fall-through
|
||||||
|
XIAllowTouchEvents(x11_display, event_data->deviceid, event_data->detail, x11_window, XIAcceptTouch);
|
||||||
|
|
||||||
|
case XI_TouchEnd: {
|
||||||
|
|
||||||
|
bool is_begin = event_data->evtype == XI_TouchBegin;
|
||||||
|
|
||||||
|
input_event.type = InputEvent::SCREEN_TOUCH;
|
||||||
|
input_event.screen_touch.index = index;
|
||||||
|
input_event.screen_touch.x = pos.x;
|
||||||
|
input_event.screen_touch.y = pos.y;
|
||||||
|
input_event.screen_touch.pressed = is_begin;
|
||||||
|
|
||||||
|
if (is_begin) {
|
||||||
|
if (touch.state.has(index)) // Defensive
|
||||||
|
break;
|
||||||
|
touch.state[index] = pos;
|
||||||
|
input->parse_input_event(input_event);
|
||||||
|
} else {
|
||||||
|
if (!touch.state.has(index)) // Defensive
|
||||||
|
break;
|
||||||
|
touch.state.erase(index);
|
||||||
|
input->parse_input_event(input_event);
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case XI_TouchUpdate: {
|
||||||
|
|
||||||
|
Map<int, Point2i>::Element *curr_pos_elem = touch.state.find(index);
|
||||||
|
if (!curr_pos_elem) // Defensive
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (curr_pos_elem->value() != pos) {
|
||||||
|
|
||||||
|
input_event.type = InputEvent::SCREEN_DRAG;
|
||||||
|
input_event.screen_drag.index = index;
|
||||||
|
input_event.screen_drag.x = pos.x;
|
||||||
|
input_event.screen_drag.y = pos.y;
|
||||||
|
input_event.screen_drag.relative_x = pos.x - curr_pos_elem->value().x;
|
||||||
|
input_event.screen_drag.relative_y = pos.y - curr_pos_elem->value().y;
|
||||||
|
input->parse_input_event(input_event);
|
||||||
|
|
||||||
|
curr_pos_elem->value() = pos;
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
XFreeEventData(x11_display, &event.xcookie);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
switch (event.type) {
|
switch (event.type) {
|
||||||
case Expose:
|
case Expose:
|
||||||
Main::force_redraw();
|
Main::force_redraw();
|
||||||
|
@ -1331,6 +1472,12 @@ void OS_X11::process_xevents() {
|
||||||
ButtonPressMask | ButtonReleaseMask | PointerMotionMask,
|
ButtonPressMask | ButtonReleaseMask | PointerMotionMask,
|
||||||
GrabModeAsync, GrabModeAsync, x11_window, None, CurrentTime);
|
GrabModeAsync, GrabModeAsync, x11_window, None, CurrentTime);
|
||||||
}
|
}
|
||||||
|
#ifdef TOUCH_ENABLED
|
||||||
|
// Grab touch devices to avoid OS gesture interference
|
||||||
|
for (int i = 0; i < touch.devices.size(); ++i) {
|
||||||
|
XIGrabDevice(x11_display, touch.devices[i], x11_window, CurrentTime, None, XIGrabModeAsync, XIGrabModeAsync, False, &touch.event_mask);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case FocusOut:
|
case FocusOut:
|
||||||
|
@ -1339,6 +1486,25 @@ void OS_X11::process_xevents() {
|
||||||
//dear X11, I try, I really try, but you never work, you do whathever you want.
|
//dear X11, I try, I really try, but you never work, you do whathever you want.
|
||||||
XUngrabPointer(x11_display, CurrentTime);
|
XUngrabPointer(x11_display, CurrentTime);
|
||||||
}
|
}
|
||||||
|
#ifdef TOUCH_ENABLED
|
||||||
|
// Ungrab touch devices so input works as usual while we are unfocused
|
||||||
|
for (int i = 0; i < touch.devices.size(); ++i) {
|
||||||
|
XIUngrabDevice(x11_display, touch.devices[i], CurrentTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Release every pointer to avoid sticky points
|
||||||
|
for (Map<int, Point2i>::Element *E = touch.state.front(); E; E = E->next()) {
|
||||||
|
|
||||||
|
InputEvent input_event;
|
||||||
|
input_event.type = InputEvent::SCREEN_TOUCH;
|
||||||
|
input_event.screen_touch.index = E->key();
|
||||||
|
input_event.screen_touch.x = E->get().x;
|
||||||
|
input_event.screen_touch.y = E->get().y;
|
||||||
|
input_event.screen_touch.pressed = false;
|
||||||
|
input->parse_input_event(input_event);
|
||||||
|
}
|
||||||
|
touch.state.clear();
|
||||||
|
#endif
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ConfigureNotify:
|
case ConfigureNotify:
|
||||||
|
|
|
@ -55,6 +55,9 @@
|
||||||
#include <X11/Xlib.h>
|
#include <X11/Xlib.h>
|
||||||
#include <X11/extensions/Xrandr.h>
|
#include <X11/extensions/Xrandr.h>
|
||||||
#include <X11/keysym.h>
|
#include <X11/keysym.h>
|
||||||
|
#ifdef TOUCH_ENABLED
|
||||||
|
#include <X11/extensions/XInput2.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
// Hints for X11 fullscreen
|
// Hints for X11 fullscreen
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
@ -122,6 +125,14 @@ class OS_X11 : public OS_Unix {
|
||||||
uint64_t last_click_ms;
|
uint64_t last_click_ms;
|
||||||
unsigned int event_id;
|
unsigned int event_id;
|
||||||
uint32_t last_button_state;
|
uint32_t last_button_state;
|
||||||
|
#ifdef TOUCH_ENABLED
|
||||||
|
struct {
|
||||||
|
int opcode;
|
||||||
|
Vector<int> devices;
|
||||||
|
XIEventMask event_mask;
|
||||||
|
Map<int, Point2i> state;
|
||||||
|
} touch;
|
||||||
|
#endif
|
||||||
|
|
||||||
PhysicsServer *physics_server;
|
PhysicsServer *physics_server;
|
||||||
unsigned int get_mouse_button_state(unsigned int p_x11_state);
|
unsigned int get_mouse_button_state(unsigned int p_x11_state);
|
||||||
|
|
Loading…
Reference in a new issue