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