Compare commits

...

44 Commits

Author SHA1 Message Date
Carlos Garnacho
79adbda38f 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-03-01 15:18:28 -05:00
Carlos Garnacho
d9af0a5f75 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-03-01 15:18:28 -05:00
Carlos Garnacho
f115a81b50 gdk: Set correct GdkModifierType on pointer emulated events 2012-03-01 15:18:28 -05:00
Carlos Garnacho
7a7108ba1a gdk: translate correctly from touch events into emulated pointer events 2012-03-01 15:18:27 -05:00
Matthias Clasen
d6c1fd4ed4 gdk: Don't treat touch events as button events
One more place where we assumed that touch events have a button
field.
2012-03-01 15:18:27 -05:00
Matthias Clasen
a32dee7680 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-03-01 15:18:27 -05:00
Carlos Garnacho
0eb89c7109 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-03-01 15:18:27 -05:00
Carlos Garnacho
688a974570 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-03-01 15:18:26 -05:00
Carlos Garnacho
f12f0b3e3a 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-03-01 15:18:26 -05:00
Carlos Garnacho
71a8c8007b 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-03-01 15:18:26 -05:00
Carlos Garnacho
8ac0f73f03 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-03-01 15:18:26 -05:00
Carlos Garnacho
22c4b27fec 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-03-01 15:18:25 -05:00
Carlos Garnacho
8e9f616697 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.
2012-03-01 15:18:23 -05:00
Carlos Garnacho
07b38f4658 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-03-01 15:03:01 -05:00
Carlos Garnacho
16c6d9ce94 gdk: Don't change window_under_pointer for pure touch events
Only touch events that emulate the pointer do change it.
2012-03-01 15:03:01 -05:00
Carlos Garnacho
5d10309214 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-03-01 15:03:01 -05:00
Carlos Garnacho
f18d8370f9 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-03-01 15:03:01 -05:00
Carlos Garnacho
bfa2d55030 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-03-01 15:03:00 -05:00
Carlos Garnacho
b7fbb544dc settings: Deprecate gtk-touchscreen-mode
It's not used anywhere in GTK+ anymore.
2012-03-01 15:03:00 -05:00
Carlos Garnacho
592ae129c3 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-03-01 15:03:00 -05:00
Carlos Garnacho
cab579a11f 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-03-01 15:03:00 -05:00
Carlos Garnacho
bcf6d9d924 menushell: Remove gtk-touchscreen-mode usage
This usage in a keybinding signal is hardly related to touchscreens,
so just remove it.
2012-03-01 15:03:00 -05:00
Carlos Garnacho
8f4fd88528 menus: Remove gtk-touchscreen-mode from scrolling code
Scrolling is handled via ::captured-event dynamically, so remove
this now unused code.
2012-03-01 15:02:59 -05:00
Carlos Garnacho
cb09e7d38b 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-03-01 15:02:59 -05:00
Carlos Garnacho
e4c52a8038 menus: Implement scrolling through event capture 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-03-01 15:02:59 -05:00
Carlos Garnacho
73093655e5 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-03-01 15:02:58 -05:00
Carlos Garnacho
c569438e53 tooltips: Use the source device instead of gtk-touchscreen-mode
This makes tooltips behavior dynamic based on the interacting device.
2012-03-01 15:02:58 -05:00
Carlos Garcia Campos
0c61976c74 tests: Add new test for kinetic scrolling 2012-03-01 15:02:58 -05:00
Carlos Garcia Campos
6dc8f5b840 tests: Add checkbox to enable kinetic scrolling in scrolled window test 2012-03-01 15:02:58 -05:00
Matthias Clasen
139a27a96f 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-03-01 15:02:57 -05:00
Carlos Garcia Campos
6840d3eda0 scrolledwindow: 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.

If the scrolling doesn't start after a long press, the scrolling is
cancelled and events are handled by child widgets normally.

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.

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-03-01 15:02:57 -05:00
Carlos Garnacho
4ff34b7ca3 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_BEGIN/END are handled, and those are sent
around touch begin/end. 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-03-01 15:02:57 -05:00
Carlos Garcia Campos
edebccd1ad gtk: Add a way to do event capture
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
 and then back up. The second phase is using the existing
::event signal, the new capture phase is using a private
API instead of a public signal for now.

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

We automatically request 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.

