VirtualBox

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

Last change on this file since 40128 was 40128, checked in by vboxsync, 13 years ago

VBoxService: Fixed unknown command line parameter handling of sub-services.

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