VirtualBox

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

Last change on this file since 34079 was 33540, checked in by vboxsync, 14 years ago

*: spelling fixes, thanks Timeless!

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