We are not making event capture part of the public API for 3.4,
which is why there is no ::captured-event signal.
2012-03-01 15:02:57 -05:00
Matthias Clasen
b411a7e9fc gtk: translate unhandled touch events to button events
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-03-01 15:02:56 -05:00
Matthias Clasen
a74b3356b3 gtk: Add a separate ::touch-event signal 2012-03-01 15:02:56 -05:00
Matthias Clasen
3d1550be51 gdk: Add some debug output for touch events and devices 2012-03-01 15:02:56 -05:00
Matthias Clasen
4276ff89b9 xi2: Translate touch events
Translate XI_TouchBegin/Update/End to GDK_TOUCH_BEGIN/UPDATE/END
events.

At the same time,
set pointer-emulated flags on button events with XIPointerEmulated
and on touch events emulating the pointer.
2012-03-01 02:12:22 -05:00
Carlos Garnacho
2a2fe61d46 gdk: Add touch event types and mask
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-03-01 02:12:16 -05:00
Carlos Garnacho
5b01c7e691 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-03-01 01:34:02 -05:00
Carlos Garnacho
73fc7c7fe5 gdk: Add GdkEventSequence
GdkEventSequence is an opaque pointer type that is used
to identify sequences of touch events that belong together.
2012-03-01 01:19:24 -05:00
Matthias Clasen
d4c7234e70 xi2: Use the new device types for touch-capable devices
Any device with a XITouchClassInfo with num_touches > 0
qualifies as multitouch.
2012-02-29 22:28:19 -05:00
Matthias Clasen
f6c2d05cfe gdk: Add device types for touch-capable devices
We introduce GDK_SOURCE_TOUCHSCREEN and GDK_SOURCE_TOUCHPAD
for direct and indirect touch devices, respecively. These
correspond to XIDirectTouch and XIDependentTouch in XI2.
2012-02-29 22:23:00 -05:00
Carlos Garnacho
884a52fa7f configure: Detect XInput 2.2 2012-02-29 21:28:13 -05:00
Carlos Garnacho
51293f7137 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-29 21:15:25 -05:00
39 changed files with 3431 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

@@ -790,6 +790,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
@@ -827,6 +829,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

@@ -59,6 +59,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.
*/
@@ -68,7 +72,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

@@ -186,6 +186,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);
@@ -234,6 +235,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);
@@ -692,6 +695,73 @@ _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 */
@@ -895,15 +965,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,
@@ -962,12 +1043,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);
@@ -1026,6 +1110,33 @@ _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

@@ -58,6 +58,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,
@@ -74,6 +87,7 @@ typedef struct
guint32 state;
guint32 button;
GdkDevice *last_slave;
guint need_touch_press_enter : 1;
} GdkPointerWindowInfo;
typedef struct
@@ -102,6 +116,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;
@@ -259,6 +274,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

