/*
 * X11 event driver
 *
 * Copyright 1993 Alexandre Julliard
 *	     1999 Noel Borthwick
 */

#include "config.h"

#include <X11/Xatom.h>
#include <X11/keysym.h>

#include "ts_xlib.h"
#include "ts_xresource.h"
#include "ts_xutil.h"
#ifdef HAVE_LIBXXF86DGA2
#include "ts_xf86dga2.h"
#endif

#include <assert.h>
#include <string.h>
#include <stdio.h>
#include "wine/winuser16.h"
#include "shlobj.h"  /* DROPFILES */

#include "clipboard.h"
#include "dce.h"
#include "wine/debug.h"
#include "input.h"
#include "options.h"
#include "win.h"
#include "winpos.h"
#include "windef.h"
#include "winreg.h"
#include "x11drv.h"
#include "shellapi.h"
#include "wine/server.h"

WINE_DEFAULT_DEBUG_CHANNEL(event);
WINE_DECLARE_DEBUG_CHANNEL(systray);
WINE_DECLARE_DEBUG_CHANNEL(win);

/* X context to associate a hwnd to an X window */
extern XContext winContext;

extern Atom wmProtocols;
extern Atom wmDeleteWindow;
extern Atom wmManager;
extern Atom dndProtocol;
extern Atom dndSelection;
extern Atom netSysTraySelection;
extern Atom xembed;

extern void X11DRV_EnableGrabs(void);
extern void X11DRV_DisableGrabs(void);
extern Window X11DRV_DD_PrimaryFrame;

extern Window systrayWindow;

#define DndNotDnd       -1    /* OffiX drag&drop */
#define DndUnknown      0
#define DndRawData      1
#define DndFile         2
#define DndFiles        3
#define DndText         4
#define DndDir          5
#define DndLink         6
#define DndExe          7

#define DndEND          8

#define DndURL          128   /* KDE drag&drop */

/* The last X window which had the focus */
static Window glastXFocusWin = 0;

static const char * const event_names[] =
{
  "", "", "KeyPress", "KeyRelease", "ButtonPress", "ButtonRelease",
  "MotionNotify", "EnterNotify", "LeaveNotify", "FocusIn", "FocusOut",
  "KeymapNotify", "Expose", "GraphicsExpose", "NoExpose", "VisibilityNotify",
  "CreateNotify", "DestroyNotify", "UnmapNotify", "MapNotify", "MapRequest",
  "ReparentNotify", "ConfigureNotify", "ConfigureRequest", "GravityNotify",
  "ResizeRequest", "CirculateNotify", "CirculateRequest", "PropertyNotify",
  "SelectionClear", "SelectionRequest", "SelectionNotify", "ColormapNotify",
  "ClientMessage", "MappingNotify"
};


static void EVENT_ProcessEvent( XEvent *event );
static BOOL X11DRV_CheckFocus(void);

  /* Event handlers */
static void EVENT_FocusIn( HWND hWnd, XFocusChangeEvent *event );
static void EVENT_FocusOut( HWND hWnd, XFocusChangeEvent *event );
static void EVENT_SelectionRequest( HWND hWnd, XSelectionRequestEvent *event, BOOL bIsMultiple );
static void EVENT_SelectionClear( HWND hWnd, XSelectionClearEvent *event);
static void EVENT_PropertyNotify( XPropertyEvent *event );
static void EVENT_ClientMessage( HWND hWnd, XClientMessageEvent *event );

extern void X11DRV_ButtonPress( HWND hwnd, XButtonEvent *event );
extern void X11DRV_ButtonRelease( HWND hwnd, XButtonEvent *event );
extern void X11DRV_MotionNotify( HWND hwnd, XMotionEvent *event );
extern void X11DRV_KeyEvent( HWND hwnd, XKeyEvent *event );
extern void X11DRV_KeymapNotify( HWND hwnd, XKeymapEvent *event );
extern void X11DRV_Expose( HWND hwnd, XExposeEvent *event );
extern void X11DRV_MapNotify( HWND hwnd, XMapEvent *event );
extern void X11DRV_UnmapNotify( HWND hwnd, XUnmapEvent *event );
extern void X11DRV_ConfigureNotify( HWND hwnd, XConfigureEvent *event );
extern void X11DRV_MappingNotify( XMappingEvent *event );
extern void X11DRV_systray_dock_all( Display* display );

#ifdef HAVE_LIBXXF86DGA2
static int DGAMotionEventType;
static int DGAButtonPressEventType;
static int DGAButtonReleaseEventType;
static int DGAKeyPressEventType;
static int DGAKeyReleaseEventType;

static BOOL DGAUsed = FALSE;
static HWND DGAhwnd = 0;

extern void X11DRV_DGAMotionEvent( HWND hwnd, XDGAMotionEvent *event );
extern void X11DRV_DGAButtonPressEvent( HWND hwnd, XDGAButtonEvent *event );
extern void X11DRV_DGAButtonReleaseEvent( HWND hwnd, XDGAButtonEvent *event );
#endif

/* Static used for the current input method */
static INPUT_TYPE current_input_type = X11DRV_INPUT_ABSOLUTE;
static BOOL in_transition = FALSE; /* This is not used as for today */


/***********************************************************************
 *           process_events
 */
static int process_events( struct x11drv_thread_data *data )
{
    XEvent event;
    int count = 0;

    wine_tsx11_lock();
    while ( XPending( data->display ) )
    {
        XNextEvent( data->display, &event );
        wine_tsx11_unlock();
        EVENT_ProcessEvent( &event );
        count++;
        wine_tsx11_lock();
    }
    wine_tsx11_unlock();
    return count;
}


/***********************************************************************
 *           MsgWaitForMultipleObjectsEx   (X11DRV.@)
 */
DWORD X11DRV_MsgWaitForMultipleObjectsEx( DWORD count, const HANDLE *handles,
                                          DWORD timeout, DWORD mask, DWORD flags )
{
    HANDLE new_handles[MAXIMUM_WAIT_OBJECTS+1];  /* FIXME! */
    DWORD i, ret;
    struct x11drv_thread_data *data = NtCurrentTeb()->driver_data;

    if (!data || data->process_event_count)
        return WaitForMultipleObjectsEx( count, handles, flags & MWMO_WAITALL,
                                         timeout, flags & MWMO_ALERTABLE );

    for (i = 0; i < count; i++) new_handles[i] = handles[i];
    new_handles[count] = data->display_fd;

    wine_tsx11_lock();
    XFlush( gdi_display );
    XFlush( data->display );
    wine_tsx11_unlock();

    data->process_event_count++;
    if (process_events( data )) ret = count;
    else
    {
        ret = WaitForMultipleObjectsEx( count+1, new_handles, flags & MWMO_WAITALL,
                                        timeout, flags & MWMO_ALERTABLE );
        if (ret == count) process_events( data );
    }
    data->process_event_count--;
    return ret;
}

static Bool another_configure( Display *display, XEvent *event, XPointer arg )
{
    /* TRACE("event=%d, window=%ld (%ld)\n", event->type, event->xany.window, (Window)arg); */
    return ((event->type == MapNotify ||
             event->type == UnmapNotify ||
             event->type == ReparentNotify ||
             event->type == CirculateNotify ||
             event->type == ConfigureNotify) &&
	    event->xany.window == (Window)arg);
}

Bool EVENT_CheckEvent( Display *display, XEvent *event_return )
{
    Bool ret;
    wine_tsx11_lock();
    ret = XPending( display );
    if (ret) XPeekEvent( display, event_return );
    wine_tsx11_unlock();
    return ret;
}

Bool EVENT_CheckIsEvent( Display *display, int type, Window window )
{
    XEvent event;
    return EVENT_CheckEvent( display, &event ) &&
           event.xany.type == type &&
           event.xany.window == window;
}

/***********************************************************************
 *           EVENT_ProcessEvent
 *
 * Process an X event.
 */
static void EVENT_ProcessEvent( XEvent *event )
{
  HWND hWnd;
  Display *display = event->xany.display;

  TRACE( "called.\n" );

  switch (event->type)
  {
    case SelectionNotify: /* all of these should be caught by XCheckTypedWindowEvent() */
	 FIXME("Got SelectionNotify - must not happen!\n");
	 /* fall through */

      /* We get all these because of StructureNotifyMask.
         This check is placed here to avoid getting error messages below,
         as X might send some of these even for windows that have already
         been deleted ... */
    case CirculateNotify:
    case CreateNotify:
    case DestroyNotify:
    case GravityNotify:
    case ReparentNotify:
      return;
  }

#ifdef HAVE_LIBXXF86DGA2
  if (DGAUsed) {
    if (event->type == DGAMotionEventType) {
      TRACE("DGAMotionEvent received.\n");
      X11DRV_DGAMotionEvent( DGAhwnd, (XDGAMotionEvent *)event );
      return;
    }
    if (event->type == DGAButtonPressEventType) {
      TRACE("DGAButtonPressEvent received.\n");
      X11DRV_DGAButtonPressEvent( DGAhwnd, (XDGAButtonEvent *)event );
      return;
    }
    if (event->type == DGAButtonReleaseEventType) {
      TRACE("DGAButtonReleaseEvent received.\n");
      X11DRV_DGAButtonReleaseEvent( DGAhwnd, (XDGAButtonEvent *)event );
      return;
    }
    if ((event->type == DGAKeyPressEventType) ||
	(event->type == DGAKeyReleaseEventType)) {
      /* Fill a XKeyEvent to send to EVENT_Key */
      XKeyEvent ke;
      XDGAKeyEvent *evt = (XDGAKeyEvent *) event;

      TRACE("DGAKeyPress/ReleaseEvent received.\n");

      if (evt->type == DGAKeyReleaseEventType)
	ke.type = KeyRelease;
      else
	ke.type = KeyPress;
      ke.serial = evt->serial;
      ke.send_event = FALSE;
      ke.display = evt->display;
      ke.window = 0;
      ke.root = 0;
      ke.subwindow = 0;
      ke.time = evt->time;
      ke.x = -1;
      ke.y = -1;
      ke.x_root = -1;
      ke.y_root = -1;
      ke.state = evt->state;
      ke.keycode = evt->keycode;
      ke.same_screen = TRUE;
      X11DRV_KeyEvent( 0, &ke );
      return;
    }
  }
#endif

  if (TSXFindContext( display, event->xany.window, winContext, (char **)&hWnd ) != 0)
      hWnd = 0;  /* Not for a registered window */

  if ( !hWnd && event->xany.window != root_window
             && event->type != PropertyNotify
             && event->type != MappingNotify
             && event->type != ClientMessage)
      WARN( "Got event %s for unknown Window %08lx\n",
            event_names[event->type], event->xany.window );
  else
      TRACE("Got event %s for hwnd %04x\n",
            event_names[event->type], hWnd );

  switch(event->type)
    {
    case KeyPress:
    case KeyRelease:
      /* FIXME: should generate a motion event if event point is different from current pos */
      X11DRV_KeyEvent( hWnd, (XKeyEvent*)event );
      break;

    case ButtonPress:
      X11DRV_ButtonPress( hWnd, (XButtonEvent*)event );
      break;

    case ButtonRelease:
      X11DRV_ButtonRelease( hWnd, (XButtonEvent*)event );
      break;

    case MotionNotify:
      if ((current_input_type == X11DRV_INPUT_ABSOLUTE) &&
          (in_transition == FALSE) &&
          !call_supported_through_shared_memory(REQ_send_message)) {
        /* if we have many consecutive MotionNotify events,
         * improve responsiveness by skipping all but the last one */
        if (EVENT_CheckIsEvent( display, MotionNotify, event->xmotion.window )) {
          TRACE("skipping\n");
          break;
        }
      }
      X11DRV_MotionNotify( hWnd, (XMotionEvent*)event );
      break;

    case FocusIn:
      EVENT_FocusIn( hWnd, (XFocusChangeEvent*)event );
      break;

    case FocusOut:
      EVENT_FocusOut( hWnd, (XFocusChangeEvent*)event );
      break;

    case Expose:
      X11DRV_Expose( hWnd, &event->xexpose );
      break;

    case ConfigureNotify:
check_configure:
      /* try to eliminate redundant/superseded configure notifications
       * by skipping all but the last consecutive one in the queue */
      if (EVENT_CheckIsEvent( display, ConfigureNotify, event->xconfigure.window )) {
        TRACE("skipping\n");
        break;
      }
      if (!hWnd) return;
      X11DRV_ConfigureNotify( hWnd, &event->xconfigure );
      break;

    case MapNotify:
#if 0
check_map:
#endif
      if (!hWnd) return;
      X11DRV_MapNotify( hWnd, &event->xmap );
      break;

    case UnmapNotify:
      if (!hWnd) return;
      /* try to eliminate superseded unmaps here (they might cause trouble
       * if the app starts doing event handling a long time after a streak
       * of window changes in managed mode) - also try to eliminate redundant
       * configure notifications as we may otherwise not find the MapNotify
       * we're looking for here */
      {
         XEvent config, map, unmap;
         int config_count = 0, map_count = 0;
         TRACE("scanning event queue\n");
         config.type = 0;
         map.type = 0;
         unmap.type = 0;
         do {
             switch (event->type) {
             case ConfigureNotify:
                 TRACE(" -> ConfigureNotify\n");
                 config = *event;
                 config_count++;
                 break;
             case MapNotify:
                 TRACE(" -> MapNotify\n");
                 map = *event;
                 map_count++;
                 break;
             case UnmapNotify:
                 TRACE(" -> UnmapNotify\n");
                 unmap = *event;
                 map_count--;
                 break;
             }
         } while (map_count && TSXCheckIfEvent(display, event, another_configure, (XPointer)event->xany.window));
         if (map_count < 0) {
             TRACE("processing UnmapNotify\n");
             X11DRV_UnmapNotify( hWnd, &event->xunmap );
         }
         else if (map_count > 0) {
             FIXME("map_count > 0 in UnmapNotify handler - shouldn't happen\n");
             TRACE("processing MapNotify\n");
             X11DRV_MapNotify( hWnd, &event->xmap );
         }
         if (config_count) {
             TRACE("processing ConfigureNotify\n");
             *event = config;
             goto check_configure;
         }
#if 0 /* if the window might be resized when remapped without a ConfigureNotify? */
         else if (map.type && !map_count) {
             TRACE("processing MapNotify\n");
             *event = map;
             goto check_map;
         }
#endif
         break;
      }

    case SelectionRequest:
      if (!hWnd) return;
      EVENT_SelectionRequest( hWnd, (XSelectionRequestEvent *)event, FALSE );
      break;

    case SelectionClear:
      if (!hWnd) return;
      EVENT_SelectionClear( hWnd, (XSelectionClearEvent*) event );
      break;

    case PropertyNotify:
      EVENT_PropertyNotify( (XPropertyEvent *)event );
      break;

    case ClientMessage:
      EVENT_ClientMessage( hWnd, (XClientMessageEvent *) event );
      break;

    case NoExpose:
      break;

    case KeymapNotify:
      X11DRV_KeymapNotify( hWnd, (XKeymapEvent *)event );
      break;

    case MappingNotify:
      X11DRV_MappingNotify( (XMappingEvent *) event );
      break;

    default:
      WARN("Unprocessed event %s for hwnd %04x\n",
	   event_names[event->type], hWnd );
      break;
    }
    TRACE( "returns.\n" );
}


/**********************************************************************
 *              EVENT_FocusIn
 */
static void EVENT_FocusIn( HWND hWnd, XFocusChangeEvent *event )
{
    WND *pWndLastFocus;
    XWindowAttributes win_attr;
    BOOL bIsDisabled;

    if (!hWnd) return;

    bIsDisabled = GetWindowLongA( hWnd, GWL_STYLE ) & WS_DISABLED;

    /* If the window has been disabled and we are in managed mode,
       * revert the X focus back to the last focus window. This is to disallow
       * the window manager from switching focus away while the app is
       * in a modal state.
       */
    if ( Options.managed && bIsDisabled && glastXFocusWin)
    {
        /* Change focus only if saved focus window is registered and viewable */
        wine_tsx11_lock();
        if (XFindContext( event->display, glastXFocusWin, winContext,
                           (char **)&pWndLastFocus ) == 0 )
        {
            if (XGetWindowAttributes( event->display, glastXFocusWin, &win_attr ) &&
                (win_attr.map_state == IsViewable) )
            {
                XSetInputFocus( event->display, glastXFocusWin, RevertToParent, CurrentTime );
                wine_tsx11_unlock();
                return;
            }
        }
        wine_tsx11_unlock();
    }

    if (event->detail != NotifyPointer && event->detail != NotifyNonlinear && hWnd != GetForegroundWindow())
    {
        SetForegroundWindow( hWnd );
        X11DRV_EnableGrabs();
    }
}


/**********************************************************************
 *              EVENT_FocusOut
 *
 * Note: only top-level override-redirect windows get FocusOut events.
 */
static void EVENT_FocusOut( HWND hWnd, XFocusChangeEvent *event )
{
    /* Save the last window which had the focus */
    glastXFocusWin = event->window;
    if (!hWnd) return;
    if (GetWindowLongA( hWnd, GWL_STYLE ) & WS_DISABLED) glastXFocusWin = 0;

    if (event->detail != NotifyPointer && hWnd == GetForegroundWindow())
    {
        /* don't reset the foreground window, if the window which is
               getting the focus is a Wine window */
        if (!X11DRV_CheckFocus())
        {
            SendMessageA( hWnd, WM_CANCELMODE, 0, 0 );
            /* Abey : 6-Oct-99. Check again if the focus out window is the
               Foreground window, because in most cases the messages sent
               above must have already changed the foreground window, in which
               case we don't have to change the foreground window to 0 */

            if (hWnd == GetForegroundWindow())
            {
                X11DRV_DisableGrabs();
                SetForegroundWindow( 0 );
            }
        }
    }
}

/**********************************************************************
 *		CheckFocus (X11DRV.@)
 */
static BOOL X11DRV_CheckFocus(void)
{
    Display *display = thread_display();
    HWND   hWnd;
    Window xW;
    int	   state;

    TSXGetInputFocus(display, &xW, &state);
    if( xW == None )
      return FALSE;
    /* doesn't seem like XSaveContext/XFindContext works across displays,
     * so I'll just use an explicit check here instead */
    if( xW == X11DRV_DD_PrimaryFrame )
      return TRUE;
    if( TSXFindContext(display, xW, winContext, (char **)&hWnd) )
      return FALSE;
    return TRUE;
}


/***********************************************************************
 *           EVENT_SelectionRequest_TARGETS
 *  Service a TARGETS selection request event
 */
static Atom EVENT_SelectionRequest_TARGETS( Display *display, Window requestor,
                                            Atom target, Atom rprop )
{
    Atom xaTargets = TSXInternAtom(display, "TARGETS", False);
    Atom* targets;
    Atom prop;
    UINT wFormat;
    unsigned long cTargets;
    BOOL bHavePixmap;
    int xRc;

    TRACE("Request for %s\n", TSXGetAtomName(display, target));

    /*
     * Count the number of items we wish to expose as selection targets.
     * We include the TARGETS item, and a PIXMAP if we have CF_DIB or CF_BITMAP
     */
    cTargets = CountClipboardFormats() + 1;
    if ( CLIPBOARD_IsPresent(CF_DIB) ||  CLIPBOARD_IsPresent(CF_BITMAP) )
       cTargets++;

    /* Allocate temp buffer */
    targets = (Atom*)HeapAlloc( GetProcessHeap(), 0, cTargets * sizeof(Atom));
    if(targets == NULL) return None;

    /* Create TARGETS property list (First item in list is TARGETS itself) */

    for ( targets[0] = xaTargets, cTargets = 1, wFormat = 0, bHavePixmap = FALSE;
          (wFormat = EnumClipboardFormats( wFormat )); )
    {
        if ( (prop = X11DRV_CLIPBOARD_MapFormatToProperty(wFormat)) != None )
        {
            /* Scan through what we have so far to avoid duplicates */
            int i;
            BOOL bExists;
            for (i = 0, bExists = FALSE; i < cTargets; i++)
            {
                if (targets[i] == prop)
                {
                    bExists = TRUE;
                    break;
                }
            }
            if (!bExists)
            {
                targets[cTargets++] = prop;

                /* Add PIXMAP prop for bitmaps additionally */
                if ( (wFormat == CF_DIB || wFormat == CF_BITMAP )
                     && !bHavePixmap )
                {
                    targets[cTargets++] = XA_PIXMAP;
                    bHavePixmap = TRUE;
                }
            }
        }
    }

    if (TRACE_ON(event))
    {
        int i;
        for ( i = 0; i < cTargets; i++)
        {
            if (targets[i])
            {
                char *itemFmtName = TSXGetAtomName(display, targets[i]);
                TRACE("\tAtom# %d:  Type %s\n", i, itemFmtName);
                TSXFree(itemFmtName);
            }
        }
    }

    /* Update the X property */
    TRACE("\tUpdating property %s...\n", TSXGetAtomName(display, rprop));

    /* We may want to consider setting the type to xaTargets instead,
     * in case some apps expect this instead of XA_ATOM */
    xRc = TSXChangeProperty(display, requestor, rprop,
                            XA_ATOM, 32, PropModeReplace,
                            (unsigned char *)targets, cTargets);
    TRACE("(Rc=%d)\n", xRc);

    HeapFree( GetProcessHeap(), 0, targets );

    return rprop;
}


/***********************************************************************
 *           EVENT_SelectionRequest_STRING
 *  Service a STRING selection request event
 */
static Atom EVENT_SelectionRequest_STRING( Display *display, Window requestor,
                                           Atom target, Atom rprop )
{
    static UINT text_cp = (UINT)-1;
    HANDLE hUnicodeText;
    LPWSTR uni_text;
    LPSTR  text;
    int    size,i,j;
    char* lpstr = 0;
    char *itemFmtName;
    int xRc;

    if(text_cp == (UINT)-1)
    {
	HKEY hkey;
	/* default value */
	text_cp = CP_ACP;
	if(!RegOpenKeyA(HKEY_LOCAL_MACHINE, "Software\\Wine\\Wine\\Config\\x11drv", &hkey))
	{
	    char buf[20];
	    DWORD type, count = sizeof(buf);
	    if(!RegQueryValueExA(hkey, "TextCP", 0, &type, buf, &count))
		text_cp = atoi(buf);
	    RegCloseKey(hkey);
	  }
    }

    /*
     * Map the requested X selection property type atom name to a
     * windows clipboard format ID.
     */
    itemFmtName = TSXGetAtomName(display, target);
    TRACE("Request for %s (wFormat=%x %s)\n",
	itemFmtName, CF_UNICODETEXT, CLIPBOARD_GetFormatName(CF_UNICODETEXT));
    TSXFree(itemFmtName);

    hUnicodeText = GetClipboardData(CF_UNICODETEXT);
    if(!hUnicodeText)
       return None;
    uni_text = GlobalLock(hUnicodeText);
    if(!uni_text)
       return None;

    size = WideCharToMultiByte(text_cp, 0, uni_text, -1, NULL, 0, NULL, NULL);
    text = HeapAlloc(GetProcessHeap(), 0, size);
    if (!text)
       return None;
    WideCharToMultiByte(text_cp, 0, uni_text, -1, text, size, NULL, NULL);

    /* remove carriage returns */

    lpstr = (char*)HeapAlloc( GetProcessHeap(), 0, size-- );
    if(lpstr == NULL) return None;
    for(i=0,j=0; i < size && text[i]; i++ )
    {
        if( text[i] == '\r' &&
            (text[i+1] == '\n' || text[i+1] == '\0') ) continue;
        lpstr[j++] = text[i];
    }
    lpstr[j]='\0';

    /* Update the X property */
    TRACE("\tUpdating property %s...\n", TSXGetAtomName(display, rprop));
    xRc = TSXChangeProperty(display, requestor, rprop,
                            XA_STRING, 8, PropModeReplace,
                            lpstr, j);
    TRACE("(Rc=%d)\n", xRc);

    GlobalUnlock(hUnicodeText);
    HeapFree(GetProcessHeap(), 0, text);
    HeapFree( GetProcessHeap(), 0, lpstr );

    return rprop;
}

/***********************************************************************
 *           EVENT_SelectionRequest_PIXMAP
 *  Service a PIXMAP selection request event
 */
static Atom EVENT_SelectionRequest_PIXMAP( Display *display, Window requestor,
                                           Atom target, Atom rprop )
{
    HANDLE hClipData = 0;
    Pixmap pixmap = 0;
    UINT   wFormat;
    char * itemFmtName;
    int xRc;
#if(0)
    XSetWindowAttributes win_attr;
    XWindowAttributes win_attr_src;
#endif

    /*
     * Map the requested X selection property type atom name to a
     * windows clipboard format ID.
     */
    itemFmtName = TSXGetAtomName(display, target);
    wFormat = X11DRV_CLIPBOARD_MapPropertyToFormat(itemFmtName);
    TRACE("Request for %s (wFormat=%x %s)\n",
                  itemFmtName, wFormat, CLIPBOARD_GetFormatName( wFormat));
    TSXFree(itemFmtName);

    hClipData = GetClipboardData(wFormat);
    if ( !hClipData )
    {
        TRACE("Could not retrieve a Pixmap compatible format from clipboard!\n");
        rprop = None; /* Fail the request */
        goto END;
    }

    if (wFormat == CF_DIB)
    {
        HWND hwnd = GetOpenClipboardWindow();
        HDC hdc = GetDC(hwnd);

        /* For convert from packed DIB to Pixmap */
        pixmap = X11DRV_DIB_CreatePixmapFromDIB(hClipData, hdc);

        ReleaseDC(hwnd, hdc);
    }
    else if (wFormat == CF_BITMAP)
    {
        HWND hwnd = GetOpenClipboardWindow();
        HDC hdc = GetDC(hwnd);

        pixmap = X11DRV_BITMAP_CreatePixmapFromBitmap(hClipData, hdc);

        ReleaseDC(hwnd, hdc);
    }
    else
    {
        FIXME("%s to PIXMAP conversion not yet implemented!\n",
                      CLIPBOARD_GetFormatName(wFormat));
        rprop = None;
        goto END;
    }

    TRACE("\tUpdating property %s on Window %ld with %s %ld...\n",
          TSXGetAtomName(display, rprop), (long)requestor,
          TSXGetAtomName(display, target), pixmap);

    /* Store the Pixmap handle in the property */
    xRc = TSXChangeProperty(display, requestor, rprop, target,
                            32, PropModeReplace,
                            (unsigned char *)&pixmap, 1);
    TRACE("(Rc=%d)\n", xRc);

    /* Enable the code below if you want to handle destroying Pixmap resources
     * in response to property notify events. Clients like XPaint don't
     * appear to be duplicating Pixmaps so they don't like us deleting,
     * the resource in response to the property being deleted.
     */
#if(0)
    /* Express interest in property notify events so that we can delete the
     * pixmap when the client deletes the property atom.
     */
    xRc = TSXGetWindowAttributes(display, requestor, &win_attr_src);
    TRACE("Turning on PropertyChangeEvent notifications from window %ld\n",
          (long)requestor);
    win_attr.event_mask = win_attr_src.your_event_mask | PropertyChangeMask;
    TSXChangeWindowAttributes(display, requestor, CWEventMask, &win_attr);

    /* Register the Pixmap we created with the request property Atom.
     * When this property is destroyed we also destroy the Pixmap in
     * response to the PropertyNotify event.
     */
    X11DRV_CLIPBOARD_RegisterPixmapResource( rprop, pixmap );
#endif

END:
    return rprop;
}


/***********************************************************************
 *           EVENT_SelectionRequest_WCF
 *  Service a Wine Clipboard Format selection request event.
 *  For <WCF>* data types we simply copy the data to X without conversion.
 */
static Atom EVENT_SelectionRequest_WCF( Display *display, Window requestor,
                                        Atom target, Atom rprop )
{
    HANDLE hClipData = 0;
    void*  lpClipData;
    UINT   wFormat;
    char * itemFmtName;
    int cBytes;
    int xRc;

    /*
     * Map the requested X selection property type atom name to a
     * windows clipboard format ID.
     */
    itemFmtName = TSXGetAtomName(display, target);
    wFormat = X11DRV_CLIPBOARD_MapPropertyToFormat(itemFmtName);
    TRACE("Request for %s (wFormat=%x %s)\n",
          itemFmtName, wFormat, CLIPBOARD_GetFormatName( wFormat));
    TSXFree(itemFmtName);

    hClipData = GetClipboardData(wFormat);

    if( hClipData && (lpClipData = GlobalLock(hClipData)) )
    {
        cBytes = GlobalSize(hClipData);

        TRACE("\tUpdating property %s, %d bytes...\n",
              TSXGetAtomName(display, rprop), cBytes);

        xRc = TSXChangeProperty(display, requestor, rprop,
                                target, 8, PropModeReplace,
                                (unsigned char *)lpClipData, cBytes);
        TRACE("(Rc=%d)\n", xRc);

        GlobalUnlock(hClipData);
    }
    else
    {
        TRACE("\tCould not retrieve native format!\n");
        rprop = None; /* Fail the request */
    }

    return rprop;
}


/***********************************************************************
 *           EVENT_SelectionRequest_MULTIPLE
 *  Service a MULTIPLE selection request event
 *  rprop contains a list of (target,property) atom pairs.
 *  The first atom names a target and the second names a property.
 *  The effect is as if we have received a sequence of SelectionRequest events
 *  (one for each atom pair) except that:
 *  1. We reply with a SelectionNotify only when all the requested conversions
 *  have been performed.
 *  2. If we fail to convert the target named by an atom in the MULTIPLE property,
 *  we replace the atom in the property by None.
 */
static Atom EVENT_SelectionRequest_MULTIPLE( HWND hWnd, XSelectionRequestEvent *pevent )
{
    Display *display = pevent->display;
    Atom           rprop;
    Atom           atype=AnyPropertyType;
    int		   aformat;
    unsigned long  remain;
    Atom*	   targetPropList=NULL;
    unsigned long  cTargetPropList = 0;
/*  Atom           xAtomPair = TSXInternAtom(display, "ATOM_PAIR", False); */

   /* If the specified property is None the requestor is an obsolete client.
    * We support these by using the specified target atom as the reply property.
    */
    rprop = pevent->property;
    if( rprop == None )
        rprop = pevent->target;
    if (!rprop)
        goto END;

    /* Read the MULTIPLE property contents. This should contain a list of
     * (target,property) atom pairs.
     */
    if(TSXGetWindowProperty(display, pevent->requestor, rprop,
                            0, 0x3FFF, False, AnyPropertyType, &atype,&aformat,
                            &cTargetPropList, &remain,
                            (unsigned char**)&targetPropList) != Success)
        TRACE("\tCouldn't read MULTIPLE property\n");
    else
    {
       TRACE("\tType %s,Format %d,nItems %ld, Remain %ld\n",
             TSXGetAtomName(display, atype), aformat, cTargetPropList, remain);

       /*
        * Make sure we got what we expect.
        * NOTE: According to the X-ICCCM Version 2.0 documentation the property sent
        * in a MULTIPLE selection request should be of type ATOM_PAIR.
        * However some X apps(such as XPaint) are not compliant with this and return
        * a user defined atom in atype when XGetWindowProperty is called.
        * The data *is* an atom pair but is not denoted as such.
        */
       if(aformat == 32 /* atype == xAtomPair */ )
       {
          int i;

          /* Iterate through the ATOM_PAIR list and execute a SelectionRequest
           * for each (target,property) pair */

          for (i = 0; i < cTargetPropList; i+=2)
          {
              char *targetName = TSXGetAtomName(display, targetPropList[i]);
              char *propName = TSXGetAtomName(display, targetPropList[i+1]);
              XSelectionRequestEvent event;

              TRACE("MULTIPLE(%d): Target='%s' Prop='%s'\n",
                    i/2, targetName, propName);
              TSXFree(targetName);
              TSXFree(propName);

              /* We must have a non "None" property to service a MULTIPLE target atom */
              if ( !targetPropList[i+1] )
              {
                  TRACE("\tMULTIPLE(%d): Skipping target with empty property!\n", i);
                  continue;
              }

              /* Set up an XSelectionRequestEvent for this (target,property) pair */
              memcpy( &event, pevent, sizeof(XSelectionRequestEvent) );
              event.target = targetPropList[i];
              event.property = targetPropList[i+1];

              /* Fire a SelectionRequest, informing the handler that we are processing
               * a MULTIPLE selection request event.
               */
              EVENT_SelectionRequest( hWnd, &event, TRUE );
          }
       }

       /* Free the list of targets/properties */
       TSXFree(targetPropList);
    }

END:
    return rprop;
}


/***********************************************************************
 *           EVENT_SelectionRequest
 *  Process an event selection request event.
 *  The bIsMultiple flag is used to signal when EVENT_SelectionRequest is called
 *  recursively while servicing a "MULTIPLE" selection target.
 *
 *  Note: We only receive this event when WINE owns the X selection
 */
static void EVENT_SelectionRequest( HWND hWnd, XSelectionRequestEvent *event, BOOL bIsMultiple )
{
    Display *display = event->display;
  XSelectionEvent result;
  Atom 	          rprop = None;
  Window          request = event->requestor;
  BOOL            couldOpen = FALSE;
  Atom            xaClipboard = TSXInternAtom(display, "CLIPBOARD", False);
  Atom            xaTargets = TSXInternAtom(display, "TARGETS", False);
  Atom            xaMultiple = TSXInternAtom(display, "MULTIPLE", False);

  /*
   * We can only handle the selection request if :
   * The selection is PRIMARY or CLIPBOARD, AND we can successfully open the clipboard.
   * Don't do these checks or open the clipboard while recursively processing MULTIPLE,
   * since this has been already done.
   */
  if ( !bIsMultiple )
  {
    if ( ( (event->selection != XA_PRIMARY) && (event->selection != xaClipboard) )
        || !(couldOpen = OpenClipboard(hWnd)) )
       goto END;
  }

  /* If the specified property is None the requestor is an obsolete client.
   * We support these by using the specified target atom as the reply property.
   */
  rprop = event->property;
  if( rprop == None )
      rprop = event->target;

  if(event->target == xaTargets)  /*  Return a list of all supported targets */
  {
      /* TARGETS selection request */
      rprop = EVENT_SelectionRequest_TARGETS( display, request, event->target, rprop );
  }
  else if(event->target == xaMultiple)  /*  rprop contains a list of (target, property) atom pairs */
  {
      /* MULTIPLE selection request */
      rprop = EVENT_SelectionRequest_MULTIPLE( hWnd, event );
  }
  else if(event->target == XA_STRING)  /* treat CF_TEXT as Unix text */
  {
      /* XA_STRING selection request */
      rprop = EVENT_SelectionRequest_STRING( display, request, event->target, rprop );
  }
  else if(event->target == XA_PIXMAP)  /*  Convert DIB's to Pixmaps */
  {
      /* XA_PIXMAP selection request */
      rprop = EVENT_SelectionRequest_PIXMAP( display, request, event->target, rprop );
  }
  else if(event->target == XA_BITMAP)  /*  Convert DIB's to 1-bit Pixmaps */
  {
      /* XA_BITMAP selection request - TODO: create a monochrome Pixmap */
      rprop = EVENT_SelectionRequest_PIXMAP( display, request, XA_PIXMAP, rprop );
  }
  else if(X11DRV_CLIPBOARD_IsNativeProperty(event->target)) /* <WCF>* */
  {
      /* All <WCF> selection requests */
      rprop = EVENT_SelectionRequest_WCF( display, request, event->target, rprop );
  }
  else
      rprop = None;  /* Don't support this format */

END:
  /* close clipboard only if we opened before */
  if(couldOpen) CloseClipboard();

  if( rprop == None)
      TRACE("\tRequest ignored\n");

  /* reply to sender
   * SelectionNotify should be sent only at the end of a MULTIPLE request
   */
  if ( !bIsMultiple )
  {
    result.type = SelectionNotify;
    result.display = display;
    result.requestor = request;
    result.selection = event->selection;
    result.property = rprop;
    result.target = event->target;
    result.time = event->time;
    TRACE("Sending SelectionNotify event...\n");
    TSXSendEvent(display,event->requestor,False,NoEventMask,(XEvent*)&result);
  }
}

/***********************************************************************
 *           EVENT_SelectionClear
 */
static void EVENT_SelectionClear( HWND hWnd, XSelectionClearEvent *event )
{
  Atom xaClipboard = TSXInternAtom(event->display, "CLIPBOARD", False);

  if (event->selection == XA_PRIMARY || event->selection == xaClipboard)
      X11DRV_CLIPBOARD_ReleaseSelection( event->selection, event->window, hWnd );
}

/***********************************************************************
 *           EVENT_PropertyNotify
 *   We use this to release resources like Pixmaps when a selection
 *   client no longer needs them.
 */
static void EVENT_PropertyNotify( XPropertyEvent *event )
{
  /* Check if we have any resources to free */
  TRACE("Received PropertyNotify event: \n");

  switch(event->state)
  {
    case PropertyDelete:
    {
      TRACE("\tPropertyDelete for atom %s on window %ld\n",
            TSXGetAtomName(event->display, event->atom), (long)event->window);

      if (X11DRV_IsSelectionOwner())
          X11DRV_CLIPBOARD_FreeResources( event->atom );
      break;
    }

    case PropertyNewValue:
    {
      TRACE("\tPropertyNewValue for atom %s on window %ld\n\n",
            TSXGetAtomName(event->display, event->atom), (long)event->window);
      break;
    }

    default:
      break;
  }
}

/**********************************************************************
 *           EVENT_DropFromOffix
 *
 * don't know if it still works (last Changlog is from 96/11/04)
 */
static void EVENT_DropFromOffiX( HWND hWnd, XClientMessageEvent *event )
{
  unsigned long		data_length;
  unsigned long		aux_long;
  unsigned char*	p_data = NULL;
  union {
    Atom		atom_aux;
    struct {
      int x;
      int y;
    } pt_aux;
    int		i;
  }		u;
  int			x, y;
  BOOL16	        bAccept;
  HGLOBAL16		hDragInfo = GlobalAlloc16( GMEM_SHARE | GMEM_ZEROINIT, sizeof(DRAGINFO16));
  LPDRAGINFO16          lpDragInfo = (LPDRAGINFO16) GlobalLock16(hDragInfo);
  SEGPTR		spDragInfo = K32WOWGlobalLock16(hDragInfo);
  Window		w_aux_root, w_aux_child;
  WND*                  pWnd;

  if( !lpDragInfo || !spDragInfo ) return;

  pWnd = WIN_FindWndPtr(hWnd);

  TSXQueryPointer( event->display, get_whole_window(pWnd), &w_aux_root, &w_aux_child,
                   &x, &y, (int *) &u.pt_aux.x, (int *) &u.pt_aux.y,
                   (unsigned int*)&aux_long);

  lpDragInfo->hScope = hWnd;
  lpDragInfo->pt.x = (INT16)x; lpDragInfo->pt.y = (INT16)y;

  /* find out drop point and drop window */
  if( x < 0 || y < 0 ||
      x > (pWnd->rectWindow.right - pWnd->rectWindow.left) ||
      y > (pWnd->rectWindow.bottom - pWnd->rectWindow.top) )
    {   bAccept = pWnd->dwExStyle & WS_EX_ACCEPTFILES; x = y = 0; }
  else
    {
      bAccept = DRAG_QueryUpdate( hWnd, spDragInfo, TRUE );
      x = lpDragInfo->pt.x; y = lpDragInfo->pt.y;
    }
  WIN_ReleaseWndPtr(pWnd);

  GlobalFree16( hDragInfo );

    if (!bAccept) return;

    TSXGetWindowProperty( event->display, DefaultRootWindow(event->display),
                          dndSelection, 0, 65535, FALSE,
                          AnyPropertyType, &u.atom_aux, (int *) &u.pt_aux.y,
                          &data_length, &aux_long, &p_data);

    if( !aux_long && p_data)  /* don't bother if > 64K */
    {
        signed char *p = (signed char*) p_data;
        char *p_drop;

        aux_long = 0;
        while( *p )  /* calculate buffer size */
        {
            p_drop = p;
            if((u.i = *p) != -1 )
            {
                INT len = GetShortPathNameA( p, NULL, 0 );
                if (len) aux_long += len + 1;
                else *p = -1;
            }
            p += strlen(p) + 1;
        }
        if( aux_long && aux_long < 65535 )
        {
            HDROP                 hDrop;
            DROPFILES *lpDrop;

            aux_long += sizeof(DROPFILES) + 1;
            hDrop = GlobalAlloc( GMEM_SHARE, aux_long );
            lpDrop = (DROPFILES*)GlobalLock( hDrop );

            if( lpDrop )
            {
                WND *pDropWnd = WIN_FindWndPtr( lpDragInfo->hScope );
                lpDrop->pFiles = sizeof(DROPFILES);
                lpDrop->pt.x = x;
                lpDrop->pt.y = y;
                lpDrop->fNC =
                    ( x < (pDropWnd->rectClient.left - pDropWnd->rectWindow.left)  ||
                      y < (pDropWnd->rectClient.top - pDropWnd->rectWindow.top)    ||
                      x > (pDropWnd->rectClient.right - pDropWnd->rectWindow.left) ||
                      y > (pDropWnd->rectClient.bottom - pDropWnd->rectWindow.top) );
                lpDrop->fWide = FALSE;
                WIN_ReleaseWndPtr(pDropWnd);
                p_drop = (char *)(lpDrop + 1);
                p = p_data;
                while(*p)
                {
                    if( *p != -1 ) /* use only "good" entries */
                    {
                        GetShortPathNameA( p, p_drop, 65535 );
                        p_drop += strlen( p_drop ) + 1;
                    }
                    p += strlen(p) + 1;
                }
                *p_drop = '\0';
                PostMessageA( hWnd, WM_DROPFILES, (WPARAM)hDrop, 0L );
            }
        }
    }
    if( p_data ) TSXFree(p_data);
}

/**********************************************************************
 *           EVENT_DropURLs
 *
 * drop items are separated by \n
 * each item is prefixed by its mime type
 *
 * event->data.l[3], event->data.l[4] contains drop x,y position
 */
static void EVENT_DropURLs( HWND hWnd, XClientMessageEvent *event )
{
  unsigned long	data_length;
  unsigned long	aux_long, drop_len = 0;
  unsigned char	*p_data = NULL; /* property data */
  char		*p_drop = NULL;
  char          *p, *next;
  int		x, y;
  DROPFILES *lpDrop;
  HDROP hDrop;
  union {
    Atom	atom_aux;
    int         i;
    Window      w_aux;
  }		u; /* unused */

  if (!(GetWindowLongW( hWnd, GWL_EXSTYLE ) & WS_EX_ACCEPTFILES)) return;

  TSXGetWindowProperty( event->display, DefaultRootWindow(event->display),
			dndSelection, 0, 65535, FALSE,
			AnyPropertyType, &u.atom_aux, &u.i,
			&data_length, &aux_long, &p_data);
  if (aux_long)
    WARN("property too large, truncated!\n");
  TRACE("urls=%s\n", p_data);

  if( !aux_long && p_data) {	/* don't bother if > 64K */
    /* calculate length */
    p = p_data;
    next = strchr(p, '\n');
    while (p) {
      if (next) *next=0;
      if (strncmp(p,"file:",5) == 0 ) {
	INT len = GetShortPathNameA( p+5, NULL, 0 );
	if (len) drop_len += len + 1;
      }
      if (next) {
	*next = '\n';
	p = next + 1;
	next = strchr(p, '\n');
      } else {
	p = NULL;
      }
    }

    if( drop_len && drop_len < 65535 ) {
      TSXQueryPointer( event->display, root_window, &u.w_aux, &u.w_aux,
		       &x, &y, &u.i, &u.i, &u.i);

      drop_len += sizeof(DROPFILES) + 1;
      hDrop = GlobalAlloc( GMEM_SHARE, drop_len );
      lpDrop = (DROPFILES *) GlobalLock( hDrop );

      if( lpDrop ) {
          WND *pDropWnd = WIN_FindWndPtr( hWnd );
	  lpDrop->pFiles = sizeof(DROPFILES);
	  lpDrop->pt.x = (INT)x;
	  lpDrop->pt.y = (INT)y;
	  lpDrop->fNC =
	    ( x < (pDropWnd->rectClient.left - pDropWnd->rectWindow.left)  ||
	      y < (pDropWnd->rectClient.top - pDropWnd->rectWindow.top)    ||
	      x > (pDropWnd->rectClient.right - pDropWnd->rectWindow.left) ||
	      y > (pDropWnd->rectClient.bottom - pDropWnd->rectWindow.top) );
	  lpDrop->fWide = FALSE;
	  p_drop = (char*)(lpDrop + 1);
          WIN_ReleaseWndPtr(pDropWnd);
      }

      /* create message content */
      if (p_drop) {
	p = p_data;
	next = strchr(p, '\n');
	while (p) {
	  if (next) *next=0;
	  if (strncmp(p,"file:",5) == 0 ) {
	    INT len = GetShortPathNameA( p+5, p_drop, 65535 );
	    if (len) {
	      TRACE("drop file %s as %s\n", p+5, p_drop);
	      p_drop += len+1;
	    } else {
	      WARN("can't convert file %s to dos name \n", p+5);
	    }
	  } else {
	    WARN("unknown mime type %s\n", p);
	  }
	  if (next) {
	    *next = '\n';
	    p = next + 1;
	    next = strchr(p, '\n');
	  } else {
	    p = NULL;
	  }
	  *p_drop = '\0';
	}

        GlobalUnlock(hDrop);
        PostMessageA( hWnd, WM_DROPFILES, (WPARAM)hDrop, 0L );
      }
    }
    if( p_data ) TSXFree(p_data);
  }
}

