Implemented Mousewheel support.
[wine/dcerpc.git] / windows / x11drv / event.c
blob3e63b9eae1222b0a4df04475ded8c7f85cc84de6
1 /*
2 * X11 event driver
4 * Copyright 1993 Alexandre Julliard
5 * 1999 Noel Borthwick
6 */
8 #include "config.h"
10 #ifndef X_DISPLAY_MISSING
12 #include <X11/Xatom.h>
13 #include <X11/keysym.h>
14 #include "ts_xlib.h"
15 #include "ts_xresource.h"
16 #include "ts_xutil.h"
17 #ifdef HAVE_LIBXXSHM
18 #include "ts_xshm.h"
19 #endif
21 #ifdef HAVE_LIBXXF86DGA2
22 #include "ts_xf86dga2.h"
23 #endif
25 #include <assert.h>
26 #include <string.h>
27 #include "callback.h"
28 #include "clipboard.h"
29 #include "dce.h"
30 #include "debugtools.h"
31 #include "drive.h"
32 #include "heap.h"
33 #include "input.h"
34 #include "keyboard.h"
35 #include "message.h"
36 #include "mouse.h"
37 #include "options.h"
38 #include "queue.h"
39 #include "shell.h"
40 #include "win.h"
41 #include "winpos.h"
42 #include "services.h"
43 #include "file.h"
44 #include "windef.h"
45 #include "x11drv.h"
47 DEFAULT_DEBUG_CHANNEL(event);
48 DECLARE_DEBUG_CHANNEL(win);
50 /* X context to associate a hwnd to an X window */
51 extern XContext winContext;
53 extern Atom wmProtocols;
54 extern Atom wmDeleteWindow;
55 extern Atom dndProtocol;
56 extern Atom dndSelection;
58 extern void X11DRV_KEYBOARD_UpdateState(void);
59 extern void X11DRV_KEYBOARD_HandleEvent(WND *pWnd, XKeyEvent *event);
61 #define NB_BUTTONS 5 /* Windows can handle 3 buttons and the wheel too */
64 #define DndNotDnd -1 /* OffiX drag&drop */
65 #define DndUnknown 0
66 #define DndRawData 1
67 #define DndFile 2
68 #define DndFiles 3
69 #define DndText 4
70 #define DndDir 5
71 #define DndLink 6
72 #define DndExe 7
74 #define DndEND 8
76 #define DndURL 128 /* KDE drag&drop */
78 /* The last X window which had the focus */
79 static Window glastXFocusWin = 0;
81 static const char * const event_names[] =
83 "", "", "KeyPress", "KeyRelease", "ButtonPress", "ButtonRelease",
84 "MotionNotify", "EnterNotify", "LeaveNotify", "FocusIn", "FocusOut",
85 "KeymapNotify", "Expose", "GraphicsExpose", "NoExpose", "VisibilityNotify",
86 "CreateNotify", "DestroyNotify", "UnmapNotify", "MapNotify", "MapRequest",
87 "ReparentNotify", "ConfigureNotify", "ConfigureRequest", "GravityNotify",
88 "ResizeRequest", "CirculateNotify", "CirculateRequest", "PropertyNotify",
89 "SelectionClear", "SelectionRequest", "SelectionNotify", "ColormapNotify",
90 "ClientMessage", "MappingNotify"
94 static void CALLBACK EVENT_Flush( ULONG_PTR arg );
95 static void CALLBACK EVENT_ProcessAllEvents( ULONG_PTR arg );
96 static void EVENT_ProcessEvent( XEvent *event );
98 /* Event handlers */
99 static void EVENT_Key( HWND hWnd, XKeyEvent *event );
100 static void EVENT_ButtonPress( HWND hWnd, XButtonEvent *event );
101 static void EVENT_ButtonRelease( HWND hWnd, XButtonEvent *event );
102 static void EVENT_MotionNotify( HWND hWnd, XMotionEvent *event );
103 static void EVENT_FocusIn( HWND hWnd, XFocusChangeEvent *event );
104 static void EVENT_FocusOut( HWND hWnd, XFocusChangeEvent *event );
105 static void EVENT_Expose( HWND hWnd, XExposeEvent *event );
106 static void EVENT_GraphicsExpose( HWND hWnd, XGraphicsExposeEvent *event );
107 static void EVENT_ConfigureNotify( HWND hWnd, XConfigureEvent *event );
108 static void EVENT_SelectionRequest( HWND hWnd, XSelectionRequestEvent *event, BOOL bIsMultiple );
109 static void EVENT_SelectionClear( HWND hWnd, XSelectionClearEvent *event);
110 static void EVENT_PropertyNotify( XPropertyEvent *event );
111 static void EVENT_ClientMessage( HWND hWnd, XClientMessageEvent *event );
112 static void EVENT_MapNotify( HWND pWnd, XMapEvent *event );
113 static void EVENT_UnmapNotify( HWND pWnd, XUnmapEvent *event );
114 static void EVENT_MappingNotify( XMappingEvent *event );
116 #ifdef HAVE_LIBXXSHM
117 static void EVENT_ShmCompletion( XShmCompletionEvent *event );
118 static int ShmAvailable, ShmCompletionType;
119 extern int XShmGetEventBase( Display * );/* Missing prototype for function in libXext. */
120 #endif
122 #ifdef HAVE_LIBXXF86DGA2
123 static int DGAMotionEventType;
124 static int DGAButtonPressEventType;
125 static int DGAButtonReleaseEventType;
126 static int DGAKeyPressEventType;
127 static int DGAKeyReleaseEventType;
129 static BOOL DGAUsed = FALSE;
130 static HWND DGAhwnd = 0;
132 static void EVENT_DGAMotionEvent( XDGAMotionEvent *event );
133 static void EVENT_DGAButtonPressEvent( XDGAButtonEvent *event );
134 static void EVENT_DGAButtonReleaseEvent( XDGAButtonEvent *event );
135 #endif
137 /* Usable only with OLVWM - compile option perhaps?
138 static void EVENT_EnterNotify( HWND hWnd, XCrossingEvent *event );
141 static void EVENT_GetGeometry( Window win, int *px, int *py,
142 unsigned int *pwidth, unsigned int *pheight );
145 static BOOL bUserRepaintDisabled = TRUE;
147 /* Static used for the current input method */
148 static INPUT_TYPE current_input_type = X11DRV_INPUT_ABSOLUTE;
149 static BOOL in_transition = FALSE; /* This is not used as for today */
151 /***********************************************************************
152 * EVENT_Init
154 BOOL X11DRV_EVENT_Init(void)
156 #ifdef HAVE_LIBXXSHM
157 ShmAvailable = XShmQueryExtension( display );
158 if (ShmAvailable) {
159 ShmCompletionType = XShmGetEventBase( display ) + ShmCompletion;
161 #endif
163 /* Install the X event processing callback */
164 SERVICE_AddObject( FILE_DupUnixHandle( ConnectionNumber(display),
165 GENERIC_READ | SYNCHRONIZE ),
166 EVENT_ProcessAllEvents, 0 );
168 /* Install the XFlush timer callback */
169 if ( Options.synchronous )
170 TSXSynchronize( display, True );
171 else
172 SERVICE_AddTimer( 200000L, EVENT_Flush, 0 );
174 return TRUE;
177 /***********************************************************************
178 * EVENT_Flush
180 static void CALLBACK EVENT_Flush( ULONG_PTR arg )
182 TSXFlush( display );
185 /***********************************************************************
186 * EVENT_ProcessAllEvents
188 static void CALLBACK EVENT_ProcessAllEvents( ULONG_PTR arg )
190 XEvent event;
192 TRACE( "called (thread %lx).\n", GetCurrentThreadId() );
194 EnterCriticalSection( &X11DRV_CritSection );
195 while ( XPending( display ) )
197 XNextEvent( display, &event );
199 LeaveCriticalSection( &X11DRV_CritSection );
200 EVENT_ProcessEvent( &event );
201 EnterCriticalSection( &X11DRV_CritSection );
203 LeaveCriticalSection( &X11DRV_CritSection );
206 /***********************************************************************
207 * EVENT_Synchronize
209 * Synchronize with the X server. Should not be used too often.
211 void X11DRV_EVENT_Synchronize( void )
213 TSXSync( display, False );
214 EVENT_ProcessAllEvents( 0 );
217 /***********************************************************************
218 * EVENT_UserRepaintDisable
220 void X11DRV_EVENT_UserRepaintDisable( BOOL bDisabled )
222 bUserRepaintDisabled = bDisabled;
225 /***********************************************************************
226 * EVENT_ProcessEvent
228 * Process an X event.
230 static void EVENT_ProcessEvent( XEvent *event )
232 HWND hWnd;
234 TRACE( "called.\n" );
236 switch (event->type)
238 case SelectionNotify: /* all of these should be caught by XCheckTypedWindowEvent() */
239 FIXME("Got SelectionNotify - must not happen!\n");
240 /* fall through */
242 /* We get all these because of StructureNotifyMask.
243 This check is placed here to avoid getting error messages below,
244 as X might send some of these even for windows that have already
245 been deleted ... */
246 case CirculateNotify:
247 case CreateNotify:
248 case DestroyNotify:
249 case GravityNotify:
250 case ReparentNotify:
251 return;
254 #ifdef HAVE_LIBXXSHM
255 if (ShmAvailable && (event->type == ShmCompletionType)) {
256 EVENT_ShmCompletion( (XShmCompletionEvent*)event );
257 return;
259 #endif
261 #ifdef HAVE_LIBXXF86DGA2
262 if (DGAUsed) {
263 if (event->type == DGAMotionEventType) {
264 TRACE("DGAMotionEvent received.\n");
265 EVENT_DGAMotionEvent((XDGAMotionEvent *) event);
266 return;
268 if (event->type == DGAButtonPressEventType) {
269 TRACE("DGAButtonPressEvent received.\n");
270 EVENT_DGAButtonPressEvent((XDGAButtonEvent *) event);
271 return;
273 if (event->type == DGAButtonReleaseEventType) {
274 TRACE("DGAButtonReleaseEvent received.\n");
275 EVENT_DGAButtonReleaseEvent((XDGAButtonEvent *) event);
276 return;
278 if ((event->type == DGAKeyPressEventType) ||
279 (event->type == DGAKeyReleaseEventType)) {
280 /* Fill a XKeyEvent to send to EVENT_Key */
281 XKeyEvent ke;
282 XDGAKeyEvent *evt = (XDGAKeyEvent *) event;
284 TRACE("DGAKeyPress/ReleaseEvent received.\n");
286 if (evt->type == DGAKeyReleaseEventType)
287 ke.type = KeyRelease;
288 else
289 ke.type = KeyPress;
290 ke.serial = evt->serial;
291 ke.send_event = FALSE;
292 ke.display = evt->display;
293 ke.window = 0;
294 ke.root = 0;
295 ke.subwindow = 0;
296 ke.time = evt->time;
297 ke.x = PosX;
298 ke.y = PosY;
299 ke.x_root = -1;
300 ke.y_root = -1;
301 ke.state = evt->state;
302 ke.keycode = evt->keycode;
303 ke.same_screen = TRUE;
305 X11DRV_KEYBOARD_HandleEvent(NULL, &ke);
306 return;
309 #endif
311 if ( TSXFindContext( display, event->xany.window, winContext,
312 (char **)&hWnd ) != 0) {
313 if ( event->type == ClientMessage) {
314 /* query window (drag&drop event contains only drag window) */
315 Window root, child;
316 int root_x, root_y, child_x, child_y;
317 unsigned u;
318 TSXQueryPointer( display, X11DRV_GetXRootWindow(), &root, &child,
319 &root_x, &root_y, &child_x, &child_y, &u);
320 if (TSXFindContext( display, child, winContext, (char **)&hWnd ) != 0)
321 return;
322 } else {
323 hWnd = 0; /* Not for a registered window */
327 if ( !hWnd && event->xany.window != X11DRV_GetXRootWindow()
328 && event->type != PropertyNotify
329 && event->type != MappingNotify)
330 ERR("Got event %s for unknown Window %08lx\n",
331 event_names[event->type], event->xany.window );
332 else
333 TRACE("Got event %s for hwnd %04x\n",
334 event_names[event->type], hWnd );
336 switch(event->type)
338 case KeyPress:
339 case KeyRelease:
340 EVENT_Key( hWnd, (XKeyEvent*)event );
341 break;
343 case ButtonPress:
344 EVENT_ButtonPress( hWnd, (XButtonEvent*)event );
345 break;
347 case ButtonRelease:
348 EVENT_ButtonRelease( hWnd, (XButtonEvent*)event );
349 break;
351 case MotionNotify:
352 /* Wine between two fast machines across the overloaded campus
353 ethernet gets very boged down in MotionEvents. The following
354 simply finds the last motion event in the queue and drops
355 the rest. On a good link events are servered before they build
356 up so this doesn't take place. On a slow link this may cause
357 problems if the event order is important. I'm not yet seen
358 of any problems. Jon 7/6/96.
360 if ((current_input_type == X11DRV_INPUT_ABSOLUTE) &&
361 (in_transition == FALSE))
362 /* Only cumulate events if in absolute mode */
363 while (TSXCheckTypedWindowEvent(display,((XAnyEvent *)event)->window,
364 MotionNotify, event));
365 EVENT_MotionNotify( hWnd, (XMotionEvent*)event );
366 break;
368 case FocusIn:
370 WND *pWndLastFocus = 0;
371 XWindowAttributes win_attr;
372 BOOL bIsDisabled;
373 XFocusChangeEvent *xfocChange = (XFocusChangeEvent*)event;
375 if (!hWnd || bUserRepaintDisabled) return;
377 bIsDisabled = GetWindowLongA( hWnd, GWL_STYLE ) & WS_DISABLED;
379 /* If the window has been disabled and we are in managed mode,
380 * revert the X focus back to the last focus window. This is to disallow
381 * the window manager from switching focus away while the app is
382 * in a modal state.
384 if ( Options.managed && bIsDisabled && glastXFocusWin)
386 /* Change focus only if saved focus window is registered and viewable */
387 if ( TSXFindContext( xfocChange->display, glastXFocusWin, winContext,
388 (char **)&pWndLastFocus ) == 0 )
390 if ( TSXGetWindowAttributes( display, glastXFocusWin, &win_attr ) &&
391 (win_attr.map_state == IsViewable) )
393 TSXSetInputFocus( xfocChange->display, glastXFocusWin, RevertToParent, CurrentTime );
394 EVENT_Synchronize();
395 break;
400 EVENT_FocusIn( hWnd, xfocChange );
401 break;
404 case FocusOut:
406 /* Save the last window which had the focus */
407 XFocusChangeEvent *xfocChange = (XFocusChangeEvent*)event;
408 glastXFocusWin = xfocChange->window;
409 if (!hWnd || bUserRepaintDisabled) return;
410 if (GetWindowLongA( hWnd, GWL_STYLE ) & WS_DISABLED) glastXFocusWin = 0;
411 EVENT_FocusOut( hWnd, (XFocusChangeEvent*)event );
412 break;
415 case Expose:
416 if (bUserRepaintDisabled) return;
417 EVENT_Expose( hWnd, (XExposeEvent *)event );
418 break;
420 case GraphicsExpose:
421 if (bUserRepaintDisabled) return;
422 EVENT_GraphicsExpose( hWnd, (XGraphicsExposeEvent *)event );
423 break;
425 case ConfigureNotify:
426 if (!hWnd || bUserRepaintDisabled) return;
427 EVENT_ConfigureNotify( hWnd, (XConfigureEvent*)event );
428 break;
430 case SelectionRequest:
431 if (!hWnd || bUserRepaintDisabled) return;
432 EVENT_SelectionRequest( hWnd, (XSelectionRequestEvent *)event, FALSE );
433 break;
435 case SelectionClear:
436 if (!hWnd || bUserRepaintDisabled) return;
437 EVENT_SelectionClear( hWnd, (XSelectionClearEvent*) event );
438 break;
440 case PropertyNotify:
441 EVENT_PropertyNotify( (XPropertyEvent *)event );
442 break;
444 case ClientMessage:
445 if (!hWnd || bUserRepaintDisabled) return;
446 EVENT_ClientMessage( hWnd, (XClientMessageEvent *) event );
447 break;
449 #if 0
450 case EnterNotify:
451 EVENT_EnterNotify( hWnd, (XCrossingEvent *) event );
452 break;
453 #endif
455 case NoExpose:
456 break;
458 case MapNotify:
459 if (!hWnd || bUserRepaintDisabled) return;
460 EVENT_MapNotify( hWnd, (XMapEvent *)event );
461 break;
463 case UnmapNotify:
464 if (!hWnd || bUserRepaintDisabled) return;
465 EVENT_UnmapNotify( hWnd, (XUnmapEvent *)event );
466 break;
468 case MappingNotify:
469 EVENT_MappingNotify( (XMappingEvent *) event );
470 break;
472 default:
473 WARN("Unprocessed event %s for hwnd %04x\n",
474 event_names[event->type], hWnd );
475 break;
477 TRACE( "returns.\n" );
480 /***********************************************************************
481 * EVENT_QueryZOrder
483 * Synchronize internal z-order with the window manager's.
485 static BOOL __check_query_condition( WND** pWndA, WND** pWndB )
487 /* return TRUE if we have at least two managed windows */
489 for( *pWndB = NULL; *pWndA; *pWndA = (*pWndA)->next )
490 if( (*pWndA)->flags & WIN_MANAGED &&
491 (*pWndA)->dwStyle & WS_VISIBLE ) break;
492 if( *pWndA )
493 for( *pWndB = (*pWndA)->next; *pWndB; *pWndB = (*pWndB)->next )
494 if( (*pWndB)->flags & WIN_MANAGED &&
495 (*pWndB)->dwStyle & WS_VISIBLE ) break;
496 return ((*pWndB) != NULL);
499 static Window __get_common_ancestor( Window A, Window B,
500 Window** children, unsigned* total )
502 /* find the real root window */
504 Window root, *childrenB;
505 unsigned totalB;
509 TSXQueryTree( display, A, &root, &A, children, total );
510 TSXQueryTree( display, B, &root, &B, &childrenB, &totalB );
511 if( childrenB ) TSXFree( childrenB );
512 if( *children ) TSXFree( *children ), *children = NULL;
513 } while( A != B && A && B );
515 if( A && B )
517 TSXQueryTree( display, A, &root, &B, children, total );
518 return A;
520 return 0 ;
523 static Window __get_top_decoration( Window w, Window ancestor )
525 Window* children, root, prev = w, parent = w;
526 unsigned total;
530 w = parent;
531 TSXQueryTree( display, w, &root, &parent, &children, &total );
532 if( children ) TSXFree( children );
533 } while( parent && parent != ancestor );
534 TRACE("\t%08x -> %08x\n", (unsigned)prev, (unsigned)w );
535 return ( parent ) ? w : 0 ;
538 static unsigned __td_lookup( Window w, Window* list, unsigned max )
540 unsigned i;
541 for( i = max - 1; i >= 0; i-- ) if( list[i] == w ) break;
542 return i;
545 static HWND EVENT_QueryZOrder( HWND hWndCheck)
547 HWND hwndInsertAfter = HWND_TOP;
548 WND *pWndCheck = WIN_FindWndPtr(hWndCheck);
549 WND *pDesktop = WIN_GetDesktop();
550 WND *pWnd, *pWndZ = WIN_LockWndPtr(pDesktop->child);
551 Window w, parent, *children = NULL;
552 unsigned total, check, pos, best;
554 if( !__check_query_condition(&pWndZ, &pWnd) )
556 WIN_ReleaseWndPtr(pWndCheck);
557 WIN_ReleaseWndPtr(pDesktop->child);
558 WIN_ReleaseDesktop();
559 return hwndInsertAfter;
561 WIN_LockWndPtr(pWndZ);
562 WIN_LockWndPtr(pWnd);
563 WIN_ReleaseWndPtr(pDesktop->child);
564 WIN_ReleaseDesktop();
566 parent = __get_common_ancestor( X11DRV_WND_GetXWindow(pWndZ),
567 X11DRV_WND_GetXWindow(pWnd),
568 &children, &total );
569 if( parent && children )
571 /* w is the ancestor if pWndCheck that is a direct descendant of 'parent' */
573 w = __get_top_decoration( X11DRV_WND_GetXWindow(pWndCheck), parent );
575 if( w != children[total-1] ) /* check if at the top */
577 /* X child at index 0 is at the bottom, at index total-1 is at the top */
578 check = __td_lookup( w, children, total );
579 best = total;
581 for( WIN_UpdateWndPtr(&pWnd,pWndZ); pWnd;WIN_UpdateWndPtr(&pWnd,pWnd->next))
583 /* go through all windows in Wine z-order... */
585 if( pWnd != pWndCheck )
587 if( !(pWnd->flags & WIN_MANAGED) ||
588 !(w = __get_top_decoration( X11DRV_WND_GetXWindow(pWnd), parent )) )
589 continue;
590 pos = __td_lookup( w, children, total );
591 if( pos < best && pos > check )
593 /* find a nearest Wine window precedes
594 * pWndCheck in the real z-order... */
595 best = pos;
596 hwndInsertAfter = pWnd->hwndSelf;
598 if( best - check == 1 ) break;
603 if( children ) TSXFree( children );
604 WIN_ReleaseWndPtr(pWnd);
605 WIN_ReleaseWndPtr(pWndZ);
606 WIN_ReleaseWndPtr(pWndCheck);
607 return hwndInsertAfter;
610 /***********************************************************************
611 * X11DRV_EVENT_XStateToKeyState
613 * Translate a X event state (Button1Mask, ShiftMask, etc...) to
614 * a Windows key state (MK_SHIFT, MK_CONTROL, etc...)
616 WORD X11DRV_EVENT_XStateToKeyState( int state )
618 int kstate = 0;
620 if (state & Button1Mask) kstate |= MK_LBUTTON;
621 if (state & Button2Mask) kstate |= MK_MBUTTON;
622 if (state & Button3Mask) kstate |= MK_RBUTTON;
623 if (state & ShiftMask) kstate |= MK_SHIFT;
624 if (state & ControlMask) kstate |= MK_CONTROL;
625 return kstate;
628 /***********************************************************************
629 * EVENT_Expose
631 static void EVENT_Expose( HWND hWnd, XExposeEvent *event )
633 RECT rect;
635 WND *pWnd = WIN_FindWndPtr(hWnd);
636 /* Make position relative to client area instead of window */
637 rect.left = event->x - (pWnd? (pWnd->rectClient.left - pWnd->rectWindow.left) : 0);
638 rect.top = event->y - (pWnd? (pWnd->rectClient.top - pWnd->rectWindow.top) : 0);
639 rect.right = rect.left + event->width;
640 rect.bottom = rect.top + event->height;
641 WIN_ReleaseWndPtr(pWnd);
643 Callout.RedrawWindow( hWnd, &rect, 0,
644 RDW_INVALIDATE | RDW_FRAME | RDW_ALLCHILDREN | RDW_ERASE |
645 (event->count ? 0 : RDW_ERASENOW) );
649 /***********************************************************************
650 * EVENT_GraphicsExpose
652 * This is needed when scrolling area is partially obscured
653 * by non-Wine X window.
655 static void EVENT_GraphicsExpose( HWND hWnd, XGraphicsExposeEvent *event )
657 RECT rect;
658 WND *pWnd = WIN_FindWndPtr(hWnd);
660 /* Make position relative to client area instead of window */
661 rect.left = event->x - (pWnd? (pWnd->rectClient.left - pWnd->rectWindow.left) : 0);
662 rect.top = event->y - (pWnd? (pWnd->rectClient.top - pWnd->rectWindow.top) : 0);
663 rect.right = rect.left + event->width;
664 rect.bottom = rect.top + event->height;
666 WIN_ReleaseWndPtr(pWnd);
668 Callout.RedrawWindow( hWnd, &rect, 0,
669 RDW_INVALIDATE | RDW_ALLCHILDREN | RDW_ERASE |
670 (event->count ? 0 : RDW_ERASENOW) );
674 /***********************************************************************
675 * EVENT_Key
677 * Handle a X key event
679 static void EVENT_Key( HWND hWnd, XKeyEvent *event )
681 WND *pWnd = WIN_FindWndPtr(hWnd);
682 X11DRV_KEYBOARD_HandleEvent( pWnd, event );
683 WIN_ReleaseWndPtr(pWnd);
688 /***********************************************************************
689 * EVENT_MotionNotify
691 static void EVENT_MotionNotify( HWND hWnd, XMotionEvent *event )
693 if (current_input_type == X11DRV_INPUT_ABSOLUTE) {
694 WND *pWnd = WIN_FindWndPtr(hWnd);
695 int xOffset = pWnd? pWnd->rectWindow.left : 0;
696 int yOffset = pWnd? pWnd->rectWindow.top : 0;
697 WIN_ReleaseWndPtr(pWnd);
699 MOUSE_SendEvent( MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE,
700 xOffset + event->x, yOffset + event->y,
701 X11DRV_EVENT_XStateToKeyState( event->state ),
702 event->time - MSG_WineStartTicks,
703 hWnd);
704 } else {
705 MOUSE_SendEvent( MOUSEEVENTF_MOVE,
706 event->x_root, event->y_root,
707 X11DRV_EVENT_XStateToKeyState( event->state ),
708 event->time - MSG_WineStartTicks,
709 hWnd);
714 /***********************************************************************
715 * EVENT_ButtonPress
717 static void EVENT_ButtonPress( HWND hWnd, XButtonEvent *event )
719 static WORD statusCodes[NB_BUTTONS] =
720 { MOUSEEVENTF_LEFTDOWN, MOUSEEVENTF_MIDDLEDOWN, MOUSEEVENTF_RIGHTDOWN, MOUSEEVENTF_WHEEL, MOUSEEVENTF_WHEEL};
721 int buttonNum = event->button - 1;
723 WND *pWnd = WIN_FindWndPtr(hWnd);
724 int xOffset = pWnd? pWnd->rectWindow.left : 0;
725 int yOffset = pWnd? pWnd->rectWindow.top : 0;
726 WORD keystate,wData = 0;
728 WIN_ReleaseWndPtr(pWnd);
730 if (buttonNum >= NB_BUTTONS) return;
733 * Get the compatible keystate
735 keystate = X11DRV_EVENT_XStateToKeyState( event->state );
738 * Make sure that the state of the button that was just
739 * pressed is "down".
741 switch (buttonNum)
743 case 0:
744 keystate |= MK_LBUTTON;
745 break;
746 case 1:
747 keystate |= MK_MBUTTON;
748 break;
749 case 2:
750 keystate |= MK_RBUTTON;
751 break;
752 case 3:
753 wData = WHEEL_DELTA;
754 break;
755 case 4:
756 wData = -WHEEL_DELTA;
757 break;
760 MOUSE_SendEvent( statusCodes[buttonNum],
761 xOffset + event->x, yOffset + event->y,
762 MAKEWPARAM(keystate,wData),
763 event->time - MSG_WineStartTicks,
764 hWnd);
768 /***********************************************************************
769 * EVENT_ButtonRelease
771 static void EVENT_ButtonRelease( HWND hWnd, XButtonEvent *event )
773 static WORD statusCodes[NB_BUTTONS] =
774 { MOUSEEVENTF_LEFTUP, MOUSEEVENTF_MIDDLEUP, MOUSEEVENTF_RIGHTUP };
775 int buttonNum = event->button - 1;
776 WND *pWnd = WIN_FindWndPtr(hWnd);
777 int xOffset = pWnd? pWnd->rectWindow.left : 0;
778 int yOffset = pWnd? pWnd->rectWindow.top : 0;
779 WORD keystate;
781 WIN_ReleaseWndPtr(pWnd);
783 if (buttonNum >= NB_BUTTONS) return;
786 * Get the compatible keystate
788 keystate = X11DRV_EVENT_XStateToKeyState( event->state );
791 * Make sure that the state of the button that was just
792 * released is "up".
794 switch (buttonNum)
796 case 0:
797 keystate &= ~MK_LBUTTON;
798 break;
799 case 1:
800 keystate &= ~MK_MBUTTON;
801 break;
802 case 2:
803 keystate &= ~MK_RBUTTON;
804 break;
805 default:
806 return;
809 MOUSE_SendEvent( statusCodes[buttonNum],
810 xOffset + event->x, yOffset + event->y,
811 keystate,
812 event->time - MSG_WineStartTicks,
813 hWnd);
817 /**********************************************************************
818 * EVENT_FocusIn
820 static void EVENT_FocusIn( HWND hWnd, XFocusChangeEvent *event )
822 if (event->detail != NotifyPointer)
823 if (hWnd != GetForegroundWindow())
825 SetForegroundWindow( hWnd );
826 X11DRV_KEYBOARD_UpdateState();
831 /**********************************************************************
832 * EVENT_FocusOut
834 * Note: only top-level override-redirect windows get FocusOut events.
836 static void EVENT_FocusOut( HWND hWnd, XFocusChangeEvent *event )
838 if (event->detail != NotifyPointer)
839 if (hWnd == GetForegroundWindow())
841 SendMessageA( hWnd, WM_CANCELMODE, 0, 0 );
843 /* Abey : 6-Oct-99. Check again if the focus out window is the
844 Foreground window, because in most cases the messages sent
845 above must have already changed the foreground window, in which
846 case we don't have to change the foreground window to 0 */
848 if (hWnd == GetForegroundWindow())
849 SetForegroundWindow( 0 );
853 /**********************************************************************
854 * X11DRV_EVENT_CheckFocus
856 BOOL X11DRV_EVENT_CheckFocus(void)
858 HWND hWnd;
859 Window xW;
860 int state;
862 TSXGetInputFocus(display, &xW, &state);
863 if( xW == None ||
864 TSXFindContext(display, xW, winContext, (char **)&hWnd) )
865 return FALSE;
866 return TRUE;
869 /**********************************************************************
870 * EVENT_GetGeometry
872 * Helper function for ConfigureNotify handling.
873 * Get the new geometry of a window relative to the root window.
875 static void EVENT_GetGeometry( Window win, int *px, int *py,
876 unsigned int *pwidth, unsigned int *pheight )
878 Window root, top;
879 int x, y, width, height, border, depth;
881 EnterCriticalSection( &X11DRV_CritSection );
883 /* Get the geometry of the window */
884 XGetGeometry( display, win, &root, &x, &y, &width, &height,
885 &border, &depth );
887 /* Translate the window origin to root coordinates */
888 XTranslateCoordinates( display, win, root, 0, 0, &x, &y, &top );
890 LeaveCriticalSection( &X11DRV_CritSection );
892 *px = x;
893 *py = y;
894 *pwidth = width;
895 *pheight = height;
898 /**********************************************************************
899 * EVENT_ConfigureNotify
901 * The ConfigureNotify event is only selected on top-level windows
902 * when the -managed flag is used.
904 static void EVENT_ConfigureNotify( HWND hWnd, XConfigureEvent *event )
906 RECT rectWindow;
907 int x, y, flags = 0;
908 unsigned int width, height;
909 HWND newInsertAfter, oldInsertAfter;
911 /* Get geometry and Z-order according to X */
913 EVENT_GetGeometry( event->window, &x, &y, &width, &height );
914 newInsertAfter = EVENT_QueryZOrder( hWnd );
916 /* Get geometry and Z-order according to Wine */
919 * Needs to find the first Visible Window above the current one
921 oldInsertAfter = hWnd;
922 for (;;)
924 oldInsertAfter = GetWindow( oldInsertAfter, GW_HWNDPREV );
925 if (!oldInsertAfter)
927 oldInsertAfter = HWND_TOP;
928 break;
930 if (GetWindowLongA( oldInsertAfter, GWL_STYLE ) & WS_VISIBLE) break;
933 /* Compare what has changed */
935 GetWindowRect( hWnd, &rectWindow );
936 if ( rectWindow.left == x && rectWindow.top == y )
937 flags |= SWP_NOMOVE;
938 else
939 TRACE_(win)( "%04x moving from (%d,%d) to (%d,%d)\n", hWnd,
940 rectWindow.left, rectWindow.top, x, y );
942 if ( rectWindow.right - rectWindow.left == width
943 && rectWindow.bottom - rectWindow.top == height )
944 flags |= SWP_NOSIZE;
945 else
946 TRACE_(win)( "%04x resizing from (%d,%d) to (%d,%d)\n", hWnd,
947 rectWindow.right - rectWindow.left,
948 rectWindow.bottom - rectWindow.top, width, height );
950 if ( newInsertAfter == oldInsertAfter )
951 flags |= SWP_NOZORDER;
952 else
953 TRACE_(win)( "%04x restacking from after %04x to after %04x\n", hWnd,
954 oldInsertAfter, newInsertAfter );
956 /* If anything changed, call SetWindowPos */
958 if ( flags != (SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER) )
959 SetWindowPos( hWnd, newInsertAfter, x, y, width, height,
960 flags | SWP_NOACTIVATE | SWP_WINE_NOHOSTMOVE );
964 /***********************************************************************
965 * EVENT_SelectionRequest_TARGETS
966 * Service a TARGETS selection request event
968 static Atom EVENT_SelectionRequest_TARGETS( Window requestor, Atom target, Atom rprop )
970 Atom xaTargets = TSXInternAtom(display, "TARGETS", False);
971 Atom* targets;
972 Atom prop;
973 UINT wFormat;
974 unsigned long cTargets;
975 BOOL bHavePixmap;
976 int xRc;
978 TRACE("Request for %s\n", TSXGetAtomName(display, target));
981 * Count the number of items we wish to expose as selection targets.
982 * We include the TARGETS item, and a PIXMAP if we have CF_DIB or CF_BITMAP
984 cTargets = CountClipboardFormats() + 1;
985 if ( CLIPBOARD_IsPresent(CF_DIB) || CLIPBOARD_IsPresent(CF_BITMAP) )
986 cTargets++;
988 /* Allocate temp buffer */
989 targets = (Atom*)HEAP_xalloc( GetProcessHeap(), 0, cTargets * sizeof(Atom));
991 /* Create TARGETS property list (First item in list is TARGETS itself) */
993 for ( targets[0] = xaTargets, cTargets = 1, wFormat = 0, bHavePixmap = FALSE;
994 (wFormat = EnumClipboardFormats( wFormat )); )
996 if ( (prop = X11DRV_CLIPBOARD_MapFormatToProperty(wFormat)) != None )
998 /* Scan through what we have so far to avoid duplicates */
999 int i;
1000 BOOL bExists;
1001 for (i = 0, bExists = FALSE; i < cTargets; i++)
1003 if (targets[i] == prop)
1005 bExists = TRUE;
1006 break;
1009 if (!bExists)
1011 targets[cTargets++] = prop;
1013 /* Add PIXMAP prop for bitmaps additionally */
1014 if ( (wFormat == CF_DIB || wFormat == CF_BITMAP )
1015 && !bHavePixmap )
1017 targets[cTargets++] = XA_PIXMAP;
1018 bHavePixmap = TRUE;
1024 if (TRACE_ON(event))
1026 int i;
1027 for ( i = 0; i < cTargets; i++)
1029 if (targets[i])
1031 char *itemFmtName = TSXGetAtomName(display, targets[i]);
1032 TRACE("\tAtom# %d: Type %s\n", i, itemFmtName);
1033 TSXFree(itemFmtName);
1038 /* Update the X property */
1039 TRACE("\tUpdating property %s...", TSXGetAtomName(display, rprop));
1041 /* We may want to consider setting the type to xaTargets instead,
1042 * in case some apps expect this instead of XA_ATOM */
1043 xRc = TSXChangeProperty(display, requestor, rprop,
1044 XA_ATOM, 32, PropModeReplace,
1045 (unsigned char *)targets, cTargets);
1046 TRACE("(Rc=%d)\n", xRc);
1048 HeapFree( GetProcessHeap(), 0, targets );
1050 return rprop;
1054 /***********************************************************************
1055 * EVENT_SelectionRequest_STRING
1056 * Service a STRING selection request event
1058 static Atom EVENT_SelectionRequest_STRING( Window requestor, Atom target, Atom rprop )
1060 HANDLE16 hText;
1061 LPSTR text;
1062 int size,i,j;
1063 char* lpstr = 0;
1064 char *itemFmtName;
1065 int xRc;
1068 * Map the requested X selection property type atom name to a
1069 * windows clipboard format ID.
1071 itemFmtName = TSXGetAtomName(display, target);
1072 TRACE("Request for %s (wFormat=%x %s)\n",
1073 itemFmtName, CF_TEXT, CLIPBOARD_GetFormatName(CF_TEXT));
1074 TSXFree(itemFmtName);
1076 hText = GetClipboardData16(CF_TEXT);
1077 if ( !hText )
1078 return None;
1079 text = GlobalLock16(hText);
1080 if (!text)
1081 return None;
1082 size = GlobalSize16(hText);
1083 /* remove carriage returns */
1085 lpstr = (char*)HEAP_xalloc( GetProcessHeap(), 0, size-- );
1086 for(i=0,j=0; i < size && text[i]; i++ )
1088 if( text[i] == '\r' &&
1089 (text[i+1] == '\n' || text[i+1] == '\0') ) continue;
1090 lpstr[j++] = text[i];
1092 lpstr[j]='\0';
1094 /* Update the X property */
1095 TRACE("\tUpdating property %s...\n", TSXGetAtomName(display, rprop));
1096 xRc = TSXChangeProperty(display, requestor, rprop,
1097 XA_STRING, 8, PropModeReplace,
1098 lpstr, j);
1099 TRACE("(Rc=%d)\n", xRc);
1101 GlobalUnlock16(hText);
1102 HeapFree( GetProcessHeap(), 0, lpstr );
1104 return rprop;
1107 /***********************************************************************
1108 * EVENT_SelectionRequest_PIXMAP
1109 * Service a PIXMAP selection request event
1111 static Atom EVENT_SelectionRequest_PIXMAP( Window requestor, Atom target, Atom rprop )
1113 HANDLE hClipData = 0;
1114 Pixmap pixmap = 0;
1115 UINT wFormat;
1116 char * itemFmtName;
1117 int xRc;
1118 #if(0)
1119 XSetWindowAttributes win_attr;
1120 XWindowAttributes win_attr_src;
1121 #endif
1124 * Map the requested X selection property type atom name to a
1125 * windows clipboard format ID.
1127 itemFmtName = TSXGetAtomName(display, target);
1128 wFormat = X11DRV_CLIPBOARD_MapPropertyToFormat(itemFmtName);
1129 TRACE("Request for %s (wFormat=%x %s)\n",
1130 itemFmtName, wFormat, CLIPBOARD_GetFormatName( wFormat));
1131 TSXFree(itemFmtName);
1133 hClipData = GetClipboardData(wFormat);
1134 if ( !hClipData )
1136 TRACE("Could not retrieve a Pixmap compatible format from clipboard!\n");
1137 rprop = None; /* Fail the request */
1138 goto END;
1141 if (wFormat == CF_DIB)
1143 HWND hwnd = GetOpenClipboardWindow();
1144 HDC hdc = GetDC(hwnd);
1146 /* For convert from packed DIB to Pixmap */
1147 pixmap = X11DRV_DIB_CreatePixmapFromDIB(hClipData, hdc);
1149 ReleaseDC(hdc, hwnd);
1151 else if (wFormat == CF_BITMAP)
1153 HWND hwnd = GetOpenClipboardWindow();
1154 HDC hdc = GetDC(hwnd);
1156 pixmap = X11DRV_BITMAP_CreatePixmapFromBitmap(hClipData, hdc);
1158 ReleaseDC(hdc, hwnd);
1160 else
1162 FIXME("%s to PIXMAP conversion not yet implemented!\n",
1163 CLIPBOARD_GetFormatName(wFormat));
1164 rprop = None;
1165 goto END;
1168 TRACE("\tUpdating property %s on Window %ld with %s %ld...\n",
1169 TSXGetAtomName(display, rprop), (long)requestor,
1170 TSXGetAtomName(display, target), pixmap);
1172 /* Store the Pixmap handle in the property */
1173 xRc = TSXChangeProperty(display, requestor, rprop, target,
1174 32, PropModeReplace,
1175 (unsigned char *)&pixmap, 1);
1176 TRACE("(Rc=%d)\n", xRc);
1178 /* Enable the code below if you want to handle destroying Pixmap resources
1179 * in response to property notify events. Clients like XPaint don't
1180 * appear to be duplicating Pixmaps so they don't like us deleting,
1181 * the resource in response to the property being deleted.
1183 #if(0)
1184 /* Express interest in property notify events so that we can delete the
1185 * pixmap when the client deletes the property atom.
1187 xRc = TSXGetWindowAttributes(display, requestor, &win_attr_src);
1188 TRACE("Turning on PropertyChangeEvent notifications from window %ld\n",
1189 (long)requestor);
1190 win_attr.event_mask = win_attr_src.your_event_mask | PropertyChangeMask;
1191 TSXChangeWindowAttributes(display, requestor, CWEventMask, &win_attr);
1193 /* Register the Pixmap we created with the request property Atom.
1194 * When this property is destroyed we also destroy the Pixmap in
1195 * response to the PropertyNotify event.
1197 X11DRV_CLIPBOARD_RegisterPixmapResource( rprop, pixmap );
1198 #endif
1200 END:
1201 return rprop;
1205 /***********************************************************************
1206 * EVENT_SelectionRequest_WCF
1207 * Service a Wine Clipboard Format selection request event.
1208 * For <WCF>* data types we simply copy the data to X without conversion.
1210 static Atom EVENT_SelectionRequest_WCF( Window requestor, Atom target, Atom rprop )
1212 HANDLE hClipData = 0;
1213 void* lpClipData;
1214 UINT wFormat;
1215 char * itemFmtName;
1216 int cBytes;
1217 int xRc;
1220 * Map the requested X selection property type atom name to a
1221 * windows clipboard format ID.
1223 itemFmtName = TSXGetAtomName(display, target);
1224 wFormat = X11DRV_CLIPBOARD_MapPropertyToFormat(itemFmtName);
1225 TRACE("Request for %s (wFormat=%x %s)\n",
1226 itemFmtName, wFormat, CLIPBOARD_GetFormatName( wFormat));
1227 TSXFree(itemFmtName);
1229 hClipData = GetClipboardData16(wFormat);
1231 if( hClipData && (lpClipData = GlobalLock16(hClipData)) )
1233 cBytes = GlobalSize16(hClipData);
1235 TRACE("\tUpdating property %s, %d bytes...\n",
1236 TSXGetAtomName(display, rprop), cBytes);
1238 xRc = TSXChangeProperty(display, requestor, rprop,
1239 target, 8, PropModeReplace,
1240 (unsigned char *)lpClipData, cBytes);
1241 TRACE("(Rc=%d)\n", xRc);
1243 GlobalUnlock16(hClipData);
1245 else
1247 TRACE("\tCould not retrieve native format!\n");
1248 rprop = None; /* Fail the request */
1251 return rprop;
1255 /***********************************************************************
1256 * EVENT_SelectionRequest_MULTIPLE
1257 * Service a MULTIPLE selection request event
1258 * rprop contains a list of (target,property) atom pairs.
1259 * The first atom names a target and the second names a property.
1260 * The effect is as if we have received a sequence of SelectionRequest events
1261 * (one for each atom pair) except that:
1262 * 1. We reply with a SelectionNotify only when all the requested conversions
1263 * have been performed.
1264 * 2. If we fail to convert the target named by an atom in the MULTIPLE property,
1265 * we replace the atom in the property by None.
1267 static Atom EVENT_SelectionRequest_MULTIPLE( HWND hWnd, XSelectionRequestEvent *pevent )
1269 Atom rprop;
1270 Atom atype=AnyPropertyType;
1271 int aformat;
1272 unsigned long remain;
1273 Atom* targetPropList=NULL;
1274 unsigned long cTargetPropList = 0;
1275 /* Atom xAtomPair = TSXInternAtom(display, "ATOM_PAIR", False); */
1277 /* If the specified property is None the requestor is an obsolete client.
1278 * We support these by using the specified target atom as the reply property.
1280 rprop = pevent->property;
1281 if( rprop == None )
1282 rprop = pevent->target;
1283 if (!rprop)
1284 goto END;
1286 /* Read the MULTIPLE property contents. This should contain a list of
1287 * (target,property) atom pairs.
1289 if(TSXGetWindowProperty(display, pevent->requestor, rprop,
1290 0, 0x3FFF, False, AnyPropertyType, &atype,&aformat,
1291 &cTargetPropList, &remain,
1292 (unsigned char**)&targetPropList) != Success)
1293 TRACE("\tCouldn't read MULTIPLE property\n");
1294 else
1296 TRACE("\tType %s,Format %d,nItems %ld, Remain %ld\n",
1297 TSXGetAtomName(display, atype), aformat, cTargetPropList, remain);
1300 * Make sure we got what we expect.
1301 * NOTE: According to the X-ICCCM Version 2.0 documentation the property sent
1302 * in a MULTIPLE selection request should be of type ATOM_PAIR.
1303 * However some X apps(such as XPaint) are not compliant with this and return
1304 * a user defined atom in atype when XGetWindowProperty is called.
1305 * The data *is* an atom pair but is not denoted as such.
1307 if(aformat == 32 /* atype == xAtomPair */ )
1309 int i;
1311 /* Iterate through the ATOM_PAIR list and execute a SelectionRequest
1312 * for each (target,property) pair */
1314 for (i = 0; i < cTargetPropList; i+=2)
1316 char *targetName = TSXGetAtomName(display, targetPropList[i]);
1317 char *propName = TSXGetAtomName(display, targetPropList[i+1]);
1318 XSelectionRequestEvent event;
1320 TRACE("MULTIPLE(%d): Target='%s' Prop='%s'\n",
1321 i/2, targetName, propName);
1322 TSXFree(targetName);
1323 TSXFree(propName);
1325 /* We must have a non "None" property to service a MULTIPLE target atom */
1326 if ( !targetPropList[i+1] )
1328 TRACE("\tMULTIPLE(%d): Skipping target with empty property!", i);
1329 continue;
1332 /* Set up an XSelectionRequestEvent for this (target,property) pair */
1333 memcpy( &event, pevent, sizeof(XSelectionRequestEvent) );
1334 event.target = targetPropList[i];
1335 event.property = targetPropList[i+1];
1337 /* Fire a SelectionRequest, informing the handler that we are processing
1338 * a MULTIPLE selection request event.
1340 EVENT_SelectionRequest( hWnd, &event, TRUE );
1344 /* Free the list of targets/properties */
1345 TSXFree(targetPropList);
1348 END:
1349 return rprop;
1353 /***********************************************************************
1354 * EVENT_SelectionRequest
1355 * Process an event selection request event.
1356 * The bIsMultiple flag is used to signal when EVENT_SelectionRequest is called
1357 * recursively while servicing a "MULTIPLE" selection target.
1359 * Note: We only receive this event when WINE owns the X selection
1361 static void EVENT_SelectionRequest( HWND hWnd, XSelectionRequestEvent *event, BOOL bIsMultiple )
1363 XSelectionEvent result;
1364 Atom rprop = None;
1365 Window request = event->requestor;
1366 BOOL couldOpen = FALSE;
1367 Atom xaClipboard = TSXInternAtom(display, "CLIPBOARD", False);
1368 Atom xaTargets = TSXInternAtom(display, "TARGETS", False);
1369 Atom xaMultiple = TSXInternAtom(display, "MULTIPLE", False);
1372 * We can only handle the selection request if :
1373 * The selection is PRIMARY or CLIPBOARD, AND we can successfully open the clipboard.
1374 * Don't do these checks or open the clipboard while recursively processing MULTIPLE,
1375 * since this has been already done.
1377 if ( !bIsMultiple )
1379 if ( ( (event->selection != XA_PRIMARY) && (event->selection != xaClipboard) )
1380 || !(couldOpen = OpenClipboard(hWnd)) )
1381 goto END;
1384 /* If the specified property is None the requestor is an obsolete client.
1385 * We support these by using the specified target atom as the reply property.
1387 rprop = event->property;
1388 if( rprop == None )
1389 rprop = event->target;
1391 if(event->target == xaTargets) /* Return a list of all supported targets */
1393 /* TARGETS selection request */
1394 rprop = EVENT_SelectionRequest_TARGETS( request, event->target, rprop );
1396 else if(event->target == xaMultiple) /* rprop contains a list of (target, property) atom pairs */
1398 /* MULTIPLE selection request */
1399 rprop = EVENT_SelectionRequest_MULTIPLE( hWnd, event );
1401 else if(event->target == XA_STRING) /* treat CF_TEXT as Unix text */
1403 /* XA_STRING selection request */
1404 rprop = EVENT_SelectionRequest_STRING( request, event->target, rprop );
1406 else if(event->target == XA_PIXMAP) /* Convert DIB's to Pixmaps */
1408 /* XA_PIXMAP selection request */
1409 rprop = EVENT_SelectionRequest_PIXMAP( request, event->target, rprop );
1411 else if(event->target == XA_BITMAP) /* Convert DIB's to 1-bit Pixmaps */
1413 /* XA_BITMAP selection request - TODO: create a monochrome Pixmap */
1414 rprop = EVENT_SelectionRequest_PIXMAP( request, XA_PIXMAP, rprop );
1416 else if(X11DRV_CLIPBOARD_IsNativeProperty(event->target)) /* <WCF>* */
1418 /* All <WCF> selection requests */
1419 rprop = EVENT_SelectionRequest_WCF( request, event->target, rprop );
1421 else
1422 rprop = None; /* Don't support this format */
1424 END:
1425 /* close clipboard only if we opened before */
1426 if(couldOpen) CloseClipboard();
1428 if( rprop == None)
1429 TRACE("\tRequest ignored\n");
1431 /* reply to sender
1432 * SelectionNotify should be sent only at the end of a MULTIPLE request
1434 if ( !bIsMultiple )
1436 result.type = SelectionNotify;
1437 result.display = display;
1438 result.requestor = request;
1439 result.selection = event->selection;
1440 result.property = rprop;
1441 result.target = event->target;
1442 result.time = event->time;
1443 TRACE("Sending SelectionNotify event...\n");
1444 TSXSendEvent(display,event->requestor,False,NoEventMask,(XEvent*)&result);
1448 /***********************************************************************
1449 * EVENT_SelectionClear
1451 static void EVENT_SelectionClear( HWND hWnd, XSelectionClearEvent *event )
1453 Atom xaClipboard = TSXInternAtom(display, "CLIPBOARD", False);
1455 if (event->selection == XA_PRIMARY || event->selection == xaClipboard)
1456 X11DRV_CLIPBOARD_ReleaseSelection( event->selection, event->window, hWnd );
1459 /***********************************************************************
1460 * EVENT_PropertyNotify
1461 * We use this to release resources like Pixmaps when a selection
1462 * client no longer needs them.
1464 static void EVENT_PropertyNotify( XPropertyEvent *event )
1466 /* Check if we have any resources to free */
1467 TRACE("Received PropertyNotify event: ");
1469 switch(event->state)
1471 case PropertyDelete:
1473 TRACE("\tPropertyDelete for atom %s on window %ld\n",
1474 TSXGetAtomName(event->display, event->atom), (long)event->window);
1476 if (X11DRV_CLIPBOARD_IsSelectionowner())
1477 X11DRV_CLIPBOARD_FreeResources( event->atom );
1478 break;
1481 case PropertyNewValue:
1483 TRACE("\tPropertyNewValue for atom %s on window %ld\n\n",
1484 TSXGetAtomName(event->display, event->atom), (long)event->window);
1485 break;
1488 default:
1489 break;
1493 /**********************************************************************
1494 * EVENT_DropFromOffix
1496 * don't know if it still works (last Changlog is from 96/11/04)
1498 static void EVENT_DropFromOffiX( HWND hWnd, XClientMessageEvent *event )
1500 unsigned long data_length;
1501 unsigned long aux_long;
1502 unsigned char* p_data = NULL;
1503 union {
1504 Atom atom_aux;
1505 struct {
1506 int x;
1507 int y;
1508 } pt_aux;
1509 int i;
1510 } u;
1511 int x, y;
1512 BOOL16 bAccept;
1513 HGLOBAL16 hDragInfo = GlobalAlloc16( GMEM_SHARE | GMEM_ZEROINIT, sizeof(DRAGINFO));
1514 LPDRAGINFO lpDragInfo = (LPDRAGINFO) GlobalLock16(hDragInfo);
1515 SEGPTR spDragInfo = (SEGPTR) WIN16_GlobalLock16(hDragInfo);
1516 Window w_aux_root, w_aux_child;
1517 WND* pDropWnd;
1518 WND* pWnd;
1520 if( !lpDragInfo || !spDragInfo ) return;
1522 pWnd = WIN_FindWndPtr(hWnd);
1524 TSXQueryPointer( display, X11DRV_WND_GetXWindow(pWnd), &w_aux_root, &w_aux_child,
1525 &x, &y, (int *) &u.pt_aux.x, (int *) &u.pt_aux.y,
1526 (unsigned int*)&aux_long);
1528 lpDragInfo->hScope = hWnd;
1529 lpDragInfo->pt.x = (INT16)x; lpDragInfo->pt.y = (INT16)y;
1531 /* find out drop point and drop window */
1532 if( x < 0 || y < 0 ||
1533 x > (pWnd->rectWindow.right - pWnd->rectWindow.left) ||
1534 y > (pWnd->rectWindow.bottom - pWnd->rectWindow.top) )
1535 { bAccept = pWnd->dwExStyle & WS_EX_ACCEPTFILES; x = y = 0; }
1536 else
1538 bAccept = DRAG_QueryUpdate( hWnd, spDragInfo, TRUE );
1539 x = lpDragInfo->pt.x; y = lpDragInfo->pt.y;
1541 pDropWnd = WIN_FindWndPtr( lpDragInfo->hScope );
1542 WIN_ReleaseWndPtr(pWnd);
1544 GlobalFree16( hDragInfo );
1546 if( bAccept )
1548 TSXGetWindowProperty( display, DefaultRootWindow(display),
1549 dndSelection, 0, 65535, FALSE,
1550 AnyPropertyType, &u.atom_aux, (int *) &u.pt_aux.y,
1551 &data_length, &aux_long, &p_data);
1553 if( !aux_long && p_data) /* don't bother if > 64K */
1555 char *p = (char*) p_data;
1556 char *p_drop;
1558 aux_long = 0;
1559 while( *p ) /* calculate buffer size */
1561 p_drop = p;
1562 if((u.i = *p) != -1 )
1563 u.i = DRIVE_FindDriveRoot( (const char **)&p_drop );
1564 if( u.i == -1 ) *p = -1; /* mark as "bad" */
1565 else
1567 INT len = GetShortPathNameA( p, NULL, 0 );
1568 if (len) aux_long += len + 1;
1569 else *p = -1;
1571 p += strlen(p) + 1;
1573 if( aux_long && aux_long < 65535 )
1575 HDROP16 hDrop;
1576 LPDROPFILESTRUCT16 lpDrop;
1578 aux_long += sizeof(DROPFILESTRUCT16) + 1;
1579 hDrop = (HDROP16)GlobalAlloc16( GMEM_SHARE, aux_long );
1580 lpDrop = (LPDROPFILESTRUCT16) GlobalLock16( hDrop );
1582 if( lpDrop )
1584 lpDrop->wSize = sizeof(DROPFILESTRUCT16);
1585 lpDrop->ptMousePos.x = (INT16)x;
1586 lpDrop->ptMousePos.y = (INT16)y;
1587 lpDrop->fInNonClientArea = (BOOL16)
1588 ( x < (pDropWnd->rectClient.left - pDropWnd->rectWindow.left) ||
1589 y < (pDropWnd->rectClient.top - pDropWnd->rectWindow.top) ||
1590 x > (pDropWnd->rectClient.right - pDropWnd->rectWindow.left) ||
1591 y > (pDropWnd->rectClient.bottom - pDropWnd->rectWindow.top) );
1592 p_drop = ((char*)lpDrop) + sizeof(DROPFILESTRUCT16);
1593 p = p_data;
1594 while(*p)
1596 if( *p != -1 ) /* use only "good" entries */
1598 GetShortPathNameA( p, p_drop, 65535 );
1599 p_drop += strlen( p_drop ) + 1;
1601 p += strlen(p) + 1;
1603 *p_drop = '\0';
1604 PostMessage16( hWnd, WM_DROPFILES,
1605 (WPARAM16)hDrop, 0L );
1609 if( p_data ) TSXFree(p_data);
1611 } /* WS_EX_ACCEPTFILES */
1613 WIN_ReleaseWndPtr(pDropWnd);
1616 /**********************************************************************
1617 * EVENT_DropURLs
1619 * drop items are separated by \n
1620 * each item is prefixed by its mime type
1622 * event->data.l[3], event->data.l[4] contains drop x,y position
1624 static void EVENT_DropURLs( HWND hWnd, XClientMessageEvent *event )
1626 WND *pDropWnd;
1627 WND *pWnd;
1628 unsigned long data_length;
1629 unsigned long aux_long, drop_len = 0;
1630 unsigned char *p_data = NULL; /* property data */
1631 char *p_drop = NULL;
1632 char *p, *next;
1633 int x, y, drop32 = FALSE ;
1634 union {
1635 Atom atom_aux;
1636 int i;
1637 Window w_aux;
1638 } u; /* unused */
1639 union {
1640 HDROP16 h16;
1641 HDROP h32;
1642 } hDrop;
1644 pWnd = WIN_FindWndPtr(hWnd);
1645 drop32 = pWnd->flags & WIN_ISWIN32;
1647 if (!(pWnd->dwExStyle & WS_EX_ACCEPTFILES))
1649 WIN_ReleaseWndPtr(pWnd);
1650 return;
1652 WIN_ReleaseWndPtr(pWnd);
1654 TSXGetWindowProperty( display, DefaultRootWindow(display),
1655 dndSelection, 0, 65535, FALSE,
1656 AnyPropertyType, &u.atom_aux, &u.i,
1657 &data_length, &aux_long, &p_data);
1658 if (aux_long)
1659 WARN("property too large, truncated!\n");
1660 TRACE("urls=%s\n", p_data);
1662 if( !aux_long && p_data) { /* don't bother if > 64K */
1663 /* calculate length */
1664 p = p_data;
1665 next = strchr(p, '\n');
1666 while (p) {
1667 if (next) *next=0;
1668 if (strncmp(p,"file:",5) == 0 ) {
1669 INT len = GetShortPathNameA( p+5, NULL, 0 );
1670 if (len) drop_len += len + 1;
1672 if (next) {
1673 *next = '\n';
1674 p = next + 1;
1675 next = strchr(p, '\n');
1676 } else {
1677 p = NULL;
1681 if( drop_len && drop_len < 65535 ) {
1682 TSXQueryPointer( display, X11DRV_GetXRootWindow(), &u.w_aux, &u.w_aux,
1683 &x, &y, &u.i, &u.i, &u.i);
1685 pDropWnd = WIN_FindWndPtr( hWnd );
1687 if (drop32) {
1688 LPDROPFILESTRUCT lpDrop;
1689 drop_len += sizeof(DROPFILESTRUCT) + 1;
1690 hDrop.h32 = (HDROP)GlobalAlloc( GMEM_SHARE, drop_len );
1691 lpDrop = (LPDROPFILESTRUCT) GlobalLock( hDrop.h32 );
1693 if( lpDrop ) {
1694 lpDrop->lSize = sizeof(DROPFILESTRUCT);
1695 lpDrop->ptMousePos.x = (INT)x;
1696 lpDrop->ptMousePos.y = (INT)y;
1697 lpDrop->fInNonClientArea = (BOOL)
1698 ( x < (pDropWnd->rectClient.left - pDropWnd->rectWindow.left) ||
1699 y < (pDropWnd->rectClient.top - pDropWnd->rectWindow.top) ||
1700 x > (pDropWnd->rectClient.right - pDropWnd->rectWindow.left) ||
1701 y > (pDropWnd->rectClient.bottom - pDropWnd->rectWindow.top) );
1702 lpDrop->fWideChar = FALSE;
1703 p_drop = ((char*)lpDrop) + sizeof(DROPFILESTRUCT);
1705 } else {
1706 LPDROPFILESTRUCT16 lpDrop;
1707 drop_len += sizeof(DROPFILESTRUCT16) + 1;
1708 hDrop.h16 = (HDROP16)GlobalAlloc16( GMEM_SHARE, drop_len );
1709 lpDrop = (LPDROPFILESTRUCT16) GlobalLock16( hDrop.h16 );
1711 if( lpDrop ) {
1712 lpDrop->wSize = sizeof(DROPFILESTRUCT16);
1713 lpDrop->ptMousePos.x = (INT16)x;
1714 lpDrop->ptMousePos.y = (INT16)y;
1715 lpDrop->fInNonClientArea = (BOOL16)
1716 ( x < (pDropWnd->rectClient.left - pDropWnd->rectWindow.left) ||
1717 y < (pDropWnd->rectClient.top - pDropWnd->rectWindow.top) ||
1718 x > (pDropWnd->rectClient.right - pDropWnd->rectWindow.left) ||
1719 y > (pDropWnd->rectClient.bottom - pDropWnd->rectWindow.top) );
1720 p_drop = ((char*)lpDrop) + sizeof(DROPFILESTRUCT16);
1724 /* create message content */
1725 if (p_drop) {
1726 p = p_data;
1727 next = strchr(p, '\n');
1728 while (p) {
1729 if (next) *next=0;
1730 if (strncmp(p,"file:",5) == 0 ) {
1731 INT len = GetShortPathNameA( p+5, p_drop, 65535 );
1732 if (len) {
1733 TRACE("drop file %s as %s\n", p+5, p_drop);
1734 p_drop += len+1;
1735 } else {
1736 WARN("can't convert file %s to dos name \n", p+5);
1738 } else {
1739 WARN("unknown mime type %s\n", p);
1741 if (next) {
1742 *next = '\n';
1743 p = next + 1;
1744 next = strchr(p, '\n');
1745 } else {
1746 p = NULL;
1748 *p_drop = '\0';
1751 if (drop32) {
1752 /* can not use PostMessage32A because it is currently based on
1753 * PostMessage16 and WPARAM32 would be truncated to WPARAM16
1755 GlobalUnlock(hDrop.h32);
1756 SendMessageA( hWnd, WM_DROPFILES,
1757 (WPARAM)hDrop.h32, 0L );
1758 } else {
1759 GlobalUnlock16(hDrop.h16);
1760 PostMessage16( hWnd, WM_DROPFILES,
1761 (WPARAM16)hDrop.h16, 0L );
1764 WIN_ReleaseWndPtr(pDropWnd);
1766 if( p_data ) TSXFree(p_data);
1770 /**********************************************************************
1771 * EVENT_ClientMessage
1773 static void EVENT_ClientMessage( HWND hWnd, XClientMessageEvent *event )
1775 if (event->message_type != None && event->format == 32) {
1776 if ((event->message_type == wmProtocols) &&
1777 (((Atom) event->data.l[0]) == wmDeleteWindow))
1779 /* Ignore the delete window request if the window has been disabled
1780 * and we are in managed mode. This is to disallow applications from
1781 * being closed by the window manager while in a modal state.
1783 BOOL bIsDisabled;
1784 bIsDisabled = GetWindowLongA( hWnd, GWL_STYLE ) & WS_DISABLED;
1786 if ( !Options.managed || !bIsDisabled )
1787 PostMessage16( hWnd, WM_SYSCOMMAND, SC_CLOSE, 0 );
1789 else if ( event->message_type == dndProtocol &&
1790 (event->data.l[0] == DndFile || event->data.l[0] == DndFiles) )
1791 EVENT_DropFromOffiX(hWnd, event);
1792 else if ( event->message_type == dndProtocol &&
1793 event->data.l[0] == DndURL )
1794 EVENT_DropURLs(hWnd, event);
1795 else {
1796 #if 0
1797 /* enable this if you want to see the message */
1798 unsigned char* p_data = NULL;
1799 union {
1800 unsigned long l;
1801 int i;
1802 Atom atom;
1803 } u; /* unused */
1804 TSXGetWindowProperty( display, DefaultRootWindow(display),
1805 dndSelection, 0, 65535, FALSE,
1806 AnyPropertyType, &u.atom, &u.i,
1807 &u.l, &u.l, &p_data);
1808 TRACE("message_type=%ld, data=%ld,%ld,%ld,%ld,%ld, msg=%s\n",
1809 event->message_type, event->data.l[0], event->data.l[1],
1810 event->data.l[2], event->data.l[3], event->data.l[4],
1811 p_data);
1812 #endif
1813 TRACE("unrecognized ClientMessage\n" );
1818 /**********************************************************************
1819 * EVENT_EnterNotify
1821 * Install colormap when Wine window is focused in
1822 * self-managed mode with private colormap
1824 #if 0
1825 void EVENT_EnterNotify( HWND hWnd, XCrossingEvent *event )
1827 if( !Options.managed && X11DRV_GetXRootWindow() == DefaultRootWindow(display) &&
1828 (COLOR_GetSystemPaletteFlags() & COLOR_PRIVATE) && GetFocus() )
1829 TSXInstallColormap( display, X11DRV_PALETTE_GetColormap() );
1831 #endif
1833 /**********************************************************************
1834 * EVENT_MapNotify
1836 void EVENT_MapNotify( HWND hWnd, XMapEvent *event )
1838 HWND hwndFocus = GetFocus();
1839 WND *wndFocus = WIN_FindWndPtr(hwndFocus);
1840 WND *pWnd = WIN_FindWndPtr(hWnd);
1841 if (pWnd->flags & WIN_MANAGED)
1843 DCE_InvalidateDCE( pWnd, &pWnd->rectWindow );
1844 pWnd->dwStyle &= ~WS_MINIMIZE;
1845 pWnd->dwStyle |= WS_VISIBLE;
1846 ShowOwnedPopups(hWnd,TRUE);
1848 WIN_ReleaseWndPtr(pWnd);
1850 if (hwndFocus && IsChild( hWnd, hwndFocus ))
1851 X11DRV_WND_SetFocus(wndFocus);
1853 WIN_ReleaseWndPtr(wndFocus);
1855 return;
1859 /**********************************************************************
1860 * EVENT_MapNotify
1862 void EVENT_UnmapNotify( HWND hWnd, XUnmapEvent *event )
1864 WND *pWnd = WIN_FindWndPtr(hWnd);
1865 if (pWnd->flags & WIN_MANAGED)
1867 EndMenu();
1868 if( pWnd->dwStyle & WS_VISIBLE )
1870 pWnd->dwStyle |= WS_MINIMIZE;
1871 pWnd->dwStyle &= ~WS_VISIBLE;
1872 ShowOwnedPopups(hWnd,FALSE);
1875 WIN_ReleaseWndPtr(pWnd);
1878 /***********************************************************************
1879 * EVENT_MappingNotify
1881 static void EVENT_MappingNotify( XMappingEvent *event )
1883 TSXRefreshKeyboardMapping(event);
1885 /* reinitialize Wine-X11 driver keyboard table */
1886 X11DRV_KEYBOARD_Init();
1890 /**********************************************************************
1891 * X11DRV_EVENT_SetInputMethod
1893 INPUT_TYPE X11DRV_EVENT_SetInputMethod(INPUT_TYPE type)
1895 INPUT_TYPE prev = current_input_type;
1897 /* Flag not used yet */
1898 in_transition = FALSE;
1899 current_input_type = type;
1901 return prev;
1904 #ifdef HAVE_LIBXXF86DGA2
1905 /**********************************************************************
1906 * X11DRV_EVENT_SetDGAStatus
1908 void X11DRV_EVENT_SetDGAStatus(HWND hwnd, int event_base)
1910 if (event_base < 0) {
1911 DGAUsed = FALSE;
1912 DGAhwnd = 0;
1913 } else {
1914 DGAUsed = TRUE;
1915 DGAhwnd = hwnd;
1916 DGAMotionEventType = event_base + MotionNotify;
1917 DGAButtonPressEventType = event_base + ButtonPress;
1918 DGAButtonReleaseEventType = event_base + ButtonRelease;
1919 DGAKeyPressEventType = event_base + KeyPress;
1920 DGAKeyReleaseEventType = event_base + KeyRelease;
1924 /* DGA2 event handlers */
1925 static void EVENT_DGAMotionEvent( XDGAMotionEvent *event )
1927 MOUSE_SendEvent( MOUSEEVENTF_MOVE,
1928 event->dx, event->dy,
1929 X11DRV_EVENT_XStateToKeyState( event->state ),
1930 event->time - MSG_WineStartTicks,
1931 DGAhwnd );
1934 static void EVENT_DGAButtonPressEvent( XDGAButtonEvent *event )
1936 static WORD statusCodes[NB_BUTTONS] =
1937 { MOUSEEVENTF_LEFTDOWN, MOUSEEVENTF_MIDDLEDOWN, MOUSEEVENTF_RIGHTDOWN };
1938 int buttonNum = event->button - 1;
1940 WORD keystate;
1942 if (buttonNum >= NB_BUTTONS) return;
1944 keystate = X11DRV_EVENT_XStateToKeyState( event->state );
1946 switch (buttonNum)
1948 case 0:
1949 keystate |= MK_LBUTTON;
1950 break;
1951 case 1:
1952 keystate |= MK_MBUTTON;
1953 break;
1954 case 2:
1955 keystate |= MK_RBUTTON;
1956 break;
1959 MOUSE_SendEvent( statusCodes[buttonNum],
1960 0, 0,
1961 keystate,
1962 event->time - MSG_WineStartTicks,
1963 DGAhwnd );
1966 static void EVENT_DGAButtonReleaseEvent( XDGAButtonEvent *event )
1968 static WORD statusCodes[NB_BUTTONS] =
1969 { MOUSEEVENTF_LEFTUP, MOUSEEVENTF_MIDDLEUP, MOUSEEVENTF_RIGHTUP };
1970 int buttonNum = event->button - 1;
1972 WORD keystate;
1974 if (buttonNum >= NB_BUTTONS) return;
1976 keystate = X11DRV_EVENT_XStateToKeyState( event->state );
1978 switch (buttonNum)
1980 case 0:
1981 keystate &= ~MK_LBUTTON;
1982 break;
1983 case 1:
1984 keystate &= ~MK_MBUTTON;
1985 break;
1986 case 2:
1987 keystate &= ~MK_RBUTTON;
1988 break;
1991 MOUSE_SendEvent( statusCodes[buttonNum],
1992 0, 0,
1993 keystate,
1994 event->time - MSG_WineStartTicks,
1995 DGAhwnd );
1998 #endif
2000 #ifdef HAVE_LIBXXSHM
2003 Normal XShm operation:
2005 X11 service thread app thread
2006 ------------- ----------------- ------------------------
2007 (idle) ddraw calls XShmPutImage
2008 (copies data) (waiting for shm_event)
2009 ShmCompletion -> (waiting for shm_event)
2010 (idle) signal shm_event ->
2011 (idle) returns to app
2013 However, this situation can occur for some reason:
2015 X11 service thread app thread
2016 ------------- ----------------- ------------------------
2017 Expose ->
2018 WM_ERASEBKGND? ->
2019 (waiting for app) ddraw calls XShmPutImage
2020 (copies data) (waiting for app) (waiting for shm_event)
2021 ShmCompletion (waiting for app) (waiting for shm_event)
2022 (idle) DEADLOCK DEADLOCK
2024 which is why I also wait for shm_read and do XCheckTypedEvent()
2025 calls in the wait loop. This results in:
2027 X11 service thread app thread
2028 ------------- ----------------- ------------------------
2029 ShmCompletion (waiting for app) waking up on shm_read
2030 (idle) (waiting for app) XCheckTypedEvent() -> signal shm_event
2031 (waiting for app) returns
2032 (idle)
2035 typedef struct {
2036 Drawable draw;
2037 LONG state, waiter;
2038 HANDLE sema;
2039 } shm_qs;
2041 /* FIXME: this is not pretty */
2042 static HANDLE shm_read = 0;
2044 #define SHM_MAX_Q 4
2045 static volatile shm_qs shm_q[SHM_MAX_Q];
2047 static void EVENT_ShmCompletion( XShmCompletionEvent *event )
2049 int n;
2051 TRACE("Got ShmCompletion for drawable %ld (time %ld)\n", event->drawable, GetTickCount() );
2053 for (n=0; n<SHM_MAX_Q; n++)
2054 if ((shm_q[n].draw == event->drawable) && (shm_q[n].state == 0)) {
2055 HANDLE sema = shm_q[n].sema;
2056 if (!InterlockedCompareExchange((PVOID*)&shm_q[n].state, (PVOID)1, (PVOID)0)) {
2057 ReleaseSemaphore(sema, 1, NULL);
2058 TRACE("Signaling ShmCompletion (#%d) (semaphore %x)\n", n, sema);
2060 return;
2063 ERR("Got ShmCompletion for unknown drawable %ld\n", event->drawable );
2066 int X11DRV_EVENT_PrepareShmCompletion( Drawable dw )
2068 int n;
2070 if (!shm_read)
2071 shm_read = FILE_DupUnixHandle( ConnectionNumber(display), GENERIC_READ | SYNCHRONIZE );
2073 for (n=0; n<SHM_MAX_Q; n++)
2074 if (!shm_q[n].draw)
2075 if (!InterlockedCompareExchange((PVOID*)&shm_q[n].draw, (PVOID)dw, (PVOID)0))
2076 break;
2078 if (n>=SHM_MAX_Q) {
2079 ERR("Maximum number of outstanding ShmCompletions exceeded!\n");
2080 return 0;
2083 shm_q[n].state = 0;
2084 if (!shm_q[n].sema) {
2085 shm_q[n].sema = CreateSemaphoreA( NULL, 0, 256, NULL );
2086 TRACE("Allocated ShmCompletion slots have been increased to %d, new semaphore is %x\n", n+1, shm_q[n].sema);
2089 TRACE("Prepared ShmCompletion (#%d) wait for drawable %ld (thread %lx) (time %ld)\n", n, dw, GetCurrentThreadId(), GetTickCount() );
2090 return n+1;
2093 static void X11DRV_EVENT_WaitReplaceShmCompletionInternal( int *compl, Drawable dw, int creat )
2095 int n = *compl;
2096 LONG nn, st;
2097 HANDLE sema;
2099 if ((!n) || (creat && (!shm_q[n-1].draw))) {
2100 nn = X11DRV_EVENT_PrepareShmCompletion(dw);
2101 if (!(n=(LONG)InterlockedCompareExchange((PVOID*)compl, (PVOID)nn, (PVOID)n)))
2102 return;
2103 /* race for compl lost, clear slot */
2104 shm_q[nn-1].draw = 0;
2105 return;
2108 if (dw && (shm_q[n-1].draw != dw)) {
2109 /* this shouldn't happen with the current ddraw implementation */
2110 FIXME("ShmCompletion replace with different drawable!\n");
2111 return;
2114 sema = shm_q[n-1].sema;
2115 if (!sema) {
2116 /* nothing to wait on (PrepareShmCompletion not done yet?), so probably nothing to wait for */
2117 return;
2120 nn = InterlockedExchangeAdd((PLONG)&shm_q[n-1].waiter, 1);
2121 if ((!shm_q[n-1].draw) || (shm_q[n-1].state == 2)) {
2122 /* too late, the wait was just cleared (wait complete) */
2123 TRACE("Wait skip for ShmCompletion (#%d) (thread %lx) (time %ld) (semaphore %x)\n", n-1, GetCurrentThreadId(), GetTickCount(), sema);
2124 } else {
2125 TRACE("Waiting for ShmCompletion (#%d) (thread %lx) (time %ld) (semaphore %x)\n", n-1, GetCurrentThreadId(), GetTickCount(), sema);
2126 if (nn) {
2127 /* another thread is already waiting, let the primary waiter do the dirty work
2128 * (to avoid TSX critical section contention - that could get really slow) */
2129 WaitForSingleObject( sema, INFINITE );
2130 } else
2131 /* we're primary waiter - first check if it's already triggered */
2132 if ( WaitForSingleObject( sema, 0 ) != WAIT_OBJECT_0 ) {
2133 /* nope, may need to poll X event queue, in case the service thread is blocked */
2134 XEvent event;
2135 HANDLE hnd[2];
2137 hnd[0] = sema;
2138 hnd[1] = shm_read;
2139 do {
2140 /* check X event queue */
2141 if (TSXCheckTypedEvent( display, ShmCompletionType, &event)) {
2142 EVENT_ProcessEvent( &event );
2144 } while ( WaitForMultipleObjects(2, hnd, FALSE, INFINITE) > WAIT_OBJECT_0 );
2146 TRACE("Wait complete (thread %lx) (time %ld)\n", GetCurrentThreadId(), GetTickCount() );
2148 /* clear wait */
2149 st = InterlockedExchange((LPLONG)&shm_q[n-1].state, 2);
2150 if (st != 2) {
2151 /* first waiter to return, release all other waiters */
2152 nn = shm_q[n-1].waiter;
2153 TRACE("Signaling %ld additional ShmCompletion (#%d) waiter(s), semaphore %x\n", nn-1, n-1, sema);
2154 ReleaseSemaphore(sema, nn-1, NULL);
2157 nn = InterlockedDecrement((LPLONG)&shm_q[n-1].waiter);
2158 if (!nn) {
2159 /* last waiter to return, replace drawable and prepare new wait */
2160 shm_q[n-1].draw = dw;
2161 shm_q[n-1].state = 0;
2165 void X11DRV_EVENT_WaitReplaceShmCompletion( int *compl, Drawable dw )
2167 X11DRV_EVENT_WaitReplaceShmCompletionInternal( compl, dw, 1 );
2170 void X11DRV_EVENT_WaitShmCompletion( int compl )
2172 if (!compl) return;
2173 X11DRV_EVENT_WaitReplaceShmCompletionInternal( &compl, 0, 0 );
2176 void X11DRV_EVENT_WaitShmCompletions( Drawable dw )
2178 int n;
2180 for (n=0; n<SHM_MAX_Q; n++)
2181 if (shm_q[n].draw == dw)
2182 X11DRV_EVENT_WaitShmCompletion( n+1 );
2185 #endif /* defined(HAVE_LIBXXSHM) */
2187 #endif /* !defined(X_DISPLAY_MISSING) */