@@ -457,6 +457,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.;
@@ -485,7 +494,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
@@ -561,6 +594,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,
@@ -639,7 +681,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)
@@ -700,6 +749,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:
@@ -775,7 +829,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;
@@ -866,6 +926,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;
@@ -920,6 +987,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;
@@ -1160,7 +1234,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;
@@ -1173,6 +1247,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;
@@ -1196,6 +1277,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;
@@ -1241,6 +1330,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;
@@ -1286,6 +1381,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:
@@ -1303,6 +1403,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:
@@ -1684,6 +1788,33 @@ gdk_event_get_screen (const GdkEvent *event)
return NULL;
}
/**
* gdk_event_get_event_sequence:
* @event: a #GdkEvent
*
* If @event if of type %GDK_TOUCH_BEGIN, %GDK_TOUCH_UPDATE,
* %GDK_TOUCH_END or %GDK_TOUCH_CANCEL, returns the #GdkEventSequence
* to which the event belongs. Otherwise, return %NULL.
*
* Returns: the event sequence that the event belongs to
*
* 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;
return NULL;
}
/**
* gdk_set_show_events:
* @show_events: %TRUE to output event debugging information.

View File

@@ -129,6 +129,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;
@@ -143,6 +144,8 @@ typedef struct _GdkEventWindowState GdkEventWindowState;
typedef struct _GdkEventSetting GdkEventSetting;
typedef struct _GdkEventGrabBroken GdkEventGrabBroken;
typedef struct _GdkEventSequence GdkEventSequence;
typedef union _GdkEvent GdkEvent;
/**
@@ -262,6 +265,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.
@@ -309,6 +320,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;
@@ -384,6 +399,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_BEGIN: crossing because a touch sequence has begun,
* this event is synthetic as the pointer might have not left the window.
* @GDK_CROSSING_TOUCH_END: crossing because a touch sequence has ended,
* 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.
*/
@@ -394,7 +416,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_BEGIN,
GDK_CROSSING_TOUCH_END,
GDK_CROSSING_DEVICE_SWITCH
} GdkCrossingMode;
/**
@@ -596,7 +621,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:
@@ -644,6 +669,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).
@@ -1072,6 +1148,7 @@ union _GdkEvent
GdkEventVisibility visibility;
GdkEventMotion motion;
GdkEventButton button;
GdkEventTouch touch;
GdkEventScroll scroll;
GdkEventKey key;
GdkEventCrossing crossing;
@@ -1155,6 +1232,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

@@ -148,7 +148,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
@@ -273,6 +279,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,

View File

@@ -348,6 +348,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.
@@ -363,6 +364,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
{
@@ -387,7 +395,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

@@ -1243,11 +1243,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;
}
@@ -8117,6 +8118,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);
@@ -8148,6 +8153,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;
}
@@ -8155,10 +8162,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)
@@ -8231,6 +8248,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;
@@ -8319,12 +8345,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)
@@ -8334,7 +8375,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_BEGIN &&
mode != GDK_CROSSING_TOUCH_END)
{
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);
@@ -9082,17 +9130,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)
{
@@ -9101,7 +9186,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;
@@ -9116,7 +9201,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;
@@ -9132,7 +9217,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;
@@ -9158,8 +9243,10 @@ proxy_pointer_event (GdkDisplay *display,
guint state;
gdouble toplevel_x, toplevel_y;
guint32 time_;
gboolean non_linear;
gboolean non_linear, need_synthetic_enter = FALSE;
gint event_type;
event_type = source_event->type;
event_window = source_event->any.window;
gdk_event_get_coords (source_event, &toplevel_x, &toplevel_y);
gdk_event_get_state (source_event, &state);
@@ -9178,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
@@ -9265,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 */
@@ -9282,28 +9381,71 @@ 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;
sequence = gdk_event_get_event_sequence (source_event);
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;
@@ -9321,22 +9463,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.
@@ -9359,6 +9537,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;
@@ -9366,6 +9546,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;
@@ -9379,9 +9561,17 @@ proxy_button_event (GdkEvent *source_event,
toplevel_x, toplevel_y,
&toplevel_x, &toplevel_y);
if (type == GDK_BUTTON_PRESS &&
sequence = gdk_event_get_event_sequence (source_event);
pointer_info = _gdk_display_get_pointer_info (display, device);
pointer_grab = _gdk_display_has_device_grab (display, device, serial);
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,
@@ -9394,23 +9584,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,
@@ -9419,9 +9632,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;
@@ -9430,6 +9666,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_BEGIN;
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)
@@ -9440,17 +9703,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;
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));
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);
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 ||
(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_END,
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_END,
toplevel_x, toplevel_y,
state, time_, NULL,
serial, FALSE);
}
return TRUE;
case GDK_SCROLL:
@@ -9680,7 +10011,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;
@@ -9697,6 +10030,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 ||
@@ -9710,16 +10046,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

@@ -386,6 +386,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;
@@ -393,6 +394,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 */
@@ -407,7 +409,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)
@@ -623,10 +627,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),
@@ -636,10 +647,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);
@@ -688,6 +703,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

@@ -52,7 +52,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)
@@ -64,6 +68,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

@@ -27,6 +27,7 @@
#include "gdkprivate-x11.h"
#include "gdkintl.h"
#include "gdkkeysyms.h"
#include "gdkinternals.h"
#ifdef XINPUT_2
@@ -47,6 +48,8 @@ struct _GdkX11DeviceManagerXI2
GList *devices;
gint opcode;
gint major;
gint minor;
};
struct _GdkX11DeviceManagerXI2Class
@@ -94,7 +97,9 @@ static GdkWindow * gdk_x11_device_manager_xi2_get_window (GdkEventTra
enum {
PROP_0,
PROP_OPCODE
PROP_OPCODE,
PROP_MAJOR,
PROP_MINOR
};
static void
@@ -118,6 +123,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
@@ -146,8 +165,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 };
@@ -168,24 +189,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
@@ -194,7 +210,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));
@@ -216,10 +232,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 */
@@ -230,18 +250,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;
@@ -252,6 +312,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;
@@ -280,6 +344,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,
@@ -406,6 +484,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);
@@ -531,6 +611,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;
@@ -552,6 +638,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;
@@ -821,6 +913,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;
@@ -1045,51 +1142,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;
@@ -1122,9 +1207,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;
@@ -1139,15 +1228,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;
@@ -1163,6 +1251,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;
@@ -1182,6 +1273,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:
{
@@ -1278,7 +1500,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
@@ -1292,7 +1515,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

@@ -245,8 +245,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

@@ -122,6 +122,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);
@@ -207,6 +209,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;
@@ -1429,9 +1432,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;
@@ -1841,6 +1845,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)
@@ -1930,6 +1956,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)
{
@@ -1942,7 +2002,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

@@ -1339,6 +1339,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:
@@ -1384,6 +1392,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);
@@ -1471,6 +1483,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;
@@ -1530,6 +1543,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.
@@ -1626,14 +1647,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:
@@ -1682,7 +1706,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:
@@ -1691,7 +1719,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;
@@ -1701,7 +1730,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;
@@ -1729,6 +1759,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);
@@ -2331,6 +2362,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
@@ -2359,79 +2519,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

