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