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