VirtualBox

source: vbox/trunk/src/VBox/Additions/common/VBoxService/VBoxServiceClipboard-os2.cpp@ 44824

Last change on this file since 44824 was 43581, checked in by vboxsync, 12 years ago

VBoxServiceClipboard-os2.cpp: Tell PM not to try send any WM_QUIT messsages on shutdown as we might hang the shutdown process otherwise. Must disconnect the HGCM service on stop, or the listener thread will not quit and hang the shutting down of the service.

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

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette