wine/windows/x11drv/event.c

2179 lines
65 KiB
C

/*
* X11 event driver
*
* Copyright 1993 Alexandre Julliard
* 1999 Noel Borthwick
*/
#include "config.h"
#ifndef X_DISPLAY_MISSING
#include <X11/Xatom.h>
#include <X11/keysym.h>
#include "ts_xlib.h"
#include "ts_xresource.h"
#include "ts_xutil.h"
#ifdef HAVE_LIBXXSHM
#include "ts_xshm.h"
#endif
#ifdef HAVE_LIBXXF86DGA2
#include "ts_xf86dga2.h"
#endif
#include <assert.h>
#include <string.h>
#include "callback.h"
#include "clipboard.h"
#include "dce.h"
#include "debugtools.h"
#include "drive.h"
#include "heap.h"
#include "input.h"
#include "keyboard.h"
#include "message.h"
#include "mouse.h"
#include "options.h"
#include "queue.h"
#include "shell.h"
#include "win.h"
#include "winpos.h"
#include "services.h"
#include "file.h"
#include "windef.h"
#include "x11drv.h"
DEFAULT_DEBUG_CHANNEL(event);
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 dndProtocol;
extern Atom dndSelection;
extern void X11DRV_KEYBOARD_UpdateState(void);
extern void X11DRV_KEYBOARD_HandleEvent(WND *pWnd, XKeyEvent *event);
#define NB_BUTTONS 3 /* Windows can handle 3 buttons */
#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 CALLBACK EVENT_Flush( ULONG_PTR arg );
static void CALLBACK EVENT_ProcessAllEvents( ULONG_PTR arg );
static void EVENT_ProcessEvent( XEvent *event );
/* Event handlers */
static void EVENT_Key( HWND hWnd, XKeyEvent *event );
static void EVENT_ButtonPress( HWND hWnd, XButtonEvent *event );
static void EVENT_ButtonRelease( HWND hWnd, XButtonEvent *event );
static void EVENT_MotionNotify( HWND hWnd, XMotionEvent *event );
static void EVENT_FocusIn( HWND hWnd, XFocusChangeEvent *event );
static void EVENT_FocusOut( HWND hWnd, XFocusChangeEvent *event );
static void EVENT_Expose( HWND hWnd, XExposeEvent *event );
static void EVENT_GraphicsExpose( HWND hWnd, XGraphicsExposeEvent *event );
static void EVENT_ConfigureNotify( HWND hWnd, XConfigureEvent *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 );
static void EVENT_MapNotify( HWND pWnd, XMapEvent *event );
static void EVENT_UnmapNotify( HWND pWnd, XUnmapEvent *event );
static void EVENT_MappingNotify( XMappingEvent *event );
#ifdef HAVE_LIBXXSHM
static void EVENT_ShmCompletion( XShmCompletionEvent *event );
static int ShmAvailable, ShmCompletionType;
extern int XShmGetEventBase( Display * );/* Missing prototype for function in libXext. */
#endif
#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;
static void EVENT_DGAMotionEvent( XDGAMotionEvent *event );
static void EVENT_DGAButtonPressEvent( XDGAButtonEvent *event );
static void EVENT_DGAButtonReleaseEvent( XDGAButtonEvent *event );
#endif
/* Usable only with OLVWM - compile option perhaps?
static void EVENT_EnterNotify( HWND hWnd, XCrossingEvent *event );
*/
static void EVENT_GetGeometry( Window win, int *px, int *py,
unsigned int *pwidth, unsigned int *pheight );
static BOOL bUserRepaintDisabled = TRUE;
/* 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 */
/***********************************************************************
* EVENT_Init
*/
BOOL X11DRV_EVENT_Init(void)
{
#ifdef HAVE_LIBXXSHM
ShmAvailable = XShmQueryExtension( display );
if (ShmAvailable) {
ShmCompletionType = XShmGetEventBase( display ) + ShmCompletion;
}
#endif
/* Install the X event processing callback */
SERVICE_AddObject( FILE_DupUnixHandle( ConnectionNumber(display),
GENERIC_READ | SYNCHRONIZE ),
EVENT_ProcessAllEvents, 0 );
/* Install the XFlush timer callback */
if ( Options.synchronous )
TSXSynchronize( display, True );
else
SERVICE_AddTimer( 200000L, EVENT_Flush, 0 );
return TRUE;
}
/***********************************************************************
* EVENT_Flush
*/
static void CALLBACK EVENT_Flush( ULONG_PTR arg )
{
TSXFlush( display );
}
/***********************************************************************
* EVENT_ProcessAllEvents
*/
static void CALLBACK EVENT_ProcessAllEvents( ULONG_PTR arg )
{
XEvent event;
TRACE( "called (thread %lx).\n", GetCurrentThreadId() );
EnterCriticalSection( &X11DRV_CritSection );
while ( XPending( display ) )
{
XNextEvent( display, &event );
LeaveCriticalSection( &X11DRV_CritSection );
EVENT_ProcessEvent( &event );
EnterCriticalSection( &X11DRV_CritSection );
}
LeaveCriticalSection( &X11DRV_CritSection );
}
/***********************************************************************
* EVENT_Synchronize
*
* Synchronize with the X server. Should not be used too often.
*/
void X11DRV_EVENT_Synchronize( void )
{
TSXSync( display, False );
EVENT_ProcessAllEvents( 0 );
}
/***********************************************************************
* EVENT_UserRepaintDisable
*/
void X11DRV_EVENT_UserRepaintDisable( BOOL bDisabled )
{
bUserRepaintDisabled = bDisabled;
}
/***********************************************************************
* EVENT_ProcessEvent
*
* Process an X event.
*/
static void EVENT_ProcessEvent( XEvent *event )
{
HWND hWnd;
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_LIBXXSHM
if (ShmAvailable && (event->type == ShmCompletionType)) {
EVENT_ShmCompletion( (XShmCompletionEvent*)event );
return;
}
#endif
#ifdef HAVE_LIBXXF86DGA2
if (DGAUsed) {
if (event->type == DGAMotionEventType) {
TRACE("DGAMotionEvent received.\n");
EVENT_DGAMotionEvent((XDGAMotionEvent *) event);
return;
}
if (event->type == DGAButtonPressEventType) {
TRACE("DGAButtonPressEvent received.\n");
EVENT_DGAButtonPressEvent((XDGAButtonEvent *) event);
return;
}
if (event->type == DGAButtonReleaseEventType) {
TRACE("DGAButtonReleaseEvent received.\n");
EVENT_DGAButtonReleaseEvent((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 = PosX;
ke.y = PosY;
ke.x_root = -1;
ke.y_root = -1;
ke.state = evt->state;
ke.keycode = evt->keycode;
ke.same_screen = TRUE;
X11DRV_KEYBOARD_HandleEvent(NULL, &ke);
return;
}
}
#endif
if ( TSXFindContext( display, event->xany.window, winContext,
(char **)&hWnd ) != 0) {
if ( event->type == ClientMessage) {
/* query window (drag&drop event contains only drag window) */
Window root, child;
int root_x, root_y, child_x, child_y;
unsigned u;
TSXQueryPointer( display, X11DRV_GetXRootWindow(), &root, &child,
&root_x, &root_y, &child_x, &child_y, &u);
if (TSXFindContext( display, child, winContext, (char **)&hWnd ) != 0)
return;
} else {
hWnd = 0; /* Not for a registered window */
}
}
if ( !hWnd && event->xany.window != X11DRV_GetXRootWindow()
&& event->type != PropertyNotify
&& event->type != MappingNotify)
ERR("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:
EVENT_Key( hWnd, (XKeyEvent*)event );
break;
case ButtonPress:
EVENT_ButtonPress( hWnd, (XButtonEvent*)event );
break;
case ButtonRelease:
EVENT_ButtonRelease( hWnd, (XButtonEvent*)event );
break;
case MotionNotify:
/* Wine between two fast machines across the overloaded campus
ethernet gets very boged down in MotionEvents. The following
simply finds the last motion event in the queue and drops
the rest. On a good link events are servered before they build
up so this doesn't take place. On a slow link this may cause
problems if the event order is important. I'm not yet seen
of any problems. Jon 7/6/96.
*/
if ((current_input_type == X11DRV_INPUT_ABSOLUTE) &&
(in_transition == FALSE))
/* Only cumulate events if in absolute mode */
while (TSXCheckTypedWindowEvent(display,((XAnyEvent *)event)->window,
MotionNotify, event));
EVENT_MotionNotify( hWnd, (XMotionEvent*)event );
break;
case FocusIn:
{
WND *pWndLastFocus = 0;
XWindowAttributes win_attr;
BOOL bIsDisabled;
XFocusChangeEvent *xfocChange = (XFocusChangeEvent*)event;
if (!hWnd || bUserRepaintDisabled) 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 */
if ( TSXFindContext( xfocChange->display, glastXFocusWin, winContext,
(char **)&pWndLastFocus ) == 0 )
{
if ( TSXGetWindowAttributes( display, glastXFocusWin, &win_attr ) &&
(win_attr.map_state == IsViewable) )
{
TSXSetInputFocus( xfocChange->display, glastXFocusWin, RevertToParent, CurrentTime );
EVENT_Synchronize();
break;
}
}
}
EVENT_FocusIn( hWnd, xfocChange );
break;
}
case FocusOut:
{
/* Save the last window which had the focus */
XFocusChangeEvent *xfocChange = (XFocusChangeEvent*)event;
glastXFocusWin = xfocChange->window;
if (!hWnd || bUserRepaintDisabled) return;
if (GetWindowLongA( hWnd, GWL_STYLE ) & WS_DISABLED) glastXFocusWin = 0;
EVENT_FocusOut( hWnd, (XFocusChangeEvent*)event );
break;
}
case Expose:
if (bUserRepaintDisabled) return;
EVENT_Expose( hWnd, (XExposeEvent *)event );
break;
case GraphicsExpose:
if (bUserRepaintDisabled) return;
EVENT_GraphicsExpose( hWnd, (XGraphicsExposeEvent *)event );
break;
case ConfigureNotify:
if (!hWnd || bUserRepaintDisabled) return;
EVENT_ConfigureNotify( hWnd, (XConfigureEvent*)event );
break;
case SelectionRequest:
if (!hWnd || bUserRepaintDisabled) return;
EVENT_SelectionRequest( hWnd, (XSelectionRequestEvent *)event, FALSE );
break;
case SelectionClear:
if (!hWnd || bUserRepaintDisabled) return;
EVENT_SelectionClear( hWnd, (XSelectionClearEvent*) event );
break;
case PropertyNotify:
EVENT_PropertyNotify( (XPropertyEvent *)event );
break;
case ClientMessage:
if (!hWnd || bUserRepaintDisabled) return;
EVENT_ClientMessage( hWnd, (XClientMessageEvent *) event );
break;
#if 0
case EnterNotify:
EVENT_EnterNotify( hWnd, (XCrossingEvent *) event );
break;
#endif
case NoExpose:
break;
case MapNotify:
if (!hWnd || bUserRepaintDisabled) return;
EVENT_MapNotify( hWnd, (XMapEvent *)event );
break;
case UnmapNotify:
if (!hWnd || bUserRepaintDisabled) return;
EVENT_UnmapNotify( hWnd, (XUnmapEvent *)event );
break;
case MappingNotify:
EVENT_MappingNotify( (XMappingEvent *) event );
break;
default:
WARN("Unprocessed event %s for hwnd %04x\n",
event_names[event->type], hWnd );
break;
}
TRACE( "returns.\n" );
}
/***********************************************************************
* EVENT_QueryZOrder
*
* Synchronize internal z-order with the window manager's.
*/
static BOOL __check_query_condition( WND** pWndA, WND** pWndB )
{
/* return TRUE if we have at least two managed windows */
for( *pWndB = NULL; *pWndA; *pWndA = (*pWndA)->next )
if( (*pWndA)->flags & WIN_MANAGED &&
(*pWndA)->dwStyle & WS_VISIBLE ) break;
if( *pWndA )
for( *pWndB = (*pWndA)->next; *pWndB; *pWndB = (*pWndB)->next )
if( (*pWndB)->flags & WIN_MANAGED &&
(*pWndB)->dwStyle & WS_VISIBLE ) break;
return ((*pWndB) != NULL);
}
static Window __get_common_ancestor( Window A, Window B,
Window** children, unsigned* total )
{
/* find the real root window */
Window root, *childrenB;
unsigned totalB;
do
{
TSXQueryTree( display, A, &root, &A, children, total );
TSXQueryTree( display, B, &root, &B, &childrenB, &totalB );
if( childrenB ) TSXFree( childrenB );
if( *children ) TSXFree( *children ), *children = NULL;
} while( A != B && A && B );
if( A && B )
{
TSXQueryTree( display, A, &root, &B, children, total );
return A;
}
return 0 ;
}
static Window __get_top_decoration( Window w, Window ancestor )
{
Window* children, root, prev = w, parent = w;
unsigned total;
do
{
w = parent;
TSXQueryTree( display, w, &root, &parent, &children, &total );
if( children ) TSXFree( children );
} while( parent && parent != ancestor );
TRACE("\t%08x -> %08x\n", (unsigned)prev, (unsigned)w );
return ( parent ) ? w : 0 ;
}
static unsigned __td_lookup( Window w, Window* list, unsigned max )
{
unsigned i;
for( i = max - 1; i >= 0; i-- ) if( list[i] == w ) break;
return i;
}
static HWND EVENT_QueryZOrder( HWND hWndCheck)
{
HWND hwndInsertAfter = HWND_TOP;
WND *pWndCheck = WIN_FindWndPtr(hWndCheck);
WND *pDesktop = WIN_GetDesktop();
WND *pWnd, *pWndZ = WIN_LockWndPtr(pDesktop->child);
Window w, parent, *children = NULL;
unsigned total, check, pos, best;
if( !__check_query_condition(&pWndZ, &pWnd) )
{
WIN_ReleaseWndPtr(pWndCheck);
WIN_ReleaseWndPtr(pDesktop->child);
WIN_ReleaseDesktop();
return hwndInsertAfter;
}
WIN_LockWndPtr(pWndZ);
WIN_LockWndPtr(pWnd);
WIN_ReleaseWndPtr(pDesktop->child);
WIN_ReleaseDesktop();
parent = __get_common_ancestor( X11DRV_WND_GetXWindow(pWndZ),
X11DRV_WND_GetXWindow(pWnd),
&children, &total );
if( parent && children )
{
/* w is the ancestor if pWndCheck that is a direct descendant of 'parent' */
w = __get_top_decoration( X11DRV_WND_GetXWindow(pWndCheck), parent );
if( w != children[total-1] ) /* check if at the top */
{
/* X child at index 0 is at the bottom, at index total-1 is at the top */
check = __td_lookup( w, children, total );
best = total;
for( WIN_UpdateWndPtr(&pWnd,pWndZ); pWnd;WIN_UpdateWndPtr(&pWnd,pWnd->next))
{
/* go through all windows in Wine z-order... */
if( pWnd != pWndCheck )
{
if( !(pWnd->flags & WIN_MANAGED) ||
!(w = __get_top_decoration( X11DRV_WND_GetXWindow(pWnd), parent )) )
continue;
pos = __td_lookup( w, children, total );
if( pos < best && pos > check )
{
/* find a nearest Wine window precedes
* pWndCheck in the real z-order... */
best = pos;
hwndInsertAfter = pWnd->hwndSelf;
}
if( best - check == 1 ) break;
}
}
}
}
if( children ) TSXFree( children );
WIN_ReleaseWndPtr(pWnd);
WIN_ReleaseWndPtr(pWndZ);
WIN_ReleaseWndPtr(pWndCheck);
return hwndInsertAfter;
}
/***********************************************************************
* X11DRV_EVENT_XStateToKeyState
*
* Translate a X event state (Button1Mask, ShiftMask, etc...) to
* a Windows key state (MK_SHIFT, MK_CONTROL, etc...)
*/
WORD X11DRV_EVENT_XStateToKeyState( int state )
{
int kstate = 0;
if (state & Button1Mask) kstate |= MK_LBUTTON;
if (state & Button2Mask) kstate |= MK_MBUTTON;
if (state & Button3Mask) kstate |= MK_RBUTTON;
if (state & ShiftMask) kstate |= MK_SHIFT;
if (state & ControlMask) kstate |= MK_CONTROL;
return kstate;
}
/***********************************************************************
* EVENT_Expose
*/
static void EVENT_Expose( HWND hWnd, XExposeEvent *event )
{
RECT rect;
WND *pWnd = WIN_FindWndPtr(hWnd);
/* Make position relative to client area instead of window */
rect.left = event->x - (pWnd? (pWnd->rectClient.left - pWnd->rectWindow.left) : 0);
rect.top = event->y - (pWnd? (pWnd->rectClient.top - pWnd->rectWindow.top) : 0);
rect.right = rect.left + event->width;
rect.bottom = rect.top + event->height;
WIN_ReleaseWndPtr(pWnd);
Callout.RedrawWindow( hWnd, &rect, 0,
RDW_INVALIDATE | RDW_FRAME | RDW_ALLCHILDREN | RDW_ERASE |
(event->count ? 0 : RDW_ERASENOW) );
}
/***********************************************************************
* EVENT_GraphicsExpose
*
* This is needed when scrolling area is partially obscured
* by non-Wine X window.
*/
static void EVENT_GraphicsExpose( HWND hWnd, XGraphicsExposeEvent *event )
{
RECT rect;
WND *pWnd = WIN_FindWndPtr(hWnd);
/* Make position relative to client area instead of window */
rect.left = event->x - (pWnd? (pWnd->rectClient.left - pWnd->rectWindow.left) : 0);
rect.top = event->y - (pWnd? (pWnd->rectClient.top - pWnd->rectWindow.top) : 0);
rect.right = rect.left + event->width;
rect.bottom = rect.top + event->height;
WIN_ReleaseWndPtr(pWnd);
Callout.RedrawWindow( hWnd, &rect, 0,
RDW_INVALIDATE | RDW_ALLCHILDREN | RDW_ERASE |
(event->count ? 0 : RDW_ERASENOW) );
}
/***********************************************************************
* EVENT_Key
*
* Handle a X key event
*/
static void EVENT_Key( HWND hWnd, XKeyEvent *event )
{
WND *pWnd = WIN_FindWndPtr(hWnd);
X11DRV_KEYBOARD_HandleEvent( pWnd, event );
WIN_ReleaseWndPtr(pWnd);
}
/***********************************************************************
* EVENT_MotionNotify
*/
static void EVENT_MotionNotify( HWND hWnd, XMotionEvent *event )
{
if (current_input_type == X11DRV_INPUT_ABSOLUTE) {
WND *pWnd = WIN_FindWndPtr(hWnd);
int xOffset = pWnd? pWnd->rectWindow.left : 0;
int yOffset = pWnd? pWnd->rectWindow.top : 0;
WIN_ReleaseWndPtr(pWnd);
MOUSE_SendEvent( MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE,
xOffset + event->x, yOffset + event->y,
X11DRV_EVENT_XStateToKeyState( event->state ),
event->time - MSG_WineStartTicks,
hWnd);
} else {
MOUSE_SendEvent( MOUSEEVENTF_MOVE,
event->x_root, event->y_root,
X11DRV_EVENT_XStateToKeyState( event->state ),
event->time - MSG_WineStartTicks,
hWnd);
}
}
/***********************************************************************
* EVENT_ButtonPress
*/
static void EVENT_ButtonPress( HWND hWnd, XButtonEvent *event )
{
static WORD statusCodes[NB_BUTTONS] =
{ MOUSEEVENTF_LEFTDOWN, MOUSEEVENTF_MIDDLEDOWN, MOUSEEVENTF_RIGHTDOWN };
int buttonNum = event->button - 1;
WND *pWnd = WIN_FindWndPtr(hWnd);
int xOffset = pWnd? pWnd->rectWindow.left : 0;
int yOffset = pWnd? pWnd->rectWindow.top : 0;
WORD keystate;
WIN_ReleaseWndPtr(pWnd);
if (buttonNum >= NB_BUTTONS) return;
/*
* Get the compatible keystate
*/
keystate = X11DRV_EVENT_XStateToKeyState( event->state );
/*
* Make sure that the state of the button that was just
* pressed is "down".
*/
switch (buttonNum)
{
case 0:
keystate |= MK_LBUTTON;
break;
case 1:
keystate |= MK_MBUTTON;
break;
case 2:
keystate |= MK_RBUTTON;
break;
}
MOUSE_SendEvent( statusCodes[buttonNum],
xOffset + event->x, yOffset + event->y,
keystate,
event->time - MSG_WineStartTicks,
hWnd);
}
/***********************************************************************
* EVENT_ButtonRelease
*/
static void EVENT_ButtonRelease( HWND hWnd, XButtonEvent *event )
{
static WORD statusCodes[NB_BUTTONS] =
{ MOUSEEVENTF_LEFTUP, MOUSEEVENTF_MIDDLEUP, MOUSEEVENTF_RIGHTUP };
int buttonNum = event->button - 1;
WND *pWnd = WIN_FindWndPtr(hWnd);
int xOffset = pWnd? pWnd->rectWindow.left : 0;
int yOffset = pWnd? pWnd->rectWindow.top : 0;
WORD keystate;
WIN_ReleaseWndPtr(pWnd);
if (buttonNum >= NB_BUTTONS) return;
/*
* Get the compatible keystate
*/
keystate = X11DRV_EVENT_XStateToKeyState( event->state );
/*
* Make sure that the state of the button that was just
* released is "up".
*/
switch (buttonNum)
{
case 0:
keystate &= ~MK_LBUTTON;
break;
case 1:
keystate &= ~MK_MBUTTON;
break;
case 2:
keystate &= ~MK_RBUTTON;
break;
}
MOUSE_SendEvent( statusCodes[buttonNum],
xOffset + event->x, yOffset + event->y,
keystate,
event->time - MSG_WineStartTicks,
hWnd);
}
/**********************************************************************
* EVENT_FocusIn
*/
static void EVENT_FocusIn( HWND hWnd, XFocusChangeEvent *event )
{
if (event->detail != NotifyPointer)
if (hWnd != GetForegroundWindow())
{
SetForegroundWindow( hWnd );
X11DRV_KEYBOARD_UpdateState();
}
}
/**********************************************************************
* EVENT_FocusOut
*
* Note: only top-level override-redirect windows get FocusOut events.
*/
static void EVENT_FocusOut( HWND hWnd, XFocusChangeEvent *event )
{
if (event->detail != NotifyPointer)
if (hWnd == GetForegroundWindow())
{
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())
SetForegroundWindow( 0 );
}
}
/**********************************************************************
* X11DRV_EVENT_CheckFocus
*/
BOOL X11DRV_EVENT_CheckFocus(void)
{
HWND hWnd;
Window xW;
int state;
TSXGetInputFocus(display, &xW, &state);
if( xW == None ||
TSXFindContext(display, xW, winContext, (char **)&hWnd) )
return FALSE;
return TRUE;
}
/**********************************************************************
* EVENT_GetGeometry
*
* Helper function for ConfigureNotify handling.
* Get the new geometry of a window relative to the root window.
*/
static void EVENT_GetGeometry( Window win, int *px, int *py,
unsigned int *pwidth, unsigned int *pheight )
{
Window root, top;
int x, y, width, height, border, depth;
EnterCriticalSection( &X11DRV_CritSection );
/* Get the geometry of the window */
XGetGeometry( display, win, &root, &x, &y, &width, &height,
&border, &depth );
/* Translate the window origin to root coordinates */
XTranslateCoordinates( display, win, root, 0, 0, &x, &y, &top );
LeaveCriticalSection( &X11DRV_CritSection );
*px = x;
*py = y;
*pwidth = width;
*pheight = height;
}
/**********************************************************************
* EVENT_ConfigureNotify
*
* The ConfigureNotify event is only selected on top-level windows
* when the -managed flag is used.
*/
static void EVENT_ConfigureNotify( HWND hWnd, XConfigureEvent *event )
{
RECT rectWindow;
int x, y, flags = 0;
unsigned int width, height;
HWND newInsertAfter, oldInsertAfter;
/* Get geometry and Z-order according to X */
EVENT_GetGeometry( event->window, &x, &y, &width, &height );
newInsertAfter = EVENT_QueryZOrder( hWnd );
/* Get geometry and Z-order according to Wine */
/*
* Needs to find the first Visible Window above the current one
*/
oldInsertAfter = hWnd;
for (;;)
{
oldInsertAfter = GetWindow( oldInsertAfter, GW_HWNDPREV );
if (!oldInsertAfter)
{
oldInsertAfter = HWND_TOP;
break;
}
if (GetWindowLongA( oldInsertAfter, GWL_STYLE ) & WS_VISIBLE) break;
}
/* Compare what has changed */
GetWindowRect( hWnd, &rectWindow );
if ( rectWindow.left == x && rectWindow.top == y )
flags |= SWP_NOMOVE;
else
TRACE_(win)( "%04x moving from (%d,%d) to (%d,%d)\n", hWnd,
rectWindow.left, rectWindow.top, x, y );
if ( rectWindow.right - rectWindow.left == width
&& rectWindow.bottom - rectWindow.top == height )
flags |= SWP_NOSIZE;
else
TRACE_(win)( "%04x resizing from (%d,%d) to (%d,%d)\n", hWnd,
rectWindow.right - rectWindow.left,
rectWindow.bottom - rectWindow.top, width, height );
if ( newInsertAfter == oldInsertAfter )
flags |= SWP_NOZORDER;
else
TRACE_(win)( "%04x restacking from after %04x to after %04x\n", hWnd,
oldInsertAfter, newInsertAfter );
/* If anything changed, call SetWindowPos */
if ( flags != (SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER) )
SetWindowPos( hWnd, newInsertAfter, x, y, width, height,
flags | SWP_NOACTIVATE | SWP_WINE_NOHOSTMOVE );
}
/***********************************************************************
* EVENT_SelectionRequest_TARGETS
* Service a TARGETS selection request event
*/
static Atom EVENT_SelectionRequest_TARGETS( 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*)HEAP_xalloc( GetProcessHeap(), 0, cTargets * sizeof(Atom));
/* 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...", 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( Window requestor, Atom target, Atom rprop )
{
HANDLE16 hText;
LPSTR text;
int size,i,j;
char* lpstr = 0;
char *itemFmtName;
int xRc;
/*
* 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_TEXT, CLIPBOARD_GetFormatName(CF_TEXT));
TSXFree(itemFmtName);
hText = GetClipboardData16(CF_TEXT);
if ( !hText )
return None;
text = GlobalLock16(hText);
if (!text)
return None;
size = GlobalSize16(hText);
/* remove carriage returns */
lpstr = (char*)HEAP_xalloc( GetProcessHeap(), 0, size-- );
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);
GlobalUnlock16(hText);
HeapFree( GetProcessHeap(), 0, lpstr );
return rprop;
}
/***********************************************************************
* EVENT_SelectionRequest_PIXMAP
* Service a PIXMAP selection request event
*/
static Atom EVENT_SelectionRequest_PIXMAP( 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(hdc, hwnd);
}
else if (wFormat == CF_BITMAP)
{
HWND hwnd = GetOpenClipboardWindow();
HDC hdc = GetDC(hwnd);
pixmap = X11DRV_BITMAP_CreatePixmapFromBitmap(hClipData, hdc);
ReleaseDC(hdc, hwnd);
}
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( 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 = GetClipboardData16(wFormat);
if( hClipData && (lpClipData = GlobalLock16(hClipData)) )
{
cBytes = GlobalSize16(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);
GlobalUnlock16(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 )
{
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!", 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 )
{
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( 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( request, event->target, rprop );
}
else if(event->target == XA_PIXMAP) /* Convert DIB's to Pixmaps */
{
/* XA_PIXMAP selection request */
rprop = EVENT_SelectionRequest_PIXMAP( 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( request, XA_PIXMAP, rprop );
}
else if(X11DRV_CLIPBOARD_IsNativeProperty(event->target)) /* <WCF>* */
{
/* All <WCF> selection requests */
rprop = EVENT_SelectionRequest_WCF( 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(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: ");
switch(event->state)
{
case PropertyDelete:
{
TRACE("\tPropertyDelete for atom %s on window %ld\n",
TSXGetAtomName(event->display, event->atom), (long)event->window);
if (X11DRV_CLIPBOARD_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(DRAGINFO));
LPDRAGINFO lpDragInfo = (LPDRAGINFO) GlobalLock16(hDragInfo);
SEGPTR spDragInfo = (SEGPTR) WIN16_GlobalLock16(hDragInfo);
Window w_aux_root, w_aux_child;
WND* pDropWnd;
WND* pWnd;
if( !lpDragInfo || !spDragInfo ) return;
pWnd = WIN_FindWndPtr(hWnd);
TSXQueryPointer( display, X11DRV_WND_GetXWindow(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;
}
pDropWnd = WIN_FindWndPtr( lpDragInfo->hScope );
WIN_ReleaseWndPtr(pWnd);
GlobalFree16( hDragInfo );
if( bAccept )
{
TSXGetWindowProperty( display, DefaultRootWindow(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 */
{
char *p = (char*) p_data;
char *p_drop;
aux_long = 0;
while( *p ) /* calculate buffer size */
{
p_drop = p;
if((u.i = *p) != -1 )
u.i = DRIVE_FindDriveRoot( (const char **)&p_drop );
if( u.i == -1 ) *p = -1; /* mark as "bad" */
else
{
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 )
{
HDROP16 hDrop;
LPDROPFILESTRUCT16 lpDrop;
aux_long += sizeof(DROPFILESTRUCT16) + 1;
hDrop = (HDROP16)GlobalAlloc16( GMEM_SHARE, aux_long );
lpDrop = (LPDROPFILESTRUCT16) GlobalLock16( hDrop );
if( lpDrop )
{
lpDrop->wSize = sizeof(DROPFILESTRUCT16);
lpDrop->ptMousePos.x = (INT16)x;
lpDrop->ptMousePos.y = (INT16)y;
lpDrop->fInNonClientArea = (BOOL16)
( 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) );
p_drop = ((char*)lpDrop) + sizeof(DROPFILESTRUCT16);
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';
PostMessage16( hWnd, WM_DROPFILES,
(WPARAM16)hDrop, 0L );
}
}
}
if( p_data ) TSXFree(p_data);
} /* WS_EX_ACCEPTFILES */
WIN_ReleaseWndPtr(pDropWnd);
}
/**********************************************************************
* 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 )
{
WND *pDropWnd;
WND *pWnd;
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, drop32 = FALSE ;
union {
Atom atom_aux;
int i;
Window w_aux;
} u; /* unused */
union {
HDROP16 h16;
HDROP h32;
} hDrop;
pWnd = WIN_FindWndPtr(hWnd);
drop32 = pWnd->flags & WIN_ISWIN32;
if (!(pWnd->dwExStyle & WS_EX_ACCEPTFILES))
{
WIN_ReleaseWndPtr(pWnd);
return;
}
WIN_ReleaseWndPtr(pWnd);
TSXGetWindowProperty( display, DefaultRootWindow(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( display, X11DRV_GetXRootWindow(), &u.w_aux, &u.w_aux,
&x, &y, &u.i, &u.i, &u.i);
pDropWnd = WIN_FindWndPtr( hWnd );
if (drop32) {
LPDROPFILESTRUCT lpDrop;
drop_len += sizeof(DROPFILESTRUCT) + 1;
hDrop.h32 = (HDROP)GlobalAlloc( GMEM_SHARE, drop_len );
lpDrop = (LPDROPFILESTRUCT) GlobalLock( hDrop.h32 );
if( lpDrop ) {
lpDrop->lSize = sizeof(DROPFILESTRUCT);
lpDrop->ptMousePos.x = (INT)x;
lpDrop->ptMousePos.y = (INT)y;
lpDrop->fInNonClientArea = (BOOL)
( 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->fWideChar = FALSE;
p_drop = ((char*)lpDrop) + sizeof(DROPFILESTRUCT);
}
} else {
LPDROPFILESTRUCT16 lpDrop;
drop_len += sizeof(DROPFILESTRUCT16) + 1;
hDrop.h16 = (HDROP16)GlobalAlloc16( GMEM_SHARE, drop_len );
lpDrop = (LPDROPFILESTRUCT16) GlobalLock16( hDrop.h16 );
if( lpDrop ) {
lpDrop->wSize = sizeof(DROPFILESTRUCT16);
lpDrop->ptMousePos.x = (INT16)x;
lpDrop->ptMousePos.y = (INT16)y;
lpDrop->fInNonClientArea = (BOOL16)
( 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) );
p_drop = ((char*)lpDrop) + sizeof(DROPFILESTRUCT16);
}
}
/* 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';
}
if (drop32) {
/* can not use PostMessage32A because it is currently based on
* PostMessage16 and WPARAM32 would be truncated to WPARAM16
*/
GlobalUnlock(hDrop.h32);
SendMessageA( hWnd, WM_DROPFILES,
(WPARAM)hDrop.h32, 0L );
} else {
GlobalUnlock16(hDrop.h16);
PostMessage16( hWnd, WM_DROPFILES,
(WPARAM16)hDrop.h16, 0L );
}
}
WIN_ReleaseWndPtr(pDropWnd);
}
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 == wmProtocols) &&
(((Atom) event->data.l[0]) == wmDeleteWindow))
{
/* Ignore the delete window request if the window has been disabled
* and we are in managed mode. This is to disallow applications from
* being closed by the window manager while in a modal state.
*/
BOOL bIsDisabled;
bIsDisabled = GetWindowLongA( hWnd, GWL_STYLE ) & WS_DISABLED;
if ( !Options.managed || !bIsDisabled )
PostMessage16( hWnd, WM_SYSCOMMAND, SC_CLOSE, 0 );
}
else if ( event->message_type == dndProtocol &&
(event->data.l[0] == DndFile || event->data.l[0] == DndFiles) )
EVENT_DropFromOffiX(hWnd, event);
else if ( event->message_type == dndProtocol &&
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( display, DefaultRootWindow(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" );
}
}
}
/**********************************************************************
* EVENT_EnterNotify
*
* Install colormap when Wine window is focused in
* self-managed mode with private colormap
*/
#if 0
void EVENT_EnterNotify( HWND hWnd, XCrossingEvent *event )
{
if( !Options.managed && X11DRV_GetXRootWindow() == DefaultRootWindow(display) &&
(COLOR_GetSystemPaletteFlags() & COLOR_PRIVATE) && GetFocus() )
TSXInstallColormap( display, X11DRV_PALETTE_GetColormap() );
}
#endif
/**********************************************************************
* EVENT_MapNotify
*/
void EVENT_MapNotify( HWND hWnd, XMapEvent *event )
{
HWND hwndFocus = GetFocus();
WND *wndFocus = WIN_FindWndPtr(hwndFocus);
WND *pWnd = WIN_FindWndPtr(hWnd);
if (pWnd->flags & WIN_MANAGED)
{
DCE_InvalidateDCE( pWnd, &pWnd->rectWindow );
pWnd->dwStyle &= ~WS_MINIMIZE;
pWnd->dwStyle |= WS_VISIBLE;
ShowOwnedPopups(hWnd,TRUE);
}
WIN_ReleaseWndPtr(pWnd);
if (hwndFocus && IsChild( hWnd, hwndFocus ))
X11DRV_WND_SetFocus(wndFocus);
WIN_ReleaseWndPtr(wndFocus);
return;
}
/**********************************************************************
* EVENT_MapNotify
*/
void EVENT_UnmapNotify( HWND hWnd, XUnmapEvent *event )
{
WND *pWnd = WIN_FindWndPtr(hWnd);
if (pWnd->flags & WIN_MANAGED)
{
EndMenu();
if( pWnd->dwStyle & WS_VISIBLE )
{
pWnd->dwStyle |= WS_MINIMIZE;
pWnd->dwStyle &= ~WS_VISIBLE;
ShowOwnedPopups(hWnd,FALSE);
}
}
WIN_ReleaseWndPtr(pWnd);
}
/***********************************************************************
* EVENT_MappingNotify
*/
static void EVENT_MappingNotify( XMappingEvent *event )
{
TSXRefreshKeyboardMapping(event);
/* reinitialize Wine-X11 driver keyboard table */
X11DRV_KEYBOARD_Init();
}
/**********************************************************************
* 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;
}
}
/* DGA2 event handlers */
static void EVENT_DGAMotionEvent( XDGAMotionEvent *event )
{
MOUSE_SendEvent( MOUSEEVENTF_MOVE,
event->dx, event->dy,
X11DRV_EVENT_XStateToKeyState( event->state ),
event->time - MSG_WineStartTicks,
DGAhwnd );
}
static void EVENT_DGAButtonPressEvent( XDGAButtonEvent *event )
{
static WORD statusCodes[NB_BUTTONS] =
{ MOUSEEVENTF_LEFTDOWN, MOUSEEVENTF_MIDDLEDOWN, MOUSEEVENTF_RIGHTDOWN };
int buttonNum = event->button - 1;
WORD keystate;
if (buttonNum >= NB_BUTTONS) return;
keystate = X11DRV_EVENT_XStateToKeyState( event->state );
switch (buttonNum)
{
case 0:
keystate |= MK_LBUTTON;
break;
case 1:
keystate |= MK_MBUTTON;
break;
case 2:
keystate |= MK_RBUTTON;
break;
}
MOUSE_SendEvent( statusCodes[buttonNum],
0, 0,
keystate,
event->time - MSG_WineStartTicks,
DGAhwnd );
}
static void EVENT_DGAButtonReleaseEvent( XDGAButtonEvent *event )
{
static WORD statusCodes[NB_BUTTONS] =
{ MOUSEEVENTF_LEFTUP, MOUSEEVENTF_MIDDLEUP, MOUSEEVENTF_RIGHTUP };
int buttonNum = event->button - 1;
WORD keystate;
if (buttonNum >= NB_BUTTONS) return;
keystate = X11DRV_EVENT_XStateToKeyState( event->state );
switch (buttonNum)
{
case 0:
keystate &= ~MK_LBUTTON;
break;
case 1:
keystate &= ~MK_MBUTTON;
break;
case 2:
keystate &= ~MK_RBUTTON;
break;
}
MOUSE_SendEvent( statusCodes[buttonNum],
0, 0,
keystate,
event->time - MSG_WineStartTicks,
DGAhwnd );
}
#endif
#ifdef HAVE_LIBXXSHM
/*
Normal XShm operation:
X11 service thread app thread
------------- ----------------- ------------------------
(idle) ddraw calls XShmPutImage
(copies data) (waiting for shm_event)
ShmCompletion -> (waiting for shm_event)
(idle) signal shm_event ->
(idle) returns to app
However, this situation can occur for some reason:
X11 service thread app thread
------------- ----------------- ------------------------
Expose ->
WM_ERASEBKGND? ->
(waiting for app) ddraw calls XShmPutImage
(copies data) (waiting for app) (waiting for shm_event)
ShmCompletion (waiting for app) (waiting for shm_event)
(idle) DEADLOCK DEADLOCK
which is why I also wait for shm_read and do XCheckTypedEvent()
calls in the wait loop. This results in:
X11 service thread app thread
------------- ----------------- ------------------------
ShmCompletion (waiting for app) waking up on shm_read
(idle) (waiting for app) XCheckTypedEvent() -> signal shm_event
(waiting for app) returns
(idle)
*/
typedef struct {
Drawable draw;
LONG state, waiter;
HANDLE sema;
} shm_qs;
/* FIXME: this is not pretty */
static HANDLE shm_read = 0;
#define SHM_MAX_Q 4
static volatile shm_qs shm_q[SHM_MAX_Q];
static void EVENT_ShmCompletion( XShmCompletionEvent *event )
{
int n;
TRACE("Got ShmCompletion for drawable %ld (time %ld)\n", event->drawable, GetTickCount() );
for (n=0; n<SHM_MAX_Q; n++)
if ((shm_q[n].draw == event->drawable) && (shm_q[n].state == 0)) {
HANDLE sema = shm_q[n].sema;
if (!InterlockedCompareExchange((PVOID*)&shm_q[n].state, (PVOID)1, (PVOID)0)) {
ReleaseSemaphore(sema, 1, NULL);
TRACE("Signaling ShmCompletion (#%d) (semaphore %x)\n", n, sema);
}
return;
}
ERR("Got ShmCompletion for unknown drawable %ld\n", event->drawable );
}
int X11DRV_EVENT_PrepareShmCompletion( Drawable dw )
{
int n;
if (!shm_read)
shm_read = FILE_DupUnixHandle( ConnectionNumber(display), GENERIC_READ | SYNCHRONIZE );
for (n=0; n<SHM_MAX_Q; n++)
if (!shm_q[n].draw)
if (!InterlockedCompareExchange((PVOID*)&shm_q[n].draw, (PVOID)dw, (PVOID)0))
break;
if (n>=SHM_MAX_Q) {
ERR("Maximum number of outstanding ShmCompletions exceeded!\n");
return 0;
}
shm_q[n].state = 0;
if (!shm_q[n].sema) {
shm_q[n].sema = CreateSemaphoreA( NULL, 0, 256, NULL );
TRACE("Allocated ShmCompletion slots have been increased to %d, new semaphore is %x\n", n+1, shm_q[n].sema);
}
TRACE("Prepared ShmCompletion (#%d) wait for drawable %ld (thread %lx) (time %ld)\n", n, dw, GetCurrentThreadId(), GetTickCount() );
return n+1;
}
static void X11DRV_EVENT_WaitReplaceShmCompletionInternal( int *compl, Drawable dw, int creat )
{
int n = *compl;
LONG nn, st;
HANDLE sema;
if ((!n) || (creat && (!shm_q[n-1].draw))) {
nn = X11DRV_EVENT_PrepareShmCompletion(dw);
if (!(n=(LONG)InterlockedCompareExchange((PVOID*)compl, (PVOID)nn, (PVOID)n)))
return;
/* race for compl lost, clear slot */
shm_q[nn-1].draw = 0;
return;
}
if (dw && (shm_q[n-1].draw != dw)) {
/* this shouldn't happen with the current ddraw implementation */
FIXME("ShmCompletion replace with different drawable!\n");
return;
}
sema = shm_q[n-1].sema;
if (!sema) {
/* nothing to wait on (PrepareShmCompletion not done yet?), so probably nothing to wait for */
return;
}
nn = InterlockedExchangeAdd((PLONG)&shm_q[n-1].waiter, 1);
if ((!shm_q[n-1].draw) || (shm_q[n-1].state == 2)) {
/* too late, the wait was just cleared (wait complete) */
TRACE("Wait skip for ShmCompletion (#%d) (thread %lx) (time %ld) (semaphore %x)\n", n-1, GetCurrentThreadId(), GetTickCount(), sema);
} else {
TRACE("Waiting for ShmCompletion (#%d) (thread %lx) (time %ld) (semaphore %x)\n", n-1, GetCurrentThreadId(), GetTickCount(), sema);
if (nn) {
/* another thread is already waiting, let the primary waiter do the dirty work
* (to avoid TSX critical section contention - that could get really slow) */
WaitForSingleObject( sema, INFINITE );
} else
/* we're primary waiter - first check if it's already triggered */
if ( WaitForSingleObject( sema, 0 ) != WAIT_OBJECT_0 ) {
/* nope, may need to poll X event queue, in case the service thread is blocked */
XEvent event;
HANDLE hnd[2];
hnd[0] = sema;
hnd[1] = shm_read;
do {
/* check X event queue */
if (TSXCheckTypedEvent( display, ShmCompletionType, &event)) {
EVENT_ProcessEvent( &event );
}
} while ( WaitForMultipleObjects(2, hnd, FALSE, INFINITE) > WAIT_OBJECT_0 );
}
TRACE("Wait complete (thread %lx) (time %ld)\n", GetCurrentThreadId(), GetTickCount() );
/* clear wait */
st = InterlockedExchange((LPLONG)&shm_q[n-1].state, 2);
if (st != 2) {
/* first waiter to return, release all other waiters */
nn = shm_q[n-1].waiter;
TRACE("Signaling %ld additional ShmCompletion (#%d) waiter(s), semaphore %x\n", nn-1, n-1, sema);
ReleaseSemaphore(sema, nn-1, NULL);
}
}
nn = InterlockedDecrement((LPLONG)&shm_q[n-1].waiter);
if (!nn) {
/* last waiter to return, replace drawable and prepare new wait */
shm_q[n-1].draw = dw;
shm_q[n-1].state = 0;
}
}
void X11DRV_EVENT_WaitReplaceShmCompletion( int *compl, Drawable dw )
{
X11DRV_EVENT_WaitReplaceShmCompletionInternal( compl, dw, 1 );
}
void X11DRV_EVENT_WaitShmCompletion( int compl )
{
if (!compl) return;
X11DRV_EVENT_WaitReplaceShmCompletionInternal( &compl, 0, 0 );
}
void X11DRV_EVENT_WaitShmCompletions( Drawable dw )
{
int n;
for (n=0; n<SHM_MAX_Q; n++)
if (shm_q[n].draw == dw)
X11DRV_EVENT_WaitShmCompletion( n+1 );
}
#endif /* defined(HAVE_LIBXXSHM) */
#endif /* !defined(X_DISPLAY_MISSING) */