Switch from using Core events to using X Input events for pointer devices in order to differentiate between mouse events and touch events. Because FLTK doesn't understand X Input 2, we intercept these events and translate them to core events where possible.tags/v1.10.90
@@ -112,6 +112,9 @@ if(BUILD_STATIC) | |||
if(X11_Xrandr_LIB) | |||
set(X11_Xrandr_LIB "-Wl,-Bstatic -lXrandr -lXrender -Wl,-Bdynamic") | |||
endif() | |||
if(X11_Xi_LIB) | |||
set(X11_Xi_LIB "-Wl,-Bstatic -lXi -Wl,-Bdynamic") | |||
endif() | |||
endif() | |||
endif() | |||
@@ -15,6 +15,7 @@ set(VNCVIEWER_SOURCES | |||
Viewport.cxx | |||
parameters.cxx | |||
keysym2ucs.c | |||
touch.cxx | |||
vncviewer.cxx) | |||
if(WIN32) | |||
@@ -32,7 +33,7 @@ if(WIN32) | |||
elseif(APPLE) | |||
set(VNCVIEWER_SOURCES ${VNCVIEWER_SOURCES} cocoa.mm osx_to_qnum.c) | |||
else() | |||
set(VNCVIEWER_SOURCES ${VNCVIEWER_SOURCES} xkb_to_qnum.c) | |||
set(VNCVIEWER_SOURCES ${VNCVIEWER_SOURCES} XInputTouchHandler.cxx xkb_to_qnum.c) | |||
endif() | |||
if(WIN32) | |||
@@ -53,12 +54,12 @@ target_link_libraries(vncviewer rfb network rdr os ${FLTK_LIBRARIES} ${GETTEXT_L | |||
if(WIN32) | |||
target_link_libraries(vncviewer msimg32) | |||
endif() | |||
if(APPLE) | |||
elseif(APPLE) | |||
target_link_libraries(vncviewer "-framework Cocoa") | |||
target_link_libraries(vncviewer "-framework Carbon") | |||
target_link_libraries(vncviewer "-framework IOKit") | |||
else() | |||
target_link_libraries(vncviewer ${X11_Xi_LIB}) | |||
endif() | |||
install(TARGETS vncviewer DESTINATION ${CMAKE_INSTALL_FULL_BINDIR}) |
@@ -37,6 +37,7 @@ | |||
#include "CConn.h" | |||
#include "Surface.h" | |||
#include "Viewport.h" | |||
#include "touch.h" | |||
#include <FL/Fl.H> | |||
#include <FL/Fl_Image_Surface.H> | |||
@@ -967,23 +968,13 @@ void DesktopWindow::ungrabKeyboard() | |||
void DesktopWindow::grabPointer() | |||
{ | |||
#if !defined(WIN32) && !defined(__APPLE__) | |||
int ret; | |||
// We also need to grab the pointer as some WMs like to grab buttons | |||
// combined with modifies (e.g. Alt+Button0 in metacity). | |||
ret = XGrabPointer(fl_display, fl_xid(this), True, | |||
ButtonPressMask|ButtonReleaseMask| | |||
ButtonMotionMask|PointerMotionMask, | |||
GrabModeAsync, GrabModeAsync, | |||
None, None, CurrentTime); | |||
if (ret) { | |||
// Having a button pressed prevents us from grabbing, we make | |||
// a new attempt in fltkHandle() | |||
if (ret == AlreadyGrabbed) | |||
return; | |||
vlog.error(_("Failure grabbing mouse")); | |||
// Having a button pressed prevents us from grabbing, we make | |||
// a new attempt in fltkHandle() | |||
if (!x11_grab_pointer(fl_xid(this))) | |||
return; | |||
} | |||
#endif | |||
mouseGrabbed = true; | |||
@@ -993,8 +984,9 @@ void DesktopWindow::grabPointer() | |||
void DesktopWindow::ungrabPointer() | |||
{ | |||
mouseGrabbed = false; | |||
#if !defined(WIN32) && !defined(__APPLE__) | |||
XUngrabPointer(fl_display, CurrentTime); | |||
x11_ungrab_pointer(fl_xid(this)); | |||
#endif | |||
} | |||
@@ -0,0 +1,332 @@ | |||
/* Copyright 2019 Aaron Sowry for Cendio AB | |||
* Copyright 2019-2020 Pierre Ossman for Cendio AB | |||
* | |||
* This is free software; you can redistribute it and/or modify | |||
* it under the terms of the GNU General Public License as published by | |||
* the Free Software Foundation; either version 2 of the License, or | |||
* (at your option) any later version. | |||
* | |||
* This software 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. | |||
* | |||
* You should have received a copy of the GNU General Public License | |||
* along with this software; if not, write to the Free Software | |||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, | |||
* USA. | |||
*/ | |||
#ifdef HAVE_CONFIG_H | |||
#include <config.h> | |||
#endif | |||
#include <assert.h> | |||
#include <string.h> | |||
#include <X11/extensions/XInput2.h> | |||
#include <X11/extensions/XI2.h> | |||
#include <FL/x.H> | |||
#include <rfb/LogWriter.h> | |||
#include "i18n.h" | |||
#include "XInputTouchHandler.h" | |||
static rfb::LogWriter vlog("XInputTouchHandler"); | |||
static bool grabbed = false; | |||
XInputTouchHandler::XInputTouchHandler(Window wnd) | |||
: wnd(wnd), fakeStateMask(0), trackingTouch(false) | |||
{ | |||
XIEventMask eventmask; | |||
unsigned char flags[XIMaskLen(XI_LASTEVENT)] = { 0 }; | |||
// Event delivery is broken when somebody else does a pointer grab, | |||
// so we need to listen to all devices and do filtering of master | |||
// devices manually | |||
eventmask.deviceid = XIAllDevices; | |||
eventmask.mask_len = sizeof(flags); | |||
eventmask.mask = flags; | |||
XISetMask(flags, XI_ButtonPress); | |||
XISetMask(flags, XI_Motion); | |||
XISetMask(flags, XI_ButtonRelease); | |||
XISetMask(flags, XI_TouchBegin); | |||
XISetMask(flags, XI_TouchUpdate); | |||
XISetMask(flags, XI_TouchEnd); | |||
// If something has a passive grab of touches (e.g. the window | |||
// manager wants to have its own gestures) then we won't get the | |||
// touch events until everyone who has a grab has indicated they | |||
// don't want these touches (via XIAllowTouchEvents()). | |||
// Unfortunately the touches are then replayed one touch point at | |||
// a time, meaning things will be delayed and out of order, | |||
// completely screwing up our gesture detection. Listening for | |||
// XI_TouchOwnership has the effect of giving us the touch events | |||
// right away, even if grabbing clients are also getting them. | |||
// | |||
// FIXME: We should really wait for the XI_TouchOwnership event | |||
// before it is safe to react to the gesture, otherwise we | |||
// might react to something that the window manager will | |||
// also react to. | |||
// | |||
if (!grabbed) | |||
XISetMask(flags, XI_TouchOwnership); | |||
XISelectEvents(fl_display, wnd, &eventmask, 1); | |||
} | |||
bool XInputTouchHandler::grabPointer() | |||
{ | |||
XIEventMask *curmasks; | |||
int num_masks; | |||
int ret, ndevices; | |||
XIDeviceInfo *devices, *device; | |||
bool gotGrab; | |||
// We grab for the same events as the window is currently interested in | |||
curmasks = XIGetSelectedEvents(fl_display, wnd, &num_masks); | |||
if (curmasks == NULL) { | |||
if (num_masks == -1) | |||
vlog.error(_("Unable to get X Input 2 event mask for window 0x%08lu"), wnd); | |||
else | |||
vlog.error(_("Window 0x%08lu has no X Input 2 event mask"), wnd); | |||
return false; | |||
} | |||
// Our windows should only have a single mask, which allows us to | |||
// simplify all the code handling the masks | |||
if (num_masks > 1) { | |||
vlog.error(_("Window 0x%08lu has more than one X Input 2 event mask"), wnd); | |||
return false; | |||
} | |||
devices = XIQueryDevice(fl_display, XIAllMasterDevices, &ndevices); | |||
// Iterate through available devices to find those which | |||
// provide pointer input, and attempt to grab all such devices. | |||
gotGrab = false; | |||
for (int i = 0; i < ndevices; i++) { | |||
device = &devices[i]; | |||
if (device->use != XIMasterPointer) | |||
continue; | |||
curmasks[0].deviceid = device->deviceid; | |||
ret = XIGrabDevice(fl_display, | |||
device->deviceid, | |||
wnd, | |||
CurrentTime, | |||
None, | |||
XIGrabModeAsync, | |||
XIGrabModeAsync, | |||
True, | |||
&(curmasks[0])); | |||
if (ret) { | |||
if (ret == XIAlreadyGrabbed) { | |||
continue; | |||
} else { | |||
vlog.error(_("Failure grabbing device %i"), device->deviceid); | |||
continue; | |||
} | |||
} | |||
gotGrab = true; | |||
} | |||
XIFreeDeviceInfo(devices); | |||
// Did we not even grab a single device? | |||
if (!gotGrab) | |||
return false; | |||
grabbed = true; | |||
return true; | |||
} | |||
void XInputTouchHandler::ungrabPointer() | |||
{ | |||
int ndevices; | |||
XIDeviceInfo *devices, *device; | |||
devices = XIQueryDevice(fl_display, XIAllMasterDevices, &ndevices); | |||
// Release all devices, hoping they are the same as when we | |||
// grabbed things | |||
for (int i = 0; i < ndevices; i++) { | |||
device = &devices[i]; | |||
if (device->use != XIMasterPointer) | |||
continue; | |||
XIUngrabDevice(fl_display, device->deviceid, CurrentTime); | |||
} | |||
XIFreeDeviceInfo(devices); | |||
grabbed = false; | |||
} | |||
void XInputTouchHandler::processEvent(const XIDeviceEvent* devev) | |||
{ | |||
// FLTK doesn't understand X Input events, and we've stopped | |||
// delivery of Core events by enabling the X Input ones. Make | |||
// FLTK happy by faking Core events based on the X Input ones. | |||
bool isMaster = devev->deviceid != devev->sourceid; | |||
// We're only interested in events from master devices | |||
if (!isMaster) { | |||
// However we need to accept TouchEnd from slave devices as they | |||
// might not get delivered if there is a pointer grab, see: | |||
// https://gitlab.freedesktop.org/xorg/xserver/-/issues/1016 | |||
if (devev->evtype != XI_TouchEnd) | |||
return; | |||
} | |||
// Avoid duplicate TouchEnd events, see above | |||
// FIXME: Doesn't handle floating slave devices | |||
if (isMaster && devev->evtype == XI_TouchEnd) | |||
return; | |||
if (devev->flags & XIPointerEmulated) { | |||
// We still want the server to do the scroll wheel to button thing | |||
// though, so keep those | |||
if (((devev->evtype == XI_ButtonPress) || | |||
(devev->evtype == XI_ButtonRelease)) && | |||
(devev->detail >= 4) && (devev->detail <= 7)) { | |||
; | |||
} else { | |||
// Sometimes the server incorrectly sends us various events with | |||
// this flag set, see: | |||
// https://gitlab.freedesktop.org/xorg/xserver/-/issues/1027 | |||
return; | |||
} | |||
} | |||
switch (devev->evtype) { | |||
case XI_Enter: | |||
case XI_Leave: | |||
// We get these when the mouse is grabbed implicitly, so just ignore them | |||
// https://gitlab.freedesktop.org/xorg/xserver/-/issues/1026 | |||
break; | |||
case XI_Motion: | |||
// FIXME: We also get XI_Motion for scroll wheel events, which | |||
// we might want to ignore | |||
fakeMotionEvent(devev); | |||
break; | |||
case XI_ButtonPress: | |||
fakeButtonEvent(true, devev->detail, devev); | |||
break; | |||
case XI_ButtonRelease: | |||
fakeButtonEvent(false, devev->detail, devev); | |||
break; | |||
case XI_TouchBegin: | |||
if (trackingTouch) | |||
break; | |||
// XInput2 wants us to explicitly accept touch sequences | |||
// for grabbed devices before it will pass events | |||
if (grabbed) { | |||
XIAllowTouchEvents(fl_display, | |||
devev->deviceid, | |||
devev->detail, | |||
devev->event, | |||
XIAcceptTouch); | |||
} | |||
trackingTouch = true; | |||
trackedTouchPoint = devev->detail; | |||
fakeMotionEvent(devev); | |||
fakeButtonEvent(true, Button1, devev); | |||
break; | |||
case XI_TouchUpdate: | |||
if (!trackingTouch || (devev->detail != trackedTouchPoint)) | |||
break; | |||
fakeMotionEvent(devev); | |||
break; | |||
case XI_TouchEnd: | |||
if (!trackingTouch || (devev->detail != trackedTouchPoint)) | |||
break; | |||
fakeButtonEvent(false, Button1, devev); | |||
trackingTouch = false; | |||
break; | |||
case XI_TouchOwnership: | |||
// FIXME: Currently ignored, see constructor | |||
break; | |||
} | |||
} | |||
void XInputTouchHandler::preparePointerEvent(XEvent* dst, const XIDeviceEvent* src) | |||
{ | |||
// XButtonEvent and XMotionEvent are almost identical, so we | |||
// don't have to care which it is for these fields | |||
dst->xbutton.serial = src->serial; | |||
dst->xbutton.display = src->display; | |||
dst->xbutton.window = src->event; | |||
dst->xbutton.root = src->root; | |||
dst->xbutton.subwindow = src->child; | |||
dst->xbutton.time = src->time; | |||
dst->xbutton.x = src->event_x; | |||
dst->xbutton.y = src->event_y; | |||
dst->xbutton.x_root = src->root_x; | |||
dst->xbutton.y_root = src->root_y; | |||
dst->xbutton.state = src->mods.effective; | |||
dst->xbutton.state |= ((src->buttons.mask[0] >> 1) & 0x1f) << 8; | |||
dst->xbutton.same_screen = True; | |||
} | |||
void XInputTouchHandler::fakeMotionEvent(const XIDeviceEvent* origEvent) | |||
{ | |||
XEvent fakeEvent; | |||
memset(&fakeEvent, 0, sizeof(XEvent)); | |||
fakeEvent.type = MotionNotify; | |||
fakeEvent.xmotion.is_hint = False; | |||
preparePointerEvent(&fakeEvent, origEvent); | |||
fakeEvent.xbutton.state |= fakeStateMask; | |||
pushFakeEvent(&fakeEvent); | |||
} | |||
void XInputTouchHandler::fakeButtonEvent(bool press, int button, | |||
const XIDeviceEvent* origEvent) | |||
{ | |||
XEvent fakeEvent; | |||
memset(&fakeEvent, 0, sizeof(XEvent)); | |||
fakeEvent.type = press ? ButtonPress : ButtonRelease; | |||
fakeEvent.xbutton.button = button; | |||
preparePointerEvent(&fakeEvent, origEvent); | |||
fakeEvent.xbutton.state |= fakeStateMask; | |||
pushFakeEvent(&fakeEvent); | |||
// Set/unset the bit for the correct button | |||
if (press) { | |||
fakeStateMask |= (1<<(7+button)); | |||
} else { | |||
fakeStateMask &= ~(1<<(7+button)); | |||
} | |||
} | |||
void XInputTouchHandler::pushFakeEvent(XEvent* event) | |||
{ | |||
// Perhaps use XPutBackEvent() to avoid round trip latency? | |||
XSendEvent(fl_display, event->xany.window, true, 0, event); | |||
} |
@@ -0,0 +1,48 @@ | |||
/* Copyright 2019 Aaron Sowry for Cendio AB | |||
* Copyright 2019-2020 Pierre Ossman for Cendio AB | |||
* | |||
* This is free software; you can redistribute it and/or modify | |||
* it under the terms of the GNU General Public License as published by | |||
* the Free Software Foundation; either version 2 of the License, or | |||
* (at your option) any later version. | |||
* | |||
* This software 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. | |||
* | |||
* You should have received a copy of the GNU General Public License | |||
* along with this software; if not, write to the Free Software | |||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, | |||
* USA. | |||
*/ | |||
#ifndef __XINPUTTOUCHHANDLER_H__ | |||
#define __XINPUTTOUCHHANDLER_H__ | |||
class XInputTouchHandler { | |||
public: | |||
XInputTouchHandler(Window wnd); | |||
bool grabPointer(); | |||
void ungrabPointer(); | |||
void processEvent(const XIDeviceEvent* devev); | |||
protected: | |||
void preparePointerEvent(XEvent* dst, const XIDeviceEvent* src); | |||
void fakeMotionEvent(const XIDeviceEvent* origEvent); | |||
void fakeButtonEvent(bool press, int button, | |||
const XIDeviceEvent* origEvent); | |||
private: | |||
void pushFakeEvent(XEvent* event); | |||
private: | |||
Window wnd; | |||
int fakeStateMask; | |||
bool trackingTouch; | |||
int trackedTouchPoint; | |||
}; | |||
#endif |
@@ -0,0 +1,220 @@ | |||
/* Copyright 2019-2020 Pierre Ossman <ossman@cendio.se> for Cendio AB | |||
* Copyright 2019 Aaron Sowry for Cendio AB | |||
* | |||
* This is free software; you can redistribute it and/or modify | |||
* it under the terms of the GNU General Public License as published by | |||
* the Free Software Foundation; either version 2 of the License, or | |||
* (at your option) any later version. | |||
* | |||
* This software 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. | |||
* | |||
* You should have received a copy of the GNU General Public License | |||
* along with this software; if not, write to the Free Software | |||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, | |||
* USA. | |||
*/ | |||
#ifdef HAVE_CONFIG_H | |||
#include <config.h> | |||
#endif | |||
#include <assert.h> | |||
#include <map> | |||
#if !defined(WIN32) && !defined(__APPLE__) | |||
#include <X11/extensions/XInput2.h> | |||
#include <X11/extensions/XI2.h> | |||
#endif | |||
#include <FL/Fl.H> | |||
#include <FL/x.H> | |||
#include <rfb/LogWriter.h> | |||
#include "i18n.h" | |||
#include "vncviewer.h" | |||
#if !defined(WIN32) && !defined(__APPLE__) | |||
#include "XInputTouchHandler.h" | |||
#endif | |||
#include "touch.h" | |||
static rfb::LogWriter vlog("Touch"); | |||
#if !defined(WIN32) && !defined(__APPLE__) | |||
static int xi_major; | |||
typedef std::map<Window, class XInputTouchHandler*> HandlerMap; | |||
static HandlerMap handlers; | |||
#endif | |||
#if !defined(WIN32) && !defined(__APPLE__) | |||
static void x11_change_touch_ownership(bool enable) | |||
{ | |||
HandlerMap::const_iterator iter; | |||
XIEventMask *curmasks; | |||
int num_masks; | |||
XIEventMask newmask; | |||
unsigned char mask[XIMaskLen(XI_LASTEVENT)] = { 0 }; | |||
newmask.mask = mask; | |||
newmask.mask_len = sizeof(mask); | |||
for (iter = handlers.begin(); iter != handlers.end(); ++iter) { | |||
curmasks = XIGetSelectedEvents(fl_display, iter->first, &num_masks); | |||
if (curmasks == NULL) { | |||
if (num_masks == -1) | |||
vlog.error(_("Unable to get X Input 2 event mask for window 0x%08lu"), iter->first); | |||
continue; | |||
} | |||
// Our windows should only have a single mask, which allows us to | |||
// simplify all the code handling the masks | |||
if (num_masks > 1) { | |||
vlog.error(_("Window 0x%08lu has more than one X Input 2 event mask"), iter->first); | |||
continue; | |||
} | |||
newmask.deviceid = curmasks[0].deviceid; | |||
assert(newmask.mask_len >= curmasks[0].mask_len); | |||
memcpy(newmask.mask, curmasks[0].mask, curmasks[0].mask_len); | |||
if (enable) | |||
XISetMask(newmask.mask, XI_TouchOwnership); | |||
else | |||
XIClearMask(newmask.mask, XI_TouchOwnership); | |||
XISelectEvents(fl_display, iter->first, &newmask, 1); | |||
XFree(curmasks); | |||
} | |||
} | |||
bool x11_grab_pointer(Window window) | |||
{ | |||
bool ret; | |||
if (handlers.count(window) == 0) { | |||
vlog.error(_("Invalid window 0x%08lu specified for pointer grab"), window); | |||
return BadWindow; | |||
} | |||
// We need to remove XI_TouchOwnership from our event masks while | |||
// grabbing as otherwise we will get double events (one for the grab, | |||
// and one because of XI_TouchOwnership) with no way of telling them | |||
// apart. See XInputTouchHandler constructor for why we use this | |||
// event. | |||
x11_change_touch_ownership(false); | |||
ret = handlers[window]->grabPointer(); | |||
if (!ret) | |||
x11_change_touch_ownership(true); | |||
return ret; | |||
} | |||
void x11_ungrab_pointer(Window window) | |||
{ | |||
if (handlers.count(window) == 0) { | |||
vlog.error(_("Invalid window 0x%08lu specified for pointer grab"), window); | |||
return; | |||
} | |||
handlers[window]->ungrabPointer(); | |||
// Restore XI_TouchOwnership now that the grab is gone | |||
x11_change_touch_ownership(true); | |||
} | |||
#endif | |||
static int handleTouchEvent(void *event, void *data) | |||
{ | |||
#if !defined(WIN32) && !defined(__APPLE__) | |||
XEvent *xevent = (XEvent*)event; | |||
if (xevent->type == MapNotify) { | |||
handlers[xevent->xmap.window] = new XInputTouchHandler(xevent->xmap.window); | |||
// Fall through as we don't want to interfere with whatever someone | |||
// else might want to do with this event | |||
} else if (xevent->type == UnmapNotify) { | |||
delete handlers[xevent->xunmap.window]; | |||
handlers.erase(xevent->xunmap.window); | |||
} else if (xevent->type == DestroyNotify) { | |||
delete handlers[xevent->xdestroywindow.window]; | |||
handlers.erase(xevent->xdestroywindow.window); | |||
} else if (xevent->type == GenericEvent) { | |||
if (xevent->xgeneric.extension == xi_major) { | |||
XIDeviceEvent *devev; | |||
if (!XGetEventData(fl_display, &xevent->xcookie)) { | |||
vlog.error(_("Failed to get event data for X Input event")); | |||
return 1; | |||
} | |||
devev = (XIDeviceEvent*)xevent->xcookie.data; | |||
if (handlers.count(devev->event) == 0) { | |||
// We get these when the mouse is grabbed implicitly, so just | |||
// ignore them | |||
// https://gitlab.freedesktop.org/xorg/xserver/-/issues/1026 | |||
if ((devev->evtype == XI_Enter) || (devev->evtype == XI_Leave)) | |||
; | |||
else | |||
vlog.error(_("X Input event for unknown window")); | |||
XFreeEventData(fl_display, &xevent->xcookie); | |||
return 1; | |||
} | |||
handlers[devev->event]->processEvent(devev); | |||
XFreeEventData(fl_display, &xevent->xcookie); | |||
return 1; | |||
} | |||
} | |||
#endif | |||
return 0; | |||
} | |||
void enable_touch() | |||
{ | |||
#if !defined(WIN32) && !defined(__APPLE__) | |||
int ev, err; | |||
int major_ver, minor_ver; | |||
fl_open_display(); | |||
if (!XQueryExtension(fl_display, "XInputExtension", &xi_major, &ev, &err)) { | |||
exit_vncviewer(_("X Input extension not available.")); | |||
return; // Not reached | |||
} | |||
major_ver = 2; | |||
minor_ver = 2; | |||
if (XIQueryVersion(fl_display, &major_ver, &minor_ver) != Success) { | |||
exit_vncviewer(_("X Input 2 (or newer) is not available.")); | |||
return; // Not reached | |||
} | |||
if ((major_ver == 2) && (minor_ver < 2)) | |||
vlog.error(_("X Input 2.2 (or newer) is not available. Touch gestures will not be supported.")); | |||
#endif | |||
Fl::add_system_handler(handleTouchEvent, NULL); | |||
} | |||
void disable_touch() | |||
{ | |||
Fl::remove_system_handler(handleTouchEvent); | |||
} | |||
@@ -0,0 +1,30 @@ | |||
/* Copyright 2019-2020 Pierre Ossman <ossman@cendio.se> for Cendio AB | |||
* | |||
* This is free software; you can redistribute it and/or modify | |||
* it under the terms of the GNU General Public License as published by | |||
* the Free Software Foundation; either version 2 of the License, or | |||
* (at your option) any later version. | |||
* | |||
* This software 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. | |||
* | |||
* You should have received a copy of the GNU General Public License | |||
* along with this software; if not, write to the Free Software | |||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, | |||
* USA. | |||
*/ | |||
#ifndef __TOUCH_H__ | |||
#define __TOUCH_H__ | |||
void enable_touch(); | |||
void disable_touch(); | |||
#if !defined(WIN32) && !defined(__APPLE__) | |||
bool x11_grab_pointer(Window window); | |||
void x11_ungrab_pointer(Window window); | |||
#endif | |||
#endif |
@@ -68,6 +68,7 @@ | |||
#include "CConn.h" | |||
#include "ServerDialog.h" | |||
#include "UserDialog.h" | |||
#include "touch.h" | |||
#include "vncviewer.h" | |||
#include "fltk_layout.h" | |||
@@ -585,6 +586,7 @@ int main(int argc, char** argv) | |||
#endif | |||
init_fltk(); | |||
enable_touch(); | |||
// Check if the server name in reality is a configuration file | |||
potentiallyLoadConfigurationFile(vncServerName); |