From: VMware, Inc <> Date: Wed, 18 Sep 2013 03:29:34 +0000 (-0700) Subject: nternal branch sync. Included in this change: X-Git-Tag: 2013.09.16-1328054~59 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=f51b4f5725fa0df44c200e1c33e17d28c07ea7e5;p=thirdparty%2Fopen-vm-tools.git nternal branch sync. Included in this change: . Tools,DND,X11: Constrain detection window to desktop work area . dndcpx11: Adjust style to match Hosted UI guidelines . changes in shared code that don't affect open-vm-tools functionality Signed-off-by: Dmitry Torokhov --- diff --git a/open-vm-tools/lib/include/mutexRankLib.h b/open-vm-tools/lib/include/mutexRankLib.h index 44f6875e2..b3e1e2eca 100644 --- a/open-vm-tools/lib/include/mutexRankLib.h +++ b/open-vm-tools/lib/include/mutexRankLib.h @@ -86,9 +86,9 @@ #define RANK_nfcLibLock (RANK_libLockBase + 0x4505) /* - * policy ops pending list lock + * Policy lib lock */ -#define RANK_popPendingListLock (RANK_libLockBase + 0x4605) +#define RANK_policyLibLock (RANK_libLockBase + 0x4605) /* * disklib and I/O related locks @@ -160,4 +160,5 @@ #define RANK_licenseCheckLock (RANK_libLockBase + 0x7090) #define RANK_preferenceLock (RANK_libLockBase + 0x7100) + #endif /* _LIBMUTEXRANK_H */ diff --git a/open-vm-tools/lib/include/vm_atomic.h b/open-vm-tools/lib/include/vm_atomic.h index 36d9a6898..af2f578cf 100644 --- a/open-vm-tools/lib/include/vm_atomic.h +++ b/open-vm-tools/lib/include/vm_atomic.h @@ -37,8 +37,6 @@ #ifndef _ATOMIC_H_ #define _ATOMIC_H_ -//#define FAKE_ATOMIC /* defined if true atomic not needed */ - #define INCLUDE_ALLOW_USERLEVEL #define INCLUDE_ALLOW_MODULE @@ -54,18 +52,15 @@ #include "vm_basic_types.h" -/* Basic atomic type: 32 bits */ +/* Basic atomic types: 32 and 64 bits */ typedef struct Atomic_uint32 { volatile uint32 value; -} Atomic_uint32; - +} Atomic_uint32 ALIGNED(4); -/* Basic atomic type: 64 bits */ typedef struct Atomic_uint64 { volatile uint64 value; } Atomic_uint64 ALIGNED(8); - /* * Prototypes for msft atomics. These are defined & inlined by the * compiler so no function definition is needed. The prototypes are @@ -122,7 +117,7 @@ __int64 _InterlockedCompareExchange64(__int64 volatile*, __int64, __int64); #endif #endif /* _MSC_VER */ -#if defined(__arm__) && !defined(FAKE_ATOMIC) +#if defined(__arm__) /* * LDREX without STREX or CLREX may cause problems in environments where the * context switch may not clear the reference monitor - according ARM manual @@ -342,34 +337,47 @@ AtomicEpilogue(void) static INLINE uint32 Atomic_Read(Atomic_uint32 const *var) // IN { - return var->value; -} -#define Atomic_Read32 Atomic_Read + uint32 value; +#if defined(VMM) + ASSERT(((uintptr_t)var % 4) == 0); +#endif -/* - *----------------------------------------------------------------------------- - * - * Atomic_Write -- - * - * Write - * - * Results: - * None. - * - * Side effects: - * None. - * - *----------------------------------------------------------------------------- - */ +#if defined(__GNUC__) + /* + * Use inline assembler to force using a single load instruction to + * ensure that the compiler doesn't split a transfer operation into multiple + * instructions. + */ -static INLINE void -Atomic_Write(Atomic_uint32 *var, // IN - uint32 val) // IN -{ - var->value = val; +#if defined(VM_ARM_V7) + __asm__ __volatile__( + "ldr %0, [%1]" + : "=r" (value) + : "r" (var->value) + ); +#else + __asm__ __volatile__( + "mov %1, %0" + : "=r" (value) + : "m" (var->value) + ); +#endif +#elif defined(_MSC_VER) + /* + * Microsoft docs guarantee simple reads and writes to properly + * aligned 32-bit variables use only a single instruction. + * http://msdn.microsoft.com/en-us/library/ms684122%28VS.85%29.aspx + */ + + value = var->value; +#else +#error No compiler defined for Atomic_Read +#endif + + return value; } -#define Atomic_Write32 Atomic_Write +#define Atomic_Read32 Atomic_Read /* @@ -392,11 +400,7 @@ static INLINE uint32 Atomic_ReadWrite(Atomic_uint32 *var, // IN uint32 val) // IN { -#ifdef FAKE_ATOMIC - uint32 retval = var->value; - var->value = val; - return retval; -#elif defined(__GNUC__) +#if defined(__GNUC__) #ifdef VM_ARM_V7 register volatile uint32 retVal; register volatile uint32 res; @@ -448,6 +452,83 @@ Atomic_ReadWrite(Atomic_uint32 *var, // IN #define Atomic_ReadWrite32 Atomic_ReadWrite +/* + *----------------------------------------------------------------------------- + * + * Atomic_Write -- + * + * Write + * + * Results: + * None. + * + * Side effects: + * None. + * + *----------------------------------------------------------------------------- + */ + +static INLINE void +Atomic_Write(Atomic_uint32 *var, // IN + uint32 val) // IN +{ +#if defined(VMM) + ASSERT(((uintptr_t)var % 4) == 0); +#endif + +#if defined(__GNUC__) +#if defined(VM_ARM_V7) + /* + * A3.4.1 ARM DDI 0406C: + * + * When a processor writes using any instruction other than a + * Store-Exclusive: + * + * - if the write is to a physical address that is not covered by its local + * monitor the write does not affect the state of the local monitor + * - if the write is to a physical address that is covered by its local + * monitor it is IMPLEMENTATION DEFINED whether the write affects the + * state of the local monitor. + * + * A3.4.5 ARM DDI 0406C: + * + * If two STREX instructions are executed without an intervening LDREX the + * second STREX returns a status value of 1. This means that: + * + * — ARM recommends that, in a given thread of execution, every STREX has a + * preceding LDREX associated with it + * — it is not necessary for every LDREX to have a subsequent STREX. + */ + + Atomic_ReadWrite(var, val); +#else + /* + * Use inline assembler to force using a single store instruction to + * ensure that the compiler doesn't split a transfer operation into multiple + * instructions. + */ + + __asm__ __volatile__( + "mov %1, %0" + : "=m" (var->value) + : "r" (val) + ); +#endif +#elif defined(_MSC_VER) + /* + * Microsoft docs guarantee simple reads and writes to properly + * aligned 32-bit variables use only a single instruction. + * http://msdn.microsoft.com/en-us/library/ms684122%28VS.85%29.aspx + */ + + var->value = val; +#else +#error No compiler defined for Atomic_Write +#endif +} +#define Atomic_Write32 Atomic_Write + + /* *----------------------------------------------------------------------------- * @@ -469,14 +550,7 @@ Atomic_ReadIfEqualWrite(Atomic_uint32 *var, // IN uint32 oldVal, // IN uint32 newVal) // IN { -#ifdef FAKE_ATOMIC - uint32 readVal = var->value; - - if (oldVal == readVal) { - var->value = newVal; - } - return oldVal; -#elif defined(__GNUC__) +#if defined(__GNUC__) #ifdef VM_ARM_V7 register uint32 retVal; register uint32 res; @@ -559,7 +633,7 @@ Atomic_ReadIfEqualWriteLockAcquire(Atomic_uint32 *var, // IN uint32 oldVal, // IN uint32 newVal) // IN { -#if !defined(FAKE_ATOMIC) && defined(__GNUC__) && defined(__x86_64__) +#if defined(__GNUC__) && defined(__x86_64__) uint32 val; __asm__ __volatile__( "repnz; lock; cmpxchgl %2, %1" @@ -668,9 +742,7 @@ static INLINE void Atomic_And(Atomic_uint32 *var, // IN uint32 val) // IN { -#ifdef FAKE_ATOMIC - var->value &= val; -#elif defined(__GNUC__) +#if defined(__GNUC__) #ifdef VM_ARM_V7 register volatile uint32 res; register volatile uint32 tmp; @@ -734,9 +806,7 @@ static INLINE void Atomic_Or(Atomic_uint32 *var, // IN uint32 val) // IN { -#ifdef FAKE_ATOMIC - var->value |= val; -#elif defined(__GNUC__) +#if defined(__GNUC__) #ifdef VM_ARM_V7 register volatile uint32 res; register volatile uint32 tmp; @@ -800,9 +870,7 @@ static INLINE void Atomic_Xor(Atomic_uint32 *var, // IN uint32 val) // IN { -#ifdef FAKE_ATOMIC - var->value ^= val; -#elif defined(__GNUC__) +#if defined(__GNUC__) #ifdef VM_ARM_V7 register volatile uint32 res; register volatile uint32 tmp; @@ -905,9 +973,7 @@ static INLINE void Atomic_Add(Atomic_uint32 *var, // IN uint32 val) // IN { -#ifdef FAKE_ATOMIC - var->value += val; -#elif defined(__GNUC__) +#if defined(__GNUC__) #ifdef VM_ARM_V7 register volatile uint32 res; register volatile uint32 tmp; @@ -1010,9 +1076,7 @@ static INLINE void Atomic_Sub(Atomic_uint32 *var, // IN uint32 val) // IN { -#ifdef FAKE_ATOMIC - var->value -= val; -#elif defined(__GNUC__) +#if defined(__GNUC__) #ifdef VM_ARM_V7 register volatile uint32 res; register volatile uint32 tmp; @@ -1115,7 +1179,7 @@ static INLINE void Atomic_Inc(Atomic_uint32 *var) // IN { #ifdef __GNUC__ -#if defined(VM_ARM_V7) || defined(FAKE_ATOMIC) +#if defined(VM_ARM_V7) Atomic_Add(var, 1); #else // VM_ARM_V7 /* Checked against the Intel manual and GCC --walken */ @@ -1161,7 +1225,7 @@ static INLINE void Atomic_Dec(Atomic_uint32 *var) // IN { #ifdef __GNUC__ -#if defined(VM_ARM_V7) || defined(FAKE_ATOMIC) +#if defined(VM_ARM_V7) Atomic_Sub(var, 1); #else // VM_ARM_V7 /* Checked against the Intel manual and GCC --walken */ @@ -1343,11 +1407,7 @@ static INLINE uint32 Atomic_FetchAndAddUnfenced(Atomic_uint32 *var, // IN uint32 val) // IN { -#ifdef FAKE_ATOMIC - uint32 res = var->value; - var->value = res + val; - return res; -#elif defined(__GNUC__) +#if defined(__GNUC__) #ifdef VM_ARM_V7 register volatile uint32 res; register volatile uint32 retVal; @@ -1428,7 +1488,7 @@ static INLINE uint32 Atomic_FetchAndAddLockReleaseUnfenced(Atomic_uint32 *var, // IN uint32 val) // IN { -#if !defined(FAKE_ATOMIC) && defined(__GNUC__) && defined(__x86_64__) +#if defined(__GNUC__) && defined(__x86_64__) __asm__ __volatile__( "repz; lock; xaddl %0, %1" : "=r" (val), @@ -1470,7 +1530,7 @@ static INLINE uint32 Atomic_FetchAndAdd(Atomic_uint32 *var, // IN uint32 val) // IN { -#if defined(__GNUC__) && !defined(VM_ARM_V7) && !defined(FAKE_ATOMIC) +#if defined(__GNUC__) && !defined(VM_ARM_V7) val = Atomic_FetchAndAddUnfenced(var, val); AtomicEpilogue(); return val; @@ -1508,7 +1568,7 @@ static INLINE uint32 Atomic_FetchAndAddLockRelease(Atomic_uint32 *var, // IN uint32 val) // IN { -#if defined(__GNUC__) && !defined(VM_ARM_V7) && !defined(FAKE_ATOMIC) +#if defined(__GNUC__) && !defined(VM_ARM_V7) val = Atomic_FetchAndAddLockReleaseUnfenced(var, val); AtomicEpilogue(); return val; @@ -1755,14 +1815,7 @@ Atomic_CMPXCHG64(Atomic_uint64 *var, // IN/OUT uint64 const *oldVal, // IN uint64 const *newVal) // IN { -#ifdef FAKE_ATOMIC - uint64 readVal = var->value; - - if (*oldVal == readVal) { - var->value = *newVal; - } - return (*oldVal == readVal); -#elif defined(__GNUC__) +#if defined(__GNUC__) #if defined(VM_ARM_V7) return (Atomic_ReadIfEqualWrite64(var, *oldVal, *newVal) == *oldVal); @@ -1925,14 +1978,7 @@ Atomic_CMPXCHG32(Atomic_uint32 *var, // IN/OUT uint32 oldVal, // IN uint32 newVal) // IN { -#ifdef FAKE_ATOMIC - uint32 readVal = var->value; - - if (oldVal == readVal) { - var->value = newVal; - } - return (oldVal == readVal); -#elif defined(__GNUC__) +#if defined(__GNUC__) #ifdef VM_ARM_V7 return (Atomic_ReadIfEqualWrite(var, oldVal, newVal) == oldVal); #else // VM_ARM_V7 @@ -1977,9 +2023,7 @@ Atomic_CMPXCHG32(Atomic_uint32 *var, // IN/OUT static INLINE uint64 Atomic_Read64(Atomic_uint64 const *var) // IN { -#ifdef FAKE_ATOMIC - return var->value; -#elif defined(__GNUC__) && defined(__x86_64__) +#if defined(__GNUC__) && defined(__x86_64__) uint64 value; #ifdef VMM diff --git a/open-vm-tools/services/plugins/dndcp/Makefile.am b/open-vm-tools/services/plugins/dndcp/Makefile.am index 04422c7b9..1e067e20a 100644 --- a/open-vm-tools/services/plugins/dndcp/Makefile.am +++ b/open-vm-tools/services/plugins/dndcp/Makefile.am @@ -18,8 +18,6 @@ plugindir = @VMUSR_PLUGIN_INSTALLDIR@ plugin_LTLIBRARIES = libdndcp.la -CFLAGS += -Wno-unused - libdndcp_la_CPPFLAGS = libdndcp_la_CPPFLAGS += @GTK_CPPFLAGS@ libdndcp_la_CPPFLAGS += @GTKMM_CPPFLAGS@ @@ -27,6 +25,7 @@ libdndcp_la_CPPFLAGS += @PLUGIN_CPPFLAGS@ libdndcp_la_CPPFLAGS += -I$(top_srcdir)/services/plugins/dndcp/dnd libdndcp_la_CPPFLAGS += -I$(top_srcdir)/services/plugins/dndcp/dndGuest libdndcp_la_CPPFLAGS += -I$(top_srcdir)/services/plugins/dndcp/stringxx +libdndcp_la_CPPFLAGS += -I$(top_srcdir)/services/plugins/dndcp/xutils libdndcp_la_CPPFLAGS += -I$(top_builddir)/include libdndcp_la_LDFLAGS = @@ -66,6 +65,7 @@ libdndcp_la_SOURCES += dndGuest/rpcV4Util.cpp libdndcp_la_SOURCES += dndGuest/dndCPTransportGuestRpc.cpp libdndcp_la_SOURCES += stringxx/string.cc +libdndcp_la_SOURCES += xutils/xutils.cc libdndcp_la_SOURCES += copyPasteCompat.c libdndcp_la_SOURCES += copyPasteCompatX11.c diff --git a/open-vm-tools/services/plugins/dndcp/dndUIX11.cpp b/open-vm-tools/services/plugins/dndcp/dndUIX11.cpp index 4548a5025..051ea736e 100644 --- a/open-vm-tools/services/plugins/dndcp/dndUIX11.cpp +++ b/open-vm-tools/services/plugins/dndcp/dndUIX11.cpp @@ -25,25 +25,30 @@ #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 /* for XTest*() */ +#include +#include +#include + +#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 -#include -#include /* for XTest*() */ -#include "vmware/guestrpc/tclodefs.h" +#include "vmblock.h" } /* IsXExtensionPointer may be not defined with old Xorg. */ @@ -53,67 +58,99 @@ extern "C" { #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 @@ -122,15 +159,15 @@ DnDUIX11::Init() 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; } @@ -142,70 +179,73 @@ DnDUIX11::Init() * 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(); @@ -238,64 +278,89 @@ DnDUIX11::SetTargetsAndCallbacks() * 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 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 @@ -303,9 +368,9 @@ DnDUIX11::CommonDragStartCB(const CPClipboard *clip, std::string stagingDir) */ targets = Gtk::TargetList::create(std::list()); - 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; @@ -318,20 +383,20 @@ DnDUIX11::CommonDragStartCB(const CPClipboard *clip, std::string stagingDir) } } - 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)); } @@ -340,7 +405,7 @@ DnDUIX11::CommonDragStartCB(const CPClipboard *clip, std::string stagingDir) /* 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; @@ -349,27 +414,38 @@ DnDUIX11::CommonDragStartCB(const CPClipboard *clip, std::string stagingDir) 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(); @@ -379,142 +455,188 @@ DnDUIX11::CommonSourceCancelCB(void) * 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 @@ -523,46 +645,54 @@ DnDUIX11::CommonUpdateDetWndCB(bool bShow, */ 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 @@ -571,110 +701,133 @@ DnDUIX11::CommonUpdateUnityDetWndCB(bool bShow, 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 &dc, - int x, - int y, - guint timeValue) +DnDUIX11::OnGtkDragMotion( + const Glib::RefPtr &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; } @@ -682,9 +835,9 @@ DnDUIX11::GtkDestDragMotionCB(const Glib::RefPtr &dc, 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; @@ -715,7 +868,7 @@ DnDUIX11::GtkDestDragMotionCB(const Glib::RefPtr &dc, return true; } - m_dc = dc->gobj(); + mDragCtx = dc->gobj(); if (target != "") { /* @@ -739,14 +892,14 @@ DnDUIX11::GtkDestDragMotionCB(const Glib::RefPtr &dc, 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)) { @@ -765,21 +918,32 @@ DnDUIX11::GtkDestDragMotionCB(const Glib::RefPtr &dc, } -/** +/* + *----------------------------------------------------------------------------- + * + * 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 &dc, - guint time) +DnDUIX11::OnGtkDragLeave( + const Glib::RefPtr &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 @@ -789,7 +953,7 @@ DnDUIX11::GtkDestDragLeaveCB(const Glib::RefPtr &dc, * 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); } @@ -797,44 +961,57 @@ DnDUIX11::GtkDestDragLeaveCB(const Glib::RefPtr &dc, /* - * 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& context) +DnDUIX11::OnGtkDragBegin( + const Glib::RefPtr& 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 &dc, - Gtk::SelectionData& selection_data, - guint info, - guint time) +DnDUIX11::OnGtkDragDataGet( + const Glib::RefPtr &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; @@ -851,23 +1028,23 @@ DnDUIX11::GtkSourceDragDataGetCB(const Glib::RefPtr &dc, 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; } @@ -887,7 +1064,7 @@ DnDUIX11::GtkSourceDragDataGetCB(const Glib::RefPtr &dc, 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; } @@ -897,11 +1074,11 @@ DnDUIX11::GtkSourceDragDataGetCB(const Glib::RefPtr &dc, /* 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; } } @@ -922,10 +1099,10 @@ DnDUIX11::GtkSourceDragDataGetCB(const Glib::RefPtr &dc, * 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__); @@ -935,29 +1112,25 @@ DnDUIX11::GtkSourceDragDataGetCB(const Glib::RefPtr &dc, 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; @@ -965,73 +1138,95 @@ DnDUIX11::GtkSourceDragDataGetCB(const Glib::RefPtr &dc, /* 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 &dc) +DnDUIX11::OnGtkDragEnd( + const Glib::RefPtr &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 &dc, - int x, - int y, - const Gtk::SelectionData& sd, - guint info, - guint time) +DnDUIX11::OnGtkDragDataReceived( + const Glib::RefPtr &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; } @@ -1044,18 +1239,18 @@ DnDUIX11::GtkDestDragDataReceivedCB(const Glib::RefPtr &dc, */ 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; } @@ -1070,41 +1265,46 @@ DnDUIX11::GtkDestDragDataReceivedCB(const Glib::RefPtr &dc, * 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 &dc, - int x, - int y, - guint time) +DnDUIX11::OnGtkDragDrop( + const Glib::RefPtr &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); @@ -1113,24 +1313,35 @@ DnDUIX11::GtkDestDragDropCB(const Glib::RefPtr &dc, 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 @@ -1148,7 +1359,7 @@ DnDUIX11::SetCPClipboardFromGtk(const Gtk::SelectionData& sd) // IN 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. @@ -1218,7 +1429,7 @@ DnDUIX11::SetCPClipboardFromGtk(const Gtk::SelectionData& sd) // IN 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); @@ -1226,16 +1437,13 @@ DnDUIX11::SetCPClipboardFromGtk(const Gtk::SelectionData& sd) // IN } /* 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__); @@ -1245,14 +1453,13 @@ DnDUIX11::SetCPClipboardFromGtk(const Gtk::SelectionData& sd) // IN } /* 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 { @@ -1264,36 +1471,46 @@ DnDUIX11::SetCPClipboardFromGtk(const Gtk::SelectionData& sd) // IN } -/** +/* + *----------------------------------------------------------------------------- + * + * 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 &dc, - guint time) +DnDUIX11::RequestData( + const Glib::RefPtr &dc, // IN: GTK drag context + guint time) // IN: event timestamp { Glib::RefPtr targets; targets = Gtk::TargetList::create(std::list()); - 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; } @@ -1302,37 +1519,50 @@ DnDUIX11::RequestData(const Glib::RefPtr &dc, 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 @@ -1361,20 +1591,33 @@ DnDUIX11::GetLastDirName(const std::string &str) } -/** +/* + *----------------------------------------------------------------------------- + * + * 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; @@ -1416,56 +1659,64 @@ DnDUIX11::GetNextPath(utf::utf8string& str, } -/** +/* + *----------------------------------------------------------------------------- + * + * 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; @@ -1527,8 +1778,8 @@ DnDUIX11::SendFakeXEvents(const bool showWidget, /* * 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; @@ -1608,11 +1859,11 @@ DnDUIX11::SendFakeXEvents(const bool showWidget, 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__); @@ -1633,18 +1884,28 @@ exit: } -/** - * 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; @@ -1709,16 +1970,27 @@ DnDUIX11::TryXTestFakeDeviceButtonEvent(void) } -/** +/* + *----------------------------------------------------------------------------- + * + * 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 * @@ -1727,10 +1999,10 @@ DnDUIX11::GetDetWndAsWidget() 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); } @@ -1738,72 +2010,102 @@ DnDUIX11::GetDetWndAsWidget() } -/** +/* + *----------------------------------------------------------------------------- + * + * 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; @@ -1828,16 +2130,25 @@ DnDUIX11::ToDropEffect(Gdk::DragAction action) } -/** +/* + *----------------------------------------------------------------------------- + * + * 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; @@ -1850,7 +2161,7 @@ DnDUIX11::WriteFileContentsToStagingDir(void) 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; } @@ -1893,7 +2204,7 @@ DnDUIX11::WriteFileContentsToStagingDir(void) goto exit; } - m_HGFileContentsUriList = ""; + mHGFileContentsUriList = ""; for (i = 0; i < nFiles; i++) { utf::string fileName; @@ -1924,15 +2235,14 @@ DnDUIX11::WriteFileContentsToStagingDir(void) 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; @@ -1952,8 +2262,7 @@ DnDUIX11::WriteFileContentsToStagingDir(void) 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. @@ -1979,17 +2288,16 @@ DnDUIX11::WriteFileContentsToStagingDir(void) 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()); } } @@ -1998,11 +2306,10 @@ DnDUIX11::WriteFileContentsToStagingDir(void) * 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: @@ -2015,61 +2322,82 @@ 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); } /* @@ -2080,14 +2408,18 @@ DnDUIX11::TargetDragEnter(void) } -/** - * 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; @@ -2096,18 +2428,22 @@ DnDUIX11::GetTimeInMillis(void) } -/** - * 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); } @@ -2260,3 +2596,48 @@ DnDUIX11::GtkButtonReleaseEventCB(GdkEventButton *event) } +/* + *----------------------------------------------------------------------------- + * + * DnDUIX11::OnWorkAreaChanged -- + * + * Updates mOrigin in response to changes to _NET_workArea. + * + * Results: + * None. + * + * Side effects: + * None. + * + *----------------------------------------------------------------------------- + */ + +void +DnDUIX11::OnWorkAreaChanged(Glib::RefPtr screen) // IN +{ + TRACE_CALL(); + + std::vector 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()); +} diff --git a/open-vm-tools/services/plugins/dndcp/dndUIX11.h b/open-vm-tools/services/plugins/dndcp/dndUIX11.h index d0af46c4d..6a98def38 100644 --- a/open-vm-tools/services/plugins/dndcp/dndUIX11.h +++ b/open-vm-tools/services/plugins/dndcp/dndUIX11.h @@ -64,90 +64,88 @@ public: bool Init(); void VmxDnDVersionChanged(RpcChannel *chan, uint32 version); void SetDnDAllowed(bool isDnDAllowed) - {ASSERT(m_DnD); m_DnD->SetDnDAllowed(isDnDAllowed);} + {ASSERT(mDnD); mDnD->SetDnDAllowed(isDnDAllowed);} void SetBlockControl(DnDBlockControl *blockCtrl); void SetUnityMode(Bool mode) - {m_unityMode = mode;}; + {mUnityMode = mode;}; - DragDetWnd *GetFullDetWnd() {return m_detWnd;} + DragDetWnd *GetFullDetWnd() {return mDetWnd;} GtkWidget *GetDetWndAsWidget(); private: - - /** + /* * Blocking FS Helper Functions. */ void AddBlock(); void RemoveBlock(); - bool TryXTestFakeDeviceButtonEvent(void); + bool TryXTestFakeDeviceButtonEvent(); - /** + /* * Callbacks from Common DnD layer. */ - void CommonResetCB(); - void CommonUpdateMouseCB(int32 x, int32 y); + void ResetUI(); + void OnMoveMouse(int32 x, int32 y); - /** + /* * Source functions for HG DnD. */ - void CommonDragStartCB(const CPClipboard *clip, std::string stagingDir); - void CommonSourceDropCB(void); + void OnSrcDragBegin(const CPClipboard *clip, std::string stagingDir); + void OnSrcDrop(); - /** + /* * Called when HG Dnd is completed. */ - void CommonSourceCancelCB(void); + void OnSrcCancel(); - /** + /* * Called when GH DnD is completed. */ - void CommonDestPrivateDropCB(int32 x, int32 y); - void CommonDestCancelCB(void); + void OnPrivateDrop(int32 x, int32 y); + void OnDestCancel(); - /** + /* * Source functions for file transfer. */ - void CommonSourceFileCopyDoneCB(bool success); + void OnGetFilesDone(bool success); - /** + /* * Callbacks for showing/hiding detection window. */ - void CommonUpdateDetWndCB(bool bShow, int32 x, int32 y); - void CommonUpdateUnityDetWndCB(bool bShow, uint32 unityWndId, bool bottom); - void CommonMoveDetWndToMousePos(void); + void OnUpdateDetWnd(bool bShow, int32 x, int32 y); + void OnUpdateUnityDetWnd(bool bShow, uint32 unityWndId, bool bottom); + void OnDestMoveDetWndToMousePos(); - /** + /* * Gtk+ Callbacks: Drag Destination. */ - void GtkDestDragDataReceivedCB(const Glib::RefPtr &dc, - int x, int y, const Gtk::SelectionData &sd, - guint info, guint time); - bool GtkDestDragDropCB(const Glib::RefPtr &dc, - int x, int y, guint time); - void GtkDestDragLeaveCB(const Glib::RefPtr &dc, - guint time); - bool GtkDestDragMotionCB(const Glib::RefPtr &dc, int x, - int y, guint time); - - /** + void OnGtkDragDataReceived(const Glib::RefPtr &dc, + int x, int y, const Gtk::SelectionData &sd, + guint info, guint time); + bool OnGtkDragDrop(const Glib::RefPtr &dc, int x, int y, + guint time); + void OnGtkDragLeave(const Glib::RefPtr &dc, guint time); + bool OnGtkDragMotion(const Glib::RefPtr &dc, int x, + int y, guint time); + + /* * Gtk+ Callbacks: Drag Source. */ - void GtkSourceDragBeginCB(const Glib::RefPtr& context); - void GtkSourceDragDataGetCB(const Glib::RefPtr& context, - Gtk::SelectionData& selection_data, guint info, - guint time); - void GtkSourceDragEndCB(const Glib::RefPtr& context); - /** + void OnGtkDragBegin(const Glib::RefPtr& context); + void OnGtkDragDataGet(const Glib::RefPtr& context, + Gtk::SelectionData& selection_data, guint info, + guint time); + void OnGtkDragEnd(const Glib::RefPtr& context); + /* * Source functions for HG DnD. Makes calls to common layer. */ - void SourceDragStartDone(void); + void SourceDragStartDone(); void SourceUpdateFeedback(DND_DROPEFFECT effect); - /** + /* * Target function for GH DnD. Makes call to common layer. */ - void TargetDragEnter(void); + void TargetDragEnter(); /* * Other signal handlers for tracing. @@ -164,50 +162,74 @@ private: bool GtkButtonPressEventCB(GdkEventButton *event); bool GtkButtonReleaseEventCB(GdkEventButton *event); - /** + /* * Misc methods. */ + void InitGtk(); + bool SetCPClipboardFromGtk(const Gtk::SelectionData& sd); bool RequestData(const Glib::RefPtr &dc, guint timeValue); std::string GetLastDirName(const std::string &str); utf::utf8string GetNextPath(utf::utf8string &str, size_t& index); - DND_DROPEFFECT ToDropEffect(Gdk::DragAction action); - void SetTargetsAndCallbacks(); + + static DND_DROPEFFECT ToDropEffect(const Gdk::DragAction action); + static unsigned long GetTimeInMillis(); + bool SendFakeXEvents(const bool showWidget, const bool buttonEvent, const bool buttonPress, const bool moveWindow, const bool coordsProvided, const int xCoord, const int yCoord); bool SendFakeMouseMove(const int x, const int y); bool WriteFileContentsToStagingDir(); - unsigned long GetTimeInMillis(); - - ToolsAppCtx *m_ctx; - GuestDnDMgr *m_DnD; - std::string m_HGStagingDir; - utf::string m_HGFileContentsUriList; - DragDetWnd *m_detWnd; - CPClipboard m_clipboard; - DnDBlockControl *m_blockCtrl; - DND_FILE_TRANSFER_STATUS m_HGGetFileStatus; - int m_HGEffect; - bool m_blockAdded; + + static inline bool TargetIsPlainText(const utf::string& target) { + return target == TARGET_NAME_STRING + || target == TARGET_NAME_TEXT_PLAIN + || target == TARGET_NAME_UTF8_STRING + || target == TARGET_NAME_COMPOUND_TEXT; + } + + static inline bool TargetIsRichText(const utf::string& target) { + return target == TARGET_NAME_APPLICATION_RTF + || target == TARGET_NAME_TEXT_RICHTEXT; + } + + void OnWorkAreaChanged(Glib::RefPtr screen); + + ToolsAppCtx *mCtx; + GuestDnDMgr *mDnD; + std::string mHGStagingDir; + utf::string mHGFileContentsUriList; + DragDetWnd *mDetWnd; + CPClipboard mClipboard; + DnDBlockControl *mBlockCtrl; + DND_FILE_TRANSFER_STATUS mHGGetFileStatus; + int mHGEffect; + bool mBlockAdded; /* State to determine if drag motion is a drag enter. */ - bool m_GHDnDInProgress; + bool mGHDnDInProgress; /* Icon updates from the guest. */ /* Only update mouse when we have clipboard contents from the host. */ - bool m_GHDnDDataReceived; - bool m_GHDnDDropOccurred; - bool m_unityMode; - bool m_inHGDrag; - DND_DROPEFFECT m_effect; - int32 m_mousePosX; - int32 m_mousePosY; - GdkDragContext *m_dc; - int m_numPendingRequest; - unsigned long m_destDropTime; + bool mGHDnDDataReceived; + bool mGHDnDDropOccurred; + bool mUnityMode; + bool mInHGDrag; + DND_DROPEFFECT mEffect; + int32 mMousePosX; + int32 mMousePosY; + GdkDragContext *mDragCtx; + int mNumPendingRequest; + unsigned long mDestDropTime; uint64 mTotalFileSize; + + /* + * Upper left corner of our work area, a safe place for us to place + * our detection window without clashing with a windows parented to the + * composite overlay window. + */ + Gdk::Point mOrigin; }; #endif // __DND_UI_X11_H__ diff --git a/open-vm-tools/services/plugins/dndcp/xutils/xutils.cc b/open-vm-tools/services/plugins/dndcp/xutils/xutils.cc new file mode 100644 index 000000000..2ed732697 --- /dev/null +++ b/open-vm-tools/services/plugins/dndcp/xutils/xutils.cc @@ -0,0 +1,1370 @@ +/********************************************************* + * 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 +#include +#if GTK_MAJOR_VERSION == 3 +#include +#endif + +#include "xutils/xutils.hh" + +/* These must be after the gtkmm includes, as gtkmm is quite picky. */ +#include +#include +#include + +extern "C" { +#include "vm_assert.h" +} + + +namespace xutils { + + +/* Actual definitions of the signals in this class. */ +sigc::signal > currentDesktopChanged; +sigc::signal > desktopLayoutChanged; +sigc::signal > desktopGeometryChanged; +sigc::signal > desktopViewportChanged; +sigc::signal > windowStackChanged; +sigc::signal > windowManagerChanged; +sigc::signal > activeWindowChanged; +sigc::signal > 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 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 display = Gdk::Display::get_default(); + ::Display* xdisplay = GDK_DISPLAY_XDISPLAY(display->gobj()); + + for (int i = 0; i < display->get_n_screens(); i++) { + Glib::RefPtr screen = display->get_screen(i); + Glib::RefPtr 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 window, // IN: Window + const utf::string& atomName, // IN: Atom name + unsigned long& retValue) // OUT: Return value +{ + ASSERT(window); + ASSERT(!atomName.empty()); + + std::vector 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 window, // IN: Window + const utf::string& atomName, // IN: Atom name + std::vector& retValues) // IN: Return values +{ + ASSERT(window); + ASSERT(window->get_display()); + ASSERT(!atomName.empty()); + + GdkDisplay* display = const_cast(window->get_display()->gobj()); + GdkWindow* gdkwin = const_cast(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 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 window, // IN: + std::vector 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 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 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 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 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 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(values[1]); + rows = static_cast(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 screen, // IN: The screen + uint32& width, // OUT: Width + uint32& height) // OUT: Height +{ + std::vector 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 screen, // IN: The screen + uint32 desktopIndex, // IN: Desktop index + VMPoint& viewport) // OUT: Viewport +{ + std::vector 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 window, // IN: Window to raise + Glib::RefPtr 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 window, // IN: Window to raise + Glib::RefPtr 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(), 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 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 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 gdkWindow = *iter; + std::vector 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 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 window, // IN + GdkAtom state1, // IN + GdkAtom state2) // IN +{ + GdkScreen* screen = window->get_screen()->gobj(); + GdkDisplay* display = const_cast(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 +GetEWMHWindowState(Glib::RefPtr window) // IN +{ + std::list atomStrings; + + GdkDisplay* display = const_cast(window->get_display()->gobj()); + GdkWindow* gdkwin = const_cast(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(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& window, // IN + int& x, // OUT + int& y, // OUT + Gdk::ModifierType& mask) // OUT +{ +#if GTK_MAJOR_VERSION == 3 + Glib::RefPtr deviceManager = + window->get_display()->get_device_manager(); + Glib::RefPtr 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 diff --git a/open-vm-tools/services/plugins/dndcp/xutils/xutils.hh b/open-vm-tools/services/plugins/dndcp/xutils/xutils.hh new file mode 100644 index 000000000..3635f8b00 --- /dev/null +++ b/open-vm-tools/services/plugins/dndcp/xutils/xutils.hh @@ -0,0 +1,117 @@ +/********************************************************* + * 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. + * + *********************************************************/ + + +#ifndef XUTILS_XUTILS_HH +#define XUTILS_XUTILS_HH + + +#include +#include + +#include "stringxx/string.hh" + + +namespace xutils { + +typedef std::list > HostWindowList; + +/* General initialization */ +void Init(); + + +/* General property helpers */ +bool GetCardinal(Glib::RefPtr window, + const utf::string& atomName, unsigned long& retValue); +bool GetCardinalList(Glib::RefPtr window, + const utf::string& atomName, + std::vector& retValues); + +/* + * Utility functions for virtual desktops. + * + * There are two components to virtual desktops: Workspaces and Viewports. + * + * Workspaces can contain one or more viewports. Workspace layouts have a + * corner origin and a direction in which the workspaces are ordered. + * + * Viewports exist inside a workspace and are essentially one large screen + * containing windows. The current viewport has an X, Y offset on this + * large screen containing the physical screen full of content to display. + * + * Some window managers (Metacity, for example) use workspaces exclusively to + * represent virtual desktops, while others (Enlightenment) may use workspaces + * and viewports combined. Compiz uses multiple viewports in a single + * workspace. + */ +void SetDesktopForWindow(Glib::RefPtr window, uint32 desktop); +uint32 GetDesktopForWindow(Glib::RefPtr window); + +uint32 GetNumDesktops(Glib::RefPtr screen); +uint32 GetCurrentDesktop(Glib::RefPtr screen); +extern sigc::signal > currentDesktopChanged; + +bool GetDesktopLayout(Glib::RefPtr screen, + uint32& rows, uint32& columns, Gtk::CornerType& corner, + Gtk::Orientation& orientation); +extern sigc::signal > desktopLayoutChanged; + +bool GetDesktopGeometry(Glib::RefPtr screen, + uint32& width, uint32& height); +extern sigc::signal > desktopGeometryChanged; + +bool GetDesktopViewport(Glib::RefPtr screen, + uint32 desktopIndex, VMPoint& viewport); +extern sigc::signal > desktopViewportChanged; + +extern sigc::signal > activeWindowChanged; + +/* Window stacking */ +void RaiseWindow(Glib::RefPtr window, + Glib::RefPtr sibling = + Glib::RefPtr(), + guint32 timestamp = 0); +HostWindowList GetHostWindowStack(); +extern sigc::signal > windowStackChanged; + + +/* Multi-head */ +void GetMonitorWorkArea(Glib::RefPtr screen, + int monitor, Gdk::Rectangle& rect); + +utf::string GetWindowManagerName(Glib::RefPtr screen); +extern sigc::signal > windowManagerChanged; + +void SetFullscreenMonitorsHint(Glib::RefPtr window, + std::vector topology); + +void ChangeEWMHWindowState(bool add, Glib::RefPtr window, + GdkAtom state1, GdkAtom state2); +std::list GetEWMHWindowState(Glib::RefPtr window); + +void GetPointerLocation(const Glib::RefPtr& window, + int& x, int& y, Gdk::ModifierType& mask); + + +extern sigc::signal > workAreaChanged; + + +} // namespace xutils + + +#endif // XUTILS_XUTILS_HH