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 window manager intergration tools service.
25
* unity.get.update [incremental]
27
* The tools service response to requests for windows events via the
28
* "unity.get.update" RPC from the host. Upon receiving the RPC, the
29
* service will crawl the window manager, taking note of the positions,
30
* window regions, etc for every window in the system. The service
31
* will reply describing the current state.
33
* If the intial request included the "incremental" argument, a list
34
* of all the changes to the windowing system since the last
35
* unity.get.update request will be sent (e.g. if a window moves
36
* or has been destroyed).
38
* If the "incremental" argument is not present, the entire state
39
* of the windowing system is returned.
41
* The reply to the RPC is a double null terminated list of null
42
* terminated strings. Each string in the list has one of the following
46
* A window with the specified integer windowid has just been
50
* The window with integer windowid has been removed. Get rid of it.
52
* move <windowid> <x1> <y1> <x2> <y2>
53
* The window with specified integer windowid has moved or resized
54
* such that its top left corner rests at x1,y1 and its bottom right
57
* region <windowid> <numrects>
58
* The window with specified windowid has a not-rectangular window
59
* region (e.g. the curved corner windows in Windows XP). Immediately,
60
* after this messages are numrects messages with the following format:
62
* rect <x1> <y1> <x2> <y2>
63
* Defines a rectangle in the coordinate system of the window
64
* for this region (not the coordinate system of the desktop!!)
66
* The actual window region is the union of all the rectangles in the
67
* list. A value of 0 for numrects indicates that the window region
68
* should be ignored (i.e. the window region is identical to the
69
* bounds of the window).
71
* title <windowid> <title>
72
* A window with the specified integer windowid has just changed its
75
* zorder <num windows> <window id 1> <window id 2> ... <window id n>
76
* Z order of windows from top to bottom(or front to rear)
78
* attr <windowid> <attr> <enabled>
79
* The window with specified windowid has an attribute enabled/disabled.
81
* type <windowid> <type>
82
* The window with specified windowid is of a certain type
84
* icon <windowid> <icontype>
85
* The window with specified windowid has changed an icon of the specified
88
* desktop <windowid> <desktopid>
89
* The window with specified windowid has been moved to a different desktop.
91
* activedesktop <desktopid>
92
* The desktop with specified desktopid has become active.
94
* The guest is also capable of pushing incremental updates to the VMX.
95
* When we enter Unity (upon getting "unity.enter" command from the VMX),
96
* start a separate Unity window update thread. This thread will gather
97
* window updates from the guest, and send them to the VMX (if there are
98
* any updates to be sent).
99
* The incremental updates will be sent using 'tools.unity.push.update' command.
110
#include "unityWindowTracker.h"
111
#include "unityCommon.h"
113
#include "unityPlatform.h"
114
#include "unityDebug.h"
119
* Singleton object for tracking the state of the service.
121
typedef struct UnityState {
122
UnityWindowTracker tracker;
125
UnityVirtualDesktopArray virtDesktopArray; // Virtual desktop configuration
127
UnityPlatform *up; // Platform-specific state
130
static UnityState unity;
137
static void UnityUpdateCallbackFn(void *param, UnityUpdate *update);
138
static Bool UnityTcloGetUpdate(char const **result, size_t *resultLen, const char *name,
139
const char *args, size_t argsSize, void *clientData);
140
static Bool UnityTcloEnter(char const **result, size_t *resultLen, const char *name,
141
const char *args, size_t argsSize, void *clientData);
142
static Bool UnityTcloExit(char const **result, size_t *resultLen, const char *name,
143
const char *args, size_t argsSize, void *clientData);
144
static Bool UnityTcloGetWindowPath(char const **result, size_t *resultLen,
145
const char *name, const char *args,
146
size_t argsSize, void *clientData);
147
static Bool UnityTcloWindowCommand(char const **result,
153
static Bool UnityTcloGetWindowContents(char const **result,
159
static Bool UnityTcloGetIconData(char const **result,
165
static Bool UnityTcloSetDesktopWorkArea(char const **result,
171
static Bool UnityTcloSetTopWindowGroup(char const **result,
177
static Bool UnityTcloShowTaskbar(char const **result,
183
static Bool UnityTcloMoveResizeWindow(char const **result,
189
static Bool UnityTcloSetDesktopConfig(char const **result,
195
static Bool UnityTcloSetDesktopActive(char const **result,
201
static Bool UnityTcloSetWindowDesktop(char const **result,
210
* Dispatch table for Unity window commands. All commands performing actions on
211
* guest unity windows go here.
216
Bool (*exec)(UnityPlatform *up, UnityWindowId window);
219
static UnityCommandElem unityCommandTable[] = {
220
{ UNITY_RPC_WINDOW_RESTORE, UnityPlatformRestoreWindow },
221
{ UNITY_RPC_WINDOW_CLOSE, UnityPlatformCloseWindow },
222
{ UNITY_RPC_WINDOW_SHOW, UnityPlatformShowWindow },
223
{ UNITY_RPC_WINDOW_HIDE, UnityPlatformHideWindow },
224
{ UNITY_RPC_WINDOW_MINIMIZE, UnityPlatformMinimizeWindow },
225
{ UNITY_RPC_WINDOW_MAXIMIZE, UnityPlatformMaximizeWindow },
226
{ UNITY_RPC_WINDOW_UNMAXIMIZE, UnityPlatformUnmaximizeWindow },
227
/* Add more commands and handlers above this. */
234
* "UnityTcloGetUpdate cannot return the contents of a DynBuf. This will leak
235
* the DynBuf's memory, since nobody at a lower level will ever free it. It's
236
* a crappy interface, but we make due by using a static buffer to hold the
239
* We ideally would not use a static buffer because the maximum size of the
240
* update is unknown. To work around this, make the DynBuf returned in
241
* UnityTcloGetUpdate file-global and recycle it across update requests.
244
static DynBuf gTcloUpdate;
248
*----------------------------------------------------------------------------
250
* Unity_IsSupported --
252
* Determine whether this guest supports unity.
255
* TRUE if the guest supports Unity (i.e. if the guest is WinXP) or
256
* if the option to always enable unity was specified in the tools
263
*----------------------------------------------------------------------------
267
Unity_IsSupported(void)
269
return UnityPlatformIsSupported() || unity.forceEnable;
274
*-----------------------------------------------------------------------------
278
* One time initialization stuff.
285
* May register with the tools poll loop.
287
*-----------------------------------------------------------------------------
291
Unity_Init(GuestApp_Dict *conf, // IN
292
int* blockedWnd) // IN
294
Debug("Unity_Init\n");
297
* Initialize the UnityWindowTracker object. The uwt does all the actual work
298
* of computing differences between two states of the windowing system. The
299
* callbacks we register here will fire when we request an update via
300
* UnityWindowTracker_RequestUpdates. See bora/lib/unityWindowTracker for more
303
UnityWindowTracker_Init(&unity.tracker, UnityUpdateCallbackFn);
306
* Initialize the host-specific portion of the unity service.
308
unity.up = UnityPlatformInit(&unity.tracker, blockedWnd);
311
* Init our global dynbuf used to send results back.
313
DynBuf_Init(&gTcloUpdate);
316
* If debugging has been enabled, initialize the debug module. On Windows,
317
* this will pop up a small HUD window which shows an echo of the current
318
* state of the windowing system.
320
if (GuestApp_GetDictEntryBool(conf, "unity.debug")) {
321
UnityDebug_Init(&unity.tracker);
325
* Check if the user specified the option to always enable unity regardless
326
* of the guest OS type.
329
unity.forceEnable = GuestApp_GetDictEntryBool(conf, "unity.forceEnable");
330
unity.isEnabled = FALSE;
332
unity.virtDesktopArray.desktopCount = 0;
337
*-----------------------------------------------------------------------------
341
* Exit Unity and do final cleanup.
349
*-----------------------------------------------------------------------------
357
Debug("%s\n", __FUNCTION__);
366
* Do one-time final platform-specific cleanup.
371
UnityPlatformCleanup(up);
373
UnityWindowTracker_Cleanup(&unity.tracker);
374
DynBuf_Destroy(&gTcloUpdate);
379
*-----------------------------------------------------------------------------
381
* Unity_InitBackdoor --
383
* One time initialization stuff for the backdoor.
392
*-----------------------------------------------------------------------------
396
Unity_InitBackdoor(struct RpcIn *rpcIn) // IN
399
* Only register the callback if the guest is capable of supporting Unity.
400
* This way, if the VMX/UI sends us a Unity request on a non-supported platform
401
* (for whatever reason), we will reply with 'command not supported'.
404
if (Unity_IsSupported()) {
405
RpcIn_RegisterCallback(rpcIn, UNITY_RPC_ENTER, UnityTcloEnter, NULL);
406
RpcIn_RegisterCallback(rpcIn, UNITY_RPC_GET_UPDATE_FULL, UnityTcloGetUpdate, NULL);
407
RpcIn_RegisterCallback(rpcIn, UNITY_RPC_GET_UPDATE_INCREMENTAL,
408
UnityTcloGetUpdate, NULL);
409
RpcIn_RegisterCallback(rpcIn, UNITY_RPC_GET_WINDOW_PATH,
410
UnityTcloGetWindowPath, NULL);
411
RpcIn_RegisterCallback(rpcIn, UNITY_RPC_WINDOW_RESTORE,
412
UnityTcloWindowCommand, NULL);
413
RpcIn_RegisterCallback(rpcIn, UNITY_RPC_WINDOW_SETTOP,
414
UnityTcloSetTopWindowGroup, NULL);
415
RpcIn_RegisterCallback(rpcIn, UNITY_RPC_WINDOW_CLOSE,
416
UnityTcloWindowCommand, NULL);
417
RpcIn_RegisterCallback(rpcIn, UNITY_RPC_GET_WINDOW_CONTENTS,
418
UnityTcloGetWindowContents, NULL);
419
RpcIn_RegisterCallback(rpcIn, UNITY_RPC_GET_ICON_DATA,
420
UnityTcloGetIconData, NULL);
421
RpcIn_RegisterCallback(rpcIn, UNITY_RPC_DESKTOP_WORK_AREA_SET,
422
UnityTcloSetDesktopWorkArea, NULL);
423
RpcIn_RegisterCallback(rpcIn, UNITY_RPC_SHOW_TASKBAR, UnityTcloShowTaskbar, NULL);
424
RpcIn_RegisterCallback(rpcIn, UNITY_RPC_EXIT, UnityTcloExit, NULL);
425
RpcIn_RegisterCallback(rpcIn, UNITY_RPC_WINDOW_MOVE_RESIZE,
426
UnityTcloMoveResizeWindow, NULL);
427
RpcIn_RegisterCallback(rpcIn, UNITY_RPC_WINDOW_SHOW,
428
UnityTcloWindowCommand, NULL);
429
RpcIn_RegisterCallback(rpcIn, UNITY_RPC_WINDOW_HIDE,
430
UnityTcloWindowCommand, NULL);
431
RpcIn_RegisterCallback(rpcIn, UNITY_RPC_WINDOW_MINIMIZE,
432
UnityTcloWindowCommand, NULL);
433
RpcIn_RegisterCallback(rpcIn, UNITY_RPC_WINDOW_MAXIMIZE,
434
UnityTcloWindowCommand, NULL);
435
RpcIn_RegisterCallback(rpcIn, UNITY_RPC_WINDOW_UNMAXIMIZE,
436
UnityTcloWindowCommand, NULL);
437
RpcIn_RegisterCallback(rpcIn, UNITY_RPC_DESKTOP_CONFIG_SET,
438
UnityTcloSetDesktopConfig, NULL);
439
RpcIn_RegisterCallback(rpcIn, UNITY_RPC_DESKTOP_ACTIVE_SET,
440
UnityTcloSetDesktopActive, NULL);
441
RpcIn_RegisterCallback(rpcIn, UNITY_RPC_WINDOW_DESKTOP_SET,
442
UnityTcloSetWindowDesktop, NULL);
448
*-----------------------------------------------------------------------------
450
* Unity_SetActiveDnDDetWnd --
452
* Right now we have 2 Unity DnD full screen detection window, one for version
453
* 2 or older, another for version 3 or newer. This function is to set active
454
* one according to host DnD version.
456
* XXX Both full-screent window is still bottom-most and is showed all time
457
* during Unity mode. Another change is needed to change it to only show the
458
* window during guest->host DnD. Also the window should not be bottom-most,
459
* but dynamicly change z-order during DnD.
467
*-----------------------------------------------------------------------------
471
Unity_SetActiveDnDDetWnd(UnityDnD *state)
473
UnityPlatformSetActiveDnDDetWnd(unity.up, state);
478
*-----------------------------------------------------------------------------
482
* Called everytime we exit Unity. This function can be called when we are
483
* not in Unity mode. Right now it is called every time a 'reset' TCLO command
484
* is sent to the guest. Therefore, there's no guarantee that we were in the
485
* Unity mode when this function is called.
487
* Try to do the following:
488
* Restore system settings if needed.
489
* Kills all unity helper threads if any are running.
490
* Hide the unity dnd detection window.
496
* Restores system settings since we are exiting Unity.
497
* Kills all unity helper threads if any.
498
* Hides the unity dnd detection window if needed.
500
*-----------------------------------------------------------------------------
506
if (unity.isEnabled) {
507
/* Hide full-screen detection window for Unity DnD. */
508
UnityPlatformUpdateDnDDetWnd(unity.up, FALSE);
510
/* Kill Unity helper threads. */
511
UnityPlatformKillHelperThreads(unity.up);
513
/* Restore previously saved user settings. */
514
UnityPlatformRestoreSystemSettings(unity.up);
516
unity.isEnabled = FALSE;
522
*-----------------------------------------------------------------------------
524
* Unity_RegisterCaps --
526
* Called by the application (VMwareUser) to allow the unity subsystem to
527
* register its capabilities.
535
*-----------------------------------------------------------------------------
539
Unity_RegisterCaps(void)
542
* Send Unity capability.
545
if (!RpcOut_sendOne(NULL, NULL, UNITY_RPC_UNITY_CAP" %d",
546
Unity_IsSupported() ? 1 : 0)) {
547
Debug("%s: could not set unity capability\n", __FUNCTION__);
551
* Register guest platform specific capabilities.
554
UnityPlatformRegisterCaps(unity.up);
559
*-----------------------------------------------------------------------------
561
* Unity_UnregisterCaps --
563
* Called by the application (VMwareUser) to allow the unity subsystem to
564
* unregister its capabilities.
572
*-----------------------------------------------------------------------------
576
Unity_UnregisterCaps(void)
579
* Unregister guest platform specific capabilities.
582
UnityPlatformUnregisterCaps(unity.up);
585
* Unregister the unity capability.
588
if (!RpcOut_sendOne(NULL, NULL, UNITY_RPC_UNITY_CAP" 0")) {
589
Debug("Failed to unregister Unity capability\n");
595
*----------------------------------------------------------------------------
599
* RPC handler for 'unity.enter'. Save and disable certain user
600
* settings. Start Unity updates thread and any other platform
601
* specific threads (like a thread that listens for
602
* the desktop switch event on Windows). Note that we first set
603
* the UI settings, and then start the threads. This way the UI
604
* settings take effect before we start sending Unity updates,
605
* so that we never send things like task bar (see bug 166085).
608
* TRUE if helper threads were started.
612
* Certain UI system settings will be disabled.
613
* Unity update thread will be started.
614
* Any other platform specific helper threads will be started as well.
616
*----------------------------------------------------------------------------
620
UnityTcloEnter(char const **result, // OUT
621
size_t *resultLen, // OUT
622
const char *name, // IN
623
const char *args, // IN
624
size_t argsSize, // ignored
625
void *clientData) // ignored
627
Debug("%s\n", __FUNCTION__);
629
if (!unity.isEnabled) {
630
/* Save and disable certain user settings here. */
631
UnityPlatformSaveSystemSettings(unity.up);
633
/* Start Unity helper threads. */
634
if (!UnityPlatformStartHelperThreads(unity.up)) {
637
* If we couldn't start one or more helper threads,
638
* we cannot enter Unity. Kill all running helper
639
* threads and restore ui settings.
642
UnityPlatformKillHelperThreads(unity.up);
643
UnityPlatformRestoreSystemSettings(unity.up);
644
return RpcIn_SetRetVals(result, resultLen,
645
"Could not start unity helper threads", FALSE);
649
* Show full-screen detection window for Unity DnD. It is a bottom-most (but
650
* still in front of desktop) transparent detection window for guest->host DnD
651
* as drop target. We need this window because:
652
* 1. All active windows except desktop will be shown on host desktop and can
653
* accept DnD signal. This full-screen detection window will block any DnD signal
654
* (even mouse signal) to the desktop, which will fix bug 164880.
655
* 2. With this full-screen but bottommost detection window, every time when user
656
* drag something out from active window, the dragEnter will always be immediately
657
* catched for Unity DnD.
659
UnityPlatformUpdateDnDDetWnd(unity.up, TRUE);
660
unity.isEnabled = TRUE;
663
return RpcIn_SetRetVals(result, resultLen, "", TRUE);
668
*----------------------------------------------------------------------------
672
* RPC handler for 'unity.exit'.
678
* Same as side effects of Unity_Exit().
680
*----------------------------------------------------------------------------
684
UnityTcloExit(char const **result, // OUT
685
size_t *resultLen, // OUT
686
const char *name, // IN
687
const char *args, // IN
688
size_t argsSize, // ignored
689
void *clientData) // ignored
691
Debug("UnityTcloExit.\n");
695
return RpcIn_SetRetVals(result, resultLen, "", TRUE);
700
*----------------------------------------------------------------------------
702
* UnityTcloGetWindowInfo --
704
* RPC handler for 'unity.get.window.info'. Get required window info
705
* and send it back to the VMX.
708
* TRUE if everything is successful.
714
*----------------------------------------------------------------------------
718
UnityTcloGetWindowPath(char const **result, // OUT
719
size_t *resultLen, // OUT
720
const char *name, // IN
721
const char *args, // IN
722
size_t argsSize, // ignored
723
void *clientData) // ignored
726
UnityWindowId window;
727
DynBuf *buf = &gTcloUpdate;
728
unsigned int index = 0;
731
Debug("UnityTcloGetWindowPath name:%s args:'%s'\n", name, args);
733
/* Parse the command & window id.*/
735
if (!StrUtil_GetNextIntToken(&window, &index, args, " ")) {
736
Debug("UnityTcloGetWindowInfo: Invalid RPC arguments.\n");
737
return RpcIn_SetRetVals(result, resultLen,
738
"Invalid arguments. Expected \"windowId\"",
742
Debug("UnityTcloGetWindowInfo: window %d\n", window);
745
* Please note that the UnityPlatformGetWindowPath implementations assume that the
746
* dynbuf passed in does not contain any existing data that needs to be appended to,
747
* so this code should continue to accomodate that assumption.
749
DynBuf_SetSize(buf, 0);
750
if (!UnityPlatformGetWindowPath(unity.up, window, buf)) {
751
Debug("UnityTcloGetWindowInfo: Could not get window path.\n");
752
return RpcIn_SetRetVals(result, resultLen,
753
"Could not get window path",
758
* Write the final result into the result out parameters and return!
760
*result = (char *)DynBuf_Get(buf);
761
*resultLen = DynBuf_GetSize(buf);
768
*----------------------------------------------------------------------------
770
* UnityTcloWindowCommand --
772
* RPC handler for 'unity.window.*' (excluding 'unity.window.settop')
775
* TRUE if everything is successful.
781
*----------------------------------------------------------------------------
785
UnityTcloWindowCommand(char const **result, // OUT
786
size_t *resultLen, // OUT
787
const char *name, // IN
788
const char *args, // IN
789
size_t argsSize, // ignored
790
void *clientData) // ignored
792
UnityWindowId window;
793
unsigned int index = 0;
796
Debug("UnityTcloWindowCommand: name:%s args:'%s'\n", name, args);
798
/* Parse the command & window id.*/
800
if (!StrUtil_GetNextIntToken(&window, &index, args, " ")) {
801
Debug("UnityTcloWindowCommand: Invalid RPC arguments.\n");
802
return RpcIn_SetRetVals(result, resultLen,
803
"Invalid arguments. Expected \"windowId\"",
808
Debug("UnityTcloWindowCommand: %s window %d\n", name, window);
810
for (i = 0; unityCommandTable[i].name != NULL; i++) {
811
if (strcmp(unityCommandTable[i].name, name) == 0) {
812
if (!unityCommandTable[i].exec(unity.up, window)) {
813
Debug("Unity window command failed.\n");
814
return RpcIn_SetRetVals(result, resultLen,
815
"Could not execute window command",
818
return RpcIn_SetRetVals(result, resultLen, "", TRUE);
823
return RpcIn_SetRetVals(result, resultLen, "Bad command", FALSE);
828
*----------------------------------------------------------------------------
830
* UnityTcloSetDesktopWorkArea --
832
* RPC handler for 'unity.desktop.work_area.set'.
835
* TRUE if everything is successful.
841
*----------------------------------------------------------------------------
845
UnityTcloSetDesktopWorkArea(char const **result, // IN
846
size_t *resultLen, // IN
847
const char *name, // IN
848
const char *args, // IN
849
size_t argsSize, // IN
850
void *clientData) // IN
852
Bool success = FALSE;
855
UnityRect *workAreas = NULL;
858
* The argument string will look something like:
859
* <count> [ , <x> <y> <w> <h> ] * count.
862
* 3 , 0 0 640 480 , 640 0 800 600 , 0 480 640 480
865
if (sscanf(args, "%u", &count) != 1) {
866
return RpcIn_SetRetVals(result, resultLen,
867
"Invalid arguments. Expected \"count\"",
871
workAreas = (UnityRect *)malloc(sizeof *workAreas * count);
873
RpcIn_SetRetVals(result, resultLen,
874
"Failed to alloc buffer for work areas",
879
for (i = 0; i < count; i++) {
880
args = strchr(args, ',');
882
RpcIn_SetRetVals(result, resultLen,
883
"Expected comma separated display list",
887
args++; /* Skip past the , */
889
if (sscanf(args, " %d %d %d %d ",
890
&workAreas[i].x, &workAreas[i].y,
891
&workAreas[i].width, &workAreas[i].height) != 4) {
892
RpcIn_SetRetVals(result, resultLen,
893
"Expected x, y, w, h in display entry",
899
if (!UnityPlatformSetDesktopWorkAreas(unity.up, workAreas, count)) {
900
RpcIn_SetRetVals(result, resultLen,
901
"UnityPlatformSetDesktopWorkAreas failed",
906
success = RpcIn_SetRetVals(result, resultLen, "", TRUE);
915
*----------------------------------------------------------------------------
917
* UnityTcloSetTopWindowGroup --
919
* RPC handler for 'unity.window.settop'.
922
* TRUE if everything is successful.
928
*----------------------------------------------------------------------------
932
UnityTcloSetTopWindowGroup(char const **result, // OUT
933
size_t *resultLen, // OUT
934
const char *name, // IN
935
const char *args, // IN
936
size_t argsSize, // ignored
937
void *clientData) // ignored
939
UnityWindowId window;
940
unsigned int index = 0;
941
unsigned int windowCount = 0;
942
UnityWindowId windows[UNITY_MAX_SETTOP_WINDOW_COUNT];
944
Debug("%s: name:%s args:'%s'\n", __FUNCTION__, name, args);
946
/* Parse the command & window ids.*/
948
while (StrUtil_GetNextUintToken(&window, &index, args, " ")) {
949
windows[windowCount] = window;
951
if (windowCount == UNITY_MAX_SETTOP_WINDOW_COUNT) {
952
Debug("%s: Too many windows.\n", __FUNCTION__);
953
return RpcIn_SetRetVals(result, resultLen,
954
"Invalid arguments. Too many windows",
959
if (windowCount == 0) {
960
Debug("%s: Invalid RPC arguments.\n", __FUNCTION__);
961
return RpcIn_SetRetVals(result, resultLen,
962
"Invalid arguments. Expected at least one windowId",
966
if (!UnityPlatformSetTopWindowGroup(unity.up, windows, windowCount)) {
967
return RpcIn_SetRetVals(result, resultLen,
968
"Could not execute window command",
972
return RpcIn_SetRetVals(result, resultLen, "", TRUE);
977
*----------------------------------------------------------------------------
979
* UnityTcloGetUpdate --
981
* RPC handler for 'unity.get.update'. Ask the unity window tracker
982
* to give us an update (either incremental or non-incremental based
983
* on whether the 'incremental' arg is present) and send the result
992
*----------------------------------------------------------------------------
996
UnityTcloGetUpdate(char const **result, // OUT
997
size_t *resultLen, // OUT
998
const char *name, // IN
999
const char *args, // IN
1000
size_t argsSize, // ignored
1001
void *clientData) // ignored
1003
DynBuf *buf = &gTcloUpdate;
1006
// Debug("UnityTcloGetUpdate name:%s args:'%s'", name, args);
1009
* Specify incremental or non-incremetal updates based on whether or
1010
* not the client set the "incremental" arg.
1012
if (strstr(name, "incremental")) {
1013
flags |= UNITY_UPDATE_INCREMENTAL;
1016
DynBuf_SetSize(buf, 0);
1018
UnityGetUpdateCommon(flags, buf);
1021
* Write the final result into the result out parameters.
1023
*result = (char *)DynBuf_Get(buf);
1024
*resultLen = DynBuf_GetSize(buf);
1027
* Give the debugger a crack to do something interesting at this point
1029
UnityDebug_OnUpdate();
1036
*----------------------------------------------------------------------------
1038
* UnityGetUpdateCommon --
1040
* Get the unity window update and append it to the specified output buffer.
1041
* This function can be called from two different threads: either from
1042
* the main thread that is trying to execute a TCLO command (unity.get.update)
1043
* or from the unity update thread that is gathering periodic updates and
1044
* pushes them to the VMX as needed (by calling 'tools.unity.push.update RPC).
1045
* Since this function can be called from two different threads, protect
1046
* the global unity singleton with locks.
1054
*----------------------------------------------------------------------------
1058
UnityGetUpdateCommon(int flags, // IN: unity update flags
1059
DynBuf *buf) // IN/OUT: unity update buffer
1064
UnityPlatformLock(unity.up);
1067
* Ask the guest to crawl the windowing system and push updates
1068
* into the unity window tracker. If the guest backend isn't able to get
1069
* notification of destroyed windows, UnityPlatformUpdateWindowState will
1070
* return TRUE, which is are signal to set the UNITY_UPDATE_REMOVE_UNTOUCHED
1071
* flag. This make the unity window tracker generate remove events for
1072
* windows that it hasn't seen an update for since the last update
1075
if (UnityPlatformUpdateWindowState(unity.up, &unity.tracker)) {
1076
flags |= UNITY_UPDATE_REMOVE_UNTOUCHED;
1080
* Generate the update string. We'll accumulate updates in the DynBuf
1081
* buf via the callbacks registered in Unity_Init(). Each update will
1082
* append a null terminated string to buf.
1084
UnityWindowTracker_RequestUpdates(&unity.tracker, flags, buf);
1086
UnityPlatformUnlock(unity.up);
1089
* Write the final '\0' to the DynBuf to signal that we're all out of
1092
DynBuf_AppendString(buf, "");
1099
*----------------------------------------------------------------------------
1101
* UnityUpdateCallbackFn --
1103
* Callback from the unity window tracker indicating something's
1106
* Write the update string into our dynbuf accumlating the update
1115
*----------------------------------------------------------------------------
1119
UnityUpdateCallbackFn(void *param, // IN: dynbuf
1120
UnityUpdate *update) // IN
1122
DynBuf *buf = (DynBuf *)param;
1124
int i, n, count = 0;
1126
char *titleUtf8 = NULL;
1128
switch (update->type) {
1130
case UNITY_UPDATE_ADD_WINDOW:
1131
Str_Sprintf(data, sizeof data, "add %u", update->u.addWindow.id);
1132
DynBuf_AppendString(buf, data);
1135
case UNITY_UPDATE_MOVE_WINDOW:
1136
Str_Sprintf(data, sizeof data, "move %u %d %d %d %d",
1137
update->u.moveWindow.id,
1138
update->u.moveWindow.rect.x1,
1139
update->u.moveWindow.rect.y1,
1140
update->u.moveWindow.rect.x2,
1141
update->u.moveWindow.rect.y2);
1142
DynBuf_AppendString(buf, data);
1145
case UNITY_UPDATE_REMOVE_WINDOW:
1146
Str_Sprintf(data, sizeof data, "remove %u", update->u.removeWindow.id);
1147
DynBuf_AppendString(buf, data);
1150
case UNITY_UPDATE_CHANGE_WINDOW_REGION:
1152
* A null region indicates that the region should be deleted.
1153
* Make sure we write "region <id> 0" for the reply.
1155
region = update->u.changeWindowRegion.region;
1157
count = REGION_NUM_RECTS(region);
1159
Str_Sprintf(data, sizeof data, "region %u %d",
1160
update->u.changeWindowRegion.id, count);
1161
DynBuf_AppendString(buf, data);
1163
for (i = 0; i < count; i++) {
1164
BoxPtr p = REGION_RECTS(region) + i;
1165
Str_Sprintf(data, sizeof data, "rect %d %d %d %d",
1166
p->x1, p->y1, p->x2, p->y2);
1167
DynBuf_AppendString(buf, data);
1171
case UNITY_UPDATE_CHANGE_WINDOW_TITLE:
1172
titleUtf8 = DynBuf_Get(&update->u.changeWindowTitle.titleUtf8);
1175
(DynBuf_GetSize(&update->u.changeWindowTitle.titleUtf8) ==
1176
strlen(titleUtf8) + 1)) {
1177
Str_Sprintf(data, sizeof data, "title %u %s",
1178
update->u.changeWindowTitle.id,
1179
(const char*)titleUtf8);
1181
Str_Sprintf(data, sizeof data, "title %u",
1182
update->u.changeWindowTitle.id);
1184
DynBuf_AppendString(buf, data);
1187
case UNITY_UPDATE_CHANGE_ZORDER:
1188
n = Str_Snprintf(data, sizeof data, "zorder %d", update->u.zorder.count);
1189
DynBuf_Append(buf, data, n);
1190
for (i = 0; i < update->u.zorder.count; i++) {
1191
n = Str_Snprintf(data, sizeof data, " %d", update->u.zorder.ids[i]);
1192
DynBuf_Append(buf, data, n);
1194
DynBuf_AppendString(buf, ""); // for appending NULL
1197
case UNITY_UPDATE_CHANGE_WINDOW_STATE:
1198
Str_Sprintf(data, sizeof data, "state %u %u",
1199
update->u.changeWindowState.id,
1200
update->u.changeWindowState.state);
1201
DynBuf_AppendString(buf, data);
1204
case UNITY_UPDATE_CHANGE_WINDOW_ATTRIBUTE:
1205
Str_Sprintf(data, sizeof data, "attr %u %u %u",
1206
update->u.changeWindowAttribute.id,
1207
update->u.changeWindowAttribute.attr,
1208
update->u.changeWindowAttribute.value);
1209
DynBuf_AppendString(buf, data);
1212
case UNITY_UPDATE_CHANGE_WINDOW_TYPE:
1213
Str_Sprintf(data, sizeof data, "type %u %d",
1214
update->u.changeWindowType.id,
1215
update->u.changeWindowType.winType);
1216
DynBuf_AppendString(buf, data);
1219
case UNITY_UPDATE_CHANGE_WINDOW_ICON:
1220
Str_Sprintf(data, sizeof data, "icon %u %u",
1221
update->u.changeWindowIcon.id,
1222
update->u.changeWindowIcon.iconType);
1223
DynBuf_AppendString(buf, data);
1226
case UNITY_UPDATE_CHANGE_WINDOW_DESKTOP:
1227
Str_Sprintf(data, sizeof data, "desktop %u %d",
1228
update->u.changeWindowDesktop.id,
1229
update->u.changeWindowDesktop.desktopId);
1230
DynBuf_AppendString(buf, data);
1233
case UNITY_UPDATE_CHANGE_ACTIVE_DESKTOP:
1234
Str_Sprintf(data, sizeof data, "activedesktop %d",
1235
update->u.changeActiveDesktop.desktopId);
1236
DynBuf_AppendString(buf, data);
1246
*-----------------------------------------------------------------------------
1248
* UnityUpdateThreadInit --
1250
* Initialize the state for the update thread.
1253
* TRUE if all needed data was initialized.
1257
* RpcOut channel might be open.
1258
* Memory for the update buffer might be allocated.
1260
*-----------------------------------------------------------------------------
1264
UnityUpdateThreadInit(UnityUpdateThreadData *updateData) // IN
1268
updateData->flags = UNITY_UPDATE_INCREMENTAL;
1269
updateData->rpcOut = NULL;
1270
updateData->cmdSize = 0;
1272
DynBuf_Init(&updateData->updates);
1273
DynBuf_AppendString(&updateData->updates, UNITY_RPC_PUSH_UPDATE_CMD " ");
1275
/* Exclude the null. */
1276
updateData->cmdSize = DynBuf_GetSize(&updateData->updates) - 1;
1278
updateData->rpcOut = RpcOut_Construct();
1279
if (updateData->rpcOut == NULL) {
1283
if (!RpcOut_start(updateData->rpcOut)) {
1284
RpcOut_Destruct(updateData->rpcOut);
1291
DynBuf_Destroy(&updateData->updates);
1298
*-----------------------------------------------------------------------------
1300
* UnityUpdateThreadCleanup --
1302
* Cleanup the unity update thread state.
1308
* RpcOut channel will be closed.
1309
* Memory will be freed.
1311
*-----------------------------------------------------------------------------
1315
UnityUpdateThreadCleanup(UnityUpdateThreadData *updateData) // IN
1319
if (updateData->rpcOut) {
1320
RpcOut_stop(updateData->rpcOut);
1321
RpcOut_Destruct(updateData->rpcOut);
1322
updateData->rpcOut = NULL;
1324
DynBuf_Destroy(&updateData->updates); // Avoid double-free by guarding this as well
1330
*-----------------------------------------------------------------------------
1332
* UnitySendUpdates --
1334
* Gather and send a round of unity updates. The caller is responsible
1335
* for gathering updates into updateData->updates buffer prior to the
1336
* function call. This function should only be called if there's data
1337
* in the update buffer to avoid sending empty update string to the VMX.
1340
* TRUE if the update was sent,
1341
* FALSE if something went wrong (an invalid RPC channel, for example).
1346
*-----------------------------------------------------------------------------
1350
UnitySendUpdates(UnityUpdateThreadData *updateData) // IN
1352
char const *myReply;
1357
ASSERT(updateData->rpcOut);
1359
/* Send 'tools.unity.push.update <updates>' to the VMX. */
1362
if (!RpcOut_send(updateData->rpcOut,
1363
(char *)DynBuf_Get(&updateData->updates),
1364
DynBuf_GetSize(&updateData->updates),
1365
&myReply, &myRepLen)) {
1368
* We could not send the RPC. If we haven't tried to reopen
1369
* the channel, try to reopen and resend. If we already
1370
* tried to resend, then it's time to give up. I hope that
1371
* trying to resend once is enough.
1376
Debug("%s: could not send rpc. Reopening channel.\n", __FUNCTION__);
1377
RpcOut_stop(updateData->rpcOut);
1378
if (!RpcOut_start(updateData->rpcOut)) {
1379
Debug("%s: could not reopen rpc channel. Exiting...\n", __FUNCTION__);
1385
Debug("%s: could not resend rpc. Giving up and exiting...\n", __FUNCTION__);
1395
*----------------------------------------------------------------------------
1397
* UnityTcloGetWindowContents --
1399
* RPC handler for 'unity.get.window.contents'. Suck the bits off the
1400
* window and return a .png image over the backdoor.
1403
* TRUE if everything is successful.
1409
*----------------------------------------------------------------------------
1413
UnityTcloGetWindowContents(char const **result, // OUT
1414
size_t *resultLen, // OUT
1415
const char *name, // IN
1416
const char *args, // IN
1417
size_t argsSize, // ignored
1418
void *clientData) // ignored
1420
unsigned int window;
1421
unsigned int index = 0;
1422
DynBuf *imageData = &gTcloUpdate;
1424
Debug("UnityTcloGetWindowContents: name:%s args:'%s'\n", name, args);
1427
* Parse the command & window id.
1429
if (!StrUtil_GetNextIntToken(&window, &index, args, " ")) {
1430
Debug("UnityTcloGetWindowContents: Invalid RPC arguments.\n");
1431
return RpcIn_SetRetVals(result, resultLen,
1432
"failed: arguments. Expected \"windowId\"",
1436
Debug("UnityTcloGetWindowContents: window %d\n", window);
1439
* Read the contents of the window, compress it as a .png and
1440
* send the .png back to the vmx as the RPC result.
1442
DynBuf_SetSize(imageData, 0);
1443
if (!UnityPlatformGetWindowContents(unity.up, window, imageData)) {
1444
return RpcIn_SetRetVals(result, resultLen,
1445
"failed: Could not read window contents",
1449
*result = (char *)DynBuf_Get(imageData);
1450
*resultLen = DynBuf_GetSize(imageData);
1457
*----------------------------------------------------------------------------
1459
* UnityTcloGetIconData --
1461
* RPC handler for 'unity.get.icon.data'. Suck the bits off the
1462
* window and return a .png image over the backdoor.
1465
* TRUE if everything is successful.
1471
*----------------------------------------------------------------------------
1475
UnityTcloGetIconData(char const **result, // OUT
1476
size_t *resultLen, // OUT
1477
const char *name, // IN
1478
const char *args, // IN
1479
size_t argsSize, // ignored
1480
void *clientData) // ignored
1482
UnityWindowId window;
1483
UnityIconType iconType;
1484
UnityIconSize iconSize;
1485
unsigned int dataOffset, dataLength;
1488
DynBuf *results = &gTcloUpdate, imageData;
1491
Debug("UnityTcloGetIconData: name:%s args:'%s'\n", name, args);
1494
* Parse the arguments.
1496
if ((sscanf(args, "%u %u %u %u %u",
1502
|| (dataLength > UNITY_MAX_ICON_DATA_CHUNK)) {
1503
Debug("UnityTcloGetIconData: Invalid RPC arguments.\n");
1504
return RpcIn_SetRetVals(result, resultLen,
1505
"failed: arguments missing",
1509
Debug("%s: window %u iconType %u" \
1510
" iconSize %u dataOffset %u dataLength %u\n",
1512
window, iconType, iconSize, dataOffset, dataLength);
1515
* Retrieve part/all of the icon in PNG format.
1517
DynBuf_Init(&imageData);
1518
if (!UnityPlatformGetIconData(unity.up, window, iconType, iconSize,
1519
dataOffset, dataLength, &imageData, &fullLength)) {
1520
return RpcIn_SetRetVals(result, resultLen,
1521
"failed: Could not read icon data properly",
1526
DynBuf_SetSize(results, 0);
1527
retLength = DynBuf_GetSize(&imageData);
1528
retLength = MIN(retLength, UNITY_MAX_ICON_DATA_CHUNK);
1529
DynBuf_Append(results, data, Str_Snprintf(data, sizeof data, "%u %" FMTSZ "u ",
1530
fullLength, retLength));
1531
DynBuf_Append(results, DynBuf_Get(&imageData), retLength);
1534
* Guarantee that the results have a trailing \0 in case anything does a strlen...
1536
DynBuf_AppendString(results, "");
1537
*result = (char *)DynBuf_Get(results);
1538
*resultLen = DynBuf_GetSize(results);
1539
DynBuf_Destroy(&imageData);
1546
*----------------------------------------------------------------------------
1548
* UnityTcloShowTaskbar --
1550
* RPC handler for 'unity.show.taskbar'.
1553
* TRUE if everything is successful.
1559
*----------------------------------------------------------------------------
1563
UnityTcloShowTaskbar(char const **result, // OUT
1564
size_t *resultLen, // OUT
1565
const char *name, // IN
1566
const char *args, // IN
1567
size_t argsSize, // IN: Size of args
1568
void *clientData) // ignored
1571
unsigned int index = 0;
1573
Debug("%s: name:%s args:'%s'\n", __FUNCTION__, name, args);
1575
if (!StrUtil_GetNextUintToken(&command, &index, args, " ")) {
1576
Debug("%s: Invalid RPC arguments.\n", __FUNCTION__);
1577
return RpcIn_SetRetVals(result, resultLen,
1578
"Invalid arguments.",
1582
Debug("%s: command %d\n", __FUNCTION__, command);
1584
UnityPlatformShowTaskbar(unity.up, (command == 0) ? FALSE : TRUE);
1586
return RpcIn_SetRetVals(result, resultLen, "", TRUE);
1591
*----------------------------------------------------------------------------
1593
* UnityTcloMoveResizeWindow --
1595
* RPC handler for 'unity.window.move_resize'.
1598
* TRUE if everything is successful.
1600
* If successful adds null terminated strings for each output coordinates.
1605
*----------------------------------------------------------------------------
1609
UnityTcloMoveResizeWindow(char const **result, // OUT
1610
size_t *resultLen, // OUT
1611
const char *name, // IN
1612
const char *args, // IN
1613
size_t argsSize, // IN: Size of args
1614
void *clientData) // ignored
1616
DynBuf *buf = &gTcloUpdate;
1617
UnityWindowId window;
1618
UnityRect moveResizeRect = {0};
1621
Debug("%s: name:%s args:'%s'\n", __FUNCTION__, name, args);
1623
if (sscanf(args, "%u %d %d %d %d",
1627
&moveResizeRect.width,
1628
&moveResizeRect.height) != 5) {
1629
Debug("%s: Invalid RPC arguments.\n", __FUNCTION__);
1630
return RpcIn_SetRetVals(result, resultLen,
1631
"Invalid arguments.",
1635
if (!UnityPlatformMoveResizeWindow(unity.up, window, &moveResizeRect)) {
1636
Debug("%s: Could not read window coordinates.\n", __FUNCTION__);
1637
return RpcIn_SetRetVals(result, resultLen,
1638
"Could not read window coordinates",
1643
* Send back the new (post move/resize operation) window coordinates.
1646
DynBuf_SetSize(buf, 0);
1647
Str_Sprintf(temp, sizeof temp, "%d %d %d %d", moveResizeRect.x,
1648
moveResizeRect.y, moveResizeRect.width, moveResizeRect.height);
1649
DynBuf_AppendString(buf, temp);
1652
* Write the final result into the result out parameters and return!
1655
*result = (char *)DynBuf_Get(buf);
1656
*resultLen = DynBuf_GetSize(buf);
1663
*----------------------------------------------------------------------------
1665
* UnityTcloSetDesktopConfig --
1667
* RPC handler for 'unity.set.desktop.config'.
1670
* TRUE if everything is successful.
1674
* Might change virtual desktop configuration in the guest.
1676
*----------------------------------------------------------------------------
1680
UnityTcloSetDesktopConfig(char const **result, // OUT
1681
size_t *resultLen, // OUT
1682
const char *name, // IN
1683
const char *args, // IN
1684
size_t argsSize, // IN
1685
void *clientData) // IN: ignored
1687
unsigned int index = 0;
1688
char *desktopStr = NULL;
1691
Debug("%s: name:%s args:'%s'\n", __FUNCTION__, name, args);
1693
if (argsSize == 0) {
1694
errorMsg = "Invalid arguments: desktop config is expected";
1698
unity.virtDesktopArray.desktopCount = 0;
1699
/* Read the virtual desktop configuration. */
1700
while ((desktopStr = StrUtil_GetNextToken(&index, args, " ")) != NULL) {
1701
UnityVirtualDesktop desktop;
1702
uint32 desktopCount = unity.virtDesktopArray.desktopCount;
1704
int res = sscanf(desktopStr, "{%d,%d}", &desktop.x, &desktop.y);
1707
if (desktopCount >= MAX_VIRT_DESK - 1) {
1708
errorMsg = "Invalid arguments: too many desktops";
1711
unity.virtDesktopArray.desktops[desktopCount] = desktop;
1712
unity.virtDesktopArray.desktopCount++;
1714
errorMsg = "Invalid arguments: invalid desktop config";
1720
* Call the platform specific function to set the desktop configuration.
1723
if (!UnityPlatformSetDesktopConfig(unity.up, &unity.virtDesktopArray)) {
1724
errorMsg = "Could not set desktop configuration";
1728
return RpcIn_SetRetVals(result, resultLen,
1732
unity.virtDesktopArray.desktopCount = 0;
1733
Debug("%s: %s\n", __FUNCTION__, errorMsg);
1735
return RpcIn_SetRetVals(result, resultLen,
1742
*----------------------------------------------------------------------------
1744
* UnityTcloSetDesktopActive --
1746
* RPC handler for 'unity.set.desktop.active'.
1749
* TRUE if everything is successful.
1753
* Might change the active virtual desktop in the guest.
1755
*----------------------------------------------------------------------------
1759
UnityTcloSetDesktopActive(char const **result, // OUT
1760
size_t *resultLen, // OUT
1761
const char *name, // IN
1762
const char *args, // IN
1763
size_t argsSize, // IN
1764
void *clientData) // IN: ignored
1766
UnityDesktopId desktopId = 0;
1769
Debug("%s: name:%s args:'%s'\n", __FUNCTION__, name, args);
1771
if (sscanf(args, " %d", &desktopId) != 1) {
1772
errorMsg = "Invalid arguments: expected \"desktopId\"";
1776
if (desktopId >= unity.virtDesktopArray.desktopCount) {
1777
errorMsg = "Desktop does not exist in the guest";
1782
* Call the platform specific function to set the desktop active.
1785
if (!UnityPlatformSetDesktopActive(unity.up, desktopId)) {
1786
errorMsg = "Could not set active desktop";
1791
* Update the uwt with the new active desktop info.
1794
UnityWindowTracker_ChangeActiveDesktop(&unity.tracker, desktopId);
1796
return RpcIn_SetRetVals(result, resultLen,
1800
Debug("%s: %s\n", __FUNCTION__, errorMsg);
1801
return RpcIn_SetRetVals(result, resultLen,
1808
*----------------------------------------------------------------------------
1810
* UnityTcloSetWindowDesktop --
1812
* RPC handler for 'unity.set.window.desktop'.
1815
* TRUE if everything is successful.
1819
* Might change the active virtual desktop in the guest.
1821
*----------------------------------------------------------------------------
1825
UnityTcloSetWindowDesktop(char const **result, // OUT
1826
size_t *resultLen, // OUT
1827
const char *name, // IN
1828
const char *args, // IN
1829
size_t argsSize, // IN
1830
void *clientData) // IN: ignored
1832
UnityWindowId windowId;
1833
uint32 desktopId = 0;
1836
Debug("%s: name:%s args:'%s'\n", __FUNCTION__, name, args);
1838
if (sscanf(args, " %u %d", &windowId, &desktopId) != 2) {
1839
errorMsg = "Invalid arguments: expected \"windowId desktopId\"";
1843
if (desktopId >= unity.virtDesktopArray.desktopCount) {
1844
errorMsg = "The desktop does not exist in the guest";
1849
* Call the platform specific function to move the window to the
1850
* specified desktop.
1853
if (!UnityPlatformSetWindowDesktop(unity.up, windowId, desktopId)) {
1854
errorMsg = "Could not move the window to the desktop";
1858
UnityWindowTracker_ChangeWindowDesktop(&unity.tracker, windowId, desktopId);
1860
return RpcIn_SetRetVals(result, resultLen,
1864
Debug("%s: %s\n", __FUNCTION__, errorMsg);
1865
return RpcIn_SetRetVals(result, resultLen,