winex11.drv: Process keyboard events when QS_HOTKEY is specified.
[wine/multimedia.git] / dlls / winex11.drv / event.c
blob00915cfa7e4bbc280ee25d21bd0f8b6b8fbd0ed8
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 if (prev->xany.window == next->xany.window && next->type == MotionNotify)
315 TRACE( "discarding duplicate MotionNotify for window %lx\n", prev->xany.window );
316 return MERGE_DISCARD;
318 break;
319 #ifdef HAVE_X11_EXTENSIONS_XINPUT2_H
320 case GenericEvent:
322 struct x11drv_thread_data *thread_data = x11drv_thread_data();
323 if (prev->xcookie.extension != xinput2_opcode) break;
324 if (prev->xcookie.evtype != XI_RawMotion) break;
325 if (thread_data->warp_serial) break;
326 switch (next->type)
328 case MotionNotify:
329 if (next->xany.window == thread_data->clip_window &&
330 next->xmotion.time - thread_data->last_motion_notify < 1000)
332 TRACE( "ignoring MotionNotify for clip window\n" );
333 return MERGE_IGNORE;
335 break;
336 case GenericEvent:
337 if (next->xcookie.extension != xinput2_opcode) break;
338 if (next->xcookie.evtype != XI_RawMotion) break;
339 return merge_raw_motion_events( prev->xcookie.data, next->xcookie.data );
342 break;
343 #endif
345 return MERGE_HANDLE;
349 /***********************************************************************
350 * call_event_handler
352 static inline void call_event_handler( Display *display, XEvent *event )
354 HWND hwnd;
355 XEvent *prev;
356 struct x11drv_thread_data *thread_data;
358 if (!handlers[event->type])
360 TRACE( "%s for win %lx, ignoring\n", dbgstr_event( event->type ), event->xany.window );
361 return; /* no handler, ignore it */
364 if (XFindContext( display, event->xany.window, winContext, (char **)&hwnd ) != 0)
365 hwnd = 0; /* not for a registered window */
366 if (!hwnd && event->xany.window == root_window) hwnd = GetDesktopWindow();
368 TRACE( "%lu %s for hwnd/window %p/%lx\n",
369 event->xany.serial, dbgstr_event( event->type ), hwnd, event->xany.window );
370 wine_tsx11_unlock();
371 thread_data = x11drv_thread_data();
372 prev = thread_data->current_event;
373 thread_data->current_event = event;
374 handlers[event->type]( hwnd, event );
375 thread_data->current_event = prev;
376 wine_tsx11_lock();
380 /***********************************************************************
381 * process_events
383 static int process_events( Display *display, Bool (*filter)(Display*, XEvent*,XPointer), ULONG_PTR arg )
385 XEvent event, prev_event;
386 int count = 0;
387 enum event_merge_action action = MERGE_DISCARD;
389 prev_event.type = 0;
390 wine_tsx11_lock();
391 while (XCheckIfEvent( display, &event, filter, (char *)arg ))
393 count++;
394 if (XFilterEvent( &event, None ))
397 * SCIM on linux filters key events strangely. It does not filter the
398 * KeyPress events for these keys however it does filter the
399 * KeyRelease events. This causes wine to become very confused as
400 * to the keyboard state.
402 * We need to let those KeyRelease events be processed so that the
403 * keyboard state is correct.
405 if (event.type == KeyRelease)
407 KeySym keysym = 0;
408 XKeyEvent *keyevent = &event.xkey;
410 XLookupString(keyevent, NULL, 0, &keysym, NULL);
411 if (!(keysym == XK_Shift_L ||
412 keysym == XK_Shift_R ||
413 keysym == XK_Control_L ||
414 keysym == XK_Control_R ||
415 keysym == XK_Alt_R ||
416 keysym == XK_Alt_L ||
417 keysym == XK_Meta_R ||
418 keysym == XK_Meta_L))
419 continue; /* not a key we care about, ignore it */
421 else
422 continue; /* filtered, ignore it */
424 get_event_data( &event );
425 if (prev_event.type) action = merge_events( &prev_event, &event );
426 switch( action )
428 case MERGE_HANDLE: /* handle prev, keep new */
429 call_event_handler( display, &prev_event );
430 /* fall through */
431 case MERGE_DISCARD: /* discard prev, keep new */
432 free_event_data( &prev_event );
433 prev_event = event;
434 break;
435 case MERGE_KEEP: /* handle new, keep prev for future merging */
436 call_event_handler( display, &event );
437 /* fall through */
438 case MERGE_IGNORE: /* ignore new, keep prev for future merging */
439 free_event_data( &event );
440 break;
443 if (prev_event.type) call_event_handler( display, &prev_event );
444 free_event_data( &prev_event );
445 XFlush( gdi_display );
446 wine_tsx11_unlock();
447 if (count) TRACE( "processed %d events\n", count );
448 return count;
452 /***********************************************************************
453 * MsgWaitForMultipleObjectsEx (X11DRV.@)
455 DWORD CDECL X11DRV_MsgWaitForMultipleObjectsEx( DWORD count, const HANDLE *handles,
456 DWORD timeout, DWORD mask, DWORD flags )
458 DWORD ret;
459 struct x11drv_thread_data *data = TlsGetValue( thread_data_tls_index );
461 if (!data)
463 if (!count && !timeout) return WAIT_TIMEOUT;
464 return WaitForMultipleObjectsEx( count, handles, flags & MWMO_WAITALL,
465 timeout, flags & MWMO_ALERTABLE );
468 if (data->current_event) mask = 0; /* don't process nested events */
470 if (process_events( data->display, filter_event, mask )) ret = count - 1;
471 else if (count || timeout)
473 ret = WaitForMultipleObjectsEx( count, handles, flags & MWMO_WAITALL,
474 timeout, flags & MWMO_ALERTABLE );
475 if (ret == count - 1) process_events( data->display, filter_event, mask );
477 else ret = WAIT_TIMEOUT;
479 return ret;
482 /***********************************************************************
483 * EVENT_x11_time_to_win32_time
485 * Make our timer and the X timer line up as best we can
486 * Pass 0 to retrieve the current adjustment value (times -1)
488 DWORD EVENT_x11_time_to_win32_time(Time time)
490 static DWORD adjust = 0;
491 DWORD now = GetTickCount();
492 DWORD ret;
494 if (! adjust && time != 0)
496 ret = now;
497 adjust = time - now;
499 else
501 /* If we got an event in the 'future', then our clock is clearly wrong.
502 If we got it more than 10000 ms in the future, then it's most likely
503 that the clock has wrapped. */
505 ret = time - adjust;
506 if (ret > now && ((ret - now) < 10000) && time != 0)
508 adjust += ret - now;
509 ret -= ret - now;
513 return ret;
517 /*******************************************************************
518 * can_activate_window
520 * Check if we can activate the specified window.
522 static inline BOOL can_activate_window( HWND hwnd )
524 LONG style = GetWindowLongW( hwnd, GWL_STYLE );
525 if (!(style & WS_VISIBLE)) return FALSE;
526 if ((style & (WS_POPUP|WS_CHILD)) == WS_CHILD) return FALSE;
527 if (style & WS_MINIMIZE) return FALSE;
528 if (GetWindowLongW( hwnd, GWL_EXSTYLE ) & WS_EX_NOACTIVATE) return FALSE;
529 if (hwnd == GetDesktopWindow()) return FALSE;
530 return !(style & WS_DISABLED);
534 /**********************************************************************
535 * set_focus
537 static void set_focus( Display *display, HWND hwnd, Time time )
539 HWND focus;
540 Window win;
541 GUITHREADINFO threadinfo;
543 TRACE( "setting foreground window to %p\n", hwnd );
544 SetForegroundWindow( hwnd );
546 threadinfo.cbSize = sizeof(threadinfo);
547 GetGUIThreadInfo(0, &threadinfo);
548 focus = threadinfo.hwndFocus;
549 if (!focus) focus = threadinfo.hwndActive;
550 if (focus) focus = GetAncestor( focus, GA_ROOT );
551 win = X11DRV_get_whole_window(focus);
553 if (win)
555 TRACE( "setting focus to %p (%lx) time=%ld\n", focus, win, time );
556 wine_tsx11_lock();
557 XSetInputFocus( display, win, RevertToParent, time );
558 wine_tsx11_unlock();
563 /**********************************************************************
564 * handle_manager_message
566 static void handle_manager_message( HWND hwnd, XClientMessageEvent *event )
568 if (hwnd != GetDesktopWindow()) return;
569 if (systray_atom && event->data.l[1] == systray_atom)
570 change_systray_owner( event->display, event->data.l[2] );
574 /**********************************************************************
575 * handle_wm_protocols
577 static void handle_wm_protocols( HWND hwnd, XClientMessageEvent *event )
579 Atom protocol = (Atom)event->data.l[0];
580 Time event_time = (Time)event->data.l[1];
582 if (!protocol) return;
584 if (protocol == x11drv_atom(WM_DELETE_WINDOW))
586 update_user_time( event_time );
588 if (hwnd == GetDesktopWindow())
590 /* The desktop window does not have a close button that we can
591 * pretend to click. Therefore, we simply send it a close command. */
592 SendMessageW(hwnd, WM_SYSCOMMAND, SC_CLOSE, 0);
593 return;
596 /* Ignore the delete window request if the window has been disabled
597 * and we are in managed mode. This is to disallow applications from
598 * being closed by the window manager while in a modal state.
600 if (IsWindowEnabled(hwnd))
602 HMENU hSysMenu;
604 if (GetClassLongW(hwnd, GCL_STYLE) & CS_NOCLOSE) return;
605 hSysMenu = GetSystemMenu(hwnd, FALSE);
606 if (hSysMenu)
608 UINT state = GetMenuState(hSysMenu, SC_CLOSE, MF_BYCOMMAND);
609 if (state == 0xFFFFFFFF || (state & (MF_DISABLED | MF_GRAYED)))
610 return;
612 if (GetActiveWindow() != hwnd)
614 LRESULT ma = SendMessageW( hwnd, WM_MOUSEACTIVATE,
615 (WPARAM)GetAncestor( hwnd, GA_ROOT ),
616 MAKELPARAM( HTCLOSE, WM_NCLBUTTONDOWN ) );
617 switch(ma)
619 case MA_NOACTIVATEANDEAT:
620 case MA_ACTIVATEANDEAT:
621 return;
622 case MA_NOACTIVATE:
623 break;
624 case MA_ACTIVATE:
625 case 0:
626 SetActiveWindow(hwnd);
627 break;
628 default:
629 WARN( "unknown WM_MOUSEACTIVATE code %d\n", (int) ma );
630 break;
634 PostMessageW( hwnd, WM_SYSCOMMAND, SC_CLOSE, 0 );
637 else if (protocol == x11drv_atom(WM_TAKE_FOCUS))
639 HWND last_focus = x11drv_thread_data()->last_focus;
641 TRACE( "got take focus msg for %p, enabled=%d, visible=%d (style %08x), focus=%p, active=%p, fg=%p, last=%p\n",
642 hwnd, IsWindowEnabled(hwnd), IsWindowVisible(hwnd), GetWindowLongW(hwnd, GWL_STYLE),
643 GetFocus(), GetActiveWindow(), GetForegroundWindow(), last_focus );
645 if (can_activate_window(hwnd))
647 /* simulate a mouse click on the caption to find out
648 * whether the window wants to be activated */
649 LRESULT ma = SendMessageW( hwnd, WM_MOUSEACTIVATE,
650 (WPARAM)GetAncestor( hwnd, GA_ROOT ),
651 MAKELONG(HTCAPTION,WM_LBUTTONDOWN) );
652 if (ma != MA_NOACTIVATEANDEAT && ma != MA_NOACTIVATE)
654 set_focus( event->display, hwnd, event_time );
655 return;
658 else if (hwnd == GetDesktopWindow())
660 hwnd = GetForegroundWindow();
661 if (!hwnd) hwnd = last_focus;
662 if (!hwnd) hwnd = GetDesktopWindow();
663 set_focus( event->display, hwnd, event_time );
664 return;
666 /* try to find some other window to give the focus to */
667 hwnd = GetFocus();
668 if (hwnd) hwnd = GetAncestor( hwnd, GA_ROOT );
669 if (!hwnd) hwnd = GetActiveWindow();
670 if (!hwnd) hwnd = last_focus;
671 if (hwnd && can_activate_window(hwnd)) set_focus( event->display, hwnd, event_time );
673 else if (protocol == x11drv_atom(_NET_WM_PING))
675 XClientMessageEvent xev;
676 xev = *event;
678 TRACE("NET_WM Ping\n");
679 wine_tsx11_lock();
680 xev.window = DefaultRootWindow(xev.display);
681 XSendEvent(xev.display, xev.window, False, SubstructureRedirectMask | SubstructureNotifyMask, (XEvent*)&xev);
682 wine_tsx11_unlock();
683 /* this line is semi-stolen from gtk2 */
684 TRACE("NET_WM Pong\n");
689 static const char * const focus_details[] =
691 "NotifyAncestor",
692 "NotifyVirtual",
693 "NotifyInferior",
694 "NotifyNonlinear",
695 "NotifyNonlinearVirtual",
696 "NotifyPointer",
697 "NotifyPointerRoot",
698 "NotifyDetailNone"
701 /**********************************************************************
702 * X11DRV_FocusIn
704 static void X11DRV_FocusIn( HWND hwnd, XEvent *xev )
706 XFocusChangeEvent *event = &xev->xfocus;
707 XIC xic;
709 if (!hwnd) return;
711 TRACE( "win %p xwin %lx detail=%s\n", hwnd, event->window, focus_details[event->detail] );
713 if (event->detail == NotifyPointer) return;
714 if (hwnd == GetDesktopWindow()) return;
716 if ((xic = X11DRV_get_ic( hwnd )))
718 wine_tsx11_lock();
719 XSetICFocus( xic );
720 wine_tsx11_unlock();
722 if (use_take_focus)
724 if (hwnd == GetForegroundWindow()) clip_fullscreen_window( hwnd, FALSE );
725 return;
728 if (!can_activate_window(hwnd))
730 HWND hwnd = GetFocus();
731 if (hwnd) hwnd = GetAncestor( hwnd, GA_ROOT );
732 if (!hwnd) hwnd = GetActiveWindow();
733 if (!hwnd) hwnd = x11drv_thread_data()->last_focus;
734 if (hwnd && can_activate_window(hwnd)) set_focus( event->display, hwnd, CurrentTime );
736 else SetForegroundWindow( hwnd );
740 /**********************************************************************
741 * X11DRV_FocusOut
743 * Note: only top-level windows get FocusOut events.
745 static void X11DRV_FocusOut( HWND hwnd, XEvent *xev )
747 XFocusChangeEvent *event = &xev->xfocus;
748 HWND hwnd_tmp;
749 Window focus_win;
750 int revert;
751 XIC xic;
753 TRACE( "win %p xwin %lx detail=%s\n", hwnd, event->window, focus_details[event->detail] );
755 if (event->detail == NotifyPointer)
757 if (!hwnd && event->window == x11drv_thread_data()->clip_window) reset_clipping_window();
758 return;
760 if (ximInComposeMode) return;
762 x11drv_thread_data()->last_focus = hwnd;
763 if ((xic = X11DRV_get_ic( hwnd )))
765 wine_tsx11_lock();
766 XUnsetICFocus( xic );
767 wine_tsx11_unlock();
769 if (root_window != DefaultRootWindow(event->display))
771 if (hwnd == GetDesktopWindow()) reset_clipping_window();
772 return;
774 if (hwnd != GetForegroundWindow()) return;
775 SendMessageW( hwnd, WM_CANCELMODE, 0, 0 );
777 /* don't reset the foreground window, if the window which is
778 getting the focus is a Wine window */
780 wine_tsx11_lock();
781 XGetInputFocus( event->display, &focus_win, &revert );
782 if (focus_win)
784 if (XFindContext( event->display, focus_win, winContext, (char **)&hwnd_tmp ) != 0)
785 focus_win = 0;
787 wine_tsx11_unlock();
789 if (!focus_win)
791 /* Abey : 6-Oct-99. Check again if the focus out window is the
792 Foreground window, because in most cases the messages sent
793 above must have already changed the foreground window, in which
794 case we don't have to change the foreground window to 0 */
795 if (hwnd == GetForegroundWindow())
797 TRACE( "lost focus, setting fg to desktop\n" );
798 SetForegroundWindow( GetDesktopWindow() );
804 /***********************************************************************
805 * X11DRV_Expose
807 static void X11DRV_Expose( HWND hwnd, XEvent *xev )
809 XExposeEvent *event = &xev->xexpose;
810 RECT rect;
811 struct x11drv_win_data *data;
812 int flags = RDW_INVALIDATE | RDW_ERASE;
814 TRACE( "win %p (%lx) %d,%d %dx%d\n",
815 hwnd, event->window, event->x, event->y, event->width, event->height );
817 if (!(data = X11DRV_get_win_data( hwnd ))) return;
819 rect.left = event->x;
820 rect.top = event->y;
821 rect.right = event->x + event->width;
822 rect.bottom = event->y + event->height;
823 if (event->window == data->whole_window)
825 OffsetRect( &rect, data->whole_rect.left - data->client_rect.left,
826 data->whole_rect.top - data->client_rect.top );
827 flags |= RDW_FRAME;
830 if (event->window != root_window)
832 if (GetWindowLongW( data->hwnd, GWL_EXSTYLE ) & WS_EX_LAYOUTRTL)
833 mirror_rect( &data->client_rect, &rect );
835 SERVER_START_REQ( update_window_zorder )
837 req->window = wine_server_user_handle( hwnd );
838 req->rect.left = rect.left;
839 req->rect.top = rect.top;
840 req->rect.right = rect.right;
841 req->rect.bottom = rect.bottom;
842 wine_server_call( req );
844 SERVER_END_REQ;
846 flags |= RDW_ALLCHILDREN;
848 else OffsetRect( &rect, virtual_screen_rect.left, virtual_screen_rect.top );
850 RedrawWindow( hwnd, &rect, 0, flags );
854 /**********************************************************************
855 * X11DRV_MapNotify
857 static void X11DRV_MapNotify( HWND hwnd, XEvent *event )
859 struct x11drv_win_data *data;
861 if (event->xany.window == x11drv_thread_data()->clip_window)
863 clipping_cursor = 1;
864 return;
866 if (!(data = X11DRV_get_win_data( hwnd ))) return;
867 if (!data->mapped || data->embedded) return;
869 if (!data->managed)
871 HWND hwndFocus = GetFocus();
872 if (hwndFocus && IsChild( hwnd, hwndFocus )) X11DRV_SetFocus(hwndFocus); /* FIXME */
877 /**********************************************************************
878 * X11DRV_UnmapNotify
880 static void X11DRV_UnmapNotify( HWND hwnd, XEvent *event )
882 if (event->xany.window == x11drv_thread_data()->clip_window) clipping_cursor = 0;
886 /***********************************************************************
887 * is_net_wm_state_maximized
889 static BOOL is_net_wm_state_maximized( Display *display, struct x11drv_win_data *data )
891 Atom type, *state;
892 int format, ret = 0;
893 unsigned long i, count, remaining;
895 if (!data->whole_window) return FALSE;
897 wine_tsx11_lock();
898 if (!XGetWindowProperty( display, data->whole_window, x11drv_atom(_NET_WM_STATE), 0,
899 65536/sizeof(CARD32), False, XA_ATOM, &type, &format, &count,
900 &remaining, (unsigned char **)&state ))
902 if (type == XA_ATOM && format == 32)
904 for (i = 0; i < count; i++)
906 if (state[i] == x11drv_atom(_NET_WM_STATE_MAXIMIZED_VERT) ||
907 state[i] == x11drv_atom(_NET_WM_STATE_MAXIMIZED_HORZ))
908 ret++;
911 XFree( state );
913 wine_tsx11_unlock();
914 return (ret == 2);
918 /***********************************************************************
919 * X11DRV_ReparentNotify
921 static void X11DRV_ReparentNotify( HWND hwnd, XEvent *xev )
923 XReparentEvent *event = &xev->xreparent;
924 struct x11drv_win_data *data;
925 HWND parent, old_parent;
926 DWORD style;
928 if (!(data = X11DRV_get_win_data( hwnd ))) return;
929 if (!data->embedded) return;
931 if (data->whole_window)
933 if (event->parent == root_window)
935 TRACE( "%p/%lx reparented to root\n", hwnd, data->whole_window );
936 data->embedder = 0;
937 SendMessageW( hwnd, WM_CLOSE, 0, 0 );
938 return;
940 data->embedder = event->parent;
943 TRACE( "%p/%lx reparented to %lx\n", hwnd, data->whole_window, event->parent );
945 style = GetWindowLongW( hwnd, GWL_STYLE );
946 if (event->parent == root_window)
948 parent = GetDesktopWindow();
949 style = (style & ~WS_CHILD) | WS_POPUP;
951 else
953 if (!(parent = create_foreign_window( event->display, event->parent ))) return;
954 style = (style & ~WS_POPUP) | WS_CHILD;
957 ShowWindow( hwnd, SW_HIDE );
958 old_parent = SetParent( hwnd, parent );
959 SetWindowLongW( hwnd, GWL_STYLE, style );
960 SetWindowPos( hwnd, HWND_TOP, event->x, event->y, 0, 0,
961 SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOCOPYBITS |
962 ((style & WS_VISIBLE) ? SWP_SHOWWINDOW : 0) );
964 /* make old parent destroy itself if it no longer has children */
965 if (old_parent != GetDesktopWindow()) PostMessageW( old_parent, WM_CLOSE, 0, 0 );
969 /***********************************************************************
970 * X11DRV_ConfigureNotify
972 void X11DRV_ConfigureNotify( HWND hwnd, XEvent *xev )
974 XConfigureEvent *event = &xev->xconfigure;
975 struct x11drv_win_data *data;
976 RECT rect;
977 UINT flags;
978 HWND parent;
979 BOOL root_coords;
980 int cx, cy, x = event->x, y = event->y;
982 if (!hwnd) return;
983 if (!(data = X11DRV_get_win_data( hwnd ))) return;
984 if (!data->mapped || data->iconic) return;
985 if (data->whole_window && !data->managed) return;
986 /* ignore synthetic events on foreign windows */
987 if (event->send_event && !data->whole_window) return;
988 if (data->configure_serial && (long)(data->configure_serial - event->serial) > 0)
990 TRACE( "win %p/%lx event %d,%d,%dx%d ignoring old serial %lu/%lu\n",
991 hwnd, data->whole_window, event->x, event->y, event->width, event->height,
992 event->serial, data->configure_serial );
993 return;
996 /* Get geometry */
998 parent = GetAncestor( hwnd, GA_PARENT );
999 root_coords = event->send_event; /* synthetic events are always in root coords */
1001 if (!root_coords && parent == GetDesktopWindow()) /* normal event, map coordinates to the root */
1003 Window child;
1004 wine_tsx11_lock();
1005 XTranslateCoordinates( event->display, event->window, root_window,
1006 0, 0, &x, &y, &child );
1007 wine_tsx11_unlock();
1008 root_coords = TRUE;
1010 rect.left = x;
1011 rect.top = y;
1012 rect.right = x + event->width;
1013 rect.bottom = y + event->height;
1014 if (root_coords) OffsetRect( &rect, virtual_screen_rect.left, virtual_screen_rect.top );
1015 TRACE( "win %p/%lx new X rect %d,%d,%dx%d (event %d,%d,%dx%d)\n",
1016 hwnd, data->whole_window, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top,
1017 event->x, event->y, event->width, event->height );
1019 X11DRV_X_to_window_rect( data, &rect );
1020 if (root_coords) MapWindowPoints( 0, parent, (POINT *)&rect, 2 );
1022 /* Compare what has changed */
1024 x = rect.left;
1025 y = rect.top;
1026 cx = rect.right - rect.left;
1027 cy = rect.bottom - rect.top;
1028 flags = SWP_NOACTIVATE | SWP_NOZORDER;
1030 if (!data->whole_window) flags |= SWP_NOCOPYBITS; /* we can't copy bits of foreign windows */
1032 if (data->window_rect.left == x && data->window_rect.top == y) flags |= SWP_NOMOVE;
1033 else
1034 TRACE( "%p moving from (%d,%d) to (%d,%d)\n",
1035 hwnd, data->window_rect.left, data->window_rect.top, x, y );
1037 if ((data->window_rect.right - data->window_rect.left == cx &&
1038 data->window_rect.bottom - data->window_rect.top == cy) ||
1039 (IsRectEmpty( &data->window_rect ) && event->width == 1 && event->height == 1))
1041 if (flags & SWP_NOMOVE) /* if nothing changed, don't do anything */
1043 TRACE( "Nothing has changed, ignoring event\n" );
1044 return;
1046 flags |= SWP_NOSIZE;
1048 else
1049 TRACE( "%p resizing from (%dx%d) to (%dx%d)\n",
1050 hwnd, data->window_rect.right - data->window_rect.left,
1051 data->window_rect.bottom - data->window_rect.top, cx, cy );
1053 if (is_net_wm_state_maximized( event->display, data ))
1055 if (!IsZoomed( data->hwnd ))
1057 TRACE( "win %p/%lx is maximized\n", data->hwnd, data->whole_window );
1058 SendMessageW( data->hwnd, WM_SYSCOMMAND, SC_MAXIMIZE, 0 );
1059 return;
1062 else
1064 if (IsZoomed( data->hwnd ))
1066 TRACE( "window %p/%lx is no longer maximized\n", data->hwnd, data->whole_window );
1067 SendMessageW( data->hwnd, WM_SYSCOMMAND, SC_RESTORE, 0 );
1068 return;
1072 SetWindowPos( hwnd, 0, x, y, cx, cy, flags );
1076 /**********************************************************************
1077 * X11DRV_GravityNotify
1079 static void X11DRV_GravityNotify( HWND hwnd, XEvent *xev )
1081 XGravityEvent *event = &xev->xgravity;
1082 struct x11drv_win_data *data = X11DRV_get_win_data( hwnd );
1083 RECT rect;
1085 if (!data || data->whole_window) return; /* only handle this for foreign windows */
1087 rect.left = event->x;
1088 rect.top = event->y;
1089 rect.right = rect.left + data->whole_rect.right - data->whole_rect.left;
1090 rect.bottom = rect.top + data->whole_rect.bottom - data->whole_rect.top;
1092 TRACE( "win %p/%lx new X rect %d,%d,%dx%d (event %d,%d)\n",
1093 hwnd, data->whole_window, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top,
1094 event->x, event->y );
1096 X11DRV_X_to_window_rect( data, &rect );
1098 if (data->window_rect.left != rect.left || data ->window_rect.top != rect.top)
1099 SetWindowPos( hwnd, 0, rect.left, rect.top, 0, 0,
1100 SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOCOPYBITS );
1104 /***********************************************************************
1105 * get_window_wm_state
1107 static int get_window_wm_state( Display *display, struct x11drv_win_data *data )
1109 struct
1111 CARD32 state;
1112 XID icon;
1113 } *state;
1114 Atom type;
1115 int format, ret = -1;
1116 unsigned long count, remaining;
1118 wine_tsx11_lock();
1119 if (!XGetWindowProperty( display, data->whole_window, x11drv_atom(WM_STATE), 0,
1120 sizeof(*state)/sizeof(CARD32), False, x11drv_atom(WM_STATE),
1121 &type, &format, &count, &remaining, (unsigned char **)&state ))
1123 if (type == x11drv_atom(WM_STATE) && get_property_size( format, count ) >= sizeof(*state))
1124 ret = state->state;
1125 XFree( state );
1127 wine_tsx11_unlock();
1128 return ret;
1132 /***********************************************************************
1133 * handle_wm_state_notify
1135 * Handle a PropertyNotify for WM_STATE.
1137 static void handle_wm_state_notify( struct x11drv_win_data *data, XPropertyEvent *event,
1138 BOOL update_window )
1140 DWORD style;
1142 switch(event->state)
1144 case PropertyDelete:
1145 TRACE( "%p/%lx: WM_STATE deleted from %d\n", data->hwnd, data->whole_window, data->wm_state );
1146 data->wm_state = WithdrawnState;
1147 break;
1148 case PropertyNewValue:
1150 int old_state = data->wm_state;
1151 int new_state = get_window_wm_state( event->display, data );
1152 if (new_state != -1 && new_state != data->wm_state)
1154 TRACE( "%p/%lx: new WM_STATE %d from %d\n",
1155 data->hwnd, data->whole_window, new_state, old_state );
1156 data->wm_state = new_state;
1157 /* ignore the initial state transition out of withdrawn state */
1158 /* metacity does Withdrawn->NormalState->IconicState when mapping an iconic window */
1159 if (!old_state) return;
1162 break;
1165 if (!update_window || !data->managed || !data->mapped) return;
1167 style = GetWindowLongW( data->hwnd, GWL_STYLE );
1169 if (data->iconic && data->wm_state == NormalState) /* restore window */
1171 data->iconic = FALSE;
1172 if (is_net_wm_state_maximized( event->display, data ))
1174 if ((style & WS_MAXIMIZEBOX) && !(style & WS_DISABLED))
1176 TRACE( "restoring to max %p/%lx\n", data->hwnd, data->whole_window );
1177 SendMessageW( data->hwnd, WM_SYSCOMMAND, SC_MAXIMIZE, 0 );
1179 else TRACE( "not restoring to max win %p/%lx style %08x\n",
1180 data->hwnd, data->whole_window, style );
1182 else if (style & (WS_MINIMIZE | WS_MAXIMIZE))
1184 TRACE( "restoring win %p/%lx\n", data->hwnd, data->whole_window );
1185 SendMessageW( data->hwnd, WM_SYSCOMMAND, SC_RESTORE, 0 );
1187 else TRACE( "not restoring win %p/%lx style %08x\n", data->hwnd, data->whole_window, style );
1189 else if (!data->iconic && data->wm_state == IconicState)
1191 data->iconic = TRUE;
1192 if ((style & WS_MINIMIZEBOX) && !(style & WS_DISABLED))
1194 TRACE( "minimizing win %p/%lx\n", data->hwnd, data->whole_window );
1195 SendMessageW( data->hwnd, WM_SYSCOMMAND, SC_MINIMIZE, 0 );
1197 else TRACE( "not minimizing win %p/%lx style %08x\n", data->hwnd, data->whole_window, style );
1202 /***********************************************************************
1203 * X11DRV_PropertyNotify
1205 static void X11DRV_PropertyNotify( HWND hwnd, XEvent *xev )
1207 XPropertyEvent *event = &xev->xproperty;
1208 struct x11drv_win_data *data;
1210 if (!hwnd) return;
1211 if (!(data = X11DRV_get_win_data( hwnd ))) return;
1213 if (event->atom == x11drv_atom(WM_STATE)) handle_wm_state_notify( data, event, TRUE );
1217 /* event filter to wait for a WM_STATE change notification on a window */
1218 static Bool is_wm_state_notify( Display *display, XEvent *event, XPointer arg )
1220 if (event->xany.window != (Window)arg) return 0;
1221 return (event->type == DestroyNotify ||
1222 (event->type == PropertyNotify && event->xproperty.atom == x11drv_atom(WM_STATE)));
1225 /***********************************************************************
1226 * wait_for_withdrawn_state
1228 void wait_for_withdrawn_state( Display *display, struct x11drv_win_data *data, BOOL set )
1230 DWORD end = GetTickCount() + 2000;
1232 if (!data->managed) return;
1234 TRACE( "waiting for window %p/%lx to become %swithdrawn\n",
1235 data->hwnd, data->whole_window, set ? "" : "not " );
1237 while (data->whole_window && ((data->wm_state == WithdrawnState) == !set))
1239 XEvent event;
1240 int count = 0;
1242 wine_tsx11_lock();
1243 while (XCheckIfEvent( display, &event, is_wm_state_notify, (char *)data->whole_window ))
1245 count++;
1246 if (XFilterEvent( &event, None )) continue; /* filtered, ignore it */
1247 if (event.type == DestroyNotify) call_event_handler( display, &event );
1248 else
1250 wine_tsx11_unlock();
1251 handle_wm_state_notify( data, &event.xproperty, FALSE );
1252 wine_tsx11_lock();
1255 wine_tsx11_unlock();
1257 if (!count)
1259 struct pollfd pfd;
1260 int timeout = end - GetTickCount();
1262 pfd.fd = ConnectionNumber(display);
1263 pfd.events = POLLIN;
1264 if (timeout <= 0 || poll( &pfd, 1, timeout ) != 1)
1266 FIXME( "window %p/%lx wait timed out\n", data->hwnd, data->whole_window );
1267 break;
1271 TRACE( "window %p/%lx state now %d\n", data->hwnd, data->whole_window, data->wm_state );
1275 static HWND find_drop_window( HWND hQueryWnd, LPPOINT lpPt )
1277 RECT tempRect;
1279 if (!IsWindowEnabled(hQueryWnd)) return 0;
1281 GetWindowRect(hQueryWnd, &tempRect);
1283 if(!PtInRect(&tempRect, *lpPt)) return 0;
1285 if (!IsIconic( hQueryWnd ))
1287 POINT pt = *lpPt;
1288 ScreenToClient( hQueryWnd, &pt );
1289 GetClientRect( hQueryWnd, &tempRect );
1291 if (PtInRect( &tempRect, pt))
1293 HWND ret = ChildWindowFromPointEx( hQueryWnd, pt, CWP_SKIPINVISIBLE|CWP_SKIPDISABLED );
1294 if (ret && ret != hQueryWnd)
1296 ret = find_drop_window( ret, lpPt );
1297 if (ret) return ret;
1302 if(!(GetWindowLongA( hQueryWnd, GWL_EXSTYLE ) & WS_EX_ACCEPTFILES)) return 0;
1304 ScreenToClient(hQueryWnd, lpPt);
1306 return hQueryWnd;
1309 /**********************************************************************
1310 * EVENT_DropFromOffix
1312 * don't know if it still works (last Changelog is from 96/11/04)
1314 static void EVENT_DropFromOffiX( HWND hWnd, XClientMessageEvent *event )
1316 struct x11drv_win_data *data;
1317 unsigned long data_length;
1318 unsigned long aux_long;
1319 unsigned char* p_data = NULL;
1320 Atom atom_aux;
1321 int x, y, dummy;
1322 BOOL bAccept;
1323 Window win, w_aux_root, w_aux_child;
1325 win = X11DRV_get_whole_window(hWnd);
1326 wine_tsx11_lock();
1327 XQueryPointer( event->display, win, &w_aux_root, &w_aux_child,
1328 &x, &y, &dummy, &dummy, (unsigned int*)&aux_long);
1329 x += virtual_screen_rect.left;
1330 y += virtual_screen_rect.top;
1331 wine_tsx11_unlock();
1333 if (!(data = X11DRV_get_win_data( hWnd ))) return;
1335 /* find out drop point and drop window */
1336 if( x < 0 || y < 0 ||
1337 x > (data->whole_rect.right - data->whole_rect.left) ||
1338 y > (data->whole_rect.bottom - data->whole_rect.top) )
1340 bAccept = GetWindowLongW( hWnd, GWL_EXSTYLE ) & WS_EX_ACCEPTFILES;
1341 x = 0;
1342 y = 0;
1344 else
1346 POINT pt = { x, y };
1347 HWND hwndDrop = find_drop_window( hWnd, &pt );
1348 if (hwndDrop)
1350 x = pt.x;
1351 y = pt.y;
1352 bAccept = TRUE;
1354 else
1356 bAccept = FALSE;
1360 if (!bAccept) return;
1362 wine_tsx11_lock();
1363 XGetWindowProperty( event->display, DefaultRootWindow(event->display),
1364 x11drv_atom(DndSelection), 0, 65535, FALSE,
1365 AnyPropertyType, &atom_aux, &dummy,
1366 &data_length, &aux_long, &p_data);
1367 wine_tsx11_unlock();
1369 if( !aux_long && p_data) /* don't bother if > 64K */
1371 char *p = (char *)p_data;
1372 char *p_drop;
1374 aux_long = 0;
1375 while( *p ) /* calculate buffer size */
1377 INT len = GetShortPathNameA( p, NULL, 0 );
1378 if (len) aux_long += len + 1;
1379 p += strlen(p) + 1;
1381 if( aux_long && aux_long < 65535 )
1383 HDROP hDrop;
1384 DROPFILES *lpDrop;
1386 aux_long += sizeof(DROPFILES) + 1;
1387 hDrop = GlobalAlloc( GMEM_SHARE, aux_long );
1388 lpDrop = GlobalLock( hDrop );
1390 if( lpDrop )
1392 lpDrop->pFiles = sizeof(DROPFILES);
1393 lpDrop->pt.x = x;
1394 lpDrop->pt.y = y;
1395 lpDrop->fNC = FALSE;
1396 lpDrop->fWide = FALSE;
1397 p_drop = (char *)(lpDrop + 1);
1398 p = (char *)p_data;
1399 while(*p)
1401 if (GetShortPathNameA( p, p_drop, aux_long - (p_drop - (char *)lpDrop) ))
1402 p_drop += strlen( p_drop ) + 1;
1403 p += strlen(p) + 1;
1405 *p_drop = '\0';
1406 PostMessageA( hWnd, WM_DROPFILES, (WPARAM)hDrop, 0L );
1410 wine_tsx11_lock();
1411 if( p_data ) XFree(p_data);
1412 wine_tsx11_unlock();
1415 /**********************************************************************
1416 * EVENT_DropURLs
1418 * drop items are separated by \n
1419 * each item is prefixed by its mime type
1421 * event->data.l[3], event->data.l[4] contains drop x,y position
1423 static void EVENT_DropURLs( HWND hWnd, XClientMessageEvent *event )
1425 struct x11drv_win_data *win_data;
1426 unsigned long data_length;
1427 unsigned long aux_long, drop_len = 0;
1428 unsigned char *p_data = NULL; /* property data */
1429 char *p_drop = NULL;
1430 char *p, *next;
1431 int x, y;
1432 DROPFILES *lpDrop;
1433 HDROP hDrop;
1434 union {
1435 Atom atom_aux;
1436 int i;
1437 Window w_aux;
1438 unsigned int u;
1439 } u; /* unused */
1441 if (!(GetWindowLongW( hWnd, GWL_EXSTYLE ) & WS_EX_ACCEPTFILES)) return;
1443 wine_tsx11_lock();
1444 XGetWindowProperty( event->display, DefaultRootWindow(event->display),
1445 x11drv_atom(DndSelection), 0, 65535, FALSE,
1446 AnyPropertyType, &u.atom_aux, &u.i,
1447 &data_length, &aux_long, &p_data);
1448 wine_tsx11_unlock();
1449 if (aux_long)
1450 WARN("property too large, truncated!\n");
1451 TRACE("urls=%s\n", p_data);
1453 if( !aux_long && p_data) { /* don't bother if > 64K */
1454 /* calculate length */
1455 p = (char*) p_data;
1456 next = strchr(p, '\n');
1457 while (p) {
1458 if (next) *next=0;
1459 if (strncmp(p,"file:",5) == 0 ) {
1460 INT len = GetShortPathNameA( p+5, NULL, 0 );
1461 if (len) drop_len += len + 1;
1463 if (next) {
1464 *next = '\n';
1465 p = next + 1;
1466 next = strchr(p, '\n');
1467 } else {
1468 p = NULL;
1472 if( drop_len && drop_len < 65535 ) {
1473 wine_tsx11_lock();
1474 XQueryPointer( event->display, root_window, &u.w_aux, &u.w_aux,
1475 &x, &y, &u.i, &u.i, &u.u);
1476 x += virtual_screen_rect.left;
1477 y += virtual_screen_rect.top;
1478 wine_tsx11_unlock();
1480 drop_len += sizeof(DROPFILES) + 1;
1481 hDrop = GlobalAlloc( GMEM_SHARE, drop_len );
1482 lpDrop = GlobalLock( hDrop );
1484 if( lpDrop && (win_data = X11DRV_get_win_data( hWnd )))
1486 lpDrop->pFiles = sizeof(DROPFILES);
1487 lpDrop->pt.x = x;
1488 lpDrop->pt.y = y;
1489 lpDrop->fNC =
1490 ( x < (win_data->client_rect.left - win_data->whole_rect.left) ||
1491 y < (win_data->client_rect.top - win_data->whole_rect.top) ||
1492 x > (win_data->client_rect.right - win_data->whole_rect.left) ||
1493 y > (win_data->client_rect.bottom - win_data->whole_rect.top) );
1494 lpDrop->fWide = FALSE;
1495 p_drop = (char*)(lpDrop + 1);
1498 /* create message content */
1499 if (p_drop) {
1500 p = (char*) p_data;
1501 next = strchr(p, '\n');
1502 while (p) {
1503 if (next) *next=0;
1504 if (strncmp(p,"file:",5) == 0 ) {
1505 INT len = GetShortPathNameA( p+5, p_drop, 65535 );
1506 if (len) {
1507 TRACE("drop file %s as %s\n", p+5, p_drop);
1508 p_drop += len+1;
1509 } else {
1510 WARN("can't convert file %s to dos name\n", p+5);
1512 } else {
1513 WARN("unknown mime type %s\n", p);
1515 if (next) {
1516 *next = '\n';
1517 p = next + 1;
1518 next = strchr(p, '\n');
1519 } else {
1520 p = NULL;
1522 *p_drop = '\0';
1525 GlobalUnlock(hDrop);
1526 PostMessageA( hWnd, WM_DROPFILES, (WPARAM)hDrop, 0L );
1529 wine_tsx11_lock();
1530 if( p_data ) XFree(p_data);
1531 wine_tsx11_unlock();
1536 /**********************************************************************
1537 * handle_xembed_protocol
1539 static void handle_xembed_protocol( HWND hwnd, XClientMessageEvent *event )
1541 struct x11drv_win_data *data = X11DRV_get_win_data( hwnd );
1543 if (!data) return;
1545 switch (event->data.l[1])
1547 case XEMBED_EMBEDDED_NOTIFY:
1548 TRACE( "win %p/%lx XEMBED_EMBEDDED_NOTIFY owner %lx\n", hwnd, event->window, event->data.l[3] );
1549 data->embedder = event->data.l[3];
1550 break;
1551 default:
1552 TRACE( "win %p/%lx XEMBED message %lu(%lu)\n",
1553 hwnd, event->window, event->data.l[1], event->data.l[2] );
1554 break;
1559 /**********************************************************************
1560 * handle_dnd_protocol
1562 static void handle_dnd_protocol( HWND hwnd, XClientMessageEvent *event )
1564 Window root, child;
1565 int root_x, root_y, child_x, child_y;
1566 unsigned int u;
1568 /* query window (drag&drop event contains only drag window) */
1569 wine_tsx11_lock();
1570 XQueryPointer( event->display, root_window, &root, &child,
1571 &root_x, &root_y, &child_x, &child_y, &u);
1572 if (XFindContext( event->display, child, winContext, (char **)&hwnd ) != 0) hwnd = 0;
1573 wine_tsx11_unlock();
1574 if (!hwnd) return;
1575 if (event->data.l[0] == DndFile || event->data.l[0] == DndFiles)
1576 EVENT_DropFromOffiX(hwnd, event);
1577 else if (event->data.l[0] == DndURL)
1578 EVENT_DropURLs(hwnd, event);
1582 struct client_message_handler
1584 int atom; /* protocol atom */
1585 void (*handler)(HWND, XClientMessageEvent *); /* corresponding handler function */
1588 static const struct client_message_handler client_messages[] =
1590 { XATOM_MANAGER, handle_manager_message },
1591 { XATOM_WM_PROTOCOLS, handle_wm_protocols },
1592 { XATOM__XEMBED, handle_xembed_protocol },
1593 { XATOM_DndProtocol, handle_dnd_protocol },
1594 { XATOM_XdndEnter, X11DRV_XDND_EnterEvent },
1595 { XATOM_XdndPosition, X11DRV_XDND_PositionEvent },
1596 { XATOM_XdndDrop, X11DRV_XDND_DropEvent },
1597 { XATOM_XdndLeave, X11DRV_XDND_LeaveEvent }
1601 /**********************************************************************
1602 * X11DRV_ClientMessage
1604 static void X11DRV_ClientMessage( HWND hwnd, XEvent *xev )
1606 XClientMessageEvent *event = &xev->xclient;
1607 unsigned int i;
1609 if (!hwnd) return;
1611 if (event->format != 32)
1613 WARN( "Don't know how to handle format %d\n", event->format );
1614 return;
1617 for (i = 0; i < sizeof(client_messages)/sizeof(client_messages[0]); i++)
1619 if (event->message_type == X11DRV_Atoms[client_messages[i].atom - FIRST_XATOM])
1621 client_messages[i].handler( hwnd, event );
1622 return;
1625 TRACE( "no handler found for %ld\n", event->message_type );