Compare commits

...

104 Commits

Author SHA1 Message Date
Matthias Clasen
2a9ef1566d viewport: select for touch events
This makes kinetic scrolling work with viewports where the
content does not otherwise select for button or touch events,
such as testscrolledwindow's label.
2012-02-29 20:08:03 -05:00
Matthias Clasen
d22e419749 scrolledwindow: remove a leftover event type check
We want the capture_button_press function to capture touch begin
events as well.
2012-02-29 20:07:03 -05:00
Matthias Clasen
e5d6191466 Make kinetic scrolling test work 2012-02-29 18:25:48 -05:00
Matthias Clasen
eea2212445 Leave GDK_EXPOSURE_MASK out of GDK_ALL_EVENTS_MASK
This used to be the case, and was accidentally changed when
GDK_TOUCH_MASK was added.
2012-02-29 16:50:55 -05:00
Matthias Clasen
bab5b76702 Improve touch event docs 2012-02-29 16:50:00 -05:00
Matthias Clasen
7fc2274025 Add missing docs 2012-02-29 16:39:41 -05:00
Matthias Clasen
0aced24ba5 Rename GdkTouchSequence to GdkEventSequence
We may eventually have sequences involving non-touch events,
so keep the name neutral.

Update all users.
2012-02-29 16:38:44 -05:00
Matthias Clasen
a7f7d5e0cc Rename touch device types
Instead of GDK_SOURCE_[IN]DIRECT_TOUCH, call then
GDK_SOURCE_TOUCHSCREEN and GDK_SOURCE_TOUCHPAD. This matches
what Qt does, is a better fit with the rest of the enumeration,
and doesn't imply that there won't be further touch device
types down the road.

Update all users.
2012-02-29 16:10:32 -05:00
Matthias Clasen
a7914d68ea menu: Capture touch events, too
Now that we get touch events separately, GtkMenu needs
to watch for them when capturing events.
2012-02-29 15:32:28 -05:00
Matthias Clasen
e50d1d1c4f scrolledwindow: Capture touch events, too
Now that we get touch events separately, GtkScrolledWindow needs
to watch for them when capturing events.
2012-02-29 06:57:11 -05:00
Matthias Clasen
11fae74750 Fix a mistake in converting to the new kinetic scrolling api 2012-02-29 01:57:55 -05:00
Matthias Clasen
9040388b18 gtk: Use 'emulating pointer' information when falling back
We don't want to fallback for 'random' touch sequences, since
that could lead to all kinds of pairedness and other violations.
Since the X server already tells us what touch events it would
have used for emulating pointer events, we just use that information
here.
2012-02-29 01:23:26 -05:00
Matthias Clasen
35c9376791 gdk: Make the 'emulating pointer' flag available in GdkEventTouch
This will be used to decide for which touch events to fall back
to button press/release handlers.
2012-02-29 01:21:53 -05:00
Matthias Clasen
3e9b4f9deb gdk: Add some debug output for touch events and devices 2012-02-29 00:44:51 -05:00
Carlos Garnacho
8d419898b7 gdk: Get the right event window for pointer emulated events
get_event_window() just checked on GDK_TOUCH_MASK, including for emulated
pointer events, so at the very least those should also match evmasks with
no touch events whatsoever
2012-02-27 13:16:36 +01:00
Carlos Garnacho
aa9a8a9e75 gdk: Set correct GdkModifierType on pointer emulated events 2012-02-27 13:08:38 +01:00
Carlos Garnacho
0a641157ec gdk: remove duplicate code
the chunk above is meant to do the same, likely a rebase artifact
2012-02-27 12:53:05 +01:00
Carlos Garnacho
7bd8d89694 gdk: translate correctly from touch events into emulated pointer events 2012-02-27 12:52:52 +01:00
Carlos Garnacho
7a1093a07d xi2: Set window on GDK_TOUCH_UPDATE events 2012-02-27 12:21:06 +01:00
Matthias Clasen
4556f13208 gtk: translate unhandled touch events to button events
We clearly need something like this, but the patch is experimental
and untested.
2012-02-26 11:19:29 -05:00
Matthias Clasen
c2b7789ef5 Don't treat touch events as button events
One more place where we assumed that touch events have a button
field.
2012-02-26 00:04:39 -05:00
Matthias Clasen
4d01e5cb2d Keep the capture phase private for now
Instead of a public ::captured-event signal, use a private
api to register handlers for the capture phase. Use this in
GtkScrolledWindow, GtkMenu and GtkPaned.
2012-02-25 13:55:03 -05:00
Matthias Clasen
af0b3648ff tests: Adapt to scrolledwindow api change 2012-02-25 10:01:41 -05:00
Matthias Clasen
6262ee58bb scrolledwindow: Drop the kinetic scrolling enumeration
Split off the button-press capture setting as a separate boolean,
that can easier be deprecated and removed in the future.
2012-02-24 15:13:36 -05:00
Matthias Clasen
678027ed18 scrolledwindow: Only look at direct touches for now 2012-02-24 14:39:11 -05:00
Matthias Clasen
bd26f9ff77 tooltip: Only look at direct touches for now 2012-02-24 14:39:11 -05:00
Matthias Clasen
97f65c9694 range: Only look at direct touches for now 2012-02-24 14:39:11 -05:00
Matthias Clasen
67245ed83e paned: Only look at direct touch devices for now 2012-02-24 14:39:10 -05:00
Matthias Clasen
4becb95192 menu: Only look at direct touch devices for now 2012-02-24 14:39:10 -05:00
Matthias Clasen
eed8b95744 gdk: Adapt to direct/indirect touch device types
We use the XITouchClass.mode information to determine if
a device is direct or indirect.
2012-02-24 14:39:10 -05:00
Matthias Clasen
adefcf2ee6 gdk: Add separate device types for direct and indirect touch 2012-02-24 14:39:10 -05:00
Matthias Clasen
2adb94eed3 gdk: Fix GDK_ALL_EVENTS_MASK 2012-02-24 14:39:09 -05:00
Matthias Clasen
dfda82acd4 gdk: Drop gdk_event_get_touch_area for now
It has no users, and the implementation probably has to be moved
to be xi2-specific anyway.
2012-02-24 14:39:07 -05:00
Matthias Clasen
3e298f8605 paned: Use touch sequences instead of touch ids 2012-02-24 14:38:45 -05:00
Matthias Clasen
5195cb807c gdk: Change touch_id to touch_sequence
Change the GdkEventTouch struct to contain a GdkTouchSequence
pointer instead of an integer and adapt all places in gdk
that were dealing with touch ids.
2012-02-24 14:38:45 -05:00
Matthias Clasen
cca0efbdf0 Replace gdk_event_get_touch_id
Change this to gdk_event_get_touch_sequence, and make it
return an opaque pointer instead of an integer.
2012-02-24 14:38:37 -05:00
Matthias Clasen
02fc18f34c Remove GdkEventButton.touch_id from the docs
Button events no longer have a touch id
2012-02-24 11:53:01 -05:00
Matthias Clasen
59e14c5b56 Remove the GdkEventTouch.button field
Touch events don't have a button.
2012-02-24 11:51:52 -05:00
Matthias Clasen
acb93bd418 Fix the build 2012-02-24 11:45:33 -05:00
Matthias Clasen
6ddf197e97 paned: Deal with new touch events 2012-02-24 10:21:06 -05:00
Matthias Clasen
953f383d62 button: Use ::touch-event 2012-02-24 10:20:40 -05:00
Matthias Clasen
468e2957e7 gtk: Add a separate ::touch-event signal
This gets emitted for touch events now.
2012-02-24 10:19:59 -05:00
Matthias Clasen
064937ba1c gtk: Adapt event propagation code to new touch event types 2012-02-24 10:18:40 -05:00
Matthias Clasen
bb4c1726ab xi2: Adapt to GdkEventTouch
Translate XI_TouchBegin/Update/End to GDK_TOUCH_BEGIN/UPDATE/END
events.
2012-02-24 10:17:43 -05:00
Matthias Clasen
4a26fcf212 gdk: Adapt more code to handle GdkEventTouch
The changes to the type masks in this patch are meant to
make it so that touch events only go to windows that explicitly
select for them. It may not be correct.
2012-02-24 10:15:43 -05:00
Matthias Clasen
9fc20c585d gdk: Adapt generic event code to deal with GdkEventTouch 2012-02-24 10:14:59 -05:00
Matthias Clasen
dda2ce7455 gdk: Adapt event documentation to mention the new types 2012-02-24 10:14:21 -05:00
Matthias Clasen
727200c6a0 gdk: Introduce an independent GdkEventTouch
This commit introduces GDK_TOUCH_BEGIN/UPDATE/END/CANCEL
and a separate GdkEventTouch struct that they use. This
is closer to the touch event API of other platforms and
matches the xi2 events closely, too.
2012-02-24 10:12:21 -05:00
Carlos Garnacho
b6f6ff901c paned: Use 2-finger interaction to resize the panes
If 2 touches happen to be both at left and right of the handle,
enter into resize mode. Else, the event(s) will be propagated
further to the children.
2012-02-24 03:24:15 -05:00
Carlos Garnacho
0a535fe441 gdk: Use the last alive grab in order to get the event window
If an active grab kicks in on a different window, _gdk_display_has_device_grab()
would still find the former implicit grab for the window below the pointer, thus
sending events to an unrelated place.
2012-02-24 03:24:15 -05:00
Carlos Garnacho
d3f8b132d6 gdk: Don't mutate pointer events to touch events just because a grab says so
If a grab with GDK_TOUCH_MASK kicks in due to a touch sequence emulating pointer
events, don't mutate the sequence into emitting touch events right away.
2012-02-24 03:24:15 -05:00
Carlos Garnacho
b04f2cfb1c gdk: Let implicit touch grabs coexist with an implicit pointer grab
Create the backing GdkTouchGrabInfo for touches even if the pointer
emulating touch sequence is already holding an implicit grab on a
window that didn't select for touch events.
2012-02-24 03:24:14 -05:00
Carlos Garnacho
1e116569eb gdk: Don't fully destroy the implicit touch grab on ::grab-broken
the backing GdkTouchGrabInfo will be needed if the overriding device
grab finishes before the touch does in order to send events back to
the implicit grab window. Instead, wait until the touch is physically
finished before removing the matching GdkTouchGrabInfo
2012-02-24 03:24:14 -05:00
Carlos Garnacho
127c0c213d xi2: Mark touch events emulating the pointer as such 2012-02-24 03:24:14 -05:00
Carlos Garnacho
0cfd17eb77 gdk: Listen to touch events by default on the native window
GDK will only receive touch events when dealing with a multitouch
device, so these must be transformed to pointer events if the
client-side window receiving the event doesn't listen to touch
events, and the touch sequence the event is from does emulate
the pointer.

If a sequence emulates pointer events, it will result in a
button-press, N motions with GDK_BUTTON1_MASK set and a
button-release event, and it will deliver crossing events
as specified by the current device grab.
2012-02-24 03:24:14 -05:00
Carlos Garnacho
30b3de7778 gdk: Only trigger motion hints machinery on motion events
Touch events have no need for it, plus the concept behind
gdk_event_request_motions() doesn't wrap around multiple
touches within a device.
2012-02-24 03:24:13 -05:00
Carlos Garnacho
2044fb1a3b gdk: Have touch grabs behave like the implicit grab wrt crossing events
These are equivalent to an implicit grab (with !owner_events), so
if the touch leaves or enters the grab window, the other window
won't receive the corresponding counter-event.
2012-02-24 03:24:13 -05:00
Carlos Garnacho
f09476686c button: Handle touch events
Touch events don't generate crossing events themselves, so
do not rely on these to determine whether the button release
happened within the event window.
2012-02-24 03:24:13 -05:00
Carlos Garnacho
123c0e0f3a gtk: Rewrite touch events wrt the window group too
Likewise to pointer events, have touch events during a device
grab with owner_events=TRUE be delivered normally as long as
the widget pertains to the same window group.
2012-02-24 03:24:12 -05:00
Carlos Garnacho
31a79bd080 Make touch events go through csw/widget event handling
In GtkWidget, touch events go through the same handler
than motion events, with the difference that touch_id
will have something meaningful there.

Touch events need to be explicitly selected, so if this
is enabled, the possibility of different motion streams
with different touch IDs must be handled in some way.
2012-02-24 03:24:12 -05:00
Carlos Garnacho
41be0fae6d gdk: handle implicit touch grabs
If the touch sequence happens on a window with GDK_TOUCH_MASK set,
a GdkTouchGrabInfo is created to back it up. Else a device grab is
only created if the sequence emulates the pointer.

If both a device and a touch grab are present on a window, the later
of them both is obeyed, Any grab on the device happening after a
touch grab generates grab broken on all the windows an implicit
touch grab was going on, and all touches would be automatically
removed from every touch cluster.
2012-02-24 03:24:12 -05:00
Carlos Garnacho
d19998d2a5 gdk: Add internal API to deal with touch implicit grabs
The necessary information about a touch implicit grab is stored in
GdkTouchGrabInfo structs, these are meant to be transient to the
touch sequence.
2012-02-24 03:24:12 -05:00
Carlos Garnacho
89e53a0c61 gdk: Don't change window_under_pointer for pure touch events
Only touch events that emulate the pointer do change it.
2012-02-24 03:24:11 -05:00
Carlos Garnacho
b835349fa5 xi2: set pointer emulated flags on events with XIPointerEmulated 2012-02-24 03:24:11 -05:00
Carlos Garnacho
27b3dfb031 gdk: Add internal API to set "pointer emulated" flag on events
This flag will be used for non-pointer events that are emulated
from eg. touch events, or pointer events being emulated.
2012-02-24 03:24:11 -05:00
Carlos Garnacho
182b1afa18 Add gdk_event_get_touch_area()
If given an event coming from a touch devices,
this functions will read the MT major/minor/orientation
axes and return a cairo_region_t with the touch shape.
2012-02-24 03:24:10 -05:00
Carlos Garnacho
48c70d1a1d Add gdk_event_get_touch_id()
Just a helper function to get the touch ID from touch events, it
returns FALSE in any other case.
2012-02-24 03:24:10 -05:00
Carlos Garnacho
910ca064e4 xi2: Set GDK_BUTTON1_MASK on touch events
GTK+ handles both touch and pointer events through the same
event handlers, so enable this modifier on touch events to
avoid much special casing.
2012-02-24 03:24:10 -05:00
Carlos Garnacho
9775183efb xi2: Use GDK_SOURCE_TOUCH for multitouch devices
Any device with a XITouchClassInfo with num_touches > 0
qualifies as multitouch.
2012-02-24 03:24:10 -05:00
Carlos Garnacho
605c7e161b xi2: Handle touch events
GdkDeviceManagerXI2 now handles touch events.
TouchMotion events are translated to GDK_TOUCH_MOTION events,
TouchBegin/End events are translated to the newly introduced
GDK_TOUCH_PRESS/RELEASE events.
2012-02-24 03:24:09 -05:00
Carlos Garnacho
f16fdbc6e4 configure: Detect XInput 2.2 2012-02-24 03:24:09 -05:00
Carlos Garnacho
2dc59d153c xi2: Add major/minor properties to XI2 device manager
This may be used to turn on/off the features that are added to
new XInput2 revisions.
2012-02-24 03:24:09 -05:00
Carlos Garnacho
936d3df7c7 range: Have slider jump to the pointer coordinates on touch devices
This widget is too narrow to make touch interaction tricky enough, so
don't add the penalty of having the slider run farther from the touch
coordinates if it happens to miss the slider.
2012-02-24 03:24:08 -05:00
Carlos Garnacho
330a9bfbc3 menus: Don't popdown submenus on button release for touch devices
This is so submenus stay open as the parent menu item is
pressed/released, since the user would typically lift the
finger in order to select a submenu item.
2012-02-24 03:24:08 -05:00
Carlos Garnacho
59f7a3b5b4 settings: Deprecate gtk-touchscreen-mode
It's not used anywhere in GTK+ anymore.
2012-02-24 03:24:08 -05:00
Carlos Garnacho
bc1ee7bd0c range: Remove gtk-touchscreen-mode usage
Emulated crossing events with mode GDK_CROSSING_TOUCH_PRESS/RELEASE
already cater dynamically for the "don't prelight on touch devices"
usecase.
2012-02-24 03:24:08 -05:00
Carlos Garnacho
5b32261d5a togglebutton: Remove gtk-touchcreen-mode usage
Emulated crossing events with mode GDK_CROSSING_TOUCH_PRESS/RELEASE
already cater dynamically for the "don't prelight on touch devices"
usecase.
2012-02-24 03:24:07 -05:00
Carlos Garnacho
1d946085ec menushell: Remove gtk-touchscreen-mode usage
This usage in a keybinding signal is hardly related to touchscreens,
so just remove it.
2012-02-24 03:24:07 -05:00
Carlos Garnacho
23b9e1257b menus: Remove gtk-touchscreen-mode from scrolling code
Scrolling is handled via ::captured-event dynamically, so remove
this now unused code.
2012-02-24 03:24:07 -05:00
Carlos Garnacho
e040c8840e menus: Select the first item for touch devices
This was done through gtk-touchscreen-mode. Now it is handled
dynamically on the current event source device.
2012-02-24 03:24:07 -05:00
Carlos Garnacho
909100ef0e menus: Implement scrolling through ::captured-event for touch devices
This makes overflown menus scrollable via direct manipulation.
Once past the threshold, the item below the pointer is unselected
and scrolling starts.
2012-02-24 03:24:06 -05:00
Carlos Garnacho
b2b3154bb5 menus: Handle item selection for touch devices dynamically
Instead of using gtk-touchscreen-mode, the behavior changes depending
on the source device in use.
2012-02-24 03:24:06 -05:00
Carlos Garnacho
1e7d9fb60d tooltips: Use the source device instead of gtk-touchscreen-mode
This makes tooltips behavior dynamic based on the interacting device.
2012-02-24 03:24:06 -05:00
Carlos Garnacho
b4f35b4183 scrolledwindow: Ensure the view snaps back when overshooting
Instead of just stopping the acceleration source ID, check whether
it is overshooting, and let it snap back if needed after ::grab-notify
and ::button-release-event.
2012-02-24 03:24:05 -05:00
Carlos Garnacho
bc5eacb185 scrolledwindow: Improve initial velocity calculation
Velocity calculation has been refactored out of the captured motion
events handler, and also has more tolerance defore determining that
a drag is actually still.
2012-02-24 03:24:05 -05:00
Carlos Garnacho
523fd1fafa scrolledwindow: Capture crossing events when dragging
Also, instead of connecting 2 more times to ::captured-event,
have it all go through a single handler.
2012-02-24 03:24:05 -05:00
Carlos Garnacho
5e2cb82209 scrolledwindow: Add GtkKineticScrollingFlags
gtk_scrolled_window_set_kinetic_scrolling() now takes a set of flags,
GTK_KINETIC_SCROLLING_CAPTURE_BUTTON_PRESS makes the "capture button
press and maybe replay later" vs "let button press go through, but
trust in ::grab-broken to undo things" an opt-in, by default that
flag is set, which is the most conservative approach.
2012-02-24 03:24:05 -05:00
Carlos Garnacho
903fe8e892 scrolledwindow: Grab only after starting drag
This is so the grab doesn't break the implicit grab on the
child widget's window, which avoids that the button press and
release are possibly sent to different windows, and after the
grab was actually broken.
2012-02-24 03:24:04 -05:00
Carlos Garnacho
c64fd529c1 gdk: Generate crossing events around touch devices' press/release
Anytime a touch device interacts, the crossing events generation
will change to a touch mode where only events with mode
GDK_CROSSING_TOUCH_PRESS/RELEASE are handled, and those are sent
around button press/release. Those are virtual as the master
device may still stay on the window.

Whenever there is a switch of slave device (the user starts
using another non-touch device), a crossing event with mode
GDK_CROSSING_DEVICE_SWITCH may generated if needed, and the normal
crossing event handling is resumed.
2012-02-24 03:24:04 -05:00
Carlos Garnacho
1385eff197 gtk: Handle motion hints for ::captured-event
Request automatically more motion events in behalf of
the original widget if it listens to motion hints. So
the capturing widget doesn't need to handle such
implementation details.
2012-02-24 03:24:04 -05:00
Carlos Garnacho
177c20bc6e scrolledwindow: Clamp early overshooting when snapping back
Fixes the situation where overshooting in scrolled windows with a
small viewport could end up overshooting right to the opposite
side, and then back again indefinitely.
2012-02-24 03:24:04 -05:00
Carlos Garnacho
193341c85d scrolledwindow: Rework physics behind kinetic scrolling
The maths being used didn't resemble much about velocities or
friction/deceleration, so reimplement it in terms of velocity
vectors and decelerations, measured in pixels/ms^2.

Overshooting is also handled within the deceleration effect,
turning into a constant acceleration vector in the opposite
direction so it returns elastically within the boundaries.
2012-02-24 03:24:03 -05:00
Carlos Garnacho
7f03de11ef scrolledwindow: Use the child widget's window bg color for the overshoot area
This makes the overshoot area seamless, if the child plays along.
2012-02-24 03:24:03 -05:00
Carlos Garnacho
6cfadef0a7 scrolledwindow: Implement overshooting
An extra GdkWindow has been added, this window is the parent
of the child widget, and is the one getting resized/moved when
overshooting.

The unclamped adjustments' values are also stored in
GtkScrolledWindowPrivate as a separate value, overshooting is
pretty specific to GtkScrolledWindow and it isn't worth to
expose API in GtkAlignment for this single purpose.

This method allows GtkScrollable children to be blissfully
unaware of overshooting, as otherwise they'd have to handle
rather odd adjustment values themselves.
2012-02-24 03:24:03 -05:00
Carlos Garnacho
7fb105e25e scrolledwindow: Add window for overshooting
This window is the child widget's parent window, and will
be the one that's moved when overshooting.
2012-02-24 03:24:03 -05:00
Carlos Garnacho
e5ff3129aa scrolledwindow: Handle nested scrolled windows in kinetic scrolling
The innermost scrolled window always gets to capture the events, all
scrolled windows above it just let the event go through. Ideally
reaching a limit on the innermost scrolled window would propagate
the dragging up the hierarchy in order to keep following the touch
coords, although that'd involve rather evil hacks just to cater
for broken UIs.
2012-02-24 03:24:02 -05:00
Carlos Garnacho
bf306a2424 scrolledwindow: Add another shortcut to bypass event capture
When clicked again close to the previous button press location
(assuming it had ~0 movement), the scrolled window will allow
the child to handle the events immediately.

This is so the user doesn't have to wait to the press-and-hold
timeout in order to operate on the scrolledwindow child.
2012-02-24 03:24:02 -05:00
Matthias Clasen
f6600be559 scrolledwindow: Allow selections and DND when kinetic scrolling is on
If the scrolling doesn't start after a long press, the scrolling is
cancelled and events are handled by child widgets normally.
2012-02-24 03:24:02 -05:00
Carlos Garcia Campos
66cb43da67 tests: Add new test for kinetic scrolling 2012-02-24 03:24:02 -05:00
Carlos Garcia Campos
fcf2bd816c test: Add checkbox to enable/disable kinetic scrolling in scrolled window test 2012-02-24 03:24:01 -05:00
Carlos Garcia Campos
bef9f5c6c4 scrolledwindow: Initial kinetic scrolling support
Kinetic scrolling is only done on touch devices, since it is
sort of meaningless on pointer devices, besides it implies
a different input event handling on child widgets that is
unnecessary there.
2012-02-24 03:24:01 -05:00
Carlos Garcia Campos
31cad1bb81 Add GtkWidget::captured-event signal
This patch adds a capture phase to GTK+'s event propagation
model. Events are first propagated from the toplevel (or the
grab widget, if a grab is in place) down to the target widget
using the new ::captured-event signal, and then back up, using
the existing ::event signal.

This mechanism can be used in many places where we currently
have to prevent child widgets from getting events by putting
an input-only window over them. It will also be used to implement
kinetic scrolling in subsequent patches.

http://bugzilla.gnome.org/show_bug.cgi?id=641836
2012-02-24 03:24:01 -05:00
Carlos Garnacho
e0c442a4e8 gdk: Add touch motion event type and mask
These events' struct is the same than GdkEventMotion, which has been
added a touch_id parameter. The gdk_event_* functions have been modified
to also handle this event type.
2012-02-24 03:24:01 -05:00
Carlos Garnacho
b5469d783c gdk: Add GDK_SOURCE_TOUCH
This device source applies to touch capable devices, most
notably touchscreens.
2012-02-24 03:24:00 -05:00
39 changed files with 3439 additions and 625 deletions

View File

@@ -935,7 +935,7 @@ if test "x$enable_x11_backend" = xyes; then
have_base_x_pc=true
X_PACKAGES="$X_PACKAGES x11 xext"
x_libs="`$PKG_CONFIG --libs x11 xext`"
X_CFLAGS="`$PKG_CONFIG --cflags x11 xext`"
X_CFLAGS="`$PKG_CONFIG --cflags x11 xext` -DXINPUT2_1_USE_UNSTABLE_PROTOCOL -DXINPUT2_2_USE_UNSTABLE_PROTOCOL"
# Strip out any .la files that pkg-config might give us (this happens
# with -uninstalled.pc files)
@@ -1126,6 +1126,10 @@ if test "x$enable_x11_backend" = xyes; then
AC_DEFINE(XINPUT_2, 1, [Define to 1 if XInput 2.0 is available]),
X_EXTENSIONS="$X_EXTENSIONS XInput")
gtk_save_LIBS="$LIBS"
LIBS="$LIBS -lXi"
AC_CHECK_FUNC(XIAllowTouchEvents, AC_DEFINE(XINPUT_2_2, 1, [Define to 1 if XInput 2.2 is available]))
LIBS="$gtk_save_LIBS"
else
AC_DEFINE(XINPUT_NONE, 1,
[Define to 1 if no XInput should be used])

View File

@@ -782,6 +782,8 @@ gdk_event_get_root_coords
gdk_event_get_scroll_direction
gdk_event_get_state
gdk_event_get_time
GdkEventSequence
gdk_event_get_event_sequence
gdk_event_request_motions
gdk_events_get_angle
gdk_events_get_center
@@ -819,6 +821,7 @@ GdkEvent
GdkEventAny
GdkEventKey
GdkEventButton
GdkEventTouch
GdkEventScroll
GdkEventMotion
GdkEventExpose

View File

@@ -2939,6 +2939,10 @@ gtk_scrolled_window_get_min_content_width
gtk_scrolled_window_set_min_content_width
gtk_scrolled_window_get_min_content_height
gtk_scrolled_window_set_min_content_height
gtk_scrolled_window_set_kinetic_scrolling
gtk_scrolled_window_get_kinetic_scrolling
gtk_scrolled_window_set_capture_button_press
gtk_scrolled_window_get_capture_button_press
<SUBSECTION Standard>
GTK_SCROLLED_WINDOW

View File

@@ -168,6 +168,7 @@ gdk_event_get_scroll_direction
gdk_event_get_source_device
gdk_event_get_state
gdk_event_get_time
gdk_event_get_event_sequence
gdk_event_get_type
gdk_event_handler_set
gdk_event_mask_get_type

View File

@@ -61,6 +61,10 @@ typedef enum
* of a stylus on a graphics tablet.
* @GDK_SOURCE_CURSOR: the device is a graphics tablet "puck" or similar device.
* @GDK_SOURCE_KEYBOARD: the device is a keyboard.
* @GDK_SOURCE_TOUCHSCREEN: the device is a direct-input touch device, such
* as a touchscreen or tablet. This device type has been added in 3.4.
* @GDK_SOURCE_TOUCHPAD: the device is an indirect touch device, such
* as a touchpad. This device type has been added in 3.4.
*
* An enumeration describing the type of an input device in general terms.
*/
@@ -70,7 +74,9 @@ typedef enum
GDK_SOURCE_PEN,
GDK_SOURCE_ERASER,
GDK_SOURCE_CURSOR,
GDK_SOURCE_KEYBOARD
GDK_SOURCE_KEYBOARD,
GDK_SOURCE_TOUCHSCREEN,
GDK_SOURCE_TOUCHPAD
} GdkInputSource;
/**

View File

@@ -188,6 +188,7 @@ gdk_display_init (GdkDisplay *display)
display->double_click_time = 250;
display->double_click_distance = 5;
display->touch_implicit_grabs = g_array_new (FALSE, FALSE, sizeof (GdkTouchGrabInfo));
display->device_grabs = g_hash_table_new (NULL, NULL);
display->motion_hint_info = g_hash_table_new_full (NULL, NULL, NULL,
(GDestroyNotify) g_free);
@@ -236,6 +237,8 @@ gdk_display_finalize (GObject *object)
NULL);
g_hash_table_destroy (display->device_grabs);
g_array_free (display->touch_implicit_grabs, TRUE);
g_hash_table_destroy (display->motion_hint_info);
g_hash_table_destroy (display->pointers_info);
g_hash_table_destroy (display->multiple_click_info);
@@ -694,6 +697,75 @@ _gdk_display_add_device_grab (GdkDisplay *display,
return info;
}
static void
_gdk_display_break_touch_grabs (GdkDisplay *display,
GdkDevice *device,
GdkWindow *new_grab_window)
{
guint i;
for (i = 0; i < display->touch_implicit_grabs->len; i++)
{
GdkTouchGrabInfo *info;
info = &g_array_index (display->touch_implicit_grabs,
GdkTouchGrabInfo, i);
if (info->device == device &&
info->window != new_grab_window)
generate_grab_broken_event (GDK_WINDOW (info->window),
device, TRUE, new_grab_window);
}
}
void
_gdk_display_add_touch_grab (GdkDisplay *display,
GdkDevice *device,
GdkEventSequence *sequence,
GdkWindow *window,
GdkWindow *native_window,
GdkEventMask event_mask,
unsigned long serial,
guint32 time)
{
GdkTouchGrabInfo info;
info.device = device;
info.sequence = sequence;
info.window = g_object_ref (window);
info.native_window = g_object_ref (native_window);
info.serial = serial;
info.event_mask = event_mask;
info.time = time;
g_array_append_val (display->touch_implicit_grabs, info);
}
gboolean
_gdk_display_end_touch_grab (GdkDisplay *display,
GdkDevice *device,
GdkEventSequence *sequence)
{
guint i;
for (i = 0; i < display->touch_implicit_grabs->len; i++)
{
GdkTouchGrabInfo *info;
info = &g_array_index (display->touch_implicit_grabs,
GdkTouchGrabInfo, i);
if (info->device == device &&
info->sequence == sequence)
{
g_array_remove_index_fast (display->touch_implicit_grabs, i);
return TRUE;
}
}
return FALSE;
}
/* _gdk_synthesize_crossing_events only works inside one toplevel.
This function splits things into two calls if needed, converting the
coordinates to the right toplevel */
@@ -897,15 +969,26 @@ switch_to_pointer_grab (GdkDisplay *display,
if (grab == NULL) /* Ungrabbed, send events */
{
pointer_window = NULL;
if (new_toplevel)
{
/* Find (possibly virtual) child window */
pointer_window =
_gdk_window_find_descendant_at (new_toplevel,
x, y,
NULL, NULL);
}
/* If the source device is a touch device, do not
* propagate any enter event yet, until one is
* synthesized when needed.
*/
if (source_device &&
(gdk_device_get_source (source_device) == GDK_SOURCE_TOUCHSCREEN ||
gdk_device_get_source (source_device) == GDK_SOURCE_TOUCHPAD))
info->need_touch_press_enter = TRUE;
pointer_window = NULL;
if (new_toplevel &&
!info->need_touch_press_enter)
{
/* Find (possibly virtual) child window */
pointer_window =
_gdk_window_find_descendant_at (new_toplevel,
x, y,
NULL, NULL);
}
if (pointer_window != last_grab->window)
synthesize_crossing_events (display, device, source_device,
@@ -964,12 +1047,15 @@ _gdk_display_device_grab_update (GdkDisplay *display,
next_grab = NULL; /* Actually its not yet active */
}
if (next_grab)
_gdk_display_break_touch_grabs (display, device, next_grab->window);
if ((next_grab == NULL && current_grab->implicit_ungrab) ||
(next_grab != NULL && current_grab->window != next_grab->window))
generate_grab_broken_event (GDK_WINDOW (current_grab->window),
(next_grab != NULL && current_grab->window != next_grab->window))
generate_grab_broken_event (GDK_WINDOW (current_grab->window),
device,
current_grab->implicit,
next_grab? next_grab->window : NULL);
current_grab->implicit,
next_grab? next_grab->window : NULL);
/* Remove old grab */
grabs = g_list_delete_link (grabs, grabs);
@@ -1028,6 +1114,34 @@ _gdk_display_has_device_grab (GdkDisplay *display,
return NULL;
}
GdkTouchGrabInfo *
_gdk_display_has_touch_grab (GdkDisplay *display,
GdkDevice *device,
GdkEventSequence *sequence,
gulong serial)
{
guint i;
for (i = 0; i < display->touch_implicit_grabs->len; i++)
{
GdkTouchGrabInfo *info;
info = &g_array_index (display->touch_implicit_grabs,
GdkTouchGrabInfo, i);
if (info->device == device &&
info->sequence == sequence)
{
if (serial >= info->serial)
return info;
else
return NULL;
}
}
return NULL;
}
/* Returns true if last grab was ended
* If if_child is non-NULL, end the grab only if the grabbed
* window is the same as if_child or a descendant of it */

View File

@@ -60,6 +60,19 @@ typedef struct
guint implicit : 1;
} GdkDeviceGrabInfo;
/* Tracks information about a touch implicit grab on this display */
typedef struct
{
GdkDevice *device;
GdkEventSequence *sequence;
GdkWindow *window;
GdkWindow *native_window;
gulong serial;
guint event_mask;
guint32 time;
} GdkTouchGrabInfo;
/* Tracks information about which window and position the pointer last was in.
* This is useful when we need to synthesize events later.
* Note that we track toplevel_under_pointer using enter/leave events,
@@ -76,6 +89,7 @@ typedef struct
guint32 state;
guint32 button;
GdkDevice *last_slave;
guint need_touch_press_enter : 1;
} GdkPointerWindowInfo;
typedef struct
@@ -104,6 +118,7 @@ struct _GdkDisplay
guint closed : 1; /* Whether this display has been closed */
guint ignore_core_events : 1; /* Don't send core motion and button event */
GArray *touch_implicit_grabs;
GHashTable *device_grabs;
GHashTable *motion_hint_info;
GdkDeviceManager *device_manager;
@@ -261,6 +276,21 @@ gboolean _gdk_display_end_device_grab (GdkDisplay *display
gboolean _gdk_display_check_grab_ownership (GdkDisplay *display,
GdkDevice *device,
gulong serial);
void _gdk_display_add_touch_grab (GdkDisplay *display,
GdkDevice *device,
GdkEventSequence *sequence,
GdkWindow *window,
GdkWindow *native_window,
GdkEventMask event_mask,
unsigned long serial_start,
guint32 time);
GdkTouchGrabInfo * _gdk_display_has_touch_grab (GdkDisplay *display,
GdkDevice *device,
GdkEventSequence *sequence,
gulong serial);
gboolean _gdk_display_end_touch_grab (GdkDisplay *display,
GdkDevice *device,
GdkEventSequence *sequence);
void _gdk_display_enable_motion_hints (GdkDisplay *display,
GdkDevice *device);
GdkPointerWindowInfo * _gdk_display_get_pointer_info (GdkDisplay *display,

View File

@@ -459,6 +459,15 @@ gdk_event_new (GdkEventType type)
new_event->button.x_root = 0.;
new_event->button.y_root = 0.;
break;
case GDK_TOUCH_BEGIN:
case GDK_TOUCH_UPDATE:
case GDK_TOUCH_END:
case GDK_TOUCH_CANCEL:
new_event->touch.x = 0.;
new_event->touch.y = 0.;
new_event->touch.x_root = 0.;
new_event->touch.y_root = 0.;
break;
case GDK_SCROLL:
new_event->scroll.x = 0.;
new_event->scroll.y = 0.;
@@ -487,7 +496,31 @@ gdk_event_is_allocated (const GdkEvent *event)
return FALSE;
}
void
_gdk_event_set_pointer_emulated (GdkEvent *event,
gboolean emulated)
{
if (gdk_event_is_allocated (event))
{
GdkEventPrivate *private = (GdkEventPrivate *) event;
if (emulated)
private->flags |= GDK_EVENT_POINTER_EMULATED;
else
private->flags &= ~(GDK_EVENT_POINTER_EMULATED);
}
}
gboolean
_gdk_event_get_pointer_emulated (GdkEvent *event)
{
if (gdk_event_is_allocated (event))
return (((GdkEventPrivate *) event)->flags & GDK_EVENT_POINTER_EMULATED) != 0;
return FALSE;
}
/**
* gdk_event_copy:
* @event: a #GdkEvent
@@ -563,6 +596,15 @@ gdk_event_copy (const GdkEvent *event)
sizeof (gdouble) * gdk_device_get_n_axes (event->button.device));
break;
case GDK_TOUCH_BEGIN:
case GDK_TOUCH_UPDATE:
case GDK_TOUCH_END:
case GDK_TOUCH_CANCEL:
if (event->touch.axes)
new_event->touch.axes = g_memdup (event->touch.axes,
sizeof (gdouble) * gdk_device_get_n_axes (event->touch.device));
break;
case GDK_MOTION_NOTIFY:
if (event->motion.axes)
new_event->motion.axes = g_memdup (event->motion.axes,
@@ -641,7 +683,14 @@ gdk_event_free (GdkEvent *event)
case GDK_BUTTON_RELEASE:
g_free (event->button.axes);
break;
case GDK_TOUCH_BEGIN:
case GDK_TOUCH_UPDATE:
case GDK_TOUCH_END:
case GDK_TOUCH_CANCEL:
g_free (event->touch.axes);
break;
case GDK_EXPOSE:
case GDK_DAMAGE:
if (event->expose.region)
@@ -702,6 +751,11 @@ gdk_event_get_time (const GdkEvent *event)
case GDK_3BUTTON_PRESS:
case GDK_BUTTON_RELEASE:
return event->button.time;
case GDK_TOUCH_BEGIN:
case GDK_TOUCH_UPDATE:
case GDK_TOUCH_END:
case GDK_TOUCH_CANCEL:
return event->touch.time;
case GDK_SCROLL:
return event->scroll.time;
case GDK_KEY_PRESS:
@@ -777,7 +831,13 @@ gdk_event_get_state (const GdkEvent *event,
case GDK_2BUTTON_PRESS:
case GDK_3BUTTON_PRESS:
case GDK_BUTTON_RELEASE:
*state = event->button.state;
*state = event->button.state;
return TRUE;
case GDK_TOUCH_BEGIN:
case GDK_TOUCH_UPDATE:
case GDK_TOUCH_END:
case GDK_TOUCH_CANCEL:
*state = event->touch.state;
return TRUE;
case GDK_SCROLL:
*state = event->scroll.state;
@@ -868,6 +928,13 @@ gdk_event_get_coords (const GdkEvent *event,
x = event->button.x;
y = event->button.y;
break;
case GDK_TOUCH_BEGIN:
case GDK_TOUCH_UPDATE:
case GDK_TOUCH_END:
case GDK_TOUCH_CANCEL:
x = event->touch.x;
y = event->touch.y;
break;
case GDK_MOTION_NOTIFY:
x = event->motion.x;
y = event->motion.y;
@@ -922,6 +989,13 @@ gdk_event_get_root_coords (const GdkEvent *event,
x = event->button.x_root;
y = event->button.y_root;
break;
case GDK_TOUCH_BEGIN:
case GDK_TOUCH_UPDATE:
case GDK_TOUCH_END:
case GDK_TOUCH_CANCEL:
x = event->touch.x_root;
y = event->touch.y_root;
break;
case GDK_ENTER_NOTIFY:
case GDK_LEAVE_NOTIFY:
x = event->crossing.x_root;
@@ -1162,7 +1236,7 @@ gdk_event_get_axis (const GdkEvent *event,
switch (event->type)
{
case GDK_MOTION_NOTIFY:
case GDK_MOTION_NOTIFY:
x = event->motion.x;
y = event->motion.y;
break;
@@ -1175,6 +1249,13 @@ gdk_event_get_axis (const GdkEvent *event,
x = event->button.x;
y = event->button.y;
break;
case GDK_TOUCH_BEGIN:
case GDK_TOUCH_UPDATE:
case GDK_TOUCH_END:
case GDK_TOUCH_CANCEL:
x = event->touch.x;
y = event->touch.y;
break;
case GDK_ENTER_NOTIFY:
case GDK_LEAVE_NOTIFY:
x = event->crossing.x;
@@ -1198,6 +1279,14 @@ gdk_event_get_axis (const GdkEvent *event,
device = event->button.device;
axes = event->button.axes;
}
else if (event->type == GDK_TOUCH_BEGIN ||
event->type == GDK_TOUCH_UPDATE ||
event->type == GDK_TOUCH_END ||
event->type == GDK_TOUCH_CANCEL)
{
device = event->touch.device;
axes = event->touch.axes;
}
else if (event->type == GDK_MOTION_NOTIFY)
{
device = event->motion.device;
@@ -1243,6 +1332,12 @@ gdk_event_set_device (GdkEvent *event,
case GDK_BUTTON_RELEASE:
event->button.device = device;
break;
case GDK_TOUCH_BEGIN:
case GDK_TOUCH_UPDATE:
case GDK_TOUCH_END:
case GDK_TOUCH_CANCEL:
event->touch.device = device;
break;
case GDK_SCROLL:
event->scroll.device = device;
break;
@@ -1288,6 +1383,11 @@ gdk_event_get_device (const GdkEvent *event)
case GDK_3BUTTON_PRESS:
case GDK_BUTTON_RELEASE:
return event->button.device;
case GDK_TOUCH_BEGIN:
case GDK_TOUCH_UPDATE:
case GDK_TOUCH_END:
case GDK_TOUCH_CANCEL:
return event->touch.device;
case GDK_SCROLL:
return event->scroll.device;
case GDK_PROXIMITY_IN:
@@ -1305,6 +1405,10 @@ gdk_event_get_device (const GdkEvent *event)
case GDK_2BUTTON_PRESS:
case GDK_3BUTTON_PRESS:
case GDK_BUTTON_RELEASE:
case GDK_TOUCH_BEGIN:
case GDK_TOUCH_UPDATE:
case GDK_TOUCH_END:
case GDK_TOUCH_CANCEL:
case GDK_ENTER_NOTIFY:
case GDK_LEAVE_NOTIFY:
case GDK_FOCUS_CHANGE:
@@ -1686,6 +1790,35 @@ gdk_event_get_screen (const GdkEvent *event)
return NULL;
}
/**
* gdk_event_get_event_sequence:
* @event: a #GdkEvent
*
* Returns the #GdkEventSequence to which @event belongs.
* Currently, only touch events (i.e. those with type %GDK_TOUCH_BEGIN,
* %GDK_TOUCH_UPDATE, %GDK_TOUCH_END or %GDK_TOUCH_CANCEL) are
* connected in sequences. If the event does not belong to an
* event sequence, %NULL is returned.
*
* Returns: the event sequence that the event belongs to, or %NULL
*
* Since: 3.4
*/
GdkEventSequence *
gdk_event_get_event_sequence (const GdkEvent *event)
{
if (!event)
return NULL;
if (event->type == GDK_TOUCH_BEGIN ||
event->type == GDK_TOUCH_UPDATE ||
event->type == GDK_TOUCH_END ||
event->type == GDK_TOUCH_CANCEL)
return event->touch.sequence;
else
return NULL;
}
/**
* gdk_set_show_events:
* @show_events: %TRUE to output event debugging information.

View File

@@ -130,6 +130,7 @@ typedef struct _GdkEventExpose GdkEventExpose;
typedef struct _GdkEventVisibility GdkEventVisibility;
typedef struct _GdkEventMotion GdkEventMotion;
typedef struct _GdkEventButton GdkEventButton;
typedef struct _GdkEventTouch GdkEventTouch;
typedef struct _GdkEventScroll GdkEventScroll;
typedef struct _GdkEventKey GdkEventKey;
typedef struct _GdkEventFocus GdkEventFocus;
@@ -144,6 +145,8 @@ typedef struct _GdkEventWindowState GdkEventWindowState;
typedef struct _GdkEventSetting GdkEventSetting;
typedef struct _GdkEventGrabBroken GdkEventGrabBroken;
typedef struct _GdkEventSequence GdkEventSequence;
typedef union _GdkEvent GdkEvent;
/**
@@ -263,6 +266,14 @@ typedef GdkFilterReturn (*GdkFilterFunc) (GdkXEvent *xevent,
* was added in 2.8.
* @GDK_DAMAGE: the content of the window has been changed. This event type
* was added in 2.14.
* @GDK_TOUCH_BEGIN: A new touch event sequence has just started. This event
* type was added in 3.4.
* @GDK_TOUCH_UPDATE: A touch event sequence has been updated. This event type
* was added in 3.4.
* @GDK_TOUCH_END: A touch event sequence has finished. This event type
* was added in 3.4.
* @GDK_TOUCH_CANCEL: A touch event sequence has been canceled. This event type
* was added in 3.4.
* @GDK_EVENT_LAST: marks the end of the GdkEventType enumeration. Added in 2.18
*
* Specifies the type of the event.
@@ -310,6 +321,10 @@ typedef enum
GDK_OWNER_CHANGE = 34,
GDK_GRAB_BROKEN = 35,
GDK_DAMAGE = 36,
GDK_TOUCH_BEGIN = 37,
GDK_TOUCH_UPDATE = 38,
GDK_TOUCH_END = 39,
GDK_TOUCH_CANCEL = 40,
GDK_EVENT_LAST /* helper variable for decls */
} GdkEventType;
@@ -385,6 +400,13 @@ typedef enum
* @GDK_CROSSING_GTK_UNGRAB: crossing because a GTK+ grab is deactivated.
* @GDK_CROSSING_STATE_CHANGED: crossing because a GTK+ widget changed
* state (e.g. sensitivity).
* @GDK_CROSSING_TOUCH_PRESS: crossing because a touch device was pressed,
* this event is synthetic as the pointer might have not left the window.
* @GDK_CROSSING_TOUCH_RELEASE: crossing because a touch device was released.
* this event is synthetic as the pointer might have not left the window.
* @GDK_CROSSING_DEVICE_SWITCH: crossing because of a device switch (i.e.
* a mouse taking control of the pointer after a touch device), this event
* is synthetic as the pointer didn't leave the window.
*
* Specifies the crossing mode for #GdkEventCrossing.
*/
@@ -395,7 +417,10 @@ typedef enum
GDK_CROSSING_UNGRAB,
GDK_CROSSING_GTK_GRAB,
GDK_CROSSING_GTK_UNGRAB,
GDK_CROSSING_STATE_CHANGED
GDK_CROSSING_STATE_CHANGED,
GDK_CROSSING_TOUCH_PRESS,
GDK_CROSSING_TOUCH_RELEASE,
GDK_CROSSING_DEVICE_SWITCH
} GdkCrossingMode;
/**
@@ -573,7 +598,7 @@ struct _GdkEventMotion
/**
* GdkEventButton:
* @type: the type of the event (%GDK_BUTTON_PRESS, %GDK_2BUTTON_PRESS,
* %GDK_3BUTTON_PRESS or %GDK_BUTTON_RELEASE).
* %GDK_3BUTTON_PRESS, %GDK_BUTTON_RELEASE).
* @window: the window which received the event.
* @send_event: %TRUE if the event was sent explicitly (e.g. using
* <function>XSendEvent</function>).
@@ -597,7 +622,7 @@ struct _GdkEventMotion
*
* Used for button press and button release events. The
* @type field will be one of %GDK_BUTTON_PRESS,
* %GDK_2BUTTON_PRESS, %GDK_3BUTTON_PRESS, and %GDK_BUTTON_RELEASE.
* %GDK_2BUTTON_PRESS, %GDK_3BUTTON_PRESS or %GDK_BUTTON_RELEASE,
*
* Double and triple-clicks result in a sequence of events being received.
* For double-clicks the order of events will be:
@@ -645,6 +670,57 @@ struct _GdkEventButton
gdouble x_root, y_root;
};
/**
* GdkEventTouch:
* @type: the type of the event (%GDK_TOUCH_BEGIN, %GDK_TOUCH_UPDATE,
* %GDK_TOUCH_END, %GDK_TOUCH_CANCEL)
* @window: the window which received the event
* @send_event: %TRUE if the event was sent explicitly (e.g. using
* <function>XSendEvent</function>)
* @time: the time of the event in milliseconds.
* @x: the x coordinate of the pointer relative to the window
* @y: the y coordinate of the pointer relative to the window
* @axes: @x, @y translated to the axes of @device, or %NULL if @device is
* the mouse
* @state: (type GdkModifierType): a bit-mask representing the state of
* the modifier keys (e.g. Control, Shift and Alt) and the pointer
* buttons. See #GdkModifierType
* @sequence: the event sequence that the event belongs to
* @emulating_pointer: whether the event should be used for emulating
* pointer event
* @device: the device where the event originated
* @x_root: the x coordinate of the pointer relative to the root of the
* screen
* @y_root: the y coordinate of the pointer relative to the root of the
* screen
*
* Used for touch events.
* @type field will be one of %GDK_TOUCH_BEGIN, %GDK_TOUCH_UPDATE,
* %GDK_TOUCH_END or %GDK_TOUCH_CANCEL.
*
* Touch events are grouped into sequences by means of the @sequence
* field, which can also be obtained with gdk_event_get_event_sequence().
* Each sequence begins with a %GDK_TOUCH_BEGIN event, followed by
* any number of %GDK_TOUCH_UPDATE events, and ends with a %GDK_TOUCH_END
* (or %GDK_TOUCH_CANCEL) event. With multitouch devices, there may be
* several active sequences at the same time.
*/
struct _GdkEventTouch
{
GdkEventType type;
GdkWindow *window;
gint8 send_event;
guint32 time;
gdouble x;
gdouble y;
gdouble *axes;
guint state;
GdkEventSequence *sequence;
gboolean emulating_pointer;
GdkDevice *device;
gdouble x_root, y_root;
};
/**
* GdkEventScroll:
* @type: the type of the event (%GDK_SCROLL).
@@ -1073,6 +1149,7 @@ union _GdkEvent
GdkEventVisibility visibility;
GdkEventMotion motion;
GdkEventButton button;
GdkEventTouch touch;
GdkEventScroll scroll;
GdkEventKey key;
GdkEventCrossing crossing;
@@ -1150,6 +1227,8 @@ void gdk_event_set_screen (GdkEvent *event,
GdkScreen *screen);
GdkScreen *gdk_event_get_screen (const GdkEvent *event);
GdkEventSequence *gdk_event_get_event_sequence (const GdkEvent *event);
void gdk_set_show_events (gboolean show_events);
gboolean gdk_get_show_events (void);

View File

@@ -150,7 +150,13 @@ typedef enum
/* Following flag is set for events on the event queue during
* translation and cleared afterwards.
*/
GDK_EVENT_PENDING = 1 << 0
GDK_EVENT_PENDING = 1 << 0,
/* The following flag is set for:
* 1) touch events emulating pointer events
* 2) pointer events being emulated by a touch sequence.
*/
GDK_EVENT_POINTER_EMULATED = 1 << 1
} GdkEventFlags;
struct _GdkEventPrivate
@@ -275,6 +281,10 @@ GdkEvent* _gdk_event_unqueue (GdkDisplay *display);
void _gdk_event_filter_unref (GdkWindow *window,
GdkEventFilter *filter);
void _gdk_event_set_pointer_emulated (GdkEvent *event,
gboolean emulated);
gboolean _gdk_event_get_pointer_emulated (GdkEvent *event);
void _gdk_event_emit (GdkEvent *event);
GList* _gdk_event_queue_find_first (GdkDisplay *display);
void _gdk_event_queue_remove_link (GdkDisplay *display,
@@ -318,6 +328,9 @@ gboolean _gdk_window_update_viewable (GdkWindow *window);
void _gdk_window_process_updates_recurse (GdkWindow *window,
cairo_region_t *expose_region);
gboolean _gdk_window_finish_touch_id (GdkWindow *window,
GdkDevice *device,
guint touch_id);
void _gdk_screen_close (GdkScreen *screen);

View File

@@ -350,6 +350,7 @@ typedef enum
* @GDK_SUBSTRUCTURE_MASK: receive events about window configuration changes of
* child windows
* @GDK_SCROLL_MASK: receive scroll events
* @GDK_TOUCH_MASK: receive touch events
* @GDK_ALL_EVENTS_MASK: the combination of all the above event masks.
*
* A set of bit-flags to indicate which events a window is to receive.
@@ -365,6 +366,13 @@ typedef enum
* some of which are marked as a hint (the is_hint member is %TRUE).
* To receive more motion events after a motion hint event, the application
* needs to asks for more, by calling gdk_event_request_motions().
*
* If %GDK_TOUCH_MASK is enabled, the window will receive touch events
* from touch-enabled devices. Those will come as sequences of #GdkEventTouch
* with type %GDK_TOUCH_UPDATE, enclosed by two events with
* type %GDK_TOUCH_BEGIN and %GDK_TOUCH_END (or %GDK_TOUCH_CANCEL).
* gdk_event_get_event_sequence() returns the event sequence for these
* events, so different sequences may be distinguished.
*/
typedef enum
{
@@ -389,7 +397,8 @@ typedef enum
GDK_PROXIMITY_OUT_MASK = 1 << 19,
GDK_SUBSTRUCTURE_MASK = 1 << 20,
GDK_SCROLL_MASK = 1 << 21,
GDK_ALL_EVENTS_MASK = 0x3FFFFE
GDK_TOUCH_MASK = 1 << 22,
GDK_ALL_EVENTS_MASK = 0x7FFFFE
} GdkEventMask;
/**

View File

@@ -1245,11 +1245,12 @@ get_native_device_event_mask (GdkWindow *private,
* lists due to some non-native child window.
*/
if (gdk_window_is_toplevel (private) ||
mask & GDK_BUTTON_PRESS_MASK)
mask |=
GDK_POINTER_MOTION_MASK |
GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
GDK_SCROLL_MASK;
mask & GDK_BUTTON_PRESS_MASK)
mask |=
GDK_TOUCH_MASK |
GDK_POINTER_MOTION_MASK |
GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
GDK_SCROLL_MASK;
return mask;
}
@@ -8119,6 +8120,10 @@ static const guint type_masks[] = {
0, /* GDK_OWNER_CHANGE = 34 */
0, /* GDK_GRAB_BROKEN = 35 */
0, /* GDK_DAMAGE = 36 */
GDK_TOUCH_MASK, /* GDK_TOUCH_BEGIN = 37 */
GDK_TOUCH_MASK, /* GDK_TOUCH_UPDATE = 38 */
GDK_TOUCH_MASK, /* GDK_TOUCH_END = 39 */
GDK_TOUCH_MASK /* GDK_TOUCH_CANCEL = 40 */
};
G_STATIC_ASSERT (G_N_ELEMENTS (type_masks) == GDK_EVENT_LAST);
@@ -8150,6 +8155,8 @@ is_button_type (GdkEventType type)
type == GDK_2BUTTON_PRESS ||
type == GDK_3BUTTON_PRESS ||
type == GDK_BUTTON_RELEASE ||
type == GDK_TOUCH_BEGIN ||
type == GDK_TOUCH_END ||
type == GDK_SCROLL;
}
@@ -8157,10 +8164,20 @@ static gboolean
is_motion_type (GdkEventType type)
{
return type == GDK_MOTION_NOTIFY ||
type == GDK_TOUCH_UPDATE ||
type == GDK_ENTER_NOTIFY ||
type == GDK_LEAVE_NOTIFY;
}
static gboolean
is_touch_type (GdkEventType type)
{
return type == GDK_TOUCH_BEGIN ||
type == GDK_TOUCH_UPDATE ||
type == GDK_TOUCH_END ||
type == GDK_TOUCH_CANCEL;
}
static GdkWindow *
find_common_ancestor (GdkWindow *win1,
GdkWindow *win2)
@@ -8233,6 +8250,15 @@ _gdk_make_event (GdkWindow *window,
event->button.state = the_state;
break;
case GDK_TOUCH_BEGIN:
case GDK_TOUCH_UPDATE:
case GDK_TOUCH_END:
case GDK_TOUCH_CANCEL:
event->touch.time = the_time;
event->touch.axes = NULL;
event->touch.state = the_state;
break;
case GDK_SCROLL:
event->scroll.time = the_time;
event->scroll.state = the_state;
@@ -8321,12 +8347,27 @@ send_crossing_event (GdkDisplay *display,
GdkEvent *event;
guint32 window_event_mask, type_event_mask;
GdkDeviceGrabInfo *grab;
GdkTouchGrabInfo *touch_grab = NULL;
GdkPointerWindowInfo *pointer_info;
gboolean block_event = FALSE;
GdkEventSequence *sequence;
grab = _gdk_display_has_device_grab (display, device, serial);
pointer_info = _gdk_display_get_pointer_info (display, device);
if (grab != NULL &&
!grab->owner_events)
sequence = gdk_event_get_event_sequence (event_in_queue);
if (sequence)
touch_grab = _gdk_display_has_touch_grab (display, device, sequence, serial);
if (touch_grab)
{
if (window != touch_grab->window)
return;
window_event_mask = touch_grab->event_mask;
}
else if (grab != NULL &&
!grab->owner_events)
{
/* !owner_event => only report events wrt grab window, ignore rest */
if ((GdkWindow *)window != grab->window)
@@ -8336,7 +8377,14 @@ send_crossing_event (GdkDisplay *display,
else
window_event_mask = window->event_mask;
if (type == GDK_LEAVE_NOTIFY)
if (type == GDK_ENTER_NOTIFY &&
pointer_info->need_touch_press_enter &&
mode != GDK_CROSSING_TOUCH_PRESS &&
mode != GDK_CROSSING_TOUCH_RELEASE)
{
block_event = TRUE;
}
else if (type == GDK_LEAVE_NOTIFY)
{
type_event_mask = GDK_LEAVE_NOTIFY_MASK;
window->devices_inside = g_list_remove (window->devices_inside, device);
@@ -9084,17 +9132,54 @@ _gdk_synthesize_crossing_events_for_geometry_change (GdkWindow *changed_window)
static GdkWindow *
get_event_window (GdkDisplay *display,
GdkDevice *device,
GdkWindow *pointer_window,
GdkEventType type,
GdkModifierType mask,
guint *evmask_out,
gulong serial)
GdkEventSequence *sequence,
GdkWindow *pointer_window,
GdkEventType type,
GdkModifierType mask,
guint *evmask_out,
gboolean pointer_emulated,
gulong serial)
{
guint evmask;
guint evmask, emulated_mask = 0;
GdkWindow *grab_window;
GdkDeviceGrabInfo *grab;
GdkTouchGrabInfo *touch_grab;
grab = _gdk_display_has_device_grab (display, device, serial);
touch_grab = _gdk_display_has_touch_grab (display, device, sequence, serial);
grab = _gdk_display_get_last_device_grab (display, device);
if (is_touch_type (type) && pointer_emulated)
{
switch (type)
{
case GDK_TOUCH_BEGIN:
emulated_mask |= GDK_BUTTON_PRESS_MASK;
break;
case GDK_TOUCH_UPDATE:
emulated_mask |= GDK_BUTTON_MOTION_MASK;
break;
case GDK_TOUCH_END:
emulated_mask |= GDK_BUTTON_RELEASE_MASK;
default:
break;
}
}
if (touch_grab != NULL &&
(!grab || grab->implicit || touch_grab->serial >= grab->serial_start))
{
evmask = touch_grab->event_mask;
evmask = update_evmask_for_button_motion (evmask, mask);
if (evmask & (type_masks[type] | emulated_mask))
{
if (evmask_out)
*evmask_out = evmask;
return touch_grab->window;
}
else
return NULL;
}
if (grab != NULL && !grab->owner_events)
{
@@ -9103,7 +9188,7 @@ get_event_window (GdkDisplay *display,
grab_window = grab->window;
if (evmask & type_masks[type])
if (evmask & (type_masks[type] | emulated_mask))
{
if (evmask_out)
*evmask_out = evmask;
@@ -9118,7 +9203,7 @@ get_event_window (GdkDisplay *display,
evmask = pointer_window->event_mask;
evmask = update_evmask_for_button_motion (evmask, mask);
if (evmask & type_masks[type])
if (evmask & (type_masks[type] | emulated_mask))
{
if (evmask_out)
*evmask_out = evmask;
@@ -9134,7 +9219,7 @@ get_event_window (GdkDisplay *display,
evmask = grab->event_mask;
evmask = update_evmask_for_button_motion (evmask, mask);
if (evmask & type_masks[type])
if (evmask & (type_masks[type] | emulated_mask))
{
if (evmask_out)
*evmask_out = evmask;
@@ -9160,7 +9245,7 @@ proxy_pointer_event (GdkDisplay *display,
guint state;
gdouble toplevel_x, toplevel_y;
guint32 time_;
gboolean non_linear;
gboolean non_linear, need_synthetic_enter = FALSE;
event_window = source_event->any.window;
gdk_event_get_coords (source_event, &toplevel_x, &toplevel_y);
@@ -9180,6 +9265,16 @@ proxy_pointer_event (GdkDisplay *display,
source_event->crossing.detail == GDK_NOTIFY_NONLINEAR_VIRTUAL))
non_linear = TRUE;
if (pointer_info->need_touch_press_enter &&
gdk_device_get_source (pointer_info->last_slave) != GDK_SOURCE_TOUCHSCREEN &&
gdk_device_get_source (pointer_info->last_slave) != GDK_SOURCE_TOUCHPAD &&
(source_event->type != GDK_TOUCH_UPDATE ||
_gdk_event_get_pointer_emulated (source_event)))
{
pointer_info->need_touch_press_enter = FALSE;
need_synthetic_enter = TRUE;
}
/* If we get crossing events with subwindow unexpectedly being NULL
that means there is a native subwindow that gdk doesn't know about.
We track these and forward them, with the correct virtual window
@@ -9267,7 +9362,9 @@ proxy_pointer_event (GdkDisplay *display,
return TRUE;
}
if (pointer_info->window_under_pointer != pointer_window)
if ((source_event->type != GDK_TOUCH_UPDATE ||
_gdk_event_get_pointer_emulated (source_event)) &&
pointer_info->window_under_pointer != pointer_window)
{
/* Either a toplevel crossing notify that ended up inside a child window,
or a motion notify that got into another child window */
@@ -9284,28 +9381,73 @@ proxy_pointer_event (GdkDisplay *display,
serial, non_linear);
_gdk_display_set_window_under_pointer (display, device, pointer_window);
}
else if (source_event->type == GDK_MOTION_NOTIFY)
else if (source_event->type == GDK_MOTION_NOTIFY ||
source_event->type == GDK_TOUCH_UPDATE)
{
GdkWindow *event_win;
guint evmask;
gboolean is_hint;
GdkEventSequence *sequence;
GdkEventType event_type;
sequence = gdk_event_get_event_sequence (source_event);
event_type = source_event->type;
event_win = get_event_window (display,
device,
sequence,
pointer_window,
source_event->type,
state,
&evmask,
_gdk_event_get_pointer_emulated (source_event),
serial);
if (event_type == GDK_TOUCH_UPDATE)
{
if (_gdk_event_get_pointer_emulated (source_event))
{
/* Touch events emulating pointer events are transformed back
* to pointer events if:
* 1 - The event window doesn't select for touch events
* 2 - There's no touch grab for this sequence, which means
* it was started as a pointer sequence, but a device
* grab added touch events afterwards, the sequence must
* not mutate in this case.
*/
if ((evmask & GDK_TOUCH_MASK) == 0 ||
!_gdk_display_has_touch_grab (display, device, sequence, serial))
event_type = GDK_MOTION_NOTIFY;
}
else if ((evmask & GDK_TOUCH_MASK) == 0)
return TRUE;
}
if (is_touch_type (source_event->type) && !is_touch_type (event_type))
state |= GDK_BUTTON1_MASK;
if (event_win &&
gdk_device_get_device_type (device) != GDK_DEVICE_TYPE_MASTER &&
gdk_window_get_device_events (event_win, device) == 0)
return TRUE;
/* The last device to interact with the window was a touch device,
* which synthesized a leave notify event, so synthesize another enter
* notify to tell the pointer is on the window.
*/
if (need_synthetic_enter)
_gdk_synthesize_crossing_events (display,
NULL, pointer_window,
device, source_device,
GDK_CROSSING_DEVICE_SWITCH,
toplevel_x, toplevel_y,
state, time_, NULL,
serial, FALSE);
is_hint = FALSE;
if (event_win &&
event_type == GDK_MOTION_NOTIFY &&
(evmask & GDK_POINTER_MOTION_HINT_MASK))
{
gulong *device_serial;
@@ -9323,22 +9465,58 @@ proxy_pointer_event (GdkDisplay *display,
}
}
if (event_win && !display->ignore_core_events)
{
event = _gdk_make_event (event_win, GDK_MOTION_NOTIFY, source_event, FALSE);
event->motion.time = time_;
convert_toplevel_coords_to_window (event_win,
toplevel_x, toplevel_y,
&event->motion.x, &event->motion.y);
event->motion.x_root = source_event->motion.x_root;
event->motion.y_root = source_event->motion.y_root;
event->motion.state = state;
event->motion.is_hint = is_hint;
event->motion.device = source_event->motion.device;
event->motion.axes = g_memdup (source_event->motion.axes,
sizeof (gdouble) * gdk_device_get_n_axes (source_event->motion.device));
if (!event_win)
return TRUE;
if (!display->ignore_core_events)
{
event = gdk_event_new (event_type);
event->any.window = g_object_ref (event_win);
event->any.send_event = source_event->any.send_event;
gdk_event_set_device (event, gdk_event_get_device (source_event));
gdk_event_set_source_device (event, source_device);
}
if (event_type == GDK_TOUCH_UPDATE)
{
event->touch.time = time_;
event->touch.state = state | GDK_BUTTON1_MASK;
event->touch.sequence = source_event->touch.sequence;
convert_toplevel_coords_to_window (event_win,
toplevel_x, toplevel_y,
&event->touch.x, &event->touch.y);
gdk_event_get_root_coords (source_event,
&event->touch.x_root,
&event->touch.y_root);
event->touch.axes = g_memdup (source_event->touch.axes,
sizeof (gdouble) * gdk_device_get_n_axes (source_event->touch.device));
}
else
{
event->motion.time = time_;
event->motion.state = state;
event->motion.is_hint = is_hint;
convert_toplevel_coords_to_window (event_win,
toplevel_x, toplevel_y,
&event->motion.x, &event->motion.y);
gdk_event_get_root_coords (source_event,
&event->motion.x_root,
&event->motion.y_root);
if (is_touch_type (source_event->type))
event->motion.axes = g_memdup (source_event->touch.axes,
sizeof (gdouble) * gdk_device_get_n_axes (source_event->touch.device));
else
event->motion.axes = g_memdup (source_event->motion.axes,
sizeof (gdouble) * gdk_device_get_n_axes (source_event->motion.device));
}
/* Just insert the event */
_gdk_event_queue_insert_after (gdk_window_get_display (event_win),
source_event, event);
}
}
/* unlink all move events from queue.
@@ -9361,6 +9539,8 @@ proxy_button_event (GdkEvent *source_event,
GdkWindow *pointer_window;
GdkWindow *parent;
GdkEvent *event;
GdkPointerWindowInfo *pointer_info;
GdkDeviceGrabInfo *pointer_grab;
guint state;
guint32 time_;
GdkEventType type;
@@ -9368,6 +9548,8 @@ proxy_button_event (GdkEvent *source_event,
GdkDisplay *display;
GdkWindow *w;
GdkDevice *device, *source_device;
GdkEventMask evmask;
GdkEventSequence *sequence;
type = source_event->any.type;
event_window = source_event->any.window;
@@ -9381,9 +9563,17 @@ proxy_button_event (GdkEvent *source_event,
toplevel_x, toplevel_y,
&toplevel_x, &toplevel_y);
if (type == GDK_BUTTON_PRESS &&
pointer_info = _gdk_display_get_pointer_info (display, device);
pointer_grab = _gdk_display_has_device_grab (display, device, serial);
sequence = gdk_event_get_event_sequence (source_event);
if ((type == GDK_BUTTON_PRESS ||
type == GDK_TOUCH_BEGIN) &&
!source_event->any.send_event &&
_gdk_display_has_device_grab (display, device, serial) == NULL)
(!pointer_grab ||
(type == GDK_TOUCH_BEGIN && pointer_grab->implicit &&
!_gdk_event_get_pointer_emulated (source_event))))
{
pointer_window =
_gdk_window_find_descendant_at (toplevel_window,
@@ -9396,23 +9586,46 @@ proxy_button_event (GdkEvent *source_event,
(parent = get_event_parent (w)) != NULL &&
parent->window_type != GDK_WINDOW_ROOT)
{
if (w->event_mask & GDK_BUTTON_PRESS_MASK)
if (w->event_mask & GDK_BUTTON_PRESS_MASK &&
(type == GDK_BUTTON_PRESS ||
_gdk_event_get_pointer_emulated (source_event)))
break;
if (type == GDK_TOUCH_BEGIN &&
w->event_mask & GDK_TOUCH_MASK)
break;
w = parent;
}
pointer_window = (GdkWindow *)w;
pointer_window = w;
_gdk_display_add_device_grab (display,
device,
pointer_window,
event_window,
GDK_OWNERSHIP_NONE,
FALSE,
gdk_window_get_events (pointer_window),
serial,
time_,
TRUE);
_gdk_display_device_grab_update (display, device, source_device, serial);
if (pointer_window)
{
if (type == GDK_TOUCH_BEGIN &&
pointer_window->event_mask & GDK_TOUCH_MASK)
{
_gdk_display_add_touch_grab (display, device, sequence,
pointer_window, event_window,
gdk_window_get_events (pointer_window),
serial, time_);
}
else if (type == GDK_BUTTON_PRESS ||
_gdk_event_get_pointer_emulated (source_event))
{
_gdk_display_add_device_grab (display,
device,
pointer_window,
event_window,
GDK_OWNERSHIP_NONE,
FALSE,
gdk_window_get_events (pointer_window),
serial,
time_,
TRUE);
_gdk_display_device_grab_update (display, device,
source_device, serial);
}
}
}
pointer_window = get_pointer_window (display, toplevel_window, device,
@@ -9421,9 +9634,32 @@ proxy_button_event (GdkEvent *source_event,
event_win = get_event_window (display,
device,
pointer_window,
type, state,
NULL, serial);
sequence,
pointer_window,
type, state,
&evmask,
_gdk_event_get_pointer_emulated (source_event),
serial);
if (type == GDK_TOUCH_BEGIN || type == GDK_TOUCH_END)
{
if (_gdk_event_get_pointer_emulated (source_event))
{
if ((evmask & GDK_TOUCH_MASK) == 0 ||
!_gdk_display_has_touch_grab (display, device, sequence, serial))
{
if (type == GDK_TOUCH_BEGIN)
type = GDK_BUTTON_PRESS;
else if (type == GDK_TOUCH_END)
type = GDK_BUTTON_RELEASE;
}
}
else if ((evmask & GDK_TOUCH_MASK) == 0)
return TRUE;
}
if (source_event->type == GDK_TOUCH_END && !is_touch_type (type))
state |= GDK_BUTTON1_MASK;
if (event_win == NULL || display->ignore_core_events)
return TRUE;
@@ -9432,6 +9668,33 @@ proxy_button_event (GdkEvent *source_event,
gdk_window_get_device_events (event_win, device) == 0)
return TRUE;
if ((type == GDK_BUTTON_PRESS ||
(type == GDK_TOUCH_BEGIN &&
_gdk_event_get_pointer_emulated (source_event))) &&
pointer_info->need_touch_press_enter)
{
GdkCrossingMode mode;
/* The last device to interact with the window was a touch device,
* which synthesized a leave notify event, so synthesize another enter
* notify to tell the pointer is on the window.
*/
if (gdk_device_get_source (source_device) == GDK_SOURCE_TOUCHSCREEN ||
gdk_device_get_source (source_device) == GDK_SOURCE_TOUCHPAD)
mode = GDK_CROSSING_TOUCH_PRESS;
else
mode = GDK_CROSSING_DEVICE_SWITCH;
pointer_info->need_touch_press_enter = FALSE;
_gdk_synthesize_crossing_events (display,
NULL,
pointer_info->window_under_pointer,
device, source_device, mode,
toplevel_x, toplevel_y,
state, time_, source_event,
serial, FALSE);
}
event = _gdk_make_event (event_win, type, source_event, FALSE);
switch (type)
@@ -9442,17 +9705,85 @@ proxy_button_event (GdkEvent *source_event,
convert_toplevel_coords_to_window (event_win,
toplevel_x, toplevel_y,
&event->button.x, &event->button.y);
event->button.x_root = source_event->button.x_root;
event->button.y_root = source_event->button.y_root;
gdk_event_get_root_coords (source_event,
&event->button.x_root,
&event->button.y_root);
gdk_event_set_device (event, gdk_event_get_device (source_event));
gdk_event_set_source_device (event, source_device);
event->button.state = state;
event->button.device = source_event->button.device;
event->button.axes = g_memdup (source_event->button.axes,
sizeof (gdouble) * gdk_device_get_n_axes (source_event->button.device));
if (type == GDK_BUTTON_RELEASE)
event->button.state |= GDK_BUTTON1_MASK;
if (is_touch_type (source_event->type))
{
event->button.button = 1;
event->button.axes = g_memdup (source_event->touch.axes,
sizeof (gdouble) * gdk_device_get_n_axes (source_event->touch.device));
}
else
{
event->button.button = source_event->button.button;
event->button.axes = g_memdup (source_event->button.axes,
sizeof (gdouble) * gdk_device_get_n_axes (source_event->button.device));
}
if (type == GDK_BUTTON_PRESS)
_gdk_event_button_generate (display, event);
else if (type == GDK_BUTTON_RELEASE &&
pointer_window == pointer_info->window_under_pointer &&
(gdk_device_get_source (source_device) == GDK_SOURCE_TOUCHSCREEN ||
gdk_device_get_source (source_device) == GDK_SOURCE_TOUCHPAD))
{
/* Synthesize a leave notify event
* whenever a touch device is released
*/
pointer_info->need_touch_press_enter = TRUE;
_gdk_synthesize_crossing_events (display,
pointer_window, NULL,
device, source_device,
GDK_CROSSING_TOUCH_RELEASE,
toplevel_x, toplevel_y,
state, time_, NULL,
serial, FALSE);
}
return TRUE;
case GDK_TOUCH_BEGIN:
case GDK_TOUCH_END:
convert_toplevel_coords_to_window (event_win,
toplevel_x, toplevel_y,
&event->button.x, &event->button.y);
gdk_event_get_root_coords (source_event,
&event->touch.x_root,
&event->touch.y_root);
event->touch.state = state;
event->touch.device = source_event->touch.device;
event->touch.axes = g_memdup (source_event->touch.axes,
sizeof (gdouble) * gdk_device_get_n_axes (source_event->touch.device));
event->touch.sequence = source_event->touch.sequence;
gdk_event_set_source_device (event, source_device);
if (type == GDK_BUTTON_PRESS)
_gdk_event_button_generate (display, event);
if ((type == GDK_TOUCH_END &&
_gdk_event_get_pointer_emulated (source_event)) &&
pointer_window == pointer_info->window_under_pointer &&
(gdk_device_get_source (source_device) == GDK_SOURCE_TOUCHSCREEN ||
gdk_device_get_source (source_device) == GDK_SOURCE_TOUCHPAD))
{
/* Synthesize a leave notify event
* whenever a touch device is released
*/
pointer_info->need_touch_press_enter = TRUE;
_gdk_synthesize_crossing_events (display,
pointer_window, NULL,
device, source_device,
GDK_CROSSING_TOUCH_RELEASE,
toplevel_x, toplevel_y,
state, time_, NULL,
serial, FALSE);
}
return TRUE;
case GDK_SCROLL:
@@ -9682,7 +10013,9 @@ _gdk_windowing_got_event (GdkDisplay *display,
}
}
if (pointer_info)
if (pointer_info &&
(!is_touch_type (event->type) ||
_gdk_event_get_pointer_emulated (event)))
{
guint old_state, old_button;
@@ -9699,6 +10032,9 @@ _gdk_windowing_got_event (GdkDisplay *display,
if (event->type == GDK_BUTTON_PRESS ||
event->type == GDK_BUTTON_RELEASE)
pointer_info->button = event->button.button;
else if (event->type == GDK_TOUCH_BEGIN ||
event->type == GDK_TOUCH_END)
pointer_info->button = 1;
if (device &&
(pointer_info->state != old_state ||
@@ -9712,16 +10048,32 @@ _gdk_windowing_got_event (GdkDisplay *display,
else if (is_button_type (event->type))
unlink_event = proxy_button_event (event, serial);
if (event->type == GDK_BUTTON_RELEASE && !event->any.send_event)
if ((event->type == GDK_BUTTON_RELEASE ||
event->type == GDK_TOUCH_END) &&
!event->any.send_event)
{
button_release_grab = _gdk_display_has_device_grab (display, device, serial);
if (button_release_grab &&
button_release_grab->implicit &&
(event->button.state & GDK_ANY_BUTTON_MASK & ~(GDK_BUTTON1_MASK << (event->button.button - 1))) == 0)
GdkEventSequence *sequence;
sequence = gdk_event_get_event_sequence (event);
if (event->type == GDK_TOUCH_END && sequence)
{
button_release_grab->serial_end = serial;
button_release_grab->implicit_ungrab = FALSE;
_gdk_display_device_grab_update (display, device, source_device, serial);
_gdk_display_end_touch_grab (display, device, sequence);
}
if (event->type == GDK_BUTTON_RELEASE ||
_gdk_event_get_pointer_emulated (event))
{
button_release_grab =
_gdk_display_has_device_grab (display, device, serial);
if (button_release_grab &&
button_release_grab->implicit &&
(event->button.state & GDK_ANY_BUTTON_MASK & ~(GDK_BUTTON1_MASK << (event->button.button - 1))) == 0)
{
button_release_grab->serial_end = serial;
button_release_grab->implicit_ungrab = FALSE;
_gdk_display_device_grab_update (display, device, source_device, serial);
}
}
}

View File

@@ -7,6 +7,8 @@ libgdkx11includedir = $(includedir)/gtk-3.0/gdk/x11
AM_CPPFLAGS = \
-DG_LOG_DOMAIN=\"Gdk\" \
-DGDK_COMPILATION \
-DXINPUT2_2_USE_UNSTABLE_PROTOCOL \
-DXINPUT2_1_USE_UNSTABLE_PROTOCOL \
-I$(top_srcdir) \
-I$(top_srcdir)/gdk \
-I$(top_builddir)/gdk \

View File

@@ -388,6 +388,7 @@ gdk_x11_device_xi2_grab (GdkDevice *device,
guint32 time_)
{
GdkX11DeviceXI2 *device_xi2 = GDK_X11_DEVICE_XI2 (device);
GdkX11DeviceManagerXI2 *device_manager_xi2;
GdkDisplay *display;
XIEventMask mask;
Window xwindow;
@@ -395,6 +396,7 @@ gdk_x11_device_xi2_grab (GdkDevice *device,
gint status;
display = gdk_device_get_display (device);
device_manager_xi2 = GDK_X11_DEVICE_MANAGER_XI2 (gdk_display_get_device_manager (display));
/* FIXME: confine_to is actually unused */
@@ -409,7 +411,9 @@ gdk_x11_device_xi2_grab (GdkDevice *device,
}
mask.deviceid = device_xi2->device_id;
mask.mask = _gdk_x11_device_xi2_translate_event_mask (event_mask, &mask.mask_len);
mask.mask = _gdk_x11_device_xi2_translate_event_mask (device_manager_xi2,
event_mask,
&mask.mask_len);
#ifdef G_ENABLE_DEBUG
if (_gdk_debug_flags & GDK_DEBUG_NOGRABS)
@@ -625,10 +629,17 @@ gdk_x11_device_xi2_select_window_events (GdkDevice *device,
GdkEventMask event_mask)
{
GdkX11DeviceXI2 *device_xi2 = GDK_X11_DEVICE_XI2 (device);
GdkX11DeviceManagerXI2 *device_manager_xi2;
GdkDisplay *display;
XIEventMask evmask;
display = gdk_device_get_display (device);
device_manager_xi2 = GDK_X11_DEVICE_MANAGER_XI2 (gdk_display_get_device_manager (display));
evmask.deviceid = device_xi2->device_id;
evmask.mask = _gdk_x11_device_xi2_translate_event_mask (event_mask, &evmask.mask_len);
evmask.mask = _gdk_x11_device_xi2_translate_event_mask (device_manager_xi2,
event_mask,
&evmask.mask_len);
XISelectEvents (GDK_WINDOW_XDISPLAY (window),
GDK_WINDOW_XID (window),
@@ -638,10 +649,14 @@ gdk_x11_device_xi2_select_window_events (GdkDevice *device,
}
guchar *
_gdk_x11_device_xi2_translate_event_mask (GdkEventMask event_mask,
gint *len)
_gdk_x11_device_xi2_translate_event_mask (GdkX11DeviceManagerXI2 *device_manager_xi2,
GdkEventMask event_mask,
gint *len)
{
guchar *mask;
gint minor;
g_object_get (device_manager_xi2, "minor", &minor, NULL);
*len = XIMaskLen (XI_LASTEVENT);
mask = g_new0 (guchar, *len);
@@ -690,6 +705,17 @@ _gdk_x11_device_xi2_translate_event_mask (GdkEventMask event_mask,
XISetMask (mask, XI_FocusOut);
}
#ifdef XINPUT_2_2
/* XInput 2.2 includes multitouch support */
if (minor >= 2 &&
event_mask & GDK_TOUCH_MASK)
{
XISetMask (mask, XI_TouchBegin);
XISetMask (mask, XI_TouchUpdate);
XISetMask (mask, XI_TouchEnd);
}
#endif /* XINPUT_2_2 */
return mask;
}

View File

@@ -54,7 +54,11 @@ _gdk_x11_device_manager_new (GdkDisplay *display)
int major, minor;
major = 2;
#ifdef XINPUT_2_2
minor = 2;
#else
minor = 0;
#endif /* XINPUT_2_2 */
if (!_gdk_disable_multidevice &&
XIQueryVersion (xdisplay, &major, &minor) != BadRequest)
@@ -66,6 +70,8 @@ _gdk_x11_device_manager_new (GdkDisplay *display)
device_manager_xi2 = g_object_new (GDK_TYPE_X11_DEVICE_MANAGER_XI2,
"display", display,
"opcode", opcode,
"major", major,
"minor", minor,
NULL);
return GDK_DEVICE_MANAGER (device_manager_xi2);

View File

@@ -29,6 +29,7 @@
#include "gdkprivate-x11.h"
#include "gdkintl.h"
#include "gdkkeysyms.h"
#include "gdkinternals.h"
#ifdef XINPUT_2
@@ -49,6 +50,8 @@ struct _GdkX11DeviceManagerXI2
GList *devices;
gint opcode;
gint major;
gint minor;
};
struct _GdkX11DeviceManagerXI2Class
@@ -96,7 +99,9 @@ static GdkWindow * gdk_x11_device_manager_xi2_get_window (GdkEventTra
enum {
PROP_0,
PROP_OPCODE
PROP_OPCODE,
PROP_MAJOR,
PROP_MINOR
};
static void
@@ -120,6 +125,20 @@ gdk_x11_device_manager_xi2_class_init (GdkX11DeviceManagerXI2Class *klass)
P_("Opcode for XInput2 requests"),
0, G_MAXINT, 0,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
g_object_class_install_property (object_class,
PROP_MAJOR,
g_param_spec_int ("major",
P_("Major"),
P_("Major version number"),
0, G_MAXINT, 0,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
g_object_class_install_property (object_class,
PROP_MINOR,
g_param_spec_int ("minor",
P_("Minor"),
P_("Minor version number"),
0, G_MAXINT, 0,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
}
static void
@@ -148,8 +167,10 @@ _gdk_x11_device_manager_xi2_select_events (GdkDeviceManager *device_manager,
static void
translate_valuator_class (GdkDisplay *display,
GdkDevice *device,
XIValuatorClassInfo *info,
gint n_valuator)
Atom valuator_label,
gdouble min,
gdouble max,
gdouble resolution)
{
static gboolean initialized = FALSE;
static Atom label_atoms [GDK_AXIS_LAST] = { 0 };
@@ -170,24 +191,19 @@ translate_valuator_class (GdkDisplay *display,
for (i = GDK_AXIS_IGNORE; i < GDK_AXIS_LAST; i++)
{
if (label_atoms[i] == info->label)
if (label_atoms[i] == valuator_label)
{
use = i;
break;
}
}
if (info->label != None)
label = gdk_x11_xatom_to_atom_for_display (display, info->label);
if (valuator_label != None)
label = gdk_x11_xatom_to_atom_for_display (display, valuator_label);
else
label = GDK_NONE;
_gdk_device_add_axis (device,
label,
use,
info->min,
info->max,
info->resolution);
_gdk_device_add_axis (device, label, use, min, max, resolution);
}
static void
@@ -196,7 +212,7 @@ translate_device_classes (GdkDisplay *display,
XIAnyClassInfo **classes,
guint n_classes)
{
gint i, n_valuator = 0;
gint i;
g_object_freeze_notify (G_OBJECT (device));
@@ -218,10 +234,14 @@ translate_device_classes (GdkDisplay *display,
}
break;
case XIValuatorClass:
translate_valuator_class (display, device,
(XIValuatorClassInfo *) class_info,
n_valuator);
n_valuator++;
{
XIValuatorClassInfo *valuator_info = (XIValuatorClassInfo *) class_info;
translate_valuator_class (display, device,
valuator_info->label,
valuator_info->min,
valuator_info->max,
valuator_info->resolution);
}
break;
default:
/* Ignore */
@@ -232,18 +252,58 @@ translate_device_classes (GdkDisplay *display,
g_object_thaw_notify (G_OBJECT (device));
}
static gboolean
is_touch_device (XIAnyClassInfo **classes,
guint n_classes,
GdkInputSource *device_type,
gint *num_touches)
{
#ifdef XINPUT_2_2
guint i;
for (i = 0; i < n_classes; i++)
{
XITouchClassInfo *class = (XITouchClassInfo *) classes[i];
if (class->type != XITouchClass)
continue;
if (class->num_touches > 0)
{
if (class->mode == XIDirectTouch)
*device_type = GDK_SOURCE_TOUCHSCREEN;
else if (class->mode == XIDependentTouch)
*device_type = GDK_SOURCE_TOUCHPAD;
else
continue;
*num_touches = class->num_touches;
return TRUE;
}
}
#endif
return FALSE;
}
static GdkDevice *
create_device (GdkDeviceManager *device_manager,
GdkDisplay *display,
XIDeviceInfo *dev)
{
GdkInputSource input_source;
GdkInputSource touch_source;
GdkDeviceType type;
GdkDevice *device;
GdkInputMode mode;
gint num_touches = 0;
if (dev->use == XIMasterKeyboard || dev->use == XISlaveKeyboard)
input_source = GDK_SOURCE_KEYBOARD;
else if (dev->use == XISlavePointer &&
is_touch_device (dev->classes, dev->num_classes, &touch_source, &num_touches))
input_source = touch_source;
else
{
gchar *tmp_name;
@@ -254,6 +314,10 @@ create_device (GdkDeviceManager *device_manager,
input_source = GDK_SOURCE_ERASER;
else if (strstr (tmp_name, "cursor"))
input_source = GDK_SOURCE_CURSOR;
else if (strstr (tmp_name, "finger") ||
(strstr (tmp_name, "touch") &&
!strstr (tmp_name, "touchpad")))
input_source = GDK_SOURCE_TOUCHSCREEN;
else if (strstr (tmp_name, "wacom") ||
strstr (tmp_name, "pen"))
input_source = GDK_SOURCE_PEN;
@@ -282,6 +346,20 @@ create_device (GdkDeviceManager *device_manager,
break;
}
GDK_NOTE (INPUT,
({
const gchar *type_names[] = { "master", "slave", "floating" };
const gchar *source_names[] = { "mouse", "pen", "eraser", "cursor", "keyboard", "direct touch", "indirect touch" };
const gchar *mode_names[] = { "disabled", "screen", "window" };
g_message ("input device:\n\tname: %s\n\ttype: %s\n\tsource: %s\n\tmode: %s\n\thas cursor: %d\n\ttouches: %d",
dev->name,
type_names[type],
source_names[input_source],
mode_names[mode],
dev->use == XIMasterPointer,
num_touches);
}));
device = g_object_new (GDK_TYPE_X11_DEVICE_XI2,
"name", dev->name,
"type", type,
@@ -408,6 +486,8 @@ gdk_x11_device_manager_xi2_constructed (GObject *object)
display = gdk_device_manager_get_display (GDK_DEVICE_MANAGER (object));
xdisplay = GDK_DISPLAY_XDISPLAY (display);
g_assert (device_manager->major == 2);
masters = g_hash_table_new (NULL, NULL);
slaves = g_hash_table_new (NULL, NULL);
@@ -533,6 +613,12 @@ gdk_x11_device_manager_xi2_set_property (GObject *object,
case PROP_OPCODE:
device_manager->opcode = g_value_get_int (value);
break;
case PROP_MAJOR:
device_manager->major = g_value_get_int (value);
break;
case PROP_MINOR:
device_manager->minor = g_value_get_int (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -554,6 +640,12 @@ gdk_x11_device_manager_xi2_get_property (GObject *object,
case PROP_OPCODE:
g_value_set_int (value, device_manager->opcode);
break;
case PROP_MAJOR:
g_value_set_int (value, device_manager->major);
break;
case PROP_MINOR:
g_value_set_int (value, device_manager->minor);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -823,6 +915,11 @@ get_event_window (GdkEventTranslator *translator,
case XI_ButtonPress:
case XI_ButtonRelease:
case XI_Motion:
#ifdef XINPUT_2_2
case XI_TouchUpdate:
case XI_TouchBegin:
case XI_TouchEnd:
#endif /* XINPUT_2_2 */
{
XIDeviceEvent *xev = (XIDeviceEvent *) ev;
@@ -1047,51 +1144,39 @@ gdk_x11_device_manager_xi2_translate_event (GdkEventTranslator *translator,
XIDeviceEvent *xev = (XIDeviceEvent *) ev;
GdkDevice *source_device;
switch (xev->detail)
if (ev->evtype == XI_ButtonPress &&
(xev->detail >= 4 && xev->detail <= 7))
{
case 4:
case 5:
case 6:
case 7:
/* Button presses of button 4-7 are scroll events */
if (ev->evtype == XI_ButtonPress)
{
event->scroll.type = GDK_SCROLL;
/* Button presses of button 4-7 are scroll events */
event->scroll.type = GDK_SCROLL;
if (xev->detail == 4)
event->scroll.direction = GDK_SCROLL_UP;
else if (xev->detail == 5)
event->scroll.direction = GDK_SCROLL_DOWN;
else if (xev->detail == 6)
event->scroll.direction = GDK_SCROLL_LEFT;
else
event->scroll.direction = GDK_SCROLL_RIGHT;
if (xev->detail == 4)
event->scroll.direction = GDK_SCROLL_UP;
else if (xev->detail == 5)
event->scroll.direction = GDK_SCROLL_DOWN;
else if (xev->detail == 6)
event->scroll.direction = GDK_SCROLL_LEFT;
else
event->scroll.direction = GDK_SCROLL_RIGHT;
event->scroll.window = window;
event->scroll.time = xev->time;
event->scroll.x = (gdouble) xev->event_x;
event->scroll.y = (gdouble) xev->event_y;
event->scroll.x_root = (gdouble) xev->root_x;
event->scroll.y_root = (gdouble) xev->root_y;
event->scroll.window = window;
event->scroll.time = xev->time;
event->scroll.x = (gdouble) xev->event_x;
event->scroll.y = (gdouble) xev->event_y;
event->scroll.x_root = (gdouble) xev->root_x;
event->scroll.y_root = (gdouble) xev->root_y;
event->scroll.device = g_hash_table_lookup (device_manager->id_table,
GUINT_TO_POINTER (xev->deviceid));
event->scroll.device = g_hash_table_lookup (device_manager->id_table,
GUINT_TO_POINTER (xev->deviceid));
source_device = g_hash_table_lookup (device_manager->id_table,
GUINT_TO_POINTER (xev->sourceid));
gdk_event_set_source_device (event, source_device);
source_device = g_hash_table_lookup (device_manager->id_table,
GUINT_TO_POINTER (xev->sourceid));
gdk_event_set_source_device (event, source_device);
event->scroll.state = _gdk_x11_device_xi2_translate_state (&xev->mods, &xev->buttons, &xev->group);
break;
}
/* Button presses of button 4-7 are scroll events, so ignore the release */
else if (ev->evtype == XI_ButtonRelease)
{
return_val = FALSE;
break;
}
/* else (XI_ButtonRelease) fall thru */
default:
event->scroll.state = _gdk_x11_device_xi2_translate_state (&xev->mods, &xev->buttons, &xev->group);
}
else
{
event->button.type = (ev->evtype == XI_ButtonPress) ? GDK_BUTTON_PRESS : GDK_BUTTON_RELEASE;
event->button.window = window;
@@ -1124,9 +1209,13 @@ gdk_x11_device_manager_xi2_translate_event (GdkEventTranslator *translator,
}
event->button.state = _gdk_x11_device_xi2_translate_state (&xev->mods, &xev->buttons, &xev->group);
event->button.button = xev->detail;
}
if (xev->flags & XIPointerEmulated)
_gdk_event_set_pointer_emulated (event, TRUE);
if (return_val == FALSE)
break;
@@ -1141,15 +1230,14 @@ gdk_x11_device_manager_xi2_translate_event (GdkEventTranslator *translator,
break;
}
case XI_Motion:
{
XIDeviceEvent *xev = (XIDeviceEvent *) ev;
GdkDevice *source_device;
event->motion.type = GDK_MOTION_NOTIFY;
event->motion.window = window;
event->motion.time = xev->time;
event->motion.x = (gdouble) xev->event_x;
event->motion.y = (gdouble) xev->event_y;
@@ -1165,6 +1253,9 @@ gdk_x11_device_manager_xi2_translate_event (GdkEventTranslator *translator,
event->motion.state = _gdk_x11_device_xi2_translate_state (&xev->mods, &xev->buttons, &xev->group);
if (xev->flags & XIPointerEmulated)
_gdk_event_set_pointer_emulated (event, TRUE);
/* There doesn't seem to be motion hints in XI */
event->motion.is_hint = FALSE;
@@ -1184,6 +1275,137 @@ gdk_x11_device_manager_xi2_translate_event (GdkEventTranslator *translator,
}
}
break;
#ifdef XINPUT_2_2
case XI_TouchBegin:
case XI_TouchEnd:
{
XIDeviceEvent *xev = (XIDeviceEvent *) ev;
GdkDevice *source_device;
GDK_NOTE(EVENTS,
g_message ("touch %s:\twindow %ld\n\ttouch id: %u\n\tpointer emulating: %d",
ev->evtype == XI_TouchBegin ? "begin" : "end",
xev->event,
xev->detail,
xev->flags & XITouchEmulatingPointer));
if (ev->evtype == XI_TouchBegin)
event->touch.type = GDK_TOUCH_BEGIN;
else if (ev->evtype == XI_TouchEnd)
event->touch.type = GDK_TOUCH_END;
event->touch.window = window;
event->touch.time = xev->time;
event->touch.x = (gdouble) xev->event_x;
event->touch.y = (gdouble) xev->event_y;
event->touch.x_root = (gdouble) xev->root_x;
event->touch.y_root = (gdouble) xev->root_y;
event->touch.device = g_hash_table_lookup (device_manager->id_table,
GUINT_TO_POINTER (xev->deviceid));
source_device = g_hash_table_lookup (device_manager->id_table,
GUINT_TO_POINTER (xev->sourceid));
gdk_event_set_source_device (event, source_device);
event->touch.axes = translate_axes (event->touch.device,
event->touch.x,
event->touch.y,
event->touch.window,
&xev->valuators);
if (gdk_device_get_mode (event->touch.device) == GDK_MODE_WINDOW)
{
GdkDevice *device = event->touch.device;
/* Update event coordinates from axes */
gdk_device_get_axis (device, event->touch.axes, GDK_AXIS_X, &event->touch.x);
gdk_device_get_axis (device, event->touch.axes, GDK_AXIS_Y, &event->touch.y);
}
event->touch.state = _gdk_x11_device_xi2_translate_state (&xev->mods, &xev->buttons, &xev->group);
if (ev->evtype == XI_TouchBegin)
event->touch.state |= GDK_BUTTON1_MASK;
event->touch.sequence = GUINT_TO_POINTER (xev->detail);
if (xev->flags & XITouchEmulatingPointer)
{
event->touch.emulating_pointer = TRUE;
_gdk_event_set_pointer_emulated (event, TRUE);
}
if (return_val == FALSE)
break;
if (!set_screen_from_root (display, event, xev->root))
{
return_val = FALSE;
break;
}
if (ev->evtype == XI_TouchBegin)
set_user_time (event);
}
break;
case XI_TouchUpdate:
{
XIDeviceEvent *xev = (XIDeviceEvent *) ev;
GdkDevice *source_device;
GDK_NOTE(EVENTS,
g_message ("touch update:\twindow %ld\n\ttouch id: %u\n\tpointer emulating: %d",
xev->event,
xev->detail,
xev->flags & XITouchEmulatingPointer));
event->touch.window = window;
event->touch.sequence = GUINT_TO_POINTER (xev->detail);
event->touch.type = GDK_TOUCH_UPDATE;
event->touch.time = xev->time;
event->touch.x = (gdouble) xev->event_x;
event->touch.y = (gdouble) xev->event_y;
event->touch.x_root = (gdouble) xev->root_x;
event->touch.y_root = (gdouble) xev->root_y;
event->touch.device = g_hash_table_lookup (device_manager->id_table,
GINT_TO_POINTER (xev->deviceid));
source_device = g_hash_table_lookup (device_manager->id_table,
GUINT_TO_POINTER (xev->sourceid));
gdk_event_set_source_device (event, source_device);
event->touch.state = _gdk_x11_device_xi2_translate_state (&xev->mods, &xev->buttons, &xev->group);
event->touch.state |= GDK_BUTTON1_MASK;
if (xev->flags & XITouchEmulatingPointer)
{
event->touch.emulating_pointer = TRUE;
_gdk_event_set_pointer_emulated (event, TRUE);
}
event->touch.axes = translate_axes (event->touch.device,
event->touch.x,
event->touch.y,
event->touch.window,
&xev->valuators);
if (gdk_device_get_mode (event->touch.device) == GDK_MODE_WINDOW)
{
GdkDevice *device = event->touch.device;
/* Update event coordinates from axes */
gdk_device_get_axis (device, event->touch.axes, GDK_AXIS_X, &event->touch.x);
gdk_device_get_axis (device, event->touch.axes, GDK_AXIS_Y, &event->touch.y);
}
}
break;
#endif
case XI_Enter:
case XI_Leave:
{
@@ -1280,7 +1502,8 @@ gdk_x11_device_manager_xi2_get_handled_events (GdkEventTranslator *translator)
GDK_BUTTON2_MOTION_MASK |
GDK_BUTTON3_MOTION_MASK |
GDK_BUTTON_MOTION_MASK |
GDK_FOCUS_CHANGE_MASK);
GDK_FOCUS_CHANGE_MASK |
GDK_TOUCH_MASK);
}
static void
@@ -1294,7 +1517,9 @@ gdk_x11_device_manager_xi2_select_window_events (GdkEventTranslator *translator,
device_manager = GDK_DEVICE_MANAGER (translator);
event_mask.deviceid = XIAllMasterDevices;
event_mask.mask = _gdk_x11_device_xi2_translate_event_mask (evmask, &event_mask.mask_len);
event_mask.mask = _gdk_x11_device_xi2_translate_event_mask (GDK_X11_DEVICE_MANAGER_XI2 (device_manager),
evmask,
&event_mask.mask_len);
_gdk_x11_device_manager_xi2_select_events (device_manager, window, &event_mask);
g_free (event_mask.mask);

View File

@@ -247,8 +247,9 @@ void _gdk_x11_device_xi_translate_axes (GdkDevice *device,
#endif
#ifdef XINPUT_2
guchar * _gdk_x11_device_xi2_translate_event_mask (GdkEventMask event_mask,
gint *len);
guchar * _gdk_x11_device_xi2_translate_event_mask (GdkX11DeviceManagerXI2 *device_manager_xi2,
GdkEventMask event_mask,
gint *len);
guint _gdk_x11_device_xi2_translate_state (XIModifierState *mods_state,
XIButtonState *buttons_state,
XIGroupState *group_state);

View File

@@ -2367,6 +2367,8 @@ gtk_scrollbar_new
gtk_scrolled_window_add_with_viewport
gtk_scrolled_window_get_hadjustment
gtk_scrolled_window_get_hscrollbar
gtk_scrolled_window_get_kinetic_scrolling
gtk_scrolled_window_get_capture_button_press
gtk_scrolled_window_get_min_content_height
gtk_scrolled_window_get_min_content_width
gtk_scrolled_window_get_placement
@@ -2377,6 +2379,8 @@ gtk_scrolled_window_get_vadjustment
gtk_scrolled_window_get_vscrollbar
gtk_scrolled_window_new
gtk_scrolled_window_set_hadjustment
gtk_scrolled_window_set_kinetic_scrolling
gtk_scrolled_window_set_capture_button_press
gtk_scrolled_window_set_min_content_height
gtk_scrolled_window_set_min_content_width
gtk_scrolled_window_set_placement

View File

@@ -124,6 +124,8 @@ static gint gtk_button_button_press (GtkWidget * widget,
GdkEventButton * event);
static gint gtk_button_button_release (GtkWidget * widget,
GdkEventButton * event);
static gboolean gtk_button_touch (GtkWidget *widget,
GdkEventTouch *event);
static gint gtk_button_grab_broken (GtkWidget * widget,
GdkEventGrabBroken * event);
static gint gtk_button_key_release (GtkWidget * widget, GdkEventKey * event);
@@ -209,6 +211,7 @@ gtk_button_class_init (GtkButtonClass *klass)
widget_class->draw = gtk_button_draw;
widget_class->button_press_event = gtk_button_button_press;
widget_class->button_release_event = gtk_button_button_release;
widget_class->touch_event = gtk_button_touch;
widget_class->grab_broken_event = gtk_button_grab_broken;
widget_class->key_release_event = gtk_button_key_release;
widget_class->enter_notify_event = gtk_button_enter_notify;
@@ -1431,9 +1434,10 @@ gtk_button_realize (GtkWidget *widget)
attributes.wclass = GDK_INPUT_ONLY;
attributes.event_mask = gtk_widget_get_events (widget);
attributes.event_mask |= (GDK_BUTTON_PRESS_MASK |
GDK_BUTTON_RELEASE_MASK |
GDK_ENTER_NOTIFY_MASK |
GDK_LEAVE_NOTIFY_MASK);
GDK_BUTTON_RELEASE_MASK |
GDK_TOUCH_MASK |
GDK_ENTER_NOTIFY_MASK |
GDK_LEAVE_NOTIFY_MASK);
attributes_mask = GDK_WA_X | GDK_WA_Y;
@@ -1843,6 +1847,28 @@ gtk_button_button_release (GtkWidget *widget,
return TRUE;
}
static gboolean
gtk_button_touch (GtkWidget *widget,
GdkEventTouch *event)
{
GtkButton *button = GTK_BUTTON (widget);
GtkButtonPrivate *priv = button->priv;
if (event->type == GDK_TOUCH_BEGIN)
{
if (priv->focus_on_click && !gtk_widget_has_focus (widget))
gtk_widget_grab_focus (widget);
g_signal_emit (button, button_signals[PRESSED], 0);
}
else if (event->type == GDK_TOUCH_END)
{
g_signal_emit (button, button_signals[RELEASED], 0);
}
return TRUE;
}
static gboolean
gtk_button_grab_broken (GtkWidget *widget,
GdkEventGrabBroken *event)
@@ -1932,6 +1958,40 @@ gtk_real_button_pressed (GtkButton *button)
gtk_button_update_state (button);
}
static gboolean
touch_release_in_button (GtkButton *button)
{
GtkButtonPrivate *priv;
gint width, height;
GdkEvent *event;
gdouble x, y;
priv = button->priv;
event = gtk_get_current_event ();
if (!event)
return FALSE;
if (event->type != GDK_TOUCH_END ||
event->touch.window != priv->event_window)
{
gdk_event_free (event);
return FALSE;
}
gdk_event_get_coords (event, &x, &y);
width = gdk_window_get_width (priv->event_window);
height = gdk_window_get_height (priv->event_window);
gdk_event_free (event);
if (x >= 0 && x <= width &&
y >= 0 && y <= height)
return TRUE;
return FALSE;
}
static void
gtk_real_button_released (GtkButton *button)
{
@@ -1944,7 +2004,8 @@ gtk_real_button_released (GtkButton *button)
if (priv->activate_timeout)
return;
if (priv->in_button)
if (priv->in_button ||
touch_release_in_button (button))
gtk_button_clicked (button);
gtk_button_update_state (button);

View File

@@ -1341,6 +1341,14 @@ rewrite_event_for_window (GdkEvent *event,
new_window,
&event->motion.x, &event->motion.y);
break;
case GDK_TOUCH_BEGIN:
case GDK_TOUCH_UPDATE:
case GDK_TOUCH_END:
case GDK_TOUCH_CANCEL:
rewrite_events_translate (event->any.window,
new_window,
&event->touch.x, &event->touch.y);
break;
case GDK_KEY_PRESS:
case GDK_KEY_RELEASE:
case GDK_PROXIMITY_IN:
@@ -1386,6 +1394,10 @@ rewrite_event_for_grabs (GdkEvent *event)
case GDK_PROXIMITY_OUT:
case GDK_KEY_PRESS:
case GDK_KEY_RELEASE:
case GDK_TOUCH_BEGIN:
case GDK_TOUCH_UPDATE:
case GDK_TOUCH_END:
case GDK_TOUCH_CANCEL:
display = gdk_window_get_display (event->any.window);
device = gdk_event_get_device (event);
@@ -1473,6 +1485,7 @@ gtk_main_do_event (GdkEvent *event)
{
GtkWidget *event_widget;
GtkWidget *grab_widget = NULL;
GtkWidget *topmost_widget = NULL;
GtkWindowGroup *window_group;
GdkEvent *rewritten_event = NULL;
GdkDevice *device;
@@ -1532,6 +1545,14 @@ gtk_main_do_event (GdkEvent *event)
if (!grab_widget)
grab_widget = gtk_window_group_get_current_grab (window_group);
/* Find out the topmost widget where captured event propagation
* should start, which is the widget holding the GTK+ grab
* if any, otherwise it's left NULL and events are emitted
* from the toplevel (or topmost parentless parent).
*/
if (grab_widget)
topmost_widget = grab_widget;
/* If the grab widget is an ancestor of the event widget
* then we send the event to the original event widget.
* This is the key to implementing modality.
@@ -1628,14 +1649,17 @@ gtk_main_do_event (GdkEvent *event)
case GDK_WINDOW_STATE:
case GDK_GRAB_BROKEN:
case GDK_DAMAGE:
gtk_widget_event (event_widget, event);
if (!_gtk_widget_captured_event (event_widget, event))
gtk_widget_event (event_widget, event);
break;
case GDK_SCROLL:
case GDK_BUTTON_PRESS:
case GDK_2BUTTON_PRESS:
case GDK_3BUTTON_PRESS:
gtk_propagate_event (grab_widget, event);
case GDK_TOUCH_BEGIN:
if (!_gtk_propagate_captured_event (grab_widget, event, topmost_widget))
gtk_propagate_event (grab_widget, event);
break;
case GDK_KEY_PRESS:
@@ -1684,7 +1708,11 @@ gtk_main_do_event (GdkEvent *event)
case GDK_BUTTON_RELEASE:
case GDK_PROXIMITY_IN:
case GDK_PROXIMITY_OUT:
gtk_propagate_event (grab_widget, event);
case GDK_TOUCH_UPDATE:
case GDK_TOUCH_END:
case GDK_TOUCH_CANCEL:
if (!_gtk_propagate_captured_event (grab_widget, event, topmost_widget))
gtk_propagate_event (grab_widget, event);
break;
case GDK_ENTER_NOTIFY:
@@ -1693,7 +1721,8 @@ gtk_main_do_event (GdkEvent *event)
_gtk_widget_set_device_window (event_widget,
gdk_event_get_device (event),
event->any.window);
if (gtk_widget_is_sensitive (grab_widget))
if (gtk_widget_is_sensitive (grab_widget) &&
!_gtk_propagate_captured_event (grab_widget, event, topmost_widget))
gtk_widget_event (grab_widget, event);
break;
@@ -1703,7 +1732,8 @@ gtk_main_do_event (GdkEvent *event)
_gtk_widget_set_device_window (event_widget,
gdk_event_get_device (event),
NULL);
if (gtk_widget_is_sensitive (grab_widget))
if (gtk_widget_is_sensitive (grab_widget) &&
!_gtk_propagate_captured_event (grab_widget, event, topmost_widget))
gtk_widget_event (grab_widget, event);
break;
@@ -1731,6 +1761,7 @@ gtk_main_do_event (GdkEvent *event)
|| event->type == GDK_DRAG_ENTER
|| event->type == GDK_GRAB_BROKEN
|| event->type == GDK_MOTION_NOTIFY
|| event->type == GDK_TOUCH_UPDATE
|| event->type == GDK_SCROLL)
{
_gtk_tooltip_handle_event (event);
@@ -2333,6 +2364,135 @@ gtk_get_event_widget (GdkEvent *event)
return widget;
}
static gboolean
propagate_event_up (GtkWidget *widget,
GdkEvent *event,
GtkWidget *topmost)
{
gboolean handled_event = FALSE;
/* Propagate event up the widget tree so that
* parents can see the button and motion
* events of the children.
*/
while (TRUE)
{
GtkWidget *tmp;
g_object_ref (widget);
/* Scroll events are special cased here because it
* feels wrong when scrolling a GtkViewport, say,
* to have children of the viewport eat the scroll
* event
*/
if (!gtk_widget_is_sensitive (widget))
handled_event = event->type != GDK_SCROLL;
else
handled_event = gtk_widget_event (widget, event);
tmp = gtk_widget_get_parent (widget);
g_object_unref (widget);
if (widget == topmost)
break;
widget = tmp;
if (handled_event || !widget)
break;
}
return handled_event;
}
static gboolean
propagate_event_down (GtkWidget *widget,
GdkEvent *event,
GtkWidget *topmost)
{
gint handled_event = FALSE;
GList *widgets = NULL;
GList *l;
widgets = g_list_prepend (widgets, g_object_ref (widget));
while (widget && widget != topmost)
{
widget = gtk_widget_get_parent (widget);
if (!widget)
break;
widgets = g_list_prepend (widgets, g_object_ref (widget));
if (widget == topmost)
break;
}
for (l = widgets; l && !handled_event; l = g_list_next (l))
{
widget = (GtkWidget *)l->data;
if (!gtk_widget_is_sensitive (widget))
handled_event = TRUE;
else
handled_event = _gtk_widget_captured_event (widget, event);
}
g_list_free_full (widgets, (GDestroyNotify)g_object_unref);
return handled_event;
}
static gboolean
propagate_event (GtkWidget *widget,
GdkEvent *event,
gboolean captured,
GtkWidget *topmost)
{
gboolean handled_event = FALSE;
gboolean (* propagate_func) (GtkWidget *widget, GdkEvent *event);
propagate_func = captured ? _gtk_widget_captured_event : gtk_widget_event;
if (event->type == GDK_KEY_PRESS || event->type == GDK_KEY_RELEASE)
{
/* Only send key events within Window widgets to the Window
* The Window widget will in turn pass the
* key event on to the currently focused widget
* for that window.
*/
GtkWidget *window;
window = gtk_widget_get_toplevel (widget);
if (GTK_IS_WINDOW (window))
{
g_object_ref (widget);
/* If there is a grab within the window, give the grab widget
* a first crack at the key event
*/
if (widget != window && gtk_widget_has_grab (widget))
handled_event = propagate_func (widget, event);
if (!handled_event)
{
window = gtk_widget_get_toplevel (widget);
if (GTK_IS_WINDOW (window))
{
if (gtk_widget_is_sensitive (window))
handled_event = propagate_func (window, event);
}
}
g_object_unref (widget);
return handled_event;
}
}
/* Other events get propagated up/down the widget tree */
return captured ?
propagate_event_down (widget, event, topmost) :
propagate_event_up (widget, event, topmost);
}
/**
* gtk_propagate_event:
* @widget: a #GtkWidget
@@ -2361,79 +2521,16 @@ void
gtk_propagate_event (GtkWidget *widget,
GdkEvent *event)
{
gint handled_event;
g_return_if_fail (GTK_IS_WIDGET (widget));
g_return_if_fail (event != NULL);
handled_event = FALSE;
g_object_ref (widget);
if ((event->type == GDK_KEY_PRESS) ||
(event->type == GDK_KEY_RELEASE))
{
/* Only send key events within Window widgets to the Window
* The Window widget will in turn pass the
* key event on to the currently focused widget
* for that window.
*/
GtkWidget *window;
window = gtk_widget_get_toplevel (widget);
if (GTK_IS_WINDOW (window))
{
/* If there is a grab within the window, give the grab widget
* a first crack at the key event
*/
if (widget != window && gtk_widget_has_grab (widget))
handled_event = gtk_widget_event (widget, event);
if (!handled_event)
{
window = gtk_widget_get_toplevel (widget);
if (GTK_IS_WINDOW (window))
{
if (gtk_widget_is_sensitive (window))
gtk_widget_event (window, event);
}
}
handled_event = TRUE; /* don't send to widget */
}
}
/* Other events get propagated up the widget tree
* so that parents can see the button and motion
* events of the children.
*/
if (!handled_event)
{
while (TRUE)
{
GtkWidget *tmp;
/* Scroll events are special cased here because it
* feels wrong when scrolling a GtkViewport, say,
* to have children of the viewport eat the scroll
* event
*/
if (!gtk_widget_is_sensitive (widget))
handled_event = event->type != GDK_SCROLL;
else
handled_event = gtk_widget_event (widget, event);
tmp = gtk_widget_get_parent (widget);
g_object_unref (widget);
widget = tmp;
if (!handled_event && widget)
g_object_ref (widget);
else
break;
}
}
else
g_object_unref (widget);
propagate_event (widget, event, FALSE, NULL);
}
gboolean
_gtk_propagate_captured_event (GtkWidget *widget,
GdkEvent *event,
GtkWidget *topmost)
{
return propagate_event (widget, event, TRUE, topmost);
}

View File

@@ -110,8 +110,10 @@
#include "gtksettings.h"
#include "gtkprivate.h"
#include "gtkwidgetprivate.h"
#include "gtkdnd.h"
#include "gtkintl.h"
#include "gtktypebuiltins.h"
#include "gtkwidgetprivate.h"
#include "deprecated/gtktearoffmenuitem.h"
@@ -227,12 +229,13 @@ static void gtk_menu_scroll_to (GtkMenu *menu,
gint offset);
static void gtk_menu_grab_notify (GtkWidget *widget,
gboolean was_grabbed);
static gboolean gtk_menu_captured_event (GtkWidget *widget,
GdkEvent *event);
static void gtk_menu_stop_scrolling (GtkMenu *menu);
static void gtk_menu_remove_scroll_timeout (GtkMenu *menu);
static gboolean gtk_menu_scroll_timeout (gpointer data);
static gboolean gtk_menu_scroll_timeout_initial (gpointer data);
static void gtk_menu_start_scrolling (GtkMenu *menu);
static void gtk_menu_scroll_item_visible (GtkMenuShell *menu_shell,
GtkWidget *menu_item);
@@ -1057,9 +1060,12 @@ gtk_menu_init (GtkMenu *menu)
priv->needs_destruction_ref = TRUE;
priv->monitor_num = -1;
priv->drag_start_y = -1;
context = gtk_widget_get_style_context (GTK_WIDGET (menu));
gtk_style_context_add_class (context, GTK_STYLE_CLASS_MENU);
_gtk_widget_set_captured_event_handler (GTK_WIDGET (menu), gtk_menu_captured_event);
}
static void
@@ -1465,7 +1471,7 @@ gtk_menu_popup_for_device (GtkMenu *menu,
GtkMenuShell *menu_shell;
gboolean grab_keyboard;
GtkWidget *parent_toplevel;
GdkDevice *keyboard, *pointer;
GdkDevice *keyboard, *pointer, *source_device = NULL;
g_return_if_fail (GTK_IS_MENU (menu));
g_return_if_fail (device == NULL || GDK_IS_DEVICE (device));
@@ -1602,6 +1608,7 @@ gtk_menu_popup_for_device (GtkMenu *menu,
(current_event->type != GDK_ENTER_NOTIFY))
menu_shell->priv->ignore_enter = TRUE;
source_device = gdk_event_get_source_device (current_event);
gdk_event_free (current_event);
}
else
@@ -1671,17 +1678,9 @@ gtk_menu_popup_for_device (GtkMenu *menu,
gtk_menu_scroll_to (menu, priv->scroll_offset);
/* if no item is selected, select the first one */
if (!menu_shell->priv->active_menu_item)
{
gboolean touchscreen_mode;
g_object_get (gtk_widget_get_settings (GTK_WIDGET (menu)),
"gtk-touchscreen-mode", &touchscreen_mode,
NULL);
if (touchscreen_mode)
gtk_menu_shell_select_first (menu_shell, TRUE);
}
if (!menu_shell->priv->active_menu_item &&
source_device && gdk_device_get_source (source_device) == GDK_SOURCE_TOUCHSCREEN)
gtk_menu_shell_select_first (menu_shell, TRUE);
/* Once everything is set up correctly, map the toplevel */
gtk_widget_show (priv->toplevel);
@@ -3316,34 +3315,6 @@ gtk_menu_get_preferred_height_for_width (GtkWidget *widget,
g_free (nat_heights);
}
static gboolean
gtk_menu_button_scroll (GtkMenu *menu,
GdkEventButton *event)
{
GtkMenuPrivate *priv = menu->priv;
if (priv->upper_arrow_prelight || priv->lower_arrow_prelight)
{
gboolean touchscreen_mode;
g_object_get (gtk_widget_get_settings (GTK_WIDGET (menu)),
"gtk-touchscreen-mode", &touchscreen_mode,
NULL);
if (touchscreen_mode)
gtk_menu_handle_scrolling (menu,
event->x_root, event->y_root,
event->type == GDK_BUTTON_PRESS,
FALSE);
return TRUE;
}
return FALSE;
}
static gboolean
pointer_in_menu_window (GtkWidget *widget,
gdouble x_root,
@@ -3380,13 +3351,16 @@ static gboolean
gtk_menu_button_press (GtkWidget *widget,
GdkEventButton *event)
{
GdkDevice *source_device;
GtkWidget *event_widget;
GtkMenu *menu;
if (event->type != GDK_BUTTON_PRESS)
return FALSE;
/* Don't pass down to menu shell for presses over scroll arrows
*/
if (gtk_menu_button_scroll (GTK_MENU (widget), event))
return TRUE;
source_device = gdk_event_get_source_device ((GdkEvent *) event);
event_widget = gtk_get_event_widget ((GdkEvent *) event);
menu = GTK_MENU (widget);
/* Don't pass down to menu shell if a non-menuitem part of the menu
* was clicked. The check for the event_widget being a GtkMenuShell
@@ -3395,10 +3369,16 @@ gtk_menu_button_press (GtkWidget *widget,
* the menu or on its border are delivered relative to
* menu_shell->window.
*/
if (GTK_IS_MENU_SHELL (gtk_get_event_widget ((GdkEvent *) event)) &&
if (GTK_IS_MENU_SHELL (event_widget) &&
pointer_in_menu_window (widget, event->x_root, event->y_root))
return TRUE;
if (GTK_IS_MENU_ITEM (event_widget) &&
gdk_device_get_source (source_device) == GDK_SOURCE_TOUCHSCREEN &&
GTK_MENU_ITEM (event_widget)->priv->submenu != NULL &&
!gtk_widget_is_drawable (GTK_MENU_ITEM (event_widget)->priv->submenu))
menu->priv->ignore_button_release = TRUE;
return GTK_WIDGET_CLASS (gtk_menu_parent_class)->button_press_event (widget, event);
}
@@ -3417,11 +3397,6 @@ gtk_menu_button_release (GtkWidget *widget,
if (event->type != GDK_BUTTON_RELEASE)
return FALSE;
/* Don't pass down to menu shell for releases over scroll arrows
*/
if (gtk_menu_button_scroll (GTK_MENU (widget), event))
return TRUE;
/* Don't pass down to menu shell if a non-menuitem part of the menu
* was clicked (see comment in button_press()).
*/
@@ -3665,10 +3640,14 @@ gtk_menu_motion_notify (GtkWidget *widget,
GtkMenu *menu;
GtkMenuShell *menu_shell;
GtkWidget *parent;
GdkDevice *source_device;
gboolean need_enter;
if (GTK_IS_MENU (widget))
source_device = gdk_event_get_source_device ((GdkEvent *) event);
if (GTK_IS_MENU (widget) &&
gdk_device_get_source (source_device) != GDK_SOURCE_TOUCHSCREEN)
{
GtkMenuPrivate *priv = GTK_MENU(widget)->priv;
@@ -3832,90 +3811,17 @@ gtk_menu_scroll_by (GtkMenu *menu,
gtk_menu_scroll_to (menu, offset);
}
static void
gtk_menu_do_timeout_scroll (GtkMenu *menu,
gboolean touchscreen_mode)
{
GtkMenuPrivate *priv = menu->priv;
gboolean upper_visible;
gboolean lower_visible;
upper_visible = priv->upper_arrow_visible;
lower_visible = priv->lower_arrow_visible;
gtk_menu_scroll_by (menu, priv->scroll_step);
if (touchscreen_mode &&
(upper_visible != priv->upper_arrow_visible ||
lower_visible != priv->lower_arrow_visible))
{
/* We are about to hide a scroll arrow while the mouse is pressed,
* this would cause the uncovered menu item to be activated on button
* release. Therefore we need to ignore button release here
*/
GTK_MENU_SHELL (menu)->priv->ignore_enter = TRUE;
priv->ignore_button_release = TRUE;
}
}
static gboolean
gtk_menu_scroll_timeout (gpointer data)
{
GtkMenu *menu;
gboolean touchscreen_mode;
menu = GTK_MENU (data);
g_object_get (gtk_widget_get_settings (GTK_WIDGET (menu)),
"gtk-touchscreen-mode", &touchscreen_mode,
NULL);
gtk_menu_do_timeout_scroll (menu, touchscreen_mode);
gtk_menu_scroll_by (menu, menu->priv->scroll_step);
return TRUE;
}
static gboolean
gtk_menu_scroll_timeout_initial (gpointer data)
{
GtkMenu *menu;
guint timeout;
gboolean touchscreen_mode;
menu = GTK_MENU (data);
g_object_get (gtk_widget_get_settings (GTK_WIDGET (menu)),
"gtk-timeout-repeat", &timeout,
"gtk-touchscreen-mode", &touchscreen_mode,
NULL);
gtk_menu_do_timeout_scroll (menu, touchscreen_mode);
gtk_menu_remove_scroll_timeout (menu);
menu->priv->scroll_timeout =
gdk_threads_add_timeout (timeout, gtk_menu_scroll_timeout, menu);
return FALSE;
}
static void
gtk_menu_start_scrolling (GtkMenu *menu)
{
guint timeout;
gboolean touchscreen_mode;
g_object_get (gtk_widget_get_settings (GTK_WIDGET (menu)),
"gtk-timeout-repeat", &timeout,
"gtk-touchscreen-mode", &touchscreen_mode,
NULL);
gtk_menu_do_timeout_scroll (menu, touchscreen_mode);
menu->priv->scroll_timeout =
gdk_threads_add_timeout (timeout, gtk_menu_scroll_timeout_initial, menu);
}
static gboolean
gtk_menu_scroll (GtkWidget *widget,
GdkEventScroll *event)
@@ -4039,14 +3945,9 @@ gtk_menu_handle_scrolling (GtkMenu *menu,
gboolean in_arrow;
gboolean scroll_fast = FALSE;
gint top_x, top_y;
gboolean touchscreen_mode;
menu_shell = GTK_MENU_SHELL (menu);
g_object_get (gtk_widget_get_settings (GTK_WIDGET (menu)),
"gtk-touchscreen-mode", &touchscreen_mode,
NULL);
gdk_window_get_position (gtk_widget_get_window (priv->toplevel),
&top_x, &top_y);
x -= top_x;
@@ -4064,82 +3965,44 @@ gtk_menu_handle_scrolling (GtkMenu *menu,
in_arrow = TRUE;
}
if (touchscreen_mode)
priv->upper_arrow_prelight = in_arrow;
if ((priv->upper_arrow_state & GTK_STATE_FLAG_INSENSITIVE) == 0)
{
gboolean arrow_pressed = FALSE;
if (priv->upper_arrow_visible && !priv->tearoff_active)
{
if (touchscreen_mode)
scroll_fast = (y < rect.y + MENU_SCROLL_FAST_ZONE);
if (enter && in_arrow &&
(!priv->upper_arrow_prelight ||
priv->scroll_fast != scroll_fast))
{
if (enter && priv->upper_arrow_prelight)
{
if (priv->scroll_timeout == 0)
{
/* Deselect the active item so that
* any submenus are popped down
*/
gtk_menu_shell_deselect (menu_shell);
priv->upper_arrow_prelight = TRUE;
priv->scroll_fast = scroll_fast;
gtk_menu_remove_scroll_timeout (menu);
priv->scroll_step = -MENU_SCROLL_STEP2; /* always fast */
/* Deselect the active item so that
* any submenus are popped down
*/
gtk_menu_shell_deselect (menu_shell);
if (!motion)
{
/* Only do stuff on click. */
gtk_menu_start_scrolling (menu);
arrow_pressed = TRUE;
}
}
else
{
arrow_pressed = TRUE;
}
}
else if (!enter)
{
gtk_menu_stop_scrolling (menu);
}
gtk_menu_remove_scroll_timeout (menu);
priv->scroll_step = scroll_fast
? -MENU_SCROLL_STEP2
: -MENU_SCROLL_STEP1;
priv->scroll_timeout =
gdk_threads_add_timeout (scroll_fast
? MENU_SCROLL_TIMEOUT2
: MENU_SCROLL_TIMEOUT1,
gtk_menu_scroll_timeout, menu);
}
else /* !touchscreen_mode */
else if (!enter && !in_arrow && priv->upper_arrow_prelight)
{
scroll_fast = (y < rect.y + MENU_SCROLL_FAST_ZONE);
if (enter && in_arrow &&
(!priv->upper_arrow_prelight ||
priv->scroll_fast != scroll_fast))
{
priv->upper_arrow_prelight = TRUE;
priv->scroll_fast = scroll_fast;
/* Deselect the active item so that
* any submenus are popped down
*/
gtk_menu_shell_deselect (menu_shell);
gtk_menu_remove_scroll_timeout (menu);
priv->scroll_step = scroll_fast
? -MENU_SCROLL_STEP2
: -MENU_SCROLL_STEP1;
priv->scroll_timeout =
gdk_threads_add_timeout (scroll_fast
? MENU_SCROLL_TIMEOUT2
: MENU_SCROLL_TIMEOUT1,
gtk_menu_scroll_timeout, menu);
}
else if (!enter && !in_arrow && priv->upper_arrow_prelight)
{
gtk_menu_stop_scrolling (menu);
}
gtk_menu_stop_scrolling (menu);
}
}
/* gtk_menu_start_scrolling() might have hit the top of the
* menu, so check if the button isn't insensitive before
/* check if the button isn't insensitive before
* changing it to something else.
*/
if ((priv->upper_arrow_state & GTK_STATE_FLAG_INSENSITIVE) == 0)
@@ -4174,82 +4037,44 @@ gtk_menu_handle_scrolling (GtkMenu *menu,
in_arrow = TRUE;
}
if (touchscreen_mode)
priv->lower_arrow_prelight = in_arrow;
if ((priv->lower_arrow_state & GTK_STATE_FLAG_INSENSITIVE) == 0)
{
gboolean arrow_pressed = FALSE;
if (priv->lower_arrow_visible && !priv->tearoff_active)
{
if (touchscreen_mode)
scroll_fast = (y > rect.y + rect.height - MENU_SCROLL_FAST_ZONE);
if (enter && in_arrow &&
(!priv->lower_arrow_prelight ||
priv->scroll_fast != scroll_fast))
{
if (enter && priv->lower_arrow_prelight)
{
if (priv->scroll_timeout == 0)
{
/* Deselect the active item so that
* any submenus are popped down
*/
gtk_menu_shell_deselect (menu_shell);
priv->lower_arrow_prelight = TRUE;
priv->scroll_fast = scroll_fast;
gtk_menu_remove_scroll_timeout (menu);
priv->scroll_step = MENU_SCROLL_STEP2; /* always fast */
/* Deselect the active item so that
* any submenus are popped down
*/
gtk_menu_shell_deselect (menu_shell);
if (!motion)
{
/* Only do stuff on click. */
gtk_menu_start_scrolling (menu);
arrow_pressed = TRUE;
}
}
else
{
arrow_pressed = TRUE;
}
}
else if (!enter)
{
gtk_menu_stop_scrolling (menu);
}
gtk_menu_remove_scroll_timeout (menu);
priv->scroll_step = scroll_fast
? MENU_SCROLL_STEP2
: MENU_SCROLL_STEP1;
priv->scroll_timeout =
gdk_threads_add_timeout (scroll_fast
? MENU_SCROLL_TIMEOUT2
: MENU_SCROLL_TIMEOUT1,
gtk_menu_scroll_timeout, menu);
}
else /* !touchscreen_mode */
else if (!enter && !in_arrow && priv->lower_arrow_prelight)
{
scroll_fast = (y > rect.y + rect.height - MENU_SCROLL_FAST_ZONE);
if (enter && in_arrow &&
(!priv->lower_arrow_prelight ||
priv->scroll_fast != scroll_fast))
{
priv->lower_arrow_prelight = TRUE;
priv->scroll_fast = scroll_fast;
/* Deselect the active item so that
* any submenus are popped down
*/
gtk_menu_shell_deselect (menu_shell);
gtk_menu_remove_scroll_timeout (menu);
priv->scroll_step = scroll_fast
? MENU_SCROLL_STEP2
: MENU_SCROLL_STEP1;
priv->scroll_timeout =
gdk_threads_add_timeout (scroll_fast
? MENU_SCROLL_TIMEOUT2
: MENU_SCROLL_TIMEOUT1,
gtk_menu_scroll_timeout, menu);
}
else if (!enter && !in_arrow && priv->lower_arrow_prelight)
{
gtk_menu_stop_scrolling (menu);
}
gtk_menu_stop_scrolling (menu);
}
}
/* gtk_menu_start_scrolling() might have hit the bottom of the
* menu, so check if the button isn't insensitive before
/* check if the button isn't insensitive before
* changing it to something else.
*/
if ((priv->lower_arrow_state & GTK_STATE_FLAG_INSENSITIVE) == 0)
@@ -4279,19 +4104,18 @@ gtk_menu_enter_notify (GtkWidget *widget,
{
GtkWidget *menu_item;
GtkWidget *parent;
gboolean touchscreen_mode;
GdkDevice *source_device;
if (event->mode == GDK_CROSSING_GTK_GRAB ||
event->mode == GDK_CROSSING_GTK_UNGRAB ||
event->mode == GDK_CROSSING_STATE_CHANGED)
return TRUE;
g_object_get (gtk_widget_get_settings (widget),
"gtk-touchscreen-mode", &touchscreen_mode,
NULL);
source_device = gdk_event_get_source_device ((GdkEvent *) event);
menu_item = gtk_get_event_widget ((GdkEvent*) event);
if (GTK_IS_MENU (widget))
if (GTK_IS_MENU (widget) &&
gdk_device_get_source (source_device) != GDK_SOURCE_TOUCHSCREEN)
{
GtkMenuShell *menu_shell = GTK_MENU_SHELL (widget);
@@ -4300,7 +4124,8 @@ gtk_menu_enter_notify (GtkWidget *widget,
event->x_root, event->y_root, TRUE, TRUE);
}
if (!touchscreen_mode && GTK_IS_MENU_ITEM (menu_item))
if (gdk_device_get_source (source_device) != GDK_SOURCE_TOUCHSCREEN &&
GTK_IS_MENU_ITEM (menu_item))
{
GtkWidget *menu = gtk_widget_get_parent (menu_item);
@@ -4357,6 +4182,7 @@ gtk_menu_leave_notify (GtkWidget *widget,
GtkMenu *menu;
GtkMenuItem *menu_item;
GtkWidget *event_widget;
GdkDevice *source_device;
if (event->mode == GDK_CROSSING_GTK_GRAB ||
event->mode == GDK_CROSSING_GTK_UNGRAB ||
@@ -4369,7 +4195,10 @@ gtk_menu_leave_notify (GtkWidget *widget,
if (gtk_menu_navigating_submenu (menu, event->x_root, event->y_root))
return TRUE;
gtk_menu_handle_scrolling (menu, event->x_root, event->y_root, FALSE, TRUE);
source_device = gdk_event_get_source_device ((GdkEvent *) event);
if (gdk_device_get_source (source_device) != GDK_SOURCE_TOUCHSCREEN)
gtk_menu_handle_scrolling (menu, event->x_root, event->y_root, FALSE, TRUE);
event_widget = gtk_get_event_widget ((GdkEvent*) event);
@@ -4404,6 +4233,142 @@ gtk_menu_leave_notify (GtkWidget *widget,
return GTK_WIDGET_CLASS (gtk_menu_parent_class)->leave_notify_event (widget, event);
}
static gboolean
pointer_on_menu_widget (GtkMenu *menu,
gdouble x_root,
gdouble y_root)
{
GtkMenuPrivate *priv = menu->priv;
GtkAllocation allocation;
gint window_x, window_y;
gtk_widget_get_allocation (GTK_WIDGET (menu), &allocation);
gdk_window_get_position (gtk_widget_get_window (priv->toplevel),
&window_x, &window_y);
if (x_root >= window_x && x_root < window_x + allocation.width &&
y_root >= window_y && y_root < window_y + allocation.height)
return TRUE;
return FALSE;
}
static gboolean
gtk_menu_captured_event (GtkWidget *widget,
GdkEvent *event)
{
GdkDevice *source_device;
gboolean retval = FALSE;
GtkMenuPrivate *priv;
GtkMenu *menu;
gdouble x_root, y_root;
guint button;
GdkModifierType state;
menu = GTK_MENU (widget);
priv = menu->priv;
if (!priv->upper_arrow_visible && !priv->lower_arrow_visible)
return retval;
source_device = gdk_event_get_source_device (event);
gdk_event_get_root_coords (event, &x_root, &y_root);
switch (event->type)
{
case GDK_TOUCH_BEGIN:
case GDK_BUTTON_PRESS:
if ((!gdk_event_get_button (event, &button) || button == 1) &&
gdk_device_get_source (source_device) == GDK_SOURCE_TOUCHSCREEN &&
pointer_on_menu_widget (menu, x_root, y_root))
{
priv->drag_start_y = event->button.y_root;
priv->initial_drag_offset = priv->scroll_offset;
priv->drag_scroll_started = FALSE;
}
else
priv->drag_start_y = -1;
priv->drag_already_pressed = TRUE;
break;
case GDK_TOUCH_END:
case GDK_BUTTON_RELEASE:
if (priv->drag_scroll_started)
{
priv->drag_scroll_started = FALSE;
priv->drag_start_y = -1;
priv->drag_already_pressed = FALSE;
retval = TRUE;
}
break;
case GDK_TOUCH_UPDATE:
case GDK_MOTION_NOTIFY:
if ((!gdk_event_get_state (event, &state) || (state & GDK_BUTTON1_MASK)
!= 0) &&
gdk_device_get_source (source_device) == GDK_SOURCE_TOUCHSCREEN)
{
if (!priv->drag_already_pressed)
{
if (pointer_on_menu_widget (menu, x_root, y_root))
{
priv->drag_start_y = y_root;
priv->initial_drag_offset = priv->scroll_offset;
priv->drag_scroll_started = FALSE;
}
else
priv->drag_start_y = -1;
priv->drag_already_pressed = TRUE;
}
if (priv->drag_start_y < 0 && !priv->drag_scroll_started)
break;
if (priv->drag_scroll_started)
{
gint offset, view_height;
GtkBorder arrow_border;
gdouble y_diff;
y_diff = y_root - priv->drag_start_y;
offset = priv->initial_drag_offset - y_diff;
view_height = gdk_window_get_height (gtk_widget_get_window (widget));
get_arrows_border (menu, &arrow_border);
if (priv->upper_arrow_visible)
view_height -= arrow_border.top;
if (priv->lower_arrow_visible)
view_height -= arrow_border.bottom;
offset = CLAMP (offset, 0, priv->requested_height - view_height);
gtk_menu_scroll_to (menu, offset);
retval = TRUE;
}
else if (gtk_drag_check_threshold (widget,
0, priv->drag_start_y,
0, y_root))
{
priv->drag_scroll_started = TRUE;
gtk_menu_shell_deselect (GTK_MENU_SHELL (menu));
retval = TRUE;
}
}
break;
case GDK_ENTER_NOTIFY:
case GDK_LEAVE_NOTIFY:
if (priv->drag_scroll_started)
retval = TRUE;
break;
default:
break;
}
return retval;
}
static void
gtk_menu_stop_navigating_submenu (GtkMenu *menu)
{
@@ -4846,19 +4811,10 @@ static void
gtk_menu_stop_scrolling (GtkMenu *menu)
{
GtkMenuPrivate *priv = menu->priv;
gboolean touchscreen_mode;
gtk_menu_remove_scroll_timeout (menu);
g_object_get (gtk_widget_get_settings (GTK_WIDGET (menu)),
"gtk-touchscreen-mode", &touchscreen_mode,
NULL);
if (!touchscreen_mode)
{
priv->upper_arrow_prelight = FALSE;
priv->lower_arrow_prelight = FALSE;
}
priv->upper_arrow_prelight = FALSE;
priv->lower_arrow_prelight = FALSE;
}
static void
@@ -5664,7 +5620,6 @@ gtk_menu_real_move_scroll (GtkMenu *menu,
}
}
/**
* gtk_menu_set_monitor:
* @menu: a #GtkMenu
@@ -5741,11 +5696,13 @@ static void
gtk_menu_grab_notify (GtkWidget *widget,
gboolean was_grabbed)
{
GtkMenu *menu;
GtkWidget *toplevel;
GtkWindowGroup *group;
GtkWidget *grab;
GdkDevice *pointer;
menu = GTK_MENU (widget);
pointer = _gtk_menu_shell_get_grab_device (GTK_MENU_SHELL (widget));
if (!pointer ||
@@ -5762,6 +5719,8 @@ gtk_menu_grab_notify (GtkWidget *widget,
if (GTK_MENU_SHELL (widget)->priv->active && !GTK_IS_MENU_SHELL (grab))
gtk_menu_shell_cancel (GTK_MENU_SHELL (widget));
menu->priv->drag_scroll_started = FALSE;
}
/**

View File

@@ -1690,13 +1690,20 @@ static void
gtk_real_menu_item_select (GtkMenuItem *menu_item)
{
GtkMenuItemPrivate *priv = menu_item->priv;
gboolean touchscreen_mode;
GdkDevice *source_device = NULL;
GdkEvent *current_event;
g_object_get (gtk_widget_get_settings (GTK_WIDGET (menu_item)),
"gtk-touchscreen-mode", &touchscreen_mode,
NULL);
current_event = gtk_get_current_event ();
if (!touchscreen_mode && priv->submenu &&
if (current_event)
{
source_device = gdk_event_get_source_device (current_event);
gdk_event_free (current_event);
}
if ((!source_device ||
gdk_device_get_source (source_device) != GDK_SOURCE_TOUCHSCREEN) &&
priv->submenu &&
(!gtk_widget_get_mapped (priv->submenu) ||
GTK_MENU (priv->submenu)->priv->tearoff_active))
{

View File

@@ -100,6 +100,8 @@ struct _GtkMenuPrivate
guint seen_item_enter : 1;
guint ignore_button_release : 1;
guint no_toggle_size : 1;
guint drag_already_pressed : 1;
guint drag_scroll_started : 1;
/* info used for the table */
guint *heights;
@@ -126,6 +128,9 @@ struct _GtkMenuPrivate
gint navigation_height;
guint navigation_timeout;
gdouble drag_start_y;
gint initial_drag_offset;
};
G_END_DECLS

View File

@@ -1084,13 +1084,11 @@ gtk_menu_shell_enter_notify (GtkWidget *widget,
if (!gtk_widget_get_visible (GTK_MENU_ITEM (menu_item)->priv->submenu))
{
gboolean touchscreen_mode;
GdkDevice *source_device;
g_object_get (gtk_widget_get_settings (widget),
"gtk-touchscreen-mode", &touchscreen_mode,
NULL);
source_device = gdk_event_get_source_device ((GdkEvent *) event);
if (touchscreen_mode)
if (gdk_device_get_source (source_device) == GDK_SOURCE_TOUCHSCREEN)
_gtk_menu_item_popup_submenu (menu_item, TRUE);
}
}
@@ -1612,45 +1610,19 @@ gtk_real_menu_shell_move_current (GtkMenuShell *menu_shell,
GtkMenuShellPrivate *priv = menu_shell->priv;
GtkMenuShell *parent_menu_shell = NULL;
gboolean had_selection;
gboolean touchscreen_mode;
priv->in_unselectable_item = FALSE;
had_selection = priv->active_menu_item != NULL;
g_object_get (gtk_widget_get_settings (GTK_WIDGET (menu_shell)),
"gtk-touchscreen-mode", &touchscreen_mode,
NULL);
if (priv->parent_menu_shell)
parent_menu_shell = GTK_MENU_SHELL (priv->parent_menu_shell);
switch (direction)
{
case GTK_MENU_DIR_PARENT:
if (touchscreen_mode &&
priv->active_menu_item &&
GTK_MENU_ITEM (priv->active_menu_item)->priv->submenu &&
gtk_widget_get_visible (GTK_MENU_ITEM (priv->active_menu_item)->priv->submenu))
if (parent_menu_shell)
{
/* if we are on a menu item that has an open submenu but the
* focus is not in that submenu (e.g. because it's empty or
* has only insensitive items), close that submenu instead of
* running into the code below which would close *this* menu.
*/
_gtk_menu_item_popdown_submenu (priv->active_menu_item);
_gtk_menu_shell_update_mnemonics (menu_shell);
}
else if (parent_menu_shell)
{
if (touchscreen_mode)
{
/* close menu when returning from submenu. */
_gtk_menu_item_popdown_submenu (GTK_MENU (menu_shell)->priv->parent_menu_item);
_gtk_menu_shell_update_mnemonics (parent_menu_shell);
break;
}
if (GTK_MENU_SHELL_GET_CLASS (parent_menu_shell)->submenu_placement ==
GTK_MENU_SHELL_GET_CLASS (menu_shell)->submenu_placement)
gtk_menu_shell_deselect (menu_shell);

View File

@@ -36,6 +36,8 @@
#include "gtktypebuiltins.h"
#include "gtkprivate.h"
#include "gtkintl.h"
#include "gtkdnd.h"
#include "gtkwidgetprivate.h"
#include "a11y/gtkpanedaccessible.h"
/**
@@ -98,6 +100,17 @@ enum {
CHILD2
};
typedef struct
{
GdkWindow *pane_window;
GdkDevice *device;
GdkEventSequence *sequence;
gdouble x;
gdouble y;
GdkEvent *button_press_event;
guint press_consumed : 1;
} TouchInfo;
struct _GtkPanedPrivate
{
GtkPaned *first_paned;
@@ -124,6 +137,8 @@ struct _GtkPanedPrivate
guint32 grab_time;
GArray *touches;
guint handle_prelit : 1;
guint in_drag : 1;
guint in_recursion : 1;
@@ -255,6 +270,8 @@ static gboolean gtk_paned_toggle_handle_focus (GtkPaned *paned);
static GType gtk_paned_child_type (GtkContainer *container);
static void gtk_paned_grab_notify (GtkWidget *widget,
gboolean was_grabbed);
static gboolean gtk_paned_captured_event (GtkWidget *widget,
GdkEvent *event);
G_DEFINE_TYPE_WITH_CODE (GtkPaned, gtk_paned, GTK_TYPE_CONTAINER,
@@ -708,6 +725,11 @@ gtk_paned_init (GtkPaned *paned)
priv->handle_pos.y = -1;
priv->drag_pos = -1;
priv->touches = g_array_sized_new (FALSE, FALSE,
sizeof (TouchInfo), 2);
_gtk_widget_set_captured_event_handler (GTK_WIDGET (paned), gtk_paned_captured_event);
}
static void
@@ -863,9 +885,11 @@ static void
gtk_paned_finalize (GObject *object)
{
GtkPaned *paned = GTK_PANED (object);
GtkPanedPrivate *priv = paned->priv;
gtk_paned_set_saved_focus (paned, NULL);
gtk_paned_set_first_paned (paned, NULL);
g_array_free (priv->touches, TRUE);
G_OBJECT_CLASS (gtk_paned_parent_class)->finalize (object);
}
@@ -1281,7 +1305,9 @@ gtk_paned_create_child_window (GtkPaned *paned,
attributes.window_type = GDK_WINDOW_CHILD;
attributes.wclass = GDK_INPUT_OUTPUT;
attributes.event_mask = gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK;
attributes.event_mask = gtk_widget_get_events (widget) |
GDK_EXPOSURE_MASK | GDK_TOUCH_MASK;
if (child)
{
GtkAllocation allocation;
@@ -1571,7 +1597,8 @@ gtk_paned_enter (GtkWidget *widget,
GtkPaned *paned = GTK_PANED (widget);
GtkPanedPrivate *priv = paned->priv;
if (priv->in_drag)
if (priv->in_drag &&
priv->touches->len == 0)
update_drag (paned, event->x, event->y);
else
{
@@ -1593,7 +1620,8 @@ gtk_paned_leave (GtkWidget *widget,
GtkPaned *paned = GTK_PANED (widget);
GtkPanedPrivate *priv = paned->priv;
if (priv->in_drag)
if (priv->in_drag &&
priv->touches->len == 0)
update_drag (paned, event->x, event->y);
else
{
@@ -1626,6 +1654,38 @@ gtk_paned_focus (GtkWidget *widget,
return retval;
}
static gboolean
start_drag (GtkPaned *paned,
GdkDevice *device,
int xpos,
int ypos,
guint time)
{
GtkPanedPrivate *priv = paned->priv;
if (gdk_device_grab (device,
priv->handle,
GDK_OWNERSHIP_WINDOW, FALSE,
GDK_BUTTON1_MOTION_MASK
| GDK_BUTTON_RELEASE_MASK
| GDK_ENTER_NOTIFY_MASK
| GDK_LEAVE_NOTIFY_MASK
| GDK_TOUCH_MASK,
NULL, time) != GDK_GRAB_SUCCESS)
return FALSE;
priv->in_drag = TRUE;
priv->grab_time = time;
priv->grab_device = device;
if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
priv->drag_pos = xpos;
else
priv->drag_pos = ypos;
return TRUE;
}
static gboolean
gtk_paned_button_press (GtkWidget *widget,
GdkEventButton *event)
@@ -1636,27 +1696,10 @@ gtk_paned_button_press (GtkWidget *widget,
if (!priv->in_drag &&
(event->window == priv->handle) && (event->button == GDK_BUTTON_PRIMARY))
{
/* We need a server grab here, not gtk_grab_add(), since
* we don't want to pass events on to the widget's children */
if (gdk_device_grab (event->device,
priv->handle,
GDK_OWNERSHIP_WINDOW, FALSE,
GDK_BUTTON1_MOTION_MASK
| GDK_BUTTON_RELEASE_MASK
| GDK_ENTER_NOTIFY_MASK
| GDK_LEAVE_NOTIFY_MASK,
NULL, event->time) != GDK_GRAB_SUCCESS)
if (!start_drag (paned, event->device,
event->x, event->y, event->time))
return FALSE;
priv->in_drag = TRUE;
priv->grab_time = event->time;
priv->grab_device = event->device;
if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
priv->drag_pos = event->x;
else
priv->drag_pos = event->y;
return TRUE;
}
@@ -1670,9 +1713,13 @@ gtk_paned_grab_broken (GtkWidget *widget,
GtkPaned *paned = GTK_PANED (widget);
GtkPanedPrivate *priv = paned->priv;
priv->in_drag = FALSE;
priv->drag_pos = -1;
priv->position_set = TRUE;
if (event->grab_window != priv->handle &&
gdk_event_get_device ((GdkEvent *) event) == priv->grab_device)
{
priv->in_drag = FALSE;
priv->drag_pos = -1;
priv->position_set = TRUE;
}
return TRUE;
}
@@ -1687,7 +1734,7 @@ stop_drag (GtkPaned *paned)
priv->position_set = TRUE;
gdk_device_ungrab (priv->grab_device,
priv->grab_time);
gtk_get_current_event_time ());
priv->grab_device = NULL;
}
@@ -1762,6 +1809,263 @@ gtk_paned_motion (GtkWidget *widget,
return FALSE;
}
static GdkWindow *
_gtk_paned_find_pane_window (GtkWidget *widget,
GdkEvent *event)
{
GtkPanedPrivate *priv = GTK_PANED (widget)->priv;
GtkWidget *event_widget;
event_widget = gtk_get_event_widget (event);
if (event_widget == widget)
{
if (event->any.window == priv->child1_window)
return priv->child1_window;
else if (event->any.window == priv->child2_window)
return priv->child2_window;
return NULL;
}
else if (event_widget == priv->child1 ||
gtk_widget_is_ancestor (event_widget, priv->child1))
return priv->child1_window;
else if (event_widget == priv->child2 ||
gtk_widget_is_ancestor (event_widget, priv->child2))
return priv->child2_window;
return NULL;
}
static TouchInfo *
_gtk_paned_find_touch (GtkPaned *paned,
GdkDevice *device,
GdkEventSequence *sequence,
guint *index)
{
GtkPanedPrivate *priv = paned->priv;
TouchInfo *info;
guint i;
for (i = 0; i < priv->touches->len; i++)
{
info = &g_array_index (priv->touches, TouchInfo, i);
if (info->device == device && info->sequence == sequence)
{
if (index)
*index = i;
return info;
}
}
return NULL;
}
static void
gtk_paned_release_captured_event (GtkPaned *paned,
TouchInfo *touch,
gboolean emit)
{
GtkPanedPrivate *priv = paned->priv;
GtkWidget *event_widget, *child;
if (!touch->button_press_event)
return;
event_widget = gtk_get_event_widget (touch->button_press_event);
if (touch->pane_window == priv->child1_window)
child = priv->child1;
else if (touch->pane_window == priv->child2_window)
child = priv->child2;
else
return;
if (emit &&
!_gtk_propagate_captured_event (event_widget,
touch->button_press_event,
child))
gtk_propagate_event (event_widget, touch->button_press_event);
gdk_event_free (touch->button_press_event);
touch->button_press_event = NULL;
}
static gboolean
gtk_paned_captured_event (GtkWidget *widget,
GdkEvent *event)
{
GtkPaned *paned = GTK_PANED (widget);
GtkPanedPrivate *priv = paned->priv;
GdkDevice *device, *source_device;
GdkWindow *pane_window;
TouchInfo new = { 0 }, *info;
GdkEventSequence *sequence;
guint index;
gdouble event_x, event_y;
gint x, y;
device = gdk_event_get_device (event);
source_device = gdk_event_get_source_device (event);
/* We possibly deal with both pointer and touch events,
* depending on the target window event mask, so assume
* touch ID = 0 for pointer events to ease handling.
*/
sequence = gdk_event_get_event_sequence (event);
gdk_event_get_coords (event, &event_x, &event_y);
if (!source_device ||
gdk_device_get_source (source_device) != GDK_SOURCE_TOUCHSCREEN)
return FALSE;
switch (event->type)
{
case GDK_BUTTON_PRESS:
case GDK_TOUCH_BEGIN:
if (priv->touches->len == 2)
return FALSE;
pane_window = _gtk_paned_find_pane_window (widget, event);
gtk_widget_translate_coordinates (gtk_get_event_widget (event),
widget,
(gint)event_x, (gint)event_y,
&x, &y);
for (index = 0; index < priv->touches->len; index++)
{
info = &g_array_index (priv->touches, TouchInfo, index);
/* There was already a touch in this pane */
if (info->pane_window == pane_window)
return FALSE;
}
new.device = device;
new.sequence = sequence;
new.pane_window = pane_window;
new.x = x;
new.y = y;
g_array_append_val (priv->touches, new);
if (priv->touches->len == 1)
{
/* It's the first touch, store the event and set
* a timeout in order to release the event if no
* second touch happens timely.
*/
info = &g_array_index (priv->touches, TouchInfo, 0);
info->button_press_event = gdk_event_copy (event);
return TRUE;
}
else if (priv->touches->len == 2)
{
GtkWidget *event_widget;
/* It's the second touch, release (don't emit) the
* held button/touch presses.
*/
for (index = 0; index < priv->touches->len; index++)
{
info = &g_array_index (priv->touches, TouchInfo, index);
gtk_paned_release_captured_event (paned, info, FALSE);
info->press_consumed = TRUE;
}
event_widget = gtk_get_event_widget (event);
if (event_widget == widget)
{
if (pane_window == priv->child2_window)
{
if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
event_x += priv->handle_pos.x + priv->handle_pos.width;
else
event_y += priv->handle_pos.y + priv->handle_pos.height;
}
}
else
gtk_widget_translate_coordinates (event_widget, widget,
(gint)event_x, (gint)event_y,
&x, &y);
start_drag (paned, device, x, y,
event->button.time);
if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
priv->drag_pos = x - priv->handle_pos.x;
else
priv->drag_pos = y - priv->handle_pos.y;
return TRUE;
}
break;
case GDK_BUTTON_RELEASE:
case GDK_TOUCH_END:
info = _gtk_paned_find_touch (GTK_PANED (widget), device, sequence, &index);
if (info)
{
gboolean press_consumed;
if (priv->touches->len == 2)
stop_drag (paned);
press_consumed = info->press_consumed;
/* Release the held button/touch press, if still queued */
gtk_paned_release_captured_event (paned, info, TRUE);
g_array_remove_index_fast (priv->touches, index);
if (press_consumed)
return TRUE;
}
break;
case GDK_MOTION_NOTIFY:
case GDK_TOUCH_UPDATE:
info = _gtk_paned_find_touch (GTK_PANED (widget), device, sequence, &index);
if (info)
{
gtk_widget_translate_coordinates (gtk_get_event_widget (event),
widget,
event_x, event_y,
&x, &y);
/* If there is a single touch and this isn't a continuation
* from a previous successful 2-touch operation, check
* the threshold to let the child handle events.
*/
if (priv->touches->len == 1 &&
gtk_drag_check_threshold (widget, info->x, info->y, x, y))
{
gtk_paned_release_captured_event (paned, info, TRUE);
g_array_remove_index_fast (priv->touches, index);
return FALSE;
}
else if (priv->touches->len == 2 && index == 1)
{
/* The device grab on priv->handle is in effect now,
* so the event coordinates are already relative to
* that window.
*/
update_drag (paned, event_x, event_y);
}
return TRUE;
}
break;
default:
break;
}
return FALSE;
}
/**
* gtk_paned_new:
* @orientation: the paned's orientation.

View File

@@ -74,6 +74,10 @@ gboolean _gtk_translate_keyboard_accel_state (GdkKeymap *keymap,
gint *level,
GdkModifierType *consumed_modifiers);
gboolean _gtk_propagate_captured_event (GtkWidget *widget,
GdkEvent *event,
GtkWidget *topmost);
G_END_DECLS
#endif /* __GTK_PRIVATE_H__ */

View File

@@ -2009,15 +2009,11 @@ gtk_range_draw (GtkWidget *widget,
GtkStateFlags widget_state;
gint focus_line_width = 0;
gint focus_padding = 0;
gboolean touchscreen;
gboolean draw_trough = TRUE;
gboolean draw_slider = TRUE;
GtkStyleContext *context;
context = gtk_widget_get_style_context (widget);
g_object_get (gtk_widget_get_settings (widget),
"gtk-touchscreen-mode", &touchscreen,
NULL);
if (GTK_IS_SCALE (widget) &&
gtk_adjustment_get_upper (priv->adjustment) == gtk_adjustment_get_lower (priv->adjustment))
@@ -2279,7 +2275,7 @@ gtk_range_draw (GtkWidget *widget,
state &= ~(GTK_STATE_FLAG_PRELIGHT | GTK_STATE_FLAG_ACTIVE);
if (!touchscreen && priv->mouse_location == MOUSE_SLIDER && !(state & GTK_STATE_FLAG_INSENSITIVE))
if (priv->mouse_location == MOUSE_SLIDER && !(state & GTK_STATE_FLAG_INSENSITIVE))
state |= GTK_STATE_FLAG_PRELIGHT;
if (priv->grab_location == MOUSE_SLIDER)
@@ -2309,28 +2305,28 @@ gtk_range_draw (GtkWidget *widget,
draw_stepper (range, STEPPER_A, cr,
priv->orientation == GTK_ORIENTATION_VERTICAL ? GTK_ARROW_UP : GTK_ARROW_LEFT,
priv->grab_location == MOUSE_STEPPER_A,
!touchscreen && priv->mouse_location == MOUSE_STEPPER_A,
priv->mouse_location == MOUSE_STEPPER_A,
widget_state);
if (priv->has_stepper_b)
draw_stepper (range, STEPPER_B, cr,
priv->orientation == GTK_ORIENTATION_VERTICAL ? GTK_ARROW_DOWN : GTK_ARROW_RIGHT,
priv->grab_location == MOUSE_STEPPER_B,
!touchscreen && priv->mouse_location == MOUSE_STEPPER_B,
priv->mouse_location == MOUSE_STEPPER_B,
widget_state);
if (priv->has_stepper_c)
draw_stepper (range, STEPPER_C, cr,
priv->orientation == GTK_ORIENTATION_VERTICAL ? GTK_ARROW_UP : GTK_ARROW_LEFT,
priv->grab_location == MOUSE_STEPPER_C,
!touchscreen && priv->mouse_location == MOUSE_STEPPER_C,
priv->mouse_location == MOUSE_STEPPER_C,
widget_state);
if (priv->has_stepper_d)
draw_stepper (range, STEPPER_D, cr,
priv->orientation == GTK_ORIENTATION_VERTICAL ? GTK_ARROW_DOWN : GTK_ARROW_RIGHT,
priv->grab_location == MOUSE_STEPPER_D,
!touchscreen && priv->mouse_location == MOUSE_STEPPER_D,
priv->mouse_location == MOUSE_STEPPER_D,
widget_state);
return FALSE;
@@ -2528,7 +2524,8 @@ gtk_range_button_press (GtkWidget *widget,
{
GtkRange *range = GTK_RANGE (widget);
GtkRangePrivate *priv = range->priv;
GdkDevice *device;
GdkDevice *device, *source_device;
GdkInputSource source;
if (!gtk_widget_has_focus (widget))
gtk_widget_grab_focus (widget);
@@ -2538,6 +2535,9 @@ gtk_range_button_press (GtkWidget *widget,
return FALSE;
device = gdk_event_get_device ((GdkEvent *) event);
source_device = gdk_event_get_source_device ((GdkEvent *) event);
source = gdk_device_get_source (source_device);
priv->mouse_x = event->x;
priv->mouse_y = event->y;
@@ -2554,23 +2554,24 @@ gtk_range_button_press (GtkWidget *widget,
return TRUE;
}
if (priv->mouse_location == MOUSE_TROUGH &&
if (source != GDK_SOURCE_TOUCHSCREEN &&
priv->mouse_location == MOUSE_TROUGH &&
event->button == GDK_BUTTON_PRIMARY)
{
/* button 1 steps by page increment, as with button 2 on a stepper
*/
GtkScrollType scroll;
gdouble click_value;
click_value = coord_to_value (range,
priv->orientation == GTK_ORIENTATION_VERTICAL ?
event->y : event->x);
priv->trough_click_forward = click_value > gtk_adjustment_get_value (priv->adjustment);
range_grab_add (range, device, MOUSE_TROUGH, event->button);
scroll = range_get_scroll_for_grab (range);
gtk_range_add_step_timer (range, scroll);
return TRUE;
@@ -2605,17 +2606,17 @@ gtk_range_button_press (GtkWidget *widget,
return TRUE;
}
else if ((priv->mouse_location == MOUSE_TROUGH &&
event->button == GDK_BUTTON_MIDDLE) ||
(source == GDK_SOURCE_TOUCHSCREEN || event->button == GDK_BUTTON_MIDDLE)) ||
priv->mouse_location == MOUSE_SLIDER)
{
gboolean need_value_update = FALSE;
/* Any button can be used to drag the slider, but you can start
* dragging the slider with a trough click using button 2;
* On button 2 press, we warp the slider to mouse position,
* then begin the slider drag.
* On button 2 press and touch devices, we warp the slider to
* mouse position, then begin the slider drag.
*/
if (event->button == GDK_BUTTON_MIDDLE)
if (event->button == GDK_BUTTON_MIDDLE || source == GDK_SOURCE_TOUCHSCREEN)
{
gdouble slider_low_value, slider_high_value, new_value;

File diff suppressed because it is too large Load Diff

View File

@@ -117,6 +117,13 @@ void gtk_scrolled_window_set_min_content_width (GtkScrolledWindow *sc
gint gtk_scrolled_window_get_min_content_height (GtkScrolledWindow *scrolled_window);
void gtk_scrolled_window_set_min_content_height (GtkScrolledWindow *scrolled_window,
gint height);
void gtk_scrolled_window_set_kinetic_scrolling (GtkScrolledWindow *scrolled_window,
gboolean kinetic_scrolling);
gboolean gtk_scrolled_window_get_kinetic_scrolling (GtkScrolledWindow *scrolled_window);
void gtk_scrolled_window_set_capture_button_press (GtkScrolledWindow *scrolled_window,
gboolean capture_button_press);
gboolean gtk_scrolled_window_get_capture_button_press (GtkScrolledWindow *scrolled_window);
gint _gtk_scrolled_window_get_scrollbar_spacing (GtkScrolledWindow *scrolled_window);

View File

@@ -701,13 +701,16 @@ gtk_settings_class_init (GtkSettingsClass *class)
* functionality.
*
* Since: 2.10
*
* Deprecated: 3.4. Generally the behavior touchscreen input should be
* performed dynamically based on gdk_event_get_source_device().
*/
result = settings_install_property_parser (class,
g_param_spec_boolean ("gtk-touchscreen-mode",
P_("Enable Touchscreen Mode"),
P_("When TRUE, there are no motion notify events delivered on this screen"),
FALSE,
GTK_PARAM_READWRITE),
GTK_PARAM_READWRITE | G_PARAM_DEPRECATED),
NULL);
g_assert (result == PROP_TOUCHSCREEN_MODE);

View File

@@ -658,13 +658,9 @@ gtk_toggle_button_update_state (GtkButton *button)
{
GtkToggleButton *toggle_button = GTK_TOGGLE_BUTTON (button);
GtkToggleButtonPrivate *priv = toggle_button->priv;
gboolean depressed, touchscreen;
gboolean depressed;
GtkStateFlags new_state = 0;
g_object_get (gtk_widget_get_settings (GTK_WIDGET (button)),
"gtk-touchscreen-mode", &touchscreen,
NULL);
new_state = gtk_widget_get_state_flags (GTK_WIDGET (button)) &
~(GTK_STATE_FLAG_INCONSISTENT |
GTK_STATE_FLAG_PRELIGHT |
@@ -680,7 +676,7 @@ gtk_toggle_button_update_state (GtkButton *button)
else
depressed = priv->active;
if (!touchscreen && button->priv->in_button && (!button->priv->button_down || priv->draw_indicator))
if (button->priv->in_button && (!button->priv->button_down || priv->draw_indicator))
new_state |= GTK_STATE_FLAG_PRELIGHT;
if (depressed)

View File

@@ -1537,22 +1537,33 @@ _gtk_tooltip_hide (GtkWidget *widget)
}
static gboolean
tooltips_enabled (GdkWindow *window)
tooltips_enabled (GdkEvent *event)
{
GdkDevice *source_device;
GdkInputSource source;
GdkWindow *window;
gboolean enabled;
gboolean touchscreen;
GdkScreen *screen;
GtkSettings *settings;
window = event->any.window;
source_device = gdk_event_get_source_device (event);
if (!source_device)
return FALSE;
source = gdk_device_get_source (source_device);
screen = gdk_window_get_screen (window);
settings = gtk_settings_get_for_screen (screen);
g_object_get (settings,
"gtk-touchscreen-mode", &touchscreen,
"gtk-enable-tooltips", &enabled,
NULL);
return (!touchscreen && enabled);
if (enabled && source != GDK_SOURCE_TOUCHSCREEN)
return TRUE;
return FALSE;
}
void
@@ -1564,7 +1575,7 @@ _gtk_tooltip_handle_event (GdkEvent *event)
GdkDisplay *display;
GtkTooltip *current_tooltip;
if (!tooltips_enabled (event->any.window))
if (!tooltips_enabled (event))
return;
/* Returns coordinates relative to has_tooltip_widget's allocation. */

View File

@@ -684,7 +684,7 @@ gtk_viewport_realize (GtkWidget *widget)
event_mask = gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK;
/* We select on button_press_mask so that button 4-5 scrolls are trapped.
*/
attributes.event_mask = event_mask | GDK_BUTTON_PRESS_MASK;
attributes.event_mask = event_mask | GDK_BUTTON_PRESS_MASK | GDK_TOUCH_MASK;
attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;

View File

@@ -484,6 +484,7 @@ enum {
QUERY_TOOLTIP,
DRAG_FAILED,
STYLE_UPDATED,
TOUCH_EVENT,
LAST_SIGNAL
};
@@ -589,6 +590,8 @@ static gboolean gtk_widget_real_focus_in_event (GtkWidget *widget,
GdkEventFocus *event);
static gboolean gtk_widget_real_focus_out_event (GtkWidget *widget,
GdkEventFocus *event);
static gboolean gtk_widget_real_touch_event (GtkWidget *widget,
GdkEventTouch *event);
static gboolean gtk_widget_real_focus (GtkWidget *widget,
GtkDirectionType direction);
static void gtk_widget_real_move_focus (GtkWidget *widget,
@@ -705,6 +708,7 @@ static void gtk_widget_set_device_enabled_internal (GtkWidget *widget,
GdkDevice *device,
gboolean recurse,
gboolean enabled);
static gboolean event_window_is_still_viewable (GdkEvent *event);
/* --- variables --- */
static gpointer gtk_widget_parent_class = NULL;
@@ -904,6 +908,7 @@ gtk_widget_class_init (GtkWidgetClass *klass)
klass->button_press_event = NULL;
klass->button_release_event = NULL;
klass->motion_notify_event = NULL;
klass->touch_event = gtk_widget_real_touch_event;
klass->delete_event = NULL;
klass->destroy_event = NULL;
klass->key_press_event = gtk_widget_real_key_press_event;
@@ -1888,6 +1893,15 @@ gtk_widget_class_init (GtkWidgetClass *klass)
G_TYPE_BOOLEAN, 1,
GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
widget_signals[TOUCH_EVENT] =
g_signal_new (I_("touch-event"),
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (GtkWidgetClass, touch_event),
_gtk_boolean_handled_accumulator, NULL,
_gtk_marshal_BOOLEAN__BOXED,
G_TYPE_BOOLEAN, 1,
GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
/**
* GtkWidget::scroll-event:
* @widget: the object which received the signal.
@@ -5832,6 +5846,76 @@ gtk_widget_real_focus_out_event (GtkWidget *widget,
return FALSE;
}
static gboolean
gtk_widget_real_touch_event (GtkWidget *widget,
GdkEventTouch *event)
{
GdkEvent *bevent;
gboolean return_val;
gint signum;
if (!event->emulating_pointer)
return FALSE;
if (event->type == GDK_TOUCH_BEGIN ||
event->type == GDK_TOUCH_END)
{
GdkEventType type;
if (event->type == GDK_TOUCH_BEGIN)
{
type = GDK_BUTTON_PRESS;
signum = BUTTON_PRESS_EVENT;
}
else
{
type = GDK_BUTTON_RELEASE;
signum = BUTTON_RELEASE_EVENT;
}
bevent = gdk_event_new (type);
bevent->any.window = g_object_ref (event->window);
bevent->any.send_event = FALSE;
bevent->button.time = event->time;
bevent->button.state = event->state;
bevent->button.button = 1;
bevent->button.x_root = event->x_root;
bevent->button.y_root = event->y_root;
bevent->button.x = event->x;
bevent->button.y = event->y;
bevent->button.device = event->device;
bevent->button.axes = g_memdup (event->axes,
sizeof (gdouble) * gdk_device_get_n_axes (event->device));
gdk_event_set_source_device (bevent, gdk_event_get_source_device ((GdkEvent*)event));
}
else if (event->type == GDK_TOUCH_UPDATE)
{
signum = MOTION_NOTIFY_EVENT;
bevent = gdk_event_new (GDK_MOTION_NOTIFY);
bevent->any.window = g_object_ref (event->window);
bevent->any.send_event = FALSE;
bevent->motion.time = event->time;
bevent->motion.state = event->state;
bevent->motion.x_root = event->x_root;
bevent->motion.y_root = event->y_root;
bevent->motion.x = event->x;
bevent->motion.y = event->y;
bevent->motion.device = event->device;
bevent->motion.is_hint = FALSE;
bevent->motion.axes = g_memdup (event->axes,
sizeof (gdouble) * gdk_device_get_n_axes (event->device));
gdk_event_set_source_device (bevent, gdk_event_get_source_device ((GdkEvent*)event));
}
else
return FALSE;
g_signal_emit (widget, widget_signals[signum], 0, bevent, &return_val);
gdk_event_free (bevent);
return return_val;
}
#define WIDGET_REALIZED_FOR_EVENT(widget, event) \
(event->type == GDK_FOCUS_CHANGE || gtk_widget_get_realized(widget))
@@ -5870,6 +5954,59 @@ gtk_widget_event (GtkWidget *widget,
return gtk_widget_event_internal (widget, event);
}
void
_gtk_widget_set_captured_event_handler (GtkWidget *widget,
GtkCapturedEventHandler callback)
{
g_object_set_data (G_OBJECT (widget), "captured-event-handler", callback);
}
gboolean
_gtk_widget_captured_event (GtkWidget *widget,
GdkEvent *event)
{
gboolean return_val = FALSE;
GtkCapturedEventHandler handler;
g_return_val_if_fail (GTK_IS_WIDGET (widget), TRUE);
g_return_val_if_fail (WIDGET_REALIZED_FOR_EVENT (widget, event), TRUE);
if (event->type == GDK_EXPOSE)
{
g_warning ("Events of type GDK_EXPOSE cannot be synthesized. To get "
"the same effect, call gdk_window_invalidate_rect/region(), "
"followed by gdk_window_process_updates().");
return TRUE;
}
if (!event_window_is_still_viewable (event))
return TRUE;
handler = g_object_get_data (G_OBJECT (widget), "captured-event-handler");
if (!handler)
return FALSE;
g_object_ref (widget);
return_val = handler (widget, event);
return_val |= !WIDGET_REALIZED_FOR_EVENT (widget, event);
/* The widget that was originally to receive the event
* handles motion hints, but the capturing widget might
* not, so ensure we get further motion events.
*/
if (return_val &&
event->type == GDK_MOTION_NOTIFY &&
event->motion.is_hint &&
(gdk_window_get_events (event->any.window) &
GDK_POINTER_MOTION_HINT_MASK) != 0)
gdk_event_request_motions (&event->motion);
g_object_unref (widget);
return return_val;
}
/* Returns TRUE if a translation should be done */
gboolean
_gtk_widget_get_translation_to_window (GtkWidget *widget,
@@ -6070,6 +6207,12 @@ gtk_widget_event_internal (GtkWidget *widget,
case GDK_2BUTTON_PRESS:
case GDK_3BUTTON_PRESS:
signal_num = BUTTON_PRESS_EVENT;
break;
case GDK_TOUCH_BEGIN:
case GDK_TOUCH_UPDATE:
case GDK_TOUCH_END:
case GDK_TOUCH_CANCEL:
signal_num = TOUCH_EVENT;
break;
case GDK_SCROLL:
signal_num = SCROLL_EVENT;

View File

@@ -425,6 +425,9 @@ struct _GtkWidgetClass
void (* style_updated) (GtkWidget *widget);
gboolean (* touch_event) (GtkWidget *widget,
GdkEventTouch *event);
/*< private >*/
GtkWidgetClassPrivate *priv;
@@ -436,7 +439,6 @@ struct _GtkWidgetClass
void (*_gtk_reserved5) (void);
void (*_gtk_reserved6) (void);
void (*_gtk_reserved7) (void);
void (*_gtk_reserved8) (void);
};
struct _GtkWidgetAuxInfo

View File

@@ -167,6 +167,13 @@ GtkStyle * _gtk_widget_get_style (GtkWidget *widget);
void _gtk_widget_set_style (GtkWidget *widget,
GtkStyle *style);
typedef gboolean (*GtkCapturedEventHandler) (GtkWidget *widget, GdkEvent *event);
void _gtk_widget_set_captured_event_handler (GtkWidget *widget,
GtkCapturedEventHandler handler);
gboolean _gtk_widget_captured_event (GtkWidget *widget,
GdkEvent *event);
G_END_DECLS

View File

@@ -64,6 +64,7 @@ noinst_PROGRAMS = $(TEST_PROGS) \
testicontheme \
testimage \
testinput \
testkineticscrolling \
testlockbutton \
testmenubars \
testmountoperation \
@@ -181,6 +182,7 @@ testgrid_DEPENDENCIES = $(TEST_DEPS)
testgtk_DEPENDENCIES = $(TEST_DEPS)
testinput_DEPENDENCIES = $(TEST_DEPS)
testimage_DEPENDENCIES = $(TEST_DEPS)
testkineticscrolling_DEPENDENCIES = $(TEST_DEPS)
testlockbutton_DEPENDENCIES = $(TEST_DEPS)
testmenubars_DEPENDENCIES = $(TEST_DEPS)
testmountoperation_DEPENDENCIES = $(TEST_DEPS)
@@ -280,6 +282,7 @@ testiconview_LDADD = $(LDADDS)
testiconview_keynav_LDADD = $(LDADDS)
testinput_LDADD = $(LDADDS)
testimage_LDADD = $(LDADDS)
testkineticscrolling_LDADD = $(LDADDS)
testlockbutton_LDADD = $(LDADDS)
testmenubars_LDADD = $(LDADDS)
testmountoperation_LDADD = $(LDADDS)
@@ -524,6 +527,8 @@ testpixbuf_save_SOURCES = testpixbuf-save.c
testcolorchooser_SOURCES = testcolorchooser.c
testkineticscrolling_SOURCES = testkineticscrolling.c
EXTRA_DIST += \
gradient1.png \
prop-editor.h \

View File

@@ -0,0 +1,148 @@
#include <gtk/gtk.h>
enum
{
TARGET_GTK_TREE_MODEL_ROW
};
static GtkTargetEntry row_targets[] =
{
{ "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_APP, TARGET_GTK_TREE_MODEL_ROW }
};
static void
on_button_clicked (GtkWidget *widget, gpointer data)
{
g_print ("Button %d clicked\n", GPOINTER_TO_INT (data));
}
static void
kinetic_scrolling (void)
{
GtkWidget *window, *swindow, *grid;
GtkWidget *label;
GtkWidget *button_grid, *button;
GtkWidget *treeview;
GtkCellRenderer *renderer;
GtkListStore *store;
GtkWidget *textview;
gint i;
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_container_set_border_width (GTK_CONTAINER (window), 5);
gtk_window_set_default_size (GTK_WINDOW (window), 400, 400);
g_signal_connect (window, "delete_event",
G_CALLBACK (gtk_main_quit), NULL);
grid = gtk_grid_new ();
label = gtk_label_new ("Non scrollable widget using viewport");
gtk_grid_attach (GTK_GRID (grid), label, 0, 0, 1, 1);
gtk_widget_set_hexpand (label, TRUE);
gtk_widget_show (label);
label = gtk_label_new ("Scrollable widget: TreeView");
gtk_grid_attach (GTK_GRID (grid), label, 1, 0, 1, 1);
gtk_widget_set_hexpand (label, TRUE);
gtk_widget_show (label);
label = gtk_label_new ("Scrollable widget: TextView");
gtk_grid_attach (GTK_GRID (grid), label, 2, 0, 1, 1);
gtk_widget_set_hexpand (label, TRUE);
gtk_widget_show (label);
button_grid = gtk_grid_new ();
for (i = 0; i < 80; i++)
{
gchar *label = g_strdup_printf ("Button number %d", i);
button = gtk_button_new_with_label (label);
gtk_grid_attach (GTK_GRID (button_grid), button, 0, i, 1, 1);
gtk_widget_set_hexpand (button, TRUE);
gtk_widget_show (button);
g_signal_connect (button, "clicked",
G_CALLBACK (on_button_clicked),
GINT_TO_POINTER (i));
g_free (label);
}
swindow = gtk_scrolled_window_new (NULL, NULL);
gtk_scrolled_window_set_kinetic_scrolling (GTK_SCROLLED_WINDOW (swindow), TRUE);
gtk_scrolled_window_set_capture_button_press (GTK_SCROLLED_WINDOW (swindow), TRUE);
gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (swindow), button_grid);
gtk_widget_show (button_grid);
gtk_grid_attach (GTK_GRID (grid), swindow, 0, 1, 1, 1);
gtk_widget_show (swindow);
treeview = gtk_tree_view_new ();
gtk_tree_view_enable_model_drag_source (GTK_TREE_VIEW (treeview),
GDK_BUTTON1_MASK,
row_targets,
G_N_ELEMENTS (row_targets),
GDK_ACTION_MOVE | GDK_ACTION_COPY);
gtk_tree_view_enable_model_drag_dest (GTK_TREE_VIEW (treeview),
row_targets,
G_N_ELEMENTS (row_targets),
GDK_ACTION_MOVE | GDK_ACTION_COPY);
renderer = gtk_cell_renderer_text_new ();
g_object_set (renderer, "editable", TRUE, NULL);
gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (treeview),
0, "Title",
renderer,
"text", 0,
NULL);
store = gtk_list_store_new (1, G_TYPE_STRING);
for (i = 0; i < 80; i++)
{
GtkTreeIter iter;
gchar *label = g_strdup_printf ("Row number %d", i);
gtk_list_store_append (store, &iter);
gtk_list_store_set (store, &iter, 0, label, -1);
g_free (label);
}
gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), GTK_TREE_MODEL (store));
g_object_unref (store);
swindow = gtk_scrolled_window_new (NULL, NULL);
gtk_scrolled_window_set_kinetic_scrolling (GTK_SCROLLED_WINDOW (swindow), TRUE);
gtk_scrolled_window_set_capture_button_press (GTK_SCROLLED_WINDOW (swindow), TRUE);
gtk_container_add (GTK_CONTAINER (swindow), treeview);
gtk_widget_show (treeview);
gtk_grid_attach (GTK_GRID (grid), swindow, 1, 1, 1, 1);
gtk_widget_set_hexpand (swindow, TRUE);
gtk_widget_set_vexpand (swindow, TRUE);
gtk_widget_show (swindow);
textview = gtk_text_view_new ();
swindow = gtk_scrolled_window_new (NULL, NULL);
gtk_scrolled_window_set_kinetic_scrolling (GTK_SCROLLED_WINDOW (swindow), TRUE);
gtk_scrolled_window_set_capture_button_press (GTK_SCROLLED_WINDOW (swindow), TRUE);
gtk_container_add (GTK_CONTAINER (swindow), textview);
gtk_widget_show (textview);
gtk_grid_attach (GTK_GRID (grid), swindow, 2, 1, 1, 1);
gtk_widget_set_hexpand (swindow, TRUE);
gtk_widget_set_vexpand (swindow, TRUE);
gtk_widget_show (swindow);
gtk_container_add (GTK_CONTAINER (window), grid);
gtk_widget_show (grid);
gtk_widget_show (window);
}
int
main (int argc, char **argv)
{
gtk_init (NULL, NULL);
kinetic_scrolling ();
gtk_main ();
return 0;
}

View File

@@ -53,6 +53,16 @@ content_height_changed (GtkSpinButton *spin_button,
gtk_scrolled_window_set_min_content_height (swindow, (gint)value);
}
static void
kinetic_scrolling_changed (GtkToggleButton *toggle_button,
gpointer data)
{
GtkScrolledWindow *swindow = data;
gboolean enabled = gtk_toggle_button_get_active (toggle_button);
gtk_scrolled_window_set_kinetic_scrolling (swindow, enabled);
}
static void
scrollable_policy (void)
{
@@ -199,6 +209,13 @@ scrollable_policy (void)
g_signal_connect (G_OBJECT (widget), "changed",
G_CALLBACK (label_flip_changed), label);
/* Add Kinetic scrolling control here */
widget = gtk_check_button_new_with_label ("Kinetic scrolling");
gtk_widget_show (widget);
gtk_box_pack_start (GTK_BOX (cntl), widget, TRUE, TRUE, 0);
g_signal_connect (G_OBJECT (widget), "toggled",
G_CALLBACK (kinetic_scrolling_changed), swindow);
gtk_widget_show (window);
}