winex11: Rely on PropertyNotify events instead of Map/UnmapNotify to detect iconifica...
[wine.git] / dlls / winex11.drv / event.c
blobc57d497e81cf7c0d8b7f790690e8365f6f90f3b1
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>
36 #include <assert.h>
37 #include <stdarg.h>
38 #include <string.h>
40 #define NONAMELESSUNION
41 #define NONAMELESSSTRUCT
42 #include "windef.h"
43 #include "winbase.h"
44 #include "winuser.h"
45 #include "wingdi.h"
47 #include "x11drv.h"
49 /* avoid conflict with field names in included win32 headers */
50 #undef Status
51 #include "shlobj.h" /* DROPFILES */
52 #include "shellapi.h"
54 #include "wine/debug.h"
56 WINE_DEFAULT_DEBUG_CHANNEL(event);
58 extern BOOL ximInComposeMode;
60 #define DndNotDnd -1 /* OffiX drag&drop */
61 #define DndUnknown 0
62 #define DndRawData 1
63 #define DndFile 2
64 #define DndFiles 3
65 #define DndText 4
66 #define DndDir 5
67 #define DndLink 6
68 #define DndExe 7
70 #define DndEND 8
72 #define DndURL 128 /* KDE drag&drop */
74 /* Event handlers */
75 static void EVENT_FocusIn( HWND hwnd, XEvent *event );
76 static void EVENT_FocusOut( HWND hwnd, XEvent *event );
77 static void EVENT_PropertyNotify( HWND hwnd, XEvent *event );
78 static void EVENT_ClientMessage( HWND hwnd, XEvent *event );
80 struct event_handler
82 int type; /* event type */
83 x11drv_event_handler handler; /* corresponding handler function */
86 #define MAX_EVENT_HANDLERS 64
88 static struct event_handler handlers[MAX_EVENT_HANDLERS] =
90 /* list must be sorted by event type */
91 { KeyPress, X11DRV_KeyEvent },
92 { KeyRelease, X11DRV_KeyEvent },
93 { ButtonPress, X11DRV_ButtonPress },
94 { ButtonRelease, X11DRV_ButtonRelease },
95 { MotionNotify, X11DRV_MotionNotify },
96 { EnterNotify, X11DRV_EnterNotify },
97 /* LeaveNotify */
98 { FocusIn, EVENT_FocusIn },
99 { FocusOut, EVENT_FocusOut },
100 { KeymapNotify, X11DRV_KeymapNotify },
101 { Expose, X11DRV_Expose },
102 /* GraphicsExpose */
103 /* NoExpose */
104 /* VisibilityNotify */
105 /* CreateNotify */
106 { DestroyNotify, X11DRV_DestroyNotify },
107 /* UnmapNotify */
108 { MapNotify, X11DRV_MapNotify },
109 /* MapRequest */
110 /* ReparentNotify */
111 { ConfigureNotify, X11DRV_ConfigureNotify },
112 /* ConfigureRequest */
113 /* GravityNotify */
114 /* ResizeRequest */
115 /* CirculateNotify */
116 /* CirculateRequest */
117 { PropertyNotify, EVENT_PropertyNotify },
118 { SelectionClear, X11DRV_SelectionClear },
119 { SelectionRequest, X11DRV_SelectionRequest },
120 /* SelectionNotify */
121 /* ColormapNotify */
122 { ClientMessage, EVENT_ClientMessage },
123 { MappingNotify, X11DRV_MappingNotify },
126 static int nb_event_handlers = 18; /* change this if you add handlers above */
129 /* return the name of an X event */
130 static const char *dbgstr_event( int type )
132 static const char * const event_names[] =
134 "KeyPress", "KeyRelease", "ButtonPress", "ButtonRelease",
135 "MotionNotify", "EnterNotify", "LeaveNotify", "FocusIn", "FocusOut",
136 "KeymapNotify", "Expose", "GraphicsExpose", "NoExpose", "VisibilityNotify",
137 "CreateNotify", "DestroyNotify", "UnmapNotify", "MapNotify", "MapRequest",
138 "ReparentNotify", "ConfigureNotify", "ConfigureRequest", "GravityNotify",
139 "ResizeRequest", "CirculateNotify", "CirculateRequest", "PropertyNotify",
140 "SelectionClear", "SelectionRequest", "SelectionNotify", "ColormapNotify",
141 "ClientMessage", "MappingNotify"
144 if (type >= KeyPress && type <= MappingNotify) return event_names[type - KeyPress];
145 return wine_dbg_sprintf( "Extension event %d", type );
149 /***********************************************************************
150 * find_handler
152 * Find the handler for a given event type. Caller must hold the x11 lock.
154 static inline x11drv_event_handler find_handler( int type )
156 int min = 0, max = nb_event_handlers - 1;
158 while (min <= max)
160 int pos = (min + max) / 2;
161 if (handlers[pos].type == type) return handlers[pos].handler;
162 if (handlers[pos].type > type) max = pos - 1;
163 else min = pos + 1;
165 return NULL;
169 /***********************************************************************
170 * X11DRV_register_event_handler
172 * Register a handler for a given event type.
173 * If already registered, overwrite the previous handler.
175 void X11DRV_register_event_handler( int type, x11drv_event_handler handler )
177 int min, max;
179 wine_tsx11_lock();
180 min = 0;
181 max = nb_event_handlers - 1;
182 while (min <= max)
184 int pos = (min + max) / 2;
185 if (handlers[pos].type == type)
187 handlers[pos].handler = handler;
188 goto done;
190 if (handlers[pos].type > type) max = pos - 1;
191 else min = pos + 1;
193 /* insert it between max and min */
194 memmove( &handlers[min+1], &handlers[min], (nb_event_handlers - min) * sizeof(handlers[0]) );
195 handlers[min].type = type;
196 handlers[min].handler = handler;
197 nb_event_handlers++;
198 assert( nb_event_handlers <= MAX_EVENT_HANDLERS );
199 done:
200 wine_tsx11_unlock();
201 TRACE("registered handler %p for event %d count %d\n", handler, type, nb_event_handlers );
205 /***********************************************************************
206 * filter_event
208 static Bool filter_event( Display *display, XEvent *event, char *arg )
210 ULONG_PTR mask = (ULONG_PTR)arg;
212 if ((mask & QS_ALLINPUT) == QS_ALLINPUT) return 1;
214 switch(event->type)
216 case KeyPress:
217 case KeyRelease:
218 case KeymapNotify:
219 case MappingNotify:
220 return (mask & QS_KEY) != 0;
221 case ButtonPress:
222 case ButtonRelease:
223 return (mask & QS_MOUSEBUTTON) != 0;
224 case MotionNotify:
225 case EnterNotify:
226 case LeaveNotify:
227 return (mask & QS_MOUSEMOVE) != 0;
228 case Expose:
229 return (mask & QS_PAINT) != 0;
230 case FocusIn:
231 case FocusOut:
232 case MapNotify:
233 case UnmapNotify:
234 case ConfigureNotify:
235 case PropertyNotify:
236 case ClientMessage:
237 return (mask & QS_POSTMESSAGE) != 0;
238 default:
239 return (mask & QS_SENDMESSAGE) != 0;
244 enum event_merge_action
246 MERGE_DISCARD, /* discard the old event */
247 MERGE_HANDLE, /* handle the old event */
248 MERGE_KEEP /* keep the old event for future merging */
251 /***********************************************************************
252 * merge_events
254 * Try to merge 2 consecutive events.
256 static enum event_merge_action merge_events( XEvent *prev, XEvent *next )
258 switch (prev->type)
260 case ConfigureNotify:
261 switch (next->type)
263 case ConfigureNotify:
264 if (prev->xany.window == next->xany.window)
266 TRACE( "discarding duplicate ConfigureNotify for window %lx\n", prev->xany.window );
267 return MERGE_DISCARD;
269 break;
270 case Expose:
271 case PropertyNotify:
272 return MERGE_KEEP;
274 break;
275 case MotionNotify:
276 if (prev->xany.window == next->xany.window && next->type == MotionNotify)
278 TRACE( "discarding duplicate MotionNotify for window %lx\n", prev->xany.window );
279 return MERGE_DISCARD;
281 break;
283 return MERGE_HANDLE;
287 /***********************************************************************
288 * call_event_handler
290 static inline void call_event_handler( Display *display, XEvent *event )
292 HWND hwnd;
293 x11drv_event_handler handler;
294 XEvent *prev;
295 struct x11drv_thread_data *thread_data;
297 if (!(handler = find_handler( event->type )))
299 TRACE( "%s for win %lx, ignoring\n", dbgstr_event( event->type ), event->xany.window );
300 return; /* no handler, ignore it */
303 if (XFindContext( display, event->xany.window, winContext, (char **)&hwnd ) != 0)
304 hwnd = 0; /* not for a registered window */
305 if (!hwnd && event->xany.window == root_window) hwnd = GetDesktopWindow();
307 TRACE( "%s for hwnd/window %p/%lx\n",
308 dbgstr_event( event->type ), hwnd, event->xany.window );
309 wine_tsx11_unlock();
310 thread_data = x11drv_thread_data();
311 prev = thread_data->current_event;
312 thread_data->current_event = event;
313 handler( hwnd, event );
314 thread_data->current_event = prev;
315 wine_tsx11_lock();
319 /***********************************************************************
320 * process_events
322 static int process_events( Display *display, Bool (*filter)(), ULONG_PTR arg )
324 XEvent event, prev_event;
325 int count = 0;
326 enum event_merge_action action = MERGE_DISCARD;
328 prev_event.type = 0;
329 wine_tsx11_lock();
330 while (XCheckIfEvent( display, &event, filter, (char *)arg ))
332 count++;
333 if (XFilterEvent( &event, None )) continue; /* filtered, ignore it */
334 if (prev_event.type) action = merge_events( &prev_event, &event );
335 switch( action )
337 case MERGE_DISCARD: /* discard prev, keep new */
338 prev_event = event;
339 break;
340 case MERGE_HANDLE: /* handle prev, keep new */
341 call_event_handler( display, &prev_event );
342 prev_event = event;
343 break;
344 case MERGE_KEEP: /* handle new, keep prev for future merging */
345 call_event_handler( display, &event );
346 break;
349 XFlush( gdi_display );
350 if (prev_event.type) call_event_handler( display, &prev_event );
351 wine_tsx11_unlock();
352 if (count) TRACE( "processed %d events\n", count );
353 return count;
357 /***********************************************************************
358 * MsgWaitForMultipleObjectsEx (X11DRV.@)
360 DWORD X11DRV_MsgWaitForMultipleObjectsEx( DWORD count, const HANDLE *handles,
361 DWORD timeout, DWORD mask, DWORD flags )
363 DWORD ret;
364 struct x11drv_thread_data *data = TlsGetValue( thread_data_tls_index );
366 if (!data)
368 if (!count && !timeout) return WAIT_TIMEOUT;
369 return WaitForMultipleObjectsEx( count, handles, flags & MWMO_WAITALL,
370 timeout, flags & MWMO_ALERTABLE );
373 if (data->current_event) mask = 0; /* don't process nested events */
375 if (process_events( data->display, filter_event, mask )) ret = count - 1;
376 else if (count || timeout)
378 ret = WaitForMultipleObjectsEx( count, handles, flags & MWMO_WAITALL,
379 timeout, flags & MWMO_ALERTABLE );
380 if (ret == count - 1) process_events( data->display, filter_event, mask );
382 else ret = WAIT_TIMEOUT;
384 return ret;
387 /***********************************************************************
388 * EVENT_x11_time_to_win32_time
390 * Make our timer and the X timer line up as best we can
391 * Pass 0 to retrieve the current adjustment value (times -1)
393 DWORD EVENT_x11_time_to_win32_time(Time time)
395 static DWORD adjust = 0;
396 DWORD now = GetTickCount();
397 DWORD ret;
399 if (! adjust && time != 0)
401 ret = now;
402 adjust = time - now;
404 else
406 /* If we got an event in the 'future', then our clock is clearly wrong.
407 If we got it more than 10000 ms in the future, then it's most likely
408 that the clock has wrapped. */
410 ret = time - adjust;
411 if (ret > now && ((ret - now) < 10000) && time != 0)
413 adjust += ret - now;
414 ret -= ret - now;
418 return ret;
422 /*******************************************************************
423 * can_activate_window
425 * Check if we can activate the specified window.
427 static inline BOOL can_activate_window( HWND hwnd )
429 LONG style = GetWindowLongW( hwnd, GWL_STYLE );
430 if (!(style & WS_VISIBLE)) return FALSE;
431 if ((style & (WS_POPUP|WS_CHILD)) == WS_CHILD) return FALSE;
432 if (hwnd == GetDesktopWindow()) return FALSE;
433 return !(style & WS_DISABLED);
437 /**********************************************************************
438 * set_focus
440 static void set_focus( HWND hwnd, Time time )
442 HWND focus;
443 Window win;
445 TRACE( "setting foreground window to %p\n", hwnd );
446 SetForegroundWindow( hwnd );
448 focus = GetFocus();
449 if (focus) focus = GetAncestor( focus, GA_ROOT );
450 win = X11DRV_get_whole_window(focus);
452 if (win)
454 TRACE( "setting focus to %p (%lx) time=%ld\n", focus, win, time );
455 wine_tsx11_lock();
456 XSetInputFocus( thread_display(), win, RevertToParent, time );
457 wine_tsx11_unlock();
462 /**********************************************************************
463 * handle_wm_protocols
465 static void handle_wm_protocols( HWND hwnd, XClientMessageEvent *event )
467 Atom protocol = (Atom)event->data.l[0];
469 if (!protocol) return;
471 if (protocol == x11drv_atom(WM_DELETE_WINDOW))
473 /* Ignore the delete window request if the window has been disabled
474 * and we are in managed mode. This is to disallow applications from
475 * being closed by the window manager while in a modal state.
477 if (IsWindowEnabled(hwnd))
479 HMENU hSysMenu;
481 if (GetClassLongW(hwnd, GCL_STYLE) & CS_NOCLOSE) return;
482 hSysMenu = GetSystemMenu(hwnd, FALSE);
483 if (hSysMenu)
485 UINT state = GetMenuState(hSysMenu, SC_CLOSE, MF_BYCOMMAND);
486 if (state == 0xFFFFFFFF || (state & (MF_DISABLED | MF_GRAYED)))
487 return;
489 if (GetActiveWindow() != hwnd)
491 LRESULT ma = SendMessageW( hwnd, WM_MOUSEACTIVATE,
492 (WPARAM)GetAncestor( hwnd, GA_ROOT ),
493 MAKELONG(HTCLOSE,WM_LBUTTONDOWN) );
494 switch(ma)
496 case MA_NOACTIVATEANDEAT:
497 case MA_ACTIVATEANDEAT:
498 return;
499 case MA_NOACTIVATE:
500 break;
501 case MA_ACTIVATE:
502 case 0:
503 SetActiveWindow(hwnd);
504 break;
505 default:
506 WARN( "unknown WM_MOUSEACTIVATE code %d\n", (int) ma );
507 break;
510 PostMessageW( hwnd, WM_X11DRV_DELETE_WINDOW, 0, 0 );
513 else if (protocol == x11drv_atom(WM_TAKE_FOCUS))
515 Time event_time = (Time)event->data.l[1];
516 HWND last_focus = x11drv_thread_data()->last_focus;
518 TRACE( "got take focus msg for %p, enabled=%d, visible=%d (style %08x), focus=%p, active=%p, fg=%p, last=%p\n",
519 hwnd, IsWindowEnabled(hwnd), IsWindowVisible(hwnd), GetWindowLongW(hwnd, GWL_STYLE),
520 GetFocus(), GetActiveWindow(), GetForegroundWindow(), last_focus );
522 if (can_activate_window(hwnd))
524 /* simulate a mouse click on the caption to find out
525 * whether the window wants to be activated */
526 LRESULT ma = SendMessageW( hwnd, WM_MOUSEACTIVATE,
527 (WPARAM)GetAncestor( hwnd, GA_ROOT ),
528 MAKELONG(HTCAPTION,WM_LBUTTONDOWN) );
529 if (ma != MA_NOACTIVATEANDEAT && ma != MA_NOACTIVATE)
531 set_focus( hwnd, event_time );
532 return;
535 /* try to find some other window to give the focus to */
536 hwnd = GetFocus();
537 if (hwnd) hwnd = GetAncestor( hwnd, GA_ROOT );
538 if (!hwnd) hwnd = GetActiveWindow();
539 if (!hwnd) hwnd = last_focus;
540 if (hwnd && can_activate_window(hwnd)) set_focus( hwnd, event_time );
542 else if (protocol == x11drv_atom(_NET_WM_PING))
544 XClientMessageEvent xev;
545 xev = *event;
547 TRACE("NET_WM Ping\n");
548 wine_tsx11_lock();
549 xev.window = DefaultRootWindow(xev.display);
550 XSendEvent(xev.display, xev.window, False, SubstructureRedirectMask | SubstructureNotifyMask, (XEvent*)&xev);
551 wine_tsx11_unlock();
552 /* this line is semi-stolen from gtk2 */
553 TRACE("NET_WM Pong\n");
558 static const char * const focus_details[] =
560 "NotifyAncestor",
561 "NotifyVirtual",
562 "NotifyInferior",
563 "NotifyNonlinear",
564 "NotifyNonlinearVirtual",
565 "NotifyPointer",
566 "NotifyPointerRoot",
567 "NotifyDetailNone"
570 /**********************************************************************
571 * EVENT_FocusIn
573 static void EVENT_FocusIn( HWND hwnd, XEvent *xev )
575 XFocusChangeEvent *event = &xev->xfocus;
576 XIC xic;
578 if (!hwnd) return;
580 TRACE( "win %p xwin %lx detail=%s\n", hwnd, event->window, focus_details[event->detail] );
582 if (event->detail == NotifyPointer) return;
584 if ((xic = X11DRV_get_ic( hwnd )))
586 wine_tsx11_lock();
587 XSetICFocus( xic );
588 wine_tsx11_unlock();
590 if (use_take_focus) return; /* ignore FocusIn if we are using take focus */
592 if (!can_activate_window(hwnd))
594 HWND hwnd = GetFocus();
595 if (hwnd) hwnd = GetAncestor( hwnd, GA_ROOT );
596 if (!hwnd) hwnd = GetActiveWindow();
597 if (!hwnd) hwnd = x11drv_thread_data()->last_focus;
598 if (hwnd && can_activate_window(hwnd)) set_focus( hwnd, CurrentTime );
600 else SetForegroundWindow( hwnd );
604 /**********************************************************************
605 * EVENT_FocusOut
607 * Note: only top-level windows get FocusOut events.
609 static void EVENT_FocusOut( HWND hwnd, XEvent *xev )
611 XFocusChangeEvent *event = &xev->xfocus;
612 HWND hwnd_tmp;
613 Window focus_win;
614 int revert;
615 XIC xic;
617 if (!hwnd) return;
619 TRACE( "win %p xwin %lx detail=%s\n", hwnd, event->window, focus_details[event->detail] );
621 if (event->detail == NotifyPointer) return;
622 if (ximInComposeMode) return;
624 x11drv_thread_data()->last_focus = hwnd;
625 if ((xic = X11DRV_get_ic( hwnd )))
627 wine_tsx11_lock();
628 XUnsetICFocus( xic );
629 wine_tsx11_unlock();
631 if (hwnd != GetForegroundWindow()) return;
632 SendMessageW( hwnd, WM_CANCELMODE, 0, 0 );
634 /* don't reset the foreground window, if the window which is
635 getting the focus is a Wine window */
637 wine_tsx11_lock();
638 XGetInputFocus( thread_display(), &focus_win, &revert );
639 if (focus_win)
641 if (XFindContext( thread_display(), focus_win, winContext, (char **)&hwnd_tmp ) != 0)
642 focus_win = 0;
644 wine_tsx11_unlock();
646 if (!focus_win)
648 /* Abey : 6-Oct-99. Check again if the focus out window is the
649 Foreground window, because in most cases the messages sent
650 above must have already changed the foreground window, in which
651 case we don't have to change the foreground window to 0 */
652 if (hwnd == GetForegroundWindow())
654 TRACE( "lost focus, setting fg to desktop\n" );
655 SetForegroundWindow( GetDesktopWindow() );
661 /***********************************************************************
662 * get_window_wm_state
664 int get_window_wm_state( Display *display, struct x11drv_win_data *data )
666 struct
668 CARD32 state;
669 XID icon;
670 } *state;
671 Atom type;
672 int format, ret = -1;
673 unsigned long count, remaining;
675 wine_tsx11_lock();
676 if (!XGetWindowProperty( display, data->whole_window, x11drv_atom(WM_STATE), 0,
677 sizeof(*state)/sizeof(CARD32), False, x11drv_atom(WM_STATE),
678 &type, &format, &count, &remaining, (unsigned char **)&state ))
680 if (type == x11drv_atom(WM_STATE) && format && count >= sizeof(*state)/(format/8))
681 ret = state->state;
682 XFree( state );
684 wine_tsx11_unlock();
685 return ret;
689 /***********************************************************************
690 * handle_wm_state_notify
692 * Handle a PropertyNotify for WM_STATE.
694 static void handle_wm_state_notify( struct x11drv_win_data *data, XPropertyEvent *event,
695 BOOL update_window )
697 switch(event->state)
699 case PropertyDelete:
700 data->wm_state = WithdrawnState;
701 TRACE( "%p/%lx: WM_STATE deleted\n", data->hwnd, data->whole_window );
702 break;
703 case PropertyNewValue:
705 int new_state = get_window_wm_state( event->display, data );
706 if (new_state != -1 && new_state != data->wm_state)
708 TRACE( "%p/%lx: new WM_STATE %d\n", data->hwnd, data->whole_window, new_state );
709 data->wm_state = new_state;
712 break;
715 if (!update_window || !data->managed || !data->mapped) return;
717 if (data->iconic && data->wm_state == NormalState) /* restore window */
719 int x, y;
720 unsigned int width, height, border, depth;
721 Window root, top;
722 WINDOWPLACEMENT wp;
723 RECT rect;
725 /* FIXME: hack */
726 wine_tsx11_lock();
727 XGetGeometry( event->display, data->whole_window, &root, &x, &y, &width, &height,
728 &border, &depth );
729 XTranslateCoordinates( event->display, data->whole_window, root, 0, 0, &x, &y, &top );
730 wine_tsx11_unlock();
731 rect.left = x;
732 rect.top = y;
733 rect.right = x + width;
734 rect.bottom = y + height;
735 OffsetRect( &rect, virtual_screen_rect.left, virtual_screen_rect.top );
736 X11DRV_X_to_window_rect( data, &rect );
738 wp.length = sizeof(wp);
739 GetWindowPlacement( data->hwnd, &wp );
740 wp.flags = 0;
741 wp.showCmd = SW_RESTORE;
742 wp.rcNormalPosition = rect;
744 TRACE( "restoring win %p/%lx\n", data->hwnd, data->whole_window );
745 data->iconic = FALSE;
746 data->lock_changes++;
747 SetWindowPlacement( data->hwnd, &wp );
748 data->lock_changes--;
750 else if (!data->iconic && data->wm_state == IconicState)
752 TRACE( "minimizing win %p/%lx\n", data->hwnd, data->whole_window );
753 data->iconic = TRUE;
754 data->lock_changes++;
755 ShowWindow( data->hwnd, SW_MINIMIZE );
756 data->lock_changes--;
761 /***********************************************************************
762 * EVENT_PropertyNotify
764 static void EVENT_PropertyNotify( HWND hwnd, XEvent *xev )
766 XPropertyEvent *event = &xev->xproperty;
767 struct x11drv_win_data *data;
769 if (!hwnd) return;
770 if (!(data = X11DRV_get_win_data( hwnd ))) return;
772 if (event->atom == x11drv_atom(WM_STATE)) handle_wm_state_notify( data, event, TRUE );
776 /* event filter to wait for a WM_STATE change notification on a window */
777 static Bool is_wm_state_notify( Display *display, XEvent *event, XPointer arg )
779 if (event->xany.window != (Window)arg) return 0;
780 return (event->type == DestroyNotify ||
781 (event->type == PropertyNotify && event->xproperty.atom == x11drv_atom(WM_STATE)));
784 /***********************************************************************
785 * wait_for_withdrawn_state
787 void wait_for_withdrawn_state( Display *display, struct x11drv_win_data *data, BOOL set )
789 DWORD end = GetTickCount() + 2000;
791 if (!data->managed) return;
793 TRACE( "waiting for window %p/%lx to become %swithdrawn\n",
794 data->hwnd, data->whole_window, set ? "" : "not " );
796 while (data->whole_window && ((data->wm_state == WithdrawnState) == !set))
798 XEvent event;
799 int count = 0;
801 wine_tsx11_lock();
802 while (XCheckIfEvent( display, &event, is_wm_state_notify, (char *)data->whole_window ))
804 count++;
805 if (XFilterEvent( &event, None )) continue; /* filtered, ignore it */
806 if (event.type == DestroyNotify) call_event_handler( display, &event );
807 else
809 wine_tsx11_unlock();
810 handle_wm_state_notify( data, &event.xproperty, FALSE );
811 wine_tsx11_lock();
814 wine_tsx11_unlock();
816 if (!count)
818 struct pollfd pfd;
819 int timeout = end - GetTickCount();
821 pfd.fd = ConnectionNumber(display);
822 pfd.events = POLLIN;
823 if (timeout <= 0 || poll( &pfd, 1, timeout ) != 1)
825 FIXME( "window %p/%lx wait timed out\n", data->hwnd, data->whole_window );
826 break;
830 TRACE( "window %p/%lx state now %d\n", data->hwnd, data->whole_window, data->wm_state );
834 static HWND find_drop_window( HWND hQueryWnd, LPPOINT lpPt )
836 RECT tempRect;
838 if (!IsWindowEnabled(hQueryWnd)) return 0;
840 GetWindowRect(hQueryWnd, &tempRect);
842 if(!PtInRect(&tempRect, *lpPt)) return 0;
844 if (!IsIconic( hQueryWnd ))
846 POINT pt = *lpPt;
847 ScreenToClient( hQueryWnd, &pt );
848 GetClientRect( hQueryWnd, &tempRect );
850 if (PtInRect( &tempRect, pt))
852 HWND ret = ChildWindowFromPointEx( hQueryWnd, pt, CWP_SKIPINVISIBLE|CWP_SKIPDISABLED );
853 if (ret && ret != hQueryWnd)
855 ret = find_drop_window( ret, lpPt );
856 if (ret) return ret;
861 if(!(GetWindowLongA( hQueryWnd, GWL_EXSTYLE ) & WS_EX_ACCEPTFILES)) return 0;
863 ScreenToClient(hQueryWnd, lpPt);
865 return hQueryWnd;
868 /**********************************************************************
869 * EVENT_DropFromOffix
871 * don't know if it still works (last Changelog is from 96/11/04)
873 static void EVENT_DropFromOffiX( HWND hWnd, XClientMessageEvent *event )
875 struct x11drv_win_data *data;
876 unsigned long data_length;
877 unsigned long aux_long;
878 unsigned char* p_data = NULL;
879 Atom atom_aux;
880 int x, y, dummy;
881 BOOL bAccept;
882 Window win, w_aux_root, w_aux_child;
883 HWND hScope = hWnd;
885 win = X11DRV_get_whole_window(hWnd);
886 wine_tsx11_lock();
887 XQueryPointer( event->display, win, &w_aux_root, &w_aux_child,
888 &x, &y, &dummy, &dummy, (unsigned int*)&aux_long);
889 x += virtual_screen_rect.left;
890 y += virtual_screen_rect.top;
891 wine_tsx11_unlock();
893 if (!(data = X11DRV_get_win_data( hWnd ))) return;
895 /* find out drop point and drop window */
896 if( x < 0 || y < 0 ||
897 x > (data->whole_rect.right - data->whole_rect.left) ||
898 y > (data->whole_rect.bottom - data->whole_rect.top) )
900 bAccept = GetWindowLongW( hWnd, GWL_EXSTYLE ) & WS_EX_ACCEPTFILES;
901 x = 0;
902 y = 0;
904 else
906 POINT pt = { x, y };
907 HWND hwndDrop = find_drop_window( hWnd, &pt );
908 if (hwndDrop)
910 x = pt.x;
911 y = pt.y;
912 hScope = hwndDrop;
913 bAccept = TRUE;
915 else
917 bAccept = FALSE;
921 if (!bAccept) return;
923 wine_tsx11_lock();
924 XGetWindowProperty( event->display, DefaultRootWindow(event->display),
925 x11drv_atom(DndSelection), 0, 65535, FALSE,
926 AnyPropertyType, &atom_aux, &dummy,
927 &data_length, &aux_long, &p_data);
928 wine_tsx11_unlock();
930 if( !aux_long && p_data) /* don't bother if > 64K */
932 char *p = (char *)p_data;
933 char *p_drop;
935 aux_long = 0;
936 while( *p ) /* calculate buffer size */
938 INT len = GetShortPathNameA( p, NULL, 0 );
939 if (len) aux_long += len + 1;
940 p += strlen(p) + 1;
942 if( aux_long && aux_long < 65535 )
944 HDROP hDrop;
945 DROPFILES *lpDrop;
947 aux_long += sizeof(DROPFILES) + 1;
948 hDrop = GlobalAlloc( GMEM_SHARE, aux_long );
949 lpDrop = (DROPFILES*)GlobalLock( hDrop );
951 if( lpDrop )
953 lpDrop->pFiles = sizeof(DROPFILES);
954 lpDrop->pt.x = x;
955 lpDrop->pt.y = y;
956 lpDrop->fNC = FALSE;
957 lpDrop->fWide = FALSE;
958 p_drop = (char *)(lpDrop + 1);
959 p = (char *)p_data;
960 while(*p)
962 if (GetShortPathNameA( p, p_drop, aux_long - (p_drop - (char *)lpDrop) ))
963 p_drop += strlen( p_drop ) + 1;
964 p += strlen(p) + 1;
966 *p_drop = '\0';
967 PostMessageA( hWnd, WM_DROPFILES, (WPARAM)hDrop, 0L );
971 wine_tsx11_lock();
972 if( p_data ) XFree(p_data);
973 wine_tsx11_unlock();
976 /**********************************************************************
977 * EVENT_DropURLs
979 * drop items are separated by \n
980 * each item is prefixed by its mime type
982 * event->data.l[3], event->data.l[4] contains drop x,y position
984 static void EVENT_DropURLs( HWND hWnd, XClientMessageEvent *event )
986 struct x11drv_win_data *win_data;
987 unsigned long data_length;
988 unsigned long aux_long, drop_len = 0;
989 unsigned char *p_data = NULL; /* property data */
990 char *p_drop = NULL;
991 char *p, *next;
992 int x, y;
993 DROPFILES *lpDrop;
994 HDROP hDrop;
995 union {
996 Atom atom_aux;
997 int i;
998 Window w_aux;
999 unsigned int u;
1000 } u; /* unused */
1002 if (!(GetWindowLongW( hWnd, GWL_EXSTYLE ) & WS_EX_ACCEPTFILES)) return;
1004 wine_tsx11_lock();
1005 XGetWindowProperty( event->display, DefaultRootWindow(event->display),
1006 x11drv_atom(DndSelection), 0, 65535, FALSE,
1007 AnyPropertyType, &u.atom_aux, &u.i,
1008 &data_length, &aux_long, &p_data);
1009 wine_tsx11_unlock();
1010 if (aux_long)
1011 WARN("property too large, truncated!\n");
1012 TRACE("urls=%s\n", p_data);
1014 if( !aux_long && p_data) { /* don't bother if > 64K */
1015 /* calculate length */
1016 p = (char*) p_data;
1017 next = strchr(p, '\n');
1018 while (p) {
1019 if (next) *next=0;
1020 if (strncmp(p,"file:",5) == 0 ) {
1021 INT len = GetShortPathNameA( p+5, NULL, 0 );
1022 if (len) drop_len += len + 1;
1024 if (next) {
1025 *next = '\n';
1026 p = next + 1;
1027 next = strchr(p, '\n');
1028 } else {
1029 p = NULL;
1033 if( drop_len && drop_len < 65535 ) {
1034 wine_tsx11_lock();
1035 XQueryPointer( event->display, root_window, &u.w_aux, &u.w_aux,
1036 &x, &y, &u.i, &u.i, &u.u);
1037 x += virtual_screen_rect.left;
1038 y += virtual_screen_rect.top;
1039 wine_tsx11_unlock();
1041 drop_len += sizeof(DROPFILES) + 1;
1042 hDrop = GlobalAlloc( GMEM_SHARE, drop_len );
1043 lpDrop = (DROPFILES *) GlobalLock( hDrop );
1045 if( lpDrop && (win_data = X11DRV_get_win_data( hWnd )))
1047 lpDrop->pFiles = sizeof(DROPFILES);
1048 lpDrop->pt.x = x;
1049 lpDrop->pt.y = y;
1050 lpDrop->fNC =
1051 ( x < (win_data->client_rect.left - win_data->whole_rect.left) ||
1052 y < (win_data->client_rect.top - win_data->whole_rect.top) ||
1053 x > (win_data->client_rect.right - win_data->whole_rect.left) ||
1054 y > (win_data->client_rect.bottom - win_data->whole_rect.top) );
1055 lpDrop->fWide = FALSE;
1056 p_drop = (char*)(lpDrop + 1);
1059 /* create message content */
1060 if (p_drop) {
1061 p = (char*) p_data;
1062 next = strchr(p, '\n');
1063 while (p) {
1064 if (next) *next=0;
1065 if (strncmp(p,"file:",5) == 0 ) {
1066 INT len = GetShortPathNameA( p+5, p_drop, 65535 );
1067 if (len) {
1068 TRACE("drop file %s as %s\n", p+5, p_drop);
1069 p_drop += len+1;
1070 } else {
1071 WARN("can't convert file %s to dos name\n", p+5);
1073 } else {
1074 WARN("unknown mime type %s\n", p);
1076 if (next) {
1077 *next = '\n';
1078 p = next + 1;
1079 next = strchr(p, '\n');
1080 } else {
1081 p = NULL;
1083 *p_drop = '\0';
1086 GlobalUnlock(hDrop);
1087 PostMessageA( hWnd, WM_DROPFILES, (WPARAM)hDrop, 0L );
1090 wine_tsx11_lock();
1091 if( p_data ) XFree(p_data);
1092 wine_tsx11_unlock();
1096 /**********************************************************************
1097 * handle_dnd_protocol
1099 static void handle_dnd_protocol( HWND hwnd, XClientMessageEvent *event )
1101 Window root, child;
1102 int root_x, root_y, child_x, child_y;
1103 unsigned int u;
1105 /* query window (drag&drop event contains only drag window) */
1106 wine_tsx11_lock();
1107 XQueryPointer( event->display, root_window, &root, &child,
1108 &root_x, &root_y, &child_x, &child_y, &u);
1109 if (XFindContext( event->display, child, winContext, (char **)&hwnd ) != 0) hwnd = 0;
1110 wine_tsx11_unlock();
1111 if (!hwnd) return;
1112 if (event->data.l[0] == DndFile || event->data.l[0] == DndFiles)
1113 EVENT_DropFromOffiX(hwnd, event);
1114 else if (event->data.l[0] == DndURL)
1115 EVENT_DropURLs(hwnd, event);
1119 struct client_message_handler
1121 int atom; /* protocol atom */
1122 void (*handler)(HWND, XClientMessageEvent *); /* corresponding handler function */
1125 static const struct client_message_handler client_messages[] =
1127 { XATOM_WM_PROTOCOLS, handle_wm_protocols },
1128 { XATOM_DndProtocol, handle_dnd_protocol },
1129 { XATOM_XdndEnter, X11DRV_XDND_EnterEvent },
1130 { XATOM_XdndPosition, X11DRV_XDND_PositionEvent },
1131 { XATOM_XdndDrop, X11DRV_XDND_DropEvent },
1132 { XATOM_XdndLeave, X11DRV_XDND_LeaveEvent }
1136 /**********************************************************************
1137 * EVENT_ClientMessage
1139 static void EVENT_ClientMessage( HWND hwnd, XEvent *xev )
1141 XClientMessageEvent *event = &xev->xclient;
1142 unsigned int i;
1144 if (!hwnd) return;
1146 if (event->format != 32)
1148 WARN( "Don't know how to handle format %d\n", event->format );
1149 return;
1152 for (i = 0; i < sizeof(client_messages)/sizeof(client_messages[0]); i++)
1154 if (event->message_type == X11DRV_Atoms[client_messages[i].atom - FIRST_XATOM])
1156 client_messages[i].handler( hwnd, event );
1157 return;
1160 TRACE( "no handler found for %ld\n", event->message_type );
1164 /**********************************************************************
1165 * X11DRV_WindowMessage (X11DRV.@)
1167 LRESULT X11DRV_WindowMessage( HWND hwnd, UINT msg, WPARAM wp, LPARAM lp )
1169 switch(msg)
1171 case WM_X11DRV_ACQUIRE_SELECTION:
1172 return X11DRV_AcquireClipboard( hwnd );
1173 case WM_X11DRV_DELETE_WINDOW:
1174 return SendMessageW( hwnd, WM_SYSCOMMAND, SC_CLOSE, 0 );
1175 case WM_X11DRV_SET_WIN_FORMAT:
1176 return X11DRV_set_win_format( hwnd, (XID)wp );
1177 case WM_X11DRV_RESIZE_DESKTOP:
1178 X11DRV_resize_desktop( LOWORD(lp), HIWORD(lp) );
1179 return 0;
1180 default:
1181 FIXME( "got window msg %x hwnd %p wp %lx lp %lx\n", msg, hwnd, wp, lp );
1182 return 0;
1187 /***********************************************************************
1188 * X11DRV_SendInput (X11DRV.@)
1190 UINT X11DRV_SendInput( UINT count, LPINPUT inputs, int size )
1192 UINT i;
1194 for (i = 0; i < count; i++, inputs++)
1196 switch(inputs->type)
1198 case INPUT_MOUSE:
1199 X11DRV_send_mouse_input( 0, inputs->u.mi.dwFlags, inputs->u.mi.dx, inputs->u.mi.dy,
1200 inputs->u.mi.mouseData, inputs->u.mi.time,
1201 inputs->u.mi.dwExtraInfo, LLMHF_INJECTED );
1202 break;
1203 case INPUT_KEYBOARD:
1204 X11DRV_send_keyboard_input( inputs->u.ki.wVk, inputs->u.ki.wScan, inputs->u.ki.dwFlags,
1205 inputs->u.ki.time, inputs->u.ki.dwExtraInfo, LLKHF_INJECTED );
1206 break;
1207 case INPUT_HARDWARE:
1208 FIXME( "INPUT_HARDWARE not supported\n" );
1209 break;
1212 return count;