1
/*********************************************************
2
* Copyright (C) 2007 VMware, Inc. All rights reserved.
4
* This program is free software; you can redistribute it and/or modify it
5
* under the terms of the GNU Lesser General Public License as published
6
* by the Free Software Foundation version 2.1 and no later version.
8
* This program is distributed in the hope that it will be useful, but
9
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
10
* or FITNESS FOR A PARTICULAR PURPOSE. See the Lesser GNU General Public
11
* License for more details.
13
* You should have received a copy of the GNU Lesser General Public License
14
* along with this program; if not, write to the Free Software Foundation, Inc.,
15
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
17
*********************************************************/
22
* Unity: Guest window manager integration service.
24
* This file implements the guest-side Unity agent as part of the VMware Tools.
25
* It contains entry points for embedding within the VMware Tools User Agent and
26
* handles the GuestRpc (TCLO, RPCI) interface.
28
* UnityWindowTracker updates are sent to the MKS in two ways:
29
* @li @ref UNITY_RPC_GET_UPDATE GuestRpc (host-to-guest).
30
* @li @ref UNITY_RPC_PUSH_UPDATE_CMD GuestRpc (guest-to-host).
32
* @note Looking for the old "unity.get.update" return syntax? See @ref
33
* UNITY_RPC_GET_UPDATE and @ref UnityGetUpdateReturn instead.
47
#include "unityWindowTracker.h"
48
#include "unityCommon.h"
50
#include "unityPlatform.h"
51
#include "unityDebug.h"
53
#include "guestrpc/unity.h"
54
#include "guestrpc/unityActive.h"
55
#include "guestrpc/unity.h"
62
* Singleton object for tracking the state of the service.
64
typedef struct UnityState {
65
UnityWindowTracker tracker;
68
uint32 currentOptions; // Last feature mask received via 'set.options'
69
UnityVirtualDesktopArray virtDesktopArray; // Virtual desktop configuration
70
UnityUpdateChannel updateChannel; // Unity update transmission channel.
71
UnityPlatform *up; // Platform-specific state
74
static UnityState unity;
76
static GuestCapabilities unityCaps[] = {
77
UNITY_CAP_STATUS_UNITY_ACTIVE
85
static Bool UnityUpdateState(void);
86
static void UnityUpdateCallbackFn(void *param, UnityUpdate *update);
87
static Bool UnityTcloGetUpdate(char const **result, size_t *resultLen, const char *name,
88
const char *args, size_t argsSize, void *clientData);
89
static Bool UnityTcloEnter(char const **result, size_t *resultLen, const char *name,
90
const char *args, size_t argsSize, void *clientData);
91
static Bool UnityTcloExit(char const **result, size_t *resultLen, const char *name,
92
const char *args, size_t argsSize, void *clientData);
93
static Bool UnityTcloGetWindowPath(char const **result, size_t *resultLen,
94
const char *name, const char *args,
95
size_t argsSize, void *clientData);
96
static Bool UnityTcloWindowCommand(char const **result,
102
static Bool UnityTcloGetWindowContents(char const **result,
108
static Bool UnityTcloGetIconData(char const **result,
114
static Bool UnityTcloSetDesktopWorkArea(char const **result,
120
static Bool UnityTcloSetTopWindowGroup(char const **result,
126
static Bool UnityTcloShowTaskbar(char const **result,
132
static Bool UnityTcloMoveResizeWindow(char const **result,
138
static Bool UnityTcloSetDesktopConfig(char const **result,
144
static Bool UnityTcloSetDesktopActive(char const **result,
150
static Bool UnityTcloSetWindowDesktop(char const **result,
156
static Bool UnityTcloConfirmOperation(char const **result,
163
static void UnitySetAddHiddenWindows(Bool enabled);
164
static void UnitySetInterlockMinimizeOperation(Bool enabled);
165
static void UnitySetSendWindowContents(Bool enabled);
168
* Wrapper function for the "unity.set.options" RPC.
170
static Bool UnityTcloSetUnityOptions(RpcInData *data);
173
* Wrapper function for the "unity.window.contents.request" RPC.
175
static Bool UnityTcloRequestWindowContents(RpcInData *data);
177
/* Sends the unity.window.contents.start RPC to the host. */
178
Bool UnitySendWindowContentsStart(UnityWindowId window,
183
/* Sends the unity.window.contents.chunk RPC to the host. */
184
Bool UnitySendWindowContentsChunk(UnityWindowId window,
189
/* Sends the unity.window.contents.end RPC to the host. */
190
Bool UnitySendWindowContentsEnd(UnityWindowId window);
193
* Callback function used by UnityXdrSendRpc() to encode XDR-serialized
196
typedef Bool(*UnityXdrEncodeFunc)(XDR*,void*);
199
* Helper function used to send an RPC to the host with XDR-serialized
200
* arguments. Calls encodeFn on the XDR* and the provied arg to perform
203
Bool UnityXdrSendRpc(const char *rpcName, UnityXdrEncodeFunc encodeFn, void *arg);
206
* Dispatch table for Unity window commands. All commands performing actions on
207
* guest unity windows go here.
212
Bool (*exec)(UnityPlatform *up, UnityWindowId window);
215
static UnityCommandElem unityCommandTable[] = {
216
{ UNITY_RPC_WINDOW_CLOSE, UnityPlatformCloseWindow },
217
{ UNITY_RPC_WINDOW_SHOW, UnityPlatformShowWindow },
218
{ UNITY_RPC_WINDOW_HIDE, UnityPlatformHideWindow },
219
{ UNITY_RPC_WINDOW_MINIMIZE, UnityPlatformMinimizeWindow },
220
{ UNITY_RPC_WINDOW_UNMINIMIZE, UnityPlatformUnminimizeWindow },
221
{ UNITY_RPC_WINDOW_MAXIMIZE, UnityPlatformMaximizeWindow },
222
{ UNITY_RPC_WINDOW_UNMAXIMIZE, UnityPlatformUnmaximizeWindow },
223
{ UNITY_RPC_WINDOW_STICK, UnityPlatformStickWindow },
224
{ UNITY_RPC_WINDOW_UNSTICK, UnityPlatformUnstickWindow },
225
/* Add more commands and handlers above this. */
231
void (*setter)(Bool enabled);
232
} UnityFeatureSetter;
235
* Dispatch table for each unity option and a specific function to handle enabling
236
* or disabling the option. The function is called with an enable (TRUE) bool value.
238
static UnityFeatureSetter unityFeatureTable[] = {
239
{ UNITY_ADD_HIDDEN_WINDOWS_TO_TRACKER, UnitySetAddHiddenWindows },
240
{ UNITY_INTERLOCK_MINIMIZE_OPERATION, UnitySetInterlockMinimizeOperation },
241
{ UNITY_SEND_WINDOW_CONTENTS, UnitySetSendWindowContents },
242
/* Add more Unity Feature Setters above this. */
249
* "UnityTcloGetUpdate cannot return the contents of a DynBuf. This will leak
250
* the DynBuf's memory, since nobody at a lower level will ever free it. It's
251
* a crappy interface, but we make due by using a static buffer to hold the
254
* We ideally would not use a static buffer because the maximum size of the
255
* update is unknown. To work around this, make the DynBuf returned in
256
* UnityTcloGetUpdate file-global and recycle it across update requests.
259
static DynBuf gTcloUpdate;
263
*----------------------------------------------------------------------------
265
* Unity_IsSupported --
267
* Determine whether this guest supports unity.
270
* TRUE if the guest supports Unity (i.e. if the guest is WinXP) or
271
* if the option to always enable unity was specified in the tools
278
*----------------------------------------------------------------------------
282
Unity_IsSupported(void)
284
return UnityPlatformIsSupported() || unity.forceEnable;
289
*----------------------------------------------------------------------------
293
* Determine whether we are in Unity mode at this moment.
296
* TRUE if Unity is active.
297
* FALSE is Unity is not active.
302
*----------------------------------------------------------------------------
308
return unity.isEnabled;
313
*-----------------------------------------------------------------------------
317
* One time initialization stuff.
324
* May register with the tools poll loop.
326
*-----------------------------------------------------------------------------
330
Unity_Init(GuestApp_Dict *conf, // IN
331
int *blockedWnd, // IN
332
DesktopSwitchCallbackManager *desktopSwitchCallbackMgr) // IN
335
* If no preferred color is in the config file then use a light gray tone,
336
* the value is stored as xBGR.
338
int desktopColor = /* red */ 0xdc |
339
/* green */ 0xdc << 8 |
340
/* blue */ 0xdc << 16;
341
Debug("Unity_Init\n");
344
* Initialize the UnityWindowTracker object. The uwt does all the actual work
345
* of computing differences between two states of the windowing system. The
346
* callbacks we register here will fire when we request an update via
347
* UnityWindowTracker_RequestUpdates. See bora/lib/unityWindowTracker for more
350
UnityWindowTracker_Init(&unity.tracker, UnityUpdateCallbackFn);
353
* Initialize the update channel.
355
if (UnityUpdateChannelInit(&unity.updateChannel) == FALSE) {
356
Warning("%s: Unable to initialize Unity update channel.\n", __FUNCTION__);
361
* Initialize the host-specific portion of the unity service.
363
unity.up = UnityPlatformInit(&unity.tracker,
364
&unity.updateChannel,
366
desktopSwitchCallbackMgr);
369
* Init our global dynbuf used to send results back.
371
DynBuf_Init(&gTcloUpdate);
374
* If debugging has been enabled, initialize the debug module. On Windows,
375
* this will pop up a small HUD window which shows an echo of the current
376
* state of the windowing system.
378
if (GuestApp_GetDictEntryBool(conf, "unity.debug")) {
379
UnityDebug_Init(&unity.tracker);
383
* Check if the user specified the option to always enable unity regardless
384
* of the guest OS type.
387
unity.forceEnable = GuestApp_GetDictEntryBool(conf, "unity.forceEnable");
388
unity.isEnabled = FALSE;
390
GuestApp_GetDictEntryInt(conf, "unity.desktop.backgroundColor", &desktopColor);
391
UnityPlatformSetConfigDesktopColor(unity.up, desktopColor);
392
unity.virtDesktopArray.desktopCount = 0;
397
*-----------------------------------------------------------------------------
401
* Exit Unity and do final cleanup.
409
*-----------------------------------------------------------------------------
417
Debug("%s\n", __FUNCTION__);
426
* Do one-time final platform-specific cleanup.
430
UnityPlatformCleanup(up);
432
UnityUpdateChannelCleanup(&unity.updateChannel);
433
UnityWindowTracker_Cleanup(&unity.tracker);
434
DynBuf_Destroy(&gTcloUpdate);
439
*-----------------------------------------------------------------------------
441
* Unity_InitBackdoor --
443
* One time initialization stuff for the backdoor.
452
*-----------------------------------------------------------------------------
456
Unity_InitBackdoor(struct RpcIn *rpcIn) // IN
459
* Only register the callback if the guest is capable of supporting Unity.
460
* This way, if the VMX/UI sends us a Unity request on a non-supported platform
461
* (for whatever reason), we will reply with 'command not supported'.
464
if (Unity_IsSupported()) {
465
UnityCommandElem *elem;
467
RpcIn_RegisterCallback(rpcIn, UNITY_RPC_ENTER, UnityTcloEnter, NULL);
468
RpcIn_RegisterCallback(rpcIn, UNITY_RPC_GET_UPDATE_FULL, UnityTcloGetUpdate, NULL);
469
RpcIn_RegisterCallback(rpcIn, UNITY_RPC_GET_UPDATE_INCREMENTAL,
470
UnityTcloGetUpdate, NULL);
471
RpcIn_RegisterCallback(rpcIn, UNITY_RPC_GET_WINDOW_PATH,
472
UnityTcloGetWindowPath, NULL);
473
RpcIn_RegisterCallback(rpcIn, UNITY_RPC_WINDOW_SETTOP,
474
UnityTcloSetTopWindowGroup, NULL);
475
RpcIn_RegisterCallback(rpcIn, UNITY_RPC_GET_WINDOW_CONTENTS,
476
UnityTcloGetWindowContents, NULL);
477
RpcIn_RegisterCallback(rpcIn, UNITY_RPC_GET_ICON_DATA,
478
UnityTcloGetIconData, NULL);
479
RpcIn_RegisterCallback(rpcIn, UNITY_RPC_DESKTOP_WORK_AREA_SET,
480
UnityTcloSetDesktopWorkArea, NULL);
481
RpcIn_RegisterCallback(rpcIn, UNITY_RPC_SHOW_TASKBAR, UnityTcloShowTaskbar, NULL);
482
RpcIn_RegisterCallback(rpcIn, UNITY_RPC_EXIT, UnityTcloExit, NULL);
483
RpcIn_RegisterCallback(rpcIn, UNITY_RPC_WINDOW_MOVE_RESIZE,
484
UnityTcloMoveResizeWindow, NULL);
485
RpcIn_RegisterCallback(rpcIn, UNITY_RPC_DESKTOP_CONFIG_SET,
486
UnityTcloSetDesktopConfig, NULL);
487
RpcIn_RegisterCallback(rpcIn, UNITY_RPC_DESKTOP_ACTIVE_SET,
488
UnityTcloSetDesktopActive, NULL);
489
RpcIn_RegisterCallback(rpcIn, UNITY_RPC_WINDOW_DESKTOP_SET,
490
UnityTcloSetWindowDesktop, NULL);
491
RpcIn_RegisterCallback(rpcIn, UNITY_RPC_CONFIRM_OPERATION,
492
UnityTcloConfirmOperation, NULL);
494
RpcIn_RegisterCallbackEx(rpcIn, UNITY_RPC_SET_OPTIONS,
495
UnityTcloSetUnityOptions, NULL);
497
RpcIn_RegisterCallbackEx(rpcIn, UNITY_RPC_WINDOW_CONTENTS_REQUEST,
498
UnityTcloRequestWindowContents, NULL);
501
* Handle all of the UnityTcloWindowCommand RPCs at once.
503
for (elem = unityCommandTable; elem->name != NULL; elem++) {
504
RpcIn_RegisterCallback(rpcIn, elem->name, UnityTcloWindowCommand,
512
*-----------------------------------------------------------------------------
514
* Unity_SetActiveDnDDetWnd --
516
* Right now we have 2 Unity DnD full screen detection window, one for version
517
* 2 or older, another for version 3 or newer. This function is to set active
518
* one according to host DnD version.
520
* XXX Both full-screent window is still bottom-most and is showed all time
521
* during Unity mode. Another change is needed to change it to only show the
522
* window during guest->host DnD. Also the window should not be bottom-most,
523
* but dynamicly change z-order during DnD.
531
*-----------------------------------------------------------------------------
535
Unity_SetActiveDnDDetWnd(UnityDnD *state)
537
if (unity.up != NULL) {
538
UnityPlatformSetActiveDnDDetWnd(unity.up, state);
544
*-----------------------------------------------------------------------------
548
* Called everytime we exit Unity. This function can be called when we are
549
* not in Unity mode. Right now it is called every time a 'reset' TCLO command
550
* is sent to the guest. Therefore, there's no guarantee that we were in the
551
* Unity mode when this function is called.
553
* Try to do the following:
554
* Restore system settings if needed.
555
* Kills all unity helper threads if any are running.
556
* Hide the unity dnd detection window.
562
* Restores system settings since we are exiting Unity.
563
* Kills all unity helper threads if any.
564
* Hides the unity dnd detection window if needed.
566
*-----------------------------------------------------------------------------
572
int featureIndex = 0;
574
if (unity.isEnabled) {
576
* Reset any Unity Options - they'll be re-enabled as required before the
577
* next UnityTcloEnter.
579
while (unityFeatureTable[featureIndex].featureBit != 0) {
580
if (unity.currentOptions & unityFeatureTable[featureIndex].featureBit) {
581
unityFeatureTable[featureIndex].setter(FALSE);
585
unity.currentOptions = 0;
587
/* Hide full-screen detection window for Unity DnD. */
588
UnityPlatformUpdateDnDDetWnd(unity.up, FALSE);
590
/* Kill Unity helper threads. */
591
UnityPlatformKillHelperThreads(unity.up);
593
/* Restore previously saved user settings. */
594
UnityPlatformRestoreSystemSettings(unity.up);
596
unity.isEnabled = FALSE;
602
*-----------------------------------------------------------------------------
604
* Unity_RegisterCaps --
606
* Called by the application (VMwareUser) to allow the unity subsystem to
607
* register its capabilities.
615
*-----------------------------------------------------------------------------
619
Unity_RegisterCaps(void)
622
* Send Unity capability.
625
if (!RpcOut_sendOne(NULL, NULL, UNITY_RPC_UNITY_CAP" %d",
626
Unity_IsSupported() ? 1 : 0)) {
627
Debug("%s: could not set unity capability\n", __FUNCTION__);
631
* Register guest platform specific capabilities.
634
UnityPlatformRegisterCaps(unity.up);
635
AppUtil_SendGuestCaps(unityCaps, ARRAYSIZE(unityCaps), TRUE);
640
*-----------------------------------------------------------------------------
642
* Unity_UnregisterCaps --
644
* Called by the application (VMwareUser) to allow the unity subsystem to
645
* unregister its capabilities.
653
*-----------------------------------------------------------------------------
657
Unity_UnregisterCaps(void)
660
* Unregister guest platform specific capabilities.
663
UnityPlatformUnregisterCaps(unity.up);
666
* Unregister the unity capability.
669
if (!RpcOut_sendOne(NULL, NULL, UNITY_RPC_UNITY_CAP" 0")) {
670
Debug("Failed to unregister Unity capability\n");
672
AppUtil_SendGuestCaps(unityCaps, ARRAYSIZE(unityCaps), FALSE);
677
*----------------------------------------------------------------------------
681
* RPC handler for 'unity.enter'. Save and disable certain user
682
* settings. Start Unity updates thread and any other platform
683
* specific threads (like a thread that listens for
684
* the desktop switch event on Windows). Note that we first set
685
* the UI settings, and then start the threads. This way the UI
686
* settings take effect before we start sending Unity updates,
687
* so that we never send things like task bar (see bug 166085).
690
* TRUE if helper threads were started.
694
* Certain UI system settings will be disabled.
695
* Unity update thread will be started.
696
* Any other platform specific helper threads will be started as well.
698
*----------------------------------------------------------------------------
702
UnityTcloEnter(char const **result, // OUT
703
size_t *resultLen, // OUT
704
const char *name, // IN
705
const char *args, // IN
706
size_t argsSize, // ignored
707
void *clientData) // ignored
709
Debug("%s\n", __FUNCTION__);
711
if (!unity.isEnabled) {
712
/* Save and disable certain user settings here. */
713
UnityPlatformSaveSystemSettings(unity.up);
715
/* Start Unity helper threads. */
716
if (!UnityPlatformStartHelperThreads(unity.up)) {
719
* If we couldn't start one or more helper threads,
720
* we cannot enter Unity. Kill all running helper
721
* threads and restore ui settings.
724
UnityPlatformKillHelperThreads(unity.up);
725
UnityPlatformRestoreSystemSettings(unity.up);
726
return RpcIn_SetRetVals(result, resultLen,
727
"Could not start unity helper threads", FALSE);
731
* Show full-screen detection window for Unity DnD. It is a bottom-most (but
732
* still in front of desktop) transparent detection window for guest->host DnD
733
* as drop target. We need this window because:
734
* 1. All active windows except desktop will be shown on host desktop and can
735
* accept DnD signal. This full-screen detection window will block any DnD signal
736
* (even mouse signal) to the desktop, which will fix bug 164880.
737
* 2. With this full-screen but bottommost detection window, every time when user
738
* drag something out from active window, the dragEnter will always be immediately
739
* catched for Unity DnD.
741
UnityPlatformUpdateDnDDetWnd(unity.up, TRUE);
742
unity.isEnabled = TRUE;
747
return RpcIn_SetRetVals(result, resultLen, "", TRUE);
752
*----------------------------------------------------------------------------
756
* RPC handler for 'unity.exit'.
762
* Same as side effects of Unity_Exit().
764
*----------------------------------------------------------------------------
768
UnityTcloExit(char const **result, // OUT
769
size_t *resultLen, // OUT
770
const char *name, // IN
771
const char *args, // IN
772
size_t argsSize, // ignored
773
void *clientData) // ignored
775
Debug("UnityTcloExit.\n");
780
return RpcIn_SetRetVals(result, resultLen, "", TRUE);
785
*----------------------------------------------------------------------------
787
* UnityTcloGetWindowPath --
789
* RPC handler for UNITY_RPC_GET_WINDOW_PATH.
791
* Get the information needed to re-launch a window and retrieve further
792
* information on it. Returns double-NUL-terminated buffer consisting of
793
* NUL-terminated strings "windowPath" and "execPath" strings, the first
794
* uniquely identifying the window and the second uniquely identifying the
795
* window's owning executable.
798
* TRUE if everything is successful.
804
*----------------------------------------------------------------------------
808
UnityTcloGetWindowPath(char const **result, // OUT
809
size_t *resultLen, // OUT
810
const char *name, // IN
811
const char *args, // IN
812
size_t argsSize, // ignored
813
void *clientData) // ignored
816
UnityWindowId window;
817
DynBuf windowPathUtf8;
820
unsigned int index = 0;
823
Debug("UnityTcloGetWindowPath name:%s args:'%s'\n", name, args);
825
/* Parse the command & window id.*/
827
if (!StrUtil_GetNextIntToken(&window, &index, args, " ")) {
828
Debug("UnityTcloGetWindowInfo: Invalid RPC arguments.\n");
829
return RpcIn_SetRetVals(result, resultLen,
830
"Invalid arguments. Expected \"windowId\"",
834
Debug("UnityTcloGetWindowInfo: window %d\n", window);
837
* Please note that the UnityPlatformGetWindowPath implementations assume that the
838
* dynbuf passed in does not contain any existing data that needs to be appended to,
839
* so this code should continue to accomodate that assumption.
841
DynBuf_Destroy(&gTcloUpdate);
842
DynBuf_Init(&gTcloUpdate);
843
DynBuf_Init(&windowPathUtf8);
844
DynBuf_Init(&execPathUtf8);
845
if (!UnityPlatformGetWindowPath(unity.up, window, &windowPathUtf8, &execPathUtf8)) {
846
Debug("UnityTcloGetWindowInfo: Could not get window path.\n");
847
ret = RpcIn_SetRetVals(result, resultLen,
848
"Could not get window path",
854
* Construct the buffer holding the result. Note that we need to use gTcloUpdate
855
* here to avoid leaking during the RPC handler.
857
DynBuf_Copy(&windowPathUtf8, &gTcloUpdate);
858
DynBuf_Append(&gTcloUpdate, DynBuf_Get(&execPathUtf8), DynBuf_GetSize(&execPathUtf8));
861
* Write the final result into the result out parameters and return!
863
*result = (char *)DynBuf_Get(&gTcloUpdate);
864
*resultLen = DynBuf_GetSize(&gTcloUpdate);
867
DynBuf_Destroy(&windowPathUtf8);
868
DynBuf_Destroy(&execPathUtf8);
874
*----------------------------------------------------------------------------
876
* UnityTcloWindowCommand --
878
* RPC handler for 'unity.window.*' (excluding 'unity.window.settop')
881
* TRUE if everything is successful.
887
*----------------------------------------------------------------------------
891
UnityTcloWindowCommand(char const **result, // OUT
892
size_t *resultLen, // OUT
893
const char *name, // IN
894
const char *args, // IN
895
size_t argsSize, // ignored
896
void *clientData) // ignored
898
UnityWindowId window;
899
unsigned int index = 0;
902
Debug("UnityTcloWindowCommand: name:%s args:'%s'\n", name, args);
904
/* Parse the command & window id.*/
906
if (!StrUtil_GetNextIntToken(&window, &index, args, " ")) {
907
Debug("UnityTcloWindowCommand: Invalid RPC arguments.\n");
908
return RpcIn_SetRetVals(result, resultLen,
909
"Invalid arguments. Expected \"windowId\"",
914
Debug("UnityTcloWindowCommand: %s window %d\n", name, window);
916
for (i = 0; unityCommandTable[i].name != NULL; i++) {
917
if (strcmp(unityCommandTable[i].name, name) == 0) {
918
if (!unityCommandTable[i].exec(unity.up, window)) {
919
Debug("Unity window command failed.\n");
920
return RpcIn_SetRetVals(result, resultLen,
921
"Could not execute window command",
924
return RpcIn_SetRetVals(result, resultLen, "", TRUE);
929
return RpcIn_SetRetVals(result, resultLen, "Bad command", FALSE);
934
*----------------------------------------------------------------------------
936
* UnityTcloSetDesktopWorkArea --
938
* RPC handler for 'unity.desktop.work_area.set'.
941
* TRUE if everything is successful.
947
*----------------------------------------------------------------------------
951
UnityTcloSetDesktopWorkArea(char const **result, // IN
952
size_t *resultLen, // IN
953
const char *name, // IN
954
const char *args, // IN
955
size_t argsSize, // IN
956
void *clientData) // IN
958
Bool success = FALSE;
961
UnityRect *workAreas = NULL;
964
* The argument string will look something like:
965
* <count> [ , <x> <y> <w> <h> ] * count.
968
* 3 , 0 0 640 480 , 640 0 800 600 , 0 480 640 480
971
if (sscanf(args, "%u", &count) != 1) {
972
return RpcIn_SetRetVals(result, resultLen,
973
"Invalid arguments. Expected \"count\"",
977
workAreas = (UnityRect *)malloc(sizeof *workAreas * count);
979
RpcIn_SetRetVals(result, resultLen,
980
"Failed to alloc buffer for work areas",
985
for (i = 0; i < count; i++) {
986
args = strchr(args, ',');
988
RpcIn_SetRetVals(result, resultLen,
989
"Expected comma separated display list",
993
args++; /* Skip past the , */
995
if (sscanf(args, " %d %d %d %d ",
996
&workAreas[i].x, &workAreas[i].y,
997
&workAreas[i].width, &workAreas[i].height) != 4) {
998
RpcIn_SetRetVals(result, resultLen,
999
"Expected x, y, w, h in display entry",
1004
if (workAreas[i].x < 0 || workAreas[i].y < 0 ||
1005
workAreas[i].width <= 0 || workAreas[i].height <= 0) {
1006
RpcIn_SetRetVals(result, resultLen, "Invalid argument", FALSE);
1011
if (!UnityPlatformSetDesktopWorkAreas(unity.up, workAreas, count)) {
1012
RpcIn_SetRetVals(result, resultLen,
1013
"UnityPlatformSetDesktopWorkAreas failed",
1018
success = RpcIn_SetRetVals(result, resultLen, "", TRUE);
1027
*----------------------------------------------------------------------------
1029
* UnityTcloSetTopWindowGroup --
1031
* RPC handler for 'unity.window.settop'.
1034
* TRUE if everything is successful.
1040
*----------------------------------------------------------------------------
1044
UnityTcloSetTopWindowGroup(char const **result, // OUT
1045
size_t *resultLen, // OUT
1046
const char *name, // IN
1047
const char *args, // IN
1048
size_t argsSize, // ignored
1049
void *clientData) // ignored
1051
UnityWindowId window;
1052
unsigned int index = 0;
1053
unsigned int windowCount = 0;
1054
UnityWindowId windows[UNITY_MAX_SETTOP_WINDOW_COUNT];
1056
Debug("%s: name:%s args:'%s'\n", __FUNCTION__, name, args);
1058
/* Parse the command & window ids.*/
1060
while (StrUtil_GetNextUintToken(&window, &index, args, " ")) {
1061
windows[windowCount] = window;
1063
if (windowCount == UNITY_MAX_SETTOP_WINDOW_COUNT) {
1064
Debug("%s: Too many windows.\n", __FUNCTION__);
1065
return RpcIn_SetRetVals(result, resultLen,
1066
"Invalid arguments. Too many windows",
1071
if (windowCount == 0) {
1072
Debug("%s: Invalid RPC arguments.\n", __FUNCTION__);
1073
return RpcIn_SetRetVals(result, resultLen,
1074
"Invalid arguments. Expected at least one windowId",
1078
if (!UnityPlatformSetTopWindowGroup(unity.up, windows, windowCount)) {
1079
return RpcIn_SetRetVals(result, resultLen,
1080
"Could not execute window command",
1084
return RpcIn_SetRetVals(result, resultLen, "", TRUE);
1089
*----------------------------------------------------------------------------
1091
* UnityTcloGetUpdate --
1093
* RPC handler for 'unity.get.update'. Ask the unity window tracker
1094
* to give us an update (either incremental or non-incremental based
1095
* on whether the 'incremental' arg is present) and send the result
1104
*----------------------------------------------------------------------------
1108
UnityTcloGetUpdate(char const **result, // OUT
1109
size_t *resultLen, // OUT
1110
const char *name, // IN
1111
const char *args, // IN
1112
size_t argsSize, // ignored
1113
void *clientData) // ignored
1115
Bool incremental = FALSE;
1117
Debug("UnityTcloGetUpdate name:%s args:'%s'", name, args);
1120
* Specify incremental or non-incremetal updates based on whether or
1121
* not the client set the "incremental" arg.
1123
if (strstr(name, "incremental")) {
1128
* Call into platform-specific implementation to gather and send updates
1129
* back via RPCI. (This is done to ensure all updates are sent to the
1130
* Unity server in sequence via the same channel.)
1132
UnityPlatformDoUpdate(unity.up, incremental);
1135
* To maintain compatibility, we'll return a successful but empty response.
1141
* Give the debugger a crack to do something interesting at this point
1143
* XXX Not sure if this is worth keeping around since this routine no
1144
* longer returns updates directly.
1146
UnityDebug_OnUpdate();
1153
*----------------------------------------------------------------------------
1155
* UnityTcloConfirmOperation --
1157
* RPC handler for 'unity.operation.confirm'.
1160
* TRUE if the confirmation could be handled sucessfully.
1166
*----------------------------------------------------------------------------
1170
UnityTcloConfirmOperation(char const **result, // OUT
1171
size_t *resultLen, // OUT
1172
const char *name, // IN
1173
const char *args, // IN
1174
size_t argsSize, // IN
1175
void *clientData) // ignored
1177
UnityConfirmOperation unityConfirmOpMsg = {0};
1178
UnityConfirmOperationV1 *confirmV1 = NULL;
1179
Bool retVal = FALSE;
1181
Debug("%s: Enter.\n", __FUNCTION__);
1184
* Deserialize the XDR data. Note that the data begins with args + 1 since
1185
* there is a space between the RPC name and the XDR serialization.
1187
if (!XdrUtil_Deserialize((char *)args + 1, argsSize - 1,
1188
xdr_UnityConfirmOperation, &unityConfirmOpMsg)) {
1189
ret = RpcIn_SetRetVals(result, resultLen, "Failed to deserialize data", FALSE);
1193
confirmV1 = unityConfirmOpMsg.UnityConfirmOperation_u.unityConfirmOpV1;
1194
if (MINIMIZE == confirmV1->details.op) {
1195
retVal = UnityPlatformConfirmMinimizeOperation(unity.up,
1196
confirmV1->windowId,
1197
confirmV1->sequence,
1200
Debug("%s: Confirmation for unknown operation ID = %d\n", __FUNCTION__,
1201
confirmV1->details.op);
1203
/* Free any memory allocated by XDR - we're done with unityConfirmOpMsg */
1204
VMX_XDR_FREE(xdr_UnityConfirmOperation, &unityConfirmOpMsg);
1205
ret = RpcIn_SetRetVals(result, resultLen, "", retVal);
1208
Debug("%s: Exit.\n", __FUNCTION__);
1214
*----------------------------------------------------------------------------
1216
* UnityGetUpdateCommon --
1218
* Get the unity window update and append it to the specified output buffer.
1219
* This function can be called from two different threads: either from
1220
* the main thread that is trying to execute a TCLO command (unity.get.update)
1221
* or from the unity update thread that is gathering periodic updates and
1222
* pushes them to the VMX as needed (by calling 'tools.unity.push.update RPC).
1223
* Since this function can be called from two different threads, protect
1224
* the global unity singleton with locks.
1232
*----------------------------------------------------------------------------
1236
UnityGetUpdateCommon(int flags, // IN: unity update flags
1237
DynBuf *buf) // IN/OUT: unity update buffer
1242
UnityPlatformLock(unity.up);
1245
* Ask the guest to crawl the windowing system and push updates
1246
* into the unity window tracker. If the guest backend isn't able to get
1247
* notification of destroyed windows, UnityPlatformUpdateWindowState will
1248
* return TRUE, which is are signal to set the UNITY_UPDATE_REMOVE_UNTOUCHED
1249
* flag. This make the unity window tracker generate remove events for
1250
* windows that it hasn't seen an update for since the last update
1253
if (UnityPlatformUpdateWindowState(unity.up, &unity.tracker)) {
1254
flags |= UNITY_UPDATE_REMOVE_UNTOUCHED;
1258
* Generate the update string. We'll accumulate updates in the DynBuf
1259
* buf via the callbacks registered in Unity_Init(). Each update will
1260
* append a null terminated string to buf.
1262
UnityWindowTracker_RequestUpdates(&unity.tracker, flags, buf);
1264
UnityPlatformUnlock(unity.up);
1267
* Write the final '\0' to the DynBuf to signal that we're all out of
1270
DynBuf_AppendString(buf, "");
1277
*----------------------------------------------------------------------------
1279
* UnityUpdateCallbackFn --
1281
* Callback from the unity window tracker indicating something's
1284
* Write the update string into our dynbuf accumlating the update
1293
*----------------------------------------------------------------------------
1297
UnityUpdateCallbackFn(void *param, // IN: dynbuf
1298
UnityUpdate *update) // IN
1300
DynBuf *buf = (DynBuf *)param;
1302
int i, n, count = 0;
1304
char *titleUtf8 = NULL;
1305
char *windowPathUtf8 = "";
1306
char *execPathUtf8 = "";
1308
switch (update->type) {
1310
case UNITY_UPDATE_ADD_WINDOW:
1311
if (DynBuf_GetSize(&update->u.addWindow.windowPathUtf8) > 0) {
1312
windowPathUtf8 = DynBuf_Get(&update->u.addWindow.windowPathUtf8);
1314
if (DynBuf_GetSize(&update->u.addWindow.execPathUtf8) > 0) {
1315
execPathUtf8 = DynBuf_Get(&update->u.addWindow.execPathUtf8);
1318
Str_Sprintf(data, sizeof data, "add %u windowPath=%s execPath=%s",
1319
update->u.addWindow.id,
1322
DynBuf_AppendString(buf, data);
1325
case UNITY_UPDATE_MOVE_WINDOW:
1326
Str_Sprintf(data, sizeof data, "move %u %d %d %d %d",
1327
update->u.moveWindow.id,
1328
update->u.moveWindow.rect.x1,
1329
update->u.moveWindow.rect.y1,
1330
update->u.moveWindow.rect.x2,
1331
update->u.moveWindow.rect.y2);
1332
DynBuf_AppendString(buf, data);
1335
case UNITY_UPDATE_REMOVE_WINDOW:
1337
* Let the platform know that this window has been removed. This is
1338
* useful on platforms that must poll for window changes.
1340
UnityPlatformWillRemoveWindow(unity.up, update->u.removeWindow.id);
1342
Str_Sprintf(data, sizeof data, "remove %u", update->u.removeWindow.id);
1343
DynBuf_AppendString(buf, data);
1346
case UNITY_UPDATE_CHANGE_WINDOW_REGION:
1348
* A null region indicates that the region should be deleted.
1349
* Make sure we write "region <id> 0" for the reply.
1351
region = update->u.changeWindowRegion.region;
1353
count = REGION_NUM_RECTS(region);
1355
Str_Sprintf(data, sizeof data, "region %u %d",
1356
update->u.changeWindowRegion.id, count);
1357
DynBuf_AppendString(buf, data);
1359
for (i = 0; i < count; i++) {
1360
BoxPtr p = REGION_RECTS(region) + i;
1361
Str_Sprintf(data, sizeof data, "rect %d %d %d %d",
1362
p->x1, p->y1, p->x2, p->y2);
1363
DynBuf_AppendString(buf, data);
1367
case UNITY_UPDATE_CHANGE_WINDOW_TITLE:
1368
titleUtf8 = DynBuf_Get(&update->u.changeWindowTitle.titleUtf8);
1371
(DynBuf_GetSize(&update->u.changeWindowTitle.titleUtf8) ==
1372
strlen(titleUtf8) + 1)) {
1373
Str_Sprintf(data, sizeof data, "title %u ",
1374
update->u.changeWindowTitle.id);
1375
Str_Strncat(data, sizeof data, titleUtf8, sizeof data - strlen(data) - 1);
1376
data[sizeof data - 1] = '\0';
1378
Str_Sprintf(data, sizeof data, "title %u",
1379
update->u.changeWindowTitle.id);
1381
DynBuf_AppendString(buf, data);
1384
case UNITY_UPDATE_CHANGE_ZORDER:
1385
n = Str_Snprintf(data, sizeof data, "zorder %d", update->u.zorder.count);
1386
DynBuf_Append(buf, data, n);
1387
for (i = 0; i < update->u.zorder.count; i++) {
1388
n = Str_Snprintf(data, sizeof data, " %d", update->u.zorder.ids[i]);
1389
DynBuf_Append(buf, data, n);
1391
DynBuf_AppendString(buf, ""); // for appending NULL
1394
case UNITY_UPDATE_CHANGE_WINDOW_STATE:
1395
Str_Sprintf(data, sizeof data, "state %u %u",
1396
update->u.changeWindowState.id,
1397
update->u.changeWindowState.state);
1398
DynBuf_AppendString(buf, data);
1401
case UNITY_UPDATE_CHANGE_WINDOW_ATTRIBUTE:
1402
Str_Sprintf(data, sizeof data, "attr %u %u %u",
1403
update->u.changeWindowAttribute.id,
1404
update->u.changeWindowAttribute.attr,
1405
update->u.changeWindowAttribute.value);
1406
DynBuf_AppendString(buf, data);
1409
case UNITY_UPDATE_CHANGE_WINDOW_TYPE:
1410
Str_Sprintf(data, sizeof data, "type %u %d",
1411
update->u.changeWindowType.id,
1412
update->u.changeWindowType.winType);
1413
DynBuf_AppendString(buf, data);
1416
case UNITY_UPDATE_CHANGE_WINDOW_ICON:
1417
Str_Sprintf(data, sizeof data, "icon %u %u",
1418
update->u.changeWindowIcon.id,
1419
update->u.changeWindowIcon.iconType);
1420
DynBuf_AppendString(buf, data);
1423
case UNITY_UPDATE_CHANGE_WINDOW_DESKTOP:
1424
Str_Sprintf(data, sizeof data, "desktop %u %d",
1425
update->u.changeWindowDesktop.id,
1426
update->u.changeWindowDesktop.desktopId);
1427
DynBuf_AppendString(buf, data);
1430
case UNITY_UPDATE_CHANGE_ACTIVE_DESKTOP:
1431
Str_Sprintf(data, sizeof data, "activedesktop %d",
1432
update->u.changeActiveDesktop.desktopId);
1433
DynBuf_AppendString(buf, data);
1443
*-----------------------------------------------------------------------------
1445
* UnityUpdateChannelInit --
1447
* Initialize the state for the update thread.
1450
* TRUE if all needed data was initialized.
1454
* RpcOut channel might be open.
1455
* Memory for the update buffer might be allocated.
1457
*-----------------------------------------------------------------------------
1461
UnityUpdateChannelInit(UnityUpdateChannel *updateChannel) // IN
1463
ASSERT(updateChannel);
1465
updateChannel->rpcOut = NULL;
1466
updateChannel->cmdSize = 0;
1468
DynBuf_Init(&updateChannel->updates);
1469
DynBuf_AppendString(&updateChannel->updates, UNITY_RPC_PUSH_UPDATE_CMD " ");
1471
/* Exclude the null. */
1472
updateChannel->cmdSize = DynBuf_GetSize(&updateChannel->updates) - 1;
1473
DynBuf_SetSize(&updateChannel->updates, updateChannel->cmdSize);
1475
updateChannel->rpcOut = RpcOut_Construct();
1476
if (updateChannel->rpcOut == NULL) {
1480
if (!RpcOut_start(updateChannel->rpcOut)) {
1481
RpcOut_Destruct(updateChannel->rpcOut);
1488
DynBuf_Destroy(&updateChannel->updates);
1495
*-----------------------------------------------------------------------------
1497
* UnityUpdateChannelCleanup --
1499
* Cleanup the unity update thread state.
1505
* RpcOut channel will be closed.
1506
* Memory will be freed.
1508
*-----------------------------------------------------------------------------
1512
UnityUpdateChannelCleanup(UnityUpdateChannel *updateChannel) // IN
1514
if (updateChannel && updateChannel->rpcOut) {
1515
RpcOut_stop(updateChannel->rpcOut);
1516
RpcOut_Destruct(updateChannel->rpcOut);
1517
updateChannel->rpcOut = NULL;
1519
DynBuf_Destroy(&updateChannel->updates); // Avoid double-free by guarding this as well
1526
*-----------------------------------------------------------------------------
1530
* Prints a Unity update via debug output. NUL is represented as '!'.
1538
*-----------------------------------------------------------------------------
1542
DumpUpdate(UnityUpdateChannel *updateChannel) // IN
1547
len = updateChannel->updates.size;
1548
buf = Util_SafeMalloc(len + 1);
1549
memcpy(buf, updateChannel->updates.data, len);
1551
for (i = 0 ; i < len; i++) {
1552
if (buf[i] == '\0') {
1557
Debug("%s: Sending update: %s\n", __FUNCTION__, buf);
1561
#endif // ifdef VMX86_DEVEL
1565
*-----------------------------------------------------------------------------
1567
* UnitySendUpdates --
1569
* Gather and send a round of unity updates. The caller is responsible
1570
* for gathering updates into updateChannel->updates buffer prior to the
1571
* function call. This function should only be called if there's data
1572
* in the update buffer to avoid sending empty update string to the VMX.
1575
* TRUE if the update was sent,
1576
* FALSE if something went wrong (an invalid RPC channel, for example).
1581
*-----------------------------------------------------------------------------
1585
UnitySendUpdates(UnityUpdateChannel *updateChannel) // IN
1587
char const *myReply;
1591
ASSERT(updateChannel);
1592
ASSERT(updateChannel->rpcOut);
1594
/* Send 'tools.unity.push.update <updates>' to the VMX. */
1597
DumpUpdate(updateChannel);
1601
if (!RpcOut_send(updateChannel->rpcOut,
1602
(char *)DynBuf_Get(&updateChannel->updates),
1603
DynBuf_GetSize(&updateChannel->updates),
1604
&myReply, &myRepLen)) {
1607
* We could not send the RPC. If we haven't tried to reopen
1608
* the channel, try to reopen and resend. If we already
1609
* tried to resend, then it's time to give up. I hope that
1610
* trying to resend once is enough.
1615
Debug("%s: could not send rpc. Reopening channel.\n", __FUNCTION__);
1616
RpcOut_stop(updateChannel->rpcOut);
1617
if (!RpcOut_start(updateChannel->rpcOut)) {
1618
Debug("%s: could not reopen rpc channel. Exiting...\n", __FUNCTION__);
1624
Debug("%s: could not resend rpc. Giving up and exiting...\n", __FUNCTION__);
1630
* With the update queue sent, purge the DynBuf by trimming it to the length
1631
* of the command preamble.
1633
DynBuf_SetSize(&updateChannel->updates, updateChannel->cmdSize);
1640
*----------------------------------------------------------------------------
1642
* UnityTcloGetWindowContents --
1644
* RPC handler for 'unity.get.window.contents'. Suck the bits off the
1645
* window and return a .png image over the backdoor.
1648
* TRUE if everything is successful.
1654
*----------------------------------------------------------------------------
1658
UnityTcloGetWindowContents(char const **result, // OUT
1659
size_t *resultLen, // OUT
1660
const char *name, // IN
1661
const char *args, // IN
1662
size_t argsSize, // ignored
1663
void *clientData) // ignored
1665
unsigned int window;
1666
unsigned int index = 0;
1667
DynBuf *imageData = &gTcloUpdate;
1671
Debug("UnityTcloGetWindowContents: name:%s args:'%s'\n", name, args);
1674
* Parse the command & window id.
1676
if (!StrUtil_GetNextIntToken(&window, &index, args, " ")) {
1677
Debug("UnityTcloGetWindowContents: Invalid RPC arguments.\n");
1678
return RpcIn_SetRetVals(result, resultLen,
1679
"failed: arguments. Expected \"windowId\"",
1683
Debug("UnityTcloGetWindowContents: window %d\n", window);
1686
* Read the contents of the window, compress it as a .png and
1687
* send the .png back to the vmx as the RPC result.
1689
DynBuf_SetSize(imageData, 0);
1690
if (!UnityPlatformGetWindowContents(unity.up, window, imageData, &width, &height)) {
1691
return RpcIn_SetRetVals(result, resultLen,
1692
"failed: Could not read window contents",
1696
*result = (char *)DynBuf_Get(imageData);
1697
*resultLen = DynBuf_GetSize(imageData);
1704
*----------------------------------------------------------------------------
1706
* UnityTcloGetIconData --
1708
* RPC handler for 'unity.get.icon.data'. Suck the bits off the
1709
* window and return a .png image over the backdoor.
1712
* TRUE if everything is successful.
1718
*----------------------------------------------------------------------------
1722
UnityTcloGetIconData(char const **result, // OUT
1723
size_t *resultLen, // OUT
1724
const char *name, // IN
1725
const char *args, // IN
1726
size_t argsSize, // ignored
1727
void *clientData) // ignored
1729
UnityWindowId window;
1730
UnityIconType iconType;
1731
UnityIconSize iconSize;
1732
unsigned int dataOffset, dataLength;
1735
DynBuf *results = &gTcloUpdate, imageData;
1738
Debug("UnityTcloGetIconData: name:%s args:'%s'\n", name, args);
1741
* Parse the arguments.
1743
if ((sscanf(args, "%u %u %u %u %u",
1749
|| (dataLength > UNITY_MAX_ICON_DATA_CHUNK)) {
1750
Debug("UnityTcloGetIconData: Invalid RPC arguments.\n");
1751
return RpcIn_SetRetVals(result, resultLen,
1752
"failed: arguments missing",
1756
Debug("%s: window %u iconType %u" \
1757
" iconSize %u dataOffset %u dataLength %u\n",
1759
window, iconType, iconSize, dataOffset, dataLength);
1762
* Retrieve part/all of the icon in PNG format.
1764
DynBuf_Init(&imageData);
1765
if (!UnityPlatformGetIconData(unity.up, window, iconType, iconSize,
1766
dataOffset, dataLength, &imageData, &fullLength)) {
1767
return RpcIn_SetRetVals(result, resultLen,
1768
"failed: Could not read icon data properly",
1773
DynBuf_SetSize(results, 0);
1774
retLength = DynBuf_GetSize(&imageData);
1775
retLength = MIN(retLength, UNITY_MAX_ICON_DATA_CHUNK);
1776
DynBuf_Append(results, data, Str_Snprintf(data, sizeof data, "%u %" FMTSZ "u ",
1777
fullLength, retLength));
1778
DynBuf_Append(results, DynBuf_Get(&imageData), retLength);
1781
* Guarantee that the results have a trailing \0 in case anything does a strlen...
1783
DynBuf_AppendString(results, "");
1784
*result = (char *)DynBuf_Get(results);
1785
*resultLen = DynBuf_GetSize(results);
1786
DynBuf_Destroy(&imageData);
1793
*----------------------------------------------------------------------------
1795
* UnityTcloShowTaskbar --
1797
* RPC handler for 'unity.show.taskbar'.
1800
* TRUE if everything is successful.
1806
*----------------------------------------------------------------------------
1810
UnityTcloShowTaskbar(char const **result, // OUT
1811
size_t *resultLen, // OUT
1812
const char *name, // IN
1813
const char *args, // IN
1814
size_t argsSize, // IN: Size of args
1815
void *clientData) // ignored
1818
unsigned int index = 0;
1820
Debug("%s: name:%s args:'%s'\n", __FUNCTION__, name, args);
1822
if (!StrUtil_GetNextUintToken(&command, &index, args, " ")) {
1823
Debug("%s: Invalid RPC arguments.\n", __FUNCTION__);
1824
return RpcIn_SetRetVals(result, resultLen,
1825
"Invalid arguments.",
1829
Debug("%s: command %d\n", __FUNCTION__, command);
1831
UnityPlatformShowTaskbar(unity.up, (command == 0) ? FALSE : TRUE);
1833
return RpcIn_SetRetVals(result, resultLen, "", TRUE);
1838
*----------------------------------------------------------------------------
1840
* UnityTcloMoveResizeWindow --
1842
* RPC handler for 'unity.window.move_resize'.
1845
* TRUE if everything is successful.
1847
* If successful adds null terminated strings for each output coordinates.
1852
*----------------------------------------------------------------------------
1856
UnityTcloMoveResizeWindow(char const **result, // OUT
1857
size_t *resultLen, // OUT
1858
const char *name, // IN
1859
const char *args, // IN
1860
size_t argsSize, // IN: Size of args
1861
void *clientData) // ignored
1863
DynBuf *buf = &gTcloUpdate;
1864
UnityWindowId window;
1865
UnityRect moveResizeRect = {0};
1868
Debug("%s: name:%s args:'%s'\n", __FUNCTION__, name, args);
1870
if (sscanf(args, "%u %d %d %d %d",
1874
&moveResizeRect.width,
1875
&moveResizeRect.height) != 5) {
1876
Debug("%s: Invalid RPC arguments.\n", __FUNCTION__);
1877
return RpcIn_SetRetVals(result, resultLen,
1878
"Invalid arguments.",
1882
if (!UnityPlatformMoveResizeWindow(unity.up, window, &moveResizeRect)) {
1883
Debug("%s: Could not read window coordinates.\n", __FUNCTION__);
1884
return RpcIn_SetRetVals(result, resultLen,
1885
"Could not read window coordinates",
1890
* Send back the new (post move/resize operation) window coordinates.
1893
DynBuf_SetSize(buf, 0);
1894
Str_Sprintf(temp, sizeof temp, "%d %d %d %d", moveResizeRect.x,
1895
moveResizeRect.y, moveResizeRect.width, moveResizeRect.height);
1896
DynBuf_AppendString(buf, temp);
1899
* Write the final result into the result out parameters and return!
1902
*result = (char *)DynBuf_Get(buf);
1903
*resultLen = DynBuf_GetSize(buf);
1910
*----------------------------------------------------------------------------
1912
* UnityTcloSetDesktopConfig --
1914
* RPC handler for 'unity.set.desktop.config'. The RPC takes the form of:
1915
* {1,1} {1,2} {2,1} {2,2} 1
1916
* for a 2 x 2 virtual desktop where the upper right {1,2} is the currently
1920
* TRUE if everything is successful.
1924
* Might change virtual desktop configuration in the guest.
1926
*----------------------------------------------------------------------------
1930
UnityTcloSetDesktopConfig(char const **result, // OUT
1931
size_t *resultLen, // OUT
1932
const char *name, // IN
1933
const char *args, // IN
1934
size_t argsSize, // IN
1935
void *clientData) // IN: ignored
1937
unsigned int index = 0;
1938
char *desktopStr = NULL;
1940
uint32 initialDesktopIndex = 0;
1942
Debug("%s: name:%s args:'%s'\n", __FUNCTION__, name, args);
1944
if (argsSize == 0) {
1945
errorMsg = "Invalid arguments: desktop config is expected";
1949
unity.virtDesktopArray.desktopCount = 0;
1950
/* Read the virtual desktop configuration. */
1951
while ((desktopStr = StrUtil_GetNextToken(&index, args, " ")) != NULL) {
1952
UnityVirtualDesktop desktop;
1953
uint32 desktopCount = unity.virtDesktopArray.desktopCount;
1955
if (sscanf(desktopStr, "{%d,%d}", &desktop.x, &desktop.y) == 2) {
1956
if (desktopCount >= MAX_VIRT_DESK - 1) {
1957
errorMsg = "Invalid arguments: too many desktops";
1960
unity.virtDesktopArray.desktops[desktopCount] = desktop;
1961
unity.virtDesktopArray.desktopCount++;
1962
} else if (sscanf(desktopStr, "%u", &initialDesktopIndex) == 1) {
1963
if (initialDesktopIndex >= unity.virtDesktopArray.desktopCount) {
1964
errorMsg = "Invalid arguments: current desktop is out of bounds";
1967
/* All done with arguments at this point - stop processing */
1971
errorMsg = "Invalid arguments: invalid desktop config";
1979
* Call the platform specific function to set the desktop configuration.
1982
if (!UnityPlatformSetDesktopConfig(unity.up, &unity.virtDesktopArray)) {
1983
errorMsg = "Could not set desktop configuration";
1987
if (!UnityPlatformSetInitialDesktop(unity.up, initialDesktopIndex)) {
1988
errorMsg = "Could not set initial desktop";
1992
return RpcIn_SetRetVals(result, resultLen,
1997
unity.virtDesktopArray.desktopCount = 0;
1998
Debug("%s: %s\n", __FUNCTION__, errorMsg);
2000
return RpcIn_SetRetVals(result, resultLen,
2007
*----------------------------------------------------------------------------
2009
* UnityTcloSetDesktopActive --
2011
* RPC handler for 'unity.set.desktop.active'.
2014
* TRUE if everything is successful.
2018
* Might change the active virtual desktop in the guest.
2020
*----------------------------------------------------------------------------
2024
UnityTcloSetDesktopActive(char const **result, // OUT
2025
size_t *resultLen, // OUT
2026
const char *name, // IN
2027
const char *args, // IN
2028
size_t argsSize, // IN
2029
void *clientData) // IN: ignored
2031
UnityDesktopId desktopId = 0;
2034
Debug("%s: name:%s args:'%s'\n", __FUNCTION__, name, args);
2036
if (unity.isEnabled == FALSE) {
2037
errorMsg = "Unity not enabled - cannot change active desktop";
2041
if (sscanf(args, " %d", &desktopId) != 1) {
2042
errorMsg = "Invalid arguments: expected \"desktopId\"";
2046
if (desktopId >= unity.virtDesktopArray.desktopCount) {
2047
errorMsg = "Desktop does not exist in the guest";
2052
* Call the platform specific function to set the desktop active.
2055
if (!UnityPlatformSetDesktopActive(unity.up, desktopId)) {
2056
errorMsg = "Could not set active desktop";
2060
return RpcIn_SetRetVals(result, resultLen,
2064
Debug("%s: %s\n", __FUNCTION__, errorMsg);
2065
return RpcIn_SetRetVals(result, resultLen,
2072
*----------------------------------------------------------------------------
2074
* UnityTcloSetWindowDesktop --
2076
* RPC handler for 'unity.set.window.desktop'.
2079
* TRUE if everything is successful.
2083
* Might change the active virtual desktop in the guest.
2085
*----------------------------------------------------------------------------
2089
UnityTcloSetWindowDesktop(char const **result, // OUT
2090
size_t *resultLen, // OUT
2091
const char *name, // IN
2092
const char *args, // IN
2093
size_t argsSize, // IN
2094
void *clientData) // IN: ignored
2096
UnityWindowId windowId;
2097
uint32 desktopId = 0;
2100
Debug("%s: name:%s args:'%s'\n", __FUNCTION__, name, args);
2102
if (unity.isEnabled == FALSE) {
2103
errorMsg = "Unity not enabled - cannot set window desktop";
2107
if (sscanf(args, " %u %d", &windowId, &desktopId) != 2) {
2108
errorMsg = "Invalid arguments: expected \"windowId desktopId\"";
2112
if (desktopId >= unity.virtDesktopArray.desktopCount) {
2113
errorMsg = "The desktop does not exist in the guest";
2118
* Set the desktop id for this window in the tracker.
2119
* We need to do this before moving the window since on MS Windows platforms
2120
* moving the window will hide it and there's a danger that we may enumerate the
2121
* hidden window before changing it's desktop ID. The Window tracker will ignore
2122
* hidden windows on the current desktop - which ultimately can lead to this window
2123
* being reaped from the tracker.
2125
UnityWindowTracker_ChangeWindowDesktop(&unity.tracker, windowId, desktopId);
2128
* Call the platform specific function to move the window to the
2129
* specified desktop.
2132
if (!UnityPlatformSetWindowDesktop(unity.up, windowId, desktopId)) {
2133
errorMsg = "Could not move the window to the desktop";
2137
return RpcIn_SetRetVals(result, resultLen,
2141
Debug("%s: %s\n", __FUNCTION__, errorMsg);
2142
return RpcIn_SetRetVals(result, resultLen,
2149
*----------------------------------------------------------------------------
2151
* UnityTcloSetUnityOptions --
2153
* Set the Unity options - must be be called before entering Unity mode.
2156
* TRUE if RPC was succesfully handled.
2162
*----------------------------------------------------------------------------
2166
UnityTcloSetUnityOptions(RpcInData *data)
2169
UnityOptions optionsMsg;
2170
int featureIndex = 0;
2171
uint32 featuresChanged;
2173
memset(&optionsMsg, 0, sizeof optionsMsg);
2175
/* Check our arguments. */
2180
if (!(data && data->name && data->args)) {
2181
Debug("%s: Invalid arguments.\n", __FUNCTION__);
2182
ret = RPCIN_SETRETVALS(data, "Invalid arguments.", FALSE);
2186
Debug("%s: Got RPC, name: \"%s\", argument length: %"FMTSZ"u.\n",
2187
__FUNCTION__, data->name, data->argsSize);
2190
* Deserialize the XDR data. Note that the data begins with args + 1 since
2191
* there is a space between the RPC name and the XDR serialization.
2193
if (!XdrUtil_Deserialize((char *)data->args + 1, data->argsSize - 1,
2194
xdr_UnityOptions, &optionsMsg)) {
2195
Debug("%s: Failed to deserialize data\n", __FUNCTION__);
2196
ret = RPCIN_SETRETVALS(data, "Failed to deserialize data.", FALSE);
2201
* For each potential feature bit XOR the current mask with the newly
2202
* specified set, then if the bit has changed call the specific setter
2203
* function with TRUE/FALSE according to the new state of the bit.
2205
featuresChanged = optionsMsg.UnityOptions_u.unityOptionsV1->featureMask ^
2206
unity.currentOptions;
2207
while (unityFeatureTable[featureIndex].featureBit != 0) {
2208
if (featuresChanged & unityFeatureTable[featureIndex].featureBit) {
2209
unityFeatureTable[featureIndex].setter(
2210
(optionsMsg.UnityOptions_u.unityOptionsV1->featureMask &
2211
unityFeatureTable[featureIndex].featureBit) != 0);
2216
unity.currentOptions = optionsMsg.UnityOptions_u.unityOptionsV1->featureMask;
2218
ret = RPCIN_SETRETVALS(data,
2222
VMX_XDR_FREE(xdr_UnityOptions, &optionsMsg);
2229
*----------------------------------------------------------------------------
2231
* UnityTcloRequestWindowContents --
2233
* Request the window contents for a set of windows.
2236
* TRUE if all the window IDs are valid.
2242
*----------------------------------------------------------------------------
2246
UnityTcloRequestWindowContents(RpcInData *data) // IN
2249
UnityWindowContentsRequest requestMsg;
2250
UnityWindowContentsRequestV1 *requestV1 = NULL;
2251
memset(&requestMsg, 0, sizeof requestMsg);
2253
/* Check our arguments. */
2258
if (!(data && data->name && data->args)) {
2259
Debug("%s: Invalid arguments.\n", __FUNCTION__);
2260
ret = RPCIN_SETRETVALS(data, "Invalid arguments.", FALSE);
2264
Debug("%s: Got RPC, name: \"%s\", argument length: %"FMTSZ"u.\n",
2265
__FUNCTION__, data->name, data->argsSize);
2268
* Deserialize the XDR data. Note that the data begins with args + 1 since
2269
* there is a space between the RPC name and the XDR serialization.
2271
if (!XdrUtil_Deserialize((char *)data->args + 1, data->argsSize - 1,
2272
xdr_UnityWindowContentsRequest, &requestMsg)) {
2273
Debug("%s: Failed to deserialize data\n", __FUNCTION__);
2274
ret = RPCIN_SETRETVALS(data, "Failed to deserialize data.", FALSE);
2278
if (requestMsg.ver != UNITY_WINDOW_CONTENTS_V1) {
2279
Debug("%s: Unexpected XDR version = %d\n", __FUNCTION__, requestMsg.ver);
2283
requestV1 = requestMsg.UnityWindowContentsRequest_u.requestV1;
2286
* Call the platform implementation of the RPC handler.
2288
if (!UnityPlatformRequestWindowContents(unity.up,
2289
requestV1->windowID.windowID_val,
2290
requestV1->windowID.windowID_len)) {
2291
ret = RPCIN_SETRETVALS(data, "Invalid list of windows.", FALSE);
2295
ret = RPCIN_SETRETVALS(data,
2299
VMX_XDR_FREE(xdr_UnityWindowContentsRequest, &requestMsg);
2306
*----------------------------------------------------------------------------
2308
* UnityUpdateState --
2310
* Communicate unity state changes to vmx.
2313
* TRUE if everything is successful.
2319
*----------------------------------------------------------------------------
2323
UnityUpdateState(void)
2327
UnityActiveProto message;
2330
if (DynXdr_Create(&xdrs) == NULL) {
2334
val = Str_Asprintf(NULL, "%s ", UNITY_RPC_UNITY_ACTIVE);
2335
if (!val || !DynXdr_AppendRaw(&xdrs, val, strlen(val))) {
2336
Debug("%s: Failed to create state string.\n", __FUNCTION__);
2340
memset(&message, 0, sizeof message);
2341
message.ver = UNITY_ACTIVE_V1;
2342
message.UnityActiveProto_u.unityActive = unity.isEnabled;
2343
if (!xdr_UnityActiveProto(&xdrs, &message)) {
2344
Debug("%s: Failed to append message content.\n", __FUNCTION__);
2349
if (!RpcOut_SendOneRaw(DynXdr_Get(&xdrs), xdr_getpos(&xdrs), NULL, NULL)) {
2350
Debug("%s: Failed to send Unity state RPC.\n", __FUNCTION__);
2353
Debug("%s: success\n", __FUNCTION__);
2357
DynXdr_Destroy(&xdrs, TRUE);
2363
*----------------------------------------------------------------------------
2365
* UnityXdrRequestOperation --
2367
* XDR encoder function for UnityRequestOperation.
2369
* See UnityXdrSendRpc().
2372
* Returns true if the XDR struct was encoded successfully.
2376
*------------------------------------------------------------------------------
2380
UnityXdrRequestOperation(XDR *xdrs, // IN
2385
return xdr_UnityRequestOperation(xdrs, (UnityRequestOperation *) arg);
2390
*------------------------------------------------------------------------------
2392
* UnitySendRequestMinimizeOperation --
2394
* Send a request for a minimize operation to the host.
2397
* TRUE if everything is successful.
2403
*----------------------------------------------------------------------------
2407
UnitySendRequestMinimizeOperation(UnityWindowId windowId, // IN
2408
uint32 sequence) // IN
2411
UnityRequestOperation msg = { 0 };
2412
UnityRequestOperationV1 v1 = { 0 };
2414
Debug("%s: Enter.\n", __FUNCTION__);
2416
v1.windowId = windowId;
2417
v1.sequence = sequence;
2418
v1.details.op = MINIMIZE;
2420
msg.ver = UNITY_OP_V1;
2421
msg.UnityRequestOperation_u.unityRequestOpV1 = &v1;
2423
ret = UnityXdrSendRpc(UNITY_RPC_REQUEST_OPERATION,
2424
&UnityXdrRequestOperation,
2427
Debug("%s: Exit.\n", __FUNCTION__);
2433
*----------------------------------------------------------------------------
2435
* UnitySendWindowContents --
2437
* Sends the content of a window to the host, as a PNG encoded image. If the
2438
* image is larger than the maximum size of a GuestMsg, this function breaks
2439
* the image down into a number of chunks, then transfers each of the chunks
2440
* independently. See guest_msg_def.h and unity.x.
2443
* Returns true if the image was transferred successfully.
2448
*------------------------------------------------------------------------------
2452
UnitySendWindowContents(UnityWindowId windowID, // IN
2453
uint32 imageWidth, // IN
2454
uint32 imageHeight, // IN
2455
const char *imageData, // IN
2456
uint32 imageLength) // IN
2459
uint32 count = 0; /* count of chunks sent */
2460
uint32 len = 0; /* length of the next chunk */
2461
const char *readptr = imageData; /* pointer to start of next chunk in imageData */
2463
ASSERT(imageWidth > 0);
2464
ASSERT(imageHeight > 0);
2465
ASSERT(imageLength > 0);
2468
Debug("%s: Enter.\n", __FUNCTION__);
2469
Debug("%s: Sending contents of window 0x%x.\n", __FUNCTION__, windowID);
2470
Debug("%s: Contents are (%u x %u) image, %u bytes.\n", __FUNCTION__,
2471
imageWidth, imageHeight, imageLength);
2473
/* Send the unity.window.contents.start RPC to the host. */
2474
if (!UnitySendWindowContentsStart(windowID,
2481
/* Send the image data. */
2482
while (imageLength > 0) {
2484
* Get the length of the next chunk to send, up to a maximum of
2485
* UNITY_WINDOW_CONTENTS_MAX_CHUNK_SIZE bytes.
2487
len = MIN(UNITY_WINDOW_CONTENTS_MAX_CHUNK_SIZE, imageLength);
2489
Debug("%s: Sending chunk %u at offset 0x%p, size %u.\n", __FUNCTION__,
2490
count, readptr, len);
2492
/* Send the next chunk to the host. */
2493
if (!UnitySendWindowContentsChunk(windowID, count, readptr, len)) {
2502
/* Send the unity.window.contents.end RPC to the host. */
2503
if (!UnitySendWindowContentsEnd(windowID)) {
2515
*----------------------------------------------------------------------------
2517
* UnitySetAddHiddenWindows --
2519
* Set (or unset) whether hidden windows should be added to the tracker.
2527
*----------------------------------------------------------------------------
2531
UnitySetAddHiddenWindows(Bool enabled)
2534
* Should we add hidden windows to the tracker (the host will use the trackers
2535
* attribute field to display hidden windows in the appropriate manner.)
2538
Debug("%s: Adding hidden windows to tracker\n", __FUNCTION__);
2540
Debug("%s: Do not add hidden windows to tracker\n", __FUNCTION__);
2546
*----------------------------------------------------------------------------
2548
* UnitySetInterlockMinimizeOperation --
2550
* Set (or unset) whether window operations should be denied/delayed and
2551
* relayed to the host for later confirmation.
2559
*----------------------------------------------------------------------------
2563
UnitySetInterlockMinimizeOperation(Bool enabled)
2566
* Should we interlock operations through the host. For example: instead of
2567
* allowing minimize to occur immediately in the guest should we prevent the
2568
* minimize of a window in the guest, then relay the minimize to the host and wait
2569
* for the hosts confirmation before actually minimizing the window in the guest.
2572
Debug("%s: Interlocking minimize operations through the host\n",
2575
Debug("%s: Do not interlock minimize operations through the host\n",
2578
UnityPlatformSetInterlockMinimizeOperation(unity.up, enabled);
2583
*----------------------------------------------------------------------------
2585
* UnitySetSendWindowContents --
2587
* Set (or unset) whether window contents should be sent to the host.
2595
*----------------------------------------------------------------------------
2599
UnitySetSendWindowContents(Bool enabled)
2602
* Is the host prepared to receive scraped window contents at any time - even though
2603
* it may not have previously requested the window contents. Explicit requests from
2604
* the host will always be honored - this flag determines whether the guest will send
2605
* the window contents directly after a qualifying operation (like changes in the
2606
* z-order of a window).
2609
Debug("%s: Sending window contents to the host on appropriate events\n",
2612
Debug("%s: Do not send window contents to the host on appropriate events\n",
2619
*------------------------------------------------------------------------------
2621
* UnityXdrEncodeWindowContentsStart --
2623
* XDR encoder function for UnityWindowContentsStart.
2625
* See UnityXdrSendRpc().
2628
* Returns true if the XDR struct was encoded successfully.
2632
*------------------------------------------------------------------------------
2636
UnityXdrEncodeWindowContentsStart(XDR *xdrs,
2641
return xdr_UnityWindowContentsStart(xdrs, (UnityWindowContentsStart *) arg);
2646
*------------------------------------------------------------------------------
2648
* UnitySendWindowContentsStart --
2650
* Sends the unity.window.contents.start RPC to the host.
2653
* Returns true if the RPC was sent successfully.
2658
*------------------------------------------------------------------------------
2662
UnitySendWindowContentsStart(UnityWindowId windowID, // IN
2663
uint32 imageWidth, // IN
2664
uint32 imageHeight, // IN
2665
uint32 imageLength) // IN
2668
UnityWindowContentsStart msg = { 0 };
2669
UnityWindowContentsStartV1 v1 = { 0 };
2671
Debug("%s: Enter.\n", __FUNCTION__);
2673
v1.windowID = windowID;
2674
v1.imageWidth = imageWidth;
2675
v1.imageHeight = imageHeight;
2676
v1.imageLength = imageLength;
2678
msg.ver = UNITY_WINDOW_CONTENTS_V1;
2679
msg.UnityWindowContentsStart_u.startV1 = &v1;
2681
ret = UnityXdrSendRpc(UNITY_RPC_WINDOW_CONTENTS_START,
2682
&UnityXdrEncodeWindowContentsStart,
2685
Debug("%s: Exit.\n", __FUNCTION__);
2691
*------------------------------------------------------------------------------
2693
* UnityXdrEncodeWindowContentsChunk --
2695
* XDR encoder function for UnityWindowContentsChunk.
2697
* See UnityXdrSendRpc().
2700
* Returns true if the XDR struct was encoded successfully.
2704
*------------------------------------------------------------------------------
2708
UnityXdrEncodeWindowContentsChunk(XDR *xdrs,
2713
return xdr_UnityWindowContentsChunk(xdrs, (UnityWindowContentsChunk *) arg);
2718
*------------------------------------------------------------------------------
2720
* UnitySendWindowContentsChunk --
2722
* Sends a unity.window.contents.chunk RPC to the host.
2725
* Returns true if the RPC was sent successfully.
2730
*------------------------------------------------------------------------------
2734
UnitySendWindowContentsChunk(UnityWindowId windowID,
2740
UnityWindowContentsChunk msg = { 0 };
2741
UnityWindowContentsChunkV1 v1 = { 0 };
2743
Debug("%s: Enter.\n", __FUNCTION__);
2745
v1.windowID = windowID;
2746
v1.chunkID = chunkID;
2747
v1.data.data_val = (char *) data;
2748
v1.data.data_len = len;
2750
msg.ver = UNITY_WINDOW_CONTENTS_V1;
2751
msg.UnityWindowContentsChunk_u.chunkV1 = &v1;
2753
ret = UnityXdrSendRpc(UNITY_RPC_WINDOW_CONTENTS_CHUNK,
2754
&UnityXdrEncodeWindowContentsChunk,
2757
Debug("%s: Exit.\n", __FUNCTION__);
2763
*------------------------------------------------------------------------------
2765
* UnityXdrEncodeWindowContentsEnd --
2767
* XDR encoder function for UnityWindowContentsEnd.
2770
* Returns true if the XDR struct was encoded successfully.
2774
*------------------------------------------------------------------------------
2778
UnityXdrEncodeWindowContentsEnd(XDR *xdrs,
2783
return xdr_UnityWindowContentsEnd(xdrs, (UnityWindowContentsEnd*) arg);
2788
*------------------------------------------------------------------------------
2790
* UnitySendWindowContentsEnd --
2792
* Sends a unity.window.contents.end RPC to the host.
2795
* Returns true if the RPC was sent successfully.
2800
*------------------------------------------------------------------------------
2804
UnitySendWindowContentsEnd(UnityWindowId windowID)
2807
UnityWindowContentsEnd msg = { 0 };
2808
UnityWindowContentsEndV1 v1 = { 0 };
2810
Debug("%s: Enter.\n", __FUNCTION__);
2812
v1.windowID = windowID;
2814
msg.ver = UNITY_WINDOW_CONTENTS_V1;
2815
msg.UnityWindowContentsEnd_u.endV1 = &v1;
2817
ret = UnityXdrSendRpc(UNITY_RPC_WINDOW_CONTENTS_END,
2818
&UnityXdrEncodeWindowContentsEnd,
2821
Debug("%s: Exit.\n", __FUNCTION__);
2827
*------------------------------------------------------------------------------
2829
* UnityXdrSendRpc --
2831
* Sends an RPC with XDR-serialized arguments to the host. The provided
2832
* encodeFn will be called to perform XDR encoding of the RPC, with the XDR
2833
* struct and the provided data pointer as its parameters.
2836
* True if the RPC was sent successfully.
2841
*------------------------------------------------------------------------------
2845
UnityXdrSendRpc(const char *rpcName,
2846
UnityXdrEncodeFunc encodeFn,
2854
Debug("%s: Enter.\n", __FUNCTION__);
2856
if (!DynXdr_Create(&xdrs)) {
2857
Debug("%s: Failed to create DynXdr.\n", __FUNCTION__);
2861
if (!DynXdr_AppendRaw(&xdrs, rpcName, strlen(rpcName))) {
2862
Debug("%s: Failed to append RPC name to DynXdr.\n", __FUNCTION__);
2863
goto dynxdr_destroy;
2866
if (!DynXdr_AppendRaw(&xdrs, " ", 1)) {
2867
Debug("%s: Failed to append space to DynXdr.\n", __FUNCTION__);
2868
goto dynxdr_destroy;
2871
if (!(*encodeFn)(&xdrs, data)) {
2872
Debug("%s: Failed to serialize RPC data.\n", __FUNCTION__);
2873
goto dynxdr_destroy;
2876
if (!RpcOut_SendOneRaw(DynXdr_Get(&xdrs), xdr_getpos(&xdrs), NULL, NULL)) {
2877
Debug("%s: Failed to send RPC.\n", __FUNCTION__);
2878
goto dynxdr_destroy;
2884
DynXdr_Destroy(&xdrs, TRUE);
2887
Debug("%s: Exit.\n", __FUNCTION__);