Compare commits

...

15 Commits

Author SHA1 Message Date
Matthias Clasen
3f27073f55 Use actual drop status
We have this information in the XDND protocol, so we don't have
to blindly assume that the drop succeeded.
2016-01-19 07:13:57 -05:00
Matthias Clasen
79d7f6b415 Typo fix 2016-01-19 07:12:12 -05:00
Matthias Clasen
9daf885dcc Fix remaining references to ::action 2016-01-19 07:11:15 -05:00
Carlos Garnacho
5afe78cf99 gdk: Document the functions that don't need calling on managed DnD
These functions will be automatically called by the windowing backend.
The usual hooks to run this from in gtk/ shouldn't even happen, but
it is worth to document which calls are expected and which aren't.
2016-01-19 00:00:06 +01:00
Carlos Garnacho
be016ad745 wayland: Set weak reference on the current grab window
If the grab window is destroyed the grab will be implicitly removed,
although we won't get GdkSeat:ungrab called in order to clear our
internal window<->seat relation entirely. Setting a weak ref will
nullify the pointer we keep on the seat to the window, avoiding the
expected crashes.
2016-01-19 00:00:06 +01:00
Carlos Garnacho
9038313a1a wayland: Unset button modifiers on pointer enter
Due to implicit grabs, we basically can guarantee that the pointer
won't have any buttons pressed at the time of wl_pointer.enter.
Seems like a good place to unset any button modifiers that might
have been left stale by compositor grabs.
2016-01-19 00:00:06 +01:00
Carlos Garnacho
a7c6d906ed wayland: Implement DnD actions as per wl_data_device v3
Implement as a managed GdkDragContext, which actually fits nicely
with the drag-and-drop model in wayland.
2016-01-19 00:00:06 +01:00
Carlos Garnacho
b7bb9d9025 wayland: Add internal gdk_wayland_seat_set_global_cursor()
This can be used for cases (like DnD) where there isn't necessarily
a grab, but we want a global pointer cursor.
2016-01-19 00:00:06 +01:00
Carlos Garnacho
6fcfc4f7c6 gtkdnd: Reset widget controllers when starting DnD.
Otherwise there's no full guarantees that this will happen.
2016-01-19 00:00:05 +01:00
Carlos Garnacho
c2e043b7d6 gtk: Add private gtk_widget_reset_controllers()
This is a one-shot call that can be called on the places we
know we're taking input away.
2016-01-19 00:00:05 +01:00
Carlos Garnacho
3da9ad48b1 gtkdnd: Optionally use gdk_drag_context_manage_dnd()
When this is in use, there's essentially a bunch of dead code here.
When all backends are ported, we'll be able to remove grab/cursor
management plus a bunch of source-side event handlers.
2016-01-19 00:00:05 +01:00
Carlos Garnacho
3600602fb3 x11: Implement gdk_drag_context_manage_dnd()
This includes managing input events and source-side DND events,
as well as setting the appropriate cursor and emitting the signals
that are expected in this mode of operation.
2016-01-19 00:00:05 +01:00
Carlos Garnacho
6d6bbe6605 gdk: Add gdk_drag_get_cursor()
This function (most similar to gtk_drag_get_cursor() helps figure out
the right cursor that applies to a given action. To be used by the
various backends.
2016-01-19 00:00:05 +01:00
Carlos Garnacho
049cd5f9d6 gdk: Run DnD internal handlers before the main event handler
We'll be stealing those to GTK+, if the GdkDragContext manages
the DnD operation.
2016-01-19 00:00:05 +01:00
Carlos Garnacho
5aee1b5e5a gdk: Allow internal management of source-side DnD
We've traditionally left GTK+ to handle the input side of things,
letting GDK only manage the windowing-specific messaging. This
way of splitting responsibilities is not compatible however with
some backends, we must fold then input management at the DnD stage
into GDK (and backends) domain.

The gdk_drag_context_manage_dnd() call is meant to be the entry
point for this mode of operation, if the drag and drop operation
becomes managed, the caller (i.e. gtkdnd.c) doesn't need to perform
grabs, nor manage input events itself.

As a consequence of this, different aspects now belong to the
backend GdkDragContext implementation:
- Because the caller doesn't see keyboard events anymore,
  keyboard navigation must be managed in GDK, so is the decision
  of the current action based on modifiers/button pressed.
- Because the caller won't see input events in general, the lifetime
  of the drag and drop operation is now communicated through the
  ::drop-performed, ::dnd-finished and ::cancel events
- Because the caller doesn't participate anymore on the action
  being chosen, the pointer cursor must be set by the backend.
  The caller is rather notified of the final action through the
  ::action signal.

The caller is still responsible of dealing with the corresponding
GdkSelection, ensuring its ownership and communicating the supported
mimetypes.
2016-01-19 00:00:05 +01:00
13 changed files with 1301 additions and 167 deletions

View File

@@ -27,7 +27,33 @@
#include "gdkdndprivate.h"
#include "gdkdisplay.h"
#include "gdkwindow.h"
#include "gdkintl.h"
#include "gdkenumtypes.h"
#include "gdkcursor.h"
static struct {
GdkDragAction action;
const gchar *name;
GdkCursor *cursor;
} drag_cursors[] = {
{ GDK_ACTION_DEFAULT, NULL, NULL },
{ GDK_ACTION_ASK, "dnd-ask", NULL },
{ GDK_ACTION_COPY, "dnd-copy", NULL },
{ GDK_ACTION_MOVE, "dnd-move", NULL },
{ GDK_ACTION_LINK, "dnd-link", NULL },
{ 0, "dnd-none", NULL },
};
enum {
CANCEL,
DROP_PERFORMED,
DND_FINISHED,
ACTION_CHANGED,
N_SIGNALS
};
static guint signals[N_SIGNALS] = { 0 };
static GList *contexts = NULL;
/**
* SECTION:dnd
@@ -217,6 +243,7 @@ G_DEFINE_TYPE (GdkDragContext, gdk_drag_context, G_TYPE_OBJECT)
static void
gdk_drag_context_init (GdkDragContext *context)
{
contexts = g_list_prepend (contexts, context);
}
static void
@@ -224,6 +251,7 @@ gdk_drag_context_finalize (GObject *object)
{
GdkDragContext *context = GDK_DRAG_CONTEXT (object);
contexts = g_list_remove (contexts, context);
g_list_free (context->targets);
if (context->source_window)
@@ -241,6 +269,90 @@ gdk_drag_context_class_init (GdkDragContextClass *klass)
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = gdk_drag_context_finalize;
/**
* GdkDragContext::cancel:
*
* The drag and drop operation was cancelled.
*
* This signal will only be emitted if the #GdkDragContext manages
* the drag and drop operation. See gdk_drag_context_manage_dnd()
* for more information.
*
* Since: 3.20
*/
signals[CANCEL] =
g_signal_new (P_("cancel"),
G_TYPE_FROM_CLASS (object_class),
G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET (GdkDragContextClass, cancel),
NULL, NULL,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0);
/**
* GdkDragContext::drop-performed:
* @time: the time at which the drop happened.
*
* The drag and drop operation was performed on an accepting client.
*
* This signal will only be emitted if the #GdkDragContext manages
* the drag and drop operation. See gdk_drag_context_manage_dnd()
* for more information.
*
* Since: 3.20
*/
signals[DROP_PERFORMED] =
g_signal_new (P_("drop-performed"),
G_TYPE_FROM_CLASS (object_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (GdkDragContextClass, drop_performed),
NULL, NULL,
g_cclosure_marshal_VOID__INT,
G_TYPE_NONE, 1, G_TYPE_INT);
/**
* GdkDragContext::dnd-finished:
*
* The drag and drop operation was finished, the drag destination
* finished reading all data. The drag source can now free all
* miscellaneous data.
*
* This signal will only be emitted if the #GdkDragContext manages
* the drag and drop operation. See gdk_drag_context_manage_dnd()
* for more information.
*
* Since: 3.20
*/
signals[DND_FINISHED] =
g_signal_new (P_("dnd-finished"),
G_TYPE_FROM_CLASS (object_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (GdkDragContextClass, dnd_finished),
NULL, NULL,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0);
/**
* GdkDragContext::action-changed:
* @action: The action currently chosen
*
* A new action is being chosen for the drag and drop operation.
*
* This signal will only be emitted if the #GdkDragContext manages
* the drag and drop operation. See gdk_drag_context_manage_dnd()
* for more information.
*
* Since: 3.20
*/
signals[ACTION_CHANGED] =
g_signal_new (P_("action-changed"),
G_TYPE_FROM_CLASS (object_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (GdkDragContextClass, action_changed),
NULL, NULL,
g_cclosure_marshal_VOID__FLAGS,
G_TYPE_NONE, 1, GDK_TYPE_DRAG_ACTION);
}
/**
@@ -316,6 +428,9 @@ gdk_drag_status (GdkDragContext *context,
*
* This function is called by the drag source.
*
* This function does not need to be called in managed drag and drop
* operations. See gdk_drag_context_manage_dnd() for more information.
*
* Returns:
*/
gboolean
@@ -349,6 +464,9 @@ gdk_drag_motion (GdkDragContext *context,
* Aborts a drag without dropping.
*
* This function is called by the drag source.
*
* This function does not need to be called in managed drag and drop
* operations. See gdk_drag_context_manage_dnd() for more information.
*/
void
gdk_drag_abort (GdkDragContext *context,
@@ -367,6 +485,9 @@ gdk_drag_abort (GdkDragContext *context,
* Drops on the current destination.
*
* This function is called by the drag source.
*
* This function does not need to be called in managed drag and drop
* operations. See gdk_drag_context_manage_dnd() for more information.
*/
void
gdk_drag_drop (GdkDragContext *context,
@@ -527,3 +648,105 @@ gdk_drag_drop_done (GdkDragContext *context,
if (GDK_DRAG_CONTEXT_GET_CLASS (context)->drop_done)
GDK_DRAG_CONTEXT_GET_CLASS (context)->drop_done (context, success);
}
/**
* gdk_drag_context_manage_dnd:
* @context: a #GdkDragContext
* @ipc_window: Window to use for IPC messaging/events
* @actions: the actions supported by the drag source
*
* Requests the drag and drop operation to be managed by @context.
* When a drag and drop operation becomes managed, the #GdkDragContext
* will internally handle all input and source-side #GdkEventDND events
* as required by the windowing system.
*
* Once the drag and drop operation is managed, the drag context will
* emit the following signals:
* - The #GdkDragContext::action-changed signal whenever the final action
* to be performed by the drag and drop operation changes.
* - The #GdkDragContext::drop-performed signal after the user performs
* the drag and drop gesture (typically by releasing the mouse button).
* - The #GdkDragContext::dnd-finished signal after the drag and drop
* operation concludes (after all #GdkSelection transfers happen).
* - The #GdkDragContext::cancel signal if the drag and drop operation is
* finished but doesn't happen over an accepting destination, or is
* cancelled through other means.
*
* Returns: #TRUE if the drag and drop operation is managed.
**/
gboolean
gdk_drag_context_manage_dnd (GdkDragContext *context,
GdkWindow *ipc_window,
GdkDragAction actions)
{
g_return_val_if_fail (GDK_IS_DRAG_CONTEXT (context), FALSE);
g_return_val_if_fail (GDK_IS_WINDOW (ipc_window), FALSE);
if (GDK_DRAG_CONTEXT_GET_CLASS (context)->manage_dnd)
return GDK_DRAG_CONTEXT_GET_CLASS (context)->manage_dnd (context, ipc_window,
actions);
return FALSE;
}
void
gdk_drag_context_set_cursor (GdkDragContext *context,
GdkCursor *cursor)
{
g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
if (GDK_DRAG_CONTEXT_GET_CLASS (context)->set_cursor)
GDK_DRAG_CONTEXT_GET_CLASS (context)->set_cursor (context, cursor);
}
void
gdk_drag_context_cancel (GdkDragContext *context)
{
g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
g_signal_emit (context, signals[CANCEL], 0);
}
GList *
gdk_drag_context_list (void)
{
return contexts;
}
gboolean
gdk_drag_context_handle_source_event (GdkEvent *event)
{
GdkDragContext *context;
GList *l;
for (l = contexts; l; l = l->next)
{
context = l->data;
if (!context->is_source)
continue;
if (!GDK_DRAG_CONTEXT_GET_CLASS (context)->handle_event)
continue;
if (GDK_DRAG_CONTEXT_GET_CLASS (context)->handle_event (context, event))
return TRUE;
}
return FALSE;
}
GdkCursor *
gdk_drag_get_cursor (GdkDragAction action)
{
gint i;
for (i = 0 ; i < G_N_ELEMENTS (drag_cursors) - 1; i++)
if (drag_cursors[i].action == action)
break;
if (drag_cursors[i].cursor == NULL)
drag_cursors[i].cursor = gdk_cursor_new_from_name (gdk_display_get_default (),
drag_cursors[i].name);
return drag_cursors[i].cursor;
}

View File

@@ -190,6 +190,10 @@ void gdk_drag_context_set_hotspot (GdkDragContext *context,
gint hot_x,
gint hot_y);
GDK_AVAILABLE_IN_3_20
gboolean gdk_drag_context_manage_dnd (GdkDragContext *context,
GdkWindow *ipc_window,
GdkDragAction actions);
G_END_DECLS
#endif /* __GDK_DND_H__ */

View File

@@ -68,6 +68,21 @@ struct _GdkDragContextClass {
gint hot_y);
void (*drop_done) (GdkDragContext *context,
gboolean success);
gboolean (*manage_dnd) (GdkDragContext *context,
GdkWindow *ipc_window,
GdkDragAction actions);
void (*set_cursor) (GdkDragContext *context,
GdkCursor *cursor);
void (*cancel) (GdkDragContext *context);
void (*drop_performed) (GdkDragContext *context,
guint32 time);
void (*dnd_finished) (GdkDragContext *context);
gboolean (*handle_event) (GdkDragContext *context,
const GdkEvent *event);
void (*action_changed) (GdkDragContext *context,
GdkDragAction action);
};
struct _GdkDragContext {
@@ -91,6 +106,14 @@ struct _GdkDragContext {
GdkDevice *device;
};
GList * gdk_drag_context_list (void);
void gdk_drag_context_set_cursor (GdkDragContext *context,
GdkCursor *cursor);
void gdk_drag_context_cancel (GdkDragContext *context);
gboolean gdk_drag_context_handle_source_event (GdkEvent *event);
GdkCursor * gdk_drag_get_cursor (GdkDragAction action);
G_END_DECLS
#endif

View File

@@ -26,6 +26,7 @@
#include "gdkinternals.h"
#include "gdkdisplayprivate.h"
#include "gdkdndprivate.h"
#include <string.h>
#include <math.h>
@@ -65,6 +66,9 @@ static GDestroyNotify _gdk_event_notify = NULL;
void
_gdk_event_emit (GdkEvent *event)
{
if (gdk_drag_context_handle_source_event (event))
return;
if (_gdk_event_func)
(*_gdk_event_func) (event, _gdk_event_data);
}

View File

@@ -306,7 +306,7 @@ gdk_wayland_device_set_window_cursor (GdkDevice *device,
if (device == wd->touch_master)
return;
if (wd->pointer_grab_window)
if (wd->grab_cursor)
cursor = wd->grab_cursor;
/* Setting the cursor to NULL means that we should use
@@ -883,16 +883,10 @@ data_device_drop (void *data,
struct wl_data_device *data_device)
{
GdkWaylandDeviceData *device = (GdkWaylandDeviceData *) data;
GdkWindow *local_dnd_owner;
GDK_NOTE (EVENTS,
g_message ("data device drop, data device %p", data_device));
local_dnd_owner = gdk_selection_owner_get_for_display (device->display, gdk_drag_get_selection (device->drop_context));
if (local_dnd_owner)
gdk_wayland_device_unset_grab (device->master_pointer);
_gdk_wayland_drag_context_emit_event (device->drop_context,
GDK_DROP_START, GDK_CURRENT_TIME);
}
@@ -1078,6 +1072,8 @@ pointer_handle_enter (void *data,
device->pointer_focus = wl_surface_get_user_data(surface);
g_object_ref(device->pointer_focus);
device->button_modifiers = 0;
device->surface_x = wl_fixed_to_double (sx);
device->surface_y = wl_fixed_to_double (sy);
device->enter_serial = serial;
@@ -2618,6 +2614,27 @@ gdk_wayland_seat_get_capabilities (GdkSeat *seat)
return caps;
}
static void
gdk_wayland_seat_set_grab_window (GdkWaylandSeat *seat,
GdkWindow *window)
{
if (seat->pointer_grab_window)
{
_gdk_wayland_window_set_grab_seat (seat->pointer_grab_window, NULL);
g_object_remove_weak_pointer (G_OBJECT (seat->pointer_grab_window),
(gpointer *) &seat->pointer_grab_window);
seat->pointer_grab_window = NULL;
}
if (window)
{
seat->pointer_grab_window = window;
g_object_add_weak_pointer (G_OBJECT (window),
(gpointer *) &seat->pointer_grab_window);
_gdk_wayland_window_set_grab_seat (window, GDK_SEAT (seat));
}
}
static GdkGrabStatus
gdk_wayland_seat_grab (GdkSeat *seat,
GdkWindow *window,
@@ -2650,16 +2667,15 @@ gdk_wayland_seat_grab (GdkSeat *seat,
if (native == NULL || GDK_WINDOW_DESTROYED (native))
return GDK_GRAB_NOT_VIEWABLE;
wayland_seat->pointer_grab_window = window;
gdk_wayland_seat_set_grab_window (wayland_seat, window);
wayland_seat->pointer_grab_time = evtime;
_gdk_wayland_window_set_grab_seat (window, seat);
if (prepare_func)
(prepare_func) (seat, window, prepare_func_data);
if (!gdk_window_is_visible (window))
{
_gdk_wayland_window_set_grab_seat (window, NULL);
gdk_wayland_seat_set_grab_window (wayland_seat, NULL);
g_critical ("Window %p has not been made visible in GdkSeatGrabPrepareFunc",
window);
return GDK_GRAB_NOT_VIEWABLE;
@@ -2685,7 +2701,7 @@ gdk_wayland_seat_grab (GdkSeat *seat,
evtime,
FALSE);
g_set_object (&wayland_seat->grab_cursor, cursor);
gdk_wayland_seat_set_global_cursor (seat, cursor);
g_set_object (&wayland_seat->cursor, cursor);
gdk_wayland_device_update_window_cursor (wayland_seat);
}
@@ -2744,12 +2760,7 @@ gdk_wayland_seat_ungrab (GdkSeat *seat)
g_clear_object (&wayland_seat->grab_cursor);
if (wayland_seat->pointer_grab_window)
{
_gdk_wayland_window_set_grab_seat (wayland_seat->pointer_grab_window,
NULL);
wayland_seat->pointer_grab_window = NULL;
}
gdk_wayland_seat_set_grab_window (wayland_seat, NULL);
if (wayland_seat->master_pointer)
{
@@ -3066,6 +3077,21 @@ gdk_wayland_device_unset_touch_grab (GdkDevice *gdk_device,
_gdk_wayland_display_deliver_event (device->display, event);
}
void
gdk_wayland_seat_set_global_cursor (GdkSeat *seat,
GdkCursor *cursor)
{
GdkWaylandSeat *wayland_seat = GDK_WAYLAND_SEAT (seat);
GdkDevice *pointer;
pointer = gdk_seat_get_pointer (seat);
g_set_object (&wayland_seat->grab_cursor, cursor);
gdk_wayland_device_set_window_cursor (pointer,
gdk_wayland_device_get_focus (pointer),
NULL);
}
struct wl_data_device *
gdk_wayland_device_get_data_device (GdkDevice *gdk_device)
{
@@ -3090,61 +3116,6 @@ gdk_wayland_device_set_selection (GdkDevice *gdk_device,
_gdk_wayland_display_get_serial (display_wayland));
}
void
gdk_wayland_device_unset_grab (GdkDevice *gdk_device)
{
GdkWaylandDeviceData *device;
GdkEventSequence *sequence;
GdkModifierType state;
GdkEvent *event;
guint button;
gdouble x, y;
device = GDK_WAYLAND_DEVICE (gdk_device)->device;
_gdk_wayland_device_get_last_implicit_grab_serial (GDK_WAYLAND_DEVICE (gdk_device), &sequence);
gdk_window_get_device_position_double (device->pointer_grab_window,
gdk_device, &x, &y, &state);
if (sequence)
{
event = gdk_event_new (GDK_TOUCH_END);
event->touch.window = g_object_ref (device->pointer_grab_window);
event->touch.send_event = TRUE;
event->touch.sequence = sequence;
event->touch.time = GDK_CURRENT_TIME;
event->touch.x = event->touch.x_root = x;
event->touch.y = event->touch.y_root = y;
}
else if (state & (GDK_BUTTON1_MASK | GDK_BUTTON2_MASK | GDK_BUTTON3_MASK))
{
if (state & GDK_BUTTON1_MASK)
button = 1;
else if (state & GDK_BUTTON2_MASK)
button = 2;
else if (state & GDK_BUTTON3_MASK)
button = 3;
else
return;
event = gdk_event_new (GDK_BUTTON_RELEASE);
event->button.window = g_object_ref (device->pointer_grab_window);
event->button.send_event = TRUE;
event->button.button = button;
event->button.time = GDK_CURRENT_TIME;
event->button.x = event->button.x_root = x;
event->button.y = event->button.y_root = y;
}
else
return;
device->button_modifiers = 0;
gdk_event_set_device (event, gdk_device);
gdk_event_set_source_device (event, gdk_device);
gdk_event_set_seat (event, gdk_device_get_seat (gdk_device));
_gdk_wayland_display_deliver_event (gdk_device_get_display (gdk_device), event);
}
struct wl_seat *
gdk_wayland_seat_get_wl_seat (GdkSeat *seat)
{
@@ -3152,3 +3123,11 @@ gdk_wayland_seat_get_wl_seat (GdkSeat *seat)
return GDK_WAYLAND_SEAT (seat)->wl_seat;
}
GdkDragContext *
gdk_wayland_device_get_drop_context (GdkDevice *device)
{
GdkSeat *seat = gdk_device_get_seat (device);
return GDK_WAYLAND_SEAT (seat)->drop_context;
}

View File

@@ -315,7 +315,7 @@ gdk_registry_handle_global (void *data,
else if (strcmp (interface, "wl_data_device_manager") == 0)
{
display_wayland->data_device_manager =
wl_registry_bind (display_wayland->wl_registry, id, &wl_data_device_manager_interface, 1);
wl_registry_bind (display_wayland->wl_registry, id, &wl_data_device_manager_interface, 3);
}
else if (strcmp (interface, "wl_subcompositor") == 0)
{

View File

@@ -24,6 +24,7 @@
#include "gdkproperty.h"
#include "gdkprivate-wayland.h"
#include "gdkdisplay-wayland.h"
#include "gdkseat-wayland.h"
#include "gdkdeviceprivate.h"
@@ -45,6 +46,7 @@ struct _GdkWaylandDragContext
GdkWindow *dnd_window;
struct wl_surface *dnd_surface;
struct wl_data_source *data_source;
GdkDragAction selected_action;
uint32_t serial;
gdouble x;
gdouble y;
@@ -82,6 +84,8 @@ gdk_wayland_drag_context_finalize (GObject *object)
selection_owner = gdk_selection_owner_get_for_display (display, selection);
if (selection_owner == context->source_window)
gdk_wayland_selection_unset_data_source (display, selection);
gdk_drag_context_set_cursor (context, NULL);
}
if (wayland_context->data_source)
@@ -157,6 +161,21 @@ gdk_wayland_drag_context_find_window (GdkDragContext *context,
return NULL;
}
static inline uint32_t
gdk_to_wl_actions (GdkDragAction action)
{
uint32_t dnd_actions = 0;
if (action & (GDK_ACTION_COPY | GDK_ACTION_LINK | GDK_ACTION_PRIVATE))
dnd_actions |= WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY;
if (action & GDK_ACTION_MOVE)
dnd_actions |= WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE;
if (action & GDK_ACTION_ASK)
dnd_actions |= WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK;
return dnd_actions;
}
void
gdk_wayland_drag_context_set_action (GdkDragContext *context,
GdkDragAction action)
@@ -208,6 +227,9 @@ gdk_wayland_drop_context_set_status (GdkDragContext *context,
GdkDisplay *display;
struct wl_data_offer *wl_offer;
if (!context->dest_window)
return;
context_wayland = GDK_WAYLAND_DRAG_CONTEXT (context);
display = gdk_device_get_display (gdk_drag_context_get_device (context));
@@ -240,12 +262,33 @@ gdk_wayland_drop_context_set_status (GdkDragContext *context,
wl_data_offer_accept (wl_offer, context_wayland->serial, NULL);
}
static void
gdk_wayland_drag_context_commit_status (GdkDragContext *context)
{
GdkWaylandDragContext *wayland_context;
GdkDisplay *display;
uint32_t dnd_actions;
wayland_context = GDK_WAYLAND_DRAG_CONTEXT (context);
display = gdk_device_get_display (gdk_drag_context_get_device (context));
dnd_actions = gdk_to_wl_actions (wayland_context->selected_action);
gdk_wayland_selection_set_current_offer_actions (display, dnd_actions);
gdk_wayland_drop_context_set_status (context, wayland_context->selected_action != 0);
}
static void
gdk_wayland_drag_context_drag_status (GdkDragContext *context,
GdkDragAction action,
guint32 time_)
{
gdk_wayland_drop_context_set_status (context, action != 0);
GdkWaylandDragContext *wayland_context;
wayland_context = GDK_WAYLAND_DRAG_CONTEXT (context);
wayland_context->selected_action = action;
gdk_wayland_drag_context_commit_status (context);
}
static void
@@ -263,12 +306,20 @@ gdk_wayland_drag_context_drop_finish (GdkDragContext *context,
guint32 time)
{
GdkDisplay *display = gdk_device_get_display (gdk_drag_context_get_device (context));
GdkWaylandDragContext *wayland_context;
struct wl_data_offer *wl_offer;
GdkAtom selection;
wayland_context = GDK_WAYLAND_DRAG_CONTEXT (context);
selection = gdk_drag_get_selection (context);
wl_offer = gdk_wayland_selection_get_offer (display, selection);
if (gdk_selection_owner_get_for_display (display, selection))
gdk_wayland_selection_unset_data_source (display, selection);
if (success && wayland_context->selected_action &&
wayland_context->selected_action != GDK_ACTION_ASK)
{
gdk_wayland_drag_context_commit_status (context);
wl_data_offer_finish (wl_offer);
}
gdk_wayland_selection_set_offer (display, selection, NULL);
}
@@ -325,6 +376,67 @@ gdk_wayland_drag_context_set_hotspot (GdkDragContext *context,
gdk_window_invalidate_rect (context_wayland->dnd_window, &damage_rect, FALSE);
}
static gboolean
gdk_wayland_drag_context_manage_dnd (GdkDragContext *context,
GdkWindow *ipc_window,
GdkDragAction actions)
{
GdkWaylandDragContext *context_wayland;
GdkWaylandDisplay *display_wayland;
GdkDevice *device;
GdkWindow *toplevel;
device = gdk_drag_context_get_device (context);
display_wayland = GDK_WAYLAND_DISPLAY (gdk_device_get_display (device));
toplevel = _gdk_device_window_at_position (device, NULL, NULL, NULL, TRUE);
context_wayland = GDK_WAYLAND_DRAG_CONTEXT (context);
wl_data_source_set_actions (context_wayland->data_source,
gdk_to_wl_actions (actions));
wl_data_device_start_drag (gdk_wayland_device_get_data_device (device),
context_wayland->data_source,
gdk_wayland_window_get_wl_surface (toplevel),
context_wayland->dnd_surface,
_gdk_wayland_display_get_serial (display_wayland));
gdk_seat_ungrab (gdk_device_get_seat (device));
return TRUE;
}
static void
gdk_wayland_drag_context_set_cursor (GdkDragContext *context,
GdkCursor *cursor)
{
GdkDevice *device = gdk_drag_context_get_device (context);
gdk_wayland_seat_set_global_cursor (gdk_device_get_seat (device), cursor);
}
static void
gdk_wayland_drag_context_action_changed (GdkDragContext *context,
GdkDragAction action)
{
GdkCursor *cursor;
cursor = gdk_drag_get_cursor (action);
gdk_drag_context_set_cursor (context, cursor);
}
static void
gdk_wayland_drag_context_drop_performed (GdkDragContext *context,
guint32 time_)
{
gdk_drag_context_set_cursor (context, NULL);
}
static void
gdk_wayland_drag_context_cancel (GdkDragContext *context)
{
gdk_drag_context_set_cursor (context, NULL);
}
static void
gdk_wayland_drag_context_class_init (GdkWaylandDragContextClass *klass)
{
@@ -344,6 +456,11 @@ gdk_wayland_drag_context_class_init (GdkWaylandDragContextClass *klass)
context_class->get_selection = gdk_wayland_drag_context_get_selection;
context_class->get_drag_window = gdk_wayland_drag_context_get_drag_window;
context_class->set_hotspot = gdk_wayland_drag_context_set_hotspot;
context_class->manage_dnd = gdk_wayland_drag_context_manage_dnd;
context_class->set_cursor = gdk_wayland_drag_context_set_cursor;
context_class->action_changed = gdk_wayland_drag_context_action_changed;
context_class->drop_performed = gdk_wayland_drag_context_drop_performed;
context_class->cancel = gdk_wayland_drag_context_cancel;
}
GdkDragProtocol
@@ -383,13 +500,9 @@ _gdk_wayland_window_drag_begin (GdkWindow *window,
gint y_root)
{
GdkWaylandDragContext *context_wayland;
GdkWaylandDisplay *display_wayland;
GdkDragContext *context;
GdkWindow *toplevel;
GList *l;
toplevel = _gdk_device_window_at_position (device, NULL, NULL, NULL, TRUE);
context_wayland = g_object_new (GDK_TYPE_WAYLAND_DRAG_CONTEXT, NULL);
context = GDK_DRAG_CONTEXT (context_wayland);
context->source_window = g_object_ref (window);
@@ -397,7 +510,6 @@ _gdk_wayland_window_drag_begin (GdkWindow *window,
context->targets = g_list_copy (targets);
gdk_drag_context_set_device (context, device);
display_wayland = GDK_WAYLAND_DISPLAY (gdk_device_get_display (device));
context_wayland->dnd_window = create_dnd_window (gdk_window_get_screen (window));
context_wayland->dnd_surface = gdk_wayland_window_get_wl_surface (context_wayland->dnd_window);
@@ -413,12 +525,6 @@ _gdk_wayland_window_drag_begin (GdkWindow *window,
g_free (mimetype);
}
wl_data_device_start_drag (gdk_wayland_device_get_data_device (device),
context_wayland->data_source,
gdk_wayland_window_get_wl_surface (toplevel),
context_wayland->dnd_surface,
_gdk_wayland_display_get_serial (display_wayland));
return context;
}

View File

@@ -188,7 +188,7 @@ struct wl_data_device * gdk_wayland_device_get_data_device (GdkDevice *gdk_devic
void gdk_wayland_device_set_selection (GdkDevice *gdk_device,
struct wl_data_source *source);
void gdk_wayland_device_unset_grab (GdkDevice *device);
GdkDragContext * gdk_wayland_device_get_drop_context (GdkDevice *gdk_device);
void gdk_wayland_device_unset_touch_grab (GdkDevice *device,
GdkEventSequence *sequence);
@@ -258,10 +258,15 @@ void gdk_wayland_selection_store (GdkWindow *window,
struct wl_data_source * gdk_wayland_selection_get_data_source (GdkWindow *owner,
GdkAtom selection);
void gdk_wayland_selection_unset_data_source (GdkDisplay *display, GdkAtom selection);
gboolean gdk_wayland_selection_set_current_offer_actions (GdkDisplay *display,
uint32_t actions);
EGLSurface gdk_wayland_window_get_egl_surface (GdkWindow *window,
EGLConfig config);
EGLSurface gdk_wayland_window_get_dummy_egl_surface (GdkWindow *window,
EGLConfig config);
void gdk_wayland_seat_set_global_cursor (GdkSeat *seat,
GdkCursor *cursor);
#endif /* __GDK_PRIVATE_WAYLAND_H__ */

View File

@@ -350,8 +350,69 @@ data_offer_offer (void *data,
info->targets = g_list_prepend (info->targets, atom);
}
static inline GdkDragAction
_wl_to_gdk_actions (uint32_t dnd_actions)
{
GdkDragAction actions = 0;
if (dnd_actions & WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY)
actions |= GDK_ACTION_COPY;
if (dnd_actions & WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE)
actions |= GDK_ACTION_MOVE;
if (dnd_actions & WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK)
actions |= GDK_ACTION_ASK;
return actions;
}
static void
data_offer_source_actions (void *data,
struct wl_data_offer *wl_data_offer,
uint32_t source_actions)
{
GdkDragContext *drop_context;
GdkDisplay *display;
GdkDevice *device;
GdkSeat *seat;
display = gdk_display_get_default ();
seat = gdk_display_get_default_seat (display);
device = gdk_seat_get_pointer (seat);
drop_context = gdk_wayland_device_get_drop_context (device);
drop_context->actions = _wl_to_gdk_actions (source_actions);
if (gdk_drag_context_get_dest_window (drop_context))
_gdk_wayland_drag_context_emit_event (drop_context, GDK_DRAG_MOTION,
GDK_CURRENT_TIME);
}
static void
data_offer_action (void *data,
struct wl_data_offer *wl_data_offer,
uint32_t action)
{
GdkDragContext *drop_context;
GdkDisplay *display;
GdkDevice *device;
GdkSeat *seat;
display = gdk_display_get_default ();
seat = gdk_display_get_default_seat (display);
device = gdk_seat_get_pointer (seat);
drop_context = gdk_wayland_device_get_drop_context (device);
drop_context->action = _wl_to_gdk_actions (action);
if (gdk_drag_context_get_dest_window (drop_context))
_gdk_wayland_drag_context_emit_event (drop_context, GDK_DRAG_MOTION,
GDK_CURRENT_TIME);
}
static const struct wl_data_offer_listener data_offer_listener = {
data_offer_offer,
data_offer_source_actions,
data_offer_action
};
DataOfferData *
@@ -569,6 +630,9 @@ gdk_wayland_selection_store (GdkWindow *window,
GdkWaylandSelection *selection = gdk_wayland_display_get_selection (display);
GArray *array;
if (type == gdk_atom_intern_static_string ("NULL"))
return;
array = g_array_new (TRUE, FALSE, sizeof (guchar));
g_array_append_vals (array, data, len);
@@ -701,32 +765,16 @@ data_source_target (void *data,
const char *mime_type)
{
GdkWaylandSelection *wayland_selection = data;
GdkDragContext *context = NULL;
GdkWindow *window = NULL;
g_debug (G_STRLOC ": %s source = %p, mime_type = %s",
G_STRFUNC, source, mime_type);
context = gdk_wayland_drag_context_lookup_by_data_source (source);
if (!mime_type)
{
if (context)
{
gdk_wayland_drag_context_set_action (context, 0);
_gdk_wayland_drag_context_emit_event (context, GDK_DRAG_STATUS,
GDK_CURRENT_TIME);
}
return;
}
return;
if (source == wayland_selection->dnd_source)
{
window = wayland_selection->dnd_owner;
gdk_wayland_drag_context_set_action (context, GDK_ACTION_COPY);
_gdk_wayland_drag_context_emit_event (context, GDK_DRAG_STATUS,
GDK_CURRENT_TIME);
}
window = wayland_selection->dnd_owner;
else if (source == wayland_selection->clipboard_source)
window = wayland_selection->clipboard_owner;
@@ -745,7 +793,6 @@ data_source_send (void *data,
int32_t fd)
{
GdkWaylandSelection *wayland_selection = data;
GdkDragContext *context;
GdkWindow *window;
g_debug (G_STRLOC ": %s source = %p, mime_type = %s, fd = %d",
@@ -757,8 +804,6 @@ data_source_send (void *data,
return;
}
context = gdk_wayland_drag_context_lookup_by_data_source (source);
if (source == wayland_selection->dnd_source)
window = wayland_selection->dnd_owner;
else if (source == wayland_selection->clipboard_source)
@@ -776,13 +821,6 @@ data_source_send (void *data,
gdk_atom_intern (mime_type, FALSE),
fd))
gdk_wayland_selection_check_write (wayland_selection);
if (context)
{
_gdk_wayland_drag_context_emit_event (context, GDK_DROP_FINISHED,
GDK_CURRENT_TIME);
gdk_wayland_device_unset_grab (gdk_drag_context_get_device (context));
}
}
static void
@@ -806,22 +844,79 @@ data_source_cancelled (void *data,
else
return;
gdk_wayland_selection_unset_data_source (display, atom);
context = gdk_wayland_drag_context_lookup_by_data_source (source);
if (context)
gdk_drag_context_cancel (context);
gdk_selection_owner_set (NULL, atom, GDK_CURRENT_TIME, TRUE);
gdk_wayland_selection_unset_data_source (display, atom);
}
if (source == wayland_selection->dnd_source)
static void
data_source_dnd_drop_performed (void *data,
struct wl_data_source *source)
{
GdkDragContext *context;
context = gdk_wayland_drag_context_lookup_by_data_source (source);
if (!context)
return;
g_signal_emit_by_name (context, "drop-performed", GDK_CURRENT_TIME);
}
static void
data_source_dnd_finished (void *data,
struct wl_data_source *source)
{
GdkDisplay *display = gdk_display_get_default ();
GdkDragContext *context;
context = gdk_wayland_drag_context_lookup_by_data_source (source);
if (!context)
return;
if (context->action == GDK_ACTION_MOVE)
{
context = gdk_wayland_drag_context_lookup_by_data_source (source);
if (context)
gdk_wayland_device_unset_grab (gdk_drag_context_get_device (context));
gdk_wayland_selection_emit_request (context->source_window,
atoms[ATOM_DND],
gdk_atom_intern_static_string ("DELETE"));
}
g_signal_emit_by_name (context, "dnd-finished");
gdk_selection_owner_set (NULL, atoms[ATOM_DND], GDK_CURRENT_TIME, TRUE);
gdk_wayland_selection_unset_data_source (display, atoms[ATOM_DND]);
}
static void
data_source_action (void *data,
struct wl_data_source *source,
uint32_t action)
{
GdkDragContext *context;
g_debug (G_STRLOC ": %s source = %p action=%x",
G_STRFUNC, source, action);
context = gdk_wayland_drag_context_lookup_by_data_source (source);
if (!context)
return;
context->action = _wl_to_gdk_actions (action);
g_signal_emit_by_name (context, "action-changed", context->action);
}
static const struct wl_data_source_listener data_source_listener = {
data_source_target,
data_source_send,
data_source_cancelled
data_source_cancelled,
data_source_dnd_drop_performed,
data_source_dnd_finished,
data_source_action,
};
struct wl_data_source *
@@ -1009,7 +1104,7 @@ _gdk_wayland_display_convert_selection (GdkDisplay *display,
offer = gdk_wayland_selection_get_offer (display, selection);
target_list = gdk_wayland_selection_get_targets (display, selection);
if (!offer)
if (!offer || target == gdk_atom_intern_static_string ("DELETE"))
{
GdkEvent *event;
@@ -1185,3 +1280,24 @@ gdk_wayland_selection_clear_targets (GdkDisplay *display,
g_array_set_size (wayland_selection->source_targets, 0);
gdk_wayland_selection_unset_data_source (display, selection);
}
gboolean
gdk_wayland_selection_set_current_offer_actions (GdkDisplay *display,
uint32_t action)
{
struct wl_data_offer *offer;
uint32_t all_actions = 0;
offer = gdk_wayland_selection_get_offer (display, atoms[ATOM_DND]);
if (!offer)
return FALSE;
if (action != 0)
all_actions = WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY |
WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE |
WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK;
wl_data_offer_set_actions (offer, all_actions, action);
return TRUE;
}

View File

@@ -89,6 +89,12 @@ struct _GdkX11DragContext
GdkWindow *drag_window;
GdkWindow *ipc_window;
GdkCursor *cursor;
GdkSeat *grab_seat;
GdkDragAction actions;
GdkDragAction current_action;
gint hot_x;
gint hot_y;
@@ -106,6 +112,35 @@ struct _GdkX11DragContextClass
GdkDragContextClass parent_class;
};
typedef struct {
gint keysym;
gint modifiers;
} GrabKey;
static GrabKey grab_keys[] = {
{ XK_Escape, 0 },
{ XK_space, 0 },
{ XK_KP_Space, 0 },
{ XK_Return, 0 },
{ XK_KP_Enter, 0 },
{ XK_Up, 0 },
{ XK_Up, Mod1Mask },
{ XK_Down, 0 },
{ XK_Down, Mod1Mask },
{ XK_Left, 0 },
{ XK_Left, Mod1Mask },
{ XK_Right, 0 },
{ XK_Right, Mod1Mask },
{ XK_KP_Up, 0 },
{ XK_KP_Up, Mod1Mask },
{ XK_KP_Down, 0 },
{ XK_KP_Down, Mod1Mask },
{ XK_KP_Left, 0 },
{ XK_KP_Left, Mod1Mask },
{ XK_KP_Right, 0 },
{ XK_KP_Right, Mod1Mask }
};
/* Forward declarations */
static GdkWindowCache *gdk_window_cache_get (GdkScreen *screen);
@@ -135,6 +170,11 @@ static void xdnd_manage_source_filter (GdkDragContext *context,
GdkWindow *window,
gboolean add_filter);
gboolean gdk_x11_drag_context_handle_event (GdkDragContext *context,
const GdkEvent *event);
void gdk_x11_drag_context_action_changed (GdkDragContext *context,
GdkDragAction action);
static GList *contexts;
static GSList *window_caches;
@@ -195,6 +235,14 @@ static void gdk_x11_drag_context_set_hotspot (GdkDragContext *context,
gint hot_y);
static void gdk_x11_drag_context_drop_done (GdkDragContext *context,
gboolean success);
static gboolean gdk_x11_drag_context_manage_dnd (GdkDragContext *context,
GdkWindow *window,
GdkDragAction actions);
static void gdk_x11_drag_context_set_cursor (GdkDragContext *context,
GdkCursor *cursor);
static void gdk_x11_drag_context_cancel (GdkDragContext *context);
static void gdk_x11_drag_context_drop_performed (GdkDragContext *context,
guint32 time);
static void
gdk_x11_drag_context_class_init (GdkX11DragContextClass *klass)
@@ -216,6 +264,12 @@ gdk_x11_drag_context_class_init (GdkX11DragContextClass *klass)
context_class->get_drag_window = gdk_x11_drag_context_get_drag_window;
context_class->set_hotspot = gdk_x11_drag_context_set_hotspot;
context_class->drop_done = gdk_x11_drag_context_drop_done;
context_class->manage_dnd = gdk_x11_drag_context_manage_dnd;
context_class->set_cursor = gdk_x11_drag_context_set_cursor;
context_class->cancel = gdk_x11_drag_context_cancel;
context_class->drop_performed = gdk_x11_drag_context_drop_performed;
context_class->handle_event = gdk_x11_drag_context_handle_event;
context_class->action_changed = gdk_x11_drag_context_action_changed;
}
static void
@@ -2592,3 +2646,489 @@ gdk_x11_drag_context_drop_done (GdkDragContext *context,
gdk_drag_anim_timeout, anim,
(GDestroyNotify) gdk_drag_anim_destroy);
}
static gboolean
drag_context_grab (GdkDragContext *context)
{
GdkX11DragContext *x11_context = GDK_X11_DRAG_CONTEXT (context);
GdkDevice *device = gdk_drag_context_get_device (context);
GdkWindow *root;
GdkSeat *seat;
gint keycode, i;
if (!x11_context->ipc_window)
return FALSE;
root = gdk_screen_get_root_window (gdk_window_get_screen (x11_context->ipc_window));
seat = gdk_device_get_seat (gdk_drag_context_get_device (context));
if (gdk_seat_grab (seat, x11_context->ipc_window,
GDK_SEAT_CAPABILITY_ALL, FALSE,
x11_context->cursor, NULL, NULL, NULL) != GDK_GRAB_SUCCESS)
return FALSE;
g_set_object (&x11_context->grab_seat, seat);
gdk_error_trap_push ();
for (i = 0; i < G_N_ELEMENTS (grab_keys); ++i)
{
keycode = XKeysymToKeycode (GDK_WINDOW_XDISPLAY (x11_context->ipc_window),
grab_keys[i].keysym);
if (keycode == NoSymbol)
continue;
#ifdef XINPUT_2
if (GDK_IS_X11_DEVICE_XI2 (device))
{
gint deviceid = gdk_x11_device_get_id (gdk_seat_get_keyboard (seat));
unsigned char mask[XIMaskLen(XI_LASTEVENT)];
XIGrabModifiers mods;
XIEventMask evmask;
gint num_mods;
memset (mask, 0, sizeof (mask));
XISetMask (mask, XI_KeyPress);
XISetMask (mask, XI_KeyRelease);
evmask.deviceid = deviceid;
evmask.mask_len = sizeof (mask);
evmask.mask = mask;
num_mods = 1;
mods.modifiers = grab_keys[i].modifiers;
XIGrabKeycode (GDK_WINDOW_XDISPLAY (x11_context->ipc_window),
deviceid,
keycode,
GDK_WINDOW_XID (root),
GrabModeAsync,
GrabModeAsync,
False,
&evmask,
num_mods,
&mods);
}
else
#endif
{
XGrabKey (GDK_WINDOW_XDISPLAY (x11_context->ipc_window),
keycode, grab_keys[i].modifiers,
GDK_WINDOW_XID (root),
FALSE,
GrabModeAsync,
GrabModeAsync);
}
}
return TRUE;
}
static void
drag_context_ungrab (GdkDragContext *context)
{
GdkX11DragContext *x11_context = GDK_X11_DRAG_CONTEXT (context);
GdkDevice *keyboard;
GdkWindow *root;
gint keycode, i;
if (!x11_context->grab_seat)
return;
gdk_seat_ungrab (x11_context->grab_seat);
keyboard = gdk_seat_get_keyboard (x11_context->grab_seat);
root = gdk_screen_get_root_window (gdk_window_get_screen (x11_context->ipc_window));
g_clear_object (&x11_context->grab_seat);
for (i = 0; i < G_N_ELEMENTS (grab_keys); ++i)
{
keycode = XKeysymToKeycode (GDK_WINDOW_XDISPLAY (x11_context->ipc_window),
grab_keys[i].keysym);
if (keycode == NoSymbol)
continue;
#ifdef XINPUT_2
if (GDK_IS_X11_DEVICE_XI2 (keyboard))
{
XIGrabModifiers mods;
gint num_mods;
num_mods = 1;
mods.modifiers = grab_keys[i].modifiers;
XIUngrabKeycode (GDK_WINDOW_XDISPLAY (x11_context->ipc_window),
gdk_x11_device_get_id (keyboard),
keycode,
GDK_WINDOW_XID (root),
num_mods,
&mods);
}
else
#endif /* XINPUT_2 */
{
XUngrabKey (GDK_WINDOW_XDISPLAY (x11_context->ipc_window),
keycode, grab_keys[i].modifiers,
GDK_WINDOW_XID (root));
}
}
}
static gboolean
gdk_x11_drag_context_manage_dnd (GdkDragContext *context,
GdkWindow *ipc_window,
GdkDragAction actions)
{
GdkX11DragContext *x11_context = GDK_X11_DRAG_CONTEXT (context);
if (x11_context->ipc_window)
return FALSE;
context->protocol = GDK_DRAG_PROTO_XDND;
x11_context->ipc_window = g_object_ref (ipc_window);
if (drag_context_grab (context))
{
x11_context->actions = actions;
return TRUE;
}
else
{
g_clear_object (&x11_context->ipc_window);
return FALSE;
}
}
static void
gdk_x11_drag_context_set_cursor (GdkDragContext *context,
GdkCursor *cursor)
{
GdkX11DragContext *x11_context = GDK_X11_DRAG_CONTEXT (context);
if (!g_set_object (&x11_context->cursor, cursor))
return;
if (x11_context->grab_seat)
{
G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
gdk_device_grab (gdk_seat_get_pointer (x11_context->grab_seat),
x11_context->ipc_window,
GDK_OWNERSHIP_APPLICATION, FALSE,
GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
cursor, GDK_CURRENT_TIME);
G_GNUC_END_IGNORE_DEPRECATIONS;
}
}
static void
gdk_x11_drag_context_cancel (GdkDragContext *context)
{
drag_context_ungrab (context);
gdk_drag_drop_done (context, FALSE);
}
static void
gdk_x11_drag_context_drop_performed (GdkDragContext *context,
guint32 time_)
{
drag_context_ungrab (context);
}
#define BIG_STEP 20
#define SMALL_STEP 1
static void
gdk_drag_get_current_actions (GdkModifierType state,
gint button,
GdkDragAction actions,
GdkDragAction *suggested_action,
GdkDragAction *possible_actions)
{
*suggested_action = 0;
*possible_actions = 0;
if ((button == GDK_BUTTON_MIDDLE || button == GDK_BUTTON_SECONDARY) && (actions & GDK_ACTION_ASK))
{
*suggested_action = GDK_ACTION_ASK;
*possible_actions = actions;
}
else if (state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))
{
if ((state & GDK_SHIFT_MASK) && (state & GDK_CONTROL_MASK))
{
if (actions & GDK_ACTION_LINK)
{
*suggested_action = GDK_ACTION_LINK;
*possible_actions = GDK_ACTION_LINK;
}
}
else if (state & GDK_CONTROL_MASK)
{
if (actions & GDK_ACTION_COPY)
{
*suggested_action = GDK_ACTION_COPY;
*possible_actions = GDK_ACTION_COPY;
}
}
else
{
if (actions & GDK_ACTION_MOVE)
{
*suggested_action = GDK_ACTION_MOVE;
*possible_actions = GDK_ACTION_MOVE;
}
}
}
else
{
*possible_actions = actions;
if ((state & (GDK_MOD1_MASK)) && (actions & GDK_ACTION_ASK))
*suggested_action = GDK_ACTION_ASK;
else if (actions & GDK_ACTION_COPY)
*suggested_action = GDK_ACTION_COPY;
else if (actions & GDK_ACTION_MOVE)
*suggested_action = GDK_ACTION_MOVE;
else if (actions & GDK_ACTION_LINK)
*suggested_action = GDK_ACTION_LINK;
}
}
static void
gdk_drag_update (GdkDragContext *context,
gdouble x_root,
gdouble y_root,
GdkModifierType mods,
guint32 evtime)
{
GdkX11DragContext *x11_context = GDK_X11_DRAG_CONTEXT (context);
GdkDragAction action, possible_actions;
GdkWindow *dest_window;
GdkDragProtocol protocol;
gdk_drag_get_current_actions (mods, GDK_BUTTON_PRIMARY, x11_context->actions,
&action, &possible_actions);
gdk_drag_find_window_for_screen (context,
x11_context->drag_window,
gdk_display_get_default_screen (gdk_display_get_default ()),
x_root, y_root, &dest_window, &protocol);
gdk_drag_motion (context, dest_window, protocol, x_root, y_root,
action, possible_actions, evtime);
}
static gboolean
gdk_dnd_handle_motion_event (GdkDragContext *context,
const GdkEventMotion *event)
{
GdkModifierType state;
if (!gdk_event_get_state ((GdkEvent *) event, &state))
return FALSE;
gdk_drag_update (context, event->x_root, event->y_root, state,
gdk_event_get_time ((GdkEvent *) event));
return TRUE;
}
static gboolean
gdk_dnd_handle_key_event (GdkDragContext *context,
const GdkEventKey *event)
{
GdkX11DragContext *x11_context = GDK_X11_DRAG_CONTEXT (context);
GdkModifierType state;
GdkWindow *root_window;
GdkDevice *pointer;
gint dx, dy;
dx = dy = 0;
state = event->state;
pointer = gdk_device_get_associated_device (gdk_event_get_device ((GdkEvent *) event));
if (event->type == GDK_KEY_PRESS)
{
switch (event->keyval)
{
case GDK_KEY_Escape:
gdk_drag_context_cancel (context);
return TRUE;
case GDK_KEY_space:
case GDK_KEY_Return:
case GDK_KEY_ISO_Enter:
case GDK_KEY_KP_Enter:
case GDK_KEY_KP_Space:
if ((gdk_drag_context_get_selected_action (context) != 0) &&
(gdk_drag_context_get_dest_window (context) != NULL))
{
g_signal_emit_by_name (context, "drop-performed",
gdk_event_get_time ((GdkEvent *) event));
}
else
gdk_drag_context_cancel (context);
return TRUE;
case GDK_KEY_Up:
case GDK_KEY_KP_Up:
dy = (state & GDK_MOD1_MASK) ? -BIG_STEP : -SMALL_STEP;
break;
case GDK_KEY_Down:
case GDK_KEY_KP_Down:
dy = (state & GDK_MOD1_MASK) ? BIG_STEP : SMALL_STEP;
break;
case GDK_KEY_Left:
case GDK_KEY_KP_Left:
dx = (state & GDK_MOD1_MASK) ? -BIG_STEP : -SMALL_STEP;
break;
case GDK_KEY_Right:
case GDK_KEY_KP_Right:
dx = (state & GDK_MOD1_MASK) ? BIG_STEP : SMALL_STEP;
break;
}
}
/* The state is not yet updated in the event, so we need
* to query it here. We could use XGetModifierMapping, but
* that would be overkill.
*/
root_window = gdk_screen_get_root_window (gdk_window_get_screen (x11_context->ipc_window));
gdk_window_get_device_position (root_window, pointer, NULL, NULL, &state);
if (dx != 0 || dy != 0)
{
x11_context->last_x += dx;
x11_context->last_y += dy;
gdk_device_warp (pointer,
gdk_window_get_screen (x11_context->ipc_window),
x11_context->last_x, x11_context->last_y);
}
gdk_drag_update (context, x11_context->last_x, x11_context->last_y, state,
gdk_event_get_time ((GdkEvent *) event));
return TRUE;
}
static gboolean
gdk_dnd_handle_grab_broken_event (GdkDragContext *context,
const GdkEventGrabBroken *event)
{
GdkX11DragContext *x11_context = GDK_X11_DRAG_CONTEXT (context);
/* Don't cancel if we break the implicit grab from the initial button_press.
* Also, don't cancel if we re-grab on the widget or on our IPC window, for
* example, when changing the drag cursor.
*/
if (event->implicit ||
event->grab_window == x11_context->drag_window ||
event->grab_window == x11_context->ipc_window)
return FALSE;
gdk_drag_context_cancel (context);
return TRUE;
}
static gboolean
gdk_dnd_handle_button_event (GdkDragContext *context,
const GdkEventButton *event)
{
#if 0
/* FIXME: Check the button matches */
if (event->button != x11_context->button)
return FALSE;
#endif
if ((gdk_drag_context_get_selected_action (context) != 0) &&
(gdk_drag_context_get_dest_window (context) != NULL))
{
g_signal_emit_by_name (context, "drop-performed",
gdk_event_get_time ((GdkEvent *) event));
}
else
gdk_drag_context_cancel (context);
return TRUE;
}
gboolean
gdk_dnd_handle_drag_status (GdkDragContext *context,
const GdkEventDND *event)
{
GdkX11DragContext *context_x11 = GDK_X11_DRAG_CONTEXT (context);
GdkDragAction action;
if (context != event->context)
return FALSE;
action = gdk_drag_context_get_selected_action (context);
if (action != context_x11->current_action)
{
context_x11->current_action = action;
g_signal_emit_by_name (context, "action-changed", action);
}
return TRUE;
}
static gboolean
gdk_dnd_handle_drop_finished (GdkDragContext *context,
const GdkEventDND *event)
{
GdkX11DragContext *x11_context = GDK_X11_DRAG_CONTEXT (context);
if (context != event->context)
return FALSE;
g_signal_emit_by_name (context, "dnd-finished");
gdk_drag_drop_done (context, !x11_context->drop_failed);
return TRUE;
}
gboolean
gdk_x11_drag_context_handle_event (GdkDragContext *context,
const GdkEvent *event)
{
GdkX11DragContext *x11_context = GDK_X11_DRAG_CONTEXT (context);
if (!context->is_source)
return FALSE;
if (!x11_context->grab_seat && event->type != GDK_DROP_FINISHED)
return FALSE;
switch (event->type)
{
case GDK_MOTION_NOTIFY:
return gdk_dnd_handle_motion_event (context, &event->motion);
case GDK_BUTTON_RELEASE:
return gdk_dnd_handle_button_event (context, &event->button);
case GDK_KEY_PRESS:
case GDK_KEY_RELEASE:
return gdk_dnd_handle_key_event (context, &event->key);
case GDK_GRAB_BROKEN:
return gdk_dnd_handle_grab_broken_event (context, &event->grab_broken);
case GDK_DRAG_STATUS:
return gdk_dnd_handle_drag_status (context, &event->dnd);
case GDK_DROP_FINISHED:
return gdk_dnd_handle_drop_finished (context, &event->dnd);
default:
break;
}
return FALSE;
}
void
gdk_x11_drag_context_action_changed (GdkDragContext *context,
GdkDragAction action)
{
GdkCursor *cursor;
cursor = gdk_drag_get_cursor (action);
gdk_drag_context_set_cursor (context, cursor);
}

View File

@@ -42,6 +42,10 @@
#endif
#endif
#ifdef GDK_WINDOWING_WAYLAND
#include <gdk/wayland/gdkwayland.h>
#endif
#include "gtkgesturedrag.h"
#include "gtkgesturesingle.h"
#include "gtkicontheme.h"
@@ -55,6 +59,7 @@
#include "gtkselectionprivate.h"
#include "gtkwindowgroup.h"
#include "gtkwindowprivate.h"
#include "gtkwidgetprivate.h"
/**
@@ -234,6 +239,17 @@ static void gtk_drag_selection_get (GtkWidget *widget,
gpointer data);
static void gtk_drag_remove_icon (GtkDragSourceInfo *info);
static void gtk_drag_source_info_destroy (GtkDragSourceInfo *info);
static void gtk_drag_context_drop_performed_cb (GdkDragContext *context,
guint time,
GtkDragSourceInfo *info);
static void gtk_drag_context_cancel_cb (GdkDragContext *context,
GtkDragSourceInfo *info);
static void gtk_drag_context_action_cb (GdkDragContext *context,
GdkDragAction action,
GtkDragSourceInfo *info);
static void gtk_drag_context_dnd_finished_cb (GdkDragContext *context,
GtkDragSourceInfo *info);
static void gtk_drag_add_update_idle (GtkDragSourceInfo *info);
static void gtk_drag_update (GtkDragSourceInfo *info,
@@ -2159,6 +2175,17 @@ gtk_drag_begin_internal (GtkWidget *widget,
GdkDevice *pointer, *keyboard;
GdkWindow *ipc_window;
gint start_x, start_y;
GdkAtom selection;
gboolean managed = FALSE;
managed =
#ifdef GDK_WINDOWING_X11
GDK_IS_X11_DISPLAY (gtk_widget_get_display (widget)) ||
#endif
#ifdef GDK_WINDOWING_WAYLAND
GDK_IS_WAYLAND_DISPLAY (gtk_widget_get_display (widget)) ||
#endif
FALSE;
pointer = keyboard = NULL;
ipc_widget = gtk_drag_get_ipc_widget (widget);
@@ -2201,24 +2228,27 @@ gtk_drag_begin_internal (GtkWidget *widget,
ipc_window = gtk_widget_get_window (ipc_widget);
if (gdk_device_grab (pointer, ipc_window,
GDK_OWNERSHIP_APPLICATION, FALSE,
GDK_POINTER_MOTION_MASK |
GDK_BUTTON_RELEASE_MASK,
cursor, time) != GDK_GRAB_SUCCESS)
if (!managed)
{
gtk_drag_release_ipc_widget (ipc_widget);
return NULL;
if (gdk_device_grab (pointer, ipc_window,
GDK_OWNERSHIP_APPLICATION, FALSE,
GDK_POINTER_MOTION_MASK |
GDK_BUTTON_RELEASE_MASK,
cursor, time) != GDK_GRAB_SUCCESS)
{
gtk_drag_release_ipc_widget (ipc_widget);
return NULL;
}
if (keyboard)
grab_dnd_keys (ipc_widget, keyboard, time);
/* We use a GTK grab here to override any grabs that the widget
* we are dragging from might have held
*/
gtk_device_grab_add (ipc_widget, pointer, FALSE);
}
if (keyboard)
grab_dnd_keys (ipc_widget, keyboard, time);
/* We use a GTK grab here to override any grabs that the widget
* we are dragging from might have held
*/
gtk_device_grab_add (ipc_widget, pointer, FALSE);
tmp_list = g_list_last (target_list->list);
while (tmp_list)
{
@@ -2250,6 +2280,14 @@ gtk_drag_begin_internal (GtkWidget *widget,
gdk_drag_context_set_device (context, pointer);
g_list_free (targets);
if (managed &&
!gdk_drag_context_manage_dnd (context, ipc_window, actions))
{
gtk_drag_release_ipc_widget (ipc_widget);
g_object_unref (context);
return NULL;
}
info = gtk_drag_get_source_info (context, TRUE);
@@ -2280,6 +2318,8 @@ gtk_drag_begin_internal (GtkWidget *widget,
info->start_x = start_x;
info->start_y = start_y;
gtk_widget_reset_controllers (widget);
g_signal_emit_by_name (widget, "drag-begin", info->context);
/* Ensure that we have an icon before we start the drag; the
@@ -2300,26 +2340,45 @@ gtk_drag_begin_internal (GtkWidget *widget,
}
}
info->cur_x = info->start_x;
info->cur_y = info->start_y;
if (managed)
{
g_signal_connect (context, "drop-performed",
G_CALLBACK (gtk_drag_context_drop_performed_cb), info);
g_signal_connect (context, "dnd-finished",
G_CALLBACK (gtk_drag_context_dnd_finished_cb), info);
g_signal_connect (context, "cancel",
G_CALLBACK (gtk_drag_context_cancel_cb), info);
g_signal_connect (context, "action-changed",
G_CALLBACK (gtk_drag_context_action_cb), info);
if (event && event->type == GDK_MOTION_NOTIFY)
gtk_drag_motion_cb (info->ipc_widget, (GdkEventMotion *)event, info);
selection = gdk_drag_get_selection (context);
if (selection)
gtk_drag_source_check_selection (info, selection, time);
}
else
gtk_drag_update (info, info->cur_screen, info->cur_x, info->cur_y, event);
{
info->cur_x = info->start_x;
info->cur_y = info->start_y;
if (event && event->type == GDK_MOTION_NOTIFY)
gtk_drag_motion_cb (info->ipc_widget, (GdkEventMotion *)event, info);
else
gtk_drag_update (info, info->cur_screen, info->cur_x, info->cur_y, event);
g_signal_connect (info->ipc_widget, "grab-broken-event",
G_CALLBACK (gtk_drag_grab_broken_event_cb), info);
g_signal_connect (info->ipc_widget, "grab-notify",
G_CALLBACK (gtk_drag_grab_notify_cb), info);
g_signal_connect (info->ipc_widget, "button-release-event",
G_CALLBACK (gtk_drag_button_release_cb), info);
g_signal_connect (info->ipc_widget, "motion-notify-event",
G_CALLBACK (gtk_drag_motion_cb), info);
g_signal_connect (info->ipc_widget, "key-press-event",
G_CALLBACK (gtk_drag_key_cb), info);
g_signal_connect (info->ipc_widget, "key-release-event",
G_CALLBACK (gtk_drag_key_cb), info);
}
g_signal_connect (info->ipc_widget, "grab-broken-event",
G_CALLBACK (gtk_drag_grab_broken_event_cb), info);
g_signal_connect (info->ipc_widget, "grab-notify",
G_CALLBACK (gtk_drag_grab_notify_cb), info);
g_signal_connect (info->ipc_widget, "button-release-event",
G_CALLBACK (gtk_drag_button_release_cb), info);
g_signal_connect (info->ipc_widget, "motion-notify-event",
G_CALLBACK (gtk_drag_motion_cb), info);
g_signal_connect (info->ipc_widget, "key-press-event",
G_CALLBACK (gtk_drag_key_cb), info);
g_signal_connect (info->ipc_widget, "key-release-event",
G_CALLBACK (gtk_drag_key_cb), info);
g_signal_connect (info->ipc_widget, "selection-get",
G_CALLBACK (gtk_drag_selection_get), info);
@@ -3351,6 +3410,74 @@ gtk_drag_cancel_internal (GtkDragSourceInfo *info,
gtk_drag_drop_finished (info, result, time);
}
static void
gtk_drag_context_drop_performed_cb (GdkDragContext *context,
guint32 time_,
GtkDragSourceInfo *info)
{
gtk_drag_end (info, time_);
gtk_drag_drop (info, time_);
}
static void
gtk_drag_context_cancel_cb (GdkDragContext *context,
GtkDragSourceInfo *info)
{
gtk_drag_cancel_internal (info, GTK_DRAG_RESULT_ERROR, GDK_CURRENT_TIME);
}
static void
gtk_drag_context_action_cb (GdkDragContext *context,
GdkDragAction action,
GtkDragSourceInfo *info)
{
if (info->proxy_dest)
{
if (info->proxy_dest->proxy_drop_wait)
{
gboolean result = gdk_drag_context_get_selected_action (context) != 0;
/* Aha - we can finally pass the DROP on... */
gdk_drop_reply (info->proxy_dest->context, result, info->proxy_dest->proxy_drop_time);
if (result)
gdk_drag_drop (info->context, info->proxy_dest->proxy_drop_time);
else
gtk_drag_finish (info->proxy_dest->context, FALSE, FALSE, info->proxy_dest->proxy_drop_time);
}
else
{
gdk_drag_status (info->proxy_dest->context,
gdk_drag_context_get_selected_action (context),
GDK_CURRENT_TIME);
}
g_signal_stop_emission_by_name (context, "action");
}
}
static void
gtk_drag_context_dnd_finished_cb (GdkDragContext *context,
GtkDragSourceInfo *info)
{
gtk_drag_source_release_selections (info, GDK_CURRENT_TIME);
if (info->proxy_dest)
{
/* The time from the event isn't reliable for Xdnd drags */
gtk_drag_finish (info->proxy_dest->context, TRUE, FALSE,
info->proxy_dest->proxy_drop_time);
}
if (gdk_drag_context_get_selected_action (context) == GDK_ACTION_MOVE)
{
g_signal_emit_by_name (info->widget,
"drag-data-delete",
context);
}
gtk_drag_source_info_destroy (info);
}
/* “motion-notify-event” callback during drag. */
static gboolean
gtk_drag_motion_cb (GtkWidget *widget,

View File

@@ -13006,17 +13006,7 @@ gtk_widget_propagate_state (GtkWidget *widget,
}
if (!gtk_widget_is_sensitive (widget))
{
EventControllerData *controller_data;
GList *l;
/* Reset all controllers */
for (l = priv->event_controllers; l; l = l->next)
{
controller_data = l->data;
gtk_event_controller_reset (controller_data->controller);
}
}
gtk_widget_reset_controllers (widget);
if (GTK_IS_CONTAINER (widget))
{
@@ -17596,3 +17586,18 @@ _gtk_widget_consumes_motion (GtkWidget *widget,
return FALSE;
}
void
gtk_widget_reset_controllers (GtkWidget *widget)
{
EventControllerData *controller_data;
GtkWidgetPrivate *priv = widget->priv;
GList *l;
/* Reset all controllers */
for (l = priv->event_controllers; l; l = l->next)
{
controller_data = l->data;
gtk_event_controller_reset (controller_data->controller);
}
}

View File

@@ -294,6 +294,8 @@ void gtk_widget_set_csd_input_shape (GtkWidget
gboolean gtk_widget_has_size_request (GtkWidget *widget);
void gtk_widget_reset_controllers (GtkWidget *widget);
/* inline getters */
static inline gboolean