clipboard.c 40.8 KB
Newer Older
Alexandre Julliard's avatar
Alexandre Julliard committed
1
/*
2
 * WIN32 clipboard implementation
Alexandre Julliard's avatar
Alexandre Julliard committed
3 4
 *
 * Copyright 1994 Martin Ayotte
Alexandre Julliard's avatar
Alexandre Julliard committed
5
 *	     1996 Alex Korobka
6 7
 *	     1999 Noel Borthwick
 *
8 9 10 11 12 13 14 15 16 17 18 19 20 21
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
22 23 24 25 26 27 28
 * NOTES:
 *    This file contains the implementation for the WIN32 Clipboard API
 * and Wine's internal clipboard cache.
 * The actual contents of the clipboard are held in the clipboard cache.
 * The internal implementation talks to a "clipboard driver" to fill or
 * expose the cache to the native device. (Currently only the X11 and
 * TTY clipboard  driver are available)
Alexandre Julliard's avatar
Alexandre Julliard committed
29
 */
Alexandre Julliard's avatar
Alexandre Julliard committed
30

Alexandre Julliard's avatar
Alexandre Julliard committed
31
#include <stdlib.h>
Alexandre Julliard's avatar
Alexandre Julliard committed
32 33 34
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
Alexandre Julliard's avatar
Alexandre Julliard committed
35
#include <string.h>
36

37
#include "windef.h"
38
#include "winbase.h"
39
#include "wingdi.h"
40
#include "winuser.h"
41
#include "wine/winuser16.h"
42
#include "wine/winbase16.h"
Alexandre Julliard's avatar
Alexandre Julliard committed
43
#include "heap.h"
44
#include "user.h"
45
#include "win.h"
Alexandre Julliard's avatar
Alexandre Julliard committed
46
#include "clipboard.h"
47
#include "wine/debug.h"
Alexandre Julliard's avatar
Alexandre Julliard committed
48

49
WINE_DEFAULT_DEBUG_CHANNEL(clipboard);
50

Alexandre Julliard's avatar
Alexandre Julliard committed
51 52
#define  CF_REGFORMATBASE 	0xC000

53
/**************************************************************************
54
 *	  Clipboard context global variables
Alexandre Julliard's avatar
Alexandre Julliard committed
55 56
 */

57
static HANDLE hClipLock   = 0;
58
static BOOL bCBHasChanged  = FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
59

60 61 62 63
static HWND hWndClipWindow;        /* window that last opened clipboard */
static HWND hWndClipOwner;         /* current clipboard owner */
static HANDLE16 hTaskClipOwner;    /* clipboard owner's task  */
static HWND hWndViewer;            /* start of viewers chain */
Alexandre Julliard's avatar
Alexandre Julliard committed
64 65 66

static WORD LastRegFormat = CF_REGFORMATBASE;

67
/* Clipboard cache initial data.
68
 * WARNING: This data ordering is dependent on the WINE_CLIPFORMAT structure
69 70
 * declared in clipboard.h
 */
71
WINE_CLIPFORMAT ClipFormats[]  = {
72 73 74 75 76 77 78 79 80 81 82 83
    { CF_TEXT, 1, 0, "Text",  0, 0, 0, 0, NULL, &ClipFormats[1]},
    { CF_BITMAP, 1, 0, "Bitmap", 0, 0, 0, 0, &ClipFormats[0], &ClipFormats[2]},
    { CF_METAFILEPICT, 1, 0, "MetaFile Picture", 0, 0, 0, 0, &ClipFormats[1], &ClipFormats[3]},
    { CF_SYLK, 1, 0, "Sylk", 0, 0, 0, 0, &ClipFormats[2], &ClipFormats[4]},
    { CF_DIF, 1, 0, "DIF", 0, 0, 0, 0, &ClipFormats[3], &ClipFormats[5]},
    { CF_TIFF, 1, 0, "TIFF", 0, 0, 0, 0, &ClipFormats[4], &ClipFormats[6]},
    { CF_OEMTEXT, 1, 0, "OEM Text", 0, 0, 0, 0, &ClipFormats[5], &ClipFormats[7]},
    { CF_DIB, 1, 0, "DIB", 0, 0, 0, 0, &ClipFormats[6], &ClipFormats[8]},
    { CF_PALETTE, 1, 0, "Palette", 0, 0, 0, 0, &ClipFormats[7], &ClipFormats[9]},
    { CF_PENDATA, 1, 0, "PenData", 0, 0, 0, 0, &ClipFormats[8], &ClipFormats[10]},
    { CF_RIFF, 1, 0, "RIFF", 0, 0, 0, 0, &ClipFormats[9], &ClipFormats[11]},
    { CF_WAVE, 1, 0, "Wave", 0, 0, 0, 0, &ClipFormats[10], &ClipFormats[12]},
84 85 86 87 88 89
    { CF_UNICODETEXT, 1, 0, "Unicode Text", 0, 0, 0, 0, &ClipFormats[11], &ClipFormats[13]},
    { CF_OWNERDISPLAY, 1, 0, "Owner Display", 0, 0, 0, 0, &ClipFormats[12], &ClipFormats[14]},
    { CF_DSPTEXT, 1, 0, "DSPText", 0, 0, 0, 0, &ClipFormats[13], &ClipFormats[15]},
    { CF_DSPMETAFILEPICT, 1, 0, "DSPMetaFile Picture", 0, 0, 0, 0, &ClipFormats[14], &ClipFormats[16]},
    { CF_DSPBITMAP, 1, 0, "DSPBitmap", 0, 0, 0, 0, &ClipFormats[15], &ClipFormats[17]},
    { CF_HDROP, 1, 0, "HDROP", 0, 0, 0, 0, &ClipFormats[16], NULL}
90
};
Alexandre Julliard's avatar
Alexandre Julliard committed
91

92 93 94 95 96 97 98 99 100

/**************************************************************************
 *                Internal Clipboard implementation methods
 **************************************************************************/


/**************************************************************************
 *                      CLIPBOARD_LookupFormat
 */
101
static LPWINE_CLIPFORMAT __lookup_format( LPWINE_CLIPFORMAT lpFormat, WORD wID )
Alexandre Julliard's avatar
Alexandre Julliard committed
102 103 104 105 106 107 108 109 110 111
{
    while(TRUE)
    {
	if (lpFormat == NULL ||
	    lpFormat->wFormatID == wID) break;
	lpFormat = lpFormat->NextFormat;
    }
    return lpFormat;
}

112 113 114 115 116
LPWINE_CLIPFORMAT CLIPBOARD_LookupFormat( WORD wID )
{
  return __lookup_format( ClipFormats, wID );
}

Alexandre Julliard's avatar
Alexandre Julliard committed
117
/**************************************************************************
118 119
 *                      CLIPBOARD_IsLocked
 *  Check if the clipboard cache is available to the caller
Alexandre Julliard's avatar
Alexandre Julliard committed
120
 */
121
BOOL CLIPBOARD_IsLocked()
Alexandre Julliard's avatar
Alexandre Julliard committed
122
{
123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143
  BOOL bIsLocked = TRUE;
  HANDLE16 hTaskCur = GetCurrentTask();

  /*
   * The clipboard is available:
   * 1. if the caller's task has opened the clipboard,
   * or
   * 2. if the caller is the clipboard owners task, AND is responding to a
   *    WM_RENDERFORMAT message.
   */
  if ( hClipLock == hTaskCur )
      bIsLocked = FALSE;
      
  else if ( hTaskCur == hTaskClipOwner )
  {
      /* Check if we're currently executing inside a window procedure
       * called in response to a WM_RENDERFORMAT message. A WM_RENDERFORMAT
       * handler is not permitted to open the clipboard since it has been opened
       * by another client. However the handler must have access to the
       * clipboard in order to update data in response to this message.
       */
144
#if 0
145
      MESSAGEQUEUE *queue = QUEUE_Current();
146 147 148 149 150 151 152
      
      if ( queue
           && queue->smWaiting
           && queue->smWaiting->msg == WM_RENDERFORMAT
           && queue->smWaiting->hSrcQueue
         )
  	bIsLocked = FALSE;
153 154 155 156
#else
      /* FIXME: queue check no longer possible */
      bIsLocked = FALSE;
#endif
157 158 159
  }

  return bIsLocked;
Alexandre Julliard's avatar
Alexandre Julliard committed
160 161
}

162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191
/**************************************************************************
 *                      CLIPBOARD_ReleaseOwner
 *   Gives up ownership of the clipboard
 */
void CLIPBOARD_ReleaseOwner()
{
   hWndClipOwner = 0;
   hTaskClipOwner = 0;
}

