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