riched20: Move underline drawing to a common function.
[wine.git] / dlls / winex11.drv / event.c
blob5e5b19ec925baee10b57a4446521e01c34f0e03a
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 thread_data = x11drv_thread_data();
370 prev = thread_data->current_event;
371 thread_data->current_event = event;
372 handlers[event->type]( hwnd, event );
373 thread_data->current_event = prev;
377 /***********************************************************************
378 * process_events
380 static int process_events( Display *display, Bool (*filter)(Display*, XEvent*,XPointer), ULONG_PTR arg )
382 XEvent event, prev_event;
383 int count = 0;
384 enum event_merge_action action = MERGE_DISCARD;
386 prev_event.type = 0;
387 while (XCheckIfEvent( display, &event, filter, (char *)arg ))
389 count++;
390 if (XFilterEvent( &event, None ))
393 * SCIM on linux filters key events strangely. It does not filter the
394 * KeyPress events for these keys however it does filter the
395 * KeyRelease events. This causes wine to become very confused as
396 * to the keyboard state.
398 * We need to let those KeyRelease events be processed so that the
399 * keyboard state is correct.
401 if (event.type == KeyRelease)
403 KeySym keysym = 0;
404 XKeyEvent *keyevent = &event.xkey;
406 XLookupString(keyevent, NULL, 0, &keysym, NULL);
407 if (!(keysym == XK_Shift_L ||
408 keysym == XK_Shift_R ||
409 keysym == XK_Control_L ||
410 keysym == XK_Control_R ||
411 keysym == XK_Alt_R ||
412 keysym == XK_Alt_L ||
413 keysym == XK_Meta_R ||
414 keysym == XK_Meta_L))
415 continue; /* not a key we care about, ignore it */
417 else
418 continue; /* filtered, ignore it */
420 get_event_data( &event );
421 if (prev_event.type) action = merge_events( &prev_event, &event );
422 switch( action )
424 case MERGE_HANDLE: /* handle prev, keep new */
425 call_event_handler( display, &prev_event );
426 /* fall through */
427 case MERGE_DISCARD: /* discard prev, keep new */
428 free_event_data( &prev_event );
429 prev_event = event;
430 break;
431 case MERGE_KEEP: /* handle new, keep prev for future merging */
432 call_event_handler( display, &event );
433 /* fall through */
434 case MERGE_IGNORE: /* ignore new, keep prev for future merging */
435 free_event_data( &event );
436 break;
439 if (prev_event.type) call_event_handler( display, &prev_event );
440 free_event_data( &prev_event );
441 XFlush( gdi_display );
442 if (count) TRACE( "processed %d events\n", count );
443 return count;
447 /***********************************************************************
448 * MsgWaitForMultipleObjectsEx (X11DRV.@)
450 DWORD CDECL X11DRV_MsgWaitForMultipleObjectsEx( DWORD count, const HANDLE *handles,
451 DWORD timeout, DWORD mask, DWORD flags )
453 DWORD ret;
454 struct x11drv_thread_data *data = TlsGetValue( thread_data_tls_index );
456 if (!data)
458 if (!count && !timeout) return WAIT_TIMEOUT;
459 return WaitForMultipleObjectsEx( count, handles, flags & MWMO_WAITALL,
460 timeout, flags & MWMO_ALERTABLE );
463 if (data->current_event) mask = 0; /* don't process nested events */
465 if (process_events( data->display, filter_event, mask )) ret = count - 1;
466 else if (count || timeout)
468 ret = WaitForMultipleObjectsEx( count, handles, flags & MWMO_WAITALL,
469 timeout, flags & MWMO_ALERTABLE );
470 if (ret == count - 1) process_events( data->display, filter_event, mask );
472 else ret = WAIT_TIMEOUT;
474 return ret;
477 /***********************************************************************
478 * EVENT_x11_time_to_win32_time
480 * Make our timer and the X timer line up as best we can
481 * Pass 0 to retrieve the current adjustment value (times -1)
483 DWORD EVENT_x11_time_to_win32_time(Time time)
485 static DWORD adjust = 0;
486 DWORD now = GetTickCount();
487 DWORD ret;
489 if (! adjust && time != 0)
491 ret = now;
492 adjust = time - now;
494 else
496 /* If we got an event in the 'future', then our clock is clearly wrong.
497 If we got it more than 10000 ms in the future, then it's most likely
498 that the clock has wrapped. */
500 ret = time - adjust;
501 if (ret > now && ((ret - now) < 10000) && time != 0)
503 adjust += ret - now;
504 ret -= ret - now;
508 return ret;
512 /*******************************************************************
513 * can_activate_window
515 * Check if we can activate the specified window.
517 static inline BOOL can_activate_window( HWND hwnd )
519 LONG style = GetWindowLongW( hwnd, GWL_STYLE );
520 RECT rect;
522 if (!(style & WS_VISIBLE)) return FALSE;
523 if ((style & (WS_POPUP|WS_CHILD)) == WS_CHILD) return FALSE;
524 if (style & WS_MINIMIZE) return FALSE;
525 if (GetWindowLongW( hwnd, GWL_EXSTYLE ) & WS_EX_NOACTIVATE) return FALSE;
526 if (hwnd == GetDesktopWindow()) return FALSE;
527 if (GetWindowRect( hwnd, &rect ) && IsRectEmpty( &rect )) return FALSE;
528 return !(style & WS_DISABLED);
532 /**********************************************************************
533 * set_input_focus
535 * Try to force focus for non-managed windows.
537 static void set_input_focus( Display *display, Window window )
539 XWindowChanges changes;
540 DWORD timestamp;
542 if (!window) return;
544 if (EVENT_x11_time_to_win32_time(0))
545 /* ICCCM says don't use CurrentTime, so try to use last message time if possible */
546 /* FIXME: this is not entirely correct */
547 timestamp = GetMessageTime() - EVENT_x11_time_to_win32_time(0);
548 else
549 timestamp = CurrentTime;
551 /* Set X focus and install colormap */
552 changes.stack_mode = Above;
553 XConfigureWindow( display, window, CWStackMode, &changes );
554 XSetInputFocus( display, window, RevertToParent, timestamp );
557 /**********************************************************************
558 * set_focus
560 static void set_focus( Display *display, HWND hwnd, Time time )
562 HWND focus;
563 Window win;
564 GUITHREADINFO threadinfo;
566 TRACE( "setting foreground window to %p\n", hwnd );
567 SetForegroundWindow( hwnd );
569 threadinfo.cbSize = sizeof(threadinfo);
570 GetGUIThreadInfo(0, &threadinfo);
571 focus = threadinfo.hwndFocus;
572 if (!focus) focus = threadinfo.hwndActive;
573 if (focus) focus = GetAncestor( focus, GA_ROOT );
574 win = X11DRV_get_whole_window(focus);
576 if (win)
578 TRACE( "setting focus to %p (%lx) time=%ld\n", focus, win, time );
579 XSetInputFocus( display, win, RevertToParent, time );
584 /**********************************************************************
585 * handle_manager_message
587 static void handle_manager_message( HWND hwnd, XClientMessageEvent *event )
589 if (hwnd != GetDesktopWindow()) return;
590 if (systray_atom && event->data.l[1] == systray_atom)
591 change_systray_owner( event->display, event->data.l[2] );
595 /**********************************************************************
596 * handle_wm_protocols
598 static void handle_wm_protocols( HWND hwnd, XClientMessageEvent *event )
600 Atom protocol = (Atom)event->data.l[0];
601 Time event_time = (Time)event->data.l[1];
603 if (!protocol) return;
605 if (protocol == x11drv_atom(WM_DELETE_WINDOW))
607 update_user_time( event_time );
609 if (hwnd == GetDesktopWindow())
611 /* The desktop window does not have a close button that we can
612 * pretend to click. Therefore, we simply send it a close command. */
613 SendMessageW(hwnd, WM_SYSCOMMAND, SC_CLOSE, 0);
614 return;
617 /* Ignore the delete window request if the window has been disabled
618 * and we are in managed mode. This is to disallow applications from
619 * being closed by the window manager while in a modal state.
621 if (IsWindowEnabled(hwnd))
623 HMENU hSysMenu;
625 if (GetClassLongW(hwnd, GCL_STYLE) & CS_NOCLOSE) return;
626 hSysMenu = GetSystemMenu(hwnd, FALSE);
627 if (hSysMenu)
629 UINT state = GetMenuState(hSysMenu, SC_CLOSE, MF_BYCOMMAND);
630 if (state == 0xFFFFFFFF || (state & (MF_DISABLED | MF_GRAYED)))
631 return;
633 if (GetActiveWindow() != hwnd)
635 LRESULT ma = SendMessageW( hwnd, WM_MOUSEACTIVATE,
636 (WPARAM)GetAncestor( hwnd, GA_ROOT ),
637 MAKELPARAM( HTCLOSE, WM_NCLBUTTONDOWN ) );
638 switch(ma)
640 case MA_NOACTIVATEANDEAT:
641 case MA_ACTIVATEANDEAT:
642 return;
643 case MA_NOACTIVATE:
644 break;
645 case MA_ACTIVATE:
646 case 0:
647 SetActiveWindow(hwnd);
648 break;
649 default:
650 WARN( "unknown WM_MOUSEACTIVATE code %d\n", (int) ma );
651 break;
655 PostMessageW( hwnd, WM_SYSCOMMAND, SC_CLOSE, 0 );
658 else if (protocol == x11drv_atom(WM_TAKE_FOCUS))
660 HWND last_focus = x11drv_thread_data()->last_focus;
662 TRACE( "got take focus msg for %p, enabled=%d, visible=%d (style %08x), focus=%p, active=%p, fg=%p, last=%p\n",
663 hwnd, IsWindowEnabled(hwnd), IsWindowVisible(hwnd), GetWindowLongW(hwnd, GWL_STYLE),
664 GetFocus(), GetActiveWindow(), GetForegroundWindow(), last_focus );
666 if (can_activate_window(hwnd))
668 /* simulate a mouse click on the caption to find out
669 * whether the window wants to be activated */
670 LRESULT ma = SendMessageW( hwnd, WM_MOUSEACTIVATE,
671 (WPARAM)GetAncestor( hwnd, GA_ROOT ),
672 MAKELONG(HTCAPTION,WM_LBUTTONDOWN) );
673 if (ma != MA_NOACTIVATEANDEAT && ma != MA_NOACTIVATE)
675 set_focus( event->display, hwnd, event_time );
676 return;
679 else if (hwnd == GetDesktopWindow())
681 hwnd = GetForegroundWindow();
682 if (!hwnd) hwnd = last_focus;
683 if (!hwnd) hwnd = GetDesktopWindow();
684 set_focus( event->display, hwnd, event_time );
685 return;
687 /* try to find some other window to give the focus to */
688 hwnd = GetFocus();
689 if (hwnd) hwnd = GetAncestor( hwnd, GA_ROOT );
690 if (!hwnd) hwnd = GetActiveWindow();
691 if (!hwnd) hwnd = last_focus;
692 if (hwnd && can_activate_window(hwnd)) set_focus( event->display, hwnd, event_time );
694 else if (protocol == x11drv_atom(_NET_WM_PING))
696 XClientMessageEvent xev;
697 xev = *event;
699 TRACE("NET_WM Ping\n");
700 xev.window = DefaultRootWindow(xev.display);
701 XSendEvent(xev.display, xev.window, False, SubstructureRedirectMask | SubstructureNotifyMask, (XEvent*)&xev);
706 static const char * const focus_details[] =
708 "NotifyAncestor",
709 "NotifyVirtual",
710 "NotifyInferior",
711 "NotifyNonlinear",
712 "NotifyNonlinearVirtual",
713 "NotifyPointer",
714 "NotifyPointerRoot",
715 "NotifyDetailNone"
718 /**********************************************************************
719 * X11DRV_FocusIn
721 static void X11DRV_FocusIn( HWND hwnd, XEvent *xev )
723 XFocusChangeEvent *event = &xev->xfocus;
724 XIC xic;
726 if (!hwnd) return;
728 TRACE( "win %p xwin %lx detail=%s\n", hwnd, event->window, focus_details[event->detail] );
730 if (event->detail == NotifyPointer) return;
731 if (hwnd == GetDesktopWindow()) return;
733 if ((xic = X11DRV_get_ic( hwnd ))) XSetICFocus( xic );
734 if (use_take_focus)
736 if (hwnd == GetForegroundWindow()) clip_fullscreen_window( hwnd, FALSE );
737 return;
740 if (!can_activate_window(hwnd))
742 HWND hwnd = GetFocus();
743 if (hwnd) hwnd = GetAncestor( hwnd, GA_ROOT );
744 if (!hwnd) hwnd = GetActiveWindow();
745 if (!hwnd) hwnd = x11drv_thread_data()->last_focus;
746 if (hwnd && can_activate_window(hwnd)) set_focus( event->display, hwnd, CurrentTime );
748 else SetForegroundWindow( hwnd );
752 /**********************************************************************
753 * X11DRV_FocusOut
755 * Note: only top-level windows get FocusOut events.
757 static void X11DRV_FocusOut( HWND hwnd, XEvent *xev )
759 XFocusChangeEvent *event = &xev->xfocus;
760 HWND hwnd_tmp;
761 Window focus_win;
762 int revert;
763 XIC xic;
765 TRACE( "win %p xwin %lx detail=%s\n", hwnd, event->window, focus_details[event->detail] );
767 if (event->detail == NotifyPointer)
769 if (!hwnd && event->window == x11drv_thread_data()->clip_window) reset_clipping_window();
770 return;
772 if (!hwnd) return;
773 if (ximInComposeMode) return;
775 x11drv_thread_data()->last_focus = hwnd;
776 if ((xic = X11DRV_get_ic( hwnd ))) XUnsetICFocus( xic );
778 if (root_window != DefaultRootWindow(event->display))
780 if (hwnd == GetDesktopWindow()) reset_clipping_window();
781 return;
783 if (hwnd != GetForegroundWindow()) return;
784 SendMessageW( hwnd, WM_CANCELMODE, 0, 0 );
786 /* don't reset the foreground window, if the window which is
787 getting the focus is a Wine window */
789 XGetInputFocus( event->display, &focus_win, &revert );
790 if (focus_win)
792 if (XFindContext( event->display, focus_win, winContext, (char **)&hwnd_tmp ) != 0)
793 focus_win = 0;
796 if (!focus_win)
798 /* Abey : 6-Oct-99. Check again if the focus out window is the
799 Foreground window, because in most cases the messages sent
800 above must have already changed the foreground window, in which
801 case we don't have to change the foreground window to 0 */
802 if (hwnd == GetForegroundWindow())
804 TRACE( "lost focus, setting fg to desktop\n" );
805 SetForegroundWindow( GetDesktopWindow() );
811 /***********************************************************************
812 * X11DRV_Expose
814 static void X11DRV_Expose( HWND hwnd, XEvent *xev )
816 XExposeEvent *event = &xev->xexpose;
817 RECT rect;
818 struct x11drv_win_data *data;
819 HRGN surface_region = 0;
820 UINT flags = RDW_INVALIDATE | RDW_ERASE | RDW_FRAME | RDW_ALLCHILDREN;
822 TRACE( "win %p (%lx) %d,%d %dx%d\n",
823 hwnd, event->window, event->x, event->y, event->width, event->height );
825 if (!(data = get_win_data( hwnd ))) return;
827 rect.left = event->x;
828 rect.top = event->y;
829 rect.right = event->x + event->width;
830 rect.bottom = event->y + event->height;
832 if (event->window != data->client_window)
834 if (data->surface)
836 surface_region = expose_surface( data->surface, &rect );
837 if (!surface_region) flags = 0;
838 else OffsetRgn( surface_region, data->whole_rect.left - data->client_rect.left,
839 data->whole_rect.top - data->client_rect.top );
841 if (data->vis.visualid != default_visual.visualid)
842 data->surface->funcs->flush( data->surface );
844 OffsetRect( &rect, data->whole_rect.left - data->client_rect.left,
845 data->whole_rect.top - data->client_rect.top );
848 if (event->window != root_window)
850 if (GetWindowLongW( data->hwnd, GWL_EXSTYLE ) & WS_EX_LAYOUTRTL)
851 mirror_rect( &data->client_rect, &rect );
853 SERVER_START_REQ( update_window_zorder )
855 req->window = wine_server_user_handle( hwnd );
856 req->rect.left = rect.left;
857 req->rect.top = rect.top;
858 req->rect.right = rect.right;
859 req->rect.bottom = rect.bottom;
860 wine_server_call( req );
862 SERVER_END_REQ;
864 else
866 OffsetRect( &rect, virtual_screen_rect.left, virtual_screen_rect.top );
867 flags &= ~RDW_ALLCHILDREN;
869 release_win_data( data );
871 if (flags) RedrawWindow( hwnd, &rect, surface_region, flags );
872 if (surface_region) DeleteObject( surface_region );
876 /**********************************************************************
877 * X11DRV_MapNotify
879 static void X11DRV_MapNotify( HWND hwnd, XEvent *event )
881 struct x11drv_win_data *data;
883 if (event->xany.window == x11drv_thread_data()->clip_window)
885 clipping_cursor = 1;
886 return;
888 if (!(data = get_win_data( hwnd ))) return;
890 if (!data->managed && !data->embedded && data->mapped)
892 HWND hwndFocus = GetFocus();
893 if (hwndFocus && IsChild( hwnd, hwndFocus ))
894 set_input_focus( data->display, data->whole_window );
896 release_win_data( data );
900 /**********************************************************************
901 * X11DRV_UnmapNotify
903 static void X11DRV_UnmapNotify( HWND hwnd, XEvent *event )
905 if (event->xany.window == x11drv_thread_data()->clip_window) clipping_cursor = 0;
909 /***********************************************************************
910 * is_net_wm_state_maximized
912 static BOOL is_net_wm_state_maximized( Display *display, struct x11drv_win_data *data )
914 Atom type, *state;
915 int format, ret = 0;
916 unsigned long i, count, remaining;
918 if (!data->whole_window) return FALSE;
920 if (!XGetWindowProperty( display, data->whole_window, x11drv_atom(_NET_WM_STATE), 0,
921 65536/sizeof(CARD32), False, XA_ATOM, &type, &format, &count,
922 &remaining, (unsigned char **)&state ))
924 if (type == XA_ATOM && format == 32)
926 for (i = 0; i < count; i++)
928 if (state[i] == x11drv_atom(_NET_WM_STATE_MAXIMIZED_VERT) ||
929 state[i] == x11drv_atom(_NET_WM_STATE_MAXIMIZED_HORZ))
930 ret++;
933 XFree( state );
935 return (ret == 2);
939 /***********************************************************************
940 * X11DRV_ReparentNotify
942 static void X11DRV_ReparentNotify( HWND hwnd, XEvent *xev )
944 XReparentEvent *event = &xev->xreparent;
945 struct x11drv_win_data *data;
946 HWND parent, old_parent;
947 DWORD style;
949 if (!(data = get_win_data( hwnd ))) return;
951 if (!data->embedded)
953 release_win_data( data );
954 return;
957 if (data->whole_window)
959 if (event->parent == root_window)
961 TRACE( "%p/%lx reparented to root\n", hwnd, data->whole_window );
962 data->embedder = 0;
963 release_win_data( data );
964 SendMessageW( hwnd, WM_CLOSE, 0, 0 );
965 return;
967 data->embedder = event->parent;
970 TRACE( "%p/%lx reparented to %lx\n", hwnd, data->whole_window, event->parent );
971 release_win_data( data );
973 style = GetWindowLongW( hwnd, GWL_STYLE );
974 if (event->parent == root_window)
976 parent = GetDesktopWindow();
977 style = (style & ~WS_CHILD) | WS_POPUP;
979 else
981 if (!(parent = create_foreign_window( event->display, event->parent ))) return;
982 style = (style & ~WS_POPUP) | WS_CHILD;
985 ShowWindow( hwnd, SW_HIDE );
986 old_parent = SetParent( hwnd, parent );
987 SetWindowLongW( hwnd, GWL_STYLE, style );
988 SetWindowPos( hwnd, HWND_TOP, event->x, event->y, 0, 0,
989 SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOCOPYBITS |
990 ((style & WS_VISIBLE) ? SWP_SHOWWINDOW : 0) );
992 /* make old parent destroy itself if it no longer has children */
993 if (old_parent != GetDesktopWindow()) PostMessageW( old_parent, WM_CLOSE, 0, 0 );
997 /***********************************************************************
998 * X11DRV_ConfigureNotify
1000 void X11DRV_ConfigureNotify( HWND hwnd, XEvent *xev )
1002 XConfigureEvent *event = &xev->xconfigure;
1003 struct x11drv_win_data *data;
1004 RECT rect;
1005 UINT flags;
1006 HWND parent;
1007 BOOL root_coords;
1008 int cx, cy, x = event->x, y = event->y;
1009 DWORD style;
1011 if (!hwnd) return;
1012 if (!(data = get_win_data( hwnd ))) return;
1013 if (!data->mapped || data->iconic) goto done;
1014 if (data->whole_window && !data->managed) goto done;
1015 /* ignore synthetic events on foreign windows */
1016 if (event->send_event && !data->whole_window) goto done;
1017 if (data->configure_serial && (long)(data->configure_serial - event->serial) > 0)
1019 TRACE( "win %p/%lx event %d,%d,%dx%d ignoring old serial %lu/%lu\n",
1020 hwnd, data->whole_window, event->x, event->y, event->width, event->height,
1021 event->serial, data->configure_serial );
1022 goto done;
1025 /* Get geometry */
1027 parent = GetAncestor( hwnd, GA_PARENT );
1028 root_coords = event->send_event; /* synthetic events are always in root coords */
1030 if (!root_coords && parent == GetDesktopWindow()) /* normal event, map coordinates to the root */
1032 Window child;
1033 XTranslateCoordinates( event->display, event->window, root_window,
1034 0, 0, &x, &y, &child );
1035 root_coords = TRUE;
1037 rect.left = x;
1038 rect.top = y;
1039 rect.right = x + event->width;
1040 rect.bottom = y + event->height;
1041 if (root_coords) OffsetRect( &rect, virtual_screen_rect.left, virtual_screen_rect.top );
1042 TRACE( "win %p/%lx new X rect %d,%d,%dx%d (event %d,%d,%dx%d)\n",
1043 hwnd, data->whole_window, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top,
1044 event->x, event->y, event->width, event->height );
1046 X11DRV_X_to_window_rect( data, &rect );
1047 if (root_coords) MapWindowPoints( 0, parent, (POINT *)&rect, 2 );
1049 /* Compare what has changed */
1051 x = rect.left;
1052 y = rect.top;
1053 cx = rect.right - rect.left;
1054 cy = rect.bottom - rect.top;
1055 flags = SWP_NOACTIVATE | SWP_NOZORDER;
1057 if (!data->whole_window) flags |= SWP_NOCOPYBITS; /* we can't copy bits of foreign windows */
1059 if (data->window_rect.left == x && data->window_rect.top == y) flags |= SWP_NOMOVE;
1060 else
1061 TRACE( "%p moving from (%d,%d) to (%d,%d)\n",
1062 hwnd, data->window_rect.left, data->window_rect.top, x, y );
1064 if ((data->window_rect.right - data->window_rect.left == cx &&
1065 data->window_rect.bottom - data->window_rect.top == cy) ||
1066 (IsRectEmpty( &data->window_rect ) && event->width == 1 && event->height == 1))
1067 flags |= SWP_NOSIZE;
1068 else
1069 TRACE( "%p resizing from (%dx%d) to (%dx%d)\n",
1070 hwnd, data->window_rect.right - data->window_rect.left,
1071 data->window_rect.bottom - data->window_rect.top, cx, cy );
1073 style = GetWindowLongW( data->hwnd, GWL_STYLE );
1074 if ((style & WS_CAPTION) == WS_CAPTION)
1076 if (is_net_wm_state_maximized( event->display, data ))
1078 if (!(style & WS_MAXIMIZE))
1080 TRACE( "win %p/%lx is maximized\n", data->hwnd, data->whole_window );
1081 release_win_data( data );
1082 SendMessageW( data->hwnd, WM_SYSCOMMAND, SC_MAXIMIZE, 0 );
1083 return;
1086 else if (style & WS_MAXIMIZE)
1088 TRACE( "window %p/%lx is no longer maximized\n", data->hwnd, data->whole_window );
1089 release_win_data( data );
1090 SendMessageW( data->hwnd, WM_SYSCOMMAND, SC_RESTORE, 0 );
1091 return;
1095 if ((flags & (SWP_NOSIZE | SWP_NOMOVE)) != (SWP_NOSIZE | SWP_NOMOVE))
1097 release_win_data( data );
1098 SetWindowPos( hwnd, 0, x, y, cx, cy, flags );
1099 return;
1102 done:
1103 release_win_data( data );
1107 /**********************************************************************
1108 * X11DRV_GravityNotify
1110 static void X11DRV_GravityNotify( HWND hwnd, XEvent *xev )
1112 XGravityEvent *event = &xev->xgravity;
1113 struct x11drv_win_data *data = get_win_data( hwnd );
1114 RECT rect, window_rect;
1116 if (!data) return;
1118 if (data->whole_window) /* only handle this for foreign windows */
1120 release_win_data( data );
1121 return;
1124 rect.left = event->x;
1125 rect.top = event->y;
1126 rect.right = rect.left + data->whole_rect.right - data->whole_rect.left;
1127 rect.bottom = rect.top + data->whole_rect.bottom - data->whole_rect.top;
1129 TRACE( "win %p/%lx new X rect %d,%d,%dx%d (event %d,%d)\n",
1130 hwnd, data->whole_window, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top,
1131 event->x, event->y );
1133 X11DRV_X_to_window_rect( data, &rect );
1134 window_rect = data->window_rect;
1135 release_win_data( data );
1137 if (window_rect.left != rect.left || window_rect.top != rect.top)
1138 SetWindowPos( hwnd, 0, rect.left, rect.top, 0, 0,
1139 SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOCOPYBITS );
1143 /***********************************************************************
1144 * get_window_wm_state
1146 static int get_window_wm_state( Display *display, Window window )
1148 struct
1150 CARD32 state;
1151 XID icon;
1152 } *state;
1153 Atom type;
1154 int format, ret = -1;
1155 unsigned long count, remaining;
1157 if (!XGetWindowProperty( display, window, x11drv_atom(WM_STATE), 0,
1158 sizeof(*state)/sizeof(CARD32), False, x11drv_atom(WM_STATE),
1159 &type, &format, &count, &remaining, (unsigned char **)&state ))
1161 if (type == x11drv_atom(WM_STATE) && get_property_size( format, count ) >= sizeof(*state))
1162 ret = state->state;
1163 XFree( state );
1165 return ret;
1169 /***********************************************************************
1170 * handle_wm_state_notify
1172 * Handle a PropertyNotify for WM_STATE.
1174 static void handle_wm_state_notify( HWND hwnd, XPropertyEvent *event, BOOL update_window )
1176 struct x11drv_win_data *data = get_win_data( hwnd );
1177 DWORD style;
1179 if (!data) return;
1181 switch(event->state)
1183 case PropertyDelete:
1184 TRACE( "%p/%lx: WM_STATE deleted from %d\n", data->hwnd, data->whole_window, data->wm_state );
1185 data->wm_state = WithdrawnState;
1186 break;
1187 case PropertyNewValue:
1189 int old_state = data->wm_state;
1190 int new_state = get_window_wm_state( event->display, data->whole_window );
1191 if (new_state != -1 && new_state != data->wm_state)
1193 TRACE( "%p/%lx: new WM_STATE %d from %d\n",
1194 data->hwnd, data->whole_window, new_state, old_state );
1195 data->wm_state = new_state;
1196 /* ignore the initial state transition out of withdrawn state */
1197 /* metacity does Withdrawn->NormalState->IconicState when mapping an iconic window */
1198 if (!old_state) goto done;
1201 break;
1204 if (!update_window || !data->managed || !data->mapped) goto done;
1206 style = GetWindowLongW( data->hwnd, GWL_STYLE );
1208 if (data->iconic && data->wm_state == NormalState) /* restore window */
1210 data->iconic = FALSE;
1211 if ((style & WS_CAPTION) == WS_CAPTION && is_net_wm_state_maximized( event->display, data ))
1213 if ((style & WS_MAXIMIZEBOX) && !(style & WS_DISABLED))
1215 TRACE( "restoring to max %p/%lx\n", data->hwnd, data->whole_window );
1216 release_win_data( data );
1217 SendMessageW( hwnd, WM_SYSCOMMAND, SC_MAXIMIZE, 0 );
1218 return;
1220 TRACE( "not restoring to max win %p/%lx style %08x\n", data->hwnd, data->whole_window, style );
1222 else
1224 if (style & (WS_MINIMIZE | WS_MAXIMIZE))
1226 TRACE( "restoring win %p/%lx\n", data->hwnd, data->whole_window );
1227 release_win_data( data );
1228 SendMessageW( hwnd, WM_SYSCOMMAND, SC_RESTORE, 0 );
1229 return;
1231 TRACE( "not restoring win %p/%lx style %08x\n", data->hwnd, data->whole_window, style );
1234 else if (!data->iconic && data->wm_state == IconicState)
1236 data->iconic = TRUE;
1237 if ((style & WS_MINIMIZEBOX) && !(style & WS_DISABLED))
1239 TRACE( "minimizing win %p/%lx\n", data->hwnd, data->whole_window );
1240 release_win_data( data );
1241 SendMessageW( hwnd, WM_SYSCOMMAND, SC_MINIMIZE, 0 );
1242 return;
1244 TRACE( "not minimizing win %p/%lx style %08x\n", data->hwnd, data->whole_window, style );
1246 done:
1247 release_win_data( data );
1251 /***********************************************************************
1252 * X11DRV_PropertyNotify
1254 static void X11DRV_PropertyNotify( HWND hwnd, XEvent *xev )
1256 XPropertyEvent *event = &xev->xproperty;
1258 if (!hwnd) return;
1259 if (event->atom == x11drv_atom(WM_STATE)) handle_wm_state_notify( hwnd, event, TRUE );
1263 /* event filter to wait for a WM_STATE change notification on a window */
1264 static Bool is_wm_state_notify( Display *display, XEvent *event, XPointer arg )
1266 if (event->xany.window != (Window)arg) return 0;
1267 return (event->type == DestroyNotify ||
1268 (event->type == PropertyNotify && event->xproperty.atom == x11drv_atom(WM_STATE)));
1271 /***********************************************************************
1272 * wait_for_withdrawn_state
1274 void wait_for_withdrawn_state( HWND hwnd, BOOL set )
1276 Display *display = thread_display();
1277 struct x11drv_win_data *data;
1278 DWORD end = GetTickCount() + 2000;
1280 TRACE( "waiting for window %p to become %swithdrawn\n", hwnd, set ? "" : "not " );
1282 for (;;)
1284 XEvent event;
1285 Window window;
1286 int count = 0;
1288 if (!(data = get_win_data( hwnd ))) break;
1289 if (!data->managed || data->embedded || data->display != display) break;
1290 if (!(window = data->whole_window)) break;
1291 if (!data->mapped == !set)
1293 TRACE( "window %p/%lx now %smapped\n", hwnd, window, data->mapped ? "" : "un" );
1294 break;
1296 if ((data->wm_state == WithdrawnState) != !set)
1298 TRACE( "window %p/%lx state now %d\n", hwnd, window, data->wm_state );
1299 break;
1301 release_win_data( data );
1303 while (XCheckIfEvent( display, &event, is_wm_state_notify, (char *)window ))
1305 count++;
1306 if (XFilterEvent( &event, None )) continue; /* filtered, ignore it */
1307 if (event.type == DestroyNotify) call_event_handler( display, &event );
1308 else handle_wm_state_notify( hwnd, &event.xproperty, FALSE );
1311 if (!count)
1313 struct pollfd pfd;
1314 int timeout = end - GetTickCount();
1316 pfd.fd = ConnectionNumber(display);
1317 pfd.events = POLLIN;
1318 if (timeout <= 0 || poll( &pfd, 1, timeout ) != 1)
1320 FIXME( "window %p/%lx wait timed out\n", hwnd, window );
1321 return;
1325 release_win_data( data );
1329 /*****************************************************************
1330 * SetFocus (X11DRV.@)
1332 * Set the X focus.
1334 void CDECL X11DRV_SetFocus( HWND hwnd )
1336 struct x11drv_win_data *data;
1338 if (!(hwnd = GetAncestor( hwnd, GA_ROOT ))) return;
1339 if (!(data = get_win_data( hwnd ))) return;
1340 if (!data->managed) set_input_focus( data->display, data->whole_window );
1341 release_win_data( data );
1345 static HWND find_drop_window( HWND hQueryWnd, LPPOINT lpPt )
1347 RECT tempRect;
1349 if (!IsWindowEnabled(hQueryWnd)) return 0;
1351 GetWindowRect(hQueryWnd, &tempRect);
1353 if(!PtInRect(&tempRect, *lpPt)) return 0;
1355 if (!IsIconic( hQueryWnd ))
1357 POINT pt = *lpPt;
1358 ScreenToClient( hQueryWnd, &pt );
1359 GetClientRect( hQueryWnd, &tempRect );
1361 if (PtInRect( &tempRect, pt))
1363 HWND ret = ChildWindowFromPointEx( hQueryWnd, pt, CWP_SKIPINVISIBLE|CWP_SKIPDISABLED );
1364 if (ret && ret != hQueryWnd)
1366 ret = find_drop_window( ret, lpPt );
1367 if (ret) return ret;
1372 if(!(GetWindowLongA( hQueryWnd, GWL_EXSTYLE ) & WS_EX_ACCEPTFILES)) return 0;
1374 ScreenToClient(hQueryWnd, lpPt);
1376 return hQueryWnd;
1379 /**********************************************************************
1380 * EVENT_DropFromOffix
1382 * don't know if it still works (last Changelog is from 96/11/04)
1384 static void EVENT_DropFromOffiX( HWND hWnd, XClientMessageEvent *event )
1386 struct x11drv_win_data *data;
1387 unsigned long data_length;
1388 unsigned long aux_long;
1389 unsigned char* p_data = NULL;
1390 Atom atom_aux;
1391 int x, y, cx, cy, dummy;
1392 BOOL bAccept;
1393 Window win, w_aux_root, w_aux_child;
1395 if (!(data = get_win_data( hWnd ))) return;
1396 cx = data->whole_rect.right - data->whole_rect.left;
1397 cy = data->whole_rect.bottom - data->whole_rect.top;
1398 win = data->whole_window;
1399 release_win_data( data );
1401 XQueryPointer( event->display, win, &w_aux_root, &w_aux_child,
1402 &x, &y, &dummy, &dummy, (unsigned int*)&aux_long);
1403 x += virtual_screen_rect.left;
1404 y += virtual_screen_rect.top;
1406 /* find out drop point and drop window */
1407 if (x < 0 || y < 0 || x > cx || y > cy)
1409 bAccept = GetWindowLongW( hWnd, GWL_EXSTYLE ) & WS_EX_ACCEPTFILES;
1410 x = 0;
1411 y = 0;
1413 else
1415 POINT pt = { x, y };
1416 HWND hwndDrop = find_drop_window( hWnd, &pt );
1417 if (hwndDrop)
1419 x = pt.x;
1420 y = pt.y;
1421 bAccept = TRUE;
1423 else
1425 bAccept = FALSE;
1429 if (!bAccept) return;
1431 XGetWindowProperty( event->display, DefaultRootWindow(event->display),
1432 x11drv_atom(DndSelection), 0, 65535, FALSE,
1433 AnyPropertyType, &atom_aux, &dummy,
1434 &data_length, &aux_long, &p_data);
1436 if( !aux_long && p_data) /* don't bother if > 64K */
1438 char *p = (char *)p_data;
1439 char *p_drop;
1441 aux_long = 0;
1442 while( *p ) /* calculate buffer size */
1444 INT len = GetShortPathNameA( p, NULL, 0 );
1445 if (len) aux_long += len + 1;
1446 p += strlen(p) + 1;
1448 if( aux_long && aux_long < 65535 )
1450 HDROP hDrop;
1451 DROPFILES *lpDrop;
1453 aux_long += sizeof(DROPFILES) + 1;
1454 hDrop = GlobalAlloc( GMEM_SHARE, aux_long );
1455 lpDrop = GlobalLock( hDrop );
1457 if( lpDrop )
1459 lpDrop->pFiles = sizeof(DROPFILES);
1460 lpDrop->pt.x = x;
1461 lpDrop->pt.y = y;
1462 lpDrop->fNC = FALSE;
1463 lpDrop->fWide = FALSE;
1464 p_drop = (char *)(lpDrop + 1);
1465 p = (char *)p_data;
1466 while(*p)
1468 if (GetShortPathNameA( p, p_drop, aux_long - (p_drop - (char *)lpDrop) ))
1469 p_drop += strlen( p_drop ) + 1;
1470 p += strlen(p) + 1;
1472 *p_drop = '\0';
1473 PostMessageA( hWnd, WM_DROPFILES, (WPARAM)hDrop, 0L );
1477 if( p_data ) XFree(p_data);
1480 /**********************************************************************
1481 * EVENT_DropURLs
1483 * drop items are separated by \n
1484 * each item is prefixed by its mime type
1486 * event->data.l[3], event->data.l[4] contains drop x,y position
1488 static void EVENT_DropURLs( HWND hWnd, XClientMessageEvent *event )
1490 struct x11drv_win_data *win_data;
1491 unsigned long data_length;
1492 unsigned long aux_long, drop_len = 0;
1493 unsigned char *p_data = NULL; /* property data */
1494 char *p_drop = NULL;
1495 char *p, *next;
1496 int x, y;
1497 DROPFILES *lpDrop;
1498 HDROP hDrop;
1499 union {
1500 Atom atom_aux;
1501 int i;
1502 Window w_aux;
1503 unsigned int u;
1504 } u; /* unused */
1506 if (!(GetWindowLongW( hWnd, GWL_EXSTYLE ) & WS_EX_ACCEPTFILES)) return;
1508 XGetWindowProperty( event->display, DefaultRootWindow(event->display),
1509 x11drv_atom(DndSelection), 0, 65535, FALSE,
1510 AnyPropertyType, &u.atom_aux, &u.i,
1511 &data_length, &aux_long, &p_data);
1512 if (aux_long)
1513 WARN("property too large, truncated!\n");
1514 TRACE("urls=%s\n", p_data);
1516 if( !aux_long && p_data) { /* don't bother if > 64K */
1517 /* calculate length */
1518 p = (char*) p_data;
1519 next = strchr(p, '\n');
1520 while (p) {
1521 if (next) *next=0;
1522 if (strncmp(p,"file:",5) == 0 ) {
1523 INT len = GetShortPathNameA( p+5, NULL, 0 );
1524 if (len) drop_len += len + 1;
1526 if (next) {
1527 *next = '\n';
1528 p = next + 1;
1529 next = strchr(p, '\n');
1530 } else {
1531 p = NULL;
1535 if( drop_len && drop_len < 65535 ) {
1536 XQueryPointer( event->display, root_window, &u.w_aux, &u.w_aux,
1537 &x, &y, &u.i, &u.i, &u.u);
1538 x += virtual_screen_rect.left;
1539 y += virtual_screen_rect.top;
1541 drop_len += sizeof(DROPFILES) + 1;
1542 hDrop = GlobalAlloc( GMEM_SHARE, drop_len );
1543 lpDrop = GlobalLock( hDrop );
1545 if( lpDrop && (win_data = get_win_data( hWnd )))
1547 lpDrop->pFiles = sizeof(DROPFILES);
1548 lpDrop->pt.x = x;
1549 lpDrop->pt.y = y;
1550 lpDrop->fNC =
1551 ( x < (win_data->client_rect.left - win_data->whole_rect.left) ||
1552 y < (win_data->client_rect.top - win_data->whole_rect.top) ||
1553 x > (win_data->client_rect.right - win_data->whole_rect.left) ||
1554 y > (win_data->client_rect.bottom - win_data->whole_rect.top) );
1555 lpDrop->fWide = FALSE;
1556 p_drop = (char*)(lpDrop + 1);
1557 release_win_data( win_data );
1560 /* create message content */
1561 if (p_drop) {
1562 p = (char*) p_data;
1563 next = strchr(p, '\n');
1564 while (p) {
1565 if (next) *next=0;
1566 if (strncmp(p,"file:",5) == 0 ) {
1567 INT len = GetShortPathNameA( p+5, p_drop, 65535 );
1568 if (len) {
1569 TRACE("drop file %s as %s\n", p+5, p_drop);
1570 p_drop += len+1;
1571 } else {
1572 WARN("can't convert file %s to dos name\n", p+5);
1574 } else {
1575 WARN("unknown mime type %s\n", p);
1577 if (next) {
1578 *next = '\n';
1579 p = next + 1;
1580 next = strchr(p, '\n');
1581 } else {
1582 p = NULL;
1584 *p_drop = '\0';
1587 GlobalUnlock(hDrop);
1588 PostMessageA( hWnd, WM_DROPFILES, (WPARAM)hDrop, 0L );
1591 if( p_data ) XFree(p_data);
1596 /**********************************************************************
1597 * handle_xembed_protocol
1599 static void handle_xembed_protocol( HWND hwnd, XClientMessageEvent *event )
1601 struct x11drv_win_data *data = get_win_data( hwnd );
1603 if (!data) return;
1605 switch (event->data.l[1])
1607 case XEMBED_EMBEDDED_NOTIFY:
1608 TRACE( "win %p/%lx XEMBED_EMBEDDED_NOTIFY owner %lx\n", hwnd, event->window, event->data.l[3] );
1609 data->embedder = event->data.l[3];
1610 break;
1611 default:
1612 TRACE( "win %p/%lx XEMBED message %lu(%lu)\n",
1613 hwnd, event->window, event->data.l[1], event->data.l[2] );
1614 break;
1616 release_win_data( data );
1620 /**********************************************************************
1621 * handle_dnd_protocol
1623 static void handle_dnd_protocol( HWND hwnd, XClientMessageEvent *event )
1625 Window root, child;
1626 int root_x, root_y, child_x, child_y;
1627 unsigned int u;
1629 /* query window (drag&drop event contains only drag window) */
1630 XQueryPointer( event->display, root_window, &root, &child,
1631 &root_x, &root_y, &child_x, &child_y, &u);
1632 if (XFindContext( event->display, child, winContext, (char **)&hwnd ) != 0) hwnd = 0;
1633 if (!hwnd) return;
1634 if (event->data.l[0] == DndFile || event->data.l[0] == DndFiles)
1635 EVENT_DropFromOffiX(hwnd, event);
1636 else if (event->data.l[0] == DndURL)
1637 EVENT_DropURLs(hwnd, event);
1641 struct client_message_handler
1643 int atom; /* protocol atom */
1644 void (*handler)(HWND, XClientMessageEvent *); /* corresponding handler function */
1647 static const struct client_message_handler client_messages[] =
1649 { XATOM_MANAGER, handle_manager_message },
1650 { XATOM_WM_PROTOCOLS, handle_wm_protocols },
1651 { XATOM__XEMBED, handle_xembed_protocol },
1652 { XATOM_DndProtocol, handle_dnd_protocol },
1653 { XATOM_XdndEnter, X11DRV_XDND_EnterEvent },
1654 { XATOM_XdndPosition, X11DRV_XDND_PositionEvent },
1655 { XATOM_XdndDrop, X11DRV_XDND_DropEvent },
1656 { XATOM_XdndLeave, X11DRV_XDND_LeaveEvent }
1660 /**********************************************************************
1661 * X11DRV_ClientMessage
1663 static void X11DRV_ClientMessage( HWND hwnd, XEvent *xev )
1665 XClientMessageEvent *event = &xev->xclient;
1666 unsigned int i;
1668 if (!hwnd) return;
1670 if (event->format != 32)
1672 WARN( "Don't know how to handle format %d\n", event->format );
1673 return;
1676 for (i = 0; i < sizeof(client_messages)/sizeof(client_messages[0]); i++)
1678 if (event->message_type == X11DRV_Atoms[client_messages[i].atom - FIRST_XATOM])
1680 client_messages[i].handler( hwnd, event );
1681 return;
1684 TRACE( "no handler found for %ld\n", event->message_type );