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
*********************************************************/
20
* unityWindowTracker.h --
22
* Used to buffer state about a window manager.
31
#include "unityWindowTracker.h"
33
#define LOGLEVEL_MODULE uwt
34
#include "loglevel_user.h"
40
static void FreeWindowInfo(UnityWindowInfo *info);
41
static int RemoveUntouchedWindow(const char *key, void *value,
43
static int GarbageCollectRemovedWindows(const char *key, void *value,
45
static int ResetChangedBits(const char *key, void *value, void *clientData);
46
static int PushUpdates(const char *key, void *value, void *clientData);
47
static int PushRemoveWindow(const char *key, void *value, void *clientData);
48
static Bool TitlesEqual(DynBuf *first, DynBuf *second);
49
static int PushZOrder(UnityWindowTracker *tracker);
50
static int PushActiveDesktop(UnityWindowTracker *tracker);
54
*----------------------------------------------------------------------------
56
* UnityWindowTracker_Init --
58
* Create a new unity window tracker. The client should pass in a
59
* callbacks object, which will be used to notify them of updates
60
* in UnityWindowTracker_RequestUpdates.
68
*----------------------------------------------------------------------------
72
UnityWindowTracker_Init(UnityWindowTracker *tracker, // IN
73
UnityUpdateCallback cb) // IN
75
memset(tracker, 0, sizeof(UnityWindowTracker));
77
tracker->windows = HashTable_Alloc(128, HASH_INT_KEY,
78
(HashTableFreeEntryFn)FreeWindowInfo);
83
*----------------------------------------------------------------------------
85
* UnityWindowTracker_Cleanup --
87
* Destory a unity window tracker.
95
*----------------------------------------------------------------------------
99
UnityWindowTracker_Cleanup(UnityWindowTracker *tracker) // IN
101
if (NULL != tracker->windows) {
102
HashTable_Free(tracker->windows);
104
memset(tracker, 0, sizeof(UnityWindowTracker));
109
*-----------------------------------------------------------------------------
111
* UnityWindowTracker_SetDataFreeFunc --
113
* Sets the function that will be called to free the app data associated with a
122
*-----------------------------------------------------------------------------
126
UnityWindowTracker_SetDataFreeFunc(UnityWindowTracker *tracker, // IN
127
UnityDataFreeFunc freeFn) // IN
129
tracker->freeFn = freeFn;
134
*----------------------------------------------------------------------------
136
* UnityWindowTracker_LookupWindow --
138
* Returns the window with the specified window id, or NULL if no
139
* such window exists.
147
*----------------------------------------------------------------------------
151
UnityWindowTracker_LookupWindow(UnityWindowTracker *tracker, // IN
152
UnityWindowId id) // IN
154
UnityWindowInfo *info = NULL;
155
HashTable_Lookup(tracker->windows, (const char *)(long)id, (void **)&info);
161
*----------------------------------------------------------------------------
163
* UnityWindowTracker_AddWindow --
165
* Add a new window to the window tracker
168
* A pointer to the UnityWindowInfo for the added window
173
*----------------------------------------------------------------------------
177
UnityWindowTracker_AddWindow(UnityWindowTracker *tracker, // IN
178
UnityWindowId id, // IN
179
DynBuf *windowPathUtf8, // IN
180
DynBuf *execPathUtf8) // IN
182
UnityWindowInfo *info = UnityWindowTracker_LookupWindow(tracker, id);
184
size_t windowPathSize;
187
info = (UnityWindowInfo *)Util_SafeCalloc(1, sizeof(UnityWindowInfo));
188
info->tracker = tracker;
190
info->type = UNITY_WINDOW_TYPE_NONE;
191
info->desktopId = tracker->activeDesktopId;
192
DynBuf_Init(&info->titleUtf8);
193
DynBuf_Init(&info->windowPathUtf8);
194
DynBuf_Init(&info->execPathUtf8);
197
* Ensure that the provided paths only include one NUL terminator
198
* at the end of the buffer, or keep the paths empty otherwise.
200
windowPathSize = DynBuf_GetSize(windowPathUtf8);
201
if (windowPathSize > 0) {
202
Bool isNullTerminated = Str_Strlen((char *)DynBuf_Get(windowPathUtf8),
203
windowPathSize) == windowPathSize - 1;
204
ASSERT(isNullTerminated);
205
if (isNullTerminated) {
206
DynBuf_Copy(windowPathUtf8, &info->windowPathUtf8);
209
execPathSize = DynBuf_GetSize(execPathUtf8);
210
if (execPathSize > 0) {
211
Bool isNullTerminated = Str_Strlen((char *)DynBuf_Get(execPathUtf8),
212
execPathSize) == execPathSize - 1;
213
ASSERT(isNullTerminated);
214
if (isNullTerminated) {
215
DynBuf_Copy(execPathUtf8, &info->execPathUtf8);
219
LOG(2, ("Unity adding new window (id:%d)\n", id));
220
HashTable_Insert(tracker->windows, (const char *)(long)id, info);
221
info->changed |= UNITY_CHANGED_ADDED;
222
info->changed |= UNITY_CHANGED_WINDOW_DESKTOP;
224
info->changed &= ~UNITY_CHANGED_REMOVED;
225
LOG(2, ("Window already exists in UnityAddWindow (id:%d)\n", id));
227
info->touched = TRUE;
234
*----------------------------------------------------------------------------
236
* UnityWindowTracker_AddWindowWithData --
238
* Add a new window to the window tracker, and sets its application data to the
239
* specified 'data'. The tracker's DataFreeFunc will be used to free 'data' when it
240
* needs to be destroyed (see UnityWindowTracker_SetDataFreeFunc). If the window
241
* already exists and has 'data' set on it, that will be destroyed and replaced with
242
* the new 'data' pointer.
245
* A pointer to the UnityWindowInfo for the added window.
250
*----------------------------------------------------------------------------
254
UnityWindowTracker_AddWindowWithData(UnityWindowTracker *tracker, // IN
255
UnityWindowId id, // IN
256
DynBuf *windowPathUtf8, // IN
257
DynBuf *execPathUtf8, // IN
260
UnityWindowInfo *info = UnityWindowTracker_AddWindow(tracker,
268
&& (info->data != data)) {
269
tracker->freeFn(tracker, info, info->data);
280
*----------------------------------------------------------------------------
282
* UnityWindowTracker_MoveWindow --
284
* Notify the window tracker that the window with the specified id
293
*----------------------------------------------------------------------------
297
UnityWindowTracker_MoveWindow(UnityWindowTracker *tracker, // IN
298
UnityWindowId id, // IN
304
UnityWindowInfo *info = UnityWindowTracker_LookupWindow(tracker, id);
306
info->touched = TRUE;
307
if (info->rect.x1 != x1 || info->rect.y1 != y1
308
|| x2 != info->rect.x2 || y2 != info->rect.y2) {
309
LOG(2, ("Unity moving window (id:%d pos:%d,%d, %d,%d)\n", id, x1, y1, x2, y2));
314
info->changed |= UNITY_CHANGED_POSITION;
321
*----------------------------------------------------------------------------
323
* UnityWindowTracker_SetWindowTitle --
325
* Notify the window tracker that the window with the specified id
326
* has changed its title.
328
* This function does not take ownership of the DynBuf; caller
329
* is assumed to free it.
337
*----------------------------------------------------------------------------
341
UnityWindowTracker_SetWindowTitle(UnityWindowTracker *tracker, // IN
342
UnityWindowId id, // IN
343
DynBuf *titleUtf8) // IN
345
UnityWindowInfo *info = UnityWindowTracker_LookupWindow(tracker, id);
347
info->touched = TRUE;
348
if (!TitlesEqual(&info->titleUtf8, titleUtf8)) {
349
LOG(2, ("Unity setting window title (id:%d title:%s)\n", id,
350
(const unsigned char*)DynBuf_Get(titleUtf8)));
351
info->changed |= UNITY_CHANGED_TITLE;
352
DynBuf_Destroy(&info->titleUtf8);
353
DynBuf_Copy(titleUtf8, &info->titleUtf8);
360
*----------------------------------------------------------------------------
362
* UnityWindowTracker_ChangeWindowRegion --
364
* Change the window region of the specified window. A NULL region
365
* means the window region is simply the bounds of the window.
367
* This function does not take ownership of the RegionPtr; caller is
368
* assumed to free it.
376
*----------------------------------------------------------------------------
380
UnityWindowTracker_ChangeWindowRegion(UnityWindowTracker *tracker, // IN
381
UnityWindowId id, // IN
382
RegionPtr region) // IN
384
UnityWindowInfo *info = UnityWindowTracker_LookupWindow(tracker, id);
386
info->touched = TRUE;
389
LOG(2, ("Unity adding window region (id:%d)\n", id));
390
info->changed |= UNITY_CHANGED_REGION;
391
info->region = miRegionCreate(&miEmptyBox, 0);
393
if (!miRegionsEqual(info->region, region)) {
394
LOG(2, ("Unity changing window region (id:%d)\n", id));
395
info->changed |= UNITY_CHANGED_REGION;
396
miRegionCopy(info->region, region);
400
LOG(2, ("Unity removing window region (id:%d)\n", id));
401
info->changed |= UNITY_CHANGED_REGION;
402
miRegionDestroy(info->region);
411
*----------------------------------------------------------------------------
413
* UnityWindowTracker_ChangeWindowState --
415
* Change window state (minimized or not) of the specified window.
423
*----------------------------------------------------------------------------
427
UnityWindowTracker_ChangeWindowState(UnityWindowTracker *tracker, // IN
428
UnityWindowId id, // IN
431
UnityWindowInfo *info = UnityWindowTracker_LookupWindow(tracker, id);
433
info->touched = TRUE;
434
if (state != info->state) {
435
info->changed |= UNITY_CHANGED_WINDOW_STATE;
437
LOG(2, ("Unity changing window state (id:%d) to %d\n", id, state));
444
*----------------------------------------------------------------------------
446
* UnityWindowTracker_GetWindowState --
448
* Get window state (minimized or not) of the specified window.
451
* TRUE if the window exists in the window tracker.
457
*----------------------------------------------------------------------------
461
UnityWindowTracker_GetWindowState(UnityWindowTracker *tracker, // IN
462
UnityWindowId id, // IN
465
UnityWindowInfo *info = UnityWindowTracker_LookupWindow(tracker, id);
468
*state = info->state;
476
*-----------------------------------------------------------------------------
478
* UnityWindowTracker_ChangeWindowAttribute --
480
* Sets the value of a particular attribute on a particular window.
486
* Marks the window tracker as having updates.
488
*-----------------------------------------------------------------------------
492
UnityWindowTracker_ChangeWindowAttribute(UnityWindowTracker *tracker, // IN
493
UnityWindowId id, // IN
494
UnityWindowAttribute attr, // IN
497
UnityWindowInfo *info;
499
ASSERT(attr < UNITY_MAX_ATTRIBUTES);
501
info = UnityWindowTracker_LookupWindow(tracker, id);
503
info->touched = TRUE;
506
* If this is a new attribute (that didn't exist before) or
507
* if the attribute value has changed, remember the new value
508
* (enabled or disabled) and mark it as existing and
512
if (!(info->attributes[attr] & UNITY_INFO_ATTR_EXISTS) ||
513
((info->attributes[attr] & UNITY_INFO_ATTR_ENABLED) !=
514
(enabled ? UNITY_INFO_ATTR_ENABLED : 0))) {
515
info->changed |= UNITY_CHANGED_WINDOW_ATTRIBUTES;
516
info->attributes[attr] = (UNITY_INFO_ATTR_EXISTS |
517
UNITY_INFO_ATTR_CHANGED |
518
(enabled ? UNITY_INFO_ATTR_ENABLED : 0));
519
LOG(2, ("Unity changing window (id:%d) attribute %d = %s\n",
520
id, attr, enabled ? "TRUE" : "FALSE"));
527
*-----------------------------------------------------------------------------
529
* UnityWindowTracker_GetWindowAttribute --
531
* Retrieves the current value of a window attribute.
534
* TRUE if the attribute value was retrieved successfully.
535
* FALSE if the attribute had never been set on the specified window, or if the
536
* window does not exist.
541
*-----------------------------------------------------------------------------
545
UnityWindowTracker_GetWindowAttribute(UnityWindowTracker *tracker, // IN
546
UnityWindowId id, // IN
547
UnityWindowAttribute attr, // IN
551
UnityWindowInfo *info;
553
ASSERT(attr < UNITY_MAX_ATTRIBUTES);
555
info = UnityWindowTracker_LookupWindow(tracker, id);
557
&& (info->attributes[attr] & UNITY_INFO_ATTR_EXISTS)) {
558
*enabled = (info->attributes[attr] & UNITY_INFO_ATTR_ENABLED) ? TRUE : FALSE;
567
*-----------------------------------------------------------------------------
569
* UnityWindowTracker_ChangeWindowType --
571
* Sets the window type of the specified window.
577
* Marks the window tracker as having updates.
579
*-----------------------------------------------------------------------------
583
UnityWindowTracker_ChangeWindowType(UnityWindowTracker *tracker, // IN
584
UnityWindowId id, // IN
585
UnityWindowType winType) // IN
587
UnityWindowInfo *info;
590
info = UnityWindowTracker_LookupWindow(tracker, id);
592
info->touched = TRUE;
593
if (winType != info->type) {
594
info->changed |= UNITY_CHANGED_WINDOW_TYPE;
595
info->type = winType;
596
LOG(2, ("Unity changing window (id:%d) type to %d\n",
604
*-----------------------------------------------------------------------------
606
* UnityWindowTracker_GetWindowType --
608
* Retrieves the window type of the specified window.
611
* TRUE if successful, FALSE if failed.
616
*-----------------------------------------------------------------------------
620
UnityWindowTracker_GetWindowType(UnityWindowTracker *tracker, // IN
621
UnityWindowId id, // IN
622
UnityWindowType *winType) // IN
625
UnityWindowInfo *info;
630
info = UnityWindowTracker_LookupWindow(tracker, id);
632
*winType = info->type;
641
*-----------------------------------------------------------------------------
643
* UnityWindowTracker_NotifyIconChanged --
645
* Marks the window tracker as having a changed icon for a window.
653
*-----------------------------------------------------------------------------
657
UnityWindowTracker_NotifyIconChanged(UnityWindowTracker *tracker, // IN
658
UnityWindowId id, // IN
659
UnityIconType iconType) // IN
661
UnityWindowInfo *info;
664
ASSERT(iconType < UNITY_MAX_ICONS);
666
info = UnityWindowTracker_LookupWindow(tracker, id);
668
LOG(2, ("Unity icon changed on window (id:%d)\n", id));
669
info->touched = TRUE;
670
info->changed |= UNITY_CHANGED_WINDOW_ICONS;
671
info->icons[iconType] |= UNITY_INFO_ATTR_CHANGED | UNITY_INFO_ATTR_EXISTS;
677
*-----------------------------------------------------------------------------
679
* UnityWindowTracker_ChangeWindowDesktop --
681
* Saves the window desktop information if that was changed.
687
* Window desktop information might be updated.
688
* Window marked as touched.
690
*-----------------------------------------------------------------------------
694
UnityWindowTracker_ChangeWindowDesktop(UnityWindowTracker *tracker, // IN
695
UnityWindowId id, // IN
696
UnityDesktopId desktopId) // IN
698
UnityWindowInfo *info;
701
info = UnityWindowTracker_LookupWindow(tracker, id);
703
info->touched = TRUE;
704
if (desktopId != info->desktopId) {
705
info->changed |= UNITY_CHANGED_WINDOW_DESKTOP;
706
info->desktopId = desktopId;
707
LOG(2, ("Unity changing window (id:%u) desktop to %d\n",
715
*-----------------------------------------------------------------------------
717
* UnityWindowTracker_GetWindowDesktop --
719
* Get the window desktop information.
722
* TRUE if this window exists in the window tracker, desktopId contains
724
* FALSE otherwise, desktopId undefined.
729
*-----------------------------------------------------------------------------
733
UnityWindowTracker_GetWindowDesktop(UnityWindowTracker *tracker, // IN
734
UnityWindowId id, // IN
735
UnityDesktopId *desktopId) // OUT
738
UnityWindowInfo *info;
743
info = UnityWindowTracker_LookupWindow(tracker, id);
745
*desktopId = info->desktopId;
754
*-----------------------------------------------------------------------------
756
* UnityWindowTracker_ChangeActiveDesktop --
758
* Saves the active desktop information if that was changed.
764
* Active desktop information might be updated.
766
*-----------------------------------------------------------------------------
770
UnityWindowTracker_ChangeActiveDesktop(UnityWindowTracker *tracker, // IN
771
UnityDesktopId desktopId) // IN
773
if (desktopId != tracker->activeDesktopId) {
774
tracker->activeDesktopId = desktopId;
775
tracker->activeDesktopChanged = TRUE;
781
*-----------------------------------------------------------------------------
783
* UnityWindowTracker_GetActiveDesktop --
785
* Return the active desktop id.
793
*-----------------------------------------------------------------------------
797
UnityWindowTracker_GetActiveDesktop(UnityWindowTracker *tracker) // IN
799
return tracker->activeDesktopId;
804
*----------------------------------------------------------------------------
806
* UnityWindowTracker_RemoveWindow --
808
* Remove the window with the specified id from the tracker.
816
*----------------------------------------------------------------------------
820
UnityWindowTracker_RemoveWindow(UnityWindowTracker *tracker, // IN
821
UnityWindowId id) // IN
823
UnityWindowInfo *info = UnityWindowTracker_LookupWindow(tracker, id);
825
LOG(2, ("Unity removing window (id:%d)\n", id));
826
info->changed |= UNITY_CHANGED_REMOVED;
827
info->touched = TRUE;
829
* Don't remove it yet. We can only do so later...
835
*------------------------------------------------------------------------------
837
* UnityWindowTracker_RemoveAllWindows --
839
* Removes all the windows from the window tracker, *without* notifying
840
* the client. This function is meant to be used to "reset" the tracker
841
* state after we have exited Unity.
849
*------------------------------------------------------------------------------
854
UnityWindowTracker_RemoveAllWindows(UnityWindowTracker *tracker)
856
HashTable_Clear(tracker->windows);
861
*----------------------------------------------------------------------------
863
* UnityWindowTracker_SendUpdate --
865
* Update the window tracker via a UnityUpdate structure instead
866
* of a call to AddWindow, MoveWindow, etc. Useful for forwarding
867
* notifications between unity windows trackers without writing
868
* a ton of boilerplate.
876
*----------------------------------------------------------------------------
880
UnityWindowTracker_SendUpdate(UnityWindowTracker *tracker, // IN
881
UnityUpdate *update) // IN
883
switch (update->type) {
884
case UNITY_UPDATE_ADD_WINDOW:
885
UnityWindowTracker_AddWindow(tracker,
886
update->u.addWindow.id,
887
&update->u.addWindow.windowPathUtf8,
888
&update->u.addWindow.execPathUtf8);
891
case UNITY_UPDATE_MOVE_WINDOW:
892
UnityWindowTracker_MoveWindow(tracker,
893
update->u.moveWindow.id,
894
update->u.moveWindow.rect.x1,
895
update->u.moveWindow.rect.y1,
896
update->u.moveWindow.rect.x2,
897
update->u.moveWindow.rect.y2);
900
case UNITY_UPDATE_REMOVE_WINDOW:
901
UnityWindowTracker_RemoveWindow(tracker,
902
update->u.removeWindow.id);
905
case UNITY_UPDATE_CHANGE_WINDOW_REGION:
906
UnityWindowTracker_ChangeWindowRegion(tracker,
907
update->u.changeWindowRegion.id,
908
update->u.changeWindowRegion.region);
911
case UNITY_UPDATE_CHANGE_WINDOW_TITLE:
912
UnityWindowTracker_SetWindowTitle(tracker,
913
update->u.changeWindowTitle.id,
914
&update->u.changeWindowTitle.titleUtf8);
917
case UNITY_UPDATE_CHANGE_ZORDER:
918
UnityWindowTracker_SetZOrder(tracker, update->u.zorder.ids,
919
update->u.zorder.count);
921
* This function is only every called from the host. Thus, if we get
922
* a zorder changed event from the guest it's safe to blindly trust it
923
* mark the zorder as changed. See bug 409742 for more info.
925
tracker->zorderChanged = TRUE;
928
case UNITY_UPDATE_CHANGE_WINDOW_STATE:
929
UnityWindowTracker_ChangeWindowState(tracker,
930
update->u.changeWindowState.id,
931
update->u.changeWindowState.state);
934
case UNITY_UPDATE_CHANGE_WINDOW_ATTRIBUTE:
935
UnityWindowTracker_ChangeWindowAttribute(tracker,
936
update->u.changeWindowAttribute.id,
937
update->u.changeWindowAttribute.attr,
938
update->u.changeWindowAttribute.value);
941
case UNITY_UPDATE_CHANGE_WINDOW_TYPE:
942
UnityWindowTracker_ChangeWindowType(tracker,
943
update->u.changeWindowType.id,
944
update->u.changeWindowType.winType);
947
case UNITY_UPDATE_CHANGE_WINDOW_ICON:
948
UnityWindowTracker_NotifyIconChanged(tracker,
949
update->u.changeWindowIcon.id,
950
update->u.changeWindowIcon.iconType);
952
case UNITY_UPDATE_CHANGE_WINDOW_DESKTOP:
953
UnityWindowTracker_ChangeWindowDesktop(tracker,
954
update->u.changeWindowDesktop.id,
955
update->u.changeWindowDesktop.desktopId);
958
case UNITY_UPDATE_CHANGE_ACTIVE_DESKTOP:
959
UnityWindowTracker_ChangeActiveDesktop(tracker,
960
update->u.changeActiveDesktop.desktopId);
970
*----------------------------------------------------------------------------
972
* UnityWindowTracker_SetZOrder --
974
* Notify the window tracker of the Z-order of all windows. Window ids
975
* at the front of the list are at the top of the z-order.
983
*----------------------------------------------------------------------------
987
UnityWindowTracker_SetZOrder(UnityWindowTracker *tracker, // IN
988
UnityWindowId zorder[], // IN
991
count = MIN(count, ARRAYSIZE(tracker->zorder));
993
if ((count != tracker->count) ||
994
(memcmp(tracker->zorder, zorder, count * sizeof(tracker->zorder[0])) != 0)) {
995
memcpy(tracker->zorder, zorder, count * sizeof(tracker->zorder[0]));
996
tracker->count = count;
997
tracker->zorderChanged = TRUE;
1003
*----------------------------------------------------------------------------
1005
* UnityWindowTracker_SetZPosition --
1007
* Notify the window tracker of the Z-order of one window.
1013
* Updates the Z-ordering of the tracker.
1015
*----------------------------------------------------------------------------
1019
UnityWindowTracker_SetZPosition(UnityWindowTracker *tracker, // IN
1020
UnityWindowId id, // IN
1021
uint32 zorder) // IN
1023
int newIndex, oldIndex;
1025
/* First, figure out where the window will be in the list */
1027
case UNITY_WINDOW_ORDER_BOTTOM:
1028
newIndex = tracker->count - 1;
1030
case UNITY_WINDOW_ORDER_TOP:
1036
/* Then, find where the window is the list. If it's not there, it's an error. */
1037
for (oldIndex = 0; oldIndex < tracker->count; oldIndex++) {
1038
if (id == tracker->zorder[oldIndex]) {
1042
ASSERT(oldIndex < tracker->count);
1044
/* Next, make space for the WindowId at its new spot */
1045
if (newIndex < oldIndex) {
1046
memmove(tracker->zorder + newIndex + 1, tracker->zorder + newIndex,
1047
oldIndex - newIndex);
1048
} else if (newIndex > oldIndex) {
1049
memmove(tracker->zorder + oldIndex, tracker->zorder + oldIndex + 1,
1050
newIndex - oldIndex);
1053
/* Finally, put it in place */
1054
tracker->zorder[newIndex] = id;
1055
tracker->zorderChanged = TRUE;
1060
*----------------------------------------------------------------------------
1062
* UnityWindowTracker_RequestUpdates --
1064
* Request a summary of all the updates pushed into the window tracker
1065
* since the last call to UnityWindowTracker_RequestUpdates.
1067
* If UNITY_UPDATE_INCREMENTAL is set in flags, callbacks will only
1068
* fire for elements which have changed since the last call to
1069
* UnityWindowTracker_RequestUpdates. Otherwise the entire state of
1070
* the window tracker is sent via the callbacks.
1072
* If UNITY_UPDATE_REMOVE_UNTOUCHED is set in flags, windows for
1073
* which there have been no updates since the last
1074
* UnityWindowTracker_RequestUpdates call will be automatically removed.
1075
* Useful if the client has no way of getting remove window notifications.
1081
* Lots of callbacks are fired.
1083
*----------------------------------------------------------------------------
1087
UnityWindowTracker_RequestUpdates(UnityWindowTracker *tracker, // IN
1091
tracker->cbparam = param;
1092
tracker->updateFlags = flags;
1095
* If necessary, remove windows which didn't receive updates.
1097
if (flags & UNITY_UPDATE_REMOVE_UNTOUCHED) {
1098
HashTable_ForEach(tracker->windows, RemoveUntouchedWindow, tracker);
1102
* Push updates for the windows remaining...
1103
* We push removed windows last to prevent temporary periods where a guest
1104
* app has no windows.
1106
HashTable_ForEach(tracker->windows, PushUpdates, tracker);
1107
HashTable_ForEach(tracker->windows, PushRemoveWindow, tracker);
1110
PushZOrder(tracker);
1112
/* Push active desktop info */
1113
PushActiveDesktop(tracker);
1116
* ...then really delete things which were removed...
1118
while (HashTable_ForEach(tracker->windows, GarbageCollectRemovedWindows,
1124
* ...and clear all the changed and touched bits of what's left to get ready
1125
* for the next iteration.
1127
HashTable_ForEach(tracker->windows, ResetChangedBits, NULL);
1132
*----------------------------------------------------------------------------
1144
*----------------------------------------------------------------------------
1148
FreeWindowInfo(UnityWindowInfo *info) // IN
1151
UnityWindowTracker *tracker = info->tracker;
1153
if (tracker->freeFn && info->data) {
1154
tracker->freeFn(tracker, info, info->data);
1158
miRegionDestroy(info->region);
1160
DynBuf_Destroy(&info->titleUtf8);
1161
DynBuf_Destroy(&info->windowPathUtf8);
1162
DynBuf_Destroy(&info->execPathUtf8);
1169
*----------------------------------------------------------------------------
1171
* RemoveUntouchedWindow --
1173
* If the window specified hasn't been touched (i.e. an update function
1174
* has been called on it), remove it from the window tracker.
1182
*----------------------------------------------------------------------------
1186
RemoveUntouchedWindow(const char *key, // IN: window id
1187
void *value, // IN: UnityWindowInfo
1188
void *clientData) // IN: UnityWindowTracker
1190
UnityWindowTracker *tracker = (UnityWindowTracker *)clientData;
1191
UnityWindowInfo *info = (UnityWindowInfo *)value;
1192
if (!info->touched) {
1193
UnityWindowId id = (UnityWindowId)(long)key;
1194
LOG(2, ("Removing untouched window (id:%d)\n", id));
1195
UnityWindowTracker_RemoveWindow(tracker, id);
1202
*----------------------------------------------------------------------------
1204
* GarbageCollectRemovedWindows --
1206
* Delete all window objects for windows which are marked as
1215
*----------------------------------------------------------------------------
1219
GarbageCollectRemovedWindows(const char *key, // IN: window id
1220
void *value, // IN: UnityWindowInfo
1221
void *clientData) // IN: UnityWindowTracker
1223
UnityWindowTracker *tracker = (UnityWindowTracker *)clientData;
1224
UnityWindowInfo *info = (UnityWindowInfo *)value;
1226
LOG(2, ("Destroying window (id:%d)\n", (UnityWindowId)(long)key));
1227
HashTable_Delete(tracker->windows, key);
1235
*----------------------------------------------------------------------------
1237
* ResetChangedBits --
1239
* Reset the changed and touched bits for this window.
1247
*----------------------------------------------------------------------------
1251
ResetChangedBits(const char *key, // IN: window id
1252
void *value, // IN: UnityWindowInfo
1253
void *clientData) // IN: UnityWindowTracker
1255
UnityWindowInfo *info = (UnityWindowInfo *)value;
1258
if (info->changed & UNITY_CHANGED_WINDOW_ATTRIBUTES) {
1259
for (i = 0; i < UNITY_MAX_ATTRIBUTES; i++) {
1260
info->attributes[i] &= ~UNITY_INFO_ATTR_CHANGED;
1264
if (info->changed & UNITY_CHANGED_WINDOW_ICONS) {
1265
for (i = 0; i < UNITY_MAX_ICONS; i++) {
1266
info->icons[i] &= ~UNITY_INFO_ATTR_CHANGED;
1271
info->touched = FALSE;
1277
*----------------------------------------------------------------------------
1279
* PushRemoveWindow --
1281
* Sends a remove window update for this window.
1289
*----------------------------------------------------------------------------
1293
PushRemoveWindow(const char *key, // IN: window id
1294
void *value, // IN: UnityWindowInfo
1295
void *clientData) // IN: UnityWindowTracker
1297
UnityWindowInfo *info = (UnityWindowInfo *)value;
1299
if (info->changed & UNITY_CHANGED_REMOVED) {
1300
UnityWindowTracker *tracker = (UnityWindowTracker *)clientData;
1301
UnityWindowId id = (UnityWindowId)(long)key;
1305
* Now that we've sent the update, mark the window as deleted
1306
* so it will be reaped.
1309
update.type = UNITY_UPDATE_REMOVE_WINDOW;
1310
update.u.removeWindow.id = id;
1311
(*tracker->cb)(tracker->cbparam, &update);
1319
*----------------------------------------------------------------------------
1323
* Fire all callback functions relevant for this window (as determined
1324
* by the changed bits).
1332
*----------------------------------------------------------------------------
1336
PushUpdates(const char *key, // IN: window id
1337
void *value, // IN: UnityWindowInfo
1338
void *clientData) // IN: UnityWindowTracker
1340
UnityWindowTracker *tracker = (UnityWindowTracker *)clientData;
1341
UnityWindowInfo *info = (UnityWindowInfo *)value;
1342
UnityWindowId id = (UnityWindowId)(long)key;
1344
Bool incremental = (tracker->updateFlags & UNITY_UPDATE_INCREMENTAL) != 0;
1346
if (info->changed & UNITY_CHANGED_REMOVED) {
1347
// This is handled in PushRemoveWindow.
1351
if (!incremental || (info->changed & UNITY_CHANGED_ADDED)) {
1352
update.type = UNITY_UPDATE_ADD_WINDOW;
1353
update.u.addWindow.id = id;
1354
DynBuf_Init(&update.u.addWindow.windowPathUtf8);
1355
DynBuf_Init(&update.u.addWindow.execPathUtf8);
1356
if (DynBuf_GetSize(&info->windowPathUtf8)) {
1357
DynBuf_Copy(&info->windowPathUtf8, &update.u.addWindow.windowPathUtf8);
1359
if (DynBuf_GetSize(&info->execPathUtf8)) {
1360
DynBuf_Copy(&info->execPathUtf8, &update.u.addWindow.execPathUtf8);
1362
(*tracker->cb)(tracker->cbparam, &update);
1363
DynBuf_Destroy(&update.u.addWindow.windowPathUtf8);
1364
DynBuf_Destroy(&update.u.addWindow.execPathUtf8);
1366
if (!incremental || (info->changed & UNITY_CHANGED_POSITION)) {
1367
update.type = UNITY_UPDATE_MOVE_WINDOW;
1368
update.u.moveWindow.id = id;
1369
update.u.moveWindow.rect = info->rect;
1370
(*tracker->cb)(tracker->cbparam, &update);
1372
if (!incremental || (info->changed & UNITY_CHANGED_REGION)) {
1373
update.type = UNITY_UPDATE_CHANGE_WINDOW_REGION;
1374
update.u.changeWindowRegion.id = id;
1375
update.u.changeWindowRegion.region = info->region;
1376
(*tracker->cb)(tracker->cbparam, &update);
1378
if (!incremental || (info->changed & UNITY_CHANGED_TITLE)) {
1379
update.type = UNITY_UPDATE_CHANGE_WINDOW_TITLE;
1380
update.u.changeWindowTitle.id = id;
1381
DynBuf_Init(&update.u.changeWindowTitle.titleUtf8);
1382
DynBuf_Copy(&info->titleUtf8, &update.u.changeWindowTitle.titleUtf8);
1383
(*tracker->cb)(tracker->cbparam, &update);
1384
DynBuf_Destroy(&update.u.changeWindowTitle.titleUtf8);
1386
if (!incremental || (info->changed & UNITY_CHANGED_WINDOW_ICONS)) {
1389
update.type = UNITY_UPDATE_CHANGE_WINDOW_ICON;
1390
update.u.changeWindowIcon.id = id;
1391
for (i = 0; i < UNITY_MAX_ICONS; i++) {
1392
if ((info->icons[i] & UNITY_INFO_ATTR_EXISTS)
1394
|| (info->icons[i] & UNITY_INFO_ATTR_CHANGED))) {
1395
update.u.changeWindowIcon.iconType = i;
1396
(*tracker->cb)(tracker->cbparam, &update);
1400
if (!incremental || (info->changed & UNITY_CHANGED_WINDOW_TYPE)) {
1401
update.type = UNITY_UPDATE_CHANGE_WINDOW_TYPE;
1402
update.u.changeWindowType.id = id;
1403
update.u.changeWindowType.winType = info->type;
1404
(*tracker->cb)(tracker->cbparam, &update);
1408
* Please make sure WINDOW_ATTRIBUTES is checked before WINDOW_STATE, to allow
1409
* vmware-vmx on the host side to only pay attention to WINDOW_ATTRIBUTES if
1412
if (!incremental || (info->changed & UNITY_CHANGED_WINDOW_ATTRIBUTES)) {
1413
UnityWindowAttribute i;
1415
update.type = UNITY_UPDATE_CHANGE_WINDOW_ATTRIBUTE;
1416
update.u.changeWindowAttribute.id = id;
1417
for (i = 0; i < UNITY_MAX_ATTRIBUTES; i++) {
1418
if ((info->attributes[i] & UNITY_INFO_ATTR_EXISTS)
1420
(info->attributes[i] & UNITY_INFO_ATTR_CHANGED))) {
1421
update.u.changeWindowAttribute.attr = i;
1422
update.u.changeWindowAttribute.value =
1423
(info->attributes[i] & UNITY_INFO_ATTR_ENABLED) ? TRUE : FALSE;
1424
(*tracker->cb)(tracker->cbparam, &update);
1429
if (!incremental || (info->changed & UNITY_CHANGED_WINDOW_STATE)) {
1430
update.type = UNITY_UPDATE_CHANGE_WINDOW_STATE;
1431
update.u.changeWindowState.id = id;
1432
update.u.changeWindowState.state = info->state;
1433
(*tracker->cb)(tracker->cbparam, &update);
1436
if (!incremental || (info->changed & UNITY_CHANGED_WINDOW_DESKTOP)) {
1437
update.type = UNITY_UPDATE_CHANGE_WINDOW_DESKTOP;
1438
update.u.changeWindowDesktop.id = id;
1439
update.u.changeWindowDesktop.desktopId = info->desktopId;
1440
(*tracker->cb)(tracker->cbparam, &update);
1448
*----------------------------------------------------------------------------
1452
* Fire callback function if Z Order was changed
1460
*----------------------------------------------------------------------------
1464
PushZOrder(UnityWindowTracker *tracker) // IN: UnityWindowTracker
1467
Bool incremental = (tracker->updateFlags & UNITY_UPDATE_INCREMENTAL) != 0;
1468
if (!incremental || tracker->zorderChanged) {
1469
update.type = UNITY_UPDATE_CHANGE_ZORDER;
1470
update.u.zorder.count = tracker->count;
1471
memcpy(&update.u.zorder.ids, tracker->zorder,
1472
tracker->count * sizeof(update.u.zorder.ids[0]));
1473
(*tracker->cb)(tracker->cbparam, &update);
1475
tracker->zorderChanged = FALSE;
1482
*----------------------------------------------------------------------------
1484
* PushActiveDesktop --
1486
* Fire callback function if the active desktop was changed.
1494
*----------------------------------------------------------------------------
1498
PushActiveDesktop(UnityWindowTracker *tracker) // IN: UnityWindowTracker
1501
Bool incremental = (tracker->updateFlags & UNITY_UPDATE_INCREMENTAL) != 0;
1502
if (!incremental || tracker->activeDesktopChanged) {
1503
update.type = UNITY_UPDATE_CHANGE_ACTIVE_DESKTOP;
1504
update.u.changeActiveDesktop.desktopId = tracker->activeDesktopId;
1505
(*tracker->cb)(tracker->cbparam, &update);
1507
tracker->activeDesktopChanged = FALSE;
1514
*----------------------------------------------------------------------------
1518
* Performs string comparison on the titles held in DynBufs
1521
* TRUE if first == second
1522
* FALSE if first != second
1527
*----------------------------------------------------------------------------
1531
TitlesEqual(DynBuf *first, // IN: First window title
1532
DynBuf *second) // IN: Second window title
1534
if (DynBuf_GetSize(first) != DynBuf_GetSize(second)) {
1537
return (strncmp((const unsigned char*)DynBuf_Get(first),
1538
(const unsigned char*)DynBuf_Get(second),
1539
DynBuf_GetSize(first)) == 0) ? TRUE : FALSE;
1544
*-----------------------------------------------------------------------------
1546
* UnityWindowTracker_ParseUnityUpdate --
1548
* Validate and parse Unity update command strings. The caller
1549
* should pass in a UnityUpdateCallback function (see lib/public/
1550
* unityWindowTracker.h). The callback will fire once for each
1551
* window update in the result string.
1559
*-----------------------------------------------------------------------------
1563
UnityWindowTracker_ParseUnityUpdate(const char *result, // IN
1565
UnityUpdateCallback cb, // IN
1568
if (result && len > 1) {
1574
* Protect against malicious guests. If the string is not double
1575
* null terminated, ignore it completely.
1578
if (result[len - 1] || result[len - 2]) {
1579
Warning("%s called with non-double null terminated string!\n",
1589
for (buf = result; *buf; buf += strlen(buf) + 1) {
1593
if (StrUtil_StartsWith(buf, "add ")) {
1594
const char *argumentStart = buf + strlen("add ");
1595
unsigned int index = 0;
1597
DynBuf_Init(&update.u.addWindow.windowPathUtf8);
1598
DynBuf_Init(&update.u.addWindow.execPathUtf8);
1601
* The arguments take the form 'add <id> [arguments]' where [arguments] are
1602
* space seperated 'key=value' pairs, if an argument is simply key= the value
1603
* should be treated as an empty string.
1604
* Only the <id> may be present if the guest is running certain older
1605
* versions of tools.
1607
ok = StrUtil_GetNextUintToken(&id, &index, argumentStart, " ");
1610
* If we failed to get the required <id> argument there's little point
1611
* carrying on. However failure to receive any of the following arguments
1612
* is not an error (they're optional) - it's also not an error to get
1613
* an expected key here since we may well add new keys in the future and
1614
* don't want to log needless error messages if the host hasn't been updated
1619
update.type = UNITY_UPDATE_ADD_WINDOW;
1620
update.u.addWindow.id = id;
1621
while ((field = StrUtil_GetNextToken(&index, argumentStart, " "))) {
1623
if (StrUtil_StartsWith(field, "windowPath=")) {
1624
ok = DynBuf_AppendString(&update.u.addWindow.windowPathUtf8,
1625
field + strlen("windowPath="));
1628
if (StrUtil_StartsWith(field, "execPath=")) {
1629
ok = DynBuf_AppendString(&update.u.addWindow.execPathUtf8,
1630
field + strlen("execPath="));
1634
* Free the results of the tokenization (field)
1639
LOG(0, ("TOOLS Expected window id but no id found in string.\n"));
1641
} else if (StrUtil_StartsWith(buf, "remove ")) {
1642
if (sscanf(buf, "remove %u", &id) == 1) {
1643
update.type = UNITY_UPDATE_REMOVE_WINDOW;
1644
update.u.removeWindow.id = id;
1647
} else if (StrUtil_StartsWith(buf, "move ")) {
1648
if (sscanf(buf, "move %u %d %d %d %d", &id, &x1, &y1,
1650
update.type = UNITY_UPDATE_MOVE_WINDOW;
1651
update.u.moveWindow.id = id;
1652
RECT_SETRECT(&update.u.moveWindow.rect, x1, y1, x2, y2);
1655
} else if (StrUtil_StartsWith(buf, "title ")) {
1656
const char *titleInfo = buf + 6;
1658
/* StrUtil_* functions do not like NULL strings. */
1660
unsigned int index = 0;
1661
if (StrUtil_GetNextUintToken(&id, &index, titleInfo, " ")) {
1662
char *titleUtf8 = NULL;
1663
update.u.changeWindowTitle.id = id;
1665
if (titleInfo[index] == ' ') {
1666
index++; // Skip the space
1667
if (titleInfo[index]) {
1668
titleUtf8 = StrUtil_GetNextToken(&index, titleInfo,
1673
titleUtf8 = Util_SafeStrdup("");
1676
DynBuf_Init(&update.u.changeWindowTitle.titleUtf8);
1677
if (DynBuf_AppendString(&update.u.changeWindowTitle.titleUtf8,
1679
update.type = UNITY_UPDATE_CHANGE_WINDOW_TITLE;
1685
("TOOLS Malformed title string, couldn't get the window id.\n"));
1688
LOG(0, ("TOOLS Empty title string, couldn't get the title.\n"));
1690
} else if (StrUtil_StartsWith(buf, "zorder ")) {
1693
const char *bufPtr = buf;
1694
bufPtr = strchr(bufPtr, ' ');
1695
if (sscanf(bufPtr, " %d", &count) == 1) {
1696
count = MIN(count, ARRAYSIZE(update.u.zorder.ids));
1697
bufPtr = strchr(++bufPtr, ' ');
1698
while (bufPtr && *bufPtr && i < count &&
1699
sscanf(bufPtr, " %u", update.u.zorder.ids + i) == 1) {
1701
bufPtr = strchr(++bufPtr, ' ');
1704
update.type = UNITY_UPDATE_CHANGE_ZORDER;
1705
update.u.zorder.count = count;
1708
LOG(0, ("TOOLS Expected %d but received %d in zorder string.\n",
1712
LOG(0, ("TOOLS Malformed zorder string, couldn't scan count.\n"));
1714
} else if (StrUtil_StartsWith(buf, "region ")) {
1715
int current = 0, numRects;
1716
if (sscanf(buf, "region %u %d", &id, &numRects) == 2) {
1717
RegionPtr region = NULL;
1719
update.type = UNITY_UPDATE_CHANGE_WINDOW_REGION;
1720
update.u.changeWindowRegion.region = NULL;
1721
update.u.changeWindowRegion.id = id;
1725
RECT_SETRECT(&rc, 0, 0, 0, 0);
1726
region = miRegionCreate(&rc, 0);
1729
buf += strlen(buf) + 1;
1731
if (sscanf(buf, "rect %d %d %d %d", &x1, &y1, &x2, &y2) == 4) {
1732
RECT_SETRECT(&rc, x1, y1, x2, y2);
1733
miApplyRect(region, region, &rc, miUnion);
1735
Warning("Malformed processing unity region" \
1736
" (attempting to continue): %s.\n",
1739
} while (current < numRects && *buf);
1741
update.u.changeWindowRegion.region = region;
1744
Warning("Malformed processing unity region %s.\n", buf);
1746
} else if (StrUtil_StartsWith(buf, "state ")) {
1749
update.type = UNITY_UPDATE_CHANGE_WINDOW_STATE;
1750
if (sscanf(buf, "state %u %d", &id, &state) == 2) {
1751
update.u.changeWindowState.id = id;
1752
update.u.changeWindowState.state = state;
1755
} else if (StrUtil_StartsWith(buf, "attr ")) {
1756
update.type = UNITY_UPDATE_CHANGE_WINDOW_ATTRIBUTE;
1757
if (sscanf(buf, "attr %u %u %u",
1758
&update.u.changeWindowAttribute.id,
1759
(unsigned int *)&update.u.changeWindowAttribute.attr,
1760
&update.u.changeWindowAttribute.value) == 3 &&
1761
update.u.changeWindowAttribute.attr < UNITY_MAX_ATTRIBUTES) {
1764
} else if (StrUtil_StartsWith(buf, "type ")) {
1765
update.type = UNITY_UPDATE_CHANGE_WINDOW_TYPE;
1766
if (sscanf(buf, "type %u %d", &update.u.changeWindowType.id,
1767
(int *)&update.u.changeWindowType.winType) == 2 &&
1768
update.u.changeWindowType.winType < UNITY_MAX_WINDOW_TYPES) {
1771
} else if (StrUtil_StartsWith(buf, "icon ")) {
1772
update.type = UNITY_UPDATE_CHANGE_WINDOW_ICON;
1773
if (sscanf(buf, "icon %u %u", &update.u.changeWindowIcon.id,
1774
(unsigned int *)&update.u.changeWindowIcon.iconType) == 2 &&
1775
update.u.changeWindowIcon.iconType < UNITY_MAX_ICONS) {
1778
} else if (StrUtil_StartsWith(buf, "desktop ")) {
1779
update.type = UNITY_UPDATE_CHANGE_WINDOW_DESKTOP;
1780
if (sscanf(buf, "desktop %u %d", &update.u.changeWindowDesktop.id,
1781
&update.u.changeWindowDesktop.desktopId) == 2) {
1784
} else if (StrUtil_StartsWith(buf, "activedesktop ")) {
1785
update.type = UNITY_UPDATE_CHANGE_ACTIVE_DESKTOP;
1786
if (sscanf(buf, "activedesktop %d",
1787
&update.u.changeActiveDesktop.desktopId) == 1) {
1793
(*cb)(param, &update);
1795
Warning("Malformed unity response string: %s.\n", buf);
1798
if (ok && (update.type == UNITY_UPDATE_ADD_WINDOW)) {
1799
DynBuf_Destroy(&update.u.addWindow.windowPathUtf8);
1800
DynBuf_Destroy(&update.u.addWindow.execPathUtf8);
1803
if (ok && (update.type == UNITY_UPDATE_CHANGE_WINDOW_TITLE)) {
1804
DynBuf_Destroy(&update.u.changeWindowTitle.titleUtf8);
1808
(update.type == UNITY_UPDATE_CHANGE_WINDOW_REGION) &&
1809
(update.u.changeWindowRegion.region != NULL)) {
1810
miRegionDestroy(update.u.changeWindowRegion.region);