[3655] | 1 | /** $Id: VBoxServiceClipboard-os2.cpp 98103 2023-01-17 14:15:46Z vboxsync $ */
|
---|
| 2 | /** @file
|
---|
| 3 | * VBoxService - Guest Additions Clipboard Service, OS/2.
|
---|
| 4 | */
|
---|
| 5 |
|
---|
| 6 | /*
|
---|
[98103] | 7 | * Copyright (C) 2007-2023 Oracle and/or its affiliates.
|
---|
[3655] | 8 | *
|
---|
[96407] | 9 | * This file is part of VirtualBox base platform packages, as
|
---|
| 10 | * available from https://www.virtualbox.org.
|
---|
| 11 | *
|
---|
| 12 | * This program is free software; you can redistribute it and/or
|
---|
| 13 | * modify it under the terms of the GNU General Public License
|
---|
| 14 | * as published by the Free Software Foundation, in version 3 of the
|
---|
| 15 | * License.
|
---|
| 16 | *
|
---|
| 17 | * This program is distributed in the hope that it will be useful, but
|
---|
| 18 | * WITHOUT ANY WARRANTY; without even the implied warranty of
|
---|
| 19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
---|
| 20 | * General Public License for more details.
|
---|
| 21 | *
|
---|
| 22 | * You should have received a copy of the GNU General Public License
|
---|
| 23 | * along with this program; if not, see <https://www.gnu.org/licenses>.
|
---|
| 24 | *
|
---|
| 25 | * SPDX-License-Identifier: GPL-3.0-only
|
---|
[3655] | 26 | */
|
---|
| 27 |
|
---|
| 28 |
|
---|
[58052] | 29 | /** @page pg_vgsvc_clipboard VBoxService - Clipboard (OS/2)
|
---|
| 30 | *
|
---|
| 31 | * The Clipboard subservice provides clipboard sharing for OS/2 guests only.
|
---|
| 32 | *
|
---|
| 33 | * This was the second subservice that was added to VBoxService. OS/2 is a
|
---|
| 34 | * single user system and we don't provide any VBoxTray or VBoxClient like
|
---|
| 35 | * processes. Because it's kind of simple system, it became natural to put the
|
---|
| 36 | * clipboard sharing here in VBoxService for OS/2.
|
---|
| 37 | *
|
---|
| 38 | * In addition to integrating with the native OS/2 PM clipboard formats, we also
|
---|
| 39 | * try provide the Odin32, a windows API layer for OS/2 (developed by Sander van
|
---|
| 40 | * Leeuwen and friends, later mainly InnoTek), with additional formats.
|
---|
| 41 | *
|
---|
| 42 | * Bitmaps are currently not supported, but that can easily be added should the
|
---|
| 43 | * need ever arrise.
|
---|
| 44 | */
|
---|
| 45 |
|
---|
| 46 |
|
---|
[57358] | 47 | /*********************************************************************************************************************************
|
---|
| 48 | * Header Files *
|
---|
| 49 | *********************************************************************************************************************************/
|
---|
[3655] | 50 | #define INCL_BASE
|
---|
| 51 | #define INCL_PM
|
---|
| 52 | #define INCL_ERRORS
|
---|
| 53 | #include <os2.h>
|
---|
| 54 |
|
---|
[76441] | 55 | #include <iprt/asm.h>
|
---|
| 56 | #include <iprt/assert.h>
|
---|
| 57 | #include <iprt/mem.h>
|
---|
| 58 | #include <iprt/param.h>
|
---|
| 59 | #include <iprt/semaphore.h>
|
---|
| 60 | #include <iprt/string.h>
|
---|
[3655] | 61 | #include <iprt/thread.h>
|
---|
| 62 | #include <iprt/time.h>
|
---|
[76441] | 63 | #include <iprt/utf16.h>
|
---|
[21218] | 64 | #include <VBox/VBoxGuestLib.h>
|
---|
[78181] | 65 | #include <VBox/GuestHost/SharedClipboard.h>
|
---|
[3655] | 66 | #include <VBox/HostServices/VBoxClipboardSvc.h>
|
---|
| 67 | #include "VBoxServiceInternal.h"
|
---|
| 68 |
|
---|
| 69 |
|
---|
[57358] | 70 | /*********************************************************************************************************************************
|
---|
| 71 | * Structures and Typedefs *
|
---|
| 72 | *********************************************************************************************************************************/
|
---|
[3655] | 73 | /** Header for Odin32 specific clipboard entries.
|
---|
| 74 | * (Used to get the correct size of the data.)
|
---|
| 75 | */
|
---|
| 76 | typedef struct _Odin32ClipboardHeader
|
---|
| 77 | {
|
---|
| 78 | /** magic number */
|
---|
| 79 | char achMagic[8];
|
---|
| 80 | /** Size of the following data.
|
---|
| 81 | * (The interpretation depends on the type.) */
|
---|
| 82 | unsigned cbData;
|
---|
| 83 | /** Odin32 format number. */
|
---|
| 84 | unsigned uFormat;
|
---|
| 85 | } CLIPHEADER, *PCLIPHEADER;
|
---|
| 86 |
|
---|
| 87 | #define CLIPHEADER_MAGIC "Odin\1\0\1"
|
---|
| 88 |
|
---|
| 89 |
|
---|
[57358] | 90 | /*********************************************************************************************************************************
|
---|
| 91 | * Global Variables *
|
---|
| 92 | *********************************************************************************************************************************/
|
---|
[3655] | 93 |
|
---|
[8565] | 94 | /** The control thread (main) handle.
|
---|
[3655] | 95 | * Only used to avoid some queue creation trouble. */
|
---|
| 96 | static RTTHREAD g_ThreadCtrl = NIL_RTTHREAD;
|
---|
| 97 | /** The HAB of the control thread (main). */
|
---|
| 98 | static HAB g_habCtrl = NULLHANDLE;
|
---|
| 99 | /** The HMQ of the control thread (main). */
|
---|
| 100 | static HMQ g_hmqCtrl = NULLHANDLE;
|
---|
| 101 |
|
---|
| 102 | /** The Listener thread handle. */
|
---|
| 103 | static RTTHREAD g_ThreadListener = NIL_RTTHREAD;
|
---|
| 104 | /** The HAB of the listener thread. */
|
---|
| 105 | static HAB g_habListener = NULLHANDLE;
|
---|
| 106 | /** The HMQ of the listener thread. */
|
---|
| 107 | static HMQ g_hmqListener = NULLHANDLE;
|
---|
| 108 | /** Indicator that gets set if the listener thread is successfully initialized. */
|
---|
| 109 | static bool volatile g_fListenerOkay = false;
|
---|
| 110 |
|
---|
| 111 | /** The HAB of the worker thread. */
|
---|
| 112 | static HAB g_habWorker = NULLHANDLE;
|
---|
| 113 | /** The HMQ of the worker thread. */
|
---|
| 114 | static HMQ g_hmqWorker = NULLHANDLE;
|
---|
| 115 | /** The object window handle. */
|
---|
| 116 | static HWND g_hwndWorker = NULLHANDLE;
|
---|
| 117 | /** The timer id returned by WinStartTimer. */
|
---|
| 118 | static ULONG g_idWorkerTimer = ~0UL;
|
---|
[8565] | 119 | /** The state of the clipboard.
|
---|
[3655] | 120 | * @remark I'm trying out the 'k' prefix from the mac here, bear with me. */
|
---|
| 121 | static enum
|
---|
[8565] | 122 | {
|
---|
[3655] | 123 | /** The clipboard hasn't been initialized yet. */
|
---|
| 124 | kClipboardState_Uninitialized = 0,
|
---|
| 125 | /** WinSetClipbrdViewer call in progress, ignore WM_DRAWCLIPBOARD. */
|
---|
| 126 | kClipboardState_SettingViewer,
|
---|
| 127 | /** We're monitoring the clipboard as a viewer. */
|
---|
| 128 | kClipboardState_Viewer,
|
---|
[8565] | 129 | /** We're monitoring the clipboard using polling.
|
---|
[3655] | 130 | * This usually means something is wrong... */
|
---|
| 131 | kClipboardState_Polling,
|
---|
| 132 | /** We're destroying the clipboard content, ignore WM_DESTROYCLIPBOARD. */
|
---|
| 133 | kClipboardState_Destroying,
|
---|
| 134 | /** We're owning the clipboard (i.e. we have data on it). */
|
---|
| 135 | kClipboardState_Owner
|
---|
| 136 | } g_enmState = kClipboardState_Uninitialized;
|
---|
| 137 | /** Set if the clipboard was empty the last time we polled it. */
|
---|
| 138 | static bool g_fEmptyClipboard = false;
|
---|
| 139 |
|
---|
[8565] | 140 | /** A clipboard format atom for the dummy clipboard data we insert
|
---|
| 141 | * watching for clipboard changes. If this format is found on the
|
---|
| 142 | * clipboard, the empty clipboard function has not been called
|
---|
[3655] | 143 | * since we last polled it. */
|
---|
| 144 | static ATOM g_atomNothingChanged = 0;
|
---|
| 145 |
|
---|
| 146 | /** The clipboard connection client ID. */
|
---|
| 147 | static uint32_t g_u32ClientId;
|
---|
| 148 | /** Odin32 CF_UNICODETEXT. See user32.cpp. */
|
---|
| 149 | static ATOM g_atomOdin32UnicodeText = 0;
|
---|
| 150 | /** Odin32 CF_UNICODETEXT. See user32.cpp. */
|
---|
| 151 | #define SZFMT_ODIN32_UNICODETEXT (PCSZ)"Odin32 UnicodeText"
|
---|
| 152 |
|
---|
| 153 |
|
---|
| 154 |
|
---|
| 155 |
|
---|
[58029] | 156 | /**
|
---|
| 157 | * @interface_method_impl{VBOXSERVICE,pfnPreInit}
|
---|
| 158 | */
|
---|
| 159 | static DECLCALLBACK(int) vgsvcClipboardOs2PreInit(void)
|
---|
[3655] | 160 | {
|
---|
| 161 | return VINF_SUCCESS;
|
---|
| 162 | }
|
---|
| 163 |
|
---|
| 164 |
|
---|
[58029] | 165 | /**
|
---|
| 166 | * @interface_method_impl{VBOXSERVICE,pfnOption}
|
---|
| 167 | */
|
---|
| 168 | static DECLCALLBACK(int) vgsvcClipboardOs2Option(const char **ppszShort, int argc, char **argv, int *pi)
|
---|
[3655] | 169 | {
|
---|
[40128] | 170 | NOREF(ppszShort);
|
---|
| 171 | NOREF(argc);
|
---|
| 172 | NOREF(argv);
|
---|
| 173 | NOREF(pi);
|
---|
| 174 |
|
---|
[3655] | 175 | return -1;
|
---|
| 176 | }
|
---|
| 177 |
|
---|
| 178 |
|
---|
[58029] | 179 | /**
|
---|
| 180 | * @interface_method_impl{VBOXSERVICE,pfnInit}
|
---|
| 181 | */
|
---|
| 182 | static DECLCALLBACK(int) vgsvcClipboardOs2Init(void)
|
---|
[3655] | 183 | {
|
---|
| 184 | int rc = VERR_GENERAL_FAILURE;
|
---|
| 185 | g_ThreadCtrl = RTThreadSelf();
|
---|
| 186 |
|
---|
| 187 | /*
|
---|
| 188 | * Make PM happy.
|
---|
| 189 | */
|
---|
| 190 | PPIB pPib;
|
---|
| 191 | PTIB pTib;
|
---|
| 192 | DosGetInfoBlocks(&pTib, &pPib);
|
---|
| 193 | pPib->pib_ultype = 3; /* PM session type */
|
---|
| 194 |
|
---|
| 195 | /*
|
---|
[8565] | 196 | * Since we have to send shutdown messages and such from the
|
---|
[3655] | 197 | * service controller (main) thread, create a HAB and HMQ for it.
|
---|
| 198 | */
|
---|
| 199 | g_habCtrl = WinInitialize(0);
|
---|
| 200 | if (g_habCtrl == NULLHANDLE)
|
---|
| 201 | {
|
---|
[58029] | 202 | VGSvcError("WinInitialize(0) failed, lasterr=%lx\n", WinGetLastError(NULLHANDLE));
|
---|
[3655] | 203 | return VERR_GENERAL_FAILURE;
|
---|
| 204 | }
|
---|
| 205 | g_hmqCtrl = WinCreateMsgQueue(g_habCtrl, 0);
|
---|
| 206 | if (g_hmqCtrl != NULLHANDLE)
|
---|
| 207 | {
|
---|
[43581] | 208 | WinCancelShutdown(g_hmqCtrl, TRUE); /* We don't care about shutdown */
|
---|
| 209 |
|
---|
[3655] | 210 | /*
|
---|
| 211 | * Create the 'nothing-changed' format.
|
---|
| 212 | */
|
---|
[4377] | 213 | g_atomNothingChanged = WinAddAtom(WinQuerySystemAtomTable(), (PCSZ)"VirtualBox Clipboard Service");
|
---|
[3655] | 214 | LONG lLastError = WinGetLastError(g_habCtrl);
|
---|
| 215 | if (g_atomNothingChanged == 0)
|
---|
[4377] | 216 | g_atomNothingChanged = WinFindAtom(WinQuerySystemAtomTable(), (PCSZ)"VirtualBox Clipboard Service");
|
---|
[3655] | 217 | if (g_atomNothingChanged)
|
---|
| 218 | {
|
---|
| 219 | /*
|
---|
| 220 | * Connect to the clipboard service.
|
---|
| 221 | */
|
---|
[58029] | 222 | VGSvcVerbose(4, "clipboard: connecting\n");
|
---|
[3655] | 223 | rc = VbglR3ClipboardConnect(&g_u32ClientId);
|
---|
| 224 | if (RT_SUCCESS(rc))
|
---|
| 225 | {
|
---|
| 226 | /*
|
---|
| 227 | * Create any extra clipboard type atoms, like the odin unicode text.
|
---|
| 228 | */
|
---|
| 229 | g_atomOdin32UnicodeText = WinAddAtom(WinQuerySystemAtomTable(), SZFMT_ODIN32_UNICODETEXT);
|
---|
| 230 | lLastError = WinGetLastError(g_habCtrl);
|
---|
| 231 | if (g_atomOdin32UnicodeText == 0)
|
---|
| 232 | g_atomOdin32UnicodeText = WinFindAtom(WinQuerySystemAtomTable(), SZFMT_ODIN32_UNICODETEXT);
|
---|
| 233 | if (g_atomOdin32UnicodeText == 0)
|
---|
[58029] | 234 | VGSvcError("WinAddAtom() failed, lasterr=%lx; WinFindAtom() failed, lasterror=%lx\n",
|
---|
[58052] | 235 | lLastError, WinGetLastError(g_habCtrl));
|
---|
[3655] | 236 |
|
---|
[58029] | 237 | VGSvcVerbose(2, "g_u32ClientId=%RX32 g_atomNothingChanged=%#x g_atomOdin32UnicodeText=%#x\n",
|
---|
[58052] | 238 | g_u32ClientId, g_atomNothingChanged, g_atomOdin32UnicodeText);
|
---|
[3655] | 239 | return VINF_SUCCESS;
|
---|
| 240 | }
|
---|
| 241 |
|
---|
[58029] | 242 | VGSvcError("Failed to connect to the clipboard service, rc=%Rrc!\n", rc);
|
---|
[3655] | 243 | }
|
---|
| 244 | else
|
---|
[58029] | 245 | VGSvcError("WinAddAtom() failed, lasterr=%lx; WinFindAtom() failed, lasterror=%lx\n",
|
---|
[58052] | 246 | lLastError, WinGetLastError(g_habCtrl));
|
---|
[3655] | 247 | }
|
---|
| 248 | else
|
---|
[58029] | 249 | VGSvcError("WinCreateMsgQueue(,0) failed, lasterr=%lx\n", WinGetLastError(g_habCtrl));
|
---|
[3655] | 250 | WinTerminate(g_habCtrl);
|
---|
| 251 | return rc;
|
---|
| 252 | }
|
---|
| 253 |
|
---|
| 254 |
|
---|
| 255 | /**
|
---|
| 256 | * Check that we're still the view / try make us the viewer.
|
---|
| 257 | */
|
---|
[58029] | 258 | static void vgsvcClipboardOs2PollViewer(void)
|
---|
[3655] | 259 | {
|
---|
| 260 | const int iOrgState = g_enmState;
|
---|
| 261 |
|
---|
| 262 | HWND hwndClipboardViewer = WinQueryClipbrdViewer(g_habWorker);
|
---|
| 263 | if (hwndClipboardViewer == g_hwndWorker)
|
---|
| 264 | return;
|
---|
| 265 |
|
---|
| 266 | if (hwndClipboardViewer == NULLHANDLE)
|
---|
| 267 | {
|
---|
| 268 | /* The API will send a WM_DRAWCLIPBOARD message before returning. */
|
---|
| 269 | g_enmState = kClipboardState_SettingViewer;
|
---|
| 270 | if (WinSetClipbrdViewer(g_habWorker, g_hwndWorker))
|
---|
| 271 | g_enmState = kClipboardState_Viewer;
|
---|
| 272 | else
|
---|
| 273 | g_enmState = kClipboardState_Polling;
|
---|
| 274 | }
|
---|
| 275 | else
|
---|
| 276 | g_enmState = kClipboardState_Polling;
|
---|
| 277 | if ((int)g_enmState != iOrgState)
|
---|
[8565] | 278 | {
|
---|
[3655] | 279 | if (g_enmState == kClipboardState_Viewer)
|
---|
[58029] | 280 | VGSvcVerbose(3, "clipboard: viewer\n");
|
---|
[3655] | 281 | else
|
---|
[58029] | 282 | VGSvcVerbose(3, "clipboard: poller\n");
|
---|
[3655] | 283 | }
|
---|
| 284 | }
|
---|
| 285 |
|
---|
| 286 |
|
---|
| 287 | /**
|
---|
| 288 | * Advertise the formats available from the host.
|
---|
[58029] | 289 | *
|
---|
| 290 | * @param fFormats The formats available on the host.
|
---|
[3655] | 291 | */
|
---|
[58029] | 292 | static void vgsvcClipboardOs2AdvertiseHostFormats(uint32_t fFormats)
|
---|
[3655] | 293 | {
|
---|
| 294 | /*
|
---|
| 295 | * Open the clipboard and switch to 'destruction' mode.
|
---|
[8565] | 296 | * Make sure we stop being viewer. Temporarily also make sure we're
|
---|
[3655] | 297 | * not the owner so that PM won't send us any WM_DESTROYCLIPBOARD message.
|
---|
| 298 | */
|
---|
| 299 | if (WinOpenClipbrd(g_habWorker))
|
---|
| 300 | {
|
---|
| 301 | if (g_enmState == kClipboardState_Viewer)
|
---|
[8565] | 302 | WinSetClipbrdViewer(g_habWorker, NULLHANDLE);
|
---|
[3655] | 303 | if (g_enmState == kClipboardState_Owner)
|
---|
[8565] | 304 | WinSetClipbrdOwner(g_habWorker, NULLHANDLE);
|
---|
[3655] | 305 |
|
---|
| 306 | g_enmState = kClipboardState_Destroying;
|
---|
| 307 | if (WinEmptyClipbrd(g_habWorker))
|
---|
| 308 | {
|
---|
| 309 | /*
|
---|
| 310 | * Take clipboard ownership.
|
---|
| 311 | */
|
---|
| 312 | if (WinSetClipbrdOwner(g_habWorker, g_hwndWorker))
|
---|
| 313 | {
|
---|
| 314 | g_enmState = kClipboardState_Owner;
|
---|
| 315 |
|
---|
| 316 | /*
|
---|
| 317 | * Do the format advertising.
|
---|
| 318 | */
|
---|
[80847] | 319 | if (fFormats & (VBOX_SHCL_FMT_UNICODETEXT/* | VBOX_SHCL_FMT_HTML ?? */))
|
---|
[3655] | 320 | {
|
---|
| 321 | if (!WinSetClipbrdData(g_habWorker, 0, CF_TEXT, CFI_POINTER))
|
---|
[58029] | 322 | VGSvcError("WinSetClipbrdData(,,CF_TEXT,) failed, lasterr=%lx\n", WinGetLastError(g_habWorker));
|
---|
[58052] | 323 | if ( g_atomOdin32UnicodeText
|
---|
| 324 | && !WinSetClipbrdData(g_habWorker, 0, g_atomOdin32UnicodeText, CFI_POINTER))
|
---|
[58029] | 325 | VGSvcError("WinSetClipbrdData(,,g_atomOdin32UnicodeText,) failed, lasterr=%lx\n", WinGetLastError(g_habWorker));
|
---|
[3655] | 326 | }
|
---|
[80847] | 327 | if (fFormats & VBOX_SHCL_FMT_BITMAP)
|
---|
[3655] | 328 | {
|
---|
| 329 | /** @todo bitmaps */
|
---|
| 330 | }
|
---|
| 331 | }
|
---|
| 332 | else
|
---|
| 333 | {
|
---|
[58029] | 334 | VGSvcError("WinSetClipbrdOwner failed, lasterr=%lx\n", WinGetLastError(g_habWorker));
|
---|
[3655] | 335 | g_enmState = kClipboardState_Polling;
|
---|
| 336 | }
|
---|
| 337 | }
|
---|
| 338 | else
|
---|
| 339 | {
|
---|
[58029] | 340 | VGSvcError("WinEmptyClipbrd failed, lasterr=%lx\n", WinGetLastError(g_habWorker));
|
---|
[3655] | 341 | g_enmState = kClipboardState_Polling;
|
---|
| 342 | }
|
---|
| 343 |
|
---|
| 344 | if (g_enmState == kClipboardState_Polling)
|
---|
| 345 | {
|
---|
| 346 | g_fEmptyClipboard = true;
|
---|
[58029] | 347 | vgsvcClipboardOs2PollViewer();
|
---|
[3655] | 348 | }
|
---|
| 349 |
|
---|
| 350 | WinCloseClipbrd(g_habWorker);
|
---|
| 351 | }
|
---|
| 352 | else
|
---|
[58029] | 353 | VGSvcError("vgsvcClipboardOs2AdvertiseHostFormats: WinOpenClipbrd failed, lasterr=%lx\n", WinGetLastError(g_habWorker));
|
---|
[3655] | 354 | }
|
---|
| 355 |
|
---|
| 356 |
|
---|
[58029] | 357 | /**
|
---|
| 358 | * Converts (render) to an Odin32 clipboard format.
|
---|
| 359 | *
|
---|
| 360 | * We ASSUME we get windows data from the host and all we've got to do here is
|
---|
| 361 | * slapping an Odin32 header on it.
|
---|
| 362 | *
|
---|
| 363 | * @returns Pointer to the data (DosFreeMem).
|
---|
| 364 | * @param fFormat The host format.
|
---|
| 365 | * @param usFmt The PM/Odin32 format.
|
---|
| 366 | * @param pv The data in host formatting.
|
---|
| 367 | * @param cb The size of the data.
|
---|
| 368 | */
|
---|
| 369 | static void *vgsvcClipboardOs2ConvertToOdin32(uint32_t fFormat, USHORT usFmt, void *pv, uint32_t cb)
|
---|
[3655] | 370 | {
|
---|
| 371 | PVOID pvPM = NULL;
|
---|
| 372 | APIRET rc = DosAllocSharedMem(&pvPM, NULL, cb + sizeof(CLIPHEADER), OBJ_GIVEABLE | OBJ_GETTABLE | OBJ_TILE | PAG_READ | PAG_WRITE | PAG_COMMIT);
|
---|
| 373 | if (rc)
|
---|
| 374 | {
|
---|
| 375 | PCLIPHEADER pHdr = (PCLIPHEADER)pvPM;
|
---|
| 376 | memcpy(pHdr->achMagic, CLIPHEADER_MAGIC, sizeof(pHdr->achMagic));
|
---|
| 377 | pHdr->cbData = cb;
|
---|
| 378 | if (usFmt == g_atomOdin32UnicodeText)
|
---|
| 379 | pHdr->uFormat = usFmt;
|
---|
| 380 | else
|
---|
| 381 | AssertFailed();
|
---|
| 382 | memcpy(pHdr + 1, pv, cb);
|
---|
| 383 | }
|
---|
| 384 | else
|
---|
| 385 | {
|
---|
[58029] | 386 | VGSvcError("DosAllocSharedMem(,,%#x,,) -> %ld\n", cb + sizeof(CLIPHEADER), rc);
|
---|
[3655] | 387 | pvPM = NULL;
|
---|
| 388 | }
|
---|
| 389 | return pvPM;
|
---|
| 390 | }
|
---|
| 391 |
|
---|
| 392 |
|
---|
[58029] | 393 | /**
|
---|
| 394 | * Converts (render) to a PM clipboard format.
|
---|
| 395 | *
|
---|
| 396 | * @returns Pointer to the data (DosFreeMem).
|
---|
| 397 | * @param fFormat The host format.
|
---|
| 398 | * @param usFmt The PM/Odin32 format.
|
---|
| 399 | * @param pv The data in host formatting.
|
---|
| 400 | * @param cb The size of the data.
|
---|
| 401 | */
|
---|
| 402 | static void *vgsvcClipboardOs2ConvertToPM(uint32_t fFormat, USHORT usFmt, void *pv, uint32_t cb)
|
---|
[3655] | 403 | {
|
---|
| 404 | void *pvPM = NULL;
|
---|
| 405 |
|
---|
| 406 | /*
|
---|
[8565] | 407 | * The Odin32 stuff is simple, we just assume windows data from the host
|
---|
[3655] | 408 | * and all we need to do is add the header.
|
---|
| 409 | */
|
---|
[8565] | 410 | if ( usFmt
|
---|
[3655] | 411 | && ( usFmt == g_atomOdin32UnicodeText
|
---|
| 412 | /* || usFmt == ...*/
|
---|
| 413 | )
|
---|
| 414 | )
|
---|
[58029] | 415 | pvPM = vgsvcClipboardOs2ConvertToOdin32(fFormat, usFmt, pv, cb);
|
---|
[3655] | 416 | else if (usFmt == CF_TEXT)
|
---|
| 417 | {
|
---|
| 418 | /*
|
---|
| 419 | * Convert the unicode text to the current ctype locale.
|
---|
| 420 | *
|
---|
| 421 | * Note that we probably should be using the current PM or DOS codepage
|
---|
[8565] | 422 | * here instead of the LC_CTYPE one which iconv uses by default.
|
---|
[3655] | 423 | * -lazybird
|
---|
| 424 | */
|
---|
[80847] | 425 | Assert(fFormat & VBOX_SHCL_FMT_UNICODETEXT);
|
---|
[3655] | 426 | char *pszUtf8;
|
---|
| 427 | int rc = RTUtf16ToUtf8((PCRTUTF16)pv, &pszUtf8);
|
---|
| 428 | if (RT_SUCCESS(rc))
|
---|
| 429 | {
|
---|
| 430 | char *pszLocale;
|
---|
| 431 | rc = RTStrUtf8ToCurrentCP(&pszLocale, pszUtf8);
|
---|
| 432 | if (RT_SUCCESS(rc))
|
---|
| 433 | {
|
---|
| 434 | size_t cbPM = strlen(pszLocale) + 1;
|
---|
[33395] | 435 | APIRET orc = DosAllocSharedMem(&pvPM, NULL, cbPM, OBJ_GIVEABLE | OBJ_GETTABLE | OBJ_TILE | PAG_READ | PAG_WRITE | PAG_COMMIT);
|
---|
| 436 | if (orc == NO_ERROR)
|
---|
[3655] | 437 | memcpy(pvPM, pszLocale, cbPM);
|
---|
| 438 | else
|
---|
| 439 | {
|
---|
[58029] | 440 | VGSvcError("DosAllocSharedMem(,,%#x,,) -> %ld\n", cb + sizeof(CLIPHEADER), orc);
|
---|
[3655] | 441 | pvPM = NULL;
|
---|
| 442 | }
|
---|
| 443 | RTStrFree(pszLocale);
|
---|
| 444 | }
|
---|
| 445 | else
|
---|
[58029] | 446 | VGSvcError("RTStrUtf8ToCurrentCP() -> %Rrc\n", rc);
|
---|
[3655] | 447 | RTStrFree(pszUtf8);
|
---|
| 448 | }
|
---|
| 449 | else
|
---|
[58029] | 450 | VGSvcError("RTUtf16ToUtf8() -> %Rrc\n", rc);
|
---|
[3655] | 451 | }
|
---|
[8565] | 452 |
|
---|
[3655] | 453 | return pvPM;
|
---|
| 454 | }
|
---|
| 455 |
|
---|
| 456 |
|
---|
| 457 | /**
|
---|
| 458 | * Tries to deliver an advertised host format.
|
---|
[8565] | 459 | *
|
---|
[3655] | 460 | * @param usFmt The PM format name.
|
---|
[8565] | 461 | *
|
---|
| 462 | * @remark We must not try open the clipboard here because WM_RENDERFMT is a
|
---|
| 463 | * request send synchronously by someone who has already opened the
|
---|
[3655] | 464 | * clipboard. We would enter a deadlock trying to open it here.
|
---|
| 465 | */
|
---|
[58029] | 466 | static void vgsvcClipboardOs2RenderFormat(USHORT usFmt)
|
---|
[3655] | 467 | {
|
---|
| 468 | bool fSucceeded = false;
|
---|
| 469 |
|
---|
| 470 | /*
|
---|
[33540] | 471 | * Determine which format.
|
---|
[3655] | 472 | */
|
---|
| 473 | uint32_t fFormat;
|
---|
| 474 | if ( usFmt == CF_TEXT
|
---|
| 475 | || usFmt == g_atomOdin32UnicodeText)
|
---|
[80847] | 476 | fFormat = VBOX_SHCL_FMT_UNICODETEXT;
|
---|
[3655] | 477 | else /** @todo bitmaps */
|
---|
| 478 | fFormat = 0;
|
---|
| 479 | if (fFormat)
|
---|
| 480 | {
|
---|
[8565] | 481 | /*
|
---|
[3655] | 482 | * Query the data from the host.
|
---|
| 483 | * This might require two iterations because of buffer guessing.
|
---|
| 484 | */
|
---|
| 485 | uint32_t cb = _4K;
|
---|
[28317] | 486 | uint32_t cbAllocated = cb;
|
---|
| 487 | int rc = VERR_NO_MEMORY;
|
---|
| 488 | void *pv = RTMemPageAllocZ(cbAllocated);
|
---|
[3655] | 489 | if (pv)
|
---|
| 490 | {
|
---|
[58029] | 491 | VGSvcVerbose(4, "clipboard: reading host data (%#x)\n", fFormat);
|
---|
[3655] | 492 | rc = VbglR3ClipboardReadData(g_u32ClientId, fFormat, pv, cb, &cb);
|
---|
| 493 | if (rc == VINF_BUFFER_OVERFLOW)
|
---|
| 494 | {
|
---|
[28317] | 495 | RTMemPageFree(pv, cbAllocated);
|
---|
| 496 | cbAllocated = cb = RT_ALIGN_32(cb, PAGE_SIZE);
|
---|
| 497 | pv = RTMemPageAllocZ(cbAllocated);
|
---|
[3655] | 498 | rc = VbglR3ClipboardReadData(g_u32ClientId, fFormat, pv, cb, &cb);
|
---|
| 499 | }
|
---|
| 500 | if (RT_FAILURE(rc))
|
---|
[28317] | 501 | RTMemPageFree(pv, cbAllocated);
|
---|
[3655] | 502 | }
|
---|
| 503 | if (RT_SUCCESS(rc))
|
---|
| 504 | {
|
---|
[58029] | 505 | VGSvcVerbose(4, "clipboard: read %u bytes\n", cb);
|
---|
[3655] | 506 |
|
---|
| 507 | /*
|
---|
| 508 | * Convert the host clipboard data to PM clipboard data and set it.
|
---|
| 509 | */
|
---|
[58029] | 510 | PVOID pvPM = vgsvcClipboardOs2ConvertToPM(fFormat, usFmt, pv, cb);
|
---|
[3655] | 511 | if (pvPM)
|
---|
| 512 | {
|
---|
| 513 | if (WinSetClipbrdData(g_habWorker, (ULONG)pvPM, usFmt, CFI_POINTER))
|
---|
| 514 | fSucceeded = true;
|
---|
| 515 | else
|
---|
| 516 | {
|
---|
[58029] | 517 | VGSvcError("vgsvcClipboardOs2RenderFormat: WinSetClipbrdData(,%p,%#x, CF_POINTER) failed, lasterror=%lx\n",
|
---|
[58052] | 518 | pvPM, usFmt, WinGetLastError(g_habWorker));
|
---|
[3655] | 519 | DosFreeMem(pvPM);
|
---|
| 520 | }
|
---|
| 521 | }
|
---|
[28317] | 522 | RTMemPageFree(pv, cbAllocated);
|
---|
[3655] | 523 | }
|
---|
| 524 | else
|
---|
[58029] | 525 | VGSvcError("vgsvcClipboardOs2RenderFormat: Failed to query / allocate data. rc=%Rrc cb=%#RX32\n", rc, cb);
|
---|
[3655] | 526 | }
|
---|
| 527 |
|
---|
| 528 | /*
|
---|
| 529 | * Empty the clipboard on failure so we don't end up in any loops.
|
---|
| 530 | */
|
---|
| 531 | if (!fSucceeded)
|
---|
| 532 | {
|
---|
| 533 | WinSetClipbrdOwner(g_habWorker, NULLHANDLE);
|
---|
| 534 | g_enmState = kClipboardState_Destroying;
|
---|
| 535 | WinEmptyClipbrd(g_habWorker);
|
---|
| 536 | g_enmState = kClipboardState_Polling;
|
---|
| 537 | g_fEmptyClipboard = true;
|
---|
[58029] | 538 | vgsvcClipboardOs2PollViewer();
|
---|
[3655] | 539 | }
|
---|
| 540 | }
|
---|
| 541 |
|
---|
| 542 |
|
---|
[58029] | 543 | /**
|
---|
| 544 | * Sends data to the host.
|
---|
| 545 | *
|
---|
| 546 | * @param fFormat The data format the host is requesting.
|
---|
| 547 | */
|
---|
| 548 | static void vgsvcClipboardOs2SendDataToHost(uint32_t fFormat)
|
---|
[3655] | 549 | {
|
---|
| 550 | if (WinOpenClipbrd(g_habWorker))
|
---|
| 551 | {
|
---|
| 552 | PRTUTF16 pwszFree = NULL;
|
---|
| 553 | void *pv = NULL;
|
---|
| 554 | uint32_t cb = 0;
|
---|
| 555 |
|
---|
[80847] | 556 | if (fFormat & VBOX_SHCL_FMT_UNICODETEXT)
|
---|
[3655] | 557 | {
|
---|
| 558 | /* Got any odin32 unicode text? */
|
---|
| 559 | PVOID pvPM;
|
---|
| 560 | PCLIPHEADER pHdr = (PCLIPHEADER)WinQueryClipbrdData(g_habWorker, g_atomOdin32UnicodeText);
|
---|
[8565] | 561 | if ( pHdr
|
---|
[3655] | 562 | && !memcmp(pHdr->achMagic, CLIPHEADER_MAGIC, sizeof(pHdr->achMagic)))
|
---|
| 563 | {
|
---|
| 564 | pv = pHdr + 1;
|
---|
| 565 | cb = pHdr->cbData;
|
---|
| 566 | }
|
---|
| 567 |
|
---|
| 568 | /* Got any CF_TEXT? */
|
---|
| 569 | if ( !pv
|
---|
| 570 | && (pvPM = (PVOID)WinQueryClipbrdData(g_habWorker, CF_TEXT)) != NULL)
|
---|
| 571 | {
|
---|
| 572 | char *pszUtf8;
|
---|
| 573 | int rc = RTStrCurrentCPToUtf8(&pszUtf8, (const char *)pvPM);
|
---|
| 574 | if (RT_SUCCESS(rc))
|
---|
| 575 | {
|
---|
| 576 | PRTUTF16 pwsz;
|
---|
| 577 | rc = RTStrToUtf16(pszUtf8, &pwsz);
|
---|
| 578 | if (RT_SUCCESS(rc))
|
---|
| 579 | {
|
---|
| 580 | pv = pwszFree = pwsz;
|
---|
| 581 | cb = (RTUtf16Len(pwsz) + 1) * sizeof(RTUTF16);
|
---|
| 582 | }
|
---|
| 583 | RTStrFree(pszUtf8);
|
---|
| 584 | }
|
---|
| 585 | }
|
---|
| 586 | }
|
---|
| 587 | if (!pv)
|
---|
[58029] | 588 | VGSvcError("vgsvcClipboardOs2SendDataToHost: couldn't find data for %#x\n", fFormat);
|
---|
[3655] | 589 |
|
---|
| 590 | /*
|
---|
| 591 | * Now, sent whatever we've got to the host (it's waiting).
|
---|
| 592 | */
|
---|
[58029] | 593 | VGSvcVerbose(4, "clipboard: writing %pv/%#d (fFormat=%#x)\n", pv, cb, fFormat);
|
---|
[3655] | 594 | VbglR3ClipboardWriteData(g_u32ClientId, fFormat, pv, cb);
|
---|
| 595 | RTUtf16Free(pwszFree);
|
---|
| 596 |
|
---|
| 597 | WinCloseClipbrd(g_habWorker);
|
---|
| 598 | }
|
---|
| 599 | else
|
---|
| 600 | {
|
---|
[58029] | 601 | VGSvcError("vgsvcClipboardOs2SendDataToHost: WinOpenClipbrd failed, lasterr=%lx\n", WinGetLastError(g_habWorker));
|
---|
| 602 | VGSvcVerbose(4, "clipboard: writing NULL/0 (fFormat=%x)\n", fFormat);
|
---|
[3655] | 603 | VbglR3ClipboardWriteData(g_u32ClientId, fFormat, NULL, 0);
|
---|
| 604 | }
|
---|
| 605 | }
|
---|
| 606 |
|
---|
| 607 |
|
---|
| 608 | /**
|
---|
| 609 | * Figure out what's on the clipboard and report it to the host.
|
---|
| 610 | */
|
---|
[58029] | 611 | static void vgsvcClipboardOs2ReportFormats(void)
|
---|
[3655] | 612 | {
|
---|
| 613 | uint32_t fFormats = 0;
|
---|
| 614 | ULONG ulFormat = 0;
|
---|
| 615 | while ((ulFormat = WinEnumClipbrdFmts(g_habWorker, ulFormat)) != 0)
|
---|
| 616 | {
|
---|
[8565] | 617 | if ( ulFormat == CF_TEXT
|
---|
[3655] | 618 | || ulFormat == g_atomOdin32UnicodeText)
|
---|
[80847] | 619 | fFormats |= VBOX_SHCL_FMT_UNICODETEXT;
|
---|
[3655] | 620 | /** @todo else bitmaps and stuff. */
|
---|
| 621 | }
|
---|
[58029] | 622 | VGSvcVerbose(4, "clipboard: reporting fFormats=%#x\n", fFormats);
|
---|
[82514] | 623 | VbglR3ClipboardReportFormats(g_u32ClientId, fFormats);
|
---|
[3655] | 624 | }
|
---|
| 625 |
|
---|
| 626 |
|
---|
| 627 | /**
|
---|
| 628 | * Poll the clipboard for changes.
|
---|
[8565] | 629 | *
|
---|
| 630 | * This is called both when we're the viewer and when we're
|
---|
[3655] | 631 | * falling back to polling. If something has changed it will
|
---|
| 632 | * notify the host.
|
---|
| 633 | */
|
---|
[58029] | 634 | static void vgsvcClipboardOs2Poll(void)
|
---|
[3655] | 635 | {
|
---|
| 636 | if (WinOpenClipbrd(g_habWorker))
|
---|
| 637 | {
|
---|
| 638 | /*
|
---|
| 639 | * If our dummy is no longer there, something has actually changed,
|
---|
| 640 | * unless the clipboard is really empty.
|
---|
| 641 | */
|
---|
| 642 | ULONG fFmtInfo;
|
---|
| 643 | if (!WinQueryClipbrdFmtInfo(g_habWorker, g_atomNothingChanged, &fFmtInfo))
|
---|
| 644 | {
|
---|
| 645 | if (WinEnumClipbrdFmts(g_habWorker, 0) != 0)
|
---|
| 646 | {
|
---|
| 647 | g_fEmptyClipboard = false;
|
---|
[58029] | 648 | vgsvcClipboardOs2ReportFormats();
|
---|
[8565] | 649 |
|
---|
[3655] | 650 | /* inject the dummy */
|
---|
| 651 | PVOID pv;
|
---|
| 652 | APIRET rc = DosAllocSharedMem(&pv, NULL, 1, OBJ_GIVEABLE | OBJ_GETTABLE | PAG_READ | PAG_WRITE | PAG_COMMIT);
|
---|
| 653 | if (rc == NO_ERROR)
|
---|
| 654 | {
|
---|
| 655 | if (WinSetClipbrdData(g_habWorker, (ULONG)pv, g_atomNothingChanged, CFI_POINTER))
|
---|
[58029] | 656 | VGSvcVerbose(4, "clipboard: Added dummy item.\n");
|
---|
[3655] | 657 | else
|
---|
| 658 | {
|
---|
[58029] | 659 | VGSvcError("vgsvcClipboardOs2Poll: WinSetClipbrdData failed, lasterr=%#lx\n", WinGetLastError(g_habWorker));
|
---|
[3655] | 660 | DosFreeMem(pv);
|
---|
| 661 | }
|
---|
| 662 | }
|
---|
| 663 | else
|
---|
[58029] | 664 | VGSvcError("vgsvcClipboardOs2Poll: DosAllocSharedMem(,,1,) -> %ld\n", rc);
|
---|
[3655] | 665 | }
|
---|
| 666 | else if (!g_fEmptyClipboard)
|
---|
| 667 | {
|
---|
| 668 | g_fEmptyClipboard = true;
|
---|
[58029] | 669 | VGSvcVerbose(3, "Reporting empty clipboard\n");
|
---|
[82514] | 670 | VbglR3ClipboardReportFormats(g_u32ClientId, 0);
|
---|
[3655] | 671 | }
|
---|
| 672 | }
|
---|
| 673 | WinCloseClipbrd(g_habWorker);
|
---|
| 674 | }
|
---|
| 675 | else
|
---|
[58029] | 676 | VGSvcError("vgsvcClipboardOs2Poll: WinOpenClipbrd failed, lasterr=%lx\n", WinGetLastError(g_habWorker));
|
---|
[3655] | 677 | }
|
---|
| 678 |
|
---|
| 679 |
|
---|
| 680 | /**
|
---|
| 681 | * The clipboard we owned was destroyed by someone else.
|
---|
| 682 | */
|
---|
[58029] | 683 | static void vgsvcClipboardOs2Destroyed(void)
|
---|
[3655] | 684 | {
|
---|
| 685 | /* make sure we're no longer the owner. */
|
---|
| 686 | if (WinQueryClipbrdOwner(g_habWorker) == g_hwndWorker)
|
---|
| 687 | WinSetClipbrdOwner(g_habWorker, NULLHANDLE);
|
---|
| 688 |
|
---|
| 689 | /* switch to polling state and notify the host. */
|
---|
| 690 | g_enmState = kClipboardState_Polling;
|
---|
| 691 | g_fEmptyClipboard = true;
|
---|
[58029] | 692 | VGSvcVerbose(3, "Reporting empty clipboard\n");
|
---|
[82514] | 693 | VbglR3ClipboardReportFormats(g_u32ClientId, 0);
|
---|
[3655] | 694 |
|
---|
[58029] | 695 | vgsvcClipboardOs2PollViewer();
|
---|
[3655] | 696 | }
|
---|
| 697 |
|
---|
| 698 |
|
---|
| 699 | /**
|
---|
| 700 | * The window procedure for the object window.
|
---|
[8565] | 701 | *
|
---|
[3655] | 702 | * @returns Message result.
|
---|
[8565] | 703 | *
|
---|
[3655] | 704 | * @param hwnd The window handle.
|
---|
| 705 | * @param msg The message.
|
---|
| 706 | * @param mp1 Message parameter 1.
|
---|
| 707 | * @param mp2 Message parameter 2.
|
---|
| 708 | */
|
---|
[58029] | 709 | static MRESULT EXPENTRY vgsvcClipboardOs2WinProc(HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2)
|
---|
[3655] | 710 | {
|
---|
| 711 | if (msg != WM_TIMER)
|
---|
[58029] | 712 | VGSvcVerbose(6, "vgsvcClipboardOs2WinProc: hwnd=%#lx msg=%#lx mp1=%#lx mp2=%#lx\n", hwnd, msg, mp1, mp2);
|
---|
[3655] | 713 |
|
---|
| 714 | switch (msg)
|
---|
| 715 | {
|
---|
| 716 | /*
|
---|
| 717 | * Handle the two system defined messages for object windows.
|
---|
| 718 | *
|
---|
| 719 | * We'll just use the CREATE/DESTROY message to create that timer we're
|
---|
| 720 | * using for the viewer checks and polling fallback.
|
---|
| 721 | */
|
---|
| 722 | case WM_CREATE:
|
---|
| 723 | g_idWorkerTimer = WinStartTimer(g_habWorker, hwnd, 1 /* id */, 1000 /* 1 second */);
|
---|
| 724 | g_fEmptyClipboard = true;
|
---|
| 725 | g_enmState = kClipboardState_Polling;
|
---|
| 726 | return NULL; /* FALSE(/NULL) == Continue*/
|
---|
| 727 |
|
---|
| 728 | case WM_DESTROY:
|
---|
| 729 | WinStopTimer(g_habWorker, hwnd, g_idWorkerTimer);
|
---|
| 730 | g_idWorkerTimer = ~0UL;
|
---|
| 731 | g_hwndWorker = NULLHANDLE;
|
---|
| 732 | break;
|
---|
| 733 |
|
---|
[8565] | 734 | /*
|
---|
[3655] | 735 | * Clipboard viewer message - the content has been changed.
|
---|
[8565] | 736 | * This is sent *after* releasing the clipboard sem
|
---|
[3655] | 737 | * and during the WinSetClipbrdViewer call.
|
---|
| 738 | */
|
---|
| 739 | case WM_DRAWCLIPBOARD:
|
---|
| 740 | if (g_enmState == kClipboardState_SettingViewer)
|
---|
| 741 | break;
|
---|
[8580] | 742 | AssertMsgBreak(g_enmState == kClipboardState_Viewer, ("g_enmState=%d\n", g_enmState));
|
---|
[58029] | 743 | vgsvcClipboardOs2Poll();
|
---|
[3655] | 744 | break;
|
---|
| 745 |
|
---|
[8565] | 746 | /*
|
---|
[3655] | 747 | * Clipboard owner message - the content was replaced.
|
---|
| 748 | * This is sent by someone with an open clipboard, so don't try open it now.
|
---|
| 749 | */
|
---|
| 750 | case WM_DESTROYCLIPBOARD:
|
---|
| 751 | if (g_enmState == kClipboardState_Destroying)
|
---|
| 752 | break; /* it's us doing the replacing, ignore. */
|
---|
[8580] | 753 | AssertMsgBreak(g_enmState == kClipboardState_Owner, ("g_enmState=%d\n", g_enmState));
|
---|
[58029] | 754 | vgsvcClipboardOs2Destroyed();
|
---|
[3655] | 755 | break;
|
---|
| 756 |
|
---|
| 757 | /*
|
---|
| 758 | * Clipboard owner message - somebody is requesting us to render a format.
|
---|
| 759 | * This is called by someone which owns the clipboard, but that's fine.
|
---|
| 760 | */
|
---|
| 761 | case WM_RENDERFMT:
|
---|
[8580] | 762 | AssertMsgBreak(g_enmState == kClipboardState_Owner, ("g_enmState=%d\n", g_enmState));
|
---|
[58029] | 763 | vgsvcClipboardOs2RenderFormat(SHORT1FROMMP(mp1));
|
---|
[3655] | 764 | break;
|
---|
| 765 |
|
---|
| 766 | /*
|
---|
[8565] | 767 | * Clipboard owner message - we're about to quit and should render all formats.
|
---|
[3655] | 768 | *
|
---|
[8565] | 769 | * However, because we're lazy, we'll just ASSUME that since we're quitting
|
---|
| 770 | * we're probably about to shutdown or something and there is no point in
|
---|
| 771 | * doing anything here except for emptying the clipboard and removing
|
---|
[3655] | 772 | * ourselves as owner. Any failures at this point are silently ignored.
|
---|
| 773 | */
|
---|
| 774 | case WM_RENDERALLFMTS:
|
---|
| 775 | WinOpenClipbrd(g_habWorker);
|
---|
| 776 | WinSetClipbrdOwner(g_habWorker, NULLHANDLE);
|
---|
| 777 | g_enmState = kClipboardState_Destroying;
|
---|
| 778 | WinEmptyClipbrd(g_habWorker);
|
---|
| 779 | g_enmState = kClipboardState_Polling;
|
---|
| 780 | g_fEmptyClipboard = true;
|
---|
| 781 | WinCloseClipbrd(g_habWorker);
|
---|
| 782 | break;
|
---|
| 783 |
|
---|
| 784 | /*
|
---|
| 785 | * Listener message - the host has new formats to offer.
|
---|
| 786 | */
|
---|
[80847] | 787 | case WM_USER + VBOX_SHCL_HOST_MSG_FORMATS_REPORT:
|
---|
[58029] | 788 | vgsvcClipboardOs2AdvertiseHostFormats(LONGFROMMP(mp1));
|
---|
[3655] | 789 | break;
|
---|
| 790 |
|
---|
| 791 | /*
|
---|
| 792 | * Listener message - the host wish to read our clipboard data.
|
---|
| 793 | */
|
---|
[80847] | 794 | case WM_USER + VBOX_SHCL_HOST_MSG_READ_DATA:
|
---|
[58029] | 795 | vgsvcClipboardOs2SendDataToHost(LONGFROMMP(mp1));
|
---|
[3655] | 796 | break;
|
---|
| 797 |
|
---|
| 798 | /*
|
---|
[8565] | 799 | * This is just a fallback polling strategy in case some other
|
---|
[3655] | 800 | * app is trying to view the clipboard too. We also use this
|
---|
| 801 | * to try recover from errors.
|
---|
| 802 | *
|
---|
| 803 | * Because the way the clipboard service works, we have to monitor
|
---|
| 804 | * it all the time and cannot get away with simpler solutions like
|
---|
| 805 | * synergy is employing (basically checking upon entering and leaving
|
---|
| 806 | * a desktop).
|
---|
| 807 | */
|
---|
| 808 | case WM_TIMER:
|
---|
[8565] | 809 | if ( g_enmState != kClipboardState_Viewer
|
---|
[3655] | 810 | && g_enmState != kClipboardState_Polling)
|
---|
| 811 | break;
|
---|
| 812 |
|
---|
[33540] | 813 | /* Lost the position as clipboard viewer?*/
|
---|
[3655] | 814 | if (g_enmState == kClipboardState_Viewer)
|
---|
| 815 | {
|
---|
| 816 | if (WinQueryClipbrdViewer(g_habWorker) == hwnd)
|
---|
| 817 | break;
|
---|
| 818 | g_enmState = kClipboardState_Polling;
|
---|
| 819 | }
|
---|
| 820 |
|
---|
| 821 | /* poll for changes */
|
---|
[58029] | 822 | vgsvcClipboardOs2Poll();
|
---|
| 823 | vgsvcClipboardOs2PollViewer();
|
---|
[3655] | 824 | break;
|
---|
| 825 |
|
---|
| 826 |
|
---|
[8565] | 827 | /*
|
---|
[3655] | 828 | * Clipboard owner messages dealing with owner drawn content.
|
---|
| 829 | * We shouldn't be seeing any of these.
|
---|
| 830 | */
|
---|
| 831 | case WM_PAINTCLIPBOARD:
|
---|
| 832 | case WM_SIZECLIPBOARD:
|
---|
| 833 | case WM_HSCROLLCLIPBOARD:
|
---|
| 834 | case WM_VSCROLLCLIPBOARD:
|
---|
| 835 | AssertMsgFailed(("msg=%lx (%ld)\n", msg, msg));
|
---|
| 836 | break;
|
---|
| 837 |
|
---|
| 838 | /*
|
---|
| 839 | * We shouldn't be seeing any other messages according to the docs.
|
---|
[8565] | 840 | * But for whatever reason, PM sends us a WM_ADJUSTWINDOWPOS message
|
---|
[3655] | 841 | * during WinCreateWindow. So, ignore that and assert on anything else.
|
---|
| 842 | */
|
---|
| 843 | default:
|
---|
| 844 | AssertMsgFailed(("msg=%lx (%ld)\n", msg, msg));
|
---|
| 845 | case WM_ADJUSTWINDOWPOS:
|
---|
| 846 | break;
|
---|
| 847 | }
|
---|
| 848 | return NULL;
|
---|
| 849 | }
|
---|
| 850 |
|
---|
| 851 |
|
---|
| 852 | /**
|
---|
| 853 | * The listener thread.
|
---|
[8565] | 854 | *
|
---|
| 855 | * This thread is dedicated to listening for host messages and forwarding
|
---|
[3655] | 856 | * these to the worker thread (using PM).
|
---|
[8565] | 857 | *
|
---|
[3655] | 858 | * The thread will set g_fListenerOkay and signal its user event when it has
|
---|
| 859 | * completed initialization. In the case of init failure g_fListenerOkay will
|
---|
| 860 | * not be set.
|
---|
[8565] | 861 | *
|
---|
[3655] | 862 | * @returns Init error code or VINF_SUCCESS.
|
---|
| 863 | * @param ThreadSelf Our thread handle.
|
---|
| 864 | * @param pvUser Pointer to the clipboard service shutdown indicator.
|
---|
| 865 | */
|
---|
[58029] | 866 | static DECLCALLBACK(int) vgsvcClipboardOs2Listener(RTTHREAD ThreadSelf, void *pvUser)
|
---|
[3655] | 867 | {
|
---|
| 868 | bool volatile *pfShutdown = (bool volatile *)pvUser;
|
---|
| 869 | int rc = VERR_GENERAL_FAILURE;
|
---|
[58029] | 870 | VGSvcVerbose(3, "vgsvcClipboardOs2Listener: ThreadSelf=%RTthrd\n", ThreadSelf);
|
---|
[3655] | 871 |
|
---|
| 872 | g_habListener = WinInitialize(0);
|
---|
| 873 | if (g_habListener != NULLHANDLE)
|
---|
| 874 | {
|
---|
| 875 | g_hmqListener = WinCreateMsgQueue(g_habListener, 0);
|
---|
| 876 | if (g_hmqListener != NULLHANDLE)
|
---|
| 877 | {
|
---|
[43581] | 878 | WinCancelShutdown(g_hmqListener, TRUE); /* We don't care about shutdown */
|
---|
| 879 |
|
---|
[3655] | 880 | /*
|
---|
| 881 | * Tell the worker thread that we're good.
|
---|
| 882 | */
|
---|
| 883 | rc = VINF_SUCCESS;
|
---|
| 884 | ASMAtomicXchgBool(&g_fListenerOkay, true);
|
---|
| 885 | RTThreadUserSignal(ThreadSelf);
|
---|
[58029] | 886 | VGSvcVerbose(3, "vgsvcClipboardOs2Listener: Started successfully\n");
|
---|
[3655] | 887 |
|
---|
| 888 | /*
|
---|
| 889 | * Loop until termination is requested.
|
---|
| 890 | */
|
---|
| 891 | bool fQuit = false;
|
---|
| 892 | while (!*pfShutdown && !fQuit)
|
---|
| 893 | {
|
---|
| 894 | uint32_t Msg;
|
---|
| 895 | uint32_t fFormats;
|
---|
[79497] | 896 | rc = VbglR3ClipboardGetHostMsgOld(g_u32ClientId, &Msg, &fFormats);
|
---|
[3655] | 897 | if (RT_SUCCESS(rc))
|
---|
[8565] | 898 | {
|
---|
[58029] | 899 | VGSvcVerbose(3, "vgsvcClipboardOs2Listener: Msg=%#x fFormats=%#x\n", Msg, fFormats);
|
---|
[3655] | 900 | switch (Msg)
|
---|
| 901 | {
|
---|
[8565] | 902 | /*
|
---|
| 903 | * The host has announced available clipboard formats.
|
---|
[3655] | 904 | * Forward the information to the window, so it can later
|
---|
| 905 | * respond do WM_RENDERFORMAT message.
|
---|
| 906 | */
|
---|
[80847] | 907 | case VBOX_SHCL_HOST_MSG_FORMATS_REPORT:
|
---|
| 908 | if (!WinPostMsg(g_hwndWorker, WM_USER + VBOX_SHCL_HOST_MSG_FORMATS_REPORT,
|
---|
[3655] | 909 | MPFROMLONG(fFormats), 0))
|
---|
[58029] | 910 | VGSvcError("WinPostMsg(%lx, FORMATS,,) failed, lasterr=%#lx\n",
|
---|
[58052] | 911 | g_hwndWorker, WinGetLastError(g_habListener));
|
---|
[3655] | 912 | break;
|
---|
| 913 |
|
---|
[8565] | 914 | /*
|
---|
[3655] | 915 | * The host needs data in the specified format.
|
---|
| 916 | */
|
---|
[80847] | 917 | case VBOX_SHCL_HOST_MSG_READ_DATA:
|
---|
| 918 | if (!WinPostMsg(g_hwndWorker, WM_USER + VBOX_SHCL_HOST_MSG_READ_DATA,
|
---|
[3655] | 919 | MPFROMLONG(fFormats), 0))
|
---|
[58029] | 920 | VGSvcError("WinPostMsg(%lx, READ_DATA,,) failed, lasterr=%#lx\n",
|
---|
[58052] | 921 | g_hwndWorker, WinGetLastError(g_habListener));
|
---|
[3655] | 922 | break;
|
---|
| 923 |
|
---|
| 924 | /*
|
---|
| 925 | * The host is terminating.
|
---|
| 926 | */
|
---|
[80847] | 927 | case VBOX_SHCL_HOST_MSG_QUIT:
|
---|
[3655] | 928 | fQuit = true;
|
---|
| 929 | break;
|
---|
| 930 |
|
---|
| 931 | default:
|
---|
[58029] | 932 | VGSvcVerbose(1, "vgsvcClipboardOs2Listener: Unknown message %RU32\n", Msg);
|
---|
[3655] | 933 | break;
|
---|
| 934 | }
|
---|
| 935 | }
|
---|
| 936 | else
|
---|
| 937 | {
|
---|
| 938 | if (*pfShutdown)
|
---|
| 939 | break;
|
---|
[58029] | 940 | VGSvcError("VbglR3ClipboardGetHostMsg failed, rc=%Rrc\n", rc);
|
---|
[3655] | 941 | RTThreadSleep(1000);
|
---|
| 942 | }
|
---|
| 943 | } /* the loop */
|
---|
| 944 |
|
---|
| 945 | WinDestroyMsgQueue(g_hmqListener);
|
---|
| 946 | }
|
---|
| 947 | WinTerminate(g_habListener);
|
---|
| 948 | g_habListener = NULLHANDLE;
|
---|
| 949 | }
|
---|
| 950 |
|
---|
| 951 | /* Signal our semaphore to make the worker catch on. */
|
---|
| 952 | RTThreadUserSignal(ThreadSelf);
|
---|
[58029] | 953 | VGSvcVerbose(3, "vgsvcClipboardOs2Listener: terminating, rc=%Rrc\n", rc);
|
---|
[3655] | 954 | return rc;
|
---|
| 955 | }
|
---|
| 956 |
|
---|
| 957 |
|
---|
[58029] | 958 | /**
|
---|
| 959 | * @interface_method_impl{VBOXSERVICE,pfnWorker}
|
---|
| 960 | */
|
---|
| 961 | static DECLCALLBACK(int) vgsvcClipboardOs2Worker(bool volatile *pfShutdown)
|
---|
[3655] | 962 | {
|
---|
| 963 | int rc = VERR_GENERAL_FAILURE;
|
---|
| 964 |
|
---|
| 965 | /*
|
---|
| 966 | * Standard PM init.
|
---|
| 967 | */
|
---|
| 968 | g_habWorker = RTThreadSelf() != g_ThreadCtrl ? WinInitialize(0) : g_habCtrl;
|
---|
| 969 | if (g_habWorker != NULLHANDLE)
|
---|
| 970 | {
|
---|
| 971 | g_hmqWorker = RTThreadSelf() != g_ThreadCtrl ? WinCreateMsgQueue(g_habWorker, 0) : g_hmqCtrl;
|
---|
| 972 | if (g_hmqWorker != NULLHANDLE)
|
---|
| 973 | {
|
---|
[43581] | 974 | if (g_hmqWorker != g_hmqCtrl)
|
---|
| 975 | WinCancelShutdown(g_hmqWorker, TRUE); /* We don't care about shutdown */
|
---|
| 976 |
|
---|
[3655] | 977 | /*
|
---|
| 978 | * Create the object window.
|
---|
| 979 | */
|
---|
[58029] | 980 | if (WinRegisterClass(g_habWorker, (PCSZ)"VBoxServiceClipboardClass", vgsvcClipboardOs2WinProc, 0, 0))
|
---|
[3655] | 981 | {
|
---|
| 982 | g_hwndWorker = WinCreateWindow(HWND_OBJECT, /* hwndParent */
|
---|
| 983 | (PCSZ)"VBoxServiceClipboardClass", /* pszClass */
|
---|
[4377] | 984 | (PCSZ)"VirtualBox Clipboard Service", /* pszName */
|
---|
[3655] | 985 | 0, /* flStyle */
|
---|
| 986 | 0, 0, 0, 0, /* x, y, cx, cy */
|
---|
| 987 | NULLHANDLE, /* hwndOwner */
|
---|
| 988 | HWND_BOTTOM, /* hwndInsertBehind */
|
---|
| 989 | 42, /* id */
|
---|
| 990 | NULL, /* pCtlData */
|
---|
| 991 | NULL); /* pPresParams */
|
---|
| 992 | if (g_hwndWorker != NULLHANDLE)
|
---|
| 993 | {
|
---|
[58029] | 994 | VGSvcVerbose(3, "g_hwndWorker=%#lx g_habWorker=%#lx g_hmqWorker=%#lx\n", g_hwndWorker, g_habWorker, g_hmqWorker);
|
---|
[3655] | 995 |
|
---|
| 996 | /*
|
---|
| 997 | * Create the listener thread.
|
---|
| 998 | */
|
---|
| 999 | g_fListenerOkay = false;
|
---|
[58029] | 1000 | rc = RTThreadCreate(&g_ThreadListener, vgsvcClipboardOs2Listener, (void *)pfShutdown, 0,
|
---|
[3655] | 1001 | RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE, "CLIPLISTEN");
|
---|
| 1002 | if (RT_SUCCESS(rc))
|
---|
| 1003 | {
|
---|
| 1004 | RTThreadUserWait(g_ThreadListener, 30*1000);
|
---|
| 1005 | RTThreadUserReset(g_ThreadListener);
|
---|
| 1006 | if (!g_fListenerOkay)
|
---|
| 1007 | RTThreadWait(g_ThreadListener, 60*1000, NULL);
|
---|
| 1008 | if (g_fListenerOkay)
|
---|
| 1009 | {
|
---|
| 1010 | /*
|
---|
[8565] | 1011 | * Tell the control thread that it can continue
|
---|
[3655] | 1012 | * spawning services.
|
---|
| 1013 | */
|
---|
| 1014 | RTThreadUserSignal(RTThreadSelf());
|
---|
| 1015 |
|
---|
| 1016 | /*
|
---|
| 1017 | * The PM event pump.
|
---|
| 1018 | */
|
---|
[58029] | 1019 | VGSvcVerbose(2, "clipboard: Entering PM message loop.\n");
|
---|
[3655] | 1020 | rc = VINF_SUCCESS;
|
---|
| 1021 | QMSG qmsg;
|
---|
| 1022 | while (WinGetMsg(g_habWorker, &qmsg, NULLHANDLE, NULLHANDLE, 0))
|
---|
[43581] | 1023 | {
|
---|
| 1024 | if (qmsg.msg != WM_TIMER)
|
---|
[58029] | 1025 | VGSvcVerbose(6, "WinGetMsg -> hwnd=%p msg=%#x mp1=%p mp2=%p time=%#x ptl=%d,%d rsrv=%#x\n",
|
---|
[58052] | 1026 | qmsg.hwnd, qmsg.msg, qmsg.mp1, qmsg.mp2, qmsg.time, qmsg.ptl.x, qmsg.ptl.y, qmsg.reserved);
|
---|
[3655] | 1027 | WinDispatchMsg(g_habWorker, &qmsg);
|
---|
[43581] | 1028 | }
|
---|
[58029] | 1029 | VGSvcVerbose(2, "clipboard: Exited PM message loop. *pfShutdown=%RTbool\n", *pfShutdown);
|
---|
[3655] | 1030 |
|
---|
| 1031 | RTThreadWait(g_ThreadListener, 60*1000, NULL);
|
---|
| 1032 | }
|
---|
| 1033 | g_ThreadListener = NIL_RTTHREAD;
|
---|
| 1034 | }
|
---|
| 1035 |
|
---|
| 1036 | /*
|
---|
| 1037 | * Got a WM_QUIT, clean up.
|
---|
| 1038 | */
|
---|
| 1039 | if (g_hwndWorker != NULLHANDLE)
|
---|
| 1040 | {
|
---|
| 1041 | WinDestroyWindow(g_hwndWorker);
|
---|
| 1042 | g_hwndWorker = NULLHANDLE;
|
---|
| 1043 | }
|
---|
| 1044 | }
|
---|
| 1045 | else
|
---|
[58029] | 1046 | VGSvcError("WinCreateWindow() failed, lasterr=%lx\n", WinGetLastError(g_habWorker));
|
---|
[3655] | 1047 | /* no class deregistration in PM. */
|
---|
| 1048 | }
|
---|
| 1049 | else
|
---|
[58029] | 1050 | VGSvcError("WinRegisterClass() failed, lasterr=%lx\n", WinGetLastError(g_habWorker));
|
---|
[3655] | 1051 |
|
---|
| 1052 | if (g_hmqCtrl != g_hmqWorker)
|
---|
| 1053 | WinDestroyMsgQueue(g_hmqWorker);
|
---|
| 1054 | g_hmqWorker = NULLHANDLE;
|
---|
| 1055 | }
|
---|
| 1056 | else
|
---|
[58029] | 1057 | VGSvcError("WinCreateMsgQueue(,0) failed, lasterr=%lx\n", WinGetLastError(g_habWorker));
|
---|
[3655] | 1058 |
|
---|
| 1059 | if (g_habCtrl != g_habWorker)
|
---|
| 1060 | WinTerminate(g_habWorker);
|
---|
| 1061 | g_habWorker = NULLHANDLE;
|
---|
| 1062 | }
|
---|
| 1063 | else
|
---|
[58029] | 1064 | VGSvcError("WinInitialize(0) failed, lasterr=%lx\n", WinGetLastError(NULLHANDLE));
|
---|
[3655] | 1065 |
|
---|
| 1066 | return rc;
|
---|
| 1067 | }
|
---|
| 1068 |
|
---|
| 1069 |
|
---|
[58029] | 1070 | /**
|
---|
| 1071 | * @interface_method_impl{VBOXSERVICE,pfnStop}
|
---|
| 1072 | */
|
---|
| 1073 | static DECLCALLBACK(void) vgsvcClipboardOs2Stop(void)
|
---|
[3655] | 1074 | {
|
---|
| 1075 | if ( g_hmqWorker != NULLHANDLE
|
---|
| 1076 | && !WinPostQueueMsg(g_hmqWorker, WM_QUIT, NULL, NULL))
|
---|
[58029] | 1077 | VGSvcError("WinPostQueueMsg(g_hmqWorker, WM_QUIT, 0,0) failed, lasterr=%lx\n", WinGetLastError(g_habCtrl));
|
---|
[43581] | 1078 |
|
---|
| 1079 | /* Must disconnect the clipboard here otherwise the listner won't quit and
|
---|
| 1080 | the service shutdown will not stop. */
|
---|
| 1081 | if (g_u32ClientId != 0)
|
---|
| 1082 | {
|
---|
| 1083 | if (g_hmqWorker != NULLHANDLE)
|
---|
| 1084 | RTThreadSleep(32); /* fudge */
|
---|
| 1085 |
|
---|
[58029] | 1086 | VGSvcVerbose(4, "clipboard: disconnecting %#x\n", g_u32ClientId);
|
---|
[43581] | 1087 | int rc = VbglR3ClipboardDisconnect(g_u32ClientId);
|
---|
| 1088 | if (RT_SUCCESS(rc))
|
---|
| 1089 | g_u32ClientId = 0;
|
---|
| 1090 | else
|
---|
[58029] | 1091 | VGSvcError("clipboard: VbglR3ClipboardDisconnect(%#x) -> %Rrc\n", g_u32ClientId, rc);
|
---|
[43581] | 1092 | }
|
---|
[3655] | 1093 | }
|
---|
| 1094 |
|
---|
| 1095 |
|
---|
[58029] | 1096 | /**
|
---|
| 1097 | * @interface_method_impl{VBOXSERVICE,pfnTerm}
|
---|
| 1098 | */
|
---|
| 1099 | static DECLCALLBACK(void) vgsvcClipboardOs2Term(void)
|
---|
[3655] | 1100 | {
|
---|
[43581] | 1101 | if (g_u32ClientId != 0)
|
---|
| 1102 | {
|
---|
[58029] | 1103 | VGSvcVerbose(4, "clipboard: disconnecting %#x\n", g_u32ClientId);
|
---|
[43581] | 1104 | int rc = VbglR3ClipboardDisconnect(g_u32ClientId);
|
---|
| 1105 | if (RT_SUCCESS(rc))
|
---|
| 1106 | g_u32ClientId = 0;
|
---|
| 1107 | else
|
---|
[58029] | 1108 | VGSvcError("clipboard: VbglR3ClipboardDisconnect(%#x) -> %Rrc\n", g_u32ClientId, rc);
|
---|
[43581] | 1109 | }
|
---|
[3655] | 1110 | WinDestroyMsgQueue(g_hmqCtrl);
|
---|
| 1111 | g_hmqCtrl = NULLHANDLE;
|
---|
| 1112 | WinTerminate(g_habCtrl);
|
---|
| 1113 | g_habCtrl = NULLHANDLE;
|
---|
| 1114 | }
|
---|
| 1115 |
|
---|
| 1116 |
|
---|
| 1117 | /**
|
---|
| 1118 | * The OS/2 'clipboard' service description.
|
---|
| 1119 | */
|
---|
[8565] | 1120 | VBOXSERVICE g_Clipboard =
|
---|
[3655] | 1121 | {
|
---|
| 1122 | /* pszName. */
|
---|
| 1123 | "clipboard",
|
---|
| 1124 | /* pszDescription. */
|
---|
| 1125 | "Shared Clipboard",
|
---|
| 1126 | /* pszUsage. */
|
---|
| 1127 | ""
|
---|
| 1128 | ,
|
---|
| 1129 | /* pszOptions. */
|
---|
| 1130 | ""
|
---|
| 1131 | ,
|
---|
| 1132 | /* methods */
|
---|
[58029] | 1133 | vgsvcClipboardOs2PreInit,
|
---|
| 1134 | vgsvcClipboardOs2Option,
|
---|
| 1135 | vgsvcClipboardOs2Init,
|
---|
| 1136 | vgsvcClipboardOs2Worker,
|
---|
| 1137 | vgsvcClipboardOs2Stop,
|
---|
| 1138 | vgsvcClipboardOs2Term
|
---|
[3655] | 1139 | };
|
---|
| 1140 |
|
---|