Bug 978679 - Implement touch events for GTK3. r=karlt

This commit is contained in:
Makato Kato 2015-10-19 23:04:52 +13:00
parent 73162fc3f2
commit becef386ca
17 changed files with 222 additions and 38 deletions

View File

@ -31,6 +31,7 @@
#include "mozilla/dom/FileList.h"
#include "mozilla/dom/BindingUtils.h"
#include "mozilla/dom/OSFileSystem.h"
#include "mozilla/dom/Promise.h"
namespace mozilla {
namespace dom {

View File

@ -20,7 +20,6 @@
#include "mozilla/Attributes.h"
#include "mozilla/EventForwards.h"
#include "mozilla/dom/File.h"
#include "mozilla/dom/Promise.h"
class nsINode;
class nsITransferable;
@ -36,6 +35,7 @@ namespace dom {
class DOMStringList;
class Element;
class FileList;
class Promise;
template<typename T> class Optional;
/**

View File

@ -186,10 +186,10 @@ TouchEvent::PrefEnabled(JSContext* aCx, JSObject* aGlobal)
int32_t flag = 0;
if (NS_SUCCEEDED(Preferences::GetInt("dom.w3c_touch_events.enabled", &flag))) {
if (flag == 2) {
#ifdef XP_WIN
#if defined(XP_WIN) || MOZ_WIDGET_GTK == 3
static bool sDidCheckTouchDeviceSupport = false;
static bool sIsTouchDeviceSupportPresent = false;
// On Windows we auto-detect based on device support.
// On Windows and GTK3 we auto-detect based on device support.
if (!sDidCheckTouchDeviceSupport) {
sDidCheckTouchDeviceSupport = true;
sIsTouchDeviceSupportPresent = WidgetUtils::IsTouchDeviceSupportPresent();

View File

@ -15,6 +15,7 @@
#include "mozilla/Services.h"
#include "nsIMobileMessageDatabaseService.h"
#include "nsIObserverService.h"
#include "nsThreadUtils.h"
using namespace mozilla::dom;
using namespace mozilla::dom::mobilemessage;

View File

@ -4530,11 +4530,15 @@ pref("dom.mozSettings.enabled", false);
pref("dom.mozPermissionSettings.enabled", false);
// W3C touch events
// 0 - disabled, 1 - enabled, 2 - autodetect (win)
// 0 - disabled, 1 - enabled, 2 - autodetect (win/gtk3)
#ifdef XP_WIN
pref("dom.w3c_touch_events.enabled", 2);
#endif
#if MOZ_WIDGET_GTK == 3
pref("dom.w3c_touch_events.enabled", 2);
#endif
// W3C draft pointer events
pref("dom.w3c_pointer_events.enabled", false);

View File

@ -8,6 +8,8 @@
#include "mozilla/WidgetUtils.h"
#ifdef XP_WIN
#include "WinUtils.h"
#elif MOZ_WIDGET_GTK == 3
#include "mozilla/WidgetUtilsGtk.h"
#endif
namespace mozilla {
@ -100,6 +102,8 @@ WidgetUtils::IsTouchDeviceSupportPresent()
{
#ifdef XP_WIN
return WinUtils::IsTouchDeviceSupportPresent();
#elif MOZ_WIDGET_GTK == 3
return WidgetUtilsGTK::IsTouchDeviceSupportPresent();
#else
return 0;
#endif

View File

@ -0,0 +1,47 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "WidgetUtilsGtk.h"
namespace mozilla {
namespace widget {
int32_t WidgetUtilsGTK::IsTouchDeviceSupportPresent()
{
int32_t result = 0;
GdkDisplay* display = gdk_display_get_default();
if (!display) {
return 0;
}
GdkDeviceManager* manager = gdk_display_get_device_manager(display);
if (!manager) {
return 0;
}
GList* devices =
gdk_device_manager_list_devices(manager, GDK_DEVICE_TYPE_SLAVE);
GList* list = devices;
while (devices) {
GdkDevice* device = static_cast<GdkDevice*>(devices->data);
if (gdk_device_get_source(device) == GDK_SOURCE_TOUCHSCREEN) {
result = 1;
break;
}
devices = devices->next;
}
if (list) {
g_list_free(list);
}
return result;
}
} // namespace widget
} // namespace mozilla

View File

@ -0,0 +1,25 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef WidgetUtilsGtk_h__
#define WidgetUtilsGtk_h__
#include <stdint.h>
namespace mozilla {
namespace widget {
class WidgetUtilsGTK
{
public:
/* See WidgetUtils::IsTouchDeviceSupportPresent(). */
static int32_t IsTouchDeviceSupportPresent();
};
} // namespace widget
} // namespace mozilla
#endif // WidgetUtilsGtk_h__

View File

@ -13,6 +13,10 @@ EXPORTS += [
'nsIImageToPixbuf.h',
]
EXPORTS.mozilla += [
'WidgetUtilsGtk.h'
]
UNIFIED_SOURCES += [
'IMContextWrapper.cpp',
'mozcontainer.c',
@ -32,6 +36,7 @@ UNIFIED_SOURCES += [
'nsWidgetFactory.cpp',
'WakeLockListener.cpp',
'WidgetTraceEvent.cpp',
'WidgetUtilsGtk.cpp',
]
SOURCES += [

View File

@ -506,6 +506,7 @@ STUB(gtk_window_unmaximize)
#ifdef GTK3_SYMBOLS
STUB(gdk_device_get_source)
STUB(gdk_device_manager_get_client_pointer)
STUB(gdk_device_manager_list_devices)
STUB(gdk_disable_multidevice)
STUB(gdk_display_get_device_manager)
STUB(gdk_error_trap_pop_ignored)

View File

@ -23,6 +23,7 @@
#include "gtkdrawing.h"
#include "nsStyleConsts.h"
#include "gfxFontConstants.h"
#include "WidgetUtilsGtk.h"
#include <dlfcn.h>
@ -634,8 +635,13 @@ nsLookAndFeel::GetIntImpl(IntID aID, int32_t &aResult)
res = NS_ERROR_NOT_IMPLEMENTED;
break;
case eIntID_TouchEnabled:
#if MOZ_WIDGET_GTK == 3
aResult = mozilla::widget::WidgetUtilsGTK::IsTouchDeviceSupportPresent();
break;
#else
aResult = 0;
res = NS_ERROR_NOT_IMPLEMENTED;
#endif
break;
case eIntID_MacGraphiteTheme:
case eIntID_MacLionTheme:

View File

@ -6,9 +6,11 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "mozilla/ArrayUtils.h"
#include "mozilla/EventForwards.h"
#include "mozilla/MiscEvents.h"
#include "mozilla/MouseEvents.h"
#include "mozilla/TextEvents.h"
#include "mozilla/TouchEvents.h"
#include <algorithm>
#include "GeckoProfiler.h"
@ -142,9 +144,8 @@ const gint kEvents = GDK_EXPOSURE_MASK | GDK_STRUCTURE_MASK |
GDK_VISIBILITY_NOTIFY_MASK |
GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
#if GTK_CHECK_VERSION(3,4,0)
GDK_SMOOTH_SCROLL_MASK |
#endif
GDK_TOUCH_MASK |
GDK_SCROLL_MASK |
GDK_POINTER_MOTION_MASK |
GDK_PROPERTY_CHANGE_MASK;
@ -214,6 +215,8 @@ static gboolean window_state_event_cb (GtkWidget *widget,
static void theme_changed_cb (GtkSettings *settings,
GParamSpec *pspec,
nsWindow *data);
static gboolean touch_event_cb (GtkWidget* aWidget,
GdkEventTouch* aEvent);
static nsWindow* GetFirstNSWindowForGDKWindow (GdkWindow *aGdkWindow);
#ifdef __cplusplus
@ -408,6 +411,7 @@ nsWindow::nsWindow()
mNeedsShow = false;
mEnabled = true;
mCreated = false;
mHandleTouchEvent = false;
mContainer = nullptr;
mGdkWindow = nullptr;
@ -450,9 +454,8 @@ nsWindow::nsWindow()
mTransparencyBitmapWidth = 0;
mTransparencyBitmapHeight = 0;
#if GTK_CHECK_VERSION(3,4,0)
mLastScrollEventTime = GDK_CURRENT_TIME;
#endif
mTouchCounter = 0;
}
nsWindow::~nsWindow()
@ -920,6 +923,13 @@ nsWindow::IsVisible() const
return mIsShown;
}
void
nsWindow::RegisterTouchWindow()
{
mHandleTouchEvent = true;
mTouches.Clear();
}
NS_IMETHODIMP
nsWindow::ConstrainPosition(bool aAllowSlop, int32_t *aX, int32_t *aY)
{
@ -3125,16 +3135,13 @@ nsWindow::OnScrollEvent(GdkEventScroll *aEvent)
// check to see if we should rollup
if (CheckForRollup(aEvent->x_root, aEvent->y_root, true, false))
return;
#if GTK_CHECK_VERSION(3,4,0)
// check for duplicate legacy scroll event, see GNOME bug 726878
if (aEvent->direction != GDK_SCROLL_SMOOTH &&
mLastScrollEventTime == aEvent->time)
return;
#endif
WidgetWheelEvent wheelEvent(true, eWheel, this);
wheelEvent.deltaMode = nsIDOMWheelEvent::DOM_DELTA_LINE;
switch (aEvent->direction) {
#if GTK_CHECK_VERSION(3,4,0)
case GDK_SCROLL_SMOOTH:
{
// As of GTK 3.4, all directional scroll events are provided by
@ -3156,7 +3163,6 @@ nsWindow::OnScrollEvent(GdkEventScroll *aEvent)
}
break;
}
#endif
case GDK_SCROLL_UP:
wheelEvent.deltaY = wheelEvent.lineOrPageDeltaY = -3;
break;
@ -3353,6 +3359,78 @@ nsWindow::OnDragDataReceivedEvent(GtkWidget *aWidget,
aSelectionData, aInfo, aTime);
}
static
PLDHashOperator
EnumTouchToAppend(GdkEventSequence* aKey, RefPtr<dom::Touch> aData, void* aArg)
{
WidgetTouchEvent* event = reinterpret_cast<WidgetTouchEvent*>(aArg);
event->touches.AppendElement(aData);
return PL_DHASH_NEXT;
}
gboolean
nsWindow::OnTouchEvent(GdkEventTouch* aEvent)
{
if (!mHandleTouchEvent) {
return FALSE;
}
EventMessage msg;
switch (aEvent->type) {
case GDK_TOUCH_BEGIN:
msg = eTouchStart;
break;
case GDK_TOUCH_UPDATE:
msg = eTouchMove;
break;
case GDK_TOUCH_END:
msg = eTouchEnd;
break;
case GDK_TOUCH_CANCEL:
msg = eTouchCancel;
break;
default:
return FALSE;
}
LayoutDeviceIntPoint touchPoint;
if (aEvent->window == mGdkWindow) {
touchPoint = LayoutDeviceIntPoint(aEvent->x, aEvent->y);
} else {
touchPoint = LayoutDeviceIntPoint(aEvent->x_root, aEvent->y_root) -
WidgetToScreenOffset();
}
RefPtr<dom::Touch> touch;
int32_t id;
if (mTouches.Get(aEvent->sequence, &touch)) {
id = touch->mIdentifier;
mTouches.Remove(aEvent->sequence);
} else {
id = ++mTouchCounter & 0x7FFFFFFF;
}
touch = new dom::Touch(id, touchPoint, nsIntPoint(1,1), 0.0f, 0.0f);
WidgetTouchEvent event(true, msg, this);
KeymapWrapper::InitInputEvent(event, aEvent->state);
event.time = aEvent->time;
if (aEvent->type == GDK_TOUCH_BEGIN || aEvent->type == GDK_TOUCH_UPDATE) {
mTouches.Put(aEvent->sequence, touch);
// add all touch points to event object
mTouches.EnumerateRead(EnumTouchToAppend, &event);
} else if (aEvent->type == GDK_TOUCH_END ||
aEvent->type == GDK_TOUCH_CANCEL) {
event.touches.AppendElement(touch);
}
nsEventStatus status;
DispatchEvent(&event, status);
return TRUE;
}
static void
GetBrandName(nsXPIDLString& brandName)
{
@ -3819,6 +3897,8 @@ nsWindow::Create(nsIWidget *aParent,
G_CALLBACK(property_notify_event_cb), nullptr);
g_signal_connect(eventWidget, "scroll-event",
G_CALLBACK(scroll_event_cb), nullptr);
g_signal_connect(eventWidget, "touch-event",
G_CALLBACK(touch_event_cb), nullptr);
}
LOG(("nsWindow [%p]\n", (void *)this));
@ -5844,6 +5924,19 @@ theme_changed_cb (GtkSettings *settings, GParamSpec *pspec, nsWindow *data)
window->ThemeChanged();
}
static gboolean
touch_event_cb(GtkWidget* aWidget, GdkEventTouch* aEvent)
{
UpdateLastInputEventTime(aEvent);
nsWindow* window = GetFirstNSWindowForGDKWindow(aEvent->window);
if (!window) {
return FALSE;
}
return window->OnTouchEvent(aEvent);
}
//////////////////////////////////////////////////////////////////////
// These are all of our drag and drop operations
@ -6695,12 +6788,9 @@ nsWindow::SynthesizeNativeMouseScrollEvent(mozilla::LayoutDeviceIntPoint aPoint,
// The delta values are backwards on Linux compared to Windows and Cocoa,
// hence the negation.
#if GTK_CHECK_VERSION(3,4,0)
// TODO: is this correct? I don't have GTK 3.4+ so I can't check
event.scroll.direction = GDK_SCROLL_SMOOTH;
event.scroll.delta_x = -aDeltaX;
event.scroll.delta_y = -aDeltaY;
#else
if (aDeltaX < 0) {
event.scroll.direction = GDK_SCROLL_RIGHT;
} else if (aDeltaX > 0) {
@ -6712,7 +6802,6 @@ nsWindow::SynthesizeNativeMouseScrollEvent(mozilla::LayoutDeviceIntPoint aPoint,
} else {
return NS_OK;
}
#endif
gdk_event_put(&event);

View File

@ -30,6 +30,7 @@
#include "mozilla/a11y/Accessible.h"
#endif
#include "mozilla/EventForwards.h"
#include "mozilla/TouchEvents.h"
#include "IMContextWrapper.h"
@ -207,6 +208,7 @@ public:
gpointer aData);
gboolean OnPropertyNotifyEvent(GtkWidget *aWidget,
GdkEventProperty *aEvent);
gboolean OnTouchEvent(GdkEventTouch* aEvent);
virtual already_AddRefed<mozilla::gfx::DrawTarget>
StartRemoteDrawingInRegion(nsIntRegion& aInvalidRegion) override;
@ -347,6 +349,8 @@ protected:
virtual nsresult NotifyIMEInternal(
const IMENotification& aIMENotification) override;
virtual void RegisterTouchWindow() override;
nsCOMPtr<nsIWidget> mParent;
// Is this a toplevel window?
bool mIsTopLevel;
@ -362,6 +366,8 @@ protected:
bool mEnabled;
// has the native window for this been created yet?
bool mCreated;
// whether we handle touch event
bool mHandleTouchEvent;
private:
void DestroyChildWindows();
@ -396,10 +402,12 @@ private:
nsIntPoint mClientOffset;
#if GTK_CHECK_VERSION(3,4,0)
// This field omits duplicate scroll events caused by GNOME bug 726878.
guint32 mLastScrollEventTime;
#endif
// for touch event handling
nsDataHashtable<nsPtrHashKey<GdkEventSequence>, RefPtr<mozilla::dom::Touch> > mTouches;
uint32_t mTouchCounter;
#ifdef MOZ_X11
Display* mXDisplay;

View File

@ -910,6 +910,14 @@ void nsBaseWidget::ConfigureAPZCTreeManager()
uint64_t rootLayerTreeId = mCompositorParent->RootLayerTreeId();
CompositorParent::SetControllerForLayerTree(rootLayerTreeId, controller);
}
// When APZ is enabled, we can actually enable raw touch events because we
// have code that can deal with them properly. If APZ is not enabled, this
// function doesn't get called.
if (Preferences::GetInt("dom.w3c_touch_events.enabled", 0) ||
Preferences::GetBool("dom.w3c_pointer_events.enabled", false)) {
RegisterTouchWindow();
}
}
void nsBaseWidget::ConfigureAPZControllerThread()

View File

@ -473,6 +473,7 @@ protected:
* Notify the widget that this window is being used with OMTC.
*/
virtual void WindowUsesOMTC() {}
virtual void RegisterTouchWindow() {}
nsIDocument* GetDocument() const;

View File

@ -1329,25 +1329,10 @@ void nsWindow::SetThemeRegion()
*
**************************************************************/
void nsWindow::ConfigureAPZCTreeManager()
{
nsBaseWidget::ConfigureAPZCTreeManager();
// When APZ is enabled, we can actually enable raw touch events because we
// have code that can deal with them properly. If APZ is not enabled, this
// function doesn't get called, and |mGesture| will take care of touch-based
// scrolling. Note that RegisterTouchWindow may still do nothing depending
// on touch events prefs, and so it is possible to enable APZ without
// also enabling touch support.
RegisterTouchWindow();
}
void nsWindow::RegisterTouchWindow() {
if (Preferences::GetInt("dom.w3c_touch_events.enabled", 0)) {
mTouchWindow = true;
mGesture.RegisterTouchWindow(mWnd);
::EnumChildWindows(mWnd, nsWindow::RegisterTouchForDescendants, 0);
}
mTouchWindow = true;
mGesture.RegisterTouchWindow(mWnd);
::EnumChildWindows(mWnd, nsWindow::RegisterTouchForDescendants, 0);
}
BOOL CALLBACK nsWindow::RegisterTouchForDescendants(HWND aWnd, LPARAM aMsg) {

View File

@ -297,8 +297,7 @@ protected:
virtual ~nsWindow();
virtual void WindowUsesOMTC() override;
virtual void ConfigureAPZCTreeManager() override;
void RegisterTouchWindow();
virtual void RegisterTouchWindow() override;
virtual nsresult NotifyIMEInternal(
const IMENotification& aIMENotification) override;