/**********************************************************************
 *           EVENT_ClientMessage
 */
static void EVENT_ClientMessage( HWND hWnd, XClientMessageEvent *event )
{
  if (event->message_type != None && event->format == 32) {

    if (event->message_type == wmManager) {
        if (event->data.l[1] == netSysTraySelection) {
            TRACE_(systray)("New NETWM systray manager detected, id=%ld\n", event->data.l[2]);
            /* NOTE: It turns out that the ability to detect when a new tray applet joins the
             * desktop is not as helpful as you might think. In order to do something useful with it,
             * we would need to be able to "store" icons unmapped as children of the root window while
             * no tray applet is available.
             *
             * The basic problem is that tray icons are always destroyed when the applet is removed.
             * This is apparently an issue with X itself, which the upcoming XFIXES extension should hopefully
             * address. The EggTrayIcon code which will be soon moving into GTK+ doesn't attempt to handle this
             * situation, so for now neither do we.
             *
             * This is theoretically fixable in Wine with enough work, we just have to modify the code in
             * shell32/systray.c to save the image and recreate the window on demand. Exactly how the
             * communication between the x11drv and shell32 takes place is left as an excercise for the
             * reader.
             *                         -Mike Hearn (3rd August 2003)
             */
        }
    } else if (event->message_type == xembed) {
        char* opcode;
        switch (event->data.l[1]) {
          case 0: opcode = "XEMBED_EMBEDDED_NOTIFY"; break;
          case 1: opcode = "XEMBED_WINDOW_ACTIVATE"; break;
          case 2: opcode = "XEMBED_WINDOW_DEACTIVATE"; break;
          case 3: opcode = "XEMBED_REQUEST_FOCUS"; break;
          case 4: opcode = "XEMBED_FOCUS_IN"; break;
          case 5: opcode = "XEMBED_FOCUS_OUT"; break;
          case 6: opcode = "XEMBED_FOCUS_NEXT"; break;
          case 7: opcode = "XEMEBD_FOCUS_PREV"; break;
          case 10: opcode = "XEMBED_MODALITY_ON"; break;
          case 11: opcode = "XEMBED_MODALITY_OFF"; break;
          case 12: opcode = "XEMBED_REGISTER_ACCELERATOR"; break;
          case 13: opcode = "XEMBED_UNREGISTER_ACCELERATOR"; break;
          case 14: opcode = "XEMBED_ACTIVATE_ACCELERATOR"; break;
          default: opcode = "[Unknown opcode]"; break;
        }
        TRACE_(systray)("XEmbed message, opcode is %s : %ld\n", opcode, event->data.l[1]);
        /* we currently don't handle these messages */
    }
    else if ((event->message_type == wmProtocols) &&
             (((Atom) event->data.l[0]) == wmDeleteWindow))
    {
        /* Ignore the delete window request if the window has been disabled */
        if (!(GetWindowLongA( hWnd, GWL_STYLE ) & WS_DISABLED))
            PostMessageA( hWnd, WM_SYSCOMMAND, SC_CLOSE, 0 );
    }
    else if (event->message_type == dndProtocol)
    {
        /* query window (drag&drop event contains only drag window) */
        Window root, child;
        int root_x, root_y, child_x, child_y;
        unsigned int u;
        TSXQueryPointer( event->display, root_window, &root, &child,
                         &root_x, &root_y, &child_x, &child_y, &u);
        if (TSXFindContext( event->display, child, winContext, (char **)&hWnd ) != 0) return;
        if (event->data.l[0] == DndFile || event->data.l[0] == DndFiles)
            EVENT_DropFromOffiX(hWnd, event);
        else if (event->data.l[0] == DndURL)
            EVENT_DropURLs(hWnd, event);
    }
    else {
#if 0
      /* enable this if you want to see the message */
      unsigned char* p_data = NULL;
      union {
	unsigned long	l;
	int            	i;
	Atom		atom;
      } u; /* unused */
      TSXGetWindowProperty( event->display, DefaultRootWindow(event->display),
			    dndSelection, 0, 65535, FALSE,
			    AnyPropertyType, &u.atom, &u.i,
			    &u.l, &u.l, &p_data);
      TRACE("message_type=%ld, data=%ld,%ld,%ld,%ld,%ld, msg=%s\n",
	    event->message_type, event->data.l[0], event->data.l[1],
	    event->data.l[2], event->data.l[3], event->data.l[4],
	    p_data);
#endif
      TRACE("unrecognized ClientMessage\n" );
    }
  }
}


/**********************************************************************
 *              X11DRV_EVENT_SetInputMethod
 */
INPUT_TYPE X11DRV_EVENT_SetInputMethod(INPUT_TYPE type)
{
  INPUT_TYPE prev = current_input_type;

  /* Flag not used yet */
  in_transition = FALSE;
  current_input_type = type;

  return prev;
}

#ifdef HAVE_LIBXXF86DGA2
/**********************************************************************
 *              X11DRV_EVENT_SetDGAStatus
 */
void X11DRV_EVENT_SetDGAStatus(HWND hwnd, int event_base)
{
  if (event_base < 0) {
    DGAUsed = FALSE;
    DGAhwnd = 0;
  } else {
    DGAUsed = TRUE;
    DGAhwnd = hwnd;
    DGAMotionEventType = event_base + MotionNotify;
    DGAButtonPressEventType = event_base + ButtonPress;
    DGAButtonReleaseEventType = event_base + ButtonRelease;
    DGAKeyPressEventType = event_base + KeyPress;
    DGAKeyReleaseEventType = event_base + KeyRelease;
  }
}
#endif
