VirtualBox

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

Last change on this file since 4071 was 4071, checked in by vboxsync, 17 years ago

Biggest check-in ever. New source code headers for all (C) innotek files.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 37.2 KB
Line 
1/** $Id: VBoxServiceClipboard-os2.cpp 4071 2007-08-07 17:07:59Z vboxsync $ */
2/** @file
3 * VBoxService - Guest Additions Clipboard Service, OS/2.
4 */
5
6/*
7 * Copyright (C) 2007 innotek GmbH
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 as published by the Free Software Foundation,
13 * in version 2 as it comes in the "COPYING" file of the VirtualBox OSE
14 * distribution. VirtualBox OSE is distributed in the hope that it will
15 * 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/VBoxGuest.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.org Clipboard Service");
171 LONG lLastError = WinGetLastError(g_habCtrl);
172 if (g_atomNothingChanged == 0)
173 g_atomNothingChanged = WinFindAtom(WinQuerySystemAtomTable(), (PCSZ)"VirtualBox.org 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 rc = DosAllocSharedMem(&pvPM, NULL, cbPM, OBJ_GIVEABLE | OBJ_GETTABLE | OBJ_TILE | PAG_READ | PAG_WRITE | PAG_COMMIT);
370 if (rc == NO_ERROR)
371 memcpy(pvPM, pszLocale, cbPM);
372 else
373 {
374 VBoxServiceError("DosAllocSharedMem(,,%#x,,) -> %ld\n", cb + sizeof(CLIPHEADER), rc);
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 * Determin 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 int rc = VERR_NO_MEMORY;
422 void *pv = RTMemPageAllocZ(cb);
423 if (pv)
424 {
425 VBoxServiceVerbose(4, "clipboard: reading host data (%#x)\n", fFormat);
426 rc = VbglR3ClipboardReadData(g_u32ClientId, fFormat, pv, cb, &cb);
427 if (rc == VINF_BUFFER_OVERFLOW)
428 {
429 RTMemPageFree(pv);
430 cb = RT_ALIGN_32(cb, PAGE_SIZE);
431 pv = RTMemPageAllocZ(cb);
432 rc = VbglR3ClipboardReadData(g_u32ClientId, fFormat, pv, cb, &cb);
433 }
434 if (RT_FAILURE(rc))
435 RTMemPageFree(pv);
436 }
437 if (RT_SUCCESS(rc))
438 {
439 VBoxServiceVerbose(4, "clipboard: read %u bytes\n", cb);
440
441 /*
442 * Convert the host clipboard data to PM clipboard data and set it.
443 */
444 PVOID pvPM = VBoxServiceClipboardOs2ConvertToPM(fFormat, usFmt, pv, cb);
445 if (pvPM)
446 {
447 if (WinSetClipbrdData(g_habWorker, (ULONG)pvPM, usFmt, CFI_POINTER))
448 fSucceeded = true;
449 else
450 {
451 VBoxServiceError("VBoxServiceClipboardOS2RenderFormat: WinSetClipbrdData(,%p,%#x, CF_POINTER) failed, lasterror=%lx\n",
452 pvPM, usFmt, WinGetLastError(g_habWorker));
453 DosFreeMem(pvPM);
454 }
455 }
456 RTMemPageFree(pv);
457 }
458 else
459 VBoxServiceError("VBoxServiceClipboardOS2RenderFormat: Failed to query / allocate data. rc=%Rrc cb=%#RX32\n", rc, cb);
460 }
461
462 /*
463 * Empty the clipboard on failure so we don't end up in any loops.
464 */
465 if (!fSucceeded)
466 {
467 WinSetClipbrdOwner(g_habWorker, NULLHANDLE);
468 g_enmState = kClipboardState_Destroying;
469 WinEmptyClipbrd(g_habWorker);
470 g_enmState = kClipboardState_Polling;
471 g_fEmptyClipboard = true;
472 VBoxServiceClipboardOS2PollViewer();
473 }
474}
475
476
477static void VBoxServiceClipboardOS2SendDataToHost(uint32_t fFormat)
478{
479 if (WinOpenClipbrd(g_habWorker))
480 {
481 PRTUTF16 pwszFree = NULL;
482 void *pv = NULL;
483 uint32_t cb = 0;
484
485 if (fFormat & VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT)
486 {
487 /* Got any odin32 unicode text? */
488 PVOID pvPM;
489 PCLIPHEADER pHdr = (PCLIPHEADER)WinQueryClipbrdData(g_habWorker, g_atomOdin32UnicodeText);
490 if ( pHdr
491 && !memcmp(pHdr->achMagic, CLIPHEADER_MAGIC, sizeof(pHdr->achMagic)))
492 {
493 pv = pHdr + 1;
494 cb = pHdr->cbData;
495 }
496
497 /* Got any CF_TEXT? */
498 if ( !pv
499 && (pvPM = (PVOID)WinQueryClipbrdData(g_habWorker, CF_TEXT)) != NULL)
500 {
501 char *pszUtf8;
502 int rc = RTStrCurrentCPToUtf8(&pszUtf8, (const char *)pvPM);
503 if (RT_SUCCESS(rc))
504 {
505 PRTUTF16 pwsz;
506 rc = RTStrToUtf16(pszUtf8, &pwsz);
507 if (RT_SUCCESS(rc))
508 {
509 pv = pwszFree = pwsz;
510 cb = (RTUtf16Len(pwsz) + 1) * sizeof(RTUTF16);
511 }
512 RTStrFree(pszUtf8);
513 }
514 }
515 }
516 if (!pv)
517 VBoxServiceError("VBoxServiceClipboardOS2SendDataToHost: couldn't find data for %#x\n", fFormat);
518
519 /*
520 * Now, sent whatever we've got to the host (it's waiting).
521 */
522 VBoxServiceVerbose(4, "clipboard: writing %pv/%#d (fFormat=%#x)\n", pv, cb, fFormat);
523 VbglR3ClipboardWriteData(g_u32ClientId, fFormat, pv, cb);
524 RTUtf16Free(pwszFree);
525
526 WinCloseClipbrd(g_habWorker);
527 }
528 else
529 {
530 VBoxServiceError("VBoxServiceClipboardOS2SendDataToHost: WinOpenClipbrd failed, lasterr=%lx\n", WinGetLastError(g_habWorker));
531 VBoxServiceVerbose(4, "clipboard: writing NULL/0 (fFormat=%x)\n", fFormat);
532 VbglR3ClipboardWriteData(g_u32ClientId, fFormat, NULL, 0);
533 }
534}
535
536
537/**
538 * Figure out what's on the clipboard and report it to the host.
539 */
540static void VBoxServiceClipboardOS2ReportFormats(void)
541{
542 uint32_t fFormats = 0;
543 ULONG ulFormat = 0;
544 while ((ulFormat = WinEnumClipbrdFmts(g_habWorker, ulFormat)) != 0)
545 {
546 if ( ulFormat == CF_TEXT
547 || ulFormat == g_atomOdin32UnicodeText)
548 fFormats |= VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT;
549 /** @todo else bitmaps and stuff. */
550 }
551 VBoxServiceVerbose(4, "clipboard: reporting fFormats=%#x\n", fFormats);
552 VbglR3ClipboardReportFormats(g_u32ClientId, fFormats);
553}
554
555
556/**
557 * Poll the clipboard for changes.
558 *
559 * This is called both when we're the viewer and when we're
560 * falling back to polling. If something has changed it will
561 * notify the host.
562 */
563static void VBoxServiceClipboardOS2Poll(void)
564{
565 if (WinOpenClipbrd(g_habWorker))
566 {
567 /*
568 * If our dummy is no longer there, something has actually changed,
569 * unless the clipboard is really empty.
570 */
571 ULONG fFmtInfo;
572 if (!WinQueryClipbrdFmtInfo(g_habWorker, g_atomNothingChanged, &fFmtInfo))
573 {
574 if (WinEnumClipbrdFmts(g_habWorker, 0) != 0)
575 {
576 g_fEmptyClipboard = false;
577 VBoxServiceClipboardOS2ReportFormats();
578
579 /* inject the dummy */
580 PVOID pv;
581 APIRET rc = DosAllocSharedMem(&pv, NULL, 1, OBJ_GIVEABLE | OBJ_GETTABLE | PAG_READ | PAG_WRITE | PAG_COMMIT);
582 if (rc == NO_ERROR)
583 {
584 if (WinSetClipbrdData(g_habWorker, (ULONG)pv, g_atomNothingChanged, CFI_POINTER))
585 VBoxServiceVerbose(4, "clipboard: Added dummy item.\n");
586 else
587 {
588 VBoxServiceError("VBoxServiceClipboardOS2Poll: WinSetClipbrdData failed, lasterr=%#lx\n", WinGetLastError(g_habWorker));
589 DosFreeMem(pv);
590 }
591 }
592 else
593 VBoxServiceError("VBoxServiceClipboardOS2Poll: DosAllocSharedMem(,,1,) -> %ld\n", rc);
594 }
595 else if (!g_fEmptyClipboard)
596 {
597 g_fEmptyClipboard = true;
598 VBoxServiceVerbose(3, "Reporting empty clipboard\n");
599 VbglR3ClipboardReportFormats(g_u32ClientId, 0);
600 }
601 }
602 WinCloseClipbrd(g_habWorker);
603 }
604 else
605 VBoxServiceError("VBoxServiceClipboardOS2Poll: WinOpenClipbrd failed, lasterr=%lx\n", WinGetLastError(g_habWorker));
606}
607
608
609/**
610 * The clipboard we owned was destroyed by someone else.
611 */
612static void VBoxServiceClipboardOS2Destroyed(void)
613{
614 /* make sure we're no longer the owner. */
615 if (WinQueryClipbrdOwner(g_habWorker) == g_hwndWorker)
616 WinSetClipbrdOwner(g_habWorker, NULLHANDLE);
617
618 /* switch to polling state and notify the host. */
619 g_enmState = kClipboardState_Polling;
620 g_fEmptyClipboard = true;
621 VBoxServiceVerbose(3, "Reporting empty clipboard\n");
622 VbglR3ClipboardReportFormats(g_u32ClientId, 0);
623
624 VBoxServiceClipboardOS2PollViewer();
625}
626
627
628/**
629 * The window procedure for the object window.
630 *
631 * @returns Message result.
632 *
633 * @param hwnd The window handle.
634 * @param msg The message.
635 * @param mp1 Message parameter 1.
636 * @param mp2 Message parameter 2.
637 */
638static MRESULT EXPENTRY VBoxServiceClipboardOS2WinProc(HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2)
639{
640 if (msg != WM_TIMER)
641 VBoxServiceVerbose(6, "VBoxServiceClipboardOS2WinProc: hwnd=%#lx msg=%#lx mp1=%#lx mp2=%#lx\n", hwnd, msg, mp1, mp2);
642
643 switch (msg)
644 {
645 /*
646 * Handle the two system defined messages for object windows.
647 *
648 * We'll just use the CREATE/DESTROY message to create that timer we're
649 * using for the viewer checks and polling fallback.
650 */
651 case WM_CREATE:
652 g_idWorkerTimer = WinStartTimer(g_habWorker, hwnd, 1 /* id */, 1000 /* 1 second */);
653 g_fEmptyClipboard = true;
654 g_enmState = kClipboardState_Polling;
655 return NULL; /* FALSE(/NULL) == Continue*/
656
657 case WM_DESTROY:
658 WinStopTimer(g_habWorker, hwnd, g_idWorkerTimer);
659 g_idWorkerTimer = ~0UL;
660 g_hwndWorker = NULLHANDLE;
661 break;
662
663 /*
664 * Clipboard viewer message - the content has been changed.
665 * This is sent *after* releasing the clipboard sem
666 * and during the WinSetClipbrdViewer call.
667 */
668 case WM_DRAWCLIPBOARD:
669 if (g_enmState == kClipboardState_SettingViewer)
670 break;
671 AssertMsgBreak(g_enmState == kClipboardState_Viewer, ("g_enmState=%d\n", g_enmState), );
672 VBoxServiceClipboardOS2Poll();
673 break;
674
675 /*
676 * Clipboard owner message - the content was replaced.
677 * This is sent by someone with an open clipboard, so don't try open it now.
678 */
679 case WM_DESTROYCLIPBOARD:
680 if (g_enmState == kClipboardState_Destroying)
681 break; /* it's us doing the replacing, ignore. */
682 AssertMsgBreak(g_enmState == kClipboardState_Owner, ("g_enmState=%d\n", g_enmState), );
683 VBoxServiceClipboardOS2Destroyed();
684 break;
685
686 /*
687 * Clipboard owner message - somebody is requesting us to render a format.
688 * This is called by someone which owns the clipboard, but that's fine.
689 */
690 case WM_RENDERFMT:
691 AssertMsgBreak(g_enmState == kClipboardState_Owner, ("g_enmState=%d\n", g_enmState), );
692 VBoxServiceClipboardOS2RenderFormat(SHORT1FROMMP(mp1));
693 break;
694
695 /*
696 * Clipboard owner message - we're about to quit and should render all formats.
697 *
698 * However, because we're lazy, we'll just ASSUME that since we're quitting
699 * we're probably about to shutdown or something and there is no point in
700 * doing anything here except for emptying the clipboard and removing
701 * ourselves as owner. Any failures at this point are silently ignored.
702 */
703 case WM_RENDERALLFMTS:
704 WinOpenClipbrd(g_habWorker);
705 WinSetClipbrdOwner(g_habWorker, NULLHANDLE);
706 g_enmState = kClipboardState_Destroying;
707 WinEmptyClipbrd(g_habWorker);
708 g_enmState = kClipboardState_Polling;
709 g_fEmptyClipboard = true;
710 WinCloseClipbrd(g_habWorker);
711 break;
712
713 /*
714 * Listener message - the host has new formats to offer.
715 */
716 case WM_USER + VBOX_SHARED_CLIPBOARD_HOST_MSG_FORMATS:
717 VBoxServiceClipboardOS2AdvertiseHostFormats(LONGFROMMP(mp1));
718 break;
719
720 /*
721 * Listener message - the host wish to read our clipboard data.
722 */
723 case WM_USER + VBOX_SHARED_CLIPBOARD_HOST_MSG_READ_DATA:
724 VBoxServiceClipboardOS2SendDataToHost(LONGFROMMP(mp1));
725 break;
726
727 /*
728 * This is just a fallback polling strategy in case some other
729 * app is trying to view the clipboard too. We also use this
730 * to try recover from errors.
731 *
732 * Because the way the clipboard service works, we have to monitor
733 * it all the time and cannot get away with simpler solutions like
734 * synergy is employing (basically checking upon entering and leaving
735 * a desktop).
736 */
737 case WM_TIMER:
738 if ( g_enmState != kClipboardState_Viewer
739 && g_enmState != kClipboardState_Polling)
740 break;
741
742 /* Lost the position as clipboard viwer?*/
743 if (g_enmState == kClipboardState_Viewer)
744 {
745 if (WinQueryClipbrdViewer(g_habWorker) == hwnd)
746 break;
747 g_enmState = kClipboardState_Polling;
748 }
749
750 /* poll for changes */
751 VBoxServiceClipboardOS2Poll();
752 VBoxServiceClipboardOS2PollViewer();
753 break;
754
755
756 /*
757 * Clipboard owner messages dealing with owner drawn content.
758 * We shouldn't be seeing any of these.
759 */
760 case WM_PAINTCLIPBOARD:
761 case WM_SIZECLIPBOARD:
762 case WM_HSCROLLCLIPBOARD:
763 case WM_VSCROLLCLIPBOARD:
764 AssertMsgFailed(("msg=%lx (%ld)\n", msg, msg));
765 break;
766
767 /*
768 * We shouldn't be seeing any other messages according to the docs.
769 * But for whatever reason, PM sends us a WM_ADJUSTWINDOWPOS message
770 * during WinCreateWindow. So, ignore that and assert on anything else.
771 */
772 default:
773 AssertMsgFailed(("msg=%lx (%ld)\n", msg, msg));
774 case WM_ADJUSTWINDOWPOS:
775 break;
776 }
777 return NULL;
778}
779
780
781/**
782 * The listener thread.
783 *
784 * This thread is dedicated to listening for host messages and forwarding
785 * these to the worker thread (using PM).
786 *
787 * The thread will set g_fListenerOkay and signal its user event when it has
788 * completed initialization. In the case of init failure g_fListenerOkay will
789 * not be set.
790 *
791 * @returns Init error code or VINF_SUCCESS.
792 * @param ThreadSelf Our thread handle.
793 * @param pvUser Pointer to the clipboard service shutdown indicator.
794 */
795static DECLCALLBACK(int) VBoxServiceClipboardOS2Listener(RTTHREAD ThreadSelf, void *pvUser)
796{
797 bool volatile *pfShutdown = (bool volatile *)pvUser;
798 int rc = VERR_GENERAL_FAILURE;
799 VBoxServiceVerbose(3, "VBoxServiceClipboardOS2Listener: ThreadSelf=%RTthrd\n", ThreadSelf);
800
801 g_habListener = WinInitialize(0);
802 if (g_habListener != NULLHANDLE)
803 {
804 g_hmqListener = WinCreateMsgQueue(g_habListener, 0);
805 if (g_hmqListener != NULLHANDLE)
806 {
807 /*
808 * Tell the worker thread that we're good.
809 */
810 rc = VINF_SUCCESS;
811 ASMAtomicXchgBool(&g_fListenerOkay, true);
812 RTThreadUserSignal(ThreadSelf);
813 VBoxServiceVerbose(3, "VBoxServiceClipboardOS2Listener: Started successfully\n");
814
815 /*
816 * Loop until termination is requested.
817 */
818 bool fQuit = false;
819 while (!*pfShutdown && !fQuit)
820 {
821 uint32_t Msg;
822 uint32_t fFormats;
823 rc = VbglR3ClipboardGetHostMsg(g_u32ClientId, &Msg, &fFormats);
824 if (RT_SUCCESS(rc))
825 {
826 VBoxServiceVerbose(3, "VBoxServiceClipboardOS2Listener: Msg=%#x fFormats=%#x\n", Msg, fFormats);
827 switch (Msg)
828 {
829 /*
830 * The host has announced available clipboard formats.
831 * Forward the information to the window, so it can later
832 * respond do WM_RENDERFORMAT message.
833 */
834 case VBOX_SHARED_CLIPBOARD_HOST_MSG_FORMATS:
835 if (!WinPostMsg(g_hwndWorker, WM_USER + VBOX_SHARED_CLIPBOARD_HOST_MSG_FORMATS,
836 MPFROMLONG(fFormats), 0))
837 VBoxServiceError("WinPostMsg(%lx, FORMATS,,) failed, lasterr=%#lx\n",
838 g_hwndWorker, WinGetLastError(g_habListener));
839 break;
840
841 /*
842 * The host needs data in the specified format.
843 */
844 case VBOX_SHARED_CLIPBOARD_HOST_MSG_READ_DATA:
845 if (!WinPostMsg(g_hwndWorker, WM_USER + VBOX_SHARED_CLIPBOARD_HOST_MSG_READ_DATA,
846 MPFROMLONG(fFormats), 0))
847 VBoxServiceError("WinPostMsg(%lx, READ_DATA,,) failed, lasterr=%#lx\n",
848 g_hwndWorker, WinGetLastError(g_habListener));
849 break;
850
851 /*
852 * The host is terminating.
853 */
854 case VBOX_SHARED_CLIPBOARD_HOST_MSG_QUIT:
855 fQuit = true;
856 break;
857
858 default:
859 VBoxServiceVerbose(1, "VBoxServiceClipboardOS2Listener: Unknown message %RU32\n", Msg);
860 break;
861 }
862 }
863 else
864 {
865 if (*pfShutdown)
866 break;
867 VBoxServiceError("VbglR3ClipboardGetHostMsg failed, rc=%Rrc\n", rc);
868 RTThreadSleep(1000);
869 }
870 } /* the loop */
871
872 WinDestroyMsgQueue(g_hmqListener);
873 }
874 WinTerminate(g_habListener);
875 g_habListener = NULLHANDLE;
876 }
877
878 /* Signal our semaphore to make the worker catch on. */
879 RTThreadUserSignal(ThreadSelf);
880 VBoxServiceVerbose(3, "VBoxServiceClipboardOS2Listener: terminating, rc=%Rrc\n", rc);
881 return rc;
882}
883
884
885/** @copydoc VBOXSERVICE::pfnWorker */
886static DECLCALLBACK(int) VBoxServiceClipboardOS2Worker(bool volatile *pfShutdown)
887{
888 int rc = VERR_GENERAL_FAILURE;
889
890 /*
891 * Standard PM init.
892 */
893 g_habWorker = RTThreadSelf() != g_ThreadCtrl ? WinInitialize(0) : g_habCtrl;
894 if (g_habWorker != NULLHANDLE)
895 {
896 g_hmqWorker = RTThreadSelf() != g_ThreadCtrl ? WinCreateMsgQueue(g_habWorker, 0) : g_hmqCtrl;
897 if (g_hmqWorker != NULLHANDLE)
898 {
899 /*
900 * Create the object window.
901 */
902 if (WinRegisterClass(g_habWorker, (PCSZ)"VBoxServiceClipboardClass", VBoxServiceClipboardOS2WinProc, 0, 0))
903 {
904 g_hwndWorker = WinCreateWindow(HWND_OBJECT, /* hwndParent */
905 (PCSZ)"VBoxServiceClipboardClass", /* pszClass */
906 (PCSZ)"VirtualBox.org Clipboard Service",/* pszName */
907 0, /* flStyle */
908 0, 0, 0, 0, /* x, y, cx, cy */
909 NULLHANDLE, /* hwndOwner */
910 HWND_BOTTOM, /* hwndInsertBehind */
911 42, /* id */
912 NULL, /* pCtlData */
913 NULL); /* pPresParams */
914 if (g_hwndWorker != NULLHANDLE)
915 {
916 VBoxServiceVerbose(3, "g_hwndWorker=%#lx g_habWorker=%#lx g_hmqWorker=%#lx\n", g_hwndWorker, g_habWorker, g_hmqWorker);
917
918 /*
919 * Create the listener thread.
920 */
921 g_fListenerOkay = false;
922 rc = RTThreadCreate(&g_ThreadListener, VBoxServiceClipboardOS2Listener, (void *)pfShutdown, 0,
923 RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE, "CLIPLISTEN");
924 if (RT_SUCCESS(rc))
925 {
926 RTThreadUserWait(g_ThreadListener, 30*1000);
927 RTThreadUserReset(g_ThreadListener);
928 if (!g_fListenerOkay)
929 RTThreadWait(g_ThreadListener, 60*1000, NULL);
930 if (g_fListenerOkay)
931 {
932 /*
933 * Tell the control thread that it can continue
934 * spawning services.
935 */
936 RTThreadUserSignal(RTThreadSelf());
937
938 /*
939 * The PM event pump.
940 */
941 VBoxServiceVerbose(2, "clipboard: Entering PM message loop.\n");
942 rc = VINF_SUCCESS;
943 QMSG qmsg;
944 while (WinGetMsg(g_habWorker, &qmsg, NULLHANDLE, NULLHANDLE, 0))
945 WinDispatchMsg(g_habWorker, &qmsg);
946 VBoxServiceVerbose(2, "clipboard: Exited PM message loop. *pfShutdown=%RTbool\n", *pfShutdown);
947
948 RTThreadWait(g_ThreadListener, 60*1000, NULL);
949 }
950 g_ThreadListener = NIL_RTTHREAD;
951 }
952
953 /*
954 * Got a WM_QUIT, clean up.
955 */
956 if (g_hwndWorker != NULLHANDLE)
957 {
958 WinDestroyWindow(g_hwndWorker);
959 g_hwndWorker = NULLHANDLE;
960 }
961 }
962 else
963 VBoxServiceError("WinCreateWindow() failed, lasterr=%lx\n", WinGetLastError(g_habWorker));
964 /* no class deregistration in PM. */
965 }
966 else
967 VBoxServiceError("WinRegisterClass() failed, lasterr=%lx\n", WinGetLastError(g_habWorker));
968
969 if (g_hmqCtrl != g_hmqWorker)
970 WinDestroyMsgQueue(g_hmqWorker);
971 g_hmqWorker = NULLHANDLE;
972 }
973 else
974 VBoxServiceError("WinCreateMsgQueue(,0) failed, lasterr=%lx\n", WinGetLastError(g_habWorker));
975
976 if (g_habCtrl != g_habWorker)
977 WinTerminate(g_habWorker);
978 g_habWorker = NULLHANDLE;
979 }
980 else
981 VBoxServiceError("WinInitialize(0) failed, lasterr=%lx\n", WinGetLastError(NULLHANDLE));
982
983 return rc;
984}
985
986
987/** @copydoc VBOXSERVICE::pfnStop */
988static DECLCALLBACK(void) VBoxServiceClipboardOS2Stop(void)
989{
990 if ( g_hmqWorker != NULLHANDLE
991 && !WinPostQueueMsg(g_hmqWorker, WM_QUIT, NULL, NULL))
992 VBoxServiceError("WinPostQueueMsg(g_hmqWorker, WM_QUIT, 0,0) failed, lasterr=%lx\n", WinGetLastError(g_habCtrl));
993}
994
995
996/** @copydoc VBOXSERVICE::pfnTerm */
997static DECLCALLBACK(void) VBoxServiceClipboardOS2Term(void)
998{
999 VBoxServiceVerbose(4, "clipboard: disconnecting %#x\n", g_u32ClientId);
1000 VbglR3ClipboardDisconnect(g_u32ClientId);
1001 g_u32ClientId = 0;
1002 WinDestroyMsgQueue(g_hmqCtrl);
1003 g_hmqCtrl = NULLHANDLE;
1004 WinTerminate(g_habCtrl);
1005 g_habCtrl = NULLHANDLE;
1006}
1007
1008
1009/**
1010 * The OS/2 'clipboard' service description.
1011 */
1012VBOXSERVICE g_Clipboard =
1013{
1014 /* pszName. */
1015 "clipboard",
1016 /* pszDescription. */
1017 "Shared Clipboard",
1018 /* pszUsage. */
1019 ""
1020 ,
1021 /* pszOptions. */
1022 ""
1023 ,
1024 /* methods */
1025 VBoxServiceClipboardOS2PreInit,
1026 VBoxServiceClipboardOS2Option,
1027 VBoxServiceClipboardOS2Init,
1028 VBoxServiceClipboardOS2Worker,
1029 VBoxServiceClipboardOS2Stop,
1030 VBoxServiceClipboardOS2Term
1031};
1032
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