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