Add multi-touch support for OSR (issue #1059)

This commit is contained in:
Riku Palomäki
2019-02-25 16:17:28 -05:00
committed by Marshall Greenblatt
parent 9ba28dd730
commit 5f615a95bc
36 changed files with 1174 additions and 15 deletions

View File

@@ -15,6 +15,7 @@
#define XK_3270 // for XK_3270_BackTab
#include <X11/XF86keysym.h>
#include <X11/Xcursor/Xcursor.h>
#include <X11/extensions/XInput2.h>
#include <X11/keysym.h>
#include "include/base/cef_logging.h"
@@ -28,6 +29,17 @@ namespace client {
namespace {
// Major opcode of XInputExtension, or -1 if XInput 2.2 is not available.
int g_xinput_extension = -1;
// Static BrowserWindowOsrGtk::EventFilter needs to forward touch events
// to correct browser, so we maintain a vector of all windows.
std::vector<BrowserWindowOsrGtk*> g_browser_windows;
bool IsTouchAvailable() {
return g_xinput_extension != -1;
}
int GetCefStateModifiers(guint state) {
int modifiers = 0;
if (state & GDK_SHIFT_MASK)
@@ -47,6 +59,20 @@ int GetCefStateModifiers(guint state) {
return modifiers;
}
int GetCefStateModifiers(XIModifierState mods, XIButtonState buttons) {
guint state = mods.effective;
if (buttons.mask_len >= 1) {
if (XIMaskIsSet(buttons.mask, 1))
state |= GDK_BUTTON1_MASK;
if (XIMaskIsSet(buttons.mask, 2))
state |= GDK_BUTTON2_MASK;
if (XIMaskIsSet(buttons.mask, 3))
state |= GDK_BUTTON3_MASK;
}
return GetCefStateModifiers(state);
}
// From ui/events/keycodes/keyboard_codes_posix.h.
enum KeyboardCode {
VKEY_BACK = 0x08,
@@ -948,9 +974,12 @@ BrowserWindowOsrGtk::BrowserWindowOsrGtk(BrowserWindow::Delegate* delegate,
drag_drop_(false),
device_scale_factor_(1.0f) {
client_handler_ = new ClientHandlerOsr(this, this, startup_url);
g_browser_windows.push_back(this);
}
BrowserWindowOsrGtk::~BrowserWindowOsrGtk() {
g_browser_windows.erase(
std::find(g_browser_windows.begin(), g_browser_windows.end(), this));
ScopedGdkThreadsEnter scoped_gdk_threads;
if (drag_trigger_event_) {
@@ -1387,6 +1416,11 @@ void BrowserWindowOsrGtk::Create(ClientWindowHandle parent_handle) {
// Make the GlArea visible in the parent container.
gtk_widget_show_all(parent_handle);
InitializeXinput(xdisplay_);
if (IsTouchAvailable())
RegisterTouch();
}
// static
@@ -1640,6 +1674,108 @@ gint BrowserWindowOsrGtk::FocusEvent(GtkWidget* widget,
return TRUE;
}
void BrowserWindowOsrGtk::TouchEvent(CefXIDeviceEvent event) {
if (!browser_.get())
return;
XIDeviceEvent* ev = static_cast<XIDeviceEvent*>(event);
CefTouchEvent cef_event;
switch (ev->evtype) {
case XI_TouchBegin:
cef_event.type = CEF_TET_PRESSED;
break;
case XI_TouchUpdate:
cef_event.type = CEF_TET_MOVED;
break;
case XI_TouchEnd:
cef_event.type = CEF_TET_RELEASED;
break;
default:
return;
}
cef_event.id = ev->detail;
cef_event.x = ev->event_x;
cef_event.y = ev->event_y;
cef_event.radius_x = 0;
cef_event.radius_y = 0;
cef_event.rotation_angle = 0;
cef_event.pressure = 0;
cef_event.modifiers = GetCefStateModifiers(ev->mods, ev->buttons);
browser_->GetHost()->SendTouchEvent(cef_event);
}
void BrowserWindowOsrGtk::RegisterTouch() {
GdkWindow* glwindow = gtk_widget_get_window(glarea_);
::Window xwindow = GDK_WINDOW_XID(glwindow);
uint32_t bitMask = XI_TouchBeginMask | XI_TouchUpdateMask | XI_TouchEndMask;
XIEventMask mask;
mask.deviceid = XIAllMasterDevices;
mask.mask = reinterpret_cast<unsigned char*>(&bitMask);
mask.mask_len = sizeof(bitMask);
XISelectEvents(xdisplay_, xwindow, &mask, 1);
}
// static
GdkFilterReturn BrowserWindowOsrGtk::EventFilter(GdkXEvent* gdk_xevent,
GdkEvent* event,
gpointer data) {
XEvent* xevent = static_cast<XEvent*>(gdk_xevent);
if (xevent->type == GenericEvent &&
xevent->xgeneric.extension == g_xinput_extension) {
XGetEventData(xevent->xcookie.display, &xevent->xcookie);
XIDeviceEvent* ev = static_cast<XIDeviceEvent*>(xevent->xcookie.data);
if (!ev)
return GDK_FILTER_REMOVE;
for (BrowserWindowOsrGtk* browser_window : g_browser_windows) {
GtkWidget* widget = browser_window->GetWindowHandle();
::Window xwindow = GDK_WINDOW_XID(gtk_widget_get_window(widget));
if (xwindow == ev->event) {
browser_window->TouchEvent(ev);
break;
}
}
XFreeEventData(xevent->xcookie.display, &xevent->xcookie);
// Even if we didn't find a consumer for this event, we will make sure Gdk
// doesn't attempt to process the event, since it can't parse GenericEvents
return GDK_FILTER_REMOVE;
}
return GDK_FILTER_CONTINUE;
}
// static
void BrowserWindowOsrGtk::InitializeXinput(XDisplay* xdisplay) {
static bool initialized = false;
if (initialized)
return;
initialized = true;
int firstEvent, firstError;
if (XQueryExtension(xdisplay, "XInputExtension", &g_xinput_extension,
&firstEvent, &firstError)) {
int major = 2, minor = 2;
// X Input Extension 2.2 is needed for multitouch events.
if (XIQueryVersion(xdisplay, &major, &minor) == Success) {
// Ideally we would add an event filter for each glarea_ window
// separately, but unfortunately GDK can't parse X GenericEvents
// which have the target window stored in different way compared
// to other X events. That is why we add this global event filter
// just once, and dispatch the event to correct BrowserWindowOsrGtk
// manually.
gdk_window_add_filter(nullptr, &BrowserWindowOsrGtk::EventFilter,
nullptr);
} else {
g_xinput_extension = -1;
}
}
}
bool BrowserWindowOsrGtk::IsOverPopupWidget(int x, int y) const {
const CefRect& rc = renderer_.popup_rect();
int popup_right = rc.x + rc.width;