[gnome-shell] Implement cross overview drag & drop
- From: Adel Gadllah <agadllah src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-shell] Implement cross overview drag & drop
- Date: Wed, 5 Jan 2011 22:20:08 +0000 (UTC)
commit ceedc7e32c4f993574378ddc532465ef2729337a
Author: Adel Gadllah <adel gadllah gmail com>
Date: Wed Jan 5 15:47:27 2011 +0100
Implement cross overview drag & drop
The gnome-panel allows the user to hover over a tasklist entry
while draging to activate a minimized or obscured window and drop onto it.
Implement a similar behaviour by allowing draging to the activities button or
the hotcorner (and thus opening the overview), which allows the user to
activate any window (even on different workspaces) as a drop target.
https://bugzilla.gnome.org/show_bug.cgi?id=601731
js/Makefile.am | 3 +-
js/ui/main.js | 3 +
js/ui/overview.js | 73 ++++++++++++++++++++++
js/ui/panel.js | 68 ++++++++++++++++++---
js/ui/workspacesView.js | 3 +
js/ui/xdndHandler.js | 130 +++++++++++++++++++++++++++++++++++++++
src/gnome-shell-plugin.c | 19 ++++--
src/shell-global-private.h | 2 +
src/shell-global.c | 145 ++++++++++++++++++++++++++++++++++++++++++++
src/shell-global.h | 2 +
src/shell-marshal.list | 1 +
11 files changed, 433 insertions(+), 16 deletions(-)
---
diff --git a/js/Makefile.am b/js/Makefile.am
index 555be7b..49de2ff 100644
--- a/js/Makefile.am
+++ b/js/Makefile.am
@@ -56,4 +56,5 @@ nobase_dist_js_DATA = \
ui/windowManager.js \
ui/workspace.js \
ui/workspacesView.js \
- ui/workspaceSwitcherPopup.js
+ ui/workspaceSwitcherPopup.js \
+ ui/xdndHandler.js
diff --git a/js/ui/main.js b/js/ui/main.js
index 4a8f8ef..e08781b 100644
--- a/js/ui/main.js
+++ b/js/ui/main.js
@@ -37,6 +37,7 @@ const ShellDBus = imports.ui.shellDBus;
const TelepathyClient = imports.ui.telepathyClient;
const WindowManager = imports.ui.windowManager;
const Magnifier = imports.ui.magnifier;
+const XdndHandler = imports.ui.xdndHandler;
const StatusIconDispatcher = imports.ui.statusIconDispatcher;
const DEFAULT_BACKGROUND_COLOR = new Clutter.Color();
@@ -60,6 +61,7 @@ let modalCount = 0;
let modalActorFocusStack = [];
let uiGroup = null;
let magnifier = null;
+let xdndHandler = null;
let statusIconDispatcher = null;
let _errorLogStack = [];
let _startDate;
@@ -129,6 +131,7 @@ function start() {
global.stage.add_actor(uiGroup);
placesManager = new PlaceDisplay.PlacesManager();
+ xdndHandler = new XdndHandler.XdndHandler();
overview = new Overview.Overview();
chrome = new Chrome.Chrome();
magnifier = new Magnifier.Magnifier();
diff --git a/js/ui/overview.js b/js/ui/overview.js
index 56da40a..bfabcb3 100644
--- a/js/ui/overview.js
+++ b/js/ui/overview.js
@@ -9,9 +9,11 @@ const St = imports.gi.St;
const Shell = imports.gi.Shell;
const Gettext = imports.gettext.domain('gnome-shell');
const _ = Gettext.gettext;
+const Gdk = imports.gi.Gdk;
const AppDisplay = imports.ui.appDisplay;
const Dash = imports.ui.dash;
+const DND = imports.ui.dnd;
const DocDisplay = imports.ui.docDisplay;
const GenericDisplay = imports.ui.genericDisplay;
const Lightbox = imports.ui.lightbox;
@@ -30,6 +32,8 @@ const ANIMATION_TIME = 0.25;
const DASH_SPLIT_FRACTION = 0.1;
+const DND_WINDOW_SWITCH_TIMEOUT = 1250;
+
function Source() {
this._init();
}
@@ -178,9 +182,72 @@ Overview.prototype = {
this._coverPane.lower_bottom();
+ // XDND
+ this._dragMonitor = {
+ dragMotion: Lang.bind(this, this._onDragMotion)
+ };
+
+ Main.xdndHandler.connect('drag-begin', Lang.bind(this, this._onDragBegin));
+ Main.xdndHandler.connect('drag-end', Lang.bind(this, this._onDragEnd));
+
+ this._windowSwitchTimeoutId = 0;
+ this._windowSwitchTimestamp = 0;
+ this._lastActiveWorkspaceIndex = -1;
+ this._needsFakePointerEvent = false;
+
this.workspaces = null;
},
+ _onDragBegin: function() {
+ DND.addDragMonitor(this._dragMonitor);
+ // Remember the workspace we started from
+ this._lastActiveWorkspaceIndex = global.screen.get_active_workspace_index();
+ },
+
+ _onDragEnd: function(time) {
+ // In case the drag was canceled while in the overview
+ // we have to go back to where we started and hide
+ // the overview
+ if (this._shownTemporarily) {
+ global.screen.get_workspace_by_index(this._lastActiveWorkspaceIndex).activate(time);
+ this.hideTemporarily();
+ }
+
+ DND.removeMonitor(this._dragMonitor);
+ },
+
+ _fakePointerEvent: function() {
+ let display = Gdk.Display.get_default();
+ let deviceManager = display.get_device_manager();
+ let pointer = deviceManager.get_client_pointer();
+ let [screen, pointerX, pointerY] = display.get_device_state(pointer);
+
+ display.warp_device(pointer, screen, pointerX, pointerY);
+ },
+
+ _onDragMotion: function(dragEvent) {
+ if (this._windowSwitchTimeoutId != 0) {
+ Mainloop.source_remove(this._windowSwitchTimeoutId);
+ this._windowSwitchTimeoutId = 0;
+ this._needsFakePointerEvent = false;
+ }
+
+ if (dragEvent.targetActor &&
+ dragEvent.targetActor._delegate &&
+ dragEvent.targetActor._delegate.metaWindow) {
+ this._windowSwitchTimestamp = global.get_current_time();
+ this._windowSwitchTimeoutId = Mainloop.timeout_add(DND_WINDOW_SWITCH_TIMEOUT,
+ Lang.bind(this, function() {
+ this._needsFakePointerEvent = true;
+ Main.activateWindow(dragEvent.targetActor._delegate.metaWindow,
+ this._windowSwitchTimestamp);
+ this.hideTemporarily();
+ }));
+ }
+
+ return DND.DragMotionResult.CONTINUE;
+ },
+
_getDesktopClone: function() {
let windows = global.get_window_actors().filter(function(w) {
return w.meta_window.get_window_type() == Meta.WindowType.DESKTOP;
@@ -531,6 +598,12 @@ Overview.prototype = {
this._animateVisible();
this._syncInputMode();
+
+ // Fake a pointer event if requested
+ if (this._needsFakePointerEvent) {
+ this._fakePointerEvent();
+ this._needsFakePointerEvent = false;
+ }
}
};
Signals.addSignalMethods(Overview.prototype);
diff --git a/js/ui/panel.js b/js/ui/panel.js
index 4439d3d..4fe60a9 100644
--- a/js/ui/panel.js
+++ b/js/ui/panel.js
@@ -26,6 +26,8 @@ const PANEL_ICON_SIZE = 24;
const HOT_CORNER_ACTIVATION_TIMEOUT = 0.5;
+const BUTTON_DND_ACTIVATION_TIMEOUT = 250;
+
const ANIMATED_ICON_UPDATE_TIMEOUT = 100;
const SPINNER_UPDATE_TIMEOUT = 130;
const SPINNER_SPEED = 0.02;
@@ -728,7 +730,20 @@ Panel.prototype = {
reactive: true,
can_focus: true });
this.button.set_child(label);
-
+ this.button._delegate = this.button;
+ this.button._xdndTimeOut = 0;
+ this.button.handleDragOver = Lang.bind(this,
+ function(source, actor, x, y, time) {
+ if (source == Main.xdndHandler) {
+ if (this.button._xdndTimeOut != 0)
+ Mainloop.source_remove(this.button._xdndTimeOut);
+ this.button._xdndTimeOut = Mainloop.timeout_add(BUTTON_DND_ACTIVATION_TIMEOUT,
+ Lang.bind(this,
+ function() {
+ this._xdndShowOverview(actor);
+ }));
+ }
+ });
this._leftBox.add(this.button);
// We use this flag to mark the case where the user has entered the
@@ -766,6 +781,18 @@ Panel.prototype = {
this._hotCorner.connect('leave-event',
Lang.bind(this, this._onHotCornerLeft));
+ this._hotCorner._delegate = this._hotCorner;
+ this._hotCorner.handleDragOver = Lang.bind(this,
+ function(source, actor, x, y, time) {
+ if (source == Main.xdndHandler) {
+ if(!Main.overview.visible && !Main.overview.animationInProgress) {
+ this.rippleAnimation();
+ Main.overview.showTemporarily();
+ Main.overview.beginItemDrag(actor);
+ }
+ }
+ });
+
this._boxContainer.add_actor(this._hotCornerEnvirons);
this._boxContainer.add_actor(this._hotCorner);
@@ -821,6 +848,25 @@ Panel.prototype = {
Main.chrome.addActor(this.actor, { visibleInOverview: true });
},
+ _xdndShowOverview: function (actor) {
+ let [x, y, mask] = global.get_pointer();
+ let pickedActor = global.stage.get_actor_at_pos(Clutter.PickMode.REACTIVE, x, y);
+
+ if (pickedActor != this.button) {
+ Mainloop.source_remove(this.button._xdndTimeOut);
+ this.button._xdndTimeOut = 0;
+ return;
+ }
+
+ if(!Main.overview.visible && !Main.overview.animationInProgress) {
+ Main.overview.showTemporarily();
+ Main.overview.beginItemDrag(actor);
+ }
+
+ Mainloop.source_remove(this.button._xdndTimeOut);
+ this.button._xdndTimeOut = 0;
+ },
+
startStatusArea: function() {
for (let i = 0; i < STANDARD_TRAY_ICON_ORDER.length; i++) {
let role = STANDARD_TRAY_ICON_ORDER[i];
@@ -915,6 +961,17 @@ Panel.prototype = {
Main.uiGroup.add_actor(ripple);
},
+ rippleAnimation: function() {
+ // Show three concentric ripples expanding outwards; the exact
+ // parameters were found by trial and error, so don't look
+ // for them to make perfect sense mathematically
+
+ // delay time scale opacity => scale opacity
+ this._addRipple(0.0, 0.83, 0.25, 1.0, 1.5, 0.0);
+ this._addRipple(0.05, 1.0, 0.0, 0.7, 1.25, 0.0);
+ this._addRipple(0.35, 1.0, 0.0, 0.3, 1, 0.0);
+ },
+
_onHotCornerEntered : function() {
if (this._menus.grabbed)
return false;
@@ -923,14 +980,7 @@ Panel.prototype = {
if (!Main.overview.animationInProgress) {
this._hotCornerActivationTime = Date.now() / 1000;
- // Show three concentric ripples expanding outwards; the exact
- // parameters were found by trial and error, so don't look
- // for them to make perfect sense mathematically
-
- // delay time scale opacity => scale opacity
- this._addRipple(0.0, 0.83, 0.25, 1.0, 1.5, 0.0);
- this._addRipple(0.05, 1.0, 0.0, 0.7, 1.25, 0.0);
- this._addRipple(0.35, 1.0, 0.0, 0.3, 1, 0.0);
+ this.rippleAnimation();
Main.overview.toggle();
}
}
diff --git a/js/ui/workspacesView.js b/js/ui/workspacesView.js
index 678a9e0..7478003 100644
--- a/js/ui/workspacesView.js
+++ b/js/ui/workspacesView.js
@@ -644,6 +644,9 @@ WorkspacesView.prototype = {
},
_onDragMotion: function(dragEvent) {
+ if (Main.overview.animationInProgress)
+ return DND.DragMotionResult.CONTINUE;
+
let primary = global.get_primary_monitor();
let activeWorkspaceIndex = global.screen.get_active_workspace_index();
diff --git a/js/ui/xdndHandler.js b/js/ui/xdndHandler.js
new file mode 100644
index 0000000..0275559
--- /dev/null
+++ b/js/ui/xdndHandler.js
@@ -0,0 +1,130 @@
+/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
+
+const Clutter = imports.gi.Clutter;
+const Lang = imports.lang;
+const Shell = imports.gi.Shell;
+const Signals = imports.signals;
+const Mainloop = imports.mainloop;
+const DND = imports.ui.dnd;
+
+function XdndHandler() {
+ this._init();
+}
+
+XdndHandler.prototype = {
+ _init: function() {
+ // Used to display a clone of the cursor window when the
+ // window group is hidden (like it happens in the overview)
+ this._cursorWindowClone = null;
+
+ // Used as a drag actor in case we don't have a cursor window clone
+ this._dummy = new Clutter.Rectangle({ width: 1, height: 1, opacity: 0 });
+ global.stage.add_actor(this._dummy);
+ this._dummy.hide();
+
+ // Mutter delays the creation of the output window as long
+ // as possible to avoid flicker. In case a plugin wants to
+ // access it directly it has to connect to the stage's show
+ // signal. (see comment in compositor.c:meta_compositor_manage_screen)
+ global.stage.connect('show', function () {
+ global.init_xdnd();
+ return false;
+ });
+
+ global.connect('xdnd-enter', Lang.bind(this, this._onEnter));
+ global.connect('xdnd-position-changed', Lang.bind(this, this._onPositionChanged));
+ global.connect('xdnd-leave', Lang.bind(this, this._onLeave));
+
+ this._windowGroupVisibilityHandlerId = 0;
+ },
+
+ // Called when the user cancels the drag (i.e release the button)
+ _onLeave: function() {
+ if (this._windowGroupVisibilityHandlerId != 0) {
+ Mainloop.source_remove(this._windowGroupVisibilityHandlerId);
+ this._windowGroupVisibilityHandlerId = 0;
+ }
+ this.emit('drag-end');
+ },
+
+ _onEnter: function() {
+ this._windowGroupVisibilityHandlerId =
+ global.window_group.connect('notify::visible',
+ Lang.bind(this, this._onWindowGroupVisibilityChanged));
+
+ this.emit('drag-begin', global.get_current_time());
+ },
+
+ _onWindowGroupVisibilityChanged: function() {
+ if (!global.window_group.visible) {
+ if (this._cursorWindowClone)
+ return;
+
+ let windows = global.get_window_actors();
+ let cursorWindow = windows[windows.length - 1];
+
+ // FIXME: more reliable way?
+ if (!cursorWindow.is_override_redirect())
+ return;
+
+ let constraint_x = new Clutter.BindConstraint({ coordinate : Clutter.BindCoordinate.X,
+ source: cursorWindow});
+ let constraint_y = new Clutter.BindConstraint({ coordinate : Clutter.BindCoordinate.Y,
+ source: cursorWindow});
+
+ this._cursorWindowClone = new Clutter.Clone({ source: cursorWindow });
+ global.overlay_group.add_actor(this._cursorWindowClone);
+ Shell.util_set_hidden_from_pick(this._cursorWindowClone, true);
+
+ // Make sure that the clone has the same position as the source
+ this._cursorWindowClone.add_constraint(constraint_x);
+ this._cursorWindowClone.add_constraint(constraint_y);
+ } else {
+ if (this._cursorWindowClone)
+ {
+ this._cursorWindowClone.destroy();
+ this._cursorWindowClone = null;
+ }
+ }
+ },
+
+ _onPositionChanged: function(obj, x, y) {
+ let pickedActor = global.stage.get_actor_at_pos(Clutter.PickMode.ALL, x, y);
+
+ // Make sure that the cursor window is on top
+ if (this._cursorWindowClone)
+ this._cursorWindowClone.raise_top();
+
+ let dragEvent = {
+ x: x,
+ y: y,
+ dragActor: this._cursorWindowClone ? this._cursorWindowClone : this._dummy,
+ source: this,
+ targetActor: pickedActor
+ };
+
+ for (let i = 0; i < DND.dragMonitors.length; i++) {
+ let motionFunc = DND.dragMonitors[i].dragMotion;
+ if (motionFunc) {
+ let result = motionFunc(dragEvent);
+ if (result != DND.DragMotionResult.CONTINUE)
+ return;
+ }
+ }
+
+ while (pickedActor) {
+ if (pickedActor._delegate && pickedActor._delegate.handleDragOver) {
+ let result = pickedActor._delegate.handleDragOver(this,
+ dragEvent.dragActor,
+ x,
+ y,
+ global.get_current_time());
+ if (result != DND.DragMotionResult.CONTINUE)
+ return;
+ }
+ pickedActor = pickedActor.get_parent();
+ }
+ }
+}
+
+Signals.addSignalMethods(XdndHandler.prototype);
diff --git a/src/gnome-shell-plugin.c b/src/gnome-shell-plugin.c
index ec23bf3..50e2c5c 100644
--- a/src/gnome-shell-plugin.c
+++ b/src/gnome-shell-plugin.c
@@ -113,6 +113,8 @@ struct _GnomeShellPlugin
int glx_error_base;
int glx_event_base;
guint have_swap_event : 1;
+
+ ShellGlobal *global;
};
struct _GnomeShellPluginClass
@@ -320,7 +322,6 @@ gnome_shell_plugin_start (MetaPlugin *plugin)
int status;
const char *shell_js;
char **search_path;
- ShellGlobal *global;
const char *glx_extensions;
bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
@@ -379,10 +380,10 @@ gnome_shell_plugin_start (MetaPlugin *plugin)
gvc_muted_debug_log_handler, NULL);
/* Initialize the global object here. */
- global = shell_global_get ();
+ shell_plugin->global = shell_global_get ();
- _shell_global_set_plugin (global, META_PLUGIN(shell_plugin));
- _shell_global_set_gjs_context (global, shell_plugin->gjs_context);
+ _shell_global_set_plugin (shell_plugin->global, META_PLUGIN(shell_plugin));
+ _shell_global_set_gjs_context (shell_plugin->global, shell_plugin->gjs_context);
add_statistics (shell_plugin);
@@ -511,9 +512,9 @@ static gboolean
gnome_shell_plugin_xevent_filter (MetaPlugin *plugin,
XEvent *xev)
{
-#ifdef GLX_INTEL_swap_event
- GnomeShellPlugin *shell_plugin = GNOME_SHELL_PLUGIN (plugin);
+ GnomeShellPlugin *shell_plugin = GNOME_SHELL_PLUGIN (plugin);
+#ifdef GLX_INTEL_swap_event
if (shell_plugin->have_swap_event &&
xev->type == (shell_plugin->glx_event_base + GLX_BufferSwapComplete))
{
@@ -545,6 +546,12 @@ gnome_shell_plugin_xevent_filter (MetaPlugin *plugin,
&& xev->xcrossing.window == clutter_x11_get_stage_window (CLUTTER_STAGE (clutter_stage_get_default ())))
return TRUE;
+ /*
+ * Pass the event to shell-global
+ */
+ if (_shell_global_check_xdnd_event (shell_plugin->global, xev))
+ return TRUE;
+
return clutter_x11_handle_event (xev) != CLUTTER_X11_FILTER_CONTINUE;
}
diff --git a/src/shell-global-private.h b/src/shell-global-private.h
index 201ace8..e175145 100644
--- a/src/shell-global-private.h
+++ b/src/shell-global-private.h
@@ -11,4 +11,6 @@ void _shell_global_set_plugin (ShellGlobal *global,
void _shell_global_set_gjs_context (ShellGlobal *global,
GjsContext *context);
+gboolean _shell_global_check_xdnd_event (ShellGlobal *global,
+ XEvent *xev);
#endif /* __SHELL_GLOBAL_PRIVATE_H__ */
diff --git a/src/shell-global.c b/src/shell-global.c
index 2cf6ec6..4c3246f 100644
--- a/src/shell-global.c
+++ b/src/shell-global.c
@@ -6,6 +6,7 @@
#include "shell-enum-types.h"
#include "shell-perf-log.h"
#include "shell-window-tracker.h"
+#include "shell-marshal.h"
#include "shell-wm.h"
#include "st.h"
@@ -70,6 +71,8 @@ struct _ShellGlobal {
/* For sound notifications */
ca_context *sound_context;
+
+ guint32 xdnd_timestamp;
};
enum {
@@ -92,8 +95,19 @@ enum {
PROP_FOCUS_MANAGER,
};
+/* Signals */
+enum
+{
+ XDND_POSITION_CHANGED,
+ XDND_LEAVE,
+ XDND_ENTER,
+ LAST_SIGNAL
+};
+
G_DEFINE_TYPE(ShellGlobal, shell_global, G_TYPE_OBJECT);
+static guint shell_global_signals [LAST_SIGNAL] = { 0 };
+
static void
shell_global_set_property(GObject *object,
guint prop_id,
@@ -238,6 +252,36 @@ shell_global_class_init (ShellGlobalClass *klass)
gobject_class->get_property = shell_global_get_property;
gobject_class->set_property = shell_global_set_property;
+ /* Emitted from gnome-shell-plugin.c during event handling */
+ shell_global_signals[XDND_POSITION_CHANGED] =
+ g_signal_new ("xdnd-position-changed",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ 0,
+ NULL, NULL,
+ _shell_marshal_VOID__INT_INT,
+ G_TYPE_NONE, 2, G_TYPE_INT, G_TYPE_INT);
+
+ /* Emitted from gnome-shell-plugin.c during event handling */
+ shell_global_signals[XDND_LEAVE] =
+ g_signal_new ("xdnd-leave",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ 0,
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ /* Emitted from gnome-shell-plugin.c during event handling */
+ shell_global_signals[XDND_ENTER] =
+ g_signal_new ("xdnd-enter",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ 0,
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
g_object_class_install_property (gobject_class,
PROP_OVERLAY_GROUP,
g_param_spec_object ("overlay-group",
@@ -1131,6 +1175,39 @@ grab_notify (GtkWidget *widget, gboolean was_grabbed, gpointer user_data)
}
/**
+ * shell_global_init_xdnd:
+ * @global: the #ShellGlobal
+ *
+ * Enables tracking of Xdnd events
+ */
+void shell_global_init_xdnd (ShellGlobal *global)
+{
+ long xdnd_version = 5;
+
+ MetaScreen *screen = shell_global_get_screen (global);
+ Window output_window = meta_get_overlay_window (screen);
+
+ MetaDisplay *display = meta_screen_get_display (screen);
+ Display *xdisplay = meta_display_get_xdisplay (display);
+
+ ClutterStage *stage = CLUTTER_STAGE(meta_plugin_get_stage (global->plugin));
+ Window stage_win = clutter_x11_get_stage_window (stage);
+
+ XChangeProperty (xdisplay, stage_win, gdk_x11_get_xatom_by_name ("XdndAware"), XA_ATOM,
+ 32, PropModeReplace, (const unsigned char *)&xdnd_version, 1);
+
+ XChangeProperty (xdisplay, output_window, gdk_x11_get_xatom_by_name ("XdndProxy"), XA_WINDOW,
+ 32, PropModeReplace, (const unsigned char *)&stage_win, 1);
+
+ /*
+ * XdndProxy is additionally set on the proxy window as verification that the
+ * XdndProxy property on the target window isn't a left-over
+ */
+ XChangeProperty (xdisplay, stage_win, gdk_x11_get_xatom_by_name ("XdndProxy"), XA_WINDOW,
+ 32, PropModeReplace, (const unsigned char *)&stage_win, 1);
+}
+
+/**
* shell_global_format_time_relative_pretty:
* @global:
* @delta: Time in seconds since the current time
@@ -1422,6 +1499,10 @@ shell_global_get_current_time (ShellGlobal *global)
guint32 time;
MetaDisplay *display;
+ /* In case we have a xdnd timestamp use it */
+ if (global->xdnd_timestamp != 0)
+ return global->xdnd_timestamp;
+
/* meta_display_get_current_time() will return the correct time
when handling an X or Gdk event, but will return CurrentTime
from some Clutter event callbacks.
@@ -1689,3 +1770,67 @@ shell_global_play_theme_sound (ShellGlobal *global,
{
ca_context_play (global->sound_context, 0, CA_PROP_EVENT_ID, name, NULL);
}
+
+/*
+ * Process Xdnd events
+ *
+ * We pass the position and leave events to JS via a signal
+ * where the actual drag & drop handling happens.
+ *
+ * http://www.freedesktop.org/wiki/Specifications/XDND
+ */
+gboolean _shell_global_check_xdnd_event (ShellGlobal *global,
+ XEvent *xev)
+{
+ MetaScreen *screen = meta_plugin_get_screen (global->plugin);
+ Window output_window = meta_get_overlay_window (screen);
+ MetaDisplay *display = meta_screen_get_display (screen);
+ Display *xdisplay = meta_display_get_xdisplay (display);
+
+ ClutterStage *stage = CLUTTER_STAGE (meta_plugin_get_stage (global->plugin));
+ Window stage_win = clutter_x11_get_stage_window (stage);
+
+ if (xev->xany.window != output_window && xev->xany.window != stage_win)
+ return FALSE;
+
+ if (xev->xany.type == ClientMessage && xev->xclient.message_type == gdk_x11_get_xatom_by_name ("XdndPosition"))
+ {
+ XEvent xevent;
+ Window src = xev->xclient.data.l[0];
+
+ memset (&xevent, 0, sizeof(xevent));
+ xevent.xany.type = ClientMessage;
+ xevent.xany.display = xdisplay;
+ xevent.xclient.window = src;
+ xevent.xclient.message_type = gdk_x11_get_xatom_by_name ("XdndStatus");
+ xevent.xclient.format = 32;
+ xevent.xclient.data.l[0] = output_window;
+ /* flags: bit 0: will we accept the drop? bit 1: do we want more position messages */
+ xevent.xclient.data.l[1] = 2;
+ xevent.xclient.data.l[4] = None;
+
+ XSendEvent (xdisplay, src, False, 0, &xevent);
+
+ /* Store the timestamp of the xdnd position event */
+ global->xdnd_timestamp = xev->xclient.data.l[3];
+ g_signal_emit_by_name (G_OBJECT (global), "xdnd-position-changed",
+ (int)(xev->xclient.data.l[2] >> 16), (int)(xev->xclient.data.l[2] & 0xFFFF));
+ global->xdnd_timestamp = 0;
+
+ return TRUE;
+ }
+ else if (xev->xany.type == ClientMessage && xev->xclient.message_type == gdk_x11_get_xatom_by_name ("XdndLeave"))
+ {
+ g_signal_emit_by_name (G_OBJECT (global), "xdnd-leave");
+
+ return TRUE;
+ }
+ else if (xev->xany.type == ClientMessage && xev->xclient.message_type == gdk_x11_get_xatom_by_name ("XdndEnter"))
+ {
+ g_signal_emit_by_name (G_OBJECT (global), "xdnd-enter");
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
diff --git a/src/shell-global.h b/src/shell-global.h
index 91a82c4..4a40bcd 100644
--- a/src/shell-global.h
+++ b/src/shell-global.h
@@ -132,6 +132,8 @@ void shell_global_run_at_leisure (ShellGlobal *global,
void shell_global_play_theme_sound (ShellGlobal *global,
const char *name);
+void shell_global_init_xdnd (ShellGlobal *global);
+
G_END_DECLS
#endif /* __SHELL_GLOBAL_H__ */
diff --git a/src/shell-marshal.list b/src/shell-marshal.list
index 588693d..34d1078 100644
--- a/src/shell-marshal.list
+++ b/src/shell-marshal.list
@@ -4,3 +4,4 @@ VOID:BOXED
VOID:BOXED,OBJECT
VOID:OBJECT,OBJECT
VOID:STRING,OBJECT,BOOLEAN
+VOID:INT,INT
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]