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