comctl32: Never release state image list.
[wine/multimedia.git] / dlls / winex11.drv / event.c
blob1b3478de5264486f8ad13381d78861fb92313983
1 /*
2 * X11 event driver
4 * Copyright 1993 Alexandre Julliard
5 * 1999 Noel Borthwick
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.
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.
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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22 #include "config.h"
24 #ifdef HAVE_POLL_H
25 #include <poll.h>
26 #endif
27 #ifdef HAVE_SYS_POLL_H
28 #include <sys/poll.h>
29 #endif
30 #include <X11/Xatom.h>
31 #include <X11/keysym.h>
32 #include <X11/Xlib.h>
33 #include <X11/Xresource.h>
34 #include <X11/Xutil.h>
35 #ifdef HAVE_X11_EXTENSIONS_XINPUT2_H
36 #include <X11/extensions/XInput2.h>
37 #endif
39 #include <assert.h>
40 #include <stdarg.h>
41 #include <string.h>
43 #define NONAMELESSUNION
44 #define NONAMELESSSTRUCT
45 #include "windef.h"
46 #include "winbase.h"
47 #include "winuser.h"
48 #include "wingdi.h"
50 #include "x11drv.h"
52 /* avoid conflict with field names in included win32 headers */
53 #undef Status
54 #include "shlobj.h" /* DROPFILES */
55 #include "shellapi.h"
57 #include "wine/server.h"
58 #include "wine/debug.h"
60 WINE_DEFAULT_DEBUG_CHANNEL(event);
62 extern BOOL ximInComposeMode;
64 #define DndNotDnd -1 /* OffiX drag&drop */
65 #define DndUnknown 0
66 #define DndRawData 1
67 #define DndFile 2
68 #define DndFiles 3
69 #define DndText 4
70 #define DndDir 5
71 #define DndLink 6
72 #define DndExe 7
74 #define DndEND 8
76 #define DndURL 128 /* KDE drag&drop */
78 #define XEMBED_EMBEDDED_NOTIFY 0
79 #define XEMBED_WINDOW_ACTIVATE 1
80 #define XEMBED_WINDOW_DEACTIVATE 2
81 #define XEMBED_REQUEST_FOCUS 3
82 #define XEMBED_FOCUS_IN 4
83 #define XEMBED_FOCUS_OUT 5
84 #define XEMBED_FOCUS_NEXT 6
85 #define XEMBED_FOCUS_PREV 7
86 #define XEMBED_MODALITY_ON 10
87 #define XEMBED_MODALITY_OFF 11
88 #define XEMBED_REGISTER_ACCELERATOR 12
89 #define XEMBED_UNREGISTER_ACCELERATOR 13
90 #define XEMBED_ACTIVATE_ACCELERATOR 14
92 Bool (*pXGetEventData)( Display *display, XEvent /*XGenericEventCookie*/ *event ) = NULL;
93 void (*pXFreeEventData)( Display *display, XEvent /*XGenericEventCookie*/ *event ) = NULL;
95 /* Event handlers */
96 static void X11DRV_FocusIn( HWND hwnd, XEvent *event );
97 static void X11DRV_FocusOut( HWND hwnd, XEvent *event );
98 static void X11DRV_Expose( HWND hwnd, XEvent *event );
99 static void X11DRV_MapNotify( HWND hwnd, XEvent *event );
100 static void X11DRV_UnmapNotify( HWND hwnd, XEvent *event );
101 static void X11DRV_ReparentNotify( HWND hwnd, XEvent *event );
102 static void X11DRV_ConfigureNotify( HWND hwnd, XEvent *event );
103 static void X11DRV_PropertyNotify( HWND hwnd, XEvent *event );
104 static void X11DRV_ClientMessage( HWND hwnd, XEvent *event );
105 static void X11DRV_GravityNotify( HWND hwnd, XEvent *event );
107 #define MAX_EVENT_HANDLERS 128
109 static x11drv_event_handler handlers[MAX_EVENT_HANDLERS] =
111 NULL, /* 0 reserved */
112 NULL, /* 1 reserved */
113 X11DRV_KeyEvent, /* 2 KeyPress */
114 X11DRV_KeyEvent, /* 3 KeyRelease */
115 X11DRV_ButtonPress, /* 4 ButtonPress */
116 X11DRV_ButtonRelease, /* 5 ButtonRelease */
117 X11DRV_MotionNotify, /* 6 MotionNotify */
118 X11DRV_EnterNotify, /* 7 EnterNotify */
119 NULL, /* 8 LeaveNotify */
120 X11DRV_FocusIn, /* 9 FocusIn */
121 X11DRV_FocusOut, /* 10 FocusOut */
122 X11DRV_KeymapNotify, /* 11 KeymapNotify */
123 X11DRV_Expose, /* 12 Expose */
124 NULL, /* 13 GraphicsExpose */
125 NULL, /* 14 NoExpose */
126 NULL, /* 15 VisibilityNotify */
127 NULL, /* 16 CreateNotify */
128 X11DRV_DestroyNotify, /* 17 DestroyNotify */
129 X11DRV_UnmapNotify, /* 18 UnmapNotify */
130 X11DRV_MapNotify, /* 19 MapNotify */
131 NULL, /* 20 MapRequest */
132 X11DRV_ReparentNotify, /* 21 ReparentNotify */
133 X11DRV_ConfigureNotify, /* 22 ConfigureNotify */
134 NULL, /* 23 ConfigureRequest */
135 X11DRV_GravityNotify, /* 24 GravityNotify */
136 NULL, /* 25 ResizeRequest */
137 NULL, /* 26 CirculateNotify */
138 NULL, /* 27 CirculateRequest */
139 X11DRV_PropertyNotify, /* 28 PropertyNotify */
140 X11DRV_SelectionClear, /* 29 SelectionClear */
141 X11DRV_SelectionRequest, /* 30 SelectionRequest */
142 NULL, /* 31 SelectionNotify */
143 NULL, /* 32 ColormapNotify */
144 X11DRV_ClientMessage, /* 33 ClientMessage */
145 X11DRV_MappingNotify, /* 34 MappingNotify */
146 X11DRV_GenericEvent /* 35 GenericEvent */
149 static const char * event_names[MAX_EVENT_HANDLERS] =
151 NULL, NULL, "KeyPress", "KeyRelease", "ButtonPress", "ButtonRelease",
152 "MotionNotify", "EnterNotify", "LeaveNotify", "FocusIn", "FocusOut",
153 "KeymapNotify", "Expose", "GraphicsExpose", "NoExpose", "VisibilityNotify",
154 "CreateNotify", "DestroyNotify", "UnmapNotify", "MapNotify", "MapRequest",
155 "ReparentNotify", "ConfigureNotify", "ConfigureRequest", "GravityNotify", "ResizeRequest",
156 "CirculateNotify", "CirculateRequest", "PropertyNotify", "SelectionClear", "SelectionRequest",
157 "SelectionNotify", "ColormapNotify", "ClientMessage", "MappingNotify", "GenericEvent"
160 int xinput2_opcode = 0;
162 /* return the name of an X event */
163 static const char *dbgstr_event( int type )
165 if (type < MAX_EVENT_HANDLERS && event_names[type]) return event_names[type];
166 return wine_dbg_sprintf( "Unknown event %d", type );
169 static inline void get_event_data( XEvent *event )
171 #if defined(GenericEvent) && defined(HAVE_XEVENT_XCOOKIE)
172 if (event->xany.type != GenericEvent) return;
173 if (!pXGetEventData || !pXGetEventData( event->xany.display, event )) event->xcookie.data = NULL;
174 #endif
177 static inline void free_event_data( XEvent *event )
179 #if defined(GenericEvent) && defined(HAVE_XEVENT_XCOOKIE)
180 if (event->xany.type != GenericEvent) return;
181 if (event->xcookie.data) pXFreeEventData( event->xany.display, event );
182 #endif
185 /***********************************************************************
186 * X11DRV_register_event_handler
188 * Register a handler for a given event type.
189 * If already registered, overwrite the previous handler.
191 void X11DRV_register_event_handler( int type, x11drv_event_handler handler, const char *name )
193 assert( type < MAX_EVENT_HANDLERS );
194 assert( !handlers[type] || handlers[type] == handler );
195 handlers[type] = handler;
196 event_names[type] = name;
197 TRACE("registered handler %p for event %d %s\n", handler, type, debugstr_a(name) );
201 /***********************************************************************
202 * filter_event
204 static Bool filter_event( Display *display, XEvent *event, char *arg )
206 ULONG_PTR mask = (ULONG_PTR)arg;
208 if ((mask & QS_ALLINPUT) == QS_ALLINPUT) return 1;
210 switch(event->type)
212 case KeyPress:
213 case KeyRelease:
214 case KeymapNotify:
215 case MappingNotify:
216 return (mask & (QS_KEY|QS_HOTKEY)) != 0;
217 case ButtonPress:
218 case ButtonRelease:
219 return (mask & QS_MOUSEBUTTON) != 0;
220 case MotionNotify:
221 case EnterNotify:
222 case LeaveNotify:
223 return (mask & QS_MOUSEMOVE) != 0;
224 case Expose:
225 return (mask & QS_PAINT) != 0;
226 case FocusIn:
227 case FocusOut:
228 case MapNotify:
229 case UnmapNotify:
230 case ConfigureNotify:
231 case PropertyNotify:
232 case ClientMessage:
233 return (mask & QS_POSTMESSAGE) != 0;
234 default:
235 return (mask & QS_SENDMESSAGE) != 0;
240 enum event_merge_action
242 MERGE_DISCARD, /* discard the old event */
243 MERGE_HANDLE, /* handle the old event */
244 MERGE_KEEP, /* keep the old event for future merging */
245 MERGE_IGNORE /* ignore the new event, keep the old one */
248 /***********************************************************************
249 * merge_raw_motion_events
251 #ifdef HAVE_X11_EXTENSIONS_XINPUT2_H
252 static enum event_merge_action merge_raw_motion_events( XIRawEvent *prev, XIRawEvent *next )
254 int i, j, k;
255 unsigned char mask;
257 if (!prev->valuators.mask_len) return MERGE_HANDLE;
258 if (!next->valuators.mask_len) return MERGE_HANDLE;
260 mask = prev->valuators.mask[0] | next->valuators.mask[0];
261 if (mask == next->valuators.mask[0]) /* keep next */
263 for (i = j = k = 0; i < 8; i++)
265 if (XIMaskIsSet( prev->valuators.mask, i ))
266 next->valuators.values[j] += prev->valuators.values[k++];
267 if (XIMaskIsSet( next->valuators.mask, i )) j++;
269 TRACE( "merging duplicate GenericEvent\n" );
270 return MERGE_DISCARD;
272 if (mask == prev->valuators.mask[0]) /* keep prev */
274 for (i = j = k = 0; i < 8; i++)
276 if (XIMaskIsSet( next->valuators.mask, i ))
277 prev->valuators.values[j] += next->valuators.values[k++];
278 if (XIMaskIsSet( prev->valuators.mask, i )) j++;
280 TRACE( "merging duplicate GenericEvent\n" );
281 return MERGE_IGNORE;
283 /* can't merge events with disjoint masks */
284 return MERGE_HANDLE;
286 #endif
288 /***********************************************************************
289 * merge_events
291 * Try to merge 2 consecutive events.
293 static enum event_merge_action merge_events( XEvent *prev, XEvent *next )
295 switch (prev->type)
297 case ConfigureNotify:
298 switch (next->type)
300 case ConfigureNotify:
301 if (prev->xany.window == next->xany.window)
303 TRACE( "discarding duplicate ConfigureNotify for window %lx\n", prev->xany.window );
304 return MERGE_DISCARD;
306 break;
307 case Expose:
308 case PropertyNotify:
309 return MERGE_KEEP;
311 break;
312 case MotionNotify:
313 switch (next->type)
315 case MotionNotify:
316 if (prev->xany.window == next->xany.window)
318 TRACE( "discarding duplicate MotionNotify for window %lx\n", prev->xany.window );
319 return MERGE_DISCARD;
321 break;
322 #ifdef HAVE_X11_EXTENSIONS_XINPUT2_H
323 case GenericEvent:
324 if (next->xcookie.extension != xinput2_opcode) break;
325 if (next->xcookie.evtype != XI_RawMotion) break;
326 if (x11drv_thread_data()->warp_serial) break;
327 return MERGE_KEEP;
329 break;
330 case GenericEvent:
331 if (prev->xcookie.extension != xinput2_opcode) break;
332 if (prev->xcookie.evtype != XI_RawMotion) break;
333 switch (next->type)
335 case GenericEvent:
336 if (next->xcookie.extension != xinput2_opcode) break;
337 if (next->xcookie.evtype != XI_RawMotion) break;
338 if (x11drv_thread_data()->warp_serial) break;
339 return merge_raw_motion_events( prev->xcookie.data, next->xcookie.data );
340 #endif
342 break;
344 return MERGE_HANDLE;
348 /***********************************************************************
349 * call_event_handler
351 static inline void call_event_handler( Display *display, XEvent *event )
353 HWND hwnd;
354 XEvent *prev;
355 struct x11drv_thread_data *thread_data;
357 if (!handlers[event->type])
359 TRACE( "%s for win %lx, ignoring\n", dbgstr_event( event->type ), event->xany.window );
360 return; /* no handler, ignore it */
363 if (XFindContext( display, event->xany.window, winContext, (char **)&hwnd ) != 0)
364 hwnd = 0; /* not for a registered window */
365 if (!hwnd && event->xany.window == root_window) hwnd = GetDesktopWindow();
367 TRACE( "%lu %s for hwnd/window %p/%lx\n",
368 event->xany.serial, dbgstr_event( event->type ), hwnd, event->xany.window );
369 wine_tsx11_unlock();
370 thread_data = x11drv_thread_data();
371 prev = thread_data->current_event;
372 thread_data->current_event = event;
373 handlers[event->type]( hwnd, event );
374 thread_data->current_event = prev;
375 wine_tsx11_lock();
379 /***********************************************************************
380 * process_events
382 static int process_events( Display *display, Bool (*filter)(Display*, XEvent*,XPointer), ULONG_PTR arg )
384 XEvent event, prev_event;
385 int count = 0;
386 enum event_merge_action action = MERGE_DISCARD;
388 prev_event.type = 0;
389 wine_tsx11_lock();
390 while (XCheckIfEvent( display, &event, filter, (char *)arg ))
392 count++;
393 if (XFilterEvent( &event, None ))
396 * SCIM on linux filters key events strangely. It does not filter the
397 * KeyPress events for these keys however it does filter the
398 * KeyRelease events. This causes wine to become very confused as
399 * to the keyboard state.
401 * We need to let those KeyRelease events be processed so that the
402 * keyboard state is correct.
404 if (event.type == KeyRelease)
406 KeySym keysym = 0;
407 XKeyEvent *keyevent = &event.xkey;
409 XLookupString(keyevent, NULL, 0, &keysym, NULL);
410 if (!(keysym == XK_Shift_L ||
411 keysym == XK_Shift_R ||
412 keysym == XK_Control_L ||
413 keysym == XK_Control_R ||
414 keysym == XK_Alt_R ||
415 keysym == XK_Alt_L ||
416 keysym == XK_Meta_R ||
417 keysym == XK_Meta_L))
418 continue; /* not a key we care about, ignore it */
420 else
421 continue; /* filtered, ignore it */
423 get_event_data( &event );
424 if (prev_event.type) action = merge_events( &prev_event, &event );
425 switch( action )
427 case MERGE_HANDLE: /* handle prev, keep new */
428 call_event_handler( display, &prev_event );
429 /* fall through */
430 case MERGE_DISCARD: /* discard prev, keep new */
431 free_event_data( &prev_event );
432 prev_event = event;
433 break;
434 case MERGE_KEEP: /* handle new, keep prev for future merging */
435 call_event_handler( display, &event );
436 /* fall through */
437 case MERGE_IGNORE: /* ignore new, keep prev for future merging */
438 free_event_data( &event );
439 break;
442 if (prev_event.type) call_event_handler( display, &prev_event );
443 free_event_data( &prev_event );
444 XFlush( gdi_display );
445 wine_tsx11_unlock();
446 if (count) TRACE( "processed %d events\n", count );
447 return count;
451 /***********************************************************************
452 * MsgWaitForMultipleObjectsEx (X11DRV.@)
454 DWORD CDECL X11DRV_MsgWaitForMultipleObjectsEx( DWORD count, const HANDLE *handles,
455 DWORD timeout, DWORD mask, DWORD flags )
457 DWORD ret;
458 struct x11drv_thread_data *data = TlsGetValue( thread_data_tls_index );
460 if (!data)
462 if (!count && !timeout) return WAIT_TIMEOUT;
463 return WaitForMultipleObjectsEx( count, handles, flags & MWMO_WAITALL,
464 timeout, flags & MWMO_ALERTABLE );
467 if (data->current_event) mask = 0; /* don't process nested events */
469 if (process_events( data->display, filter_event, mask )) ret = count - 1;
470 else if (count || timeout)
472 ret = WaitForMultipleObjectsEx( count, handles, flags & MWMO_WAITALL,
473 timeout, flags & MWMO_ALERTABLE );
474 if (ret == count - 1) process_events( data->display, filter_event, mask );
476 else ret = WAIT_TIMEOUT;
478 return ret;
481 /***********************************************************************
482 * EVENT_x11_time_to_win32_time
484 * Make our timer and the X timer line up as best we can
485 * Pass 0 to retrieve the current adjustment value (times -1)
487 DWORD EVENT_x11_time_to_win32_time(Time time)
489 static DWORD adjust = 0;
490 DWORD now = GetTickCount();
491 DWORD ret;
493 if (! adjust && time != 0)
495 ret = now;
496 adjust = time - now;
498 else
500 /* If we got an event in the 'future', then our clock is clearly wrong.
501 If we got it more than 10000 ms in the future, then it's most likely
502 that the clock has wrapped. */
504 ret = time - adjust;
505 if (ret > now && ((ret - now) < 10000) && time != 0)
507 adjust += ret - now;
508 ret -= ret - now;
512 return ret;
516 /*******************************************************************
517 * can_activate_window
519 * Check if we can activate the specified window.
521 static inline BOOL can_activate_window( HWND hwnd )
523 LONG style = GetWindowLongW( hwnd, GWL_STYLE );
524 if (!(style & WS_VISIBLE)) return FALSE;
525 if ((style & (WS_POPUP|WS_CHILD)) == WS_CHILD) return FALSE;
526 if (style & WS_MINIMIZE) return FALSE;
527 if (GetWindowLongW( hwnd, GWL_EXSTYLE ) & WS_EX_NOACTIVATE) return FALSE;
528 if (hwnd == GetDesktopWindow()) return FALSE;
529 return !(style & WS_DISABLED);
533 /**********************************************************************
534 * set_focus
536 static void set_focus( Display *display, HWND hwnd, Time time )
538 HWND focus;
539 Window win;
540 GUITHREADINFO threadinfo;
542 TRACE( "setting foreground window to %p\n", hwnd );
543 SetForegroundWindow( hwnd );
545 threadinfo.cbSize = sizeof(threadinfo);
546 GetGUIThreadInfo(0, &threadinfo);
547 focus = threadinfo.hwndFocus;
548 if (!focus) focus = threadinfo.hwndActive;
549 if (focus) focus = GetAncestor( focus, GA_ROOT );
550 win = X11DRV_get_whole_window(focus);
552 if (win)
554 TRACE( "setting focus to %p (%lx) time=%ld\n", focus, win, time );
555 wine_tsx11_lock();
556 XSetInputFocus( display, win, RevertToParent, time );
557 wine_tsx11_unlock();
562 /**********************************************************************
563 * handle_manager_message
565 static void handle_manager_message( HWND hwnd, XClientMessageEvent *event )
567 if (hwnd != GetDesktopWindow()) return;
568 if (systray_atom && event->data.l[1] == systray_atom)
569 change_systray_owner( event->display, event->data.l[2] );
573 /**********************************************************************
574 * handle_wm_protocols
576 static void handle_wm_protocols( HWND hwnd, XClientMessageEvent *event )
578 Atom protocol = (Atom)event->data.l[0];
579 Time event_time = (Time)event->data.l[1];
581 if (!protocol) return;
583 if (protocol == x11drv_atom(WM_DELETE_WINDOW))
585 update_user_time( event_time );
587 if (hwnd == GetDesktopWindow())
589 /* The desktop window does not have a close button that we can
590 * pretend to click. Therefore, we simply send it a close command. */
591 SendMessageW(hwnd, WM_SYSCOMMAND, SC_CLOSE, 0);
592 return;
595 /* Ignore the delete window request if the window has been disabled
596 * and we are in managed mode. This is to disallow applications from
597 * being closed by the window manager while in a modal state.
599 if (IsWindowEnabled(hwnd))
601 HMENU hSysMenu;
603 if (GetClassLongW(hwnd, GCL_STYLE) & CS_NOCLOSE) return;
604 hSysMenu = GetSystemMenu(hwnd, FALSE);
605 if (hSysMenu)
607 UINT state = GetMenuState(hSysMenu, SC_CLOSE, MF_BYCOMMAND);
608 if (state == 0xFFFFFFFF || (state & (MF_DISABLED | MF_GRAYED)))
609 return;
611 if (GetActiveWindow() != hwnd)
613 LRESULT ma = SendMessageW( hwnd, WM_MOUSEACTIVATE,
614 (WPARAM)GetAncestor( hwnd, GA_ROOT ),
615 MAKELPARAM( HTCLOSE, WM_NCLBUTTONDOWN ) );
616 switch(ma)
618 case MA_NOACTIVATEANDEAT:
619 case MA_ACTIVATEANDEAT:
620 return;
621 case MA_NOACTIVATE:
622 break;
623 case MA_ACTIVATE:
624 case 0:
625 SetActiveWindow(hwnd);
626 break;
627 default:
628 WARN( "unknown WM_MOUSEACTIVATE code %d\n", (int) ma );
629 break;
633 PostMessageW( hwnd, WM_SYSCOMMAND, SC_CLOSE, 0 );
636 else if (protocol == x11drv_atom(WM_TAKE_FOCUS))
638 HWND last_focus = x11drv_thread_data()->last_focus;
640 TRACE( "got take focus msg for %p, enabled=%d, visible=%d (style %08x), focus=%p, active=%p, fg=%p, last=%p\n",
641 hwnd, IsWindowEnabled(hwnd), IsWindowVisible(hwnd), GetWindowLongW(hwnd, GWL_STYLE),
642 GetFocus(), GetActiveWindow(), GetForegroundWindow(), last_focus );
644 if (can_activate_window(hwnd))
646 /* simulate a mouse click on the caption to find out
647 * whether the window wants to be activated */
648 LRESULT ma = SendMessageW( hwnd, WM_MOUSEACTIVATE,
649 (WPARAM)GetAncestor( hwnd, GA_ROOT ),
650 MAKELONG(HTCAPTION,WM_LBUTTONDOWN) );
651 if (ma != MA_NOACTIVATEANDEAT && ma != MA_NOACTIVATE)
653 set_focus( event->display, hwnd, event_time );
654 return;
657 else if (hwnd == GetDesktopWindow())
659 hwnd = GetForegroundWindow();
660 if (!hwnd) hwnd = last_focus;
661 if (!hwnd) hwnd = GetDesktopWindow();
662 set_focus( event->display, hwnd, event_time );
663 return;
665 /* try to find some other window to give the focus to */
666 hwnd = GetFocus();
667 if (hwnd) hwnd = GetAncestor( hwnd, GA_ROOT );
668 if (!hwnd) hwnd = GetActiveWindow();
669 if (!hwnd) hwnd = last_focus;
670 if (hwnd && can_activate_window(hwnd)) set_focus( event->display, hwnd, event_time );
672 else if (protocol == x11drv_atom(_NET_WM_PING))
674 XClientMessageEvent xev;
675 xev = *event;
677 TRACE("NET_WM Ping\n");
678 wine_tsx11_lock();
679 xev.window = DefaultRootWindow(xev.display);
680 XSendEvent(xev.display, xev.window, False, SubstructureRedirectMask | SubstructureNotifyMask, (XEvent*)&xev);
681 wine_tsx11_unlock();
682 /* this line is semi-stolen from gtk2 */
683 TRACE("NET_WM Pong\n");
688 static const char * const focus_details[] =
690 "NotifyAncestor",
691 "NotifyVirtual",
692 "NotifyInferior",
693 "NotifyNonlinear",
694 "NotifyNonlinearVirtual",
695 "NotifyPointer",
696 "NotifyPointerRoot",
697 "NotifyDetailNone"
700 /**********************************************************************
701 * X11DRV_FocusIn
703 static void X11DRV_FocusIn( HWND hwnd, XEvent *xev )
705 XFocusChangeEvent *event = &xev->xfocus;
706 XIC xic;
708 if (!hwnd) return;
710 TRACE( "win %p xwin %lx detail=%s\n", hwnd, event->window, focus_details[event->detail] );
712 if (event->detail == NotifyPointer) return;
713 if (hwnd == GetDesktopWindow()) return;
715 if ((xic = X11DRV_get_ic( hwnd )))
717 wine_tsx11_lock();
718 XSetICFocus( xic );
719 wine_tsx11_unlock();
721 if (use_take_focus)
723 if (hwnd == GetForegroundWindow()) clip_fullscreen_window( hwnd, FALSE );
724 return;
727 if (!can_activate_window(hwnd))
729 HWND hwnd = GetFocus();
730 if (hwnd) hwnd = GetAncestor( hwnd, GA_ROOT );
731 if (!hwnd) hwnd = GetActiveWindow();
732 if (!hwnd) hwnd = x11drv_thread_data()->last_focus;
733 if (hwnd && can_activate_window(hwnd)) set_focus( event->display, hwnd, CurrentTime );
735 else SetForegroundWindow( hwnd );
739 /**********************************************************************
740 * X11DRV_FocusOut
742 * Note: only top-level windows get FocusOut events.
744 static void X11DRV_FocusOut( HWND hwnd, XEvent *xev )
746 XFocusChangeEvent *event = &xev->xfocus;
747 HWND hwnd_tmp;
748 Window focus_win;
749 int revert;
750 XIC xic;
752 TRACE( "win %p xwin %lx detail=%s\n", hwnd, event->window, focus_details[event->detail] );
754 if (event->detail == NotifyPointer)
756 if (!hwnd && event->window == x11drv_thread_data()->clip_window) reset_clipping_window();
757 return;
759 if (ximInComposeMode) return;
761 x11drv_thread_data()->last_focus = hwnd;
762 if ((xic = X11DRV_get_ic( hwnd )))
764 wine_tsx11_lock();
765 XUnsetICFocus( xic );
766 wine_tsx11_unlock();
768 if (root_window != DefaultRootWindow(event->display))
770 if (hwnd == GetDesktopWindow()) reset_clipping_window();
771 return;
773 if (hwnd != GetForegroundWindow()) return;
774 SendMessageW( hwnd, WM_CANCELMODE, 0, 0 );
776 /* don't reset the foreground window, if the window which is
777 getting the focus is a Wine window */
779 wine_tsx11_lock();
780 XGetInputFocus( event->display, &focus_win, &revert );
781 if (focus_win)
783 if (XFindContext( event->display, focus_win, winContext, (char **)&hwnd_tmp ) != 0)
784 focus_win = 0;
786 wine_tsx11_unlock();
788 if (!focus_win)
790 /* Abey : 6-Oct-99. Check again if the focus out window is the
791 Foreground window, because in most cases the messages sent
792 above must have already changed the foreground window, in which
793 case we don't have to change the foreground window to 0 */
794 if (hwnd == GetForegroundWindow())
796 TRACE( "lost focus, setting fg to desktop\n" );
797 SetForegroundWindow( GetDesktopWindow() );
803 /***********************************************************************
804 * X11DRV_Expose
806 static void X11DRV_Expose( HWND hwnd, XEvent *xev )
808 XExposeEvent *event = &xev->xexpose;
809 RECT rect;
810 struct x11drv_win_data *data;
811 int flags = RDW_INVALIDATE | RDW_ERASE;
813 TRACE( "win %p (%lx) %d,%d %dx%d\n",
814 hwnd, event->window, event->x, event->y, event->width, event->height );
816 if (!(data = X11DRV_get_win_data( hwnd ))) return;
818 rect.left = event->x;
819 rect.top = event->y;
820 rect.right = event->x + event->width;
821 rect.bottom = event->y + event->height;
822 if (event->window == data->whole_window)
824 OffsetRect( &rect, data->whole_rect.left - data->client_rect.left,
825 data->whole_rect.top - data->client_rect.top );
826 flags |= RDW_FRAME;
829 if (event->window != root_window)
831 if (GetWindowLongW( data->hwnd, GWL_EXSTYLE ) & WS_EX_LAYOUTRTL)
832 mirror_rect( &data->client_rect, &rect );
834 SERVER_START_REQ( update_window_zorder )
836 req->window = wine_server_user_handle( hwnd );
837 req->rect.left = rect.left;
838 req->rect.top = rect.top;
839 req->rect.right = rect.right;
840 req->rect.bottom = rect.bottom;
841 wine_server_call( req );
843 SERVER_END_REQ;
845 flags |= RDW_ALLCHILDREN;
847 else OffsetRect( &rect, virtual_screen_rect.left, virtual_screen_rect.top );
849 RedrawWindow( hwnd, &rect, 0, flags );
853 /**********************************************************************
854 * X11DRV_MapNotify
856 static void X11DRV_MapNotify( HWND hwnd, XEvent *event )
858 struct x11drv_win_data *data;
860 if (event->xany.window == x11drv_thread_data()->clip_window)
862 clipping_cursor = 1;
863 return;
865 if (!(data = X11DRV_get_win_data( hwnd ))) return;
866 if (!data->mapped || data->embedded) return;
868 if (!data->managed)
870 HWND hwndFocus = GetFocus();
871 if (hwndFocus && IsChild( hwnd, hwndFocus )) X11DRV_SetFocus(hwndFocus); /* FIXME */
876 /**********************************************************************
877 * X11DRV_UnmapNotify
879 static void X11DRV_UnmapNotify( HWND hwnd, XEvent *event )
881 if (event->xany.window == x11drv_thread_data()->clip_window) clipping_cursor = 0;
885 /***********************************************************************
886 * is_net_wm_state_maximized
888 static BOOL is_net_wm_state_maximized( Display *display, struct x11drv_win_data *data )
890 Atom type, *state;
891 int format, ret = 0;
892 unsigned long i, count, remaining;
894 if (!data->whole_window) return FALSE;
896 wine_tsx11_lock();
897 if (!XGetWindowProperty( display, data->whole_window, x11drv_atom(_NET_WM_STATE), 0,
898 65536/sizeof(CARD32), False, XA_ATOM, &type, &format, &count,
899 &remaining, (unsigned char **)&state ))
901 if (type == XA_ATOM && format == 32)
903 for (i = 0; i < count; i++)
905 if (state[i] == x11drv_atom(_NET_WM_STATE_MAXIMIZED_VERT) ||
906 state[i] == x11drv_atom(_NET_WM_STATE_MAXIMIZED_HORZ))
907 ret++;
910 XFree( state );
912 wine_tsx11_unlock();
913 return (ret == 2);
917 /***********************************************************************
918 * X11DRV_ReparentNotify
920 static void X11DRV_ReparentNotify( HWND hwnd, XEvent *xev )
922 XReparentEvent *event = &xev->xreparent;
923 struct x11drv_win_data *data;
924 HWND parent, old_parent;
925 DWORD style;
927 if (!(data = X11DRV_get_win_data( hwnd ))) return;
928 if (!data->embedded) return;
930 if (data->whole_window)
932 if (event->parent == root_window)
934 TRACE( "%p/%lx reparented to root\n", hwnd, data->whole_window );
935 data->embedder = 0;
936 SendMessageW( hwnd, WM_CLOSE, 0, 0 );
937 return;
939 data->embedder = event->parent;
942 TRACE( "%p/%lx reparented to %lx\n", hwnd, data->whole_window, event->parent );
944 style = GetWindowLongW( hwnd, GWL_STYLE );
945 if (event->parent == root_window)
947 parent = GetDesktopWindow();
948 style = (style & ~WS_CHILD) | WS_POPUP;
950 else
952 if (!(parent = create_foreign_window( event->display, event->parent ))) return;
953 style = (style & ~WS_POPUP) | WS_CHILD;
956 ShowWindow( hwnd, SW_HIDE );
957 old_parent = SetParent( hwnd, parent );
958 SetWindowLongW( hwnd, GWL_STYLE, style );
959 SetWindowPos( hwnd, HWND_TOP, event->x, event->y, 0, 0,
960 SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOCOPYBITS |
961 ((style & WS_VISIBLE) ? SWP_SHOWWINDOW : 0) );
963 /* make old parent destroy itself if it no longer has children */
964 if (old_parent != GetDesktopWindow()) PostMessageW( old_parent, WM_CLOSE, 0, 0 );
968 /***********************************************************************
969 * X11DRV_ConfigureNotify
971 void X11DRV_ConfigureNotify( HWND hwnd, XEvent *xev )
973 XConfigureEvent *event = &xev->xconfigure;
974 struct x11drv_win_data *data;
975 RECT rect;
976 UINT flags;
977 HWND parent;
978 BOOL root_coords;
979 int cx, cy, x = event->x, y = event->y;
981 if (!hwnd) return;
982 if (!(data = X11DRV_get_win_data( hwnd ))) return;
983 if (!data->mapped || data->iconic) return;
984 if (data->whole_window && !data->managed) return;
985 /* ignore synthetic events on foreign windows */
986 if (event->send_event && !data->whole_window) return;
987 if (data->configure_serial && (long)(data->configure_serial - event->serial) > 0)
989 TRACE( "win %p/%lx event %d,%d,%dx%d ignoring old serial %lu/%lu\n",
990 hwnd, data->whole_window, event->x, event->y, event->width, event->height,
991 event->serial, data->configure_serial );
992 return;
995 /* Get geometry */
997 parent = GetAncestor( hwnd, GA_PARENT );
998 root_coords = event->send_event; /* synthetic events are always in root coords */
1000 if (!root_coords && parent == GetDesktopWindow()) /* normal event, map coordinates to the root */
1002 Window child;
1003 wine_tsx11_lock();
1004 XTranslateCoordinates( event->display, event->window, root_window,
1005 0, 0, &x, &y, &child );
1006 wine_tsx11_unlock();
1007 root_coords = TRUE;
1009 rect.left = x;
1010 rect.top = y;
1011 rect.right = x + event->width;
1012 rect.bottom = y + event->height;
1013 if (root_coords) OffsetRect( &rect, virtual_screen_rect.left, virtual_screen_rect.top );
1014 TRACE( "win %p/%lx new X rect %d,%d,%dx%d (event %d,%d,%dx%d)\n",
1015 hwnd, data->whole_window, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top,
1016 event->x, event->y, event->width, event->height );
1018 X11DRV_X_to_window_rect( data, &rect );
1019 if (root_coords) MapWindowPoints( 0, parent, (POINT *)&rect, 2 );
1021 /* Compare what has changed */
1023 x = rect.left;
1024 y = rect.top;
1025 cx = rect.right - rect.left;
1026 cy = rect.bottom - rect.top;
1027 flags = SWP_NOACTIVATE | SWP_NOZORDER;
1029 if (!data->whole_window) flags |= SWP_NOCOPYBITS; /* we can't copy bits of foreign windows */
1031 if (data->window_rect.left == x && data->window_rect.top == y) flags |= SWP_NOMOVE;
1032 else
1033 TRACE( "%p moving from (%d,%d) to (%d,%d)\n",
1034 hwnd, data->window_rect.left, data->window_rect.top, x, y );
1036 if ((data->window_rect.right - data->window_rect.left == cx &&
1037 data->window_rect.bottom - data->window_rect.top == cy) ||
1038 (IsRectEmpty( &data->window_rect ) && event->width == 1 && event->height == 1))
1040 if (flags & SWP_NOMOVE) /* if nothing changed, don't do anything */
1042 TRACE( "Nothing has changed, ignoring event\n" );
1043 return;
1045 flags |= SWP_NOSIZE;
1047 else
1048 TRACE( "%p resizing from (%dx%d) to (%dx%d)\n",
1049 hwnd, data->window_rect.right - data->window_rect.left,
1050 data->window_rect.bottom - data->window_rect.top, cx, cy );
1052 if (is_net_wm_state_maximized( event->display, data ))
1054 if (!IsZoomed( data->hwnd ))
1056 TRACE( "win %p/%lx is maximized\n", data->hwnd, data->whole_window );
1057 SendMessageW( data->hwnd, WM_SYSCOMMAND, SC_MAXIMIZE, 0 );
1058 return;
1061 else
1063 if (IsZoomed( data->hwnd ))
1065 TRACE( "window %p/%lx is no longer maximized\n", data->hwnd, data->whole_window );
1066 SendMessageW( data->hwnd, WM_SYSCOMMAND, SC_RESTORE, 0 );
1067 return;
1071 SetWindowPos( hwnd, 0, x, y, cx, cy, flags );
1075 /**********************************************************************
1076 * X11DRV_GravityNotify
1078 static void X11DRV_GravityNotify( HWND hwnd, XEvent *xev )
1080 XGravityEvent *event = &xev->xgravity;
1081 struct x11drv_win_data *data = X11DRV_get_win_data( hwnd );
1082 RECT rect;
1084 if (!data || data->whole_window) return; /* only handle this for foreign windows */
1086 rect.left = event->x;
1087 rect.top = event->y;
1088 rect.right = rect.left + data->whole_rect.right - data->whole_rect.left;
1089 rect.bottom = rect.top + data->whole_rect.bottom - data->whole_rect.top;
1091 TRACE( "win %p/%lx new X rect %d,%d,%dx%d (event %d,%d)\n",
1092 hwnd, data->whole_window, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top,
1093 event->x, event->y );
1095 X11DRV_X_to_window_rect( data, &rect );
1097 if (data->window_rect.left != rect.left || data ->window_rect.top != rect.top)
1098 SetWindowPos( hwnd, 0, rect.left, rect.top, 0, 0,
1099 SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOCOPYBITS );
1103 /***********************************************************************
1104 * get_window_wm_state
1106 static int get_window_wm_state( Display *display, struct x11drv_win_data *data )
1108 struct
1110 CARD32 state;
1111 XID icon;
1112 } *state;
1113 Atom type;
1114 int format, ret = -1;
1115 unsigned long count, remaining;
1117 wine_tsx11_lock();
1118 if (!XGetWindowProperty( display, data->whole_window, x11drv_atom(WM_STATE), 0,
1119 sizeof(*state)/sizeof(CARD32), False, x11drv_atom(WM_STATE),
1120 &type, &format, &count, &remaining, (unsigned char **)&state ))
1122 if (type == x11drv_atom(WM_STATE) && get_property_size( format, count ) >= sizeof(*state))
1123 ret = state->state;
1124 XFree( state );
1126 wine_tsx11_unlock();
1127 return ret;
1131 /***********************************************************************
1132 * handle_wm_state_notify
1134 * Handle a PropertyNotify for WM_STATE.
1136 static void handle_wm_state_notify( struct x11drv_win_data *data, XPropertyEvent *event,
1137 BOOL update_window )
1139 DWORD style;
1141 switch(event->state)
1143 case PropertyDelete:
1144 TRACE( "%p/%lx: WM_STATE deleted from %d\n", data->hwnd, data->whole_window, data->wm_state );
1145 data->wm_state = WithdrawnState;
1146 break;
1147 case PropertyNewValue:
1149 int old_state = data->wm_state;
1150 int new_state = get_window_wm_state( event->display, data );
1151 if (new_state != -1 && new_state != data->wm_state)
1153 TRACE( "%p/%lx: new WM_STATE %d from %d\n",
1154 data->hwnd, data->whole_window, new_state, old_state );
1155 data->wm_state = new_state;
1156 /* ignore the initial state transition out of withdrawn state */
1157 /* metacity does Withdrawn->NormalState->IconicState when mapping an iconic window */
1158 if (!old_state) return;
1161 break;
1164 if (!update_window || !data->managed || !data->mapped) return;
1166 style = GetWindowLongW( data->hwnd, GWL_STYLE );
1168 if (data->iconic && data->wm_state == NormalState) /* restore window */
1170 data->iconic = FALSE;
1171 if (is_net_wm_state_maximized( event->display, data ))
1173 if ((style & WS_MAXIMIZEBOX) && !(style & WS_DISABLED))
1175 TRACE( "restoring to max %p/%lx\n", data->hwnd, data->whole_window );
1176 SendMessageW( data->hwnd, WM_SYSCOMMAND, SC_MAXIMIZE, 0 );
1178 else TRACE( "not restoring to max win %p/%lx style %08x\n",
1179 data->hwnd, data->whole_window, style );
1181 else if (style & (WS_MINIMIZE | WS_MAXIMIZE))
1183 TRACE( "restoring win %p/%lx\n", data->hwnd, data->whole_window );
1184 SendMessageW( data->hwnd, WM_SYSCOMMAND, SC_RESTORE, 0 );
1186 else TRACE( "not restoring win %p/%lx style %08x\n", data->hwnd, data->whole_window, style );
1188 else if (!data->iconic && data->wm_state == IconicState)
1190 data->iconic = TRUE;
1191 if ((style & WS_MINIMIZEBOX) && !(style & WS_DISABLED))
1193 TRACE( "minimizing win %p/%lx\n", data->hwnd, data->whole_window );
1194 SendMessageW( data->hwnd, WM_SYSCOMMAND, SC_MINIMIZE, 0 );
1196 else TRACE( "not minimizing win %p/%lx style %08x\n", data->hwnd, data->whole_window, style );
1201 /***********************************************************************
1202 * X11DRV_PropertyNotify
1204 static void X11DRV_PropertyNotify( HWND hwnd, XEvent *xev )
1206 XPropertyEvent *event = &xev->xproperty;
1207 struct x11drv_win_data *data;
1209 if (!hwnd) return;
1210 if (!(data = X11DRV_get_win_data( hwnd ))) return;
1212 if (event->atom == x11drv_atom(WM_STATE)) handle_wm_state_notify( data, event, TRUE );
1216 /* event filter to wait for a WM_STATE change notification on a window */
1217 static Bool is_wm_state_notify( Display *display, XEvent *event, XPointer arg )
1219 if (event->xany.window != (Window)arg) return 0;
1220 return (event->type == DestroyNotify ||
1221 (event->type == PropertyNotify && event->xproperty.atom == x11drv_atom(WM_STATE)));
1224 /***********************************************************************
1225 * wait_for_withdrawn_state
1227 void wait_for_withdrawn_state( Display *display, struct x11drv_win_data *data, BOOL set )
1229 DWORD end = GetTickCount() + 2000;
1231 if (!data->managed) return;
1233 TRACE( "waiting for window %p/%lx to become %swithdrawn\n",
1234 data->hwnd, data->whole_window, set ? "" : "not " );
1236 while (data->whole_window && ((data->wm_state == WithdrawnState) == !set))
1238 XEvent event;
1239 int count = 0;
1241 wine_tsx11_lock();
1242 while (XCheckIfEvent( display, &event, is_wm_state_notify, (char *)data->whole_window ))
1244 count++;
1245 if (XFilterEvent( &event, None )) continue; /* filtered, ignore it */
1246 if (event.type == DestroyNotify) call_event_handler( display, &event );
1247 else
1249 wine_tsx11_unlock();
1250 handle_wm_state_notify( data, &event.xproperty, FALSE );
1251 wine_tsx11_lock();
1254 wine_tsx11_unlock();
1256 if (!count)
1258 struct pollfd pfd;
1259 int timeout = end - GetTickCount();
1261 pfd.fd = ConnectionNumber(display);
1262 pfd.events = POLLIN;
1263 if (timeout <= 0 || poll( &pfd, 1, timeout ) != 1)
1265 FIXME( "window %p/%lx wait timed out\n", data->hwnd, data->whole_window );
1266 break;
1270 TRACE( "window %p/%lx state now %d\n", data->hwnd, data->whole_window, data->wm_state );
1274 static HWND find_drop_window( HWND hQueryWnd, LPPOINT lpPt )
1276 RECT tempRect;
1278 if (!IsWindowEnabled(hQueryWnd)) return 0;
1280 GetWindowRect(hQueryWnd, &tempRect);
1282 if(!PtInRect(&tempRect, *lpPt)) return 0;
1284 if (!IsIconic( hQueryWnd ))
1286 POINT pt = *lpPt;
1287 ScreenToClient( hQueryWnd, &pt );
1288 GetClientRect( hQueryWnd, &tempRect );
1290 if (PtInRect( &tempRect, pt))
1292 HWND ret = ChildWindowFromPointEx( hQueryWnd, pt, CWP_SKIPINVISIBLE|CWP_SKIPDISABLED );
1293 if (ret && ret != hQueryWnd)
1295 ret = find_drop_window( ret, lpPt );
1296 if (ret) return ret;
1301 if(!(GetWindowLongA( hQueryWnd, GWL_EXSTYLE ) & WS_EX_ACCEPTFILES)) return 0;
1303 ScreenToClient(hQueryWnd, lpPt);
1305 return hQueryWnd;
1308 /**********************************************************************
1309 * EVENT_DropFromOffix
1311 * don't know if it still works (last Changelog is from 96/11/04)
1313 static void EVENT_DropFromOffiX( HWND hWnd, XClientMessageEvent *event )
1315 struct x11drv_win_data *data;
1316 unsigned long data_length;
1317 unsigned long aux_long;
1318 unsigned char* p_data = NULL;
1319 Atom atom_aux;
1320 int x, y, dummy;
1321 BOOL bAccept;
1322 Window win, w_aux_root, w_aux_child;
1324 win = X11DRV_get_whole_window(hWnd);
1325 wine_tsx11_lock();
1326 XQueryPointer( event->display, win, &w_aux_root, &w_aux_child,
1327 &x, &y, &dummy, &dummy, (unsigned int*)&aux_long);
1328 x += virtual_screen_rect.left;
1329 y += virtual_screen_rect.top;
1330 wine_tsx11_unlock();
1332 if (!(data = X11DRV_get_win_data( hWnd ))) return;
1334 /* find out drop point and drop window */
1335 if( x < 0 || y < 0 ||
1336 x > (data->whole_rect.right - data->whole_rect.left) ||
1337 y > (data->whole_rect.bottom - data->whole_rect.top) )
1339 bAccept = GetWindowLongW( hWnd, GWL_EXSTYLE ) & WS_EX_ACCEPTFILES;
1340 x = 0;
1341 y = 0;
1343 else
1345 POINT pt = { x, y };
1346 HWND hwndDrop = find_drop_window( hWnd, &pt );
1347 if (hwndDrop)
1349 x = pt.x;
1350 y = pt.y;
1351 bAccept = TRUE;
1353 else
1355 bAccept = FALSE;
1359 if (!bAccept) return;
1361 wine_tsx11_lock();
1362 XGetWindowProperty( event->display, DefaultRootWindow(event->display),
1363 x11drv_atom(DndSelection), 0, 65535, FALSE,
1364 AnyPropertyType, &atom_aux, &dummy,
1365 &data_length, &aux_long, &p_data);
1366 wine_tsx11_unlock();
1368 if( !aux_long && p_data) /* don't bother if > 64K */
1370 char *p = (char *)p_data;
1371 char *p_drop;
1373 aux_long = 0;
1374 while( *p ) /* calculate buffer size */
1376 INT len = GetShortPathNameA( p, NULL, 0 );
1377 if (len) aux_long += len + 1;
1378 p += strlen(p) + 1;
1380 if( aux_long && aux_long < 65535 )
1382 HDROP hDrop;
1383 DROPFILES *lpDrop;
1385 aux_long += sizeof(DROPFILES) + 1;
1386 hDrop = GlobalAlloc( GMEM_SHARE, aux_long );
1387 lpDrop = GlobalLock( hDrop );
1389 if( lpDrop )
1391 lpDrop->pFiles = sizeof(DROPFILES);
1392 lpDrop->pt.x = x;
1393 lpDrop->pt.y = y;
1394 lpDrop->fNC = FALSE;
1395 lpDrop->fWide = FALSE;
1396 p_drop = (char *)(lpDrop + 1);
1397 p = (char *)p_data;
1398 while(*p)
1400 if (GetShortPathNameA( p, p_drop, aux_long - (p_drop - (char *)lpDrop) ))
1401 p_drop += strlen( p_drop ) + 1;
1402 p += strlen(p) + 1;
1404 *p_drop = '\0';
1405 PostMessageA( hWnd, WM_DROPFILES, (WPARAM)hDrop, 0L );
1409 wine_tsx11_lock();
1410 if( p_data ) XFree(p_data);
1411 wine_tsx11_unlock();
1414 /**********************************************************************
1415 * EVENT_DropURLs
1417 * drop items are separated by \n
1418 * each item is prefixed by its mime type
1420 * event->data.l[3], event->data.l[4] contains drop x,y position
1422 static void EVENT_DropURLs( HWND hWnd, XClientMessageEvent *event )
1424 struct x11drv_win_data *win_data;
1425 unsigned long data_length;
1426 unsigned long aux_long, drop_len = 0;
1427 unsigned char *p_data = NULL; /* property data */
1428 char *p_drop = NULL;
1429 char *p, *next;
1430 int x, y;
1431 DROPFILES *lpDrop;
1432 HDROP hDrop;
1433 union {
1434 Atom atom_aux;
1435 int i;
1436 Window w_aux;
1437 unsigned int u;
1438 } u; /* unused */
1440 if (!(GetWindowLongW( hWnd, GWL_EXSTYLE ) & WS_EX_ACCEPTFILES)) return;
1442 wine_tsx11_lock();
1443 XGetWindowProperty( event->display, DefaultRootWindow(event->display),
1444 x11drv_atom(DndSelection), 0, 65535, FALSE,
1445 AnyPropertyType, &u.atom_aux, &u.i,
1446 &data_length, &aux_long, &p_data);
1447 wine_tsx11_unlock();
1448 if (aux_long)
1449 WARN("property too large, truncated!\n");
1450 TRACE("urls=%s\n", p_data);
1452 if( !aux_long && p_data) { /* don't bother if > 64K */
1453 /* calculate length */
1454 p = (char*) p_data;
1455 next = strchr(p, '\n');
1456 while (p) {
1457 if (next) *next=0;
1458 if (strncmp(p,"file:",5) == 0 ) {
1459 INT len = GetShortPathNameA( p+5, NULL, 0 );
1460 if (len) drop_len += len + 1;
1462 if (next) {
1463 *next = '\n';
1464 p = next + 1;
1465 next = strchr(p, '\n');
1466 } else {
1467 p = NULL;
1471 if( drop_len && drop_len < 65535 ) {
1472 wine_tsx11_lock();
1473 XQueryPointer( event->display, root_window, &u.w_aux, &u.w_aux,
1474 &x, &y, &u.i, &u.i, &u.u);
1475 x += virtual_screen_rect.left;
1476 y += virtual_screen_rect.top;
1477 wine_tsx11_unlock();
1479 drop_len += sizeof(DROPFILES) + 1;
1480 hDrop = GlobalAlloc( GMEM_SHARE, drop_len );
1481 lpDrop = GlobalLock( hDrop );
1483 if( lpDrop && (win_data = X11DRV_get_win_data( hWnd )))
1485 lpDrop->pFiles = sizeof(DROPFILES);
1486 lpDrop->pt.x = x;
1487 lpDrop->pt.y = y;
1488 lpDrop->fNC =
1489 ( x < (win_data->client_rect.left - win_data->whole_rect.left) ||
1490 y < (win_data->client_rect.top - win_data->whole_rect.top) ||
1491 x > (win_data->client_rect.right - win_data->whole_rect.left) ||
1492 y > (win_data->client_rect.bottom - win_data->whole_rect.top) );
1493 lpDrop->fWide = FALSE;
1494 p_drop = (char*)(lpDrop + 1);
1497 /* create message content */
1498 if (p_drop) {
1499 p = (char*) p_data;
1500 next = strchr(p, '\n');
1501 while (p) {
1502 if (next) *next=0;
1503 if (strncmp(p,"file:",5) == 0 ) {
1504 INT len = GetShortPathNameA( p+5, p_drop, 65535 );
1505 if (len) {
1506 TRACE("drop file %s as %s\n", p+5, p_drop);
1507 p_drop += len+1;
1508 } else {
1509 WARN("can't convert file %s to dos name\n", p+5);
1511 } else {
1512 WARN("unknown mime type %s\n", p);
1514 if (next) {
1515 *next = '\n';
1516 p = next + 1;
1517 next = strchr(p, '\n');
1518 } else {
1519 p = NULL;
1521 *p_drop = '\0';
1524 GlobalUnlock(hDrop);
1525 PostMessageA( hWnd, WM_DROPFILES, (WPARAM)hDrop, 0L );
1528 wine_tsx11_lock();
1529 if( p_data ) XFree(p_data);
1530 wine_tsx11_unlock();
1535 /**********************************************************************
1536 * handle_xembed_protocol
1538 static void handle_xembed_protocol( HWND hwnd, XClientMessageEvent *event )
1540 struct x11drv_win_data *data = X11DRV_get_win_data( hwnd );
1542 if (!data) return;
1544 switch (event->data.l[1])
1546 case XEMBED_EMBEDDED_NOTIFY:
1547 TRACE( "win %p/%lx XEMBED_EMBEDDED_NOTIFY owner %lx\n", hwnd, event->window, event->data.l[3] );
1548 data->embedder = event->data.l[3];
1549 break;
1550 default:
1551 TRACE( "win %p/%lx XEMBED message %lu(%lu)\n",
1552 hwnd, event->window, event->data.l[1], event->data.l[2] );
1553 break;
1558 /**********************************************************************
1559 * handle_dnd_protocol
1561 static void handle_dnd_protocol( HWND hwnd, XClientMessageEvent *event )
1563 Window root, child;
1564 int root_x, root_y, child_x, child_y;
1565 unsigned int u;
1567 /* query window (drag&drop event contains only drag window) */
1568 wine_tsx11_lock();
1569 XQueryPointer( event->display, root_window, &root, &child,
1570 &root_x, &root_y, &child_x, &child_y, &u);
1571 if (XFindContext( event->display, child, winContext, (char **)&hwnd ) != 0) hwnd = 0;
1572 wine_tsx11_unlock();
1573 if (!hwnd) return;
1574 if (event->data.l[0] == DndFile || event->data.l[0] == DndFiles)
1575 EVENT_DropFromOffiX(hwnd, event);
1576 else if (event->data.l[0] == DndURL)
1577 EVENT_DropURLs(hwnd, event);
1581 struct client_message_handler
1583 int atom; /* protocol atom */
1584 void (*handler)(HWND, XClientMessageEvent *); /* corresponding handler function */
1587 static const struct client_message_handler client_messages[] =
1589 { XATOM_MANAGER, handle_manager_message },
1590 { XATOM_WM_PROTOCOLS, handle_wm_protocols },
1591 { XATOM__XEMBED, handle_xembed_protocol },
1592 { XATOM_DndProtocol, handle_dnd_protocol },
1593 { XATOM_XdndEnter, X11DRV_XDND_EnterEvent },
1594 { XATOM_XdndPosition, X11DRV_XDND_PositionEvent },
1595 { XATOM_XdndDrop, X11DRV_XDND_DropEvent },
1596 { XATOM_XdndLeave, X11DRV_XDND_LeaveEvent }
1600 /**********************************************************************
1601 * X11DRV_ClientMessage
1603 static void X11DRV_ClientMessage( HWND hwnd, XEvent *xev )
1605 XClientMessageEvent *event = &xev->xclient;
1606 unsigned int i;
1608 if (!hwnd) return;
1610 if (event->format != 32)
1612 WARN( "Don't know how to handle format %d\n", event->format );
1613 return;
1616 for (i = 0; i < sizeof(client_messages)/sizeof(client_messages[0]); i++)
1618 if (event->message_type == X11DRV_Atoms[client_messages[i].atom - FIRST_XATOM])
1620 client_messages[i].handler( hwnd, event );
1621 return;
1624 TRACE( "no handler found for %ld\n", event->message_type );