Make WIN_SetStyle more thread-safe by specifying the bits to change
[wine] / dlls / x11drv / event.c
1 /*
2  * X11 event driver
3  *
4  * Copyright 1993 Alexandre Julliard
5  *           1999 Noel Borthwick
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  */
21
22 #define COM_NO_WINDOWS_H
23 #include "config.h"
24
25 #include <X11/Xatom.h>
26 #include <X11/keysym.h>
27 #include <X11/Xlib.h>
28 #include <X11/Xresource.h>
29 #include <X11/Xutil.h>
30 #ifdef HAVE_LIBXXF86DGA2
31 #include <X11/extensions/xf86dga.h>
32 #endif
33
34 #include <assert.h>
35 #include <stdarg.h>
36 #include <string.h>
37 #include "wine/winuser16.h"
38 #include "windef.h"
39 #include "winbase.h"
40 #include "winuser.h"
41 #include "wingdi.h"
42 #include "shlobj.h"  /* DROPFILES */
43
44 #include "win.h"
45 #include "winreg.h"
46 #include "x11drv.h"
47 #include "shellapi.h"
48 #include "wine/debug.h"
49
50 WINE_DEFAULT_DEBUG_CHANNEL(event);
51 WINE_DECLARE_DEBUG_CHANNEL(clipboard);
52
53 /* X context to associate a hwnd to an X window */
54 extern XContext winContext;
55
56 extern BOOL ximInComposeMode;
57
58 #define DndNotDnd       -1    /* OffiX drag&drop */
59 #define DndUnknown      0
60 #define DndRawData      1
61 #define DndFile         2
62 #define DndFiles        3
63 #define DndText         4
64 #define DndDir          5
65 #define DndLink         6
66 #define DndExe          7
67
68 #define DndEND          8
69
70 #define DndURL          128   /* KDE drag&drop */
71
72 static const char * const event_names[] =
73 {
74   "", "", "KeyPress", "KeyRelease", "ButtonPress", "ButtonRelease",
75   "MotionNotify", "EnterNotify", "LeaveNotify", "FocusIn", "FocusOut",
76   "KeymapNotify", "Expose", "GraphicsExpose", "NoExpose", "VisibilityNotify",
77   "CreateNotify", "DestroyNotify", "UnmapNotify", "MapNotify", "MapRequest",
78   "ReparentNotify", "ConfigureNotify", "ConfigureRequest", "GravityNotify",
79   "ResizeRequest", "CirculateNotify", "CirculateRequest", "PropertyNotify",
80   "SelectionClear", "SelectionRequest", "SelectionNotify", "ColormapNotify",
81   "ClientMessage", "MappingNotify"
82 };
83
84
85 static void EVENT_ProcessEvent( XEvent *event );
86
87   /* Event handlers */
88 static void EVENT_FocusIn( HWND hWnd, XFocusChangeEvent *event );
89 static void EVENT_FocusOut( HWND hWnd, XFocusChangeEvent *event );
90 static void EVENT_SelectionRequest( HWND hWnd, XSelectionRequestEvent *event, BOOL bIsMultiple );
91 static void EVENT_SelectionClear( HWND hWnd, XSelectionClearEvent *event);
92 static void EVENT_PropertyNotify( XPropertyEvent *event );
93 static void EVENT_ClientMessage( HWND hWnd, XClientMessageEvent *event );
94
95 #ifdef HAVE_LIBXXF86DGA2
96 static int DGAMotionEventType;
97 static int DGAButtonPressEventType;
98 static int DGAButtonReleaseEventType;
99 static int DGAKeyPressEventType;
100 static int DGAKeyReleaseEventType;
101
102 static BOOL DGAUsed = FALSE;
103 static HWND DGAhwnd = 0;
104 #endif
105
106 /* Static used for the current input method */
107 static INPUT_TYPE current_input_type = X11DRV_INPUT_ABSOLUTE;
108 static BOOL in_transition = FALSE; /* This is not used as for today */
109
110
111 /***********************************************************************
112  *           process_events
113  */
114 static int process_events( struct x11drv_thread_data *data )
115 {
116     XEvent event;
117     int count = 0;
118
119     wine_tsx11_lock();
120     while ( XPending( data->display ) )
121     {
122         Bool ignore;
123
124         XNextEvent( data->display, &event );
125         ignore = XFilterEvent( &event, None );
126         wine_tsx11_unlock();
127         if (!ignore) EVENT_ProcessEvent( &event );
128         count++;
129         wine_tsx11_lock();
130     }
131     wine_tsx11_unlock();
132     return count;
133 }
134
135
136 /***********************************************************************
137  *           MsgWaitForMultipleObjectsEx   (X11DRV.@)
138  */
139 DWORD X11DRV_MsgWaitForMultipleObjectsEx( DWORD count, const HANDLE *handles,
140                                           DWORD timeout, DWORD mask, DWORD flags )
141 {
142     HANDLE new_handles[MAXIMUM_WAIT_OBJECTS+1];  /* FIXME! */
143     DWORD i, ret;
144     struct x11drv_thread_data *data = NtCurrentTeb()->driver_data;
145
146     if (!data || data->process_event_count)
147         return WaitForMultipleObjectsEx( count, handles, flags & MWMO_WAITALL,
148                                          timeout, flags & MWMO_ALERTABLE );
149
150     /* check whether only server queue handle was passed in */
151     if (count < 2) flags &= ~MWMO_WAITALL;
152
153     for (i = 0; i < count; i++) new_handles[i] = handles[i];
154     new_handles[count] = data->display_fd;
155
156     wine_tsx11_lock();
157     XFlush( gdi_display );
158     XFlush( data->display );
159     wine_tsx11_unlock();
160
161     data->process_event_count++;
162     if (process_events( data )) ret = count;
163     else
164     {
165         ret = WaitForMultipleObjectsEx( count+1, new_handles, flags & MWMO_WAITALL,
166                                         timeout, flags & MWMO_ALERTABLE );
167         if (ret == count) process_events( data );
168     }
169     data->process_event_count--;
170     return ret;
171 }
172
173 /***********************************************************************
174  *           EVENT_x11_time_to_win32_time
175  *
176  * Make our timer and the X timer line up as best we can
177  *  Pass 0 to retrieve the current adjustment value (times -1)
178  */
179 DWORD EVENT_x11_time_to_win32_time(Time time)
180 {
181   static DWORD adjust = 0;
182   DWORD now = GetTickCount();
183   DWORD ret;
184
185   if (! adjust && time != 0)
186   {
187     ret = now;
188     adjust = time - now;
189   }
190   else
191   {
192       /* If we got an event in the 'future', then our clock is clearly wrong. 
193          If we got it more than 10000 ms in the future, then it's most likely
194          that the clock has wrapped.  */
195
196       ret = time - adjust;
197       if (ret > now && ((ret - now) < 10000) && time != 0)
198       {
199         adjust += ret - now;
200         ret    -= ret - now;
201       }
202   }
203
204   return ret;
205
206 }
207
208 /***********************************************************************
209  *           EVENT_ProcessEvent
210  *
211  * Process an X event.
212  */
213 static void EVENT_ProcessEvent( XEvent *event )
214 {
215   HWND hWnd;
216   Display *display = event->xany.display;
217
218   TRACE( "called.\n" );
219
220   switch (event->type)
221   {
222     case SelectionNotify: /* all of these should be caught by XCheckTypedWindowEvent() */
223          FIXME("Got SelectionNotify - must not happen!\n");
224          /* fall through */
225
226       /* We get all these because of StructureNotifyMask.
227          This check is placed here to avoid getting error messages below,
228          as X might send some of these even for windows that have already
229          been deleted ... */
230     case CirculateNotify:
231     case CreateNotify:
232     case DestroyNotify:
233     case GravityNotify:
234     case ReparentNotify:
235       return;
236   }
237
238 #ifdef HAVE_LIBXXF86DGA2
239   if (DGAUsed) {
240     if (event->type == DGAMotionEventType) {
241       TRACE("DGAMotionEvent received.\n");
242       X11DRV_DGAMotionEvent( DGAhwnd, (XDGAMotionEvent *)event );
243       return;
244     }
245     if (event->type == DGAButtonPressEventType) {
246       TRACE("DGAButtonPressEvent received.\n");
247       X11DRV_DGAButtonPressEvent( DGAhwnd, (XDGAButtonEvent *)event );
248       return;
249     }
250     if (event->type == DGAButtonReleaseEventType) {
251       TRACE("DGAButtonReleaseEvent received.\n");
252       X11DRV_DGAButtonReleaseEvent( DGAhwnd, (XDGAButtonEvent *)event );
253       return;
254     }
255     if ((event->type == DGAKeyPressEventType) ||
256         (event->type == DGAKeyReleaseEventType)) {
257       /* Fill a XKeyEvent to send to EVENT_Key */
258       XKeyEvent ke;
259       XDGAKeyEvent *evt = (XDGAKeyEvent *) event;
260
261       TRACE("DGAKeyPress/ReleaseEvent received.\n");
262
263       if (evt->type == DGAKeyReleaseEventType)
264         ke.type = KeyRelease;
265       else
266         ke.type = KeyPress;
267       ke.serial = evt->serial;
268       ke.send_event = FALSE;
269       ke.display = evt->display;
270       ke.window = 0;
271       ke.root = 0;
272       ke.subwindow = 0;
273       ke.time = evt->time;
274       ke.x = -1;
275       ke.y = -1;
276       ke.x_root = -1;
277       ke.y_root = -1;
278       ke.state = evt->state;
279       ke.keycode = evt->keycode;
280       ke.same_screen = TRUE;
281       X11DRV_KeyEvent( 0, &ke );
282       return;
283     }
284   }
285 #endif
286
287   wine_tsx11_lock();
288   if (XFindContext( display, event->xany.window, winContext, (char **)&hWnd ) != 0)
289       hWnd = 0;  /* Not for a registered window */
290   wine_tsx11_unlock();
291   if (!hWnd && event->xany.window == root_window) hWnd = GetDesktopWindow();
292
293   if (!hWnd && event->type != PropertyNotify &&
294       event->type != MappingNotify && event->type != KeymapNotify)
295       WARN( "Got event %s for unknown Window %08lx\n",
296             event_names[event->type], event->xany.window );
297   else if (event->type <= MappingNotify)
298       TRACE("Got event %s for hwnd/window %p/%lx, GetFocus()=%p\n",
299             event_names[event->type], hWnd, event->xany.window, GetFocus() );
300   else
301       TRACE("Got extension event for hwnd/window %p/%lx, GetFocus()=%p\n",
302             hWnd, event->xany.window, GetFocus() );
303
304   if (X11DRV_ProcessTabletEvent(hWnd, event))
305   {
306         TRACE("Return: filtered by tablet\n");
307         return;
308   }
309
310   switch(event->type)
311     {
312     case KeyPress:
313     case KeyRelease:
314       /* FIXME: should generate a motion event if event point is different from current pos */
315       X11DRV_KeyEvent( hWnd, (XKeyEvent*)event );
316       break;
317
318     case ButtonPress:
319       X11DRV_ButtonPress( hWnd, (XButtonEvent*)event );
320       break;
321
322     case ButtonRelease:
323       X11DRV_ButtonRelease( hWnd, (XButtonEvent*)event );
324       break;
325
326     case MotionNotify:
327       X11DRV_MotionNotify( hWnd, (XMotionEvent*)event );
328       break;
329
330     case EnterNotify:
331       X11DRV_EnterNotify( hWnd, (XCrossingEvent*)event );
332       break;
333
334     case FocusIn:
335       EVENT_FocusIn( hWnd, (XFocusChangeEvent*)event );
336       break;
337
338     case FocusOut:
339       EVENT_FocusOut( hWnd, (XFocusChangeEvent*)event );
340       break;
341
342     case Expose:
343       X11DRV_Expose( hWnd, &event->xexpose );
344       break;
345
346     case ConfigureNotify:
347       if (!hWnd) return;
348       X11DRV_ConfigureNotify( hWnd, &event->xconfigure );
349       break;
350
351     case SelectionRequest:
352       if (!hWnd) return;
353       EVENT_SelectionRequest( hWnd, (XSelectionRequestEvent *)event, FALSE );
354       break;
355
356     case SelectionClear:
357       if (!hWnd) return;
358       EVENT_SelectionClear( hWnd, (XSelectionClearEvent*) event );
359       break;
360
361     case PropertyNotify:
362       EVENT_PropertyNotify( (XPropertyEvent *)event );
363       break;
364
365     case ClientMessage:
366       if (!hWnd) return;
367       EVENT_ClientMessage( hWnd, (XClientMessageEvent *) event );
368       break;
369
370     case NoExpose:
371       break;
372
373     case MapNotify:
374       X11DRV_MapNotify( hWnd, (XMapEvent *)event );
375       break;
376
377     case UnmapNotify:
378       X11DRV_UnmapNotify( hWnd, (XUnmapEvent *)event );
379       break;
380
381     case KeymapNotify:
382       X11DRV_KeymapNotify( hWnd, (XKeymapEvent *)event );
383       break;
384
385     case MappingNotify:
386       X11DRV_MappingNotify( (XMappingEvent *) event );
387       break;
388
389     default:
390       WARN("Unprocessed event %s for hwnd %p\n", event_names[event->type], hWnd );
391       break;
392     }
393     TRACE( "returns.\n" );
394 }
395
396
397 /*******************************************************************
398  *         can_activate_window
399  *
400  * Check if we can activate the specified window.
401  */
402 inline static BOOL can_activate_window( HWND hwnd )
403 {
404     LONG style = GetWindowLongW( hwnd, GWL_STYLE );
405     if (!(style & WS_VISIBLE)) return FALSE;
406     if ((style & (WS_POPUP|WS_CHILD)) == WS_CHILD) return FALSE;
407     return !(style & WS_DISABLED);
408 }
409
410
411 /**********************************************************************
412  *              set_focus
413  */
414 static void set_focus( HWND hwnd, Time time )
415 {
416     HWND focus;
417     Window win;
418
419     TRACE( "setting foreground window to %p\n", hwnd );
420     SetForegroundWindow( hwnd );
421
422     focus = GetFocus();
423     if (focus) focus = GetAncestor( focus, GA_ROOT );
424     win = X11DRV_get_whole_window(focus);
425
426     if (win)
427     {
428         TRACE( "setting focus to %p (%lx) time=%ld\n", focus, win, time );
429         wine_tsx11_lock();
430         XSetInputFocus( thread_display(), win, RevertToParent, time );
431         wine_tsx11_unlock();
432     }
433 }
434
435
436 /**********************************************************************
437  *              handle_wm_protocols_message
438  */
439 static void handle_wm_protocols_message( HWND hwnd, XClientMessageEvent *event )
440 {
441     Atom protocol = (Atom)event->data.l[0];
442
443     if (!protocol) return;
444
445     if (protocol == x11drv_atom(WM_DELETE_WINDOW))
446     {
447         /* Ignore the delete window request if the window has been disabled
448          * and we are in managed mode. This is to disallow applications from
449          * being closed by the window manager while in a modal state.
450          */
451         if (IsWindowEnabled(hwnd)) PostMessageW( hwnd, WM_SYSCOMMAND, SC_CLOSE, 0 );
452     }
453     else if (protocol == x11drv_atom(WM_TAKE_FOCUS))
454     {
455         Time event_time = (Time)event->data.l[1];
456         HWND last_focus = x11drv_thread_data()->last_focus;
457
458         TRACE( "got take focus msg for %p, enabled=%d, focus=%p, active=%p, fg=%p, last=%p\n",
459                hwnd, IsWindowEnabled(hwnd), GetFocus(), GetActiveWindow(),
460                GetForegroundWindow(), last_focus );
461
462         if (can_activate_window(hwnd))
463         {
464             /* simulate a mouse click on the caption to find out
465              * whether the window wants to be activated */
466             LRESULT ma = SendMessageW( hwnd, WM_MOUSEACTIVATE,
467                                        (WPARAM)GetAncestor( hwnd, GA_ROOT ),
468                                        MAKELONG(HTCAPTION,WM_LBUTTONDOWN) );
469             if (ma != MA_NOACTIVATEANDEAT && ma != MA_NOACTIVATE) set_focus( hwnd, event_time );
470             else TRACE( "not setting focus to %p (%lx), ma=%ld\n", hwnd, event->window, ma );
471         }
472         else
473         {
474             hwnd = GetFocus();
475             if (hwnd) hwnd = GetAncestor( hwnd, GA_ROOT );
476             if (!hwnd) hwnd = GetActiveWindow();
477             if (!hwnd) hwnd = last_focus;
478             if (hwnd && can_activate_window(hwnd)) set_focus( hwnd, event_time );
479         }
480     }
481     else if (protocol == x11drv_atom(_NET_WM_PING))
482     {
483       XClientMessageEvent xev;
484       xev = *event;
485       
486       TRACE("NET_WM Ping\n");
487       xev.window = DefaultRootWindow(xev.display);
488       XSendEvent(xev.display, xev.window, False, SubstructureRedirectMask | SubstructureNotifyMask, (XEvent*)&xev);
489       /* this line is semi-stolen from gtk2 */
490       TRACE("NET_WM Pong\n");
491     }
492 }
493
494
495 static const char * const focus_details[] =
496 {
497     "NotifyAncestor",
498     "NotifyVirtual",
499     "NotifyInferior",
500     "NotifyNonlinear",
501     "NotifyNonlinearVirtual",
502     "NotifyPointer",
503     "NotifyPointerRoot",
504     "NotifyDetailNone"
505 };
506
507 /**********************************************************************
508  *              EVENT_FocusIn
509  */
510 static void EVENT_FocusIn( HWND hwnd, XFocusChangeEvent *event )
511 {
512     XIC xic;
513
514     if (!hwnd) return;
515
516     TRACE( "win %p xwin %lx detail=%s\n", hwnd, event->window, focus_details[event->detail] );
517
518     if (event->detail == NotifyPointer) return;
519
520     if ((xic = X11DRV_get_ic( hwnd )))
521     {
522         wine_tsx11_lock();
523         XSetICFocus( xic );
524         wine_tsx11_unlock();
525     }
526     if (use_take_focus) return;  /* ignore FocusIn if we are using take focus */
527
528     if (!can_activate_window(hwnd))
529     {
530         HWND hwnd = GetFocus();
531         if (hwnd) hwnd = GetAncestor( hwnd, GA_ROOT );
532         if (!hwnd) hwnd = GetActiveWindow();
533         if (!hwnd) hwnd = x11drv_thread_data()->last_focus;
534         if (hwnd && can_activate_window(hwnd)) set_focus( hwnd, CurrentTime );
535     }
536     else SetForegroundWindow( hwnd );
537 }
538
539
540 /**********************************************************************
541  *              EVENT_FocusOut
542  *
543  * Note: only top-level windows get FocusOut events.
544  */
545 static void EVENT_FocusOut( HWND hwnd, XFocusChangeEvent *event )
546 {
547     HWND hwnd_tmp;
548     Window focus_win;
549     int revert;
550     XIC xic;
551
552     if (!hwnd) return;
553
554     TRACE( "win %p xwin %lx detail=%s\n", hwnd, event->window, focus_details[event->detail] );
555
556     if (event->detail == NotifyPointer) return;
557     if (ximInComposeMode) return;
558
559     x11drv_thread_data()->last_focus = hwnd;
560     if ((xic = X11DRV_get_ic( hwnd )))
561     {
562         wine_tsx11_lock();
563         XUnsetICFocus( xic );
564         wine_tsx11_unlock();
565     }
566     if (hwnd != GetForegroundWindow()) return;
567     SendMessageA( hwnd, WM_CANCELMODE, 0, 0 );
568
569     /* don't reset the foreground window, if the window which is
570        getting the focus is a Wine window */
571
572     wine_tsx11_lock();
573     XGetInputFocus( thread_display(), &focus_win, &revert );
574     if (focus_win)
575     {
576         if (XFindContext( thread_display(), focus_win, winContext, (char **)&hwnd_tmp ) != 0)
577             focus_win = 0;
578     }
579     wine_tsx11_unlock();
580
581     if (!focus_win)
582     {
583         /* Abey : 6-Oct-99. Check again if the focus out window is the
584            Foreground window, because in most cases the messages sent
585            above must have already changed the foreground window, in which
586            case we don't have to change the foreground window to 0 */
587         if (hwnd == GetForegroundWindow())
588         {
589             TRACE( "lost focus, setting fg to 0\n" );
590             SetForegroundWindow( 0 );
591         }
592     }
593 }
594
595
596 /***********************************************************************
597  *           EVENT_SelectionRequest_AddTARGETS
598  *  Utility function for EVENT_SelectionRequest_TARGETS.
599  */
600 static BOOL EVENT_SelectionRequest_AddTARGETS(Atom* targets, unsigned long cTargets, Atom prop)
601 {
602     unsigned int i;
603     BOOL bExists;
604
605     /* Scan through what we have so far to avoid duplicates */
606     for (i = 0, bExists = FALSE; i < cTargets; i++)
607     {
608         if (targets[i] == prop)
609         {
610             bExists = TRUE;
611             break;
612         }
613     }
614
615     if (!bExists)
616         targets[cTargets] = prop;
617
618     return !bExists;
619 }
620
621
622 /***********************************************************************
623  *           EVENT_SelectionRequest_TARGETS
624  *  Service a TARGETS selection request event
625  */
626 static Atom EVENT_SelectionRequest_TARGETS( Display *display, Window requestor,
627                                             Atom target, Atom rprop )
628 {
629     Atom* targets;
630     UINT wFormat;
631     UINT alias;
632     ULONG cTargets;
633     LPWINE_CLIPFORMAT lpFormat;
634
635     /*
636      * Count the number of items we wish to expose as selection targets.
637      * We include the TARGETS item, and property aliases
638      */
639     cTargets = X11DRV_CountClipboardFormats() + 1;
640
641     for (wFormat = 0; (wFormat = X11DRV_EnumClipboardFormats(wFormat));)
642     {
643         lpFormat = X11DRV_CLIPBOARD_LookupFormat(wFormat);
644
645         if (lpFormat)
646         {
647             if (!lpFormat->lpDrvExportFunc)
648                 cTargets--;
649
650             if (X11DRV_CLIPBOARD_LookupPropertyAlias(lpFormat->drvData))
651                 cTargets++;
652         }
653         /* else most likely unregistered format such as CF_PRIVATE or CF_GDIOBJ */
654     }
655
656     TRACE_(clipboard)(" found %ld formats\n", cTargets);
657
658     /* Allocate temp buffer */
659     targets = (Atom*)HeapAlloc( GetProcessHeap(), 0, cTargets * sizeof(Atom));
660     if(targets == NULL) 
661         return None;
662
663     /* Create TARGETS property list (First item in list is TARGETS itself) */
664     for (targets[0] = x11drv_atom(TARGETS), cTargets = 1, wFormat = 0;
665           (wFormat = X11DRV_EnumClipboardFormats(wFormat));)
666     {
667         lpFormat = X11DRV_CLIPBOARD_LookupFormat(wFormat);
668
669         if (lpFormat)
670         {
671             if (lpFormat->lpDrvExportFunc)
672             {
673                 if (EVENT_SelectionRequest_AddTARGETS(targets, cTargets, lpFormat->drvData))
674                    cTargets++;
675             }
676
677             /* Check if any alias should be listed */
678             alias = X11DRV_CLIPBOARD_LookupPropertyAlias(lpFormat->drvData);
679             if (alias)
680             {
681                 if (EVENT_SelectionRequest_AddTARGETS(targets, cTargets, alias))
682                    cTargets++;
683             }
684         }
685     }
686
687     wine_tsx11_lock();
688
689     if (TRACE_ON(clipboard))
690     {
691         unsigned int i;
692         for ( i = 0; i < cTargets; i++)
693         {
694             if (targets[i])
695             {
696                 char *itemFmtName = XGetAtomName(display, targets[i]);
697                 TRACE_(clipboard)("\tAtom# %d:  Property %ld Type %s\n", i, targets[i], itemFmtName);
698                 XFree(itemFmtName);
699             }
700         }
701     }
702
703     /* We may want to consider setting the type to xaTargets instead,
704      * in case some apps expect this instead of XA_ATOM */
705     XChangeProperty(display, requestor, rprop, XA_ATOM, 32,
706                     PropModeReplace, (unsigned char *)targets, cTargets);
707     wine_tsx11_unlock();
708
709     HeapFree(GetProcessHeap(), 0, targets);
710
711     return rprop;
712 }
713
714
715 /***********************************************************************
716  *           EVENT_SelectionRequest_MULTIPLE
717  *  Service a MULTIPLE selection request event
718  *  rprop contains a list of (target,property) atom pairs.
719  *  The first atom names a target and the second names a property.
720  *  The effect is as if we have received a sequence of SelectionRequest events
721  *  (one for each atom pair) except that:
722  *  1. We reply with a SelectionNotify only when all the requested conversions
723  *  have been performed.
724  *  2. If we fail to convert the target named by an atom in the MULTIPLE property,
725  *  we replace the atom in the property by None.
726  */
727 static Atom EVENT_SelectionRequest_MULTIPLE( HWND hWnd, XSelectionRequestEvent *pevent )
728 {
729     Display *display = pevent->display;
730     Atom           rprop;
731     Atom           atype=AnyPropertyType;
732     int            aformat;
733     unsigned long  remain;
734     Atom*          targetPropList=NULL;
735     unsigned long  cTargetPropList = 0;
736
737    /* If the specified property is None the requestor is an obsolete client.
738     * We support these by using the specified target atom as the reply property.
739     */
740     rprop = pevent->property;
741     if( rprop == None )
742         rprop = pevent->target;
743     if (!rprop)
744         goto END;
745
746     /* Read the MULTIPLE property contents. This should contain a list of
747      * (target,property) atom pairs.
748      */
749     wine_tsx11_lock();
750     if(XGetWindowProperty(display, pevent->requestor, rprop,
751                             0, 0x3FFF, False, AnyPropertyType, &atype,&aformat,
752                             &cTargetPropList, &remain,
753                             (unsigned char**)&targetPropList) != Success)
754     {
755         wine_tsx11_unlock();
756         TRACE("\tCouldn't read MULTIPLE property\n");
757     }
758     else
759     {
760        TRACE("\tType %s,Format %d,nItems %ld, Remain %ld\n",
761              XGetAtomName(display, atype), aformat, cTargetPropList, remain);
762        wine_tsx11_unlock();
763
764        /*
765         * Make sure we got what we expect.
766         * NOTE: According to the X-ICCCM Version 2.0 documentation the property sent
767         * in a MULTIPLE selection request should be of type ATOM_PAIR.
768         * However some X apps(such as XPaint) are not compliant with this and return
769         * a user defined atom in atype when XGetWindowProperty is called.
770         * The data *is* an atom pair but is not denoted as such.
771         */
772        if(aformat == 32 /* atype == xAtomPair */ )
773        {
774           unsigned int i;
775
776           /* Iterate through the ATOM_PAIR list and execute a SelectionRequest
777            * for each (target,property) pair */
778
779           for (i = 0; i < cTargetPropList; i+=2)
780           {
781               XSelectionRequestEvent event;
782
783               if (TRACE_ON(event))
784               {
785                   char *targetName, *propName;
786                   wine_tsx11_lock();
787                   targetName = XGetAtomName(display, targetPropList[i]);
788                   propName = XGetAtomName(display, targetPropList[i+1]);
789                   TRACE("MULTIPLE(%d): Target='%s' Prop='%s'\n",
790                         i/2, targetName, propName);
791                   XFree(targetName);
792                   XFree(propName);
793                   wine_tsx11_unlock();
794               }
795
796               /* We must have a non "None" property to service a MULTIPLE target atom */
797               if ( !targetPropList[i+1] )
798               {
799                   TRACE("\tMULTIPLE(%d): Skipping target with empty property!\n", i);
800                   continue;
801               }
802
803               /* Set up an XSelectionRequestEvent for this (target,property) pair */
804               memcpy( &event, pevent, sizeof(XSelectionRequestEvent) );
805               event.target = targetPropList[i];
806               event.property = targetPropList[i+1];
807
808               /* Fire a SelectionRequest, informing the handler that we are processing
809                * a MULTIPLE selection request event.
810                */
811               EVENT_SelectionRequest( hWnd, &event, TRUE );
812           }
813        }
814
815        /* Free the list of targets/properties */
816        wine_tsx11_lock();
817        XFree(targetPropList);
818        wine_tsx11_unlock();
819     }
820
821 END:
822     return rprop;
823 }
824
825
826 /***********************************************************************
827  *           EVENT_SelectionRequest
828  *  Process an event selection request event.
829  *  The bIsMultiple flag is used to signal when EVENT_SelectionRequest is called
830  *  recursively while servicing a "MULTIPLE" selection target.
831  *
832  *  Note: We only receive this event when WINE owns the X selection
833  */
834 static void EVENT_SelectionRequest( HWND hWnd, XSelectionRequestEvent *event, BOOL bIsMultiple )
835 {
836     Display *display = event->display;
837   XSelectionEvent result;
838   Atom            rprop = None;
839   Window          request = event->requestor;
840
841   TRACE_(clipboard)("\n");
842
843   /*
844    * We can only handle the selection request if :
845    * The selection is PRIMARY or CLIPBOARD, AND we can successfully open the clipboard.
846    * Don't do these checks or open the clipboard while recursively processing MULTIPLE,
847    * since this has been already done.
848    */
849   if ( !bIsMultiple )
850   {
851     if (((event->selection != XA_PRIMARY) && (event->selection != x11drv_atom(CLIPBOARD))))
852        goto END;
853   }
854
855   /* If the specified property is None the requestor is an obsolete client.
856    * We support these by using the specified target atom as the reply property.
857    */
858   rprop = event->property;
859   if( rprop == None )
860       rprop = event->target;
861
862   if(event->target == x11drv_atom(TARGETS))  /*  Return a list of all supported targets */
863   {
864       /* TARGETS selection request */
865       rprop = EVENT_SelectionRequest_TARGETS( display, request, event->target, rprop );
866   }
867   else if(event->target == x11drv_atom(MULTIPLE))  /*  rprop contains a list of (target, property) atom pairs */
868   {
869       /* MULTIPLE selection request */
870       rprop = EVENT_SelectionRequest_MULTIPLE( hWnd, event );
871   }
872   else
873   {
874       LPWINE_CLIPFORMAT lpFormat = X11DRV_CLIPBOARD_LookupProperty(event->target);
875
876       if (!lpFormat)
877           lpFormat = X11DRV_CLIPBOARD_LookupAliasProperty(event->target);
878
879       if (lpFormat && lpFormat->lpDrvExportFunc)
880       {
881           LPWINE_CLIPDATA lpData = X11DRV_CLIPBOARD_LookupData(lpFormat->wFormatID);
882
883           if (lpData)
884           {
885               unsigned char* lpClipData;
886               DWORD cBytes;
887               HANDLE hClipData = lpFormat->lpDrvExportFunc(request, event->target,
888                   rprop, lpData, &cBytes);
889
890               if (hClipData && (lpClipData = GlobalLock(hClipData)))
891               {
892                   TRACE_(clipboard)("\tUpdating property %s, %ld bytes\n",
893                                     debugstr_w(lpFormat->Name), cBytes);
894
895                   wine_tsx11_lock();
896                   XChangeProperty(display, request, rprop, event->target,
897                       8, PropModeReplace, (unsigned char *)lpClipData, cBytes);
898                   wine_tsx11_unlock();
899
900                   GlobalUnlock(hClipData);
901                   GlobalFree(hClipData);
902               }
903           }
904       }
905   }
906
907 END:
908   /* reply to sender
909    * SelectionNotify should be sent only at the end of a MULTIPLE request
910    */
911   if ( !bIsMultiple )
912   {
913     result.type = SelectionNotify;
914     result.display = display;
915     result.requestor = request;
916     result.selection = event->selection;
917     result.property = rprop;
918     result.target = event->target;
919     result.time = event->time;
920     TRACE("Sending SelectionNotify event...\n");
921     wine_tsx11_lock();
922     XSendEvent(display,event->requestor,False,NoEventMask,(XEvent*)&result);
923     wine_tsx11_unlock();
924   }
925 }
926
927 /***********************************************************************
928  *           EVENT_SelectionClear
929  */
930 static void EVENT_SelectionClear( HWND hWnd, XSelectionClearEvent *event )
931 {
932   if (event->selection == XA_PRIMARY || event->selection == x11drv_atom(CLIPBOARD))
933       X11DRV_CLIPBOARD_ReleaseSelection( event->selection, event->window, hWnd, event->time );
934 }
935
936 /***********************************************************************
937  *           EVENT_PropertyNotify
938  *   We use this to release resources like Pixmaps when a selection
939  *   client no longer needs them.
940  */
941 static void EVENT_PropertyNotify( XPropertyEvent *event )
942 {
943   /* Check if we have any resources to free */
944   TRACE("Received PropertyNotify event: \n");
945
946   switch(event->state)
947   {
948     case PropertyDelete:
949     {
950       TRACE("\tPropertyDelete for atom %ld on window %ld\n",
951             event->atom, (long)event->window);
952       break;
953     }
954
955     case PropertyNewValue:
956     {
957       TRACE("\tPropertyNewValue for atom %ld on window %ld\n\n",
958             event->atom, (long)event->window);
959       break;
960     }
961
962     default:
963       break;
964   }
965 }
966
967 static HWND find_drop_window( HWND hQueryWnd, LPPOINT lpPt )
968 {
969     RECT tempRect;
970
971     if (!IsWindowEnabled(hQueryWnd)) return 0;
972     
973     GetWindowRect(hQueryWnd, &tempRect);
974
975     if(!PtInRect(&tempRect, *lpPt)) return 0;
976
977     if (!IsIconic( hQueryWnd ))
978     {
979         GetClientRect( hQueryWnd, &tempRect );
980         MapWindowPoints( hQueryWnd, 0, (LPPOINT)&tempRect, 2 );
981
982         if (PtInRect( &tempRect, *lpPt))
983         {
984             HWND *list = WIN_ListChildren( hQueryWnd );
985             HWND bResult = 0;
986
987             if (list)
988             {
989                 int i;
990                 
991                 for (i = 0; list[i]; i++)
992                 {
993                     if (GetWindowLongW( list[i], GWL_STYLE ) & WS_VISIBLE)
994                     {
995                         GetWindowRect( list[i], &tempRect );
996                         if (PtInRect( &tempRect, *lpPt )) break;
997                     }
998                 }
999                 if (list[i])
1000                 {
1001                     if (IsWindowEnabled( list[i] ))
1002                         bResult = find_drop_window( list[i], lpPt );
1003                 }
1004                 HeapFree( GetProcessHeap(), 0, list );
1005             }
1006             if(bResult) return bResult;
1007         }
1008     }
1009
1010     if(!(GetWindowLongA( hQueryWnd, GWL_EXSTYLE ) & WS_EX_ACCEPTFILES)) return 0;
1011     
1012     ScreenToClient(hQueryWnd, lpPt);
1013
1014     return hQueryWnd;
1015 }
1016
1017 /**********************************************************************
1018  *           EVENT_DropFromOffix
1019  *
1020  * don't know if it still works (last Changlog is from 96/11/04)
1021  */
1022 static void EVENT_DropFromOffiX( HWND hWnd, XClientMessageEvent *event )
1023 {
1024     unsigned long       data_length;
1025     unsigned long       aux_long;
1026     unsigned char*      p_data = NULL;
1027     union {
1028         Atom    atom_aux;
1029         struct {
1030             int x;
1031             int y;
1032         }       pt_aux;
1033         int     i;
1034     }                   u;
1035     int                 x, y;
1036     BOOL                bAccept;
1037     Window              win, w_aux_root, w_aux_child;
1038     WND*                pWnd;
1039     HWND                hScope = hWnd;
1040
1041     win = X11DRV_get_whole_window(hWnd);
1042     wine_tsx11_lock();
1043     XQueryPointer( event->display, win, &w_aux_root, &w_aux_child,
1044                    &x, &y, (int *) &u.pt_aux.x, (int *) &u.pt_aux.y,
1045                    (unsigned int*)&aux_long);
1046     wine_tsx11_unlock();
1047
1048     pWnd = WIN_GetPtr(hWnd);
1049
1050     /* find out drop point and drop window */
1051     if( x < 0 || y < 0 ||
1052         x > (pWnd->rectWindow.right - pWnd->rectWindow.left) ||
1053         y > (pWnd->rectWindow.bottom - pWnd->rectWindow.top) )
1054     {   
1055         bAccept = pWnd->dwExStyle & WS_EX_ACCEPTFILES; 
1056         x = 0;
1057         y = 0; 
1058     }
1059     else
1060     {
1061         POINT   pt = { x, y };
1062         HWND    hwndDrop = find_drop_window( hWnd, &pt );
1063         if (hwndDrop)
1064         {
1065             x = pt.x;
1066             y = pt.y;
1067             hScope = hwndDrop;
1068             bAccept = TRUE;
1069         }
1070         else
1071         {
1072             bAccept = FALSE;
1073         }
1074     }
1075     WIN_ReleasePtr(pWnd);
1076
1077     if (!bAccept) return;
1078
1079     wine_tsx11_lock();
1080     XGetWindowProperty( event->display, DefaultRootWindow(event->display),
1081                         x11drv_atom(DndSelection), 0, 65535, FALSE,
1082                         AnyPropertyType, &u.atom_aux, (int *) &u.pt_aux.y,
1083                         &data_length, &aux_long, &p_data);
1084     wine_tsx11_unlock();
1085
1086     if( !aux_long && p_data)  /* don't bother if > 64K */
1087     {
1088         signed char *p = (signed char*) p_data;
1089         char *p_drop;
1090
1091         aux_long = 0;
1092         while( *p )  /* calculate buffer size */
1093         {
1094             p_drop = p;
1095             if((u.i = *p) != -1 )
1096             {
1097                 INT len = GetShortPathNameA( p, NULL, 0 );
1098                 if (len) aux_long += len + 1;
1099                 else *p = -1;
1100             }
1101             p += strlen(p) + 1;
1102         }
1103         if( aux_long && aux_long < 65535 )
1104         {
1105             HDROP                 hDrop;
1106             DROPFILES *lpDrop;
1107
1108             aux_long += sizeof(DROPFILES) + 1;
1109             hDrop = GlobalAlloc( GMEM_SHARE, aux_long );
1110             lpDrop = (DROPFILES*)GlobalLock( hDrop );
1111
1112             if( lpDrop )
1113             {
1114                 WND *pDropWnd = WIN_GetPtr( hScope );
1115                 lpDrop->pFiles = sizeof(DROPFILES);
1116                 lpDrop->pt.x = x;
1117                 lpDrop->pt.y = y;
1118                 lpDrop->fNC =
1119                     ( x < (pDropWnd->rectClient.left - pDropWnd->rectWindow.left)  ||
1120                       y < (pDropWnd->rectClient.top - pDropWnd->rectWindow.top)    ||
1121                       x > (pDropWnd->rectClient.right - pDropWnd->rectWindow.left) ||
1122                       y > (pDropWnd->rectClient.bottom - pDropWnd->rectWindow.top) );
1123                 lpDrop->fWide = FALSE;
1124                 WIN_ReleasePtr(pDropWnd);
1125                 p_drop = (char *)(lpDrop + 1);
1126                 p = p_data;
1127                 while(*p)
1128                 {
1129                     if( *p != -1 ) /* use only "good" entries */
1130                     {
1131                         GetShortPathNameA( p, p_drop, 65535 );
1132                         p_drop += strlen( p_drop ) + 1;
1133                     }
1134                     p += strlen(p) + 1;
1135                 }
1136                 *p_drop = '\0';
1137                 PostMessageA( hWnd, WM_DROPFILES, (WPARAM)hDrop, 0L );
1138             }
1139         }
1140     }
1141     wine_tsx11_lock();
1142     if( p_data ) XFree(p_data);
1143     wine_tsx11_unlock();
1144 }
1145
1146 /**********************************************************************
1147  *           EVENT_DropURLs
1148  *
1149  * drop items are separated by \n
1150  * each item is prefixed by its mime type
1151  *
1152  * event->data.l[3], event->data.l[4] contains drop x,y position
1153  */
1154 static void EVENT_DropURLs( HWND hWnd, XClientMessageEvent *event )
1155 {
1156   unsigned long data_length;
1157   unsigned long aux_long, drop_len = 0;
1158   unsigned char *p_data = NULL; /* property data */
1159   char          *p_drop = NULL;
1160   char          *p, *next;
1161   int           x, y;
1162   DROPFILES *lpDrop;
1163   HDROP hDrop;
1164   union {
1165     Atom        atom_aux;
1166     int         i;
1167     Window      w_aux;
1168   }             u; /* unused */
1169
1170   if (!(GetWindowLongW( hWnd, GWL_EXSTYLE ) & WS_EX_ACCEPTFILES)) return;
1171
1172   wine_tsx11_lock();
1173   XGetWindowProperty( event->display, DefaultRootWindow(event->display),
1174                       x11drv_atom(DndSelection), 0, 65535, FALSE,
1175                       AnyPropertyType, &u.atom_aux, &u.i,
1176                       &data_length, &aux_long, &p_data);
1177   wine_tsx11_unlock();
1178   if (aux_long)
1179     WARN("property too large, truncated!\n");
1180   TRACE("urls=%s\n", p_data);
1181
1182   if( !aux_long && p_data) {    /* don't bother if > 64K */
1183     /* calculate length */
1184     p = p_data;
1185     next = strchr(p, '\n');
1186     while (p) {
1187       if (next) *next=0;
1188       if (strncmp(p,"file:",5) == 0 ) {
1189         INT len = GetShortPathNameA( p+5, NULL, 0 );
1190         if (len) drop_len += len + 1;
1191       }
1192       if (next) {
1193         *next = '\n';
1194         p = next + 1;
1195         next = strchr(p, '\n');
1196       } else {
1197         p = NULL;
1198       }
1199     }
1200
1201     if( drop_len && drop_len < 65535 ) {
1202       wine_tsx11_lock();
1203       XQueryPointer( event->display, root_window, &u.w_aux, &u.w_aux,
1204                      &x, &y, &u.i, &u.i, &u.i);
1205       wine_tsx11_unlock();
1206
1207       drop_len += sizeof(DROPFILES) + 1;
1208       hDrop = GlobalAlloc( GMEM_SHARE, drop_len );
1209       lpDrop = (DROPFILES *) GlobalLock( hDrop );
1210
1211       if( lpDrop ) {
1212           WND *pDropWnd = WIN_GetPtr( hWnd );
1213           lpDrop->pFiles = sizeof(DROPFILES);
1214           lpDrop->pt.x = (INT)x;
1215           lpDrop->pt.y = (INT)y;
1216           lpDrop->fNC =
1217             ( x < (pDropWnd->rectClient.left - pDropWnd->rectWindow.left)  ||
1218               y < (pDropWnd->rectClient.top - pDropWnd->rectWindow.top)    ||
1219               x > (pDropWnd->rectClient.right - pDropWnd->rectWindow.left) ||
1220               y > (pDropWnd->rectClient.bottom - pDropWnd->rectWindow.top) );
1221           lpDrop->fWide = FALSE;
1222           p_drop = (char*)(lpDrop + 1);
1223           WIN_ReleasePtr(pDropWnd);
1224       }
1225
1226       /* create message content */
1227       if (p_drop) {
1228         p = p_data;
1229         next = strchr(p, '\n');
1230         while (p) {
1231           if (next) *next=0;
1232           if (strncmp(p,"file:",5) == 0 ) {
1233             INT len = GetShortPathNameA( p+5, p_drop, 65535 );
1234             if (len) {
1235               TRACE("drop file %s as %s\n", p+5, p_drop);
1236               p_drop += len+1;
1237             } else {
1238               WARN("can't convert file %s to dos name \n", p+5);
1239             }
1240           } else {
1241             WARN("unknown mime type %s\n", p);
1242           }
1243           if (next) {
1244             *next = '\n';
1245             p = next + 1;
1246             next = strchr(p, '\n');
1247           } else {
1248             p = NULL;
1249           }
1250           *p_drop = '\0';
1251         }
1252
1253         GlobalUnlock(hDrop);
1254         PostMessageA( hWnd, WM_DROPFILES, (WPARAM)hDrop, 0L );
1255       }
1256     }
1257     wine_tsx11_lock();
1258     if( p_data ) XFree(p_data);
1259     wine_tsx11_unlock();
1260   }
1261 }
1262
1263 /**********************************************************************
1264  *           EVENT_ClientMessage
1265  */
1266 static void EVENT_ClientMessage( HWND hWnd, XClientMessageEvent *event )
1267 {
1268   if (event->message_type != None && event->format == 32) {
1269     if (event->message_type == x11drv_atom(WM_PROTOCOLS))
1270         handle_wm_protocols_message( hWnd, event );
1271     else if (event->message_type == x11drv_atom(DndProtocol))
1272     {
1273         /* query window (drag&drop event contains only drag window) */
1274         Window root, child;
1275         int root_x, root_y, child_x, child_y;
1276         unsigned int u;
1277
1278         wine_tsx11_lock();
1279         XQueryPointer( event->display, root_window, &root, &child,
1280                        &root_x, &root_y, &child_x, &child_y, &u);
1281         if (XFindContext( event->display, child, winContext, (char **)&hWnd ) != 0) hWnd = 0;
1282         wine_tsx11_unlock();
1283         if (!hWnd) return;
1284         if (event->data.l[0] == DndFile || event->data.l[0] == DndFiles)
1285             EVENT_DropFromOffiX(hWnd, event);
1286         else if (event->data.l[0] == DndURL)
1287             EVENT_DropURLs(hWnd, event);
1288     }
1289     else if (!X11DRV_XDND_Event(hWnd, event))
1290     {
1291 #if 0
1292       /* enable this if you want to see the message */
1293       unsigned char* p_data = NULL;
1294       union {
1295         unsigned long   l;
1296         int             i;
1297         Atom            atom;
1298       } u; /* unused */
1299       wine_tsx11_lock();
1300       XGetWindowProperty( event->display, DefaultRootWindow(event->display),
1301                             dndSelection, 0, 65535, FALSE,
1302                             AnyPropertyType, &u.atom, &u.i,
1303                             &u.l, &u.l, &p_data);
1304       wine_tsx11_unlock();
1305       TRACE("message_type=%ld, data=%ld,%ld,%ld,%ld,%ld, msg=%s\n",
1306             event->message_type, event->data.l[0], event->data.l[1],
1307             event->data.l[2], event->data.l[3], event->data.l[4],
1308             p_data);
1309 #endif
1310       TRACE("unrecognized ClientMessage\n" );
1311     }
1312   }
1313 }
1314
1315
1316 /**********************************************************************
1317  *              X11DRV_EVENT_SetInputMethod
1318  */
1319 INPUT_TYPE X11DRV_EVENT_SetInputMethod(INPUT_TYPE type)
1320 {
1321   INPUT_TYPE prev = current_input_type;
1322
1323   /* Flag not used yet */
1324   in_transition = FALSE;
1325   current_input_type = type;
1326
1327   return prev;
1328 }
1329
1330 #ifdef HAVE_LIBXXF86DGA2
1331 /**********************************************************************
1332  *              X11DRV_EVENT_SetDGAStatus
1333  */
1334 void X11DRV_EVENT_SetDGAStatus(HWND hwnd, int event_base)
1335 {
1336   if (event_base < 0) {
1337     DGAUsed = FALSE;
1338     DGAhwnd = 0;
1339   } else {
1340     DGAUsed = TRUE;
1341     DGAhwnd = hwnd;
1342     DGAMotionEventType = event_base + MotionNotify;
1343     DGAButtonPressEventType = event_base + ButtonPress;
1344     DGAButtonReleaseEventType = event_base + ButtonRelease;
1345     DGAKeyPressEventType = event_base + KeyPress;
1346     DGAKeyReleaseEventType = event_base + KeyRelease;
1347   }
1348 }
1349 #endif