/**************************************************************************
 *                      CLIPBOARD_GlobalFreeProc
 *
 * This is a callback mechanism to allow HGLOBAL data to be released in
 * the context of the process which allocated it. We post a WM_TIMER message
 * to the owner window(in CLIPBOARD_DeleteRecord) and destroy the data(in idEvent)
 * in this WndProc, which is invoked when the apps message loop calls DispatchMessage.
 * This technique is discussed in Matt Pietrek's "Under the Hood".
 * An article describing the same may be found in MSDN by searching for WM_TIMER.
 * Note that this mechanism will probably stop working when WINE supports
 * address space separation. When "queue events" are implemented in Wine we
 * should switch to using that mechanism, since it is more robust and does not
 * require a procedure address to be passed. See the SetWinEventHook API for
 * more info on this.
 */
VOID CALLBACK CLIPBOARD_GlobalFreeProc( HWND hwnd, UINT uMsg, UINT idEvent, DWORD dwTime )
{
    /* idEvent is the HGLOBAL to be deleted */
    GlobalFree( (HGLOBAL)idEvent );
}
Alexandre Julliard's avatar
Alexandre Julliard committed
192 193 194 195

/**************************************************************************
 *			CLIPBOARD_DeleteRecord
 */
196
void CLIPBOARD_DeleteRecord(LPWINE_CLIPFORMAT lpFormat, BOOL bChange)
Alexandre Julliard's avatar
Alexandre Julliard committed
197
{
Alexandre Julliard's avatar
Alexandre Julliard committed
198
    if( (lpFormat->wFormatID >= CF_GDIOBJFIRST &&
199 200
	 lpFormat->wFormatID <= CF_GDIOBJLAST) || lpFormat->wFormatID == CF_BITMAP 
	    || lpFormat->wFormatID == CF_PALETTE)
Alexandre Julliard's avatar
Alexandre Julliard committed
201
    {
Pascal Cuoq's avatar
Pascal Cuoq committed
202
      if (lpFormat->hData32)
203
	DeleteObject(lpFormat->hData32);
Pascal Cuoq's avatar
Pascal Cuoq committed
204
      if (lpFormat->hData16)
205
	DeleteObject(lpFormat->hData16);
Pascal Cuoq's avatar
Pascal Cuoq committed
206 207 208 209 210
    }
    else if( lpFormat->wFormatID == CF_METAFILEPICT )
    {
      if (lpFormat->hData32)
      {
211
        DeleteMetaFile( ((METAFILEPICT *)GlobalLock( lpFormat->hData32 ))->hMF );
212 213
        PostMessageA(hWndClipOwner, WM_TIMER,
                     (WPARAM)lpFormat->hData32, (LPARAM)CLIPBOARD_GlobalFreeProc);
214
        if (lpFormat->hDataSrc32)
215 216 217 218 219 220 221 222 223
        {
          /* Release lpFormat->hData32 in the context of the process which created it.
           * See CLIPBOARD_GlobalFreeProc for more details about this technique.
           * GlobalFree(lpFormat->hDataSrc32);
           */
          PostMessageA(hWndClipOwner, WM_TIMER,
                       (WPARAM)lpFormat->hDataSrc32, (LPARAM)CLIPBOARD_GlobalFreeProc);
        }
          
Pascal Cuoq's avatar
Pascal Cuoq committed
224 225 226 227 228 229 230
	if (lpFormat->hData16)
	  /* HMETAFILE16 and HMETAFILE32 are apparently the same thing, 
	     and a shallow copy is enough to share a METAFILEPICT
	     structure between 16bit and 32bit clipboards.  The MetaFile
	     should of course only be deleted once. */
	  GlobalFree16(lpFormat->hData16);
      }
231
      if (lpFormat->hData16)
Pascal Cuoq's avatar
Pascal Cuoq committed
232 233 234 235 236 237 238 239
      {
	DeleteMetaFile16( ((METAFILEPICT16 *)GlobalLock16( lpFormat->hData16 ))->hMF );
	GlobalFree16(lpFormat->hData16);
      }
    }
    else 
    {
      if (lpFormat->hData32)
240 241 242 243 244 245 246 247
      {
        /* Release lpFormat->hData32 in the context of the process which created it.
         * See CLIPBOARD_GlobalFreeProc for more details about this technique.
         * GlobalFree( lpFormat->hData32 );
         */
        PostMessageA(hWndClipOwner, WM_TIMER,
                     (WPARAM)lpFormat->hData32, (LPARAM)CLIPBOARD_GlobalFreeProc);
      }
248
      if (lpFormat->hDataSrc32)
249 250 251 252 253 254 255 256
      {
        /* Release lpFormat->hData32 in the context of the process which created it.
         * See CLIPBOARD_GlobalFreeProc for more details about this technique.
         * GlobalFree(lpFormat->hDataSrc32);
         */
        PostMessageA(hWndClipOwner, WM_TIMER,
                     (WPARAM)lpFormat->hDataSrc32, (LPARAM)CLIPBOARD_GlobalFreeProc);
      }
Pascal Cuoq's avatar
Pascal Cuoq committed
257 258
      if (lpFormat->hData16)
	GlobalFree16(lpFormat->hData16);
Alexandre Julliard's avatar
Alexandre Julliard committed
259
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
260

Alexandre Julliard's avatar
Alexandre Julliard committed
261
    lpFormat->wDataPresent = 0; 
Pascal Cuoq's avatar
Pascal Cuoq committed
262 263
    lpFormat->hData16 = 0;
    lpFormat->hData32 = 0;
264 265
    lpFormat->hDataSrc32 = 0;
    lpFormat->drvData = 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
266

Alexandre Julliard's avatar
Alexandre Julliard committed
267
    if( bChange ) bCBHasChanged = TRUE;
Alexandre Julliard's avatar
Alexandre Julliard committed
268 269
}

270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285
/**************************************************************************
 *			CLIPBOARD_EmptyCache
 */
void CLIPBOARD_EmptyCache( BOOL bChange )
{
    LPWINE_CLIPFORMAT lpFormat = ClipFormats; 

    while(lpFormat)
    {
	if ( lpFormat->wDataPresent || lpFormat->hData16 || lpFormat->hData32 )
	     CLIPBOARD_DeleteRecord( lpFormat, bChange );

	lpFormat = lpFormat->NextFormat;
    }
}

Alexandre Julliard's avatar
Alexandre Julliard committed
286 287 288
/**************************************************************************
 *			CLIPBOARD_IsPresent
 */
289
BOOL CLIPBOARD_IsPresent(WORD wFormat)
Alexandre Julliard's avatar
Alexandre Julliard committed
290
{
Alexandre Julliard's avatar
Alexandre Julliard committed
291 292
    /* special case */

293
    if( wFormat == CF_TEXT || wFormat == CF_OEMTEXT || wFormat == CF_UNICODETEXT )
Alexandre Julliard's avatar
Alexandre Julliard committed
294
        return ClipFormats[CF_TEXT-1].wDataPresent ||
295 296
	       ClipFormats[CF_OEMTEXT-1].wDataPresent ||
	       ClipFormats[CF_UNICODETEXT-1].wDataPresent;
Alexandre Julliard's avatar
Alexandre Julliard committed
297 298
    else
    {
299
	LPWINE_CLIPFORMAT lpFormat = __lookup_format( ClipFormats, wFormat );
Alexandre Julliard's avatar
Alexandre Julliard committed
300 301 302
	if( lpFormat ) return (lpFormat->wDataPresent);
    }
    return FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
303 304
}

305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328
/**************************************************************************
 *			CLIPBOARD_IsCacheRendered
 *  Checks if any data needs to be rendered to the clipboard cache
 *  RETURNS:
 *    TRUE  - All clipboard data is available in the cache
 *    FALSE - Some data is marked for delayed render and needs rendering
 */
BOOL CLIPBOARD_IsCacheRendered()
{
    LPWINE_CLIPFORMAT lpFormat = ClipFormats;
    
    /* check if all formats were rendered */
    while(lpFormat)
    {
        if( lpFormat->wDataPresent && !lpFormat->hData16 && !lpFormat->hData32 )
            return FALSE;
        
        lpFormat = lpFormat->NextFormat;
    }
    
    return TRUE;
}


329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385
/**************************************************************************
 *			CLIPBOARD_IsMemoryObject
 *  Tests if the clipboard format specifies a memory object
 */
BOOL CLIPBOARD_IsMemoryObject( WORD wFormat )
{
    switch(wFormat)
    {
        case CF_BITMAP:
        case CF_METAFILEPICT:
        case CF_DSPTEXT:
        case CF_ENHMETAFILE:
        case CF_HDROP:
        case CF_PALETTE:
        case CF_PENDATA:
            return FALSE;
        default:
            return TRUE;
     }
}

/***********************************************************************
 * CLIPBOARD_GlobalDupMem( HGLOBAL )
 * Helper method to duplicate an HGLOBAL chunk of memory into shared memory
 */
HGLOBAL CLIPBOARD_GlobalDupMem( HGLOBAL hGlobalSrc )
{
    HGLOBAL hGlobalDest;
    PVOID pGlobalSrc, pGlobalDest;
    DWORD cBytes;
    
    if ( !hGlobalSrc )
      return 0;

    cBytes = GlobalSize(hGlobalSrc);
    if ( 0 == cBytes )
      return 0;

    /* Turn on the DDESHARE and _MOVEABLE flags explicitly */
    hGlobalDest = GlobalAlloc( GlobalFlags(hGlobalSrc) | GMEM_DDESHARE | GMEM_MOVEABLE,
                               cBytes );
    if ( !hGlobalDest )
      return 0;
    
    pGlobalSrc = GlobalLock(hGlobalSrc);
    pGlobalDest = GlobalLock(hGlobalDest);
    if ( !pGlobalSrc || !pGlobalDest )
      return 0;

    memcpy(pGlobalDest, pGlobalSrc, cBytes);
        
    GlobalUnlock(hGlobalSrc);
    GlobalUnlock(hGlobalDest);

    return hGlobalDest;
}

386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403
/**************************************************************************
 *			CLIPBOARD_GetFormatName
 *  Gets the format name associated with an ID
 */
char * CLIPBOARD_GetFormatName(UINT wFormat)
{
    LPWINE_CLIPFORMAT lpFormat = __lookup_format( ClipFormats, wFormat );
    return (lpFormat) ? lpFormat->Name : NULL;
}


/**************************************************************************
 *                      CLIPBOARD_RenderFormat
 */
static BOOL CLIPBOARD_RenderFormat(LPWINE_CLIPFORMAT lpFormat)
{
  /*
   * If WINE is not the selection owner, and the format is available
404
   * we must ask the driver to render the data to the clipboard cache.
405
   */
406
  TRACE("enter format=%d\n", lpFormat->wFormatID);
407 408
  if ( !USER_Driver.pIsSelectionOwner() 
       && USER_Driver.pIsClipboardFormatAvailable( lpFormat->wFormatID ) )
409
  {
410
    if ( !USER_Driver.pGetClipboardData( lpFormat->wFormatID ) )
411 412 413 414 415 416 417 418 419 420 421 422 423 424
      return FALSE;
  }
  /*
   * If Wine owns the clipboard, and the data is marked for delayed render,
   * render it now.
   */
  else if( lpFormat->wDataPresent && !lpFormat->hData16 && !lpFormat->hData32 )
  {
    if( IsWindow(hWndClipOwner) )
    {
      /* Send a WM_RENDERFORMAT message to notify the owner to render the
       * data requested into the clipboard.
       */
      TRACE("Sending WM_RENDERFORMAT message\n");
425
      SendMessageW( hWndClipOwner, WM_RENDERFORMAT, (WPARAM)lpFormat->wFormatID, 0 );
426 427 428 429 430 431 432 433 434 435 436 437 438 439
    }
    else
    {
      WARN("\thWndClipOwner (%04x) is lost!\n", 
	   hWndClipOwner);
      CLIPBOARD_ReleaseOwner();
      lpFormat->wDataPresent = 0;
      return FALSE;
    }
  }

  return (lpFormat->hData16 || lpFormat->hData32) ? TRUE : FALSE;
}

440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491
/**************************************************************************
 *                      CLIPBOARD_ConvertText
 * Returns number of required/converted characters - not bytes!
 */
static INT CLIPBOARD_ConvertText(WORD src_fmt, void const *src, INT src_size,
				 WORD dst_fmt, void *dst, INT dst_size)
{
    UINT cp;

    if(src_fmt == CF_UNICODETEXT)
    {
	switch(dst_fmt)
	{
	case CF_TEXT:
	    cp = CP_ACP;
	    break;
	case CF_OEMTEXT:
	    cp = CP_OEMCP;
	    break;
	default:
	    return 0;
	}
	return WideCharToMultiByte(cp, 0, src, src_size, dst, dst_size, NULL, NULL);
    }

    if(dst_fmt == CF_UNICODETEXT)
    {
	switch(src_fmt)
	{
	case CF_TEXT:
	    cp = CP_ACP;
	    break;
	case CF_OEMTEXT:
	    cp = CP_OEMCP;
	    break;
	default:
	    return 0;
	}
	return MultiByteToWideChar(cp, 0, src, src_size, dst, dst_size);
    }

    if(!dst_size) return src_size;

    if(dst_size > src_size) dst_size = src_size;

    if(src_fmt == CF_TEXT )
	CharToOemBuffA(src, dst, dst_size);
    else
	OemToCharBuffA(src, dst, dst_size);

    return dst_size;
}
492 493 494 495 496 497 498 499 500 501 502 503 504 505

/**************************************************************************
 *                      CLIPBOARD_RenderText
 *
 * Renders text to the clipboard buffer converting between UNIX and DOS formats.
 *
 * RETURNS: pointer to the WINE_CLIPFORMAT if successful, NULL otherwise
 *
 * FIXME: Should be a pair of driver functions that convert between OEM text and Windows.
 *
 */
static LPWINE_CLIPFORMAT CLIPBOARD_RenderText( UINT wFormat )
{
    LPWINE_CLIPFORMAT lpSource = ClipFormats; 
506 507 508
    LPWINE_CLIPFORMAT lpTarget = NULL;
    BOOL foundData = FALSE;
 
509 510
    /* Asked for CF_TEXT */
    if( wFormat == CF_TEXT)
511
    {
512 513 514 515 516 517 518 519
        if(ClipFormats[CF_TEXT-1].wDataPresent)
        {
            lpSource = &ClipFormats[CF_TEXT-1];
            lpTarget = &ClipFormats[CF_TEXT-1];
            foundData = TRUE;
            TRACE("\t TEXT -> TEXT\n");
        }
	else if(ClipFormats[CF_UNICODETEXT-1].wDataPresent)
520 521 522 523
	{
	    /* Convert UNICODETEXT -> TEXT */
	    lpSource = &ClipFormats[CF_UNICODETEXT-1];
	    lpTarget = &ClipFormats[CF_TEXT-1];
524
            foundData = TRUE;
525 526 527 528 529 530 531
	    TRACE("\tUNICODETEXT -> TEXT\n");
	}
	else if(ClipFormats[CF_OEMTEXT-1].wDataPresent)
	{
	    /* Convert OEMTEXT -> TEXT */
	    lpSource = &ClipFormats[CF_OEMTEXT-1];
	    lpTarget = &ClipFormats[CF_TEXT-1];
532
            foundData = TRUE;
533 534
	    TRACE("\tOEMTEXT -> TEXT\n");
	}
535
    }
536 537
    /* Asked for CF_OEMTEXT  */
    else if( wFormat == CF_OEMTEXT)
538
    {
539 540 541 542 543 544 545 546
        if(ClipFormats[CF_OEMTEXT-1].wDataPresent)
        {
   	    lpSource = &ClipFormats[CF_OEMTEXT-1];
	    lpTarget = &ClipFormats[CF_OEMTEXT-1];
	    foundData = TRUE;
	    TRACE("\tOEMTEXT -> OEMTEXT\n");
        }
	else if(ClipFormats[CF_UNICODETEXT-1].wDataPresent)
547 548 549 550
	{
	    /* Convert UNICODETEXT -> OEMTEXT */
	    lpSource = &ClipFormats[CF_UNICODETEXT-1];
	    lpTarget = &ClipFormats[CF_OEMTEXT-1];
551
	    foundData = TRUE;
552 553 554 555 556 557 558
	    TRACE("\tUNICODETEXT -> OEMTEXT\n");
	}
	else if(ClipFormats[CF_TEXT-1].wDataPresent)
	{
	    /* Convert TEXT -> OEMTEXT */
	    lpSource = &ClipFormats[CF_TEXT-1];
	    lpTarget = &ClipFormats[CF_OEMTEXT-1];
559
            foundData = TRUE;
560 561 562
	    TRACE("\tTEXT -> OEMTEXT\n");
	}
    }
563 564
    /* Asked for CF_UNICODETEXT */
    else if( wFormat == CF_UNICODETEXT )
565
    {
566 567 568 569 570 571 572 573
	if(ClipFormats[CF_UNICODETEXT-1].wDataPresent)
	{
	    lpSource = &ClipFormats[CF_UNICODETEXT-1];
	    lpTarget = &ClipFormats[CF_UNICODETEXT-1];
            foundData = TRUE;
	    TRACE("\tUNICODETEXT -> UNICODETEXT\n");
	}
        else if(ClipFormats[CF_TEXT-1].wDataPresent)
574 575 576 577
	{
	    /* Convert TEXT -> UNICODETEXT */
	    lpSource = &ClipFormats[CF_TEXT-1];
	    lpTarget = &ClipFormats[CF_UNICODETEXT-1];
578
            foundData = TRUE;
579 580 581 582 583 584 585
	    TRACE("\tTEXT -> UNICODETEXT\n");
	}
	else if(ClipFormats[CF_OEMTEXT-1].wDataPresent)
	{
	    /* Convert OEMTEXT -> UNICODETEXT */
	    lpSource = &ClipFormats[CF_OEMTEXT-1];
	    lpTarget = &ClipFormats[CF_UNICODETEXT-1];
586
            foundData = TRUE;
587 588
	    TRACE("\tOEMTEXT -> UNICODETEXT\n");
	}
589
    }
590 591 592 593 594 595 596 597 598 599 600 601
    if (!foundData)
    {   
        if ((wFormat == CF_TEXT) || (wFormat == CF_OEMTEXT))
        {
            lpSource = &ClipFormats[CF_UNICODETEXT-1];
            lpTarget = __lookup_format( ClipFormats, wFormat );
        }
        else
        {
	    lpSource = __lookup_format( ClipFormats, wFormat );
	    lpTarget = lpSource;
        }
602 603 604 605 606 607 608 609
    }

    /* First render the source text format */
    if ( !lpSource || !CLIPBOARD_RenderFormat(lpSource) ) return NULL;

    /* Convert to the desired target text format, if necessary */
    if( lpTarget != lpSource && !lpTarget->hData16 && !lpTarget->hData32 )
    {
610
        INT src_chars, dst_chars, alloc_size;
611 612 613 614 615 616 617 618 619 620 621 622 623
        LPCSTR lpstrS; 
        LPSTR  lpstrT;
    
        if (lpSource->hData32)
        {
          lpstrS = (LPSTR)GlobalLock(lpSource->hData32);
        }
        else
        {
          lpstrS = (LPSTR)GlobalLock16(lpSource->hData16);
        }
    
        if( !lpstrS ) return NULL;
624 625 626

	/* Text always NULL terminated */
	if(lpSource->wFormatID == CF_UNICODETEXT)
627
	    src_chars = strlenW((LPCWSTR)lpstrS)+1;
628
	else
629
	    src_chars = strlen(lpstrS)+1;
630 631 632 633 634 635

	/* Calculate number of characters in the destination buffer */
	dst_chars = CLIPBOARD_ConvertText(lpSource->wFormatID, lpstrS, src_chars,
				     lpTarget->wFormatID, NULL, 0);
	if(!dst_chars) return NULL;

636
        TRACE("\tconverting from '%s' to '%s', %i chars\n",
637 638 639 640 641 642 643 644 645
		lpSource->Name, lpTarget->Name, src_chars);

	/* Convert characters to bytes */
	if(lpTarget->wFormatID == CF_UNICODETEXT)
	    alloc_size = dst_chars * sizeof(WCHAR);
	else
	    alloc_size = dst_chars;

        lpTarget->hData32 = GlobalAlloc(GMEM_ZEROINIT | GMEM_MOVEABLE | GMEM_DDESHARE, alloc_size);
646
        lpstrT = (LPSTR)GlobalLock(lpTarget->hData32);
647

648 649
        if( lpstrT )
        {
650 651
	    CLIPBOARD_ConvertText(lpSource->wFormatID, lpstrS, src_chars,
				  lpTarget->wFormatID, lpstrT, dst_chars);
652 653 654 655 656 657 658 659 660 661 662 663 664 665 666
            GlobalUnlock(lpTarget->hData32);
        }
        else
            lpTarget->hData32 = 0;

        /* Unlock source */
        if (lpSource->hData32)
          GlobalUnlock(lpSource->hData32);
        else
          GlobalUnlock16(lpSource->hData16);
    }

    return (lpTarget->hData16 || lpTarget->hData32) ? lpTarget : NULL;
}

667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722
/**************************************************************************
 *            CLIPBOARD_EnumClipboardFormats   (internal)
 */
static UINT CLIPBOARD_EnumClipboardFormats( UINT wFormat )
{
    LPWINE_CLIPFORMAT lpFormat = ClipFormats;
    BOOL bFormatPresent;

    if (wFormat == 0) /* start from the beginning */
	lpFormat = ClipFormats;
    else
    {
	/* walk up to the specified format record */

	if( !(lpFormat = __lookup_format( lpFormat, wFormat )) ) 
	    return 0;
	lpFormat = lpFormat->NextFormat; /* right */
    }

    while(TRUE) 
    {
	if (lpFormat == NULL) return 0;

	if(CLIPBOARD_IsPresent(lpFormat->wFormatID))
	    break;

        /* Query the driver if not yet in the cache */
        if (!USER_Driver.pIsSelectionOwner())
        {
	    if(lpFormat->wFormatID == CF_UNICODETEXT ||
	       lpFormat->wFormatID == CF_TEXT ||
	       lpFormat->wFormatID == CF_OEMTEXT)
	    {
		if(USER_Driver.pIsClipboardFormatAvailable(CF_UNICODETEXT) ||
		   USER_Driver.pIsClipboardFormatAvailable(CF_TEXT) ||
		   USER_Driver.pIsClipboardFormatAvailable(CF_OEMTEXT))
		    bFormatPresent = TRUE;
		else
		    bFormatPresent = FALSE;
	    }
	    else
		bFormatPresent = USER_Driver.pIsClipboardFormatAvailable(lpFormat->wFormatID);

	    if(bFormatPresent)
		break;
        }

	lpFormat = lpFormat->NextFormat;
    }

    TRACE("Next available format %d\n", lpFormat->wFormatID);

    return lpFormat->wFormatID;
}


723 724 725 726
/**************************************************************************
 *                WIN32 Clipboard implementation 
 **************************************************************************/

Alexandre Julliard's avatar
Alexandre Julliard committed
727
/**************************************************************************
728
 *		OpenClipboard (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
729 730
 *
 * Note: Netscape uses NULL hWnd to open the clipboard.
Alexandre Julliard's avatar
Alexandre Julliard committed
731
 */
732
BOOL WINAPI OpenClipboard( HWND hWnd )
Alexandre Julliard's avatar
Alexandre Julliard committed
733
{
734
    BOOL bRet;
Alexandre Julliard's avatar
Alexandre Julliard committed
735

736
    TRACE("(%04x)...\n", hWnd);
Alexandre Julliard's avatar
Alexandre Julliard committed
737

738
    if (!hClipLock)
Alexandre Julliard's avatar
Alexandre Julliard committed
739
    {
740 741 742
        hClipLock = GetCurrentTask();

        /* Save current user of the clipboard */
743
        hWndClipWindow = WIN_GetFullHandle( hWnd );
744 745
        bCBHasChanged = FALSE;
        bRet = TRUE;
Alexandre Julliard's avatar
Alexandre Julliard committed
746 747
    }
    else bRet = FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
748

749
    TRACE("   returning %i\n", bRet);
Alexandre Julliard's avatar
Alexandre Julliard committed
750
    return bRet;
Alexandre Julliard's avatar
Alexandre Julliard committed
751 752 753 754
}


/**************************************************************************
755
 *		CloseClipboard (USER.138)
Alexandre Julliard's avatar
Alexandre Julliard committed
756
 */
Alexandre Julliard's avatar
Alexandre Julliard committed
757
BOOL16 WINAPI CloseClipboard16(void)
Alexandre Julliard's avatar
Alexandre Julliard committed
758
{
759
    return CloseClipboard();
Alexandre Julliard's avatar
Alexandre Julliard committed
760 761 762 763
}


/**************************************************************************
764
 *		CloseClipboard (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
765
 */
766
BOOL WINAPI CloseClipboard(void)
Alexandre Julliard's avatar
Alexandre Julliard committed
767
{
768
    TRACE("()\n");
Alexandre Julliard's avatar
Alexandre Julliard committed
769

770
    if (hClipLock == GetCurrentTask())
Alexandre Julliard's avatar
Alexandre Julliard committed
771 772
    {
	hWndClipWindow = 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
773

774
        if (bCBHasChanged && hWndViewer) SendMessageW( hWndViewer, WM_DRAWCLIPBOARD, 0, 0 );
775
	hClipLock = 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
776
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
777 778 779 780 781
    return TRUE;
}


/**************************************************************************
782
 *		EmptyClipboard (USER.139)
Alexandre Julliard's avatar
Alexandre Julliard committed
783
 */
Alexandre Julliard's avatar
Alexandre Julliard committed
784
BOOL16 WINAPI EmptyClipboard16(void)
Alexandre Julliard's avatar
Alexandre Julliard committed
785
{
786
    return EmptyClipboard();
Alexandre Julliard's avatar
Alexandre Julliard committed
787 788 789 790
}


/**************************************************************************
791
 *		EmptyClipboard (USER32.@)
792
 *  Empties and acquires ownership of the clipboard
Alexandre Julliard's avatar
Alexandre Julliard committed
793
 */
794
BOOL WINAPI EmptyClipboard(void)
Alexandre Julliard's avatar
Alexandre Julliard committed
795
{
796
    TRACE("()\n");
Alexandre Julliard's avatar
Alexandre Julliard committed
797

798 799
    if (hClipLock != GetCurrentTask())
    {
800
        WARN("Clipboard not opened by calling task!\n");
801 802 803
        return FALSE;
    }
    
Alexandre Julliard's avatar
Alexandre Julliard committed
804 805
    /* destroy private objects */

806
    if (hWndClipOwner) SendMessageW( hWndClipOwner, WM_DESTROYCLIPBOARD, 0, 0 );
Alexandre Julliard's avatar
Alexandre Julliard committed
807

808 809
    /* empty the cache */
    CLIPBOARD_EmptyCache(TRUE);
Alexandre Julliard's avatar
Alexandre Julliard committed
810

811
    /* Assign ownership of the clipboard to the current client */
Alexandre Julliard's avatar
Alexandre Julliard committed
812 813
    hWndClipOwner = hWndClipWindow;

814 815 816 817
    /* Save the current task */
    hTaskClipOwner = GetCurrentTask();
    
    /* Tell the driver to acquire the selection */
818
    USER_Driver.pAcquireClipboard();
Alexandre Julliard's avatar
Alexandre Julliard committed
819

Alexandre Julliard's avatar
Alexandre Julliard committed
820 821 822 823
    return TRUE;
}


Alexandre Julliard's avatar
Alexandre Julliard committed
824
/**************************************************************************
825
 *		GetClipboardOwner (USER32.@)
826
 *  FIXME: Can't return the owner if the clipbard is owned by an external app
Alexandre Julliard's avatar
Alexandre Julliard committed
827
 */
828
HWND WINAPI GetClipboardOwner(void)
Alexandre Julliard's avatar
Alexandre Julliard committed
829
{
830
    TRACE("()\n");
Alexandre Julliard's avatar
Alexandre Julliard committed
831
    return hWndClipOwner;
Alexandre Julliard's avatar
Alexandre Julliard committed
832 833 834 835
}


/**************************************************************************
836
 *		SetClipboardData (USER.141)
Alexandre Julliard's avatar
Alexandre Julliard committed
837
 */
Alexandre Julliard's avatar
Alexandre Julliard committed
838
HANDLE16 WINAPI SetClipboardData16( UINT16 wFormat, HANDLE16 hData )
Alexandre Julliard's avatar
Alexandre Julliard committed
839
{
840
    LPWINE_CLIPFORMAT lpFormat = __lookup_format( ClipFormats, wFormat );
Alexandre Julliard's avatar
Alexandre Julliard committed
841

842
    TRACE("(%04X, %04x) !\n", wFormat, hData);
Alexandre Julliard's avatar
Alexandre Julliard committed
843

Alexandre Julliard's avatar
Alexandre Julliard committed
844 845
    /* NOTE: If the hData is zero and current owner doesn't match
     * the window that opened the clipboard then this application
846
     * is screwed because WM_RENDERFORMAT will go to the owner.
Alexandre Julliard's avatar
Alexandre Julliard committed
847 848 849 850
     * (to become the owner it must call EmptyClipboard() before
     *  adding new data).
     */

851 852 853
    if( CLIPBOARD_IsLocked() || !lpFormat ||
        (!hData && (!hWndClipOwner || (hWndClipOwner != hWndClipWindow))) )
    {
Juergen Schmied's avatar
Juergen Schmied committed
854
        WARN("Invalid hData or clipboard not opened by calling task!\n");
855 856
        return 0;
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
857

858
    /* Pass on the request to the driver */
859
    USER_Driver.pSetClipboardData(wFormat);
Alexandre Julliard's avatar
Alexandre Julliard committed
860

Pascal Cuoq's avatar
Pascal Cuoq committed
861
    if ( lpFormat->wDataPresent || lpFormat->hData16 || lpFormat->hData32 ) 
Alexandre Julliard's avatar
Alexandre Julliard committed
862
    {
Alexandre Julliard's avatar
Alexandre Julliard committed
863
	CLIPBOARD_DeleteRecord(lpFormat, TRUE);
Alexandre Julliard's avatar
Alexandre Julliard committed
864

865 866 867 868
	/* delete existing CF_UNICODETEXT/CF_TEXT/CF_OEMTEXT aliases */
	if(wFormat == CF_UNICODETEXT)
	{
	    CLIPBOARD_DeleteRecord(&ClipFormats[CF_TEXT-1], TRUE);
Alexandre Julliard's avatar
Alexandre Julliard committed
869
	    CLIPBOARD_DeleteRecord(&ClipFormats[CF_OEMTEXT-1], TRUE);
870 871 872 873 874 875 876 877 878
	}
	else if(wFormat == CF_TEXT)
	{
	    CLIPBOARD_DeleteRecord(&ClipFormats[CF_UNICODETEXT-1], TRUE);
	    CLIPBOARD_DeleteRecord(&ClipFormats[CF_OEMTEXT-1], TRUE);
	}
	else if(wFormat == CF_OEMTEXT)
	{
	    CLIPBOARD_DeleteRecord(&ClipFormats[CF_UNICODETEXT-1], TRUE);
Alexandre Julliard's avatar
Alexandre Julliard committed
879
	    CLIPBOARD_DeleteRecord(&ClipFormats[CF_TEXT-1], TRUE);
880
	}
Alexandre Julliard's avatar
Alexandre Julliard committed
881
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
882

Alexandre Julliard's avatar
Alexandre Julliard committed
883
    bCBHasChanged = TRUE;
Alexandre Julliard's avatar
Alexandre Julliard committed
884
    lpFormat->wDataPresent = 1;
Pascal Cuoq's avatar
Pascal Cuoq committed
885 886
    lpFormat->hData16 = hData;          /* 0 is legal, see WM_RENDERFORMAT */
    lpFormat->hData32 = 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
887

Pascal Cuoq's avatar
Pascal Cuoq committed
888
    return lpFormat->hData16;
Alexandre Julliard's avatar
Alexandre Julliard committed
889 890
}

Alexandre Julliard's avatar
Alexandre Julliard committed
891 892

/**************************************************************************
893
 *		SetClipboardData (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
894
 */
895
HANDLE WINAPI SetClipboardData( UINT wFormat, HANDLE hData )
Alexandre Julliard's avatar
Alexandre Julliard committed
896
{
897
    LPWINE_CLIPFORMAT lpFormat = __lookup_format( ClipFormats, wFormat );
Pascal Cuoq's avatar
Pascal Cuoq committed
898

899
    TRACE("(%08X, %08x) !\n", wFormat, hData);
Pascal Cuoq's avatar
Pascal Cuoq committed
900 901 902

    /* NOTE: If the hData is zero and current owner doesn't match
     * the window that opened the clipboard then this application
903
     * is screwed because WM_RENDERFORMAT will go to the owner.
Pascal Cuoq's avatar
Pascal Cuoq committed
904 905 906 907
     * (to become the owner it must call EmptyClipboard() before
     *  adding new data).
     */

908 909 910
    if( CLIPBOARD_IsLocked() || !lpFormat ||
        (!hData && (!hWndClipOwner || (hWndClipOwner != hWndClipWindow))) )
    {
Juergen Schmied's avatar
Juergen Schmied committed
911
        WARN("Invalid hData or clipboard not opened by calling task!\n");
912 913
        return 0;
    }
Pascal Cuoq's avatar
Pascal Cuoq committed
914

915
    /* Tell the driver to acquire the selection */
916
    USER_Driver.pAcquireClipboard();
Pascal Cuoq's avatar
Pascal Cuoq committed
917

918 919
    if ( lpFormat->wDataPresent &&
         (lpFormat->hData16 || lpFormat->hData32) )
Pascal Cuoq's avatar
Pascal Cuoq committed
920 921 922
    {
	CLIPBOARD_DeleteRecord(lpFormat, TRUE);

923 924 925 926
	/* delete existing CF_UNICODETEXT/CF_TEXT/CF_OEMTEXT aliases */
	if(wFormat == CF_UNICODETEXT)
	{
	    CLIPBOARD_DeleteRecord(&ClipFormats[CF_TEXT-1], TRUE);
Pascal Cuoq's avatar
Pascal Cuoq committed
927
	    CLIPBOARD_DeleteRecord(&ClipFormats[CF_OEMTEXT-1], TRUE);
928 929 930 931 932 933 934 935 936
	}
	else if(wFormat == CF_TEXT)
	{
	    CLIPBOARD_DeleteRecord(&ClipFormats[CF_UNICODETEXT-1], TRUE);
	    CLIPBOARD_DeleteRecord(&ClipFormats[CF_OEMTEXT-1], TRUE);
	}
	else if(wFormat == CF_OEMTEXT)
	{
	    CLIPBOARD_DeleteRecord(&ClipFormats[CF_UNICODETEXT-1], TRUE);
Pascal Cuoq's avatar
Pascal Cuoq committed
937
	    CLIPBOARD_DeleteRecord(&ClipFormats[CF_TEXT-1], TRUE);
938
	}
Pascal Cuoq's avatar
Pascal Cuoq committed
939 940 941 942
    }

    bCBHasChanged = TRUE;
    lpFormat->wDataPresent = 1;
943 944 945 946 947 948 949 950 951
    lpFormat->hDataSrc32 = hData;  /* Save the source handle */

    /*
     * Make a shared duplicate if the memory is not shared
     * TODO: What should be done for non-memory objects
     */
    if ( CLIPBOARD_IsMemoryObject(wFormat) && hData && !(GlobalFlags(hData) & GMEM_DDESHARE) )
        lpFormat->hData32 = CLIPBOARD_GlobalDupMem( hData );
    else
952
        lpFormat->hData32 = hData;          /* 0 is legal, see WM_RENDERFORMAT */
953
    
Pascal Cuoq's avatar
Pascal Cuoq committed
954 955
    lpFormat->hData16 = 0;

956
    return lpFormat->hData32;   /* Should we return lpFormat->hDataSrc32 */
Alexandre Julliard's avatar
Alexandre Julliard committed
957 958 959
}


Alexandre Julliard's avatar
Alexandre Julliard committed
960
/**************************************************************************
961
 *		GetClipboardData (USER.142)
Alexandre Julliard's avatar
Alexandre Julliard committed
962
 */
Alexandre Julliard's avatar
Alexandre Julliard committed
963
HANDLE16 WINAPI GetClipboardData16( UINT16 wFormat )
Alexandre Julliard's avatar
Alexandre Julliard committed
964
{
965
    LPWINE_CLIPFORMAT lpRender = ClipFormats; 
Alexandre Julliard's avatar
Alexandre Julliard committed
966

967
    TRACE("(%04X)\n", wFormat);
Alexandre Julliard's avatar
Alexandre Julliard committed
968

969
    if (CLIPBOARD_IsLocked())
Alexandre Julliard's avatar
Alexandre Julliard committed
970
    {
Juergen Schmied's avatar
Juergen Schmied committed
971
        WARN("Clipboard not opened by calling task!\n");
972
        return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
973
    }
974

975
    if( wFormat == CF_UNICODETEXT || wFormat == CF_TEXT || wFormat == CF_OEMTEXT )
Alexandre Julliard's avatar
Alexandre Julliard committed
976
    {
977 978
	lpRender = CLIPBOARD_RenderText(wFormat);
        if ( !lpRender ) return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
979 980 981
    }
    else
    {
Alexandre Julliard's avatar
Alexandre Julliard committed
982
	lpRender = __lookup_format( ClipFormats, wFormat );
983
        if( !lpRender || !CLIPBOARD_RenderFormat(lpRender) ) return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
984 985
    }
   
986
    /* Convert between 32 -> 16 bit data, if necessary */
987 988
    if( lpRender->hData32 && !lpRender->hData16
        && CLIPBOARD_IsMemoryObject(wFormat) )
Pascal Cuoq's avatar
Pascal Cuoq committed
989 990
    {
      int size;
991
      if( lpRender->wFormatID == CF_METAFILEPICT )
Pascal Cuoq's avatar
Pascal Cuoq committed
992 993
	size = sizeof( METAFILEPICT16 );
      else
994 995
          size = GlobalSize(lpRender->hData32);
      
996 997
      lpRender->hData16 = GlobalAlloc16(GMEM_ZEROINIT, size);
      if( !lpRender->hData16 )
998
	ERR("(%04X) -- not enough memory in 16b heap\n", wFormat);
Pascal Cuoq's avatar
Pascal Cuoq committed
999 1000
      else
      {
1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015
        if( lpRender->wFormatID == CF_METAFILEPICT )
        {
          FIXME("\timplement function CopyMetaFilePict32to16\n");
          FIXME("\tin the appropriate file.\n");
  #ifdef SOMEONE_IMPLEMENTED_ME
          CopyMetaFilePict32to16( GlobalLock16(lpRender->hData16), 
                                  GlobalLock(lpRender->hData32) );
  #endif
        }
        else
        {
          memcpy( GlobalLock16(lpRender->hData16), 
                  GlobalLock(lpRender->hData32), 
                  size );
        }
1016 1017
	GlobalUnlock16(lpRender->hData16);
	GlobalUnlock(lpRender->hData32);
Pascal Cuoq's avatar
Pascal Cuoq committed
1018 1019 1020
      }
    }

1021
    TRACE("\treturning %04x (type %i)\n", 
1022 1023
			      lpRender->hData16, lpRender->wFormatID);
    return lpRender->hData16;
Alexandre Julliard's avatar
Alexandre Julliard committed
1024 1025 1026
}


Alexandre Julliard's avatar
Alexandre Julliard committed
1027
/**************************************************************************
1028
 *		GetClipboardData (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
1029
 */
1030
HANDLE WINAPI GetClipboardData( UINT wFormat )
Alexandre Julliard's avatar
Alexandre Julliard committed
1031
{
1032
    LPWINE_CLIPFORMAT lpRender = ClipFormats; 
Pascal Cuoq's avatar
Pascal Cuoq committed
1033

1034
    TRACE("(%08X)\n", wFormat);
Pascal Cuoq's avatar
Pascal Cuoq committed
1035

1036
    if (CLIPBOARD_IsLocked())
Pascal Cuoq's avatar
Pascal Cuoq committed
1037
    {
1038
        WARN("Clipboard not opened by calling task!\n");
1039
        return 0;
Pascal Cuoq's avatar
Pascal Cuoq committed
1040
    }
1041

1042
    if( wFormat == CF_UNICODETEXT || wFormat == CF_TEXT || wFormat == CF_OEMTEXT )
Pascal Cuoq's avatar
Pascal Cuoq committed
1043
    {
1044 1045
	lpRender = CLIPBOARD_RenderText(wFormat);
        if ( !lpRender ) return 0;
Pascal Cuoq's avatar
Pascal Cuoq committed
1046 1047 1048 1049
    }
    else
    {
	lpRender = __lookup_format( ClipFormats, wFormat );
1050
        if( !lpRender || !CLIPBOARD_RenderFormat(lpRender) ) return 0;
Pascal Cuoq's avatar
Pascal Cuoq committed
1051 1052
    }
   
1053
    /* Convert between 16 -> 32 bit data, if necessary */
1054 1055
    if( lpRender->hData16 && !lpRender->hData32
        && CLIPBOARD_IsMemoryObject(wFormat) )
Pascal Cuoq's avatar
Pascal Cuoq committed
1056 1057
    {
      int size;
1058
      if( lpRender->wFormatID == CF_METAFILEPICT )
1059
	size = sizeof( METAFILEPICT );
Pascal Cuoq's avatar
Pascal Cuoq committed
1060
      else
1061 1062
	size = GlobalSize16(lpRender->hData16);
      lpRender->hData32 = GlobalAlloc(GMEM_ZEROINIT | GMEM_MOVEABLE | GMEM_DDESHARE,
1063
                                      size);
1064
      if( lpRender->wFormatID == CF_METAFILEPICT )
Pascal Cuoq's avatar
Pascal Cuoq committed
1065
      {
1066 1067
	FIXME("\timplement function CopyMetaFilePict16to32\n");
	FIXME("\tin the appropriate file.\n");
Pascal Cuoq's avatar
Pascal Cuoq committed
1068
#ifdef SOMEONE_IMPLEMENTED_ME
1069 1070
	CopyMetaFilePict16to32( GlobalLock16(lpRender->hData32), 
			        GlobalLock(lpRender->hData16) );
Pascal Cuoq's avatar
Pascal Cuoq committed
1071 1072 1073 1074
#endif
      }
      else
      {
1075 1076
	memcpy( GlobalLock(lpRender->hData32), 
		GlobalLock16(lpRender->hData16), 
Pascal Cuoq's avatar
Pascal Cuoq committed
1077 1078
		size );
      }
1079 1080
      GlobalUnlock(lpRender->hData32);
      GlobalUnlock16(lpRender->hData16);
Pascal Cuoq's avatar
Pascal Cuoq committed
1081 1082
    }

1083
    TRACE("\treturning %04x (type %i)\n", 
1084 1085
			      lpRender->hData32, lpRender->wFormatID);
    return lpRender->hData32;
Alexandre Julliard's avatar
Alexandre Julliard committed
1086 1087
}

1088

Alexandre Julliard's avatar
Alexandre Julliard committed
1089
/**************************************************************************
1090
 *		CountClipboardFormats (USER.143)
Alexandre Julliard's avatar
Alexandre Julliard committed
1091
 */
Alexandre Julliard's avatar
Alexandre Julliard committed
1092
INT16 WINAPI CountClipboardFormats16(void)
Alexandre Julliard's avatar
Alexandre Julliard committed
1093
{
1094
    return CountClipboardFormats();
Alexandre Julliard's avatar
Alexandre Julliard committed
1095 1096 1097 1098
}


/**************************************************************************
1099
 *		CountClipboardFormats (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
1100
 */
1101
INT WINAPI CountClipboardFormats(void)
Alexandre Julliard's avatar
Alexandre Julliard committed
1102
{
1103 1104
    INT FormatCount = 0;
    LPWINE_CLIPFORMAT lpFormat = ClipFormats; 
Alexandre Julliard's avatar
Alexandre Julliard committed
1105

1106
    TRACE("()\n");
Alexandre Julliard's avatar
Alexandre Julliard committed
1107

Alexandre Julliard's avatar
Alexandre Julliard committed
1108 1109
    while(TRUE) 
    {
Alexandre Julliard's avatar
Alexandre Julliard committed
1110
	if (lpFormat == NULL) break;
1111

1112 1113 1114 1115 1116 1117 1118 1119 1120
        if( lpFormat->wFormatID != CF_TEXT ) /* Don't count CF_TEXT */
        {
          /*
           * The format is available if either:
           * 1. The data is already in the cache.
           * 2. The selection is not owned by us(WINE) and the data is
           *    available to the clipboard driver.
           */
          if ( lpFormat->wDataPresent ||
1121 1122
               ( !USER_Driver.pIsSelectionOwner()
                 && USER_Driver.pIsClipboardFormatAvailable( lpFormat->wFormatID ) ) )
1123
          {
Juergen Schmied's avatar
Juergen Schmied committed
1124
              TRACE("\tdata found for format 0x%04x(%s)\n",
1125 1126 1127 1128 1129
                    lpFormat->wFormatID, CLIPBOARD_GetFormatName(lpFormat->wFormatID));
              FormatCount++;
          }
        }

Alexandre Julliard's avatar
Alexandre Julliard committed
1130 1131
	lpFormat = lpFormat->NextFormat;
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
1132

1133 1134 1135 1136
    /* these are equivalent, adjust the total */
    FormatCount += (ClipFormats[CF_UNICODETEXT-1].wDataPresent ||
		    ClipFormats[CF_TEXT-1].wDataPresent ||
		    ClipFormats[CF_OEMTEXT-1].wDataPresent) ? 1 : 0;
1137

1138
    TRACE("\ttotal %d\n", FormatCount);
Alexandre Julliard's avatar
Alexandre Julliard committed
1139 1140 1141 1142
    return FormatCount;
}

/**************************************************************************
1143
 *		EnumClipboardFormats (USER.144)
Alexandre Julliard's avatar
Alexandre Julliard committed
1144
 */
Alexandre Julliard's avatar
Alexandre Julliard committed
1145
UINT16 WINAPI EnumClipboardFormats16( UINT16 wFormat )
Alexandre Julliard's avatar
Alexandre Julliard committed
1146
{
1147
    return EnumClipboardFormats( wFormat );
Alexandre Julliard's avatar
Alexandre Julliard committed
1148 1149 1150 1151
}


/**************************************************************************
1152
 *		EnumClipboardFormats (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
1153
 */
1154
UINT WINAPI EnumClipboardFormats( UINT wFormat )
Alexandre Julliard's avatar
Alexandre Julliard committed
1155
{
1156
    TRACE("(%04X)\n", wFormat);
Alexandre Julliard's avatar
Alexandre Julliard committed
1157

1158 1159
    if (CLIPBOARD_IsLocked())
    {
1160
        WARN("Clipboard not opened by calling task!\n");
1161 1162
        return 0;
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
1163

1164
    return CLIPBOARD_EnumClipboardFormats(wFormat);
Alexandre Julliard's avatar
Alexandre Julliard committed
1165 1166 1167 1168
}


/**************************************************************************
1169
 *		RegisterClipboardFormatA (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
1170
 */
1171
UINT WINAPI RegisterClipboardFormatA( LPCSTR FormatName )
Alexandre Julliard's avatar
Alexandre Julliard committed
1172
{
1173 1174
    LPWINE_CLIPFORMAT lpNewFormat; 
    LPWINE_CLIPFORMAT lpFormat = ClipFormats; 
Alexandre Julliard's avatar
Alexandre Julliard committed
1175

Alexandre Julliard's avatar
Alexandre Julliard committed
1176
    if (FormatName == NULL) return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
1177

1178
    TRACE("('%s') !\n", FormatName);
Alexandre Julliard's avatar
Alexandre Julliard committed
1179 1180 1181

    /* walk format chain to see if it's already registered */

Alexandre Julliard's avatar
Alexandre Julliard committed
1182 1183
    while(TRUE) 
    {
Alexandre Julliard's avatar
Alexandre Julliard committed
1184
	if ( !strcmp(lpFormat->Name,FormatName) )
Alexandre Julliard's avatar
Alexandre Julliard committed
1185
	{
Alexandre Julliard's avatar
Alexandre Julliard committed
1186 1187
	     lpFormat->wRefCount++;
	     return lpFormat->wFormatID;
Alexandre Julliard's avatar
Alexandre Julliard committed
1188
	}
Alexandre Julliard's avatar
Alexandre Julliard committed
1189 1190 1191
 
	if ( lpFormat->NextFormat == NULL ) break;

Alexandre Julliard's avatar
Alexandre Julliard committed
1192
	lpFormat = lpFormat->NextFormat;
Alexandre Julliard's avatar
Alexandre Julliard committed
1193
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
1194 1195 1196

    /* allocate storage for new format entry */

1197 1198
    lpNewFormat = (LPWINE_CLIPFORMAT)HeapAlloc(GetProcessHeap(), 0, sizeof(WINE_CLIPFORMAT));
    if(lpNewFormat == NULL) {
1199
        WARN("No more memory for a new format!\n");
1200 1201
        return 0;
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
1202 1203 1204
    lpFormat->NextFormat = lpNewFormat;
    lpNewFormat->wFormatID = LastRegFormat;
    lpNewFormat->wRefCount = 1;
Alexandre Julliard's avatar
Alexandre Julliard committed
1205

1206 1207
    if (!(lpNewFormat->Name = HeapAlloc(GetProcessHeap(), 0, strlen(FormatName)+1 )))
    {
1208
        WARN("No more memory for the new format name!\n");
1209 1210 1211
        HeapFree(GetProcessHeap(), 0, lpNewFormat);
        return 0;
    }
1212
    strcpy( lpNewFormat->Name, FormatName );
Alexandre Julliard's avatar
Alexandre Julliard committed
1213 1214

    lpNewFormat->wDataPresent = 0;
Pascal Cuoq's avatar
Pascal Cuoq committed
1215
    lpNewFormat->hData16 = 0;
1216
    lpNewFormat->hDataSrc32 = 0;
Pascal Cuoq's avatar
Pascal Cuoq committed
1217
    lpNewFormat->hData32 = 0;
1218
    lpNewFormat->drvData = 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
1219 1220
    lpNewFormat->PrevFormat = lpFormat;
    lpNewFormat->NextFormat = NULL;
Alexandre Julliard's avatar
Alexandre Julliard committed
1221

1222
    /* Pass on the registration request to the driver */
1223
    USER_Driver.pRegisterClipboardFormat( FormatName );
1224
    
Alexandre Julliard's avatar
Alexandre Julliard committed
1225 1226 1227 1228
    return LastRegFormat++;
}


Alexandre Julliard's avatar
Alexandre Julliard committed
1229
/**************************************************************************
1230
 *		RegisterClipboardFormat (USER.145)
Alexandre Julliard's avatar
Alexandre Julliard committed
1231
 */
1232
UINT16 WINAPI RegisterClipboardFormat16( LPCSTR FormatName )
Alexandre Julliard's avatar
Alexandre Julliard committed
1233
{
1234
    return RegisterClipboardFormatA( FormatName );
Alexandre Julliard's avatar
Alexandre Julliard committed
1235 1236 1237 1238
}


/**************************************************************************
1239
 *		RegisterClipboardFormatW (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
1240
 */
1241
UINT WINAPI RegisterClipboardFormatW( LPCWSTR formatName )
Alexandre Julliard's avatar
Alexandre Julliard committed
1242
{
Alexandre Julliard's avatar
Alexandre Julliard committed
1243
    LPSTR aFormat = HEAP_strdupWtoA( GetProcessHeap(), 0, formatName );
1244
    UINT ret = RegisterClipboardFormatA( aFormat );
Alexandre Julliard's avatar
Alexandre Julliard committed
1245
    HeapFree( GetProcessHeap(), 0, aFormat );
Alexandre Julliard's avatar
Alexandre Julliard committed
1246 1247 1248
    return ret;
}

1249

Alexandre Julliard's avatar
Alexandre Julliard committed
1250
/**************************************************************************
1251
 *		GetClipboardFormatName (USER.146)
Alexandre Julliard's avatar
Alexandre Julliard committed
1252
 */
Alexandre Julliard's avatar
Alexandre Julliard committed
1253
INT16 WINAPI GetClipboardFormatName16( UINT16 wFormat, LPSTR retStr, INT16 maxlen )
Alexandre Julliard's avatar
Alexandre Julliard committed
1254
{
1255
    return GetClipboardFormatNameA( wFormat, retStr, maxlen );
Alexandre Julliard's avatar
Alexandre Julliard committed
1256 1257 1258 1259
}


/**************************************************************************
1260
 *		GetClipboardFormatNameA (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
1261
 */
1262
INT WINAPI GetClipboardFormatNameA( UINT wFormat, LPSTR retStr, INT maxlen )
Alexandre Julliard's avatar
Alexandre Julliard committed
1263
{
1264
    LPWINE_CLIPFORMAT lpFormat = __lookup_format( ClipFormats, wFormat );
Alexandre Julliard's avatar
Alexandre Julliard committed
1265

1266
    TRACE("(%04X, %p, %d) !\n", wFormat, retStr, maxlen);
Alexandre Julliard's avatar
Alexandre Julliard committed
1267

Alexandre Julliard's avatar
Alexandre Julliard committed
1268
    if (lpFormat == NULL || lpFormat->Name == NULL || 
Alexandre Julliard's avatar
Alexandre Julliard committed
1269 1270
	lpFormat->wFormatID < CF_REGFORMATBASE) return 0;

1271
    TRACE("Name='%s' !\n", lpFormat->Name);
Alexandre Julliard's avatar
Alexandre Julliard committed
1272

1273
    lstrcpynA( retStr, lpFormat->Name, maxlen );
Alexandre Julliard's avatar
Alexandre Julliard committed
1274
    return strlen(retStr);
Alexandre Julliard's avatar
Alexandre Julliard committed
1275 1276 1277
}


Alexandre Julliard's avatar
Alexandre Julliard committed
1278
/**************************************************************************
1279
 *		GetClipboardFormatNameW (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
1280
 */
1281
INT WINAPI GetClipboardFormatNameW( UINT wFormat, LPWSTR retStr, INT maxlen )
Alexandre Julliard's avatar
Alexandre Julliard committed
1282
{
Dimitrie O. Paun's avatar
Dimitrie O. Paun committed
1283 1284 1285 1286 1287
    INT ret;
    LPSTR p = HeapAlloc( GetProcessHeap(), 0, maxlen );
    if(p == NULL) return 0; /* FIXME: is this the correct failure value? */
    
    ret = GetClipboardFormatNameA( wFormat, p, maxlen );
1288 1289 1290

    if (maxlen > 0 && !MultiByteToWideChar( CP_ACP, 0, p, -1, retStr, maxlen ))
        retStr[maxlen-1] = 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
1291 1292 1293 1294 1295
    HeapFree( GetProcessHeap(), 0, p );
    return ret;
}


Alexandre Julliard's avatar
Alexandre Julliard committed
1296
/**************************************************************************
1297
 *		SetClipboardViewer (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
1298
 */
1299
HWND WINAPI SetClipboardViewer( HWND hWnd )
Alexandre Julliard's avatar
Alexandre Julliard committed
1300
{
1301
    HWND hwndPrev = hWndViewer;
Alexandre Julliard's avatar
Alexandre Julliard committed
1302

1303
    TRACE("(%04x): returning %04x\n", hWnd, hwndPrev);
Alexandre Julliard's avatar
Alexandre Julliard committed
1304

1305
    hWndViewer = WIN_GetFullHandle( hWnd );
Alexandre Julliard's avatar
Alexandre Julliard committed
1306
    return hwndPrev;
Alexandre Julliard's avatar
Alexandre Julliard committed
1307 1308 1309
}


Alexandre Julliard's avatar
Alexandre Julliard committed
1310
/**************************************************************************
1311
 *		GetClipboardViewer (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
1312
 */
1313
HWND WINAPI GetClipboardViewer(void)
Alexandre Julliard's avatar
Alexandre Julliard committed
1314
{
1315
    TRACE("()\n");
Alexandre Julliard's avatar
Alexandre Julliard committed
1316
    return hWndViewer;
Alexandre Julliard's avatar
Alexandre Julliard committed
1317 1318 1319
}


Alexandre Julliard's avatar
Alexandre Julliard committed
1320
/**************************************************************************
1321
 *		ChangeClipboardChain (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
1322
 */
1323
BOOL WINAPI ChangeClipboardChain(HWND hWnd, HWND hWndNext)
Alexandre Julliard's avatar
Alexandre Julliard committed
1324
{
1325
    BOOL bRet = 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
1326

1327
    FIXME("(0x%04x, 0x%04x): stub?\n", hWnd, hWndNext);
Alexandre Julliard's avatar
Alexandre Julliard committed
1328 1329

    if( hWndViewer )
1330
        bRet = !SendMessageW( hWndViewer, WM_CHANGECBCHAIN, (WPARAM)hWnd, (LPARAM)hWndNext );
Alexandre Julliard's avatar
Alexandre Julliard committed
1331
    else
1332
	WARN("hWndViewer is lost\n");
Alexandre Julliard's avatar
Alexandre Julliard committed
1333

1334
    if( WIN_GetFullHandle(hWnd) == hWndViewer ) hWndViewer = WIN_GetFullHandle( hWndNext );
Alexandre Julliard's avatar
Alexandre Julliard committed
1335

Alexandre Julliard's avatar
Alexandre Julliard committed
1336
    return bRet;
Alexandre Julliard's avatar
Alexandre Julliard committed
1337 1338 1339 1340
}


/**************************************************************************
1341
 *		IsClipboardFormatAvailable (USER.193)
Alexandre Julliard's avatar
Alexandre Julliard committed
1342
 */
Alexandre Julliard's avatar
Alexandre Julliard committed
1343
BOOL16 WINAPI IsClipboardFormatAvailable16( UINT16 wFormat )
Alexandre Julliard's avatar
Alexandre Julliard committed
1344
{
1345
    return IsClipboardFormatAvailable( wFormat );
Alexandre Julliard's avatar
Alexandre Julliard committed
1346 1347 1348 1349
}


/**************************************************************************
1350
 *		IsClipboardFormatAvailable (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
1351
 */
1352
BOOL WINAPI IsClipboardFormatAvailable( UINT wFormat )
Alexandre Julliard's avatar
Alexandre Julliard committed
1353
{
1354
    BOOL bRet;
Alexandre Julliard's avatar
Alexandre Julliard committed
1355

1356 1357
    if (wFormat == 0)  /* Reject this case quickly */
        bRet = FALSE;
1358
    else
1359 1360 1361 1362 1363 1364 1365
    {
        UINT iret = CLIPBOARD_EnumClipboardFormats(wFormat - 1);
        if ((wFormat == CF_TEXT) || (wFormat == CF_OEMTEXT) || (wFormat == CF_UNICODETEXT))
            bRet = ((iret == CF_TEXT) || (iret == CF_OEMTEXT) || (iret == CF_UNICODETEXT));
        else
	    bRet = iret == wFormat;
    }
1366 1367
    TRACE("(%04X)- ret(%d)\n", wFormat, bRet);
    return bRet;
Alexandre Julliard's avatar
Alexandre Julliard committed
1368 1369 1370
}


Alexandre Julliard's avatar
Alexandre Julliard committed
1371
/**************************************************************************
1372
 *		GetOpenClipboardWindow (USER32.@)
1373
 *  FIXME: This wont work if an external app owns the selection
Alexandre Julliard's avatar
Alexandre Julliard committed
1374
 */
1375
HWND WINAPI GetOpenClipboardWindow(void)
Alexandre Julliard's avatar
Alexandre Julliard committed
1376
{
1377
    TRACE("()\n");
Alexandre Julliard's avatar
Alexandre Julliard committed
1378
    return hWndClipWindow;
Alexandre Julliard's avatar
Alexandre Julliard committed
1379 1380 1381
}


Alexandre Julliard's avatar
Alexandre Julliard committed
1382
/**************************************************************************
1383
 *		GetPriorityClipboardFormat (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
1384
 */
1385
INT WINAPI GetPriorityClipboardFormat( UINT *list, INT nCount )
Alexandre Julliard's avatar
Alexandre Julliard committed
1386
{
1387
    int i;
1388
    TRACE("()\n");
Alexandre Julliard's avatar
Alexandre Julliard committed
1389

1390
    if(CountClipboardFormats() == 0) return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
1391

1392 1393
    for (i = 0; i < nCount; i++)
        if (IsClipboardFormatAvailable( list[i] )) return list[i];
Alexandre Julliard's avatar
Alexandre Julliard committed
1394
    return -1;
Alexandre Julliard's avatar
Alexandre Julliard committed
1395 1396
}

1397 1398

/**************************************************************************
1399
 *		GetClipboardSequenceNumber (USER32.@)
1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411
 * Supported on Win2k/Win98
 * MSDN: Windows clipboard code keeps a serial number for the clipboard
 * for each window station.  The number is incremented whenever the
 * contents change or are emptied.
 * If you do not have WINSTA_ACCESSCLIPBOARD then the function returns 0
 */
DWORD WINAPI GetClipboardSequenceNumber(VOID)
{
	FIXME("Returning 0, see windows/clipboard.c\n");
	/* FIXME: Use serial numbers */
	return 0;
}