#define G_LOG_DOMAIN "dndcp"
+#include "xutils/xutils.hh"
+
#include "dndUIX11.h"
#include "guestDnDCPMgr.hh"
#include "tracer.hh"
extern "C" {
-#include "vmblock.h"
-#include "file.h"
+#include <X11/extensions/XTest.h> /* for XTest*() */
+#include <gtk/gtk.h>
+#include <gdk/gdkx.h>
+#include <X11/Xatom.h>
+
+#include "vmware/guestrpc/tclodefs.h"
+
#include "copyPasteCompat.h"
-#include "dnd.h"
-#include "dndMsg.h"
-#include "dndClipboard.h"
#include "cpName.h"
#include "cpNameUtil.h"
+#include "dnd.h"
+#include "dndClipboard.h"
+#include "dndMsg.h"
+#include "file.h"
#include "hostinfo.h"
#include "rpcout.h"
-#include <gtk/gtk.h>
-#include <gdk/gdkx.h>
-#include <X11/extensions/XTest.h> /* for XTest*() */
-#include "vmware/guestrpc/tclodefs.h"
+#include "vmblock.h"
}
/* IsXExtensionPointer may be not defined with old Xorg. */
#include "copyPasteDnDWrapper.h"
-/**
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * DnDUIX11::DnDUIX11 --
*
- * Constructor.
+ * Constructor.
+ *
+ *-----------------------------------------------------------------------------
*/
DnDUIX11::DnDUIX11(ToolsAppCtx *ctx)
- : m_ctx(ctx),
- m_DnD(NULL),
- m_detWnd(NULL),
- m_blockCtrl(NULL),
- m_HGGetFileStatus(DND_FILE_TRANSFER_NOT_STARTED),
- m_blockAdded(false),
- m_GHDnDInProgress(false),
- m_GHDnDDataReceived(false),
- m_unityMode(false),
- m_inHGDrag(false),
- m_effect(DROP_NONE),
- m_mousePosX(0),
- m_mousePosY(0),
- m_dc(NULL),
- m_numPendingRequest(0),
- m_destDropTime(0),
- mTotalFileSize(0)
+ : mCtx(ctx),
+ mDnD(NULL),
+ mDetWnd(NULL),
+ mBlockCtrl(NULL),
+ mHGGetFileStatus(DND_FILE_TRANSFER_NOT_STARTED),
+ mBlockAdded(false),
+ mGHDnDInProgress(false),
+ mGHDnDDataReceived(false),
+ mUnityMode(false),
+ mInHGDrag(false),
+ mEffect(DROP_NONE),
+ mMousePosX(0),
+ mMousePosY(0),
+ mDragCtx(NULL),
+ mNumPendingRequest(0),
+ mDestDropTime(0),
+ mTotalFileSize(0),
+ mOrigin(0, 0)
{
TRACE_CALL();
+
+ xutils::Init();
+ xutils::workAreaChanged.connect(sigc::mem_fun(this, &DnDUIX11::OnWorkAreaChanged));
+
+ /*
+ * XXX Hard coded use of default screen means this doesn't work in dual-
+ * headed setups (e.g. DISPLAY=:0.1). However, the number of people running
+ * such setups in VMs is expected to be, like, hella small, so I'mma cut
+ * corners for now.
+ */
+ OnWorkAreaChanged(Gdk::Screen::get_default());
}
-/**
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * DnDUIX11::~DnDUIX11 --
+ *
+ * Destructor.
*
- * Destructor.
+ *-----------------------------------------------------------------------------
*/
DnDUIX11::~DnDUIX11()
{
TRACE_CALL();
- if (m_detWnd) {
- delete m_detWnd;
+
+ if (mDetWnd) {
+ delete mDetWnd;
}
- CPClipboard_Destroy(&m_clipboard);
+ CPClipboard_Destroy(&mClipboard);
/* Any files from last unfinished file transfer should be deleted. */
- if (DND_FILE_TRANSFER_IN_PROGRESS == m_HGGetFileStatus &&
- !m_HGStagingDir.empty()) {
- uint64 totalSize = File_GetSizeEx(m_HGStagingDir.c_str());
+ if ( DND_FILE_TRANSFER_IN_PROGRESS == mHGGetFileStatus
+ && !mHGStagingDir.empty()) {
+ uint64 totalSize = File_GetSizeEx(mHGStagingDir.c_str());
if (mTotalFileSize != totalSize) {
g_debug("%s: deleting %s, expecting %"FMT64"d, finished %"FMT64"d\n",
- __FUNCTION__, m_HGStagingDir.c_str(),
+ __FUNCTION__, mHGStagingDir.c_str(),
mTotalFileSize, totalSize);
- DnD_DeleteStagingFiles(m_HGStagingDir.c_str(), FALSE);
+ DnD_DeleteStagingFiles(mHGStagingDir.c_str(), FALSE);
} else {
g_debug("%s: file size match %s\n",
- __FUNCTION__, m_HGStagingDir.c_str());
+ __FUNCTION__, mHGStagingDir.c_str());
}
}
- CommonResetCB();
+ ResetUI();
}
-/**
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * DnDUIX11::Init --
+ *
+ * Initialize DnDUIX11 object.
+ *
+ * Results:
+ * Returns true on success and false on failure.
*
- * Initialize DnDUIX11 object.
+ *-----------------------------------------------------------------------------
*/
bool
TRACE_CALL();
bool ret = true;
- CPClipboard_Init(&m_clipboard);
+ CPClipboard_Init(&mClipboard);
GuestDnDCPMgr *p = GuestDnDCPMgr::GetInstance();
ASSERT(p);
- m_DnD = p->GetDnDMgr();
- ASSERT(m_DnD);
+ mDnD = p->GetDnDMgr();
+ ASSERT(mDnD);
- m_detWnd = new DragDetWnd();
- if (!m_detWnd) {
+ mDetWnd = new DragDetWnd();
+ if (!mDetWnd) {
g_debug("%s: unable to allocate DragDetWnd object\n", __FUNCTION__);
goto fail;
}
* Gtk::Invisible, which doesn't implement the methods that SetAttributes
* relies upon.
*/
- m_detWnd->SetAttributes();
+ mDetWnd->SetAttributes();
#endif
- SetTargetsAndCallbacks();
+ InitGtk();
#define CONNECT_SIGNAL(_obj, _sig, _cb) \
_obj->_sig.connect(sigc::mem_fun(this, &DnDUIX11::_cb))
/* Set common layer callbacks. */
- CONNECT_SIGNAL(m_DnD, srcDragBeginChanged, CommonDragStartCB);
- CONNECT_SIGNAL(m_DnD, srcDropChanged, CommonSourceDropCB);
- CONNECT_SIGNAL(m_DnD, srcCancelChanged, CommonSourceCancelCB);
- CONNECT_SIGNAL(m_DnD, destCancelChanged, CommonDestCancelCB);
- CONNECT_SIGNAL(m_DnD, destMoveDetWndToMousePosChanged, CommonMoveDetWndToMousePos);
- CONNECT_SIGNAL(m_DnD, getFilesDoneChanged, CommonSourceFileCopyDoneCB);
- CONNECT_SIGNAL(m_DnD, moveMouseChanged, CommonUpdateMouseCB);
- CONNECT_SIGNAL(m_DnD, privDropChanged, CommonDestPrivateDropCB);
- CONNECT_SIGNAL(m_DnD, updateDetWndChanged, CommonUpdateDetWndCB);
- CONNECT_SIGNAL(m_DnD, updateUnityDetWndChanged, CommonUpdateUnityDetWndCB);
+ CONNECT_SIGNAL(mDnD, srcDragBeginChanged, OnSrcDragBegin);
+ CONNECT_SIGNAL(mDnD, srcDropChanged, OnSrcDrop);
+ CONNECT_SIGNAL(mDnD, srcCancelChanged, OnSrcCancel);
+ CONNECT_SIGNAL(mDnD, destCancelChanged, OnDestCancel);
+ CONNECT_SIGNAL(mDnD, destMoveDetWndToMousePosChanged, OnDestMoveDetWndToMousePos);
+ CONNECT_SIGNAL(mDnD, getFilesDoneChanged, OnGetFilesDone);
+ CONNECT_SIGNAL(mDnD, moveMouseChanged, OnMoveMouse);
+ CONNECT_SIGNAL(mDnD, privDropChanged, OnPrivateDrop);
+ CONNECT_SIGNAL(mDnD, updateDetWndChanged, OnUpdateDetWnd);
+ CONNECT_SIGNAL(mDnD, updateUnityDetWndChanged, OnUpdateUnityDetWnd);
/* Set Gtk+ callbacks for source. */
- CONNECT_SIGNAL(m_detWnd, signal_drag_begin(), GtkSourceDragBeginCB);
- CONNECT_SIGNAL(m_detWnd, signal_drag_data_get(), GtkSourceDragDataGetCB);
- CONNECT_SIGNAL(m_detWnd, signal_drag_end(), GtkSourceDragEndCB);
- CONNECT_SIGNAL(m_detWnd, signal_enter_notify_event(), GtkEnterEventCB);
- CONNECT_SIGNAL(m_detWnd, signal_leave_notify_event(), GtkLeaveEventCB);
- CONNECT_SIGNAL(m_detWnd, signal_map_event(), GtkMapEventCB);
- CONNECT_SIGNAL(m_detWnd, signal_unmap_event(), GtkUnmapEventCB);
- CONNECT_SIGNAL(m_detWnd, signal_realize(), GtkRealizeEventCB);
- CONNECT_SIGNAL(m_detWnd, signal_unrealize(), GtkUnrealizeEventCB);
- CONNECT_SIGNAL(m_detWnd, signal_motion_notify_event(), GtkMotionNotifyEventCB);
- CONNECT_SIGNAL(m_detWnd, signal_configure_event(), GtkConfigureEventCB);
- CONNECT_SIGNAL(m_detWnd, signal_button_press_event(), GtkButtonPressEventCB);
- CONNECT_SIGNAL(m_detWnd, signal_button_release_event(), GtkButtonReleaseEventCB);
+ CONNECT_SIGNAL(mDetWnd, signal_drag_begin(), OnGtkDragBegin);
+ CONNECT_SIGNAL(mDetWnd, signal_drag_data_get(), OnGtkDragDataGet);
+ CONNECT_SIGNAL(mDetWnd, signal_drag_end(), OnGtkDragEnd);
+ CONNECT_SIGNAL(mDetWnd, signal_enter_notify_event(), GtkEnterEventCB);
+ CONNECT_SIGNAL(mDetWnd, signal_leave_notify_event(), GtkLeaveEventCB);
+ CONNECT_SIGNAL(mDetWnd, signal_map_event(), GtkMapEventCB);
+ CONNECT_SIGNAL(mDetWnd, signal_unmap_event(), GtkUnmapEventCB);
+ CONNECT_SIGNAL(mDetWnd, signal_realize(), GtkRealizeEventCB);
+ CONNECT_SIGNAL(mDetWnd, signal_unrealize(), GtkUnrealizeEventCB);
+ CONNECT_SIGNAL(mDetWnd, signal_motion_notify_event(), GtkMotionNotifyEventCB);
+ CONNECT_SIGNAL(mDetWnd, signal_configure_event(), GtkConfigureEventCB);
+ CONNECT_SIGNAL(mDetWnd, signal_button_press_event(), GtkButtonPressEventCB);
+ CONNECT_SIGNAL(mDetWnd, signal_button_release_event(), GtkButtonReleaseEventCB);
#undef CONNECT_SIGNAL
- CommonUpdateDetWndCB(false, 0, 0);
- CommonUpdateUnityDetWndCB(false, 0, false);
+ OnUpdateDetWnd(false, 0, 0);
+ OnUpdateUnityDetWnd(false, 0, false);
goto out;
fail:
ret = false;
- if (m_DnD) {
- delete m_DnD;
- m_DnD = NULL;
+ if (mDnD) {
+ delete mDnD;
+ mDnD = NULL;
}
- if (m_detWnd) {
- delete m_detWnd;
- m_detWnd = NULL;
+ if (mDetWnd) {
+ delete mDetWnd;
+ mDetWnd = NULL;
}
out:
return ret;
}
-/**
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * DnDUIX11::InitGtk --
+ *
+ * Register supported DND target types and signal handlers with GTK+.
*
- * Setup targets we support, claim ourselves as a drag destination, and
- * register callbacks for Gtk+ drag and drop callbacks the platform will
- * send to us.
+ *-----------------------------------------------------------------------------
*/
void
-DnDUIX11::SetTargetsAndCallbacks()
+DnDUIX11::InitGtk()
{
TRACE_CALL();
* but in our case, we will call drag_get_data during DragMotion, and
* will cause X dead with DEST_DEFAULT_ALL. The reason is unclear.
*/
- m_detWnd->drag_dest_set(targets, Gtk::DEST_DEFAULT_MOTION,
- Gdk::ACTION_COPY | Gdk::ACTION_MOVE);
- m_detWnd->signal_drag_leave().connect(sigc::mem_fun(this, &DnDUIX11::GtkDestDragLeaveCB));
- m_detWnd->signal_drag_motion().connect(sigc::mem_fun(this, &DnDUIX11::GtkDestDragMotionCB));
- m_detWnd->signal_drag_drop().connect(sigc::mem_fun(this, &DnDUIX11::GtkDestDragDropCB));
- m_detWnd->signal_drag_data_received().connect(sigc::mem_fun(this, &DnDUIX11::GtkDestDragDataReceivedCB));
+ mDetWnd->drag_dest_set(targets, Gtk::DEST_DEFAULT_MOTION,
+ Gdk::ACTION_COPY | Gdk::ACTION_MOVE);
+
+ mDetWnd->signal_drag_leave().connect(
+ sigc::mem_fun(this, &DnDUIX11::OnGtkDragLeave));
+ mDetWnd->signal_drag_motion().connect(
+ sigc::mem_fun(this, &DnDUIX11::OnGtkDragMotion));
+ mDetWnd->signal_drag_drop().connect(
+ sigc::mem_fun(this, &DnDUIX11::OnGtkDragDrop));
+ mDetWnd->signal_drag_data_received().connect(
+ sigc::mem_fun(this, &DnDUIX11::OnGtkDragDataReceived));
}
-/* Begin of callbacks issued by common layer code */
-/**
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * DnDUIX11::ResetUI --
*
- * Reset Callback to reset dnd ui state.
+ * Reset UI state variables.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * May remove a vmblock blocking entry.
+ *
+ *-----------------------------------------------------------------------------
*/
void
-DnDUIX11::CommonResetCB(void)
+DnDUIX11::ResetUI()
{
TRACE_CALL();
- m_GHDnDDataReceived = false;
- m_HGGetFileStatus = DND_FILE_TRANSFER_NOT_STARTED;
- m_GHDnDInProgress = false;
- m_effect = DROP_NONE;
- m_inHGDrag = false;
- m_dc = NULL;
+ mGHDnDDataReceived = false;
+ mHGGetFileStatus = DND_FILE_TRANSFER_NOT_STARTED;
+ mGHDnDInProgress = false;
+ mEffect = DROP_NONE;
+ mInHGDrag = false;
+ mDragCtx = NULL;
RemoveBlock();
}
/* Source functions for HG DnD. */
-/**
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * DnDUIX11::OnSrcDragBegin --
+ *
+ * Called when host successfully detected a pending HG drag.
+ *
+ * Results:
+ * None.
*
- * Called when host successfully detected a pending HG drag.
+ * Side effects:
+ * Calls mDetWnd->drag_begin().
*
- * param[in] clip cross-platform clipboard
- * param[in] stagingDir associated staging directory
+ *-----------------------------------------------------------------------------
*/
void
-DnDUIX11::CommonDragStartCB(const CPClipboard *clip, std::string stagingDir)
+DnDUIX11::OnSrcDragBegin(const CPClipboard *clip, // IN
+ const std::string stagingDir) // IN
{
Glib::RefPtr<Gtk::TargetList> targets;
Gdk::DragAction actions;
GdkEventMotion event;
- CPClipboard_Clear(&m_clipboard);
- CPClipboard_Copy(&m_clipboard, clip);
-
TRACE_CALL();
+ CPClipboard_Clear(&mClipboard);
+ CPClipboard_Copy(&mClipboard, clip);
+
/*
* Before the DnD, we should make sure that the mouse is released
* otherwise it may be another DnD, not ours. Send a release, then
* a press here to cover this case.
*/
SendFakeXEvents(false, true, false, false, false, 0, 0);
- SendFakeXEvents(true, true, true, false, true, 0, 0);
+ SendFakeXEvents(true, true, true, true, true, mOrigin.get_x(), mOrigin.get_y());
/*
* Construct the target and action list, as well as a fake motion notify
*/
targets = Gtk::TargetList::create(std::list<Gtk::TargetEntry>());
- if (CPClipboard_ItemExists(&m_clipboard, CPFORMAT_FILELIST)) {
- m_HGStagingDir = stagingDir;
- if (!m_HGStagingDir.empty()) {
+ if (CPClipboard_ItemExists(&mClipboard, CPFORMAT_FILELIST)) {
+ mHGStagingDir = stagingDir;
+ if (!mHGStagingDir.empty()) {
targets->add(Glib::ustring(DRAG_TARGET_NAME_URI_LIST));
/* Add private data to tag dnd as originating from this vm. */
char *pid;
}
}
- if (CPClipboard_ItemExists(&m_clipboard, CPFORMAT_FILECONTENTS)) {
+ if (CPClipboard_ItemExists(&mClipboard, CPFORMAT_FILECONTENTS)) {
if (WriteFileContentsToStagingDir()) {
targets->add(Glib::ustring(DRAG_TARGET_NAME_URI_LIST));
}
}
- if (CPClipboard_ItemExists(&m_clipboard, CPFORMAT_TEXT)) {
+ if (CPClipboard_ItemExists(&mClipboard, CPFORMAT_TEXT)) {
targets->add(Glib::ustring(TARGET_NAME_STRING));
targets->add(Glib::ustring(TARGET_NAME_TEXT_PLAIN));
targets->add(Glib::ustring(TARGET_NAME_UTF8_STRING));
targets->add(Glib::ustring(TARGET_NAME_COMPOUND_TEXT));
}
- if (CPClipboard_ItemExists(&m_clipboard, CPFORMAT_RTF)) {
+ if (CPClipboard_ItemExists(&mClipboard, CPFORMAT_RTF)) {
targets->add(Glib::ustring(TARGET_NAME_APPLICATION_RTF));
targets->add(Glib::ustring(TARGET_NAME_TEXT_RICHTEXT));
}
/* TODO set the x/y coords to the actual drag initialization point. */
event.type = GDK_MOTION_NOTIFY;
- event.window = m_detWnd->get_window()->gobj();
+ event.window = mDetWnd->get_window()->gobj();
event.send_event = false;
event.time = GDK_CURRENT_TIME;
event.x = 10;
event.state = GDK_BUTTON1_MASK;
event.is_hint = 0;
event.device = gdk_device_get_core_pointer();
- event.x_root = 0;
- event.y_root = 5;
+ event.x_root = mOrigin.get_x();
+ event.y_root = mOrigin.get_y();
/* Tell Gtk that a drag should be started from this widget. */
- m_detWnd->drag_begin(targets, actions, 1, (GdkEvent *)&event);
- m_blockAdded = false;
- m_HGGetFileStatus = DND_FILE_TRANSFER_NOT_STARTED;
+ mDetWnd->drag_begin(targets, actions, 1, (GdkEvent *)&event);
+ mBlockAdded = false;
+ mHGGetFileStatus = DND_FILE_TRANSFER_NOT_STARTED;
SourceDragStartDone();
/* Initialize host hide feedback to DROP_NONE. */
- m_effect = DROP_NONE;
- SourceUpdateFeedback(m_effect);
+ mEffect = DROP_NONE;
+ SourceUpdateFeedback(mEffect);
}
-/**
+/*
+ *-----------------------------------------------------------------------------
*
- * Cancel current HG DnD.
+ * DnDUIX11::OnSrcCancel --
+ *
+ * Handler for when host cancels HG drag.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Detection window and fake mouse events.
+ *
+ *-----------------------------------------------------------------------------
*/
void
-DnDUIX11::CommonSourceCancelCB(void)
+DnDUIX11::OnSrcCancel()
{
TRACE_CALL();
* flybacks when we cancel as user moves mouse in and out of destination
* window in a H->G DnD.
*/
- CommonUpdateDetWndCB(true, 0, 0);
- SendFakeXEvents(true, true, false, true, true, 0, 0);
- CommonUpdateDetWndCB(false, 0, 0);
- m_inHGDrag = false;
- m_HGGetFileStatus = DND_FILE_TRANSFER_NOT_STARTED;
- m_effect = DROP_NONE;
+ OnUpdateDetWnd(true, 0, 0);
+ SendFakeXEvents(true, true, false, true, true, mOrigin.get_x(), mOrigin.get_y());
+ OnUpdateDetWnd(false, 0, 0);
+ mInHGDrag = false;
+ mHGGetFileStatus = DND_FILE_TRANSFER_NOT_STARTED;
+ mEffect = DROP_NONE;
RemoveBlock();
}
-/**
+/*
+ *-----------------------------------------------------------------------------
*
- * Handle common layer private drop CB.
+ * DnDUIX11::OnPrivateDrop --
*
- * @param[in] x position to release the mouse button (ignored).
- * @param[in] y position to release the mouse button (ignored).
+ * Handler for private drop event.
*
- * @note We ignore the coordinates, because we just need to release the mouse
- * in its current position.
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Releases mouse button at current position.
+ *
+ *-----------------------------------------------------------------------------
*/
void
-DnDUIX11::CommonDestPrivateDropCB(int32 x,
- int32 y)
+DnDUIX11::OnPrivateDrop(int32 x, // UNUSED
+ int32 y) // UNUSED
{
TRACE_CALL();
+
/* Unity manager in host side may already send the drop into guest. */
- if (m_GHDnDInProgress) {
+ if (mGHDnDInProgress) {
/*
* Release the mouse button.
*/
SendFakeXEvents(false, true, false, false, false, 0, 0);
}
- CommonResetCB();
+ ResetUI();
}
-/**
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * DnDUIX11::OnDestCancel --
*
- * Cancel current DnD (G->H only).
+ * Handler for GH drag cancellation.
+ *
+ * Note: This event fires as part of the complete guest-to-host sequence,
+ * not just error or user cancellation.
+ *
+ * Results:
+ * Uses detection window and fake mouse events to intercept drop.
+ *
+ * Side effects:
+ * Reinitializes UI state.
+ *
+ *-----------------------------------------------------------------------------
*/
void
-DnDUIX11::CommonDestCancelCB(void)
+DnDUIX11::OnDestCancel()
{
TRACE_CALL();
- /* Unity manager in host side may already send the drop into guest. */
- if (m_GHDnDInProgress) {
- CommonUpdateDetWndCB(true, 0, 0);
+ /* Unity manager in host side may already send the drop into guest. */
+ if (mGHDnDInProgress) {
/*
* Show the window, move it to the mouse position, and release the
* mouse button.
*/
- SendFakeXEvents(true, true, false, true, false, 0, 0);
+ SendFakeXEvents(true, true, false, true, false, mOrigin.get_x(),
+ mOrigin.get_y());
}
- m_destDropTime = GetTimeInMillis();
- CommonResetCB();
+ mDestDropTime = GetTimeInMillis();
+ ResetUI();
}
-/**
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * DnDUIX11::OnSrcDrop --
+ *
+ * Callback when host signals drop.
*
- * Got drop from host side. Release the mouse button in the detection window
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Release the mouse button in the detection window.
+ *
+ *-----------------------------------------------------------------------------
*/
void
-DnDUIX11::CommonSourceDropCB(void)
+DnDUIX11::OnSrcDrop()
{
TRACE_CALL();
- CommonUpdateDetWndCB(true, 0, 0);
+ OnUpdateDetWnd(true, mOrigin.get_x(), mOrigin.get_y());
/*
* Move the mouse to the saved coordinates, and release the mouse button.
*/
- SendFakeXEvents(false, true, false, false, true, m_mousePosX, m_mousePosY);
- CommonUpdateDetWndCB(false, 0, 0);
+ SendFakeXEvents(false, true, false, false, true, mMousePosX, mMousePosY);
+ OnUpdateDetWnd(false, 0, 0);
}
-/**
+/*
+ *-----------------------------------------------------------------------------
*
- * Callback when file transfer is done, which finishes the file
- * copying from host to guest staging directory.
+ * DnDUIX11::OnGetFilesDone --
*
- * @param[in] success if true, transfer was successful
+ * Callback when HG file transfer completes.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Releases vmblock blocking entry.
+ *
+ *-----------------------------------------------------------------------------
*/
void
-DnDUIX11::CommonSourceFileCopyDoneCB(bool success)
+DnDUIX11::OnGetFilesDone(bool success) // IN: true if transfer succeeded
{
g_debug("%s: %s\n", __FUNCTION__, success ? "success" : "failed");
/*
- * If hg drag is not done yet, only remove block. GtkSourceDragEndCB will
- * call CommonResetCB(). Otherwise destination may miss the data because
+ * If hg drag is not done yet, only remove block. OnGtkDragEnd will
+ * call ResetUI(). Otherwise destination may miss the data because
* we are already reset.
*/
- m_HGGetFileStatus = DND_FILE_TRANSFER_FINISHED;
+ mHGGetFileStatus = DND_FILE_TRANSFER_FINISHED;
- if (!m_inHGDrag) {
- CommonResetCB();
+ if (!mInHGDrag) {
+ ResetUI();
} else {
RemoveBlock();
}
}
-/**
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * DnDUIX11::OnUpdateDetWnd --
*
- * Shows/hides drag detection windows based on the mask.
+ * Callback to show/hide drag detection window.
*
- * @param[in] bShow if true, show the window, else hide it.
- * @param[in] x x-coordinate to which the detection window needs to be moved
- * @param[in] y y-coordinate to which the detection window needs to be moved
+ * Results:
+ * Shows/hides and moves detection window.
+ *
+ * Side effects:
+ * None.
+ *
+ *-----------------------------------------------------------------------------
*/
void
-DnDUIX11::CommonUpdateDetWndCB(bool bShow,
- int32 x,
- int32 y)
+DnDUIX11::OnUpdateDetWnd(bool show, // IN: show (true) or hide (false)
+ int32 x, // IN: detection window's destination x-coord
+ int32 y) // IN: detection window's destination y-coord
{
g_debug("%s: enter 0x%lx show %d x %d y %d\n",
__FUNCTION__,
- (unsigned long) m_detWnd->get_window()->gobj(), bShow, x, y);
+ (unsigned long) mDetWnd->get_window()->gobj(), show, x, y);
/* If the window is being shown, move it to the right place. */
- if (bShow) {
+ if (show) {
x = MAX(x - DRAG_DET_WINDOW_WIDTH / 2, 0);
y = MAX(y - DRAG_DET_WINDOW_WIDTH / 2, 0);
- m_detWnd->Show();
- m_detWnd->Raise();
- m_detWnd->SetGeometry(x, y, DRAG_DET_WINDOW_WIDTH * 2, DRAG_DET_WINDOW_WIDTH * 2);
+ mDetWnd->Show();
+ mDetWnd->Raise();
+ mDetWnd->SetGeometry(x, y, DRAG_DET_WINDOW_WIDTH * 2, DRAG_DET_WINDOW_WIDTH * 2);
g_debug("%s: show at (%d, %d, %d, %d)\n", __FUNCTION__, x, y, DRAG_DET_WINDOW_WIDTH * 2, DRAG_DET_WINDOW_WIDTH * 2);
/*
* Wiggle the mouse here. Especially for G->H DnD, this improves
*/
SendFakeMouseMove(x + 2, y + 2);
- m_detWnd->SetIsVisible(true);
+ mDetWnd->SetIsVisible(true);
} else {
g_debug("%s: hide\n", __FUNCTION__);
- m_detWnd->Hide();
- m_detWnd->SetIsVisible(false);
+ mDetWnd->Hide();
+ mDetWnd->SetIsVisible(false);
}
}
-/**
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * DnDUIX11::OnUpdateUnityDetWnd --
+ *
+ * Callback to show/hide fullscreen Unity drag detection window.
*
- * Shows/hides full-screen Unity drag detection window.
+ * Results:
+ * None.
*
- * @param[in] bShow if true, show the window, else hide it.
- * @param[in] unityWndId active front window
- * @param[in] bottom if true, adjust the z-order to be bottom most.
+ * Side effects:
+ * Detection window shown, hidden.
+ *
+ *-----------------------------------------------------------------------------
*/
void
-DnDUIX11::CommonUpdateUnityDetWndCB(bool bShow,
- uint32 unityWndId,
- bool bottom)
+DnDUIX11::OnUpdateUnityDetWnd(bool show, // IN: show (true) or hide (false)
+ uint32 unityWndId, // IN: XXX ?
+ bool bottom) // IN: place window at bottom of stack?
{
g_debug("%s: enter 0x%lx unityID 0x%x\n",
__FUNCTION__,
- (unsigned long) m_detWnd->get_window()->gobj(),
+ (unsigned long) mDetWnd->get_window()->gobj(),
unityWndId);
- if (bShow && ((unityWndId > 0) || bottom)) {
- int width = m_detWnd->GetScreenWidth();
- int height = m_detWnd->GetScreenHeight();
- m_detWnd->SetGeometry(0, 0, width, height);
- m_detWnd->Show();
+
+ if (show && ((unityWndId > 0) || bottom)) {
+ int width = mDetWnd->GetScreenWidth();
+ int height = mDetWnd->GetScreenHeight();
+ mDetWnd->SetGeometry(0, 0, width, height);
+ mDetWnd->Show();
if (bottom) {
- m_detWnd->Lower();
+ mDetWnd->Lower();
}
g_debug("%s: show, (0, 0, %d, %d)\n", __FUNCTION__, width, height);
} else {
- if (m_detWnd->GetIsVisible() == true) {
- if (m_unityMode) {
+ if (mDetWnd->GetIsVisible() == true) {
+ if (mUnityMode) {
/*
* Show and move detection window to current mouse position
SendFakeXEvents(true, false, true, true, false, 0, 0);
}
} else {
- m_detWnd->Hide();
+ mDetWnd->Hide();
g_debug("%s: hide\n", __FUNCTION__);
}
}
}
-/**
+/*
+ *-----------------------------------------------------------------------------
*
- * Move detection windows to current cursor position.
+ * DnDUIX11::OnDestMoveDetWndToMousePos --
+ *
+ * Callback to move detection window to current moue position.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Detection window is moved, shown.
+ *
+ *-----------------------------------------------------------------------------
*/
void
-DnDUIX11::CommonMoveDetWndToMousePos(void)
+DnDUIX11::OnDestMoveDetWndToMousePos()
{
SendFakeXEvents(true, false, true, true, false, 0, 0);
}
-/**
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * DnDUIX11::OnMoveMouse --
+ *
+ * Callback to update mouse position.
+ *
+ * Results:
+ * None.
*
- * Handle request from common layer to update mouse position.
+ * Side effects:
+ * Moves mouse. Duh.
*
- * @param[in] x x coordinate of pointer
- * @param[in] y y coordinate of pointer
+ *-----------------------------------------------------------------------------
*/
void
-DnDUIX11::CommonUpdateMouseCB(int32 x,
- int32 y)
+DnDUIX11::OnMoveMouse(int32 x, // IN: Pointer x-coord
+ int32 y) // IN: Pointer y-coord
{
// Position the pointer, and record its position.
SendFakeXEvents(false, false, false, false, true, x, y);
- m_mousePosX = x;
- m_mousePosY = y;
+ mMousePosX = x;
+ mMousePosY = y;
- if (m_dc && !m_GHDnDInProgress) {
+ if (mDragCtx && !mGHDnDInProgress) {
// If we are the context of a DnD, send DnD feedback to the source.
DND_DROPEFFECT effect;
- effect = ToDropEffect((Gdk::DragAction)(m_dc->action));
- if (effect != m_effect) {
- m_effect = effect;
+ effect = ToDropEffect((Gdk::DragAction)(mDragCtx->action));
+ if (effect != mEffect) {
+ mEffect = effect;
g_debug("%s: Updating feedback\n", __FUNCTION__);
- SourceUpdateFeedback(m_effect);
+ SourceUpdateFeedback(mEffect);
}
}
}
-/* Beginning of Gtk+ Callbacks */
/*
- * Source callbacks from Gtk+. Most are seen only when we are acting as a
- * drag source.
+ ****************************************************************************
+ * BEGIN GTK+ Callbacks (dndcp as drag source: host-to-guest)
*/
-/**
+
+/*
+ *-----------------------------------------------------------------------------
*
- * "drag_motion" signal handler for GTK. We should respond by setting drag
- * status. Note that there is no drag enter signal. We need to figure out
- * if a new drag is happening on our own. Also, we don't respond with a
- * "allowed" drag status right away, we start a new drag operation over VMDB
- * (which tries to notify the host of the new operation). Once the host has
- * responded), we respond with a proper drag status.
- *
- * @param[in] dc associated drag context
- * @param[in] x x coordinate of the drag motion
- * @param[in] y y coordinate of the drag motion
- * @param[in] time time of the drag motion
- *
- * @return returning false means we won't get notified of future motion. So,
- * we only return false if we don't recognize the types being offered. We
- * return true otherwise, even if we don't accept the drag right now for some
- * other reason.
- *
- * @note you may see this callback during DnD when detection window is acting
- * as a source. In that case it will be ignored. In a future refactoring,
- * we will try and avoid this.
+ * DnDUIX11::OnGtkDragMotion --
+ *
+ * GTK "drag_motion" signal handler.
+ *
+ * We should respond by setting drag status. Note that there is no drag
+ * enter signal. We need to figure out if a new drag is happening on
+ * our own. Also, we don't respond with a "allowed" drag status right
+ * away, we start a new drag operation with the host (which tries to
+ * notify the host of the new operation). Once the host has responded),
+ * we respond with a proper drag status.
+ *
+ * Note: You may see this callback during DnD when detection window
+ * is acting as a source. In that case it will be ignored. In a future
+ * refactoring, we will try and avoid this.
+ *
+ * Results:
+ * Returns true unless we don't recognize the types offered.
+ *
+ * Side effects:
+ * Via RequestData issues a Gtk::Widget::drag_get_data.
+ *
+ *-----------------------------------------------------------------------------
*/
bool
-DnDUIX11::GtkDestDragMotionCB(const Glib::RefPtr<Gdk::DragContext> &dc,
- int x,
- int y,
- guint timeValue)
+DnDUIX11::OnGtkDragMotion(
+ const Glib::RefPtr<Gdk::DragContext> &dc, // IN: GTK drag context
+ int x, // IN: drag motion x-coord
+ int y, // IN: drag motion y-coord
+ guint timeValue) // IN: event timestamp
{
/*
* If this is a Host to Guest drag, we are done here, so return.
*/
unsigned long curTime = GetTimeInMillis();
- g_debug("%s: enter dc %p, m_dc %p\n", __FUNCTION__,
- dc ? dc->gobj() : NULL, m_dc ? m_dc : NULL);
- if (curTime - m_destDropTime <= 1000) {
+ g_debug("%s: enter dc %p, mDragCtx %p\n", __FUNCTION__,
+ dc ? dc->gobj() : NULL, mDragCtx ? mDragCtx : NULL);
+ if (curTime - mDestDropTime <= 1000) {
g_debug("%s: ignored %ld %ld %ld\n", __FUNCTION__,
- curTime, m_destDropTime, curTime - m_destDropTime);
+ curTime, mDestDropTime, curTime - mDestDropTime);
return true;
}
g_debug("%s: not ignored %ld %ld %ld\n", __FUNCTION__,
- curTime, m_destDropTime, curTime - m_destDropTime);
+ curTime, mDestDropTime, curTime - mDestDropTime);
- if (m_inHGDrag || (m_HGGetFileStatus != DND_FILE_TRANSFER_NOT_STARTED)) {
+ if (mInHGDrag || (mHGGetFileStatus != DND_FILE_TRANSFER_NOT_STARTED)) {
g_debug("%s: ignored not in hg drag or not getting hg data\n", __FUNCTION__);
return true;
}
Gdk::DragAction srcActions;
Gdk::DragAction suggestedAction;
Gdk::DragAction dndAction = (Gdk::DragAction)0;
- Glib::ustring target = m_detWnd->drag_dest_find_target(dc);
+ Glib::ustring target = mDetWnd->drag_dest_find_target(dc);
- if (!m_DnD->IsDnDAllowed()) {
+ if (!mDnD->IsDnDAllowed()) {
g_debug("%s: No dnd allowed!\n", __FUNCTION__);
dc->drag_status(dndAction, timeValue);
return true;
return true;
}
- m_dc = dc->gobj();
+ mDragCtx = dc->gobj();
if (target != "") {
/*
if (dndAction != (Gdk::DragAction)0) {
dc->drag_status(dndAction, timeValue);
- if (!m_GHDnDInProgress) {
+ if (!mGHDnDInProgress) {
g_debug("%s: new drag, need to get data for host\n", __FUNCTION__);
/*
* This is a new drag operation. We need to start a drag thru the
* backdoor, and to the host. Before we can tell the host, we have to
* retrieve the drop data.
*/
- m_GHDnDInProgress = true;
+ mGHDnDInProgress = true;
/* only begin drag enter after we get the data */
/* Need to grab all of the data. */
if (!RequestData(dc, timeValue)) {
}
-/**
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * DnDUIX11::OnGtkDragLeave --
*
- * "drag_leave" signal handler for GTK. Log the reception of this signal,
- * but otherwise unhandled in our implementation.
+ * GTK+ "drag_leave" signal handler.
*
- * @param[in] dc drag context
- * @param[in] time time of the drag
+ * Logs event. Acknowledges, finishes outdated sequence if drag context
+ * is not the same as we're currently interested in (i.e. != mDragCtx).
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * May cancel dnd associated with this drag context.
+ *
+ *-----------------------------------------------------------------------------
*/
void
-DnDUIX11::GtkDestDragLeaveCB(const Glib::RefPtr<Gdk::DragContext> &dc,
- guint time)
+DnDUIX11::OnGtkDragLeave(
+ const Glib::RefPtr<Gdk::DragContext> &dc, // IN: GTK drag context
+ guint time) // IN: event timestamp
{
- g_debug("%s: enter dc %p, m_dc %p\n", __FUNCTION__,
- dc ? dc->gobj() : NULL, m_dc ? m_dc : NULL);
+ g_debug("%s: enter dc %p, mDragCtx %p\n", __FUNCTION__,
+ dc ? dc->gobj() : NULL, mDragCtx ? mDragCtx : NULL);
/*
* If we reach here after reset DnD, or we are getting a late
* be 5 minutes).
* See http://bugzilla.eng.vmware.com/show_bug.cgi?id=528320
*/
- if (!m_dc || dc->gobj() != m_dc) {
+ if (!mDragCtx || dc->gobj() != mDragCtx) {
g_debug("%s: calling drag_finish\n", __FUNCTION__);
dc->drag_finish(true, false, time);
}
/*
- * Gtk+ callbacks that are seen when we are a drag source.
- */
-
-/**
+ *-----------------------------------------------------------------------------
+ *
+ * DnDUIX11::OnGtkDragBegin --
+ *
+ * GTK+ "drag_begin" signal handler.
+ *
+ * Results:
+ * None.
*
- * "drag_begin" signal handler for GTK.
+ * Side effects:
+ * Records drag context for later use.
*
- * @param[in] context drag context
+ *-----------------------------------------------------------------------------
*/
void
-DnDUIX11::GtkSourceDragBeginCB(const Glib::RefPtr<Gdk::DragContext>& context)
+DnDUIX11::OnGtkDragBegin(
+ const Glib::RefPtr<Gdk::DragContext>& context) // IN: GTK drag context
{
- g_debug("%s: enter dc %p, m_dc %p\n", __FUNCTION__,
- context ? context->gobj() : NULL, m_dc ? m_dc : NULL);
- m_dc = context->gobj();
+ g_debug("%s: enter dc %p, mDragCtx %p\n", __FUNCTION__,
+ context ? context->gobj() : NULL, mDragCtx ? mDragCtx : NULL);
+ mDragCtx = context->gobj();
}
-/**
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * DnDUIX11::OnGtkDragDataGet --
+ *
+ * GTK+ "drag_data_get" signal handler.
*
- * "drag_data_get" handler for GTK. We don't send drop until we are done.
+ * Results:
+ * None.
*
- * @param[in] dc drag state
- * @param[in] selection_data buffer for data
- * @param[in] info unused
- * @param[in] time timestamp
+ * Side effects:
+ * May insert vmblock blocking entry and request host-to-guest file transfer from
+ * host.
*
- * @note if the drop has occurred, the files are copied from the guest.
+ * If unable to obtain drag information, may instead cancel the DND
+ * operation.
*
*-----------------------------------------------------------------------------
*/
void
-DnDUIX11::GtkSourceDragDataGetCB(const Glib::RefPtr<Gdk::DragContext> &dc,
- Gtk::SelectionData& selection_data,
- guint info,
- guint time)
+DnDUIX11::OnGtkDragDataGet(
+ const Glib::RefPtr<Gdk::DragContext> &dc, // IN: GTK drag context
+ Gtk::SelectionData& selection_data, // IN: drag details
+ guint info, // UNUSED
+ guint time) // IN: event timestamp
{
size_t index = 0;
std::string str;
selection_data.set(target.c_str(), "");
- g_debug("%s: enter dc %p, m_dc %p with target %s\n", __FUNCTION__,
- dc ? dc->gobj() : NULL, m_dc ? m_dc : NULL,
- target.c_str());
+ g_debug("%s: enter dc %p, mDragCtx %p with target %s\n", __FUNCTION__,
+ dc ? dc->gobj() : NULL, mDragCtx ? mDragCtx : NULL,
+ target.c_str());
- if (!m_inHGDrag) {
+ if (!mInHGDrag) {
g_debug("%s: not in drag, return\n", __FUNCTION__);
return;
}
- if (target == DRAG_TARGET_NAME_URI_LIST &&
- CPClipboard_GetItem(&m_clipboard, CPFORMAT_FILELIST, &buf, &sz)) {
+ if ( target == DRAG_TARGET_NAME_URI_LIST
+ && CPClipboard_GetItem(&mClipboard, CPFORMAT_FILELIST, &buf, &sz)) {
/* Provide path within vmblock file system instead of actual path. */
- stagingDirName = GetLastDirName(m_HGStagingDir);
+ stagingDirName = GetLastDirName(mHGStagingDir);
if (stagingDirName.length() == 0) {
g_debug("%s: Cannot get staging directory name, stagingDir: %s\n",
- __FUNCTION__, m_HGStagingDir.c_str());
+ __FUNCTION__, mHGStagingDir.c_str());
return;
}
post = DND_URI_LIST_POST;
} else {
g_debug("%s: Unknown request target: %s\n", __FUNCTION__,
- selection_data.get_target().c_str());
+ selection_data.get_target().c_str());
return;
}
/* Provide URIs for each path in the guest's file list. */
while ((str = GetNextPath(hgData, index).c_str()).length() != 0) {
uriList += pre;
- if (DnD_BlockIsReady(m_blockCtrl)) {
- uriList += m_blockCtrl->blockRoot;
+ if (DnD_BlockIsReady(mBlockCtrl)) {
+ uriList += mBlockCtrl->blockRoot;
uriList += DIRSEPS + stagingDirName + DIRSEPS + str + post;
} else {
- uriList += DIRSEPS + m_HGStagingDir + DIRSEPS + str + post;
+ uriList += DIRSEPS + mHGStagingDir + DIRSEPS + str + post;
}
}
* Doing both of these addresses bug
* http://bugzilla.eng.vmware.com/show_bug.cgi?id=391661.
*/
- if (!m_blockAdded &&
- m_inHGDrag &&
- (m_HGGetFileStatus == DND_FILE_TRANSFER_NOT_STARTED)) {
- m_HGGetFileStatus = DND_FILE_TRANSFER_IN_PROGRESS;
+ if ( !mBlockAdded
+ && mInHGDrag
+ && (mHGGetFileStatus == DND_FILE_TRANSFER_NOT_STARTED)) {
+ mHGGetFileStatus = DND_FILE_TRANSFER_IN_PROGRESS;
AddBlock();
} else {
g_debug("%s: not calling AddBlock\n", __FUNCTION__);
return;
}
- if (target == DRAG_TARGET_NAME_URI_LIST &&
- CPClipboard_ItemExists(&m_clipboard, CPFORMAT_FILECONTENTS)) {
+ if ( target == DRAG_TARGET_NAME_URI_LIST
+ && CPClipboard_ItemExists(&mClipboard, CPFORMAT_FILECONTENTS)) {
g_debug("%s: Providing uriList [%s] for file contents DnD\n",
- __FUNCTION__, m_HGFileContentsUriList.c_str());
+ __FUNCTION__, mHGFileContentsUriList.c_str());
selection_data.set(DRAG_TARGET_NAME_URI_LIST,
- m_HGFileContentsUriList.c_str());
+ mHGFileContentsUriList.c_str());
return;
}
- if ((target == TARGET_NAME_STRING ||
- target == TARGET_NAME_TEXT_PLAIN ||
- target == TARGET_NAME_UTF8_STRING ||
- target == TARGET_NAME_COMPOUND_TEXT) &&
- CPClipboard_GetItem(&m_clipboard, CPFORMAT_TEXT, &buf, &sz)) {
+ if ( TargetIsPlainText(target)
+ && CPClipboard_GetItem(&mClipboard, CPFORMAT_TEXT, &buf, &sz)) {
g_debug("%s: providing plain text, size %"FMTSZ"u\n", __FUNCTION__, sz);
selection_data.set(target.c_str(), (const char *)buf);
return;
}
- if ((target == TARGET_NAME_APPLICATION_RTF ||
- target == TARGET_NAME_TEXT_RICHTEXT) &&
- CPClipboard_GetItem(&m_clipboard, CPFORMAT_RTF, &buf, &sz)) {
+ if ( TargetIsRichText(target)
+ && CPClipboard_GetItem(&mClipboard, CPFORMAT_RTF, &buf, &sz)) {
g_debug("%s: providing rtf text, size %"FMTSZ"u\n", __FUNCTION__, sz);
selection_data.set(target.c_str(), (const char *)buf);
return;
/* Can not get any valid data, cancel this HG DnD. */
g_debug("%s: no valid data for HG DnD\n", __FUNCTION__);
- CommonResetCB();
+ ResetUI();
}
-/**
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * DnDUIX11::OnGtkDragEnd --
+ *
+ * GTK+ "drag_end" signal handler.
*
- * "drag_end" handler for GTK. Received by drag source.
+ * Results:
+ * None.
*
- * @param[in] dc drag state
+ * Side effects:
+ * May reset UI state.
+ *
+ *-----------------------------------------------------------------------------
*/
void
-DnDUIX11::GtkSourceDragEndCB(const Glib::RefPtr<Gdk::DragContext> &dc)
+DnDUIX11::OnGtkDragEnd(
+ const Glib::RefPtr<Gdk::DragContext> &dc) // IN: GTK drag context
{
- g_debug("%s: entering dc %p, m_dc %p\n", __FUNCTION__,
- dc ? dc->gobj() : NULL, m_dc ? m_dc : NULL);
+ g_debug("%s: entering dc %p, mDragCtx %p\n", __FUNCTION__,
+ dc ? dc->gobj() : NULL, mDragCtx ? mDragCtx : NULL);
/*
* We may see a drag end for the previous DnD, but after a new
* DnD has started. If so, ignore it.
*/
- if (m_dc && dc && (m_dc != dc->gobj())) {
+ if (mDragCtx && dc && (mDragCtx != dc->gobj())) {
g_debug("%s: got old dc (new DnD started), ignoring\n", __FUNCTION__);
return;
}
/*
* If we are a file DnD and file transfer is not done yet, don't call
- * CommonResetCB() here, since we will do so in the fileCopyDoneChanged
+ * ResetUI() here, since we will do so in the fileCopyDoneChanged
* callback.
*/
- if (DND_FILE_TRANSFER_IN_PROGRESS != m_HGGetFileStatus) {
- CommonResetCB();
+ if (DND_FILE_TRANSFER_IN_PROGRESS != mHGGetFileStatus) {
+ ResetUI();
}
- m_inHGDrag = false;
+ mInHGDrag = false;
}
-/* Gtk+ callbacks seen when we are a drag destination. */
-/**
+/*
+ * END GTK+ Callbacks (dndcp as drag source: host-to-guest)
+ ****************************************************************************
+ */
+
+
+/*
+ ****************************************************************************
+ * BEGIN GTK+ Callbacks (dndcp as drag destination: guest-to-host)
+ */
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * DnDUIX11::OnGtkDragDataReceived --
+ *
+ * GTK+ "drag_data_received" signal handler.
*
- * "drag_data_received" signal handler for GTK. We requested the drag
- * data earlier from some drag source on the guest; this is the response.
+ * Results:
+ * None.
*
- * This is for G->H DnD.
+ * Side effects:
+ * May signal to host beginning of guest-to-host DND.
*
- * @param[in] dc drag context
- * @param[in] x where the drop happened
- * @param[in] y where the drop happened
- * @param[in] sd the received data
- * @param[in] info the info that has been registered with the target in the
- * target list.
- * @param[in] time the timestamp at which the data was received.
+ *-----------------------------------------------------------------------------
*/
void
-DnDUIX11::GtkDestDragDataReceivedCB(const Glib::RefPtr<Gdk::DragContext> &dc,
- int x,
- int y,
- const Gtk::SelectionData& sd,
- guint info,
- guint time)
+DnDUIX11::OnGtkDragDataReceived(
+ const Glib::RefPtr<Gdk::DragContext> &dc, // IN: GTK drag context
+ int x, // IN: drop location x-coord
+ int y, // IN: drop location y-coord
+ const Gtk::SelectionData& sd, // IN: drag content details
+ guint info, // UNUSED
+ guint time) // IN: event timestamp
{
- g_debug("%s: enter dc %p, m_dc %p\n", __FUNCTION__,
- dc ? dc->gobj() : NULL, m_dc ? m_dc : NULL);
+ g_debug("%s: enter dc %p, mDragCtx %p\n", __FUNCTION__,
+ dc ? dc->gobj() : NULL, mDragCtx ? mDragCtx : NULL);
/* The GH DnD may already finish before we got response. */
- if (!m_GHDnDInProgress) {
+ if (!mGHDnDInProgress) {
g_debug("%s: not valid\n", __FUNCTION__);
return;
}
*/
if (SetCPClipboardFromGtk(sd) == false) {
g_debug("%s: Failed to set CP clipboard.\n", __FUNCTION__);
- CommonResetCB();
+ ResetUI();
return;
}
- m_numPendingRequest--;
- if (m_numPendingRequest > 0) {
+ mNumPendingRequest--;
+ if (mNumPendingRequest > 0) {
return;
}
- if (CPClipboard_IsEmpty(&m_clipboard)) {
+ if (CPClipboard_IsEmpty(&mClipboard)) {
g_debug("%s: Failed getting item.\n", __FUNCTION__);
- CommonResetCB();
+ ResetUI();
return;
}
* Note that we prevent against sending multiple "dragStart"s or "drop"s for
* each DnD.
*/
- if (!m_GHDnDDataReceived) {
+ if (!mGHDnDDataReceived) {
g_debug("%s: Drag entering.\n", __FUNCTION__);
- m_GHDnDDataReceived = true;
+ mGHDnDDataReceived = true;
TargetDragEnter();
} else {
- g_debug("%s: not !m_GHDnDDataReceived\n", __FUNCTION__);
+ g_debug("%s: not !mGHDnDDataReceived\n", __FUNCTION__);
}
}
-/**
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * DnDUIX11::OnGtkDragDrop --
+ *
+ * GTK+ "drag_drop" signal handler.
*
- * "drag_drop" signal handler for GTK. Send the drop to the host (by
- * way of the backdoor), then tell the host to get the files.
+ * Results:
+ * Returns true so long as drag target and data are (at one point)
+ * provided (i.e. not a spurious event).
*
- * @param[in] dc drag context
- * @param[in] x x location of the drop
- * @param[in] y y location of the drop
- * @param[in] time timestamp for the drop
+ * Side effects:
+ * Signals to drag source that drop is finished.
*
- * @return true on success, false otherwise.
+ *-----------------------------------------------------------------------------
*/
bool
-DnDUIX11::GtkDestDragDropCB(const Glib::RefPtr<Gdk::DragContext> &dc,
- int x,
- int y,
- guint time)
+DnDUIX11::OnGtkDragDrop(
+ const Glib::RefPtr<Gdk::DragContext> &dc, // IN: GTK drag context
+ int x, // IN: drop location x-coord
+ int y, // IN: drop location y-coord
+ guint time) // IN: motion event timestamp
{
- g_debug("%s: enter dc %p, m_dc %p x %d y %d\n", __FUNCTION__,
- (dc ? dc->gobj() : NULL), (m_dc ? m_dc : NULL), x, y);
+ g_debug("%s: enter dc %p, mDragCtx %p x %d y %d\n", __FUNCTION__,
+ (dc ? dc->gobj() : NULL), (mDragCtx ? mDragCtx : NULL), x, y);
Glib::ustring target;
- target = m_detWnd->drag_dest_find_target(dc);
+ target = mDetWnd->drag_dest_find_target(dc);
g_debug("%s: calling drag_finish\n", __FUNCTION__);
dc->drag_finish(true, false, time);
return false;
}
- if (CPClipboard_IsEmpty(&m_clipboard)) {
- g_debug("%s: No valid data on m_clipboard.\n", __FUNCTION__);
+ if (CPClipboard_IsEmpty(&mClipboard)) {
+ g_debug("%s: No valid data on mClipboard.\n", __FUNCTION__);
return false;
}
return true;
}
-/* General utility functions */
-/**
+/*
+ * END GTK+ Callbacks (dndcp as drag destination: guest-to-host)
+ ****************************************************************************
+ */
+
+
+/*
+ *-----------------------------------------------------------------------------
*
- * Try to construct cross-platform clipboard data from selection data
- * provided to us by Gtk+.
+ * DnDUIX11::SetCPClipboardFromGtk --
*
- * @param[in] sd Gtk selection data to convert to CP clipboard data
+ * Construct cross-platform clipboard from GTK+ selection_data.
*
- * @return false on failure, true on success
+ * Results:
+ * Returns true if conversion succeeded, false otherwise.
+ *
+ * Side effects:
+ * None.
+ *
+ *-----------------------------------------------------------------------------
*/
bool
const utf::string target = sd.get_target().c_str();
/* Try to get file list. */
- if (m_DnD->CheckCapability(DND_CP_CAP_FILE_DND) && target == DRAG_TARGET_NAME_URI_LIST) {
+ if (mDnD->CheckCapability(DND_CP_CAP_FILE_DND) && target == DRAG_TARGET_NAME_URI_LIST) {
/*
* Turn the uri list into two \0 delimited lists. One for full paths and
* one for just the last path component.
DynBuf_Init(&buf);
fileList.SetFileSize(totalSize);
if (fileList.ToCPClipboard(&buf, false)) {
- CPClipboard_SetItem(&m_clipboard, CPFORMAT_FILELIST, DynBuf_Get(&buf),
+ CPClipboard_SetItem(&mClipboard, CPFORMAT_FILELIST, DynBuf_Get(&buf),
DynBuf_GetSize(&buf));
}
DynBuf_Destroy(&buf);
}
/* Try to get plain text. */
- if (m_DnD->CheckCapability(DND_CP_CAP_PLAIN_TEXT_DND) && (
- target == TARGET_NAME_STRING ||
- target == TARGET_NAME_TEXT_PLAIN ||
- target == TARGET_NAME_UTF8_STRING ||
- target == TARGET_NAME_COMPOUND_TEXT)) {
+ if ( mDnD->CheckCapability(DND_CP_CAP_PLAIN_TEXT_DND)
+ && TargetIsPlainText(target)) {
std::string source = sd.get_data_as_string();
- if (source.size() > 0 &&
- source.size() < DNDMSG_MAX_ARGSZ &&
- CPClipboard_SetItem(&m_clipboard, CPFORMAT_TEXT, source.c_str(),
- source.size() + 1)) {
+ if ( source.size() > 0
+ && source.size() < DNDMSG_MAX_ARGSZ
+ && CPClipboard_SetItem(&mClipboard, CPFORMAT_TEXT, source.c_str(),
+ source.size() + 1)) {
g_debug("%s: Got text, size %"FMTSZ"u\n", __FUNCTION__, source.size());
} else {
g_debug("%s: Failed to get text\n", __FUNCTION__);
}
/* Try to get RTF string. */
- if (m_DnD->CheckCapability(DND_CP_CAP_RTF_DND) && (
- target == TARGET_NAME_APPLICATION_RTF ||
- target == TARGET_NAME_TEXT_RICHTEXT)) {
+ if ( mDnD->CheckCapability(DND_CP_CAP_RTF_DND)
+ && TargetIsRichText(target)) {
std::string source = sd.get_data_as_string();
- if (source.size() > 0 &&
- source.size() < DNDMSG_MAX_ARGSZ &&
- CPClipboard_SetItem(&m_clipboard, CPFORMAT_RTF, source.c_str(),
- source.size() + 1)) {
+ if ( source.size() > 0
+ && source.size() < DNDMSG_MAX_ARGSZ
+ && CPClipboard_SetItem(&mClipboard, CPFORMAT_RTF, source.c_str(),
+ source.size() + 1)) {
g_debug("%s: Got RTF, size %"FMTSZ"u\n", __FUNCTION__, source.size());
return true;
} else {
}
-/**
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * DnDUIX11::RequestData --
+ *
+ * Requests clipboard data from a drag source.
*
- * Ask for clipboard data from drag source.
+ * Evaluates targets (think MIME types) offered by the drag source, and
+ * if we support any, requests the contents.
*
- * @param[in] dc Associated drag context
- * @param[in] time Time of the request
+ * Results:
+ * Returns true if we found a supported type.
*
- * @return true if there is any data request, false otherwise.
+ * Side effects:
+ * May call drag_get_data.
+ *
+ *-----------------------------------------------------------------------------
*/
bool
-DnDUIX11::RequestData(const Glib::RefPtr<Gdk::DragContext> &dc,
- guint time)
+DnDUIX11::RequestData(
+ const Glib::RefPtr<Gdk::DragContext> &dc, // IN: GTK drag context
+ guint time) // IN: event timestamp
{
Glib::RefPtr<Gtk::TargetList> targets;
targets = Gtk::TargetList::create(std::list<Gtk::TargetEntry>());
- CPClipboard_Clear(&m_clipboard);
- m_numPendingRequest = 0;
+ CPClipboard_Clear(&mClipboard);
+ mNumPendingRequest = 0;
/*
* First check file list. If file list is available, all other formats will
* be ignored.
*/
targets->add(Glib::ustring(DRAG_TARGET_NAME_URI_LIST));
- Glib::ustring target = m_detWnd->drag_dest_find_target(dc, targets);
+ Glib::ustring target = mDetWnd->drag_dest_find_target(dc, targets);
targets->remove(Glib::ustring(DRAG_TARGET_NAME_URI_LIST));
if (target != "") {
- m_detWnd->drag_get_data(dc, target, time);
- m_numPendingRequest++;
+ mDetWnd->drag_get_data(dc, target, time);
+ mNumPendingRequest++;
return true;
}
targets->add(Glib::ustring(TARGET_NAME_STRING));
targets->add(Glib::ustring(TARGET_NAME_TEXT_PLAIN));
targets->add(Glib::ustring(TARGET_NAME_COMPOUND_TEXT));
- target = m_detWnd->drag_dest_find_target(dc, targets);
+ target = mDetWnd->drag_dest_find_target(dc, targets);
targets->remove(Glib::ustring(TARGET_NAME_STRING));
targets->remove(Glib::ustring(TARGET_NAME_TEXT_PLAIN));
targets->remove(Glib::ustring(TARGET_NAME_UTF8_STRING));
targets->remove(Glib::ustring(TARGET_NAME_COMPOUND_TEXT));
if (target != "") {
- m_detWnd->drag_get_data(dc, target, time);
- m_numPendingRequest++;
+ mDetWnd->drag_get_data(dc, target, time);
+ mNumPendingRequest++;
}
/* Then check RTF. */
targets->add(Glib::ustring(TARGET_NAME_APPLICATION_RTF));
targets->add(Glib::ustring(TARGET_NAME_TEXT_RICHTEXT));
- target = m_detWnd->drag_dest_find_target(dc, targets);
+ target = mDetWnd->drag_dest_find_target(dc, targets);
targets->remove(Glib::ustring(TARGET_NAME_APPLICATION_RTF));
targets->remove(Glib::ustring(TARGET_NAME_TEXT_RICHTEXT));
if (target != "") {
- m_detWnd->drag_get_data(dc, target, time);
- m_numPendingRequest++;
+ mDetWnd->drag_get_data(dc, target, time);
+ mNumPendingRequest++;
}
- return (m_numPendingRequest > 0);
+ return (mNumPendingRequest > 0);
}
-/**
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * DnDUIX11::GetLastDirName --
+ *
+ * Try to get last directory name from a full path name.
+ *
+ * What this really means is to get the basename of the parent's directory
+ * name, intended to isolate an individual DND operation's staging directory
+ * name.
*
- * Try to get last directory name from a full path name.
+ * E.g. /tmp/VMwareDnD/abcd137/foo → abcd137
*
- * @param[in] str pathname to process
+ * Results:
+ * Returns session directory name on success, empty string otherwise.
*
- * @return last dir name in the full path name if sucess, empty str otherwise
+ * Side effects:
+ * None.
+ *
+ *-----------------------------------------------------------------------------
*/
std::string
}
-/**
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * DnDUIX11::GetNextPath --
*
- * Provide a substring containing the next path from the provided
- * NUL-delimited string starting at the provided index.
+ * Convoluted somethingerother.
*
- * @param[in] str NUL-delimited path list
- * @param[in] index current index into string
+ * XXX Something here involves URI parsing and encoding. Get to the bottom
+ * of this and use shared URI code.
*
- * @return a string with the next path or "" if there are no more paths.
+ * Original description:
+ * Provide a substring containing the next path from the provided
+ * NUL-delimited string starting at the provided index.
+ *
+ * Results:
+ * Returns "a string with the next path or empty string if there are no
+ * more paths".
+ *
+ * Side effects:
+ * Updates index.
+ *
+ *-----------------------------------------------------------------------------
*/
utf::utf8string
-DnDUIX11::GetNextPath(utf::utf8string& str,
- size_t& index)
+DnDUIX11::GetNextPath(utf::utf8string& str, // IN: NUL-delimited path list
+ size_t& index) // IN/OUT: index into string
{
utf::utf8string ret;
size_t start;
}
-/**
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * DnDUIX11::SendFakeMouseMove --
*
- * Issue a fake mouse move event to the detection window. Code stolen from
- * DnD V2 Linux guest implementation, where it was originally defined as a
- * macro.
+ * Issue a fake mouse move event to the detection window.
*
- * @param[in] x x-coordinate of location to move mouse to.
- * @param[in] y y-coordinate of location to move mouse to.
+ * Results:
+ * Returns true on success, false on failure.
*
- * @return true on success, false on failure.
+ * Side effects:
+ * Generates mouse events.
+ *
+ *-----------------------------------------------------------------------------
*/
bool
-DnDUIX11::SendFakeMouseMove(const int x,
- const int y)
+DnDUIX11::SendFakeMouseMove(const int x, // IN: x-coord
+ const int y) // IN: y-coord
{
return SendFakeXEvents(false, false, false, false, true, x, y);
}
-/**
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * DnDUIX11::SendFakeXEvents --
+ *
+ * Fake X mouse events and window movement for the detection window.
+ *
+ * This function shows the detection window and generates button
+ * press/release and pointer motion events.
*
- * Fake X mouse events and window movement for the provided Gtk widget.
+ * XXX This code should be implemented using GDK APIs.
+ * (gdk_display_warp_pointer?)
*
- * This function will optionally show the widget, move the provided widget
- * to either the provided location or the current mouse position if no
- * coordinates are provided, and cause a button press or release event.
+ * XXX This code should be moved into the detection window class
*
- * @param[in] showWidget whether to show Gtk widget
- * @param[in] buttonEvent whether to send a button event
- * @param[in] buttonPress whether to press or release mouse
- * @param[in] moveWindow: whether to move our window too
- * @param[in] coordsProvided whether coordinates provided
- * @param[in] xCoord x coordinate
- * @param[in] yCoord y coordinate
+ * Results:
+ * Returns true if generated X events, false on failure.
*
- * @note todo this code should be implemented using GDK APIs.
- * @note todo this code should be moved into the detection window class
+ * Side effects:
+ * A ton of things.
*
- * @return true on success, false on failure.
+ *-----------------------------------------------------------------------------
*/
bool
-DnDUIX11::SendFakeXEvents(const bool showWidget,
- const bool buttonEvent,
- const bool buttonPress,
- const bool moveWindow,
- const bool coordsProvided,
- const int xCoord,
- const int yCoord)
+DnDUIX11::SendFakeXEvents(
+ const bool showWidget, // IN: whether to show widget
+ const bool buttonEvent, // IN: whether to send a button event
+ const bool buttonPress, // IN: whether button event is press or release
+ const bool moveWindow, // IN: whether to move detection window
+ const bool coordsProvided, // IN: whether coords provided, else will
+ // query current mouse position
+ const int xCoord, // IN: destination x-coord
+ const int yCoord) // IN: destination y-coord
{
GtkWidget *widget;
Window rootWnd;
/*
* Position away from the edge of the window.
*/
- int width = m_detWnd->GetScreenWidth();
- int height = m_detWnd->GetScreenHeight();
+ int width = mDetWnd->GetScreenWidth();
+ int height = mDetWnd->GetScreenHeight();
bool change = false;
x = rootXReturn;
goto exit;
}
- if ((maskReturn & Button1Mask) ||
- (maskReturn & Button2Mask) ||
- (maskReturn & Button3Mask) ||
- (maskReturn & Button4Mask) ||
- (maskReturn & Button5Mask)) {
+ if ( (maskReturn & Button1Mask)
+ || (maskReturn & Button2Mask)
+ || (maskReturn & Button3Mask)
+ || (maskReturn & Button4Mask)
+ || (maskReturn & Button5Mask)) {
Debug("%s: XTestFakeButtonEvent was not working for button "
"release, trying XTestFakeDeviceButtonEvent now.\n",
__FUNCTION__);
}
-/**
- * Fake X mouse events in device level.
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * DnDUIX11::TryXTestFakeDeviceButtonEvent --
+ *
+ * Fake X mouse events in device level.
*
- * XXX The function will only be called if XTestFakeButtonEvent does not work
- * for mouse button release. Later on we may only call this one for mouse
- * button simulation if this is more reliable.
+ * XXX The function will only be called if XTestFakeButtonEvent does
+ * not work for mouse button release. Later on we may only call this
+ * one for mouse button simulation if this is more reliable.
*
- * @return true on success, false on failure.
+ * Results:
+ * Returns true on success, false on failure.
+ *
+ * Side effects:
+ * Generates mouse events.
+ *
+ *-----------------------------------------------------------------------------
*/
bool
-DnDUIX11::TryXTestFakeDeviceButtonEvent(void)
+DnDUIX11::TryXTestFakeDeviceButtonEvent()
{
XDeviceInfo *list = NULL;
XDeviceInfo *list2 = NULL;
}
-/**
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * DnDUIX11::GetDetWndAsWidget --
+ *
+ * Get the GtkWidget pointer for a DragDetWnd object.
+ *
+ * The X11 Unity implementation requires access to the drag detection
+ * window as a GtkWindow pointer, which it uses to show and hide the
+ * detection window.
+ *
+ * This function is also called by the code that issues fake X events
+ * to the detection window.
+ *
+ * Results:
+ * A GtkWidget* on success or NULL on failure.
*
- * Get the GtkWidget pointer for a DragDetWnd object. The X11 Unity
- * implementation requires access to the drag detection window as
- * a GtkWindow pointer, which it uses to show and hide the detection
- * window. This function is also called by the code that issues fake
- * X events to the detection window.
+ * Side effects:
+ * None.
*
- * @return a pointer to the GtkWidget for the detection window, or NULL
- * on failure.
+ *-----------------------------------------------------------------------------
*/
GtkWidget *
GtkInvisible *window;
GtkWidget *widget = NULL;
- if (!m_detWnd) {
+ if (!mDetWnd) {
return NULL;
}
- window = m_detWnd->gobj();
+ window = mDetWnd->gobj();
if (window) {
widget = GTK_WIDGET(window);
}
}
-/**
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * DnDUIX11::AddBlock --
*
- * Add a block for the current H->G file transfer. Must be paired with a
- * call to RemoveBlock() on finish or cancellation.
+ * Insert a vmblock blocking entry.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Caller must pair with RemoveBlock() upon dnd completion/cancellation.
+ *
+ *-----------------------------------------------------------------------------
*/
void
DnDUIX11::AddBlock()
{
TRACE_CALL();
- if (m_blockAdded) {
+
+ if (mBlockAdded) {
g_debug("%s: block already added\n", __FUNCTION__);
return;
}
- g_debug("%s: DnDBlockIsReady %d fd %d\n", __FUNCTION__, DnD_BlockIsReady(m_blockCtrl), m_blockCtrl->fd);
- if (DnD_BlockIsReady(m_blockCtrl) && m_blockCtrl->AddBlock(m_blockCtrl->fd, m_HGStagingDir.c_str())) {
- m_blockAdded = true;
- g_debug("%s: add block for %s.\n", __FUNCTION__, m_HGStagingDir.c_str());
+
+ g_debug("%s: DnDBlockIsReady %d fd %d\n", __FUNCTION__,
+ DnD_BlockIsReady(mBlockCtrl), mBlockCtrl->fd);
+
+ if ( DnD_BlockIsReady(mBlockCtrl)
+ && mBlockCtrl->AddBlock(mBlockCtrl->fd, mHGStagingDir.c_str())) {
+ mBlockAdded = true;
+ g_debug("%s: add block for %s.\n", __FUNCTION__, mHGStagingDir.c_str());
} else {
- m_blockAdded = false;
- g_debug("%s: unable to add block dir %s.\n", __FUNCTION__, m_HGStagingDir.c_str());
+ mBlockAdded = false;
+ g_debug("%s: unable to add block dir %s.\n", __FUNCTION__, mHGStagingDir.c_str());
}
}
-/**
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * DnDUIX11::RemoveBlock --
*
- * Remove block for the current H->G file transfer. Must be paired with a
- * call to AddBlock(), but it will only attempt to remove block if one is
- * currently in effect.
+ * Remove a vmblock blocking entry.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *-----------------------------------------------------------------------------
*/
void
DnDUIX11::RemoveBlock()
{
TRACE_CALL();
- if (m_blockAdded && (DND_FILE_TRANSFER_IN_PROGRESS != m_HGGetFileStatus)) {
- g_debug("%s: removing block for %s\n", __FUNCTION__, m_HGStagingDir.c_str());
+
+ if (mBlockAdded && (DND_FILE_TRANSFER_IN_PROGRESS != mHGGetFileStatus)) {
+ g_debug("%s: removing block for %s\n", __FUNCTION__, mHGStagingDir.c_str());
/* We need to make sure block subsystem has not been shut off. */
- if (DnD_BlockIsReady(m_blockCtrl)) {
- m_blockCtrl->RemoveBlock(m_blockCtrl->fd, m_HGStagingDir.c_str());
+ if (DnD_BlockIsReady(mBlockCtrl)) {
+ mBlockCtrl->RemoveBlock(mBlockCtrl->fd, mHGStagingDir.c_str());
}
- m_blockAdded = false;
+ mBlockAdded = false;
} else {
- g_debug("%s: not removing block m_blockAdded %d m_HGGetFileStatus %d\n",
+ g_debug("%s: not removing block mBlockAdded %d mHGGetFileStatus %d\n",
__FUNCTION__,
- m_blockAdded,
- m_HGGetFileStatus);
+ mBlockAdded,
+ mHGGetFileStatus);
}
}
-/**
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * DnDUIX11::ToDropEffect --
*
- * Convert a Gdk::DragAction value to its corresponding DND_DROPEFFECT.
+ * Convert a Gdk::DragAction value to its corresponding DND_DROPEFFECT.
*
- * @param[in] the Gdk::DragAction value to return.
+ * Results:
+ * Returns corresponding DND_DROPEFFECT or DROP_UNKNOWN if a match isn't
+ * found.
*
- * @return the corresponding DND_DROPEFFECT, with DROP_UNKNOWN returned
- * if no mapping is supported.
+ * Side effects:
+ * None.
*
- * @note DROP_NONE is not mapped in this function.
+ *-----------------------------------------------------------------------------
*/
-DND_DROPEFFECT
-DnDUIX11::ToDropEffect(Gdk::DragAction action)
+/* static */ DND_DROPEFFECT
+DnDUIX11::ToDropEffect(const Gdk::DragAction action)
{
DND_DROPEFFECT effect;
}
-/**
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * DnDUIX11::WriteFileContentsToStagingDir --
*
- * Try to extract file contents from m_clipboard. Write all files to a
- * temporary staging directory. Construct uri list.
+ * Try to extract file contents from mClipboard. Write all files to a
+ * temporary staging directory. Construct uri list.
*
- * @return true if success, false otherwise.
+ * Results:
+ * Returns true on success, false on failure.
+ *
+ * Side effects:
+ * Hm?
+ *
+ *-----------------------------------------------------------------------------
*/
bool
-DnDUIX11::WriteFileContentsToStagingDir(void)
+DnDUIX11::WriteFileContentsToStagingDir()
{
void *buf = NULL;
size_t sz = 0;
size_t i = 0;
bool ret = false;
- if (!CPClipboard_GetItem(&m_clipboard, CPFORMAT_FILECONTENTS, &buf, &sz)) {
+ if (!CPClipboard_GetItem(&mClipboard, CPFORMAT_FILECONTENTS, &buf, &sz)) {
return false;
}
goto exit;
}
- m_HGFileContentsUriList = "";
+ mHGFileContentsUriList = "";
for (i = 0; i < nFiles; i++) {
utf::string fileName;
filePathName = tempDir;
filePathName += DIRSEPS + fileName;
- if (fileItem[i].validFlags & CP_FILE_VALID_TYPE &&
- CP_FILE_TYPE_DIRECTORY == fileItem[i].type) {
+ if ( fileItem[i].validFlags & CP_FILE_VALID_TYPE
+ && CP_FILE_TYPE_DIRECTORY == fileItem[i].type) {
if (!File_CreateDirectory(filePathName.c_str())) {
goto exit;
}
- g_debug("%s: created directory [%s].\n",
- __FUNCTION__, filePathName.c_str());
- } else if (fileItem[i].validFlags & CP_FILE_VALID_TYPE &&
- CP_FILE_TYPE_REGULAR == fileItem[i].type) {
+ g_debug("%s: created directory [%s].\n", __FUNCTION__, filePathName.c_str());
+ } else if ( fileItem[i].validFlags & CP_FILE_VALID_TYPE
+ && CP_FILE_TYPE_REGULAR == fileItem[i].type) {
FileIODescriptor file;
FileIOResult fileErr;
NULL);
FileIO_Close(&file);
- g_debug("%s: created file [%s].\n",
- __FUNCTION__, filePathName.c_str());
+ g_debug("%s: created file [%s].\n", __FUNCTION__, filePathName.c_str());
} else {
/*
* Right now only Windows can provide CPFORMAT_FILECONTENTS data.
writeTime,
attrChangeTime)) {
/* Not a critical error, only log it. */
- g_debug("%s: File_SetTimes failed with file [%s].\n",
- __FUNCTION__, filePathName.c_str());
+ g_debug("%s: File_SetTimes failed with file [%s].\n", __FUNCTION__,
+ filePathName.c_str());
}
/* Update file permission attributes. */
if (fileItem->validFlags & CP_FILE_VALID_PERMS) {
- if (Posix_Chmod(filePathName.c_str(),
- fileItem->permissions) < 0) {
+ if (Posix_Chmod(filePathName.c_str(), fileItem->permissions) < 0) {
/* Not a critical error, only log it. */
- g_debug("%s: Posix_Chmod failed with file [%s].\n",
- __FUNCTION__, filePathName.c_str());
+ g_debug("%s: Posix_Chmod failed with file [%s].\n", __FUNCTION__,
+ filePathName.c_str());
}
}
* top level one. We only put top level name into uri list.
*/
if (fileName.find(DIRSEPS, 0) == utf::string::npos) {
- m_HGFileContentsUriList += "file://" + filePathName + "\r\n";
+ mHGFileContentsUriList += "file://" + filePathName + "\r\n";
}
}
- g_debug("%s: created uri list [%s].\n",
- __FUNCTION__, m_HGFileContentsUriList.c_str());
+ g_debug("%s: created uri list [%s].\n", __FUNCTION__, mHGFileContentsUriList.c_str());
ret = true;
exit:
}
-/**
- * Tell host that we are done with HG DnD initialization.
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * DnDUIX11::SourceDragStartDone --
+ *
+ * Tell host that we're done with host-to-guest drag-and-drop
+ * initialization.
+ *
+ *-----------------------------------------------------------------------------
*/
void
-DnDUIX11::SourceDragStartDone(void)
+DnDUIX11::SourceDragStartDone()
{
TRACE_CALL();
- m_inHGDrag = true;
- m_DnD->SrcUIDragBeginDone();
+ mInHGDrag = true;
+ mDnD->SrcUIDragBeginDone();
}
-/**
- * Set block control member.
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * DnDUIX11::SetBlockControl --
*
- * @param[in] block control as setup by vmware-user.
+ * Set block control member.
+ *
+ *-----------------------------------------------------------------------------
*/
void
-DnDUIX11::SetBlockControl(DnDBlockControl *blockCtrl)
+DnDUIX11::SetBlockControl(DnDBlockControl *blockCtrl) // IN
{
- m_blockCtrl = blockCtrl;
+ mBlockCtrl = blockCtrl;
}
-/**
- * Got feedback from our DropSource, send it over to host. Called by
- * drag motion callback.
+/*
+ *-----------------------------------------------------------------------------
*
- * @param[in] effect feedback to send to the UI-independent DnD layer.
+ * DnDUIX11::SourceUpdateFeedback --
+ *
+ * Forward feedback from our drop source to the host.
+ *
+ *-----------------------------------------------------------------------------
*/
void
-DnDUIX11::SourceUpdateFeedback(DND_DROPEFFECT effect)
+DnDUIX11::SourceUpdateFeedback(
+ DND_DROPEFFECT effect) // IN: feedback to send to the UI-independent DnD layer.
{
TRACE_CALL();
- m_DnD->SrcUIUpdateFeedback(effect);
+ mDnD->SrcUIUpdateFeedback(effect);
}
-/**
- * This is triggered when user drags valid data from guest to host. Try to
- * get clip data and notify host to start GH DnD.
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * DnDUIX11::TargetDragEnter --
+ *
+ * With the source's drag selection data on the clipboard, signal to
+ * host to begin guest-to-host drag-and-drop.
+ *
+ *-----------------------------------------------------------------------------
*/
void
-DnDUIX11::TargetDragEnter(void)
+DnDUIX11::TargetDragEnter()
{
TRACE_CALL();
/* Check if there is valid data with current detection window. */
- if (!CPClipboard_IsEmpty(&m_clipboard)) {
+ if (!CPClipboard_IsEmpty(&mClipboard)) {
g_debug("%s: got valid data from detWnd.\n", __FUNCTION__);
- m_DnD->DestUIDragEnter(&m_clipboard);
+ mDnD->DestUIDragEnter(&mClipboard);
}
/*
}
-/**
- * Get Unix time in milliseconds. See man 2 gettimeofday for details.
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * DnDUIX11::GetTimeInMillis --
*
- * @return unix time in milliseconds.
+ * Get Unix time in milliseconds.
+ *
+ *-----------------------------------------------------------------------------
*/
-unsigned long
-DnDUIX11::GetTimeInMillis(void)
+/* static */ unsigned long
+DnDUIX11::GetTimeInMillis()
{
VmTimeType atime;
}
-/**
- * Update version information in m_DnD.
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * DnDUIX11::VmXDnDVersionChanged --
*
- * @param[ignored] chan RpcChannel pointer
- * @param[in] version the version negotiated with host.
+ * Update version information in mDnD.
+ *
+ *-----------------------------------------------------------------------------
*/
void
-DnDUIX11::VmxDnDVersionChanged(RpcChannel *chan, uint32 version)
+DnDUIX11::VmxDnDVersionChanged(RpcChannel *chan, // IN
+ uint32 version) // IN
{
- ASSERT(m_DnD);
- m_DnD->VmxDnDVersionChanged(version);
+ ASSERT(mDnD);
+ mDnD->VmxDnDVersionChanged(version);
}
}
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * DnDUIX11::OnWorkAreaChanged --
+ *
+ * Updates mOrigin in response to changes to _NET_workArea.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+void
+DnDUIX11::OnWorkAreaChanged(Glib::RefPtr<Gdk::Screen> screen) // IN
+{
+ TRACE_CALL();
+
+ std::vector<unsigned long> values;
+ if ( xutils::GetCardinalList(screen->get_root_window(), "_NET_WORKAREA", values)
+ && values.size() > 0
+ && values.size() % 4 == 0) {
+ /*
+ * wm-spec: _NET_WORKAREA, x, y, width, height CARDINAL[][4]/32
+ *
+ * For the purposes of drag-and-drop, we're okay with using the screen-
+ * agnostic _NET_WORKAREA atom, as the guest VM really deals with only
+ * one logical monitor.
+ */
+ unsigned long desktop = 0;
+ xutils::GetCardinal(screen->get_root_window(), "_NET_CURRENT_DESKTOP", desktop);
+
+ mOrigin.set_x(values[0 + 4 * desktop]);
+ mOrigin.set_y(values[1 + 4 * desktop]);
+ } else {
+ mOrigin.set_x(0);
+ mOrigin.set_y(0);
+ }
+
+ g_debug("%s: new origin at (%d, %d)\n", __FUNCTION__, mOrigin.get_x(),
+ mOrigin.get_y());
+}
--- /dev/null
+/*********************************************************
+ * Copyright (C) 2008-2013 VMware, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation version 2.1 and no later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the Lesser GNU General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ *********************************************************/
+
+
+#include <cairomm/cairomm.h>
+#include <gdkmm.h>
+#if GTK_MAJOR_VERSION == 3
+#include <gdkmm/devicemanager.h>
+#endif
+
+#include "xutils/xutils.hh"
+
+/* These must be after the gtkmm includes, as gtkmm is quite picky. */
+#include <X11/Xlib.h>
+#include <X11/Xatom.h>
+#include <gdk/gdkx.h>
+
+extern "C" {
+#include "vm_assert.h"
+}
+
+
+namespace xutils {
+
+
+/* Actual definitions of the signals in this class. */
+sigc::signal<void, Glib::RefPtr<Gdk::Screen> > currentDesktopChanged;
+sigc::signal<void, Glib::RefPtr<Gdk::Screen> > desktopLayoutChanged;
+sigc::signal<void, Glib::RefPtr<Gdk::Screen> > desktopGeometryChanged;
+sigc::signal<void, Glib::RefPtr<Gdk::Screen> > desktopViewportChanged;
+sigc::signal<void, Glib::RefPtr<Gdk::Screen> > windowStackChanged;
+sigc::signal<void, Glib::RefPtr<Gdk::Screen> > windowManagerChanged;
+sigc::signal<void, Glib::RefPtr<Gdk::Screen> > activeWindowChanged;
+sigc::signal<void, Glib::RefPtr<Gdk::Screen> > workAreaChanged;
+
+/* Necessary for calculating per-monitor _NET_WORKAREA in GetMonitorWorkArea() */
+struct NETWMStrutPartial {
+ NETWMStrutPartial()
+ : left_width(0),
+ left_start(0),
+ left_end(0),
+ right_width(0),
+ right_start(0),
+ right_end(0),
+ top_height(0),
+ top_start(0),
+ top_end(0),
+ bottom_height(0),
+ bottom_start(0),
+ bottom_end(0) {}
+
+ int left_width, left_start, left_end;
+ int right_width, right_start, right_end;
+ int top_height, top_start, top_end;
+ int bottom_height, bottom_start, bottom_end;
+};
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * xutils::OnWindowFilter --
+ *
+ * Window filter handler that listens for changes to the properties we
+ * care about and emits the appropriate signals.
+ *
+ * Results:
+ * GDK_FILTER_CONTINUE, always.
+ *
+ * Side effects:
+ * None.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+GdkFilterReturn
+OnWindowFilter(XEvent* xevent, // IN: Incoming event
+ GdkEvent* event, // OUT/UNUSED
+ GdkScreen* _screen) // IN: The screen
+{
+ Glib::RefPtr<Gdk::Screen> screen = Glib::wrap(_screen, true);
+ ::Display* xdisplay = xevent->xany.display;
+ GdkDisplay* display = gdk_x11_lookup_xdisplay(xdisplay);
+ Window rootWin = GDK_WINDOW_XID(screen->get_root_window()->gobj());
+
+#define ATOM(name) gdk_x11_get_xatom_by_name_for_display(display, (name))
+
+ if (xevent->type == PropertyNotify &&
+ xevent->xproperty.window == rootWin) {
+ if (xevent->xproperty.atom == ATOM("_NET_CLIENT_LIST_STACKING")) {
+ windowStackChanged.emit(screen);
+ } else if (xevent->xproperty.atom == ATOM("_NET_DESKTOP_LAYOUT")) {
+ desktopLayoutChanged.emit(screen);
+ } else if (xevent->xproperty.atom == ATOM("_NET_NUMBER_OF_DESKTOPS")) {
+ desktopLayoutChanged.emit(screen);
+ } else if (xevent->xproperty.atom == ATOM("_NET_CURRENT_DESKTOP")) {
+ currentDesktopChanged.emit(screen);
+ } else if (xevent->xproperty.atom == ATOM("_NET_DESKTOP_GEOMETRY")) {
+ desktopGeometryChanged.emit(screen);
+ } else if (xevent->xproperty.atom == ATOM("_NET_DESKTOP_VIEWPORT")) {
+ desktopViewportChanged.emit(screen);
+ } else if (xevent->xproperty.atom == ATOM("_NET_SUPPORTING_WM_CHECK")) {
+ windowManagerChanged.emit(screen);
+ } else if (xevent->xproperty.atom == ATOM("_NET_ACTIVE_WINDOW")) {
+ activeWindowChanged.emit(screen);
+ } else if (xevent->xproperty.atom == ATOM("_NET_WORKAREA")) {
+ workAreaChanged.emit(screen);
+ }
+ }
+
+#undef ATOM
+
+ return GDK_FILTER_CONTINUE;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * xutils::Init --
+ *
+ * Base initialization function that sets up the window filter. This
+ * is required if any signals are to be used.
+ *
+ * This can be called more than once.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+void
+Init()
+{
+ static bool initialized = false;
+
+ if (!initialized) {
+ initialized = true;
+
+ /*
+ * Select PropertyChange events on the root window so that we can
+ * listen for when the host window stack changes and update our
+ * copy.
+ */
+ Glib::RefPtr<Gdk::Display> display = Gdk::Display::get_default();
+ ::Display* xdisplay = GDK_DISPLAY_XDISPLAY(display->gobj());
+
+ for (int i = 0; i < display->get_n_screens(); i++) {
+ Glib::RefPtr<Gdk::Screen> screen = display->get_screen(i);
+ Glib::RefPtr<Gdk::Window> rootWin = screen->get_root_window();
+ Window xRootWin = GDK_WINDOW_XID(rootWin->gobj());
+
+ int mask = PropertyChangeMask;
+
+#if GTK_MAJOR_VERSION == 3
+ if (gdk_x11_window_lookup_for_display(
+ display->gobj(), xRootWin) != NULL) {
+#else
+ if (gdk_xid_table_lookup(xRootWin) != NULL) {
+#endif
+ /* Make sure we don't interfere with GDK. */
+ XWindowAttributes attrs;
+ XGetWindowAttributes(xdisplay, xRootWin, &attrs);
+ mask |= attrs.your_event_mask;
+ }
+
+ XSelectInput(xdisplay, xRootWin, mask);
+
+ gdk_window_add_filter(rootWin->gobj(), (GdkFilterFunc)OnWindowFilter,
+ screen->gobj());
+ }
+ }
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * xutils::GetCardinal --
+ *
+ * Utility function to get a cardinal from a window property.
+ *
+ * Results:
+ * true if the function succeeded, along with a value for retValue.
+ * Otherwise false.
+ *
+ * Side effects:
+ * None.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+bool
+GetCardinal(Glib::RefPtr<const Gdk::Window> window, // IN: Window
+ const utf::string& atomName, // IN: Atom name
+ unsigned long& retValue) // OUT: Return value
+{
+ ASSERT(window);
+ ASSERT(!atomName.empty());
+
+ std::vector<unsigned long> retValues;
+ bool result = GetCardinalList(window, atomName, retValues);
+
+ if (result && retValues.size() == 1) {
+ retValue = retValues[0];
+ return true;
+ }
+
+ return false;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * xutils::GetCardinalList --
+ *
+ * Utility function to get a cardinal list from a window property.
+ *
+ * Results:
+ * true if the function succeeded, along with return values for retValue.
+ * Otherwise false.
+ *
+ * Side effects:
+ * None.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+bool
+GetCardinalList(Glib::RefPtr<const Gdk::Window> window, // IN: Window
+ const utf::string& atomName, // IN: Atom name
+ std::vector<unsigned long>& retValues) // IN: Return values
+{
+ ASSERT(window);
+ ASSERT(window->get_display());
+ ASSERT(!atomName.empty());
+
+ GdkDisplay* display = const_cast<GdkDisplay*>(window->get_display()->gobj());
+ GdkWindow* gdkwin = const_cast<GdkWindow*>(window->gobj());
+
+ Atom atom = gdk_x11_get_xatom_by_name_for_display(display, atomName.c_str());
+
+ Atom type;
+ int format;
+ unsigned long nitems;
+ unsigned long bytes_after;
+ uint8* values;
+
+ gdk_error_trap_push();
+ int ret = XGetWindowProperty(GDK_DISPLAY_XDISPLAY(display),
+ GDK_WINDOW_XID(gdkwin),
+ atom, 0, G_MAXLONG, False, XA_CARDINAL, &type,
+ &format, &nitems, &bytes_after, &values);
+ int err = gdk_error_trap_pop();
+
+ if (ret == Success && !err) {
+ if (type == XA_CARDINAL && nitems > 0) {
+ retValues.resize(nitems);
+
+ if (format == 8) {
+ for (unsigned long i = 0; i < nitems; i++) {
+ retValues[i] = values[i];
+ }
+ } else if (format == 16) {
+ uint16* shortValues = (uint16*)values;
+ for (unsigned long i = 0; i < nitems; i++) {
+ retValues[i] = shortValues[i];
+ }
+ } else if (format == 32) {
+ unsigned long* longValues = (unsigned long*)values;
+ for (unsigned long i = 0; i < nitems; i++) {
+ retValues[i] = longValues[i];
+ }
+ } else {
+ NOT_IMPLEMENTED();
+ }
+
+ XFree(values);
+ return true;
+ }
+
+ XFree(values);
+ }
+
+ return false;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * xutils::SetDesktopForWindow --
+ *
+ * Sets the virtual desktop that a window is on. This takes care of
+ * the workspace part of the desktop. Viewports must be handled
+ * separately by moving the window.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+void
+SetDesktopForWindow(Glib::RefPtr<Gdk::Window> window, // IN:
+ uint32 desktop) // IN:
+{
+ GdkScreen* screen = window->get_screen()->gobj();
+ Atom tempDesktop = desktop; // Cast for 64-bit correctness.
+ Window win = GDK_WINDOW_XID(window->gobj());
+ Display* display = GDK_WINDOW_XDISPLAY(window->gobj());
+
+ Atom atom = gdk_x11_get_xatom_by_name_for_display(
+ window->get_display()->gobj(), "_NET_WM_DESKTOP");
+
+ gdk_error_trap_push();
+ XChangeProperty(display, win, atom,
+ XA_CARDINAL, 32, PropModeReplace,
+ (unsigned char*)&tempDesktop, 1);
+ gdk_flush();
+ int err = gdk_error_trap_pop();
+
+ if (err) {
+ Warning("Unable to move host window (XID %d) to desktop %d\n",
+ (int)GDK_WINDOW_XID(window->gobj()), desktop);
+ }
+
+ XEvent ev;
+ ev.xclient.type = ClientMessage;
+ ev.xclient.serial = 0;
+ ev.xclient.send_event = True;
+ ev.xclient.window = win;
+ ev.xclient.message_type = atom;
+ ev.xclient.format = 32;
+ ev.xclient.data.l[0] = desktop;
+ ev.xclient.data.l[1] = 2; // source (2 gives full control)
+ ev.xclient.data.l[2] = 0; // unused
+ ev.xclient.data.l[3] = 0; // unused
+ ev.xclient.data.l[4] = 0; // unused
+
+ gdk_error_trap_push();
+ XSendEvent(display,
+ GDK_WINDOW_XID(gdk_screen_get_root_window(screen)),
+ False, SubstructureRedirectMask | SubstructureNotifyMask,
+ &ev);
+ gdk_flush();
+ err = gdk_error_trap_pop();
+
+ if (err) {
+ Warning("Unable to move host window (XID %d) to desktop %d\n",
+ (int)GDK_WINDOW_XID(window->gobj()), desktop);
+ }
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * xutils::SetFullscreenMonitorsHint --
+ *
+ * Sets the _NET_WM_FULLSCREEN_MONITORS hint for the passed in window and
+ * monitor indices.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+void
+SetFullscreenMonitorsHint(Glib::RefPtr<Gdk::Window> window, // IN:
+ std::vector<long> monitors) // IN:
+{
+ // monitors contains 4 monitor indices, per EWMH spec
+ ASSERT(monitors.size() == 4);
+
+ Display* display = GDK_WINDOW_XDISPLAY(window->gobj());
+
+ XClientMessageEvent xclient;
+ memset(&xclient, 0, sizeof xclient);
+ xclient.type = ClientMessage;
+ xclient.window = GDK_WINDOW_XID(window->gobj());
+ xclient.message_type = XInternAtom(display,
+ "_NET_WM_FULLSCREEN_MONITORS",
+ False);
+ xclient.format = 32;
+
+ for (int i = 0; i < 4; i++) {
+ xclient.data.l[i] = monitors[i];
+ }
+
+ xclient.data.l[4] = 1;
+
+ XSendEvent(display,
+ GDK_WINDOW_XID(gdk_get_default_root_window()),
+ False,
+ SubstructureRedirectMask | SubstructureNotifyMask,
+ (XEvent *) &xclient);
+
+ XSync(display, False);
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * xutils::GetDesktopForWindow --
+ *
+ * Retrieve the virtual desktop that a given window is shown on.
+ *
+ * Results:
+ * The index of the virtual desktop.
+ *
+ * Side effects:
+ * None.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+uint32
+GetDesktopForWindow(Glib::RefPtr<const Gdk::Window> window) // IN:
+{
+ unsigned long result = 0;
+ GetCardinal(window, "_NET_WM_DESKTOP", result);
+ return result;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * xutils::GetNumDesktops --
+ *
+ * Returns the number of virtual desktops.
+ *
+ * Results:
+ * The number of virtual desktops.
+ *
+ * Side effects:
+ * None.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+uint32
+GetNumDesktops(Glib::RefPtr<Gdk::Screen> screen) // IN:
+{
+ unsigned long result = 0;
+ GetCardinal(screen->get_root_window(), "_NET_NUMBER_OF_DESKTOPS", result);
+ return result;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * xutils::GetCurrentDesktop --
+ *
+ * Retrieve the current virtual desktop for the screen.
+ *
+ * Results:
+ * The index of the virtual desktop.
+ *
+ * Side effects:
+ * None.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+uint32
+GetCurrentDesktop(Glib::RefPtr<Gdk::Screen> screen) // IN
+{
+ unsigned long result = 0;
+ GetCardinal(screen->get_root_window(), "_NET_CURRENT_DESKTOP", result);
+ return result;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * xutils::GetDesktopLayout --
+ *
+ * Retrieves the current virtual desktop layout for the screen.
+ *
+ * Results:
+ * The virtual desktop layout information is set and the function
+ * returns true if successful. Otherwise false is returned.
+ *
+ * Side effects:
+ * None.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+bool
+GetDesktopLayout(Glib::RefPtr<Gdk::Screen> screen, // IN: Screen
+ uint32& rows, // OUT: Rows
+ uint32& columns, // OUT: Columns
+ Gtk::CornerType& corner, // OUT: Corner of the first
+ // desktop.
+ Gtk::Orientation& orientation) // OUT: Desktop orientation
+{
+ std::vector<unsigned long> values;
+
+ if (GetCardinalList(screen->get_root_window(),
+ "_NET_DESKTOP_LAYOUT", values)) {
+ switch (values[0]) {
+ case 0:
+ orientation = Gtk::ORIENTATION_HORIZONTAL;
+ break;
+
+ case 1:
+ orientation = Gtk::ORIENTATION_VERTICAL;
+ break;
+
+ default:
+ Warning("Unsupported orientation in _NET_DESKTOP_LAYOUT\n");
+ return false;
+ }
+
+ columns = static_cast<uint32>(values[1]);
+ rows = static_cast<uint32>(values[2]);
+
+ if (columns == 0 && rows == 0) {
+ Warning("Invalid desktop configuration in _NET_DESKTOP_LAYOUT. "
+ "Rows and columns are both 0!\n");
+ return false;
+ } else if (columns == 0 || rows == 0) {
+ uint32 numDesktops = GetNumDesktops(screen);
+
+ if (columns == 0) {
+ columns = numDesktops / rows +
+ ((numDesktops % rows) > 0 ? 1 : 0);
+ } else if (rows == 0) {
+ rows = numDesktops / columns +
+ ((numDesktops % columns) > 0 ? 1 : 0);
+ }
+ }
+
+ corner = Gtk::CORNER_TOP_LEFT;
+
+ if (values.size() == 4) {
+ switch (values[3]) {
+ case 0:
+ corner = Gtk::CORNER_TOP_LEFT;
+ break;
+
+ case 1:
+ corner = Gtk::CORNER_TOP_RIGHT;
+ break;
+
+ case 2:
+ corner = Gtk::CORNER_BOTTOM_RIGHT;
+ break;
+
+ case 3:
+ corner = Gtk::CORNER_BOTTOM_LEFT;
+ break;
+
+ default:
+ Warning("Unsupported corner in _NET_DESKTOP_LAYOUT\n");
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * xutils::GetDesktopGeometry --
+ *
+ * Retrieves the desktop geometry for this screen.
+ *
+ * Results:
+ * The desktop geometry is set and the function returns true if
+ * successful. Otherwise false is returned.
+ *
+ * Side effects:
+ * None.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+bool
+GetDesktopGeometry(Glib::RefPtr<Gdk::Screen> screen, // IN: The screen
+ uint32& width, // OUT: Width
+ uint32& height) // OUT: Height
+{
+ std::vector<unsigned long> values;
+
+ if (GetCardinalList(screen->get_root_window(),
+ "_NET_DESKTOP_GEOMETRY", values)) {
+ if (values.size() == 2) {
+ width = values[0];
+ height = values[1];
+ return true;
+ }
+ }
+
+ return false;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * xutils::GetDesktopViewport --
+ *
+ * Retrieves the viewport of the specified virtual desktop.
+ *
+ * Results:
+ * The viewport is set and the function returns true if successful.
+ * Otherwise false is returned.
+ *
+ * Side effects:
+ * None.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+bool
+GetDesktopViewport(Glib::RefPtr<Gdk::Screen> screen, // IN: The screen
+ uint32 desktopIndex, // IN: Desktop index
+ VMPoint& viewport) // OUT: Viewport
+{
+ std::vector<unsigned long> values;
+
+ if (GetCardinalList(screen->get_root_window(),
+ "_NET_DESKTOP_VIEWPORT", values)) {
+ uint32 numDesktops = GetNumDesktops(screen);
+
+ if (values.size() == numDesktops * 2) {
+ viewport.x = values[desktopIndex * 2];
+ viewport.y = values[desktopIndex * 2 + 1];
+ return true;
+ }
+ }
+
+ return false;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * xutils::RaiseWindowInternal --
+ *
+ * Internal function to handle the restack operation.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static void
+RaiseWindowInternal(Glib::RefPtr<Gdk::Window> window, // IN: Window to raise
+ Glib::RefPtr<Gdk::Window> sibling, // IN: The sibling
+ guint32 timestamp) // IN: Event timestamp
+{
+ GdkScreen* screen = window->get_screen()->gobj();
+
+ if (gdk_x11_screen_supports_net_wm_hint(screen,
+ gdk_atom_intern_static_string("_NET_RESTACK_WINDOW"))) {
+ XEvent ev;
+ ev.xclient.type = ClientMessage;
+ ev.xclient.serial = 0;
+ ev.xclient.send_event = True;
+ ev.xclient.window = GDK_WINDOW_XID(window->gobj());
+ ev.xclient.message_type =
+ gdk_x11_get_xatom_by_name_for_display(window->get_display()->gobj(),
+ "_NET_RESTACK_WINDOW");
+ ev.xclient.format = 32;
+ ev.xclient.data.l[0] = 2; // source (2 gives full control)
+ ev.xclient.data.l[1] = (sibling
+ ? GDK_WINDOW_XID(sibling->gobj())
+ : None); // sibling
+ ev.xclient.data.l[2] = Above; // direction
+ ev.xclient.data.l[3] = 0; // unused
+ ev.xclient.data.l[4] = 0; // unused
+
+ XSendEvent(GDK_WINDOW_XDISPLAY(window->gobj()),
+ GDK_WINDOW_XID(gdk_screen_get_root_window(screen)),
+ False, SubstructureRedirectMask | SubstructureNotifyMask,
+ &ev);
+ } else {
+ /*
+ * As of writing (2011-03-08), Metacity doesn't support
+ * _NET_RESTACK_WINDOW and will block our attempt to raise a window unless
+ * it's active, so we activate the window first.
+ */
+ if (gdk_x11_screen_supports_net_wm_hint(
+ screen,
+ gdk_atom_intern_static_string("_NET_ACTIVE_WINDOW"))) {
+
+ XClientMessageEvent xclient;
+ memset (&xclient, 0, sizeof (xclient));
+ xclient.type = ClientMessage;
+ xclient.window = GDK_WINDOW_XID(window->gobj());
+ xclient.message_type =
+ gdk_x11_get_xatom_by_name_for_display(window->get_display()->gobj(),
+ "_NET_ACTIVE_WINDOW");
+ xclient.format = 32;
+ xclient.data.l[0] = 2; // source (2 gives full control)
+ xclient.data.l[1] = timestamp;
+ xclient.data.l[2] = None; // currently active window
+ xclient.data.l[3] = 0;
+ xclient.data.l[4] = 0;
+
+ XSendEvent(GDK_WINDOW_XDISPLAY(window->gobj()),
+ GDK_WINDOW_XID(gdk_screen_get_root_window(screen)),
+ False, SubstructureRedirectMask | SubstructureNotifyMask,
+ (XEvent*)&xclient);
+ }
+
+ int flags = CWStackMode;
+ XWindowChanges changes;
+ changes.stack_mode = Above;
+
+ if (sibling) {
+ changes.sibling = GDK_WINDOW_XID(sibling->gobj());
+ flags |= CWSibling;
+ }
+
+ XReconfigureWMWindow(GDK_WINDOW_XDISPLAY(window->gobj()),
+ GDK_WINDOW_XID(window->gobj()),
+ DefaultScreen(GDK_WINDOW_XDISPLAY(window->gobj())),
+ flags, &changes);
+ }
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * xutils::RaiseWindow --
+ *
+ * Raises a window to the top of the window stack. This version
+ * accepts a timestamp instead of fetching it, useful when being
+ * called from an event handler or when using a common timestamp.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+void
+RaiseWindow(Glib::RefPtr<Gdk::Window> window, // IN: Window to raise
+ Glib::RefPtr<Gdk::Window> sibling, // IN/OPT: The sibling
+ guint32 timestamp) // IN/OPT: Event timestamp
+{
+ /*
+ * Fake an input event timestamp so that the window manager
+ * will allow a restacking of this window.
+ */
+ gdk_x11_window_set_user_time(window->gobj(),
+ timestamp == 0
+ ? gdk_x11_display_get_user_time(gdk_display_get_default())
+ : timestamp);
+
+ gdk_error_trap_push();
+ RaiseWindowInternal(window, sibling, timestamp);
+ gdk_flush();
+ int err = gdk_error_trap_pop();
+
+ if (err && sibling) {
+ /*
+ * This could be due to sibling not being a sibling window.
+ * Apparently, this is possible in our case. Ignore the "sibling."
+ */
+ gdk_error_trap_push();
+ RaiseWindowInternal(window, Glib::RefPtr<Gdk::Window>(), timestamp);
+ err = gdk_error_trap_pop();
+ }
+
+ if (err) {
+ /* We still have an error. Log it and continue on. */
+ Glib::ustring method;
+
+ if (gdk_x11_screen_supports_net_wm_hint(
+ window->get_screen()->gobj(),
+ gdk_atom_intern_static_string("_NET_RESTACK_WINDOW"))) {
+ method = "_NET_RESTACK_WINDOW";
+ } else {
+ method = "XReconfigureWMWindow";
+ }
+
+ if (sibling) {
+ Log("Unable to raise window (XID %d) over sibling (XID %d) using %s. "
+ "Error code = %d\n",
+ (int)GDK_WINDOW_XID(window->gobj()),
+ (int)GDK_WINDOW_XID(sibling->gobj()), method.c_str(), err);
+ } else {
+ Log("Unable to raise window (XID %d) using %s. Error code = %d\n",
+ (int)GDK_WINDOW_XID(window->gobj()), method.c_str(), err);
+ }
+ }
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * xutils::GetHostWindowStack --
+ *
+ * Returns the window stack as recorded by the window manager.
+ * This is the equivalent of gdk_screen_get_window_stack, except
+ * that function is broken on 64-bit platforms, so for the time
+ * being we need to provide our own.
+ *
+ * Results:
+ * The host window stack.
+ *
+ * Side effects:
+ * None.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+HostWindowList
+GetHostWindowStack()
+{
+ HostWindowList windows;
+ GdkScreen* screen = gdk_screen_get_default();
+
+ if (!gdk_x11_screen_supports_net_wm_hint(screen,
+ gdk_atom_intern_static_string("_NET_CLIENT_LIST_STACKING"))) {
+ /*
+ * This is bad. We don't really have an alternative. We might want to
+ * just disable Unity.
+ */
+ return windows;
+ }
+
+ GdkDisplay* display = gdk_display_get_default();
+ unsigned long numItems = 0;
+ unsigned long bytesAfter = 0;
+ gint format = 0;
+ Atom type = 0;
+ guchar* data = NULL;
+
+ GdkWindow* rootWin = gdk_screen_get_root_window(screen);
+
+ gdk_error_trap_push();
+ int ret = XGetWindowProperty(GDK_DISPLAY_XDISPLAY(display),
+ GDK_WINDOW_XID(rootWin),
+ gdk_x11_get_xatom_by_name_for_display(display,
+ "_NET_CLIENT_LIST_STACKING"),
+ 0, G_MAXLONG, False, XA_WINDOW,
+ &type, &format, &numItems, &bytesAfter,
+ &data);
+ int err = gdk_error_trap_pop();
+
+ if (ret == Success && !err &&
+ type == XA_WINDOW && format == 32 && data != NULL && numItems > 0) {
+ long* stack = (long*)data;
+
+ for (unsigned long i = 0; i < numItems; i++) {
+#if GTK_MAJOR_VERSION == 3
+ GdkWindow* win =
+ gdk_x11_window_foreign_new_for_display(display, stack[i]);
+#else
+ GdkWindow* win =
+ gdk_window_foreign_new_for_display(display, stack[i]);
+#endif
+
+ if (win != NULL) {
+#if GTK_MAJOR_VERSION == 3
+ windows.push_back(Glib::wrap(win));
+#else
+ windows.push_back(Glib::wrap((GdkWindowObject*)win));
+#endif
+ }
+ }
+ }
+
+ return windows;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * xutils::GetMonitorWorkArea --
+ *
+ * Gets the work area on a monitor. This is the area excluding docks,
+ * which a window would size to when maximized.
+ *
+ * While the window manager typically provides a work area spanning all
+ * monitors (_NET_WORKAREA), it does not provide per-monitor work areas, so
+ * we must compute our own.
+ *
+ * Results:
+ * The work area of the specified monitor.
+ *
+ * Side effects:
+ * None.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+void
+GetMonitorWorkArea(Glib::RefPtr<Gdk::Screen> screen, // IN:
+ int monitor, // IN:
+ Gdk::Rectangle& workArea) // OUT:
+{
+#if CAIRO_VERSION < CAIRO_VERSION_ENCODE(1,10,0)
+ /*
+ * Relies on Cairo::Region support available in cairo 1.10+, which is
+ * not available in our FreeBSD and Solaris toolchains. Not called by
+ * Tools code, so it's fine to just ifdef this out.
+ */
+ NOT_IMPLEMENTED();
+#else
+
+
+ /*
+ * Start off by getting the size of the monitor. We're going to subtract
+ * from this.
+ */
+ Gdk::Rectangle screenGeom;
+ screen->get_monitor_geometry(monitor, screenGeom);
+ Cairo::RectangleInt rect;
+ Cairo::RefPtr<Cairo::Region> workAreaRegion = Cairo::Region::create();
+
+ rect.x = screenGeom.get_x();
+ rect.y = screenGeom.get_y();
+ rect.width = screenGeom.get_width();
+ rect.height = screenGeom.get_height();
+ workAreaRegion->do_union(rect);
+
+ /*
+ * If we're dealing with a reparenting window manager, then using XQueryTree
+ * will _not_ give us client windows, so to get client windows reliably, we
+ * need to either use XQueryTree (get all top level windows) and iterate
+ * hierarchy (root -> frame -> client) to be able to test for application
+ * window properties, since these are only set on the client windows and not
+ * on reparenting frames, or just use _NET_CLIENT_LIST. In practice, WMs put
+ * docks and panels into _NET_CLIENT_LIST, so this should give us what we
+ * need.
+ */
+ HostWindowList windows = GetHostWindowStack();
+
+ for (HostWindowList::const_iterator iter = windows.begin();
+ iter != windows.end();
+ iter++) {
+
+ Glib::RefPtr<Gdk::Window> gdkWindow = *iter;
+ std::vector<unsigned long> values;
+ bool haveStrut = false;
+ NETWMStrutPartial strut = NETWMStrutPartial();
+
+ /*
+ * The EWMH spec says that the new _NET_WM_STRUT_PARTIAL takes precedence
+ * over the older _NET_WM_STRUT API.
+ */
+ if ( GetCardinalList(gdkWindow, "_NET_WM_STRUT_PARTIAL", values)
+ && values.size() == 12) {
+ haveStrut = true;
+ strut.left_width = values[0];
+ strut.right_width = values[1];
+ strut.top_height = values[2];
+ strut.bottom_height = values[3];
+ strut.left_start = values[4];
+ strut.left_end = values[5];
+ strut.right_start = values[6];
+ strut.right_end = values[7];
+ strut.top_start = values[8];
+ strut.top_end = values[9];
+ strut.bottom_start = values[10];
+ strut.bottom_end = values[11];
+ } else if ( GetCardinalList(gdkWindow, "_NET_WM_STRUT", values)
+ && values.size() == 4) {
+ haveStrut = true;
+ strut.left_width = values[0];
+ strut.right_width = values[1];
+ strut.top_height = values[2];
+ strut.bottom_height = values[3];
+
+ /*
+ * Per EWMH spec: "This property (_NET_WM_STRUT) is equivalent to a
+ * _NET_WM_STRUT_PARTIAL property where all start values are 0 and all
+ * end values are the height or width of the logical screen."
+ */
+ strut.left_start = 0;
+ strut.left_end = screen->get_height();
+ strut.right_start = 0;
+ strut.right_end = screen->get_height();
+ strut.top_start = 0;
+ strut.top_end = screen->get_width();
+ strut.bottom_start = 0;
+ strut.bottom_end = screen->get_width();
+ } else {
+ continue;
+ }
+
+ ASSERT(haveStrut);
+
+ /*
+ * Struts can be defined on one or more of the screen edges, so we create
+ * 4 rectangles and subtract each from the work area.
+ *
+ * Per the EWMH spec: "Struts MUST be specified in root window
+ * coordinates, that is, they are not relative to the edges of any view
+ * port or Xinerama monitor.... Note that the strut is relative to the
+ * screen edge, and not the edge of the xinerama monitor."
+ */
+ Gdk::Rectangle top(strut.top_start,
+ 0,
+ strut.top_end - strut.top_start,
+ strut.top_height);
+ Gdk::Rectangle bottom(strut.bottom_start,
+ screen->get_height() - strut.bottom_height,
+ strut.bottom_end - strut.bottom_start,
+ strut.bottom_height);
+ Gdk::Rectangle left(0,
+ strut.left_start,
+ strut.left_width,
+ strut.left_end - strut.left_start);
+ Gdk::Rectangle right(screen->get_width() - strut.right_width,
+ strut.right_start,
+ strut.right_width,
+ strut.right_end - strut.right_start);
+
+ /*
+ * We want each strut's rectangle to be used as if it was taking up the
+ * entire edge of the monitor, so artificially inflate height or width as
+ * need be. This means that instead of using the start and end strut
+ * values, we take the whole edge.
+ */
+ Gdk::Rectangle edge;
+ bool intersects = false;
+
+ edge = top.intersect(screenGeom, intersects);
+
+ if (top.get_height() > 0 && intersects && !edge.has_zero_area()) {
+ rect.x = screenGeom.get_x();
+ rect.y = screenGeom.get_y();
+ rect.width = screenGeom.get_width();
+ rect.height = edge.get_height();
+ workAreaRegion->subtract(rect);
+ }
+
+ edge = bottom.intersect(screenGeom, intersects);
+
+ if (bottom.get_height() > 0 && intersects && !edge.has_zero_area()) {
+ rect.x = screenGeom.get_x();
+ rect.y = edge.get_y();
+ rect.width = screenGeom.get_width();
+ rect.height = edge.get_height();
+ workAreaRegion->subtract(rect);
+ }
+
+ edge = left.intersect(screenGeom, intersects);
+
+ if (left.get_width() > 0 && intersects && !edge.has_zero_area()) {
+ rect.x = screenGeom.get_x();
+ rect.y = screenGeom.get_y();
+ rect.width = edge.get_width();
+ rect.height = screenGeom.get_height();
+ workAreaRegion->subtract(rect);
+ }
+
+ edge = right.intersect(screenGeom, intersects);
+
+ if (right.get_width() > 0 && intersects && !edge.has_zero_area()) {
+ rect.x = edge.get_x();
+ rect.y = screenGeom.get_y();
+ rect.width = edge.get_width();
+ rect.height = screenGeom.get_height();
+ workAreaRegion->subtract(rect);
+ }
+ }
+
+ rect = workAreaRegion->get_extents();
+ workArea.set_x(rect.x);
+ workArea.set_y(rect.y);
+ workArea.set_width(rect.width);
+ workArea.set_height(rect.height);
+#endif // if CAIRO_VERSION
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * xutils::GetWindowManagerName --
+ *
+ * Retrieves the current Window Manager name, if we can find it. This
+ * mimics the behavior of gdk_x11_screen_get_window_manager_name(), but
+ * there seem to be issues with that method returning its cached window
+ * manager name when it shouldn't
+ * (http://bugzilla.redhat.com/show_bug.cgi?id=471927).
+ *
+ * Results:
+ * If we can find the current window manager name, we'll return it. If we
+ * can't, then we'll return "unknown" -- same behavior as
+ * gdk_x11_screen_get_window_manager_name().
+ *
+ * Side effects:
+ * None.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+utf::string
+GetWindowManagerName(Glib::RefPtr<Gdk::Screen> screen) // IN: Screen
+{
+ utf::string wmName = "unknown";
+ GdkDisplay* display = gdk_display_get_default();
+ unsigned long numItems = 0;
+ unsigned long bytesAfter = 0;
+ gint format = 0;
+ Atom type = 0;
+ guchar* data = NULL;
+ ::Window* window;
+
+ GdkWindow* rootWin = gdk_screen_get_root_window(screen->gobj());
+
+ /*
+ * First, we need to get the window that our EWMH-compliant WM is using to
+ * communicate its properties with.
+ */
+ gdk_error_trap_push();
+ int ret = XGetWindowProperty(GDK_DISPLAY_XDISPLAY(display),
+ GDK_WINDOW_XID(rootWin),
+ gdk_x11_get_xatom_by_name_for_display(display,
+ "_NET_SUPPORTING_WM_CHECK"),
+ 0, G_MAXLONG, False, XA_WINDOW,
+ &type, &format, &numItems, &bytesAfter,
+ &data);
+ int err = gdk_error_trap_pop();
+
+ if (ret != Success || err || type != XA_WINDOW || data == NULL) {
+ if (data) {
+ XFree(data);
+ }
+ return wmName;
+ }
+
+ window = (::Window*)data;
+ gchar* name = NULL;
+
+ /*
+ * Now, using the window provided in _NET_SUPPORTING_WM_CHECK, look for the
+ * _NET_WM_NAME on it.
+ */
+ gdk_error_trap_push();
+ ret = XGetWindowProperty(GDK_DISPLAY_XDISPLAY(display),
+ *window,
+ gdk_x11_get_xatom_by_name_for_display(display,
+ "_NET_WM_NAME"),
+ 0, G_MAXLONG, False,
+ gdk_x11_get_xatom_by_name_for_display(display,
+ "UTF8_STRING"),
+ &type, &format, &numItems, &bytesAfter,
+ (guchar**)&name);
+ err = gdk_error_trap_pop();
+
+ XFree(window);
+
+ if (ret != Success || err || name == NULL) {
+ if (name != NULL) {
+ XFree(name);
+ }
+ return wmName;
+ }
+
+ wmName = name;
+ XFree(name);
+
+ return wmName;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * xutils::ChangeEWMHWindowState --
+ *
+ * Sends the requested _NET_WM_STATE change through to the root window for
+ * the Window Manager to act on.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+void
+ChangeEWMHWindowState(bool add, // IN
+ Glib::RefPtr<Gdk::Window> window, // IN
+ GdkAtom state1, // IN
+ GdkAtom state2) // IN
+{
+ GdkScreen* screen = window->get_screen()->gobj();
+ GdkDisplay* display = const_cast<GdkDisplay*>(window->get_display()->gobj());
+ Window win = GDK_WINDOW_XID(window->gobj());
+
+ XClientMessageEvent xclient;
+
+/* Straight from http://standards.freedesktop.org/wm-spec/wm-spec-latest.html */
+#define _NET_WM_STATE_REMOVE 0 /* remove/unset property */
+#define _NET_WM_STATE_ADD 1 /* add/set property */
+#define _NET_WM_STATE_TOGGLE 2 /* toggle property */
+
+ memset(&xclient, 0, sizeof xclient);
+ xclient.type = ClientMessage;
+ xclient.window = win;
+ xclient.message_type = gdk_x11_get_xatom_by_name_for_display(display,
+ "_NET_WM_STATE");
+ xclient.format = 32;
+ xclient.data.l[0] = add ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE;
+ xclient.data.l[1] = gdk_x11_atom_to_xatom_for_display(display, state1);
+ xclient.data.l[2] = gdk_x11_atom_to_xatom_for_display(display, state2);
+ xclient.data.l[3] = 0;
+ xclient.data.l[4] = 0;
+
+ XSendEvent(GDK_DISPLAY_XDISPLAY(display),
+ GDK_WINDOW_XID(gdk_screen_get_root_window(screen)),
+ False, SubstructureRedirectMask | SubstructureNotifyMask,
+ (XEvent*)&xclient);
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * xutils::GetEWMHWindowState --
+ *
+ * Queries _NET_WM_STATE on the provided window and returns a std::list of
+ * utf::strings which contain the X Atom names that are set as
+ * _NET_WM_STATE for the given window.
+ *
+ * Results:
+ * An std::list containing the utf::string names of all _NET_WM_STATE atoms
+ * that are set on the given window.
+ *
+ * Side effects:
+ * None.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+std::list<utf::string>
+GetEWMHWindowState(Glib::RefPtr<Gdk::Window> window) // IN
+{
+ std::list<utf::string> atomStrings;
+
+ GdkDisplay* display = const_cast<GdkDisplay*>(window->get_display()->gobj());
+ GdkWindow* gdkwin = const_cast<GdkWindow*>(window->gobj());
+
+ Atom type = None;
+ gint format;
+ gulong nitems;
+ gulong bytes_after;
+ guchar* data;
+ Atom* atoms = NULL;
+
+ gdk_error_trap_push();
+ int ret = XGetWindowProperty(GDK_DISPLAY_XDISPLAY(display),
+ GDK_WINDOW_XID(gdkwin),
+ gdk_x11_get_xatom_by_name_for_display(
+ display, "_NET_WM_STATE"),
+ 0, G_MAXLONG, False, XA_ATOM, &type, &format,
+ &nitems, &bytes_after, &data);
+ int err = gdk_error_trap_pop();
+
+ if (ret != Success || err) {
+ atomStrings.push_back("Error calling XGetWindowProperty");
+ return atomStrings;
+ }
+
+ if (type != XA_ATOM) {
+ XFree(data);
+ atomStrings.push_back("Error: type != XA_ATOM");
+ return atomStrings;
+ }
+
+ atoms = reinterpret_cast<Atom*>(data);
+
+ for (gulong i = 0; i < nitems; ++i) {
+ atomStrings.push_back(gdk_x11_get_xatom_name(atoms[i]));
+ }
+
+ XFree(atoms);
+
+ return atomStrings;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * xutils::GetPointerLocation --
+ *
+ * Get the location of the pointer relative to the root window.
+ *
+ * Results:
+ * OUT parameters are filled in.
+ *
+ * Side effects:
+ * None.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+void
+GetPointerLocation(const Glib::RefPtr<Gdk::Window>& window, // IN
+ int& x, // OUT
+ int& y, // OUT
+ Gdk::ModifierType& mask) // OUT
+{
+#if GTK_MAJOR_VERSION == 3
+ Glib::RefPtr<Gdk::DeviceManager> deviceManager =
+ window->get_display()->get_device_manager();
+ Glib::RefPtr<Gdk::Device> device = deviceManager->get_client_pointer();
+
+ window->get_device_position(device, x, y, mask);
+ window->get_root_coords(x, y, x, y);
+#else
+ window->get_display()->get_pointer(x, y, mask);
+#endif
+}
+
+
+} // namespace xutils