@@ -108,8 +108,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"
@@ -225,12 +227,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);
@@ -1064,9 +1067,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
@@ -1472,7 +1478,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));
@@ -1609,6 +1615,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
@@ -1678,17 +1685,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);
@@ -3323,34 +3322,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,
@@ -3387,13 +3358,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
@@ -3402,10 +3376,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);
}
@@ -3424,11 +3404,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()).
*/
@@ -3672,10 +3647,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;
@@ -3839,90 +3818,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)
@@ -4046,14 +3952,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;
@@ -4071,82 +3972,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)
@@ -4181,82 +4044,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)
@@ -4286,19 +4111,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);
@@ -4307,7 +4131,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);
@@ -4364,6 +4189,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 ||
@@ -4376,7 +4202,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);
@@ -4411,6 +4240,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)
{
@@ -4853,19 +4818,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
@@ -5671,7 +5627,6 @@ gtk_menu_real_move_scroll (GtkMenu *menu,
}
}
/**
* gtk_menu_set_monitor:
* @menu: a #GtkMenu
@@ -5748,11 +5703,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 ||
@@ -5769,6 +5726,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

@@ -1688,13 +1688,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

@@ -99,6 +99,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;
@@ -125,6 +127,9 @@ struct _GtkMenuPrivate
gint navigation_height;
guint navigation_timeout;
gdouble drag_start_y;
gint initial_drag_offset;
};
G_END_DECLS

View File

@@ -1083,13 +1083,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);
}
}
@@ -1611,45 +1609,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

@@ -34,6 +34,8 @@
#include "gtktypebuiltins.h"
#include "gtkprivate.h"
#include "gtkintl.h"
#include "gtkdnd.h"
#include "gtkwidgetprivate.h"
#include "a11y/gtkpanedaccessible.h"
/**
@@ -96,6 +98,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;
@@ -122,6 +135,8 @@ struct _GtkPanedPrivate
guint32 grab_time;
GArray *touches;
guint handle_prelit : 1;
guint in_drag : 1;
guint in_recursion : 1;
@@ -253,6 +268,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,
@@ -706,6 +723,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
@@ -861,9 +883,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);
}
@@ -1279,7 +1303,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;
@@ -1569,7 +1595,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
{
@@ -1591,7 +1618,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
{
@@ -1624,6 +1652,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)
@@ -1634,27 +1694,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;
}
@@ -1668,9 +1711,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;
}
@@ -1685,7 +1732,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;
}
@@ -1760,6 +1807,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

@@ -72,6 +72,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

@@ -2007,15 +2007,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))
@@ -2277,7 +2273,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)
@@ -2307,28 +2303,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;
@@ -2526,7 +2522,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);
@@ -2536,6 +2533,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;
@@ -2552,23 +2552,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;
@@ -2603,17 +2604,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

@@ -115,6 +115,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

@@ -700,13 +700,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

@@ -656,13 +656,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 |
@@ -678,7 +674,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

@@ -1535,22 +1535,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
@@ -1562,7 +1573,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

@@ -682,7 +682,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

@@ -482,6 +482,7 @@ enum {
QUERY_TOOLTIP,
DRAG_FAILED,
STYLE_UPDATED,
TOUCH_EVENT,
LAST_SIGNAL
};
@@ -587,6 +588,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,
@@ -703,6 +706,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;
@@ -902,6 +906,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;
@@ -1886,6 +1891,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.
@@ -5830,6 +5844,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))
@@ -5868,6 +5952,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,
@@ -6068,6 +6205,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

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

View File

@@ -165,6 +165,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 \
@@ -182,6 +183,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)
@@ -282,6 +284,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)
@@ -530,6 +533